Line data Source code
1 0 : /*
2 :
3 : OOVisualEffectEntity.m
4 :
5 :
6 : Oolite
7 : Copyright (C) 2004-2013 Giles C Williams and contributors
8 :
9 : This program is free software; you can redistribute it and/or
10 : modify it under the terms of the GNU General Public License
11 : as published by the Free Software Foundation; either version 2
12 : of the License, or (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the impllied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program; if not, write to the Free Software
21 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 : MA 02110-1301, USA.
23 :
24 : */
25 :
26 : #import "OOVisualEffectEntity.h"
27 :
28 : #import "OOMaths.h"
29 : #import "Universe.h"
30 : #import "OOShaderMaterial.h"
31 : #import "OOOpenGLExtensionManager.h"
32 :
33 : #import "ResourceManager.h"
34 : #import "OOStringParsing.h"
35 : #import "OOStringExpander.h"
36 : #import "OOCollectionExtractors.h"
37 : #import "OOConstToString.h"
38 : #import "OOConstToJSString.h"
39 :
40 : #import "OOMesh.h"
41 :
42 : #import "OOColor.h"
43 : #import "OOPolygonSprite.h"
44 :
45 : #import "OOFlasherEntity.h"
46 :
47 : #import "OODebugGLDrawing.h"
48 : #import "OODebugFlags.h"
49 :
50 : #import "OOJSScript.h"
51 :
52 : #import "OOFilteringEnumerator.h"
53 :
54 : #import "MyOpenGLView.h"
55 :
56 : @interface OOVisualEffectEntity (Private)
57 :
58 0 : - (void) drawSubEntityImmediate:(bool)immediate translucent:(bool)translucent;
59 :
60 0 : - (void) addSubEntity:(Entity<OOSubEntity> *) subent;
61 0 : - (BOOL) setUpOneSubentity:(NSDictionary *) subentDict;
62 0 : - (BOOL) setUpOneFlasher:(NSDictionary *) subentDict;
63 0 : - (BOOL) setUpOneStandardSubentity:(NSDictionary *)subentDict;
64 :
65 : @end
66 :
67 :
68 : @implementation OOVisualEffectEntity
69 :
70 0 : - (id) init
71 : {
72 : return [self initWithKey:@"" definition:nil];
73 : }
74 :
75 : - (id)initWithKey:(NSString *)key definition:(NSDictionary *)dict
76 : {
77 : OOJS_PROFILE_ENTER
78 :
79 : NSParameterAssert(dict != nil);
80 :
81 : self = [super init];
82 : if (self == nil) return nil;
83 :
84 : _effectKey = [key retain];
85 :
86 : if (![self setUpVisualEffectFromDictionary:dict])
87 : {
88 : [self release];
89 : self = nil;
90 : }
91 :
92 : _haveExecutedSpawnAction = NO;
93 :
94 : return self;
95 :
96 : OOJS_PROFILE_EXIT
97 : }
98 :
99 :
100 : - (BOOL) setUpVisualEffectFromDictionary:(NSDictionary *) effectDict
101 : {
102 : OOJS_PROFILE_ENTER
103 :
104 : effectinfoDictionary = [effectDict copy];
105 : if (effectinfoDictionary == nil) effectinfoDictionary = [[NSDictionary alloc] init];
106 :
107 : orientation = kIdentityQuaternion;
108 : rotMatrix = kIdentityMatrix;
109 :
110 : collision_radius = 0.0;
111 :
112 : NSString *modelName = [effectDict oo_stringForKey:@"model"];
113 : if (modelName != nil)
114 : {
115 : OOMesh *mesh = [OOMesh meshWithName:modelName
116 : cacheKey:_effectKey
117 : materialDictionary:[effectDict oo_dictionaryForKey:@"materials"]
118 : shadersDictionary:[effectDict oo_dictionaryForKey:@"shaders"]
119 : smooth:[effectDict oo_boolForKey:@"smooth" defaultValue:NO]
120 : shaderMacros:OODefaultShipShaderMacros()
121 : shaderBindingTarget:self];
122 : if (mesh == nil) return NO;
123 : [self setMesh:mesh];
124 : }
125 :
126 : isImmuneToBreakPatternHide = [effectDict oo_boolForKey:@"is_break_pattern"];
127 : scaleX = 1.0;
128 : scaleY = 1.0;
129 : scaleZ = 1.0;
130 :
131 : [self clearSubEntities];
132 : [self setUpSubEntities];
133 :
134 : [self setScannerDisplayColor1:nil];
135 : [self setScannerDisplayColor2:nil];
136 :
137 : scanClass = CLASS_VISUAL_EFFECT;
138 :
139 : [self setStatus:STATUS_EFFECT];
140 :
141 : _hullHeatLevel = 60.0 / 256.0;
142 : _shaderFloat1 = 0.0;
143 : _shaderFloat2 = 0.0;
144 : _shaderInt1 = 0;
145 : _shaderInt2 = 0;
146 : _shaderVector1 = kZeroVector;
147 : _shaderVector2 = kZeroVector;
148 :
149 : [self setBeaconCode:[effectDict oo_stringForKey:@"beacon"]];
150 : [self setBeaconLabel:[effectDict oo_stringForKey:@"beacon_label" defaultValue:[self beaconCode]]];
151 :
152 : scriptInfo = [[effectDict oo_dictionaryForKey:@"script_info" defaultValue:nil] retain];
153 : [self setScript:[effectDict oo_stringForKey:@"script"]];
154 :
155 : return YES;
156 :
157 : OOJS_PROFILE_EXIT
158 : }
159 :
160 :
161 0 : - (void) dealloc
162 : {
163 : [self clearSubEntities];
164 : DESTROY(_effectKey);
165 : DESTROY(effectinfoDictionary);
166 : DESTROY(scanner_display_color1);
167 : DESTROY(scanner_display_color2);
168 : DESTROY(scriptInfo);
169 : DESTROY(script);
170 : DESTROY(_beaconCode);
171 : DESTROY(_beaconLabel);
172 : DESTROY(_beaconDrawable);
173 :
174 : [super dealloc];
175 : }
176 :
177 :
178 0 : - (BOOL) isEffect
179 : {
180 : return YES;
181 : }
182 :
183 :
184 0 : - (BOOL) isVisualEffect
185 : {
186 : return YES;
187 : }
188 :
189 :
190 0 : - (BOOL) canCollide
191 : {
192 : return NO;
193 : }
194 :
195 :
196 : - (OOMesh *)mesh
197 : {
198 : return (OOMesh *)[self drawable];
199 : }
200 :
201 :
202 : - (void)setMesh:(OOMesh *)mesh
203 : {
204 : if (mesh != [self mesh])
205 : {
206 : [self setDrawable:mesh];
207 : }
208 : }
209 :
210 :
211 : - (NSString *)effectKey
212 : {
213 : return _effectKey;
214 : }
215 :
216 :
217 : - (GLfloat)frustumRadius
218 : {
219 : return [self scaleMax] * _profileRadius;
220 : }
221 :
222 :
223 : - (void) clearSubEntities
224 : {
225 : [subEntities makeObjectsPerformSelector:@selector(setOwner:) withObject:nil]; // Ensure backlinks are broken
226 : [subEntities release];
227 : subEntities = nil;
228 :
229 : // reset size & mass!
230 : if ([self mesh])
231 : {
232 : collision_radius = [self findCollisionRadius];
233 : }
234 : else
235 : {
236 : collision_radius = 0.0;
237 : }
238 : _profileRadius = collision_radius;
239 : }
240 :
241 :
242 : - (BOOL)setUpSubEntities
243 : {
244 : unsigned int i;
245 : _profileRadius = collision_radius;
246 : NSArray *subs = [effectinfoDictionary oo_arrayForKey:@"subentities"];
247 :
248 : for (i = 0; i < [subs count]; i++)
249 : {
250 : [self setUpOneSubentity:[subs oo_dictionaryAtIndex:i]];
251 : }
252 :
253 : [self setNoDrawDistance];
254 :
255 : return YES;
256 : }
257 :
258 :
259 : - (void) removeSubEntity:(Entity<OOSubEntity> *)sub
260 : {
261 : [sub setOwner:nil];
262 : [subEntities removeObject:sub];
263 : }
264 :
265 :
266 : - (void) setNoDrawDistance
267 : {
268 : GLfloat r = _profileRadius * [self scaleMax];
269 : no_draw_distance = r * r * NO_DRAW_DISTANCE_FACTOR * NO_DRAW_DISTANCE_FACTOR * 2.0;
270 :
271 : }
272 :
273 :
274 0 : - (BOOL) setUpOneSubentity:(NSDictionary *) subentDict
275 : {
276 : NSString *type = [subentDict oo_stringForKey:@"type"];
277 : if ([type isEqualToString:@"flasher"])
278 : {
279 : return [self setUpOneFlasher:subentDict];
280 : }
281 : else
282 : {
283 : return [self setUpOneStandardSubentity:subentDict];
284 : }
285 :
286 :
287 : }
288 :
289 :
290 0 : - (BOOL) setUpOneFlasher:(NSDictionary *) subentDict
291 : {
292 : OOFlasherEntity *flasher = [OOFlasherEntity flasherWithDictionary:subentDict];
293 : [flasher setPosition:[subentDict oo_hpvectorForKey:@"position"]];
294 : [self addSubEntity:flasher];
295 : return YES;
296 : }
297 :
298 :
299 0 : - (BOOL) setUpOneStandardSubentity:(NSDictionary *)subentDict
300 : {
301 : OOVisualEffectEntity *subentity = nil;
302 : NSString *subentKey = nil;
303 : HPVector subPosition;
304 : Quaternion subOrientation;
305 :
306 : subentKey = [subentDict oo_stringForKey:@"subentity_key"];
307 : if (subentKey == nil) {
308 : OOLog(@"setup.visualeffect.badEntry.subentities",@"Failed to set up entity - no subentKey in %@",subentDict);
309 : return NO;
310 : }
311 :
312 : subentity = [UNIVERSE newVisualEffectWithName:subentKey];
313 : if (subentity == nil) {
314 : OOLog(@"setup.visualeffect.badEntry.subentities",@"Failed to set up entity %@",subentKey);
315 : return NO;
316 : }
317 :
318 : subPosition = [subentDict oo_hpvectorForKey:@"position"];
319 : subOrientation = [subentDict oo_quaternionForKey:@"orientation"];
320 :
321 : [subentity setPosition:subPosition];
322 : [subentity setOrientation:subOrientation];
323 :
324 : [self addSubEntity:subentity];
325 :
326 : [subentity release];
327 :
328 : return YES;
329 : }
330 :
331 :
332 0 : - (void) addSubEntity:(Entity<OOSubEntity> *)sub
333 : {
334 : if (sub == nil) return;
335 :
336 : if (subEntities == nil) subEntities = [[NSMutableArray alloc] init];
337 : sub->isSubEntity = YES;
338 : // Order matters - need consistent state in setOwner:. -- Ahruman 2008-04-20
339 : [subEntities addObject:sub];
340 : [sub setOwner:self];
341 :
342 : double distance = HPmagnitude([sub position]) + [sub findCollisionRadius];
343 : if (distance > _profileRadius)
344 : {
345 : _profileRadius = distance;
346 : }
347 : }
348 :
349 :
350 : - (NSArray *)subEntities
351 : {
352 : return [[subEntities copy] autorelease];
353 : }
354 :
355 :
356 : - (NSUInteger) subEntityCount
357 : {
358 : return [subEntities count];
359 : }
360 :
361 :
362 : - (NSEnumerator *) visualEffectSubEntityEnumerator
363 : {
364 : return [[self subEntities] objectEnumeratorFilteredWithSelector:@selector(isVisualEffect)];
365 : }
366 :
367 :
368 : - (BOOL) hasSubEntity:(Entity<OOSubEntity> *)sub
369 : {
370 : return [subEntities containsObject:sub];
371 : }
372 :
373 :
374 : - (NSEnumerator *)subEntityEnumerator
375 : {
376 : return [[self subEntities] objectEnumerator];
377 : }
378 :
379 :
380 : - (NSEnumerator *)effectSubEntityEnumerator
381 : {
382 : return [[self subEntities] objectEnumeratorFilteredWithSelector:@selector(isVisualEffect)];
383 : }
384 :
385 :
386 : - (NSEnumerator *)flasherEnumerator
387 : {
388 : return [[self subEntities] objectEnumeratorFilteredWithSelector:@selector(isFlasher)];
389 : }
390 :
391 :
392 0 : - (void) drawSubEntityImmediate:(bool)immediate translucent:(bool)translucent
393 : {
394 : if (cam_zero_distance > no_draw_distance) // this test provides an opportunity to do simple LoD culling
395 : {
396 : return; // TOO FAR AWAY
397 : }
398 : OOGLPushModelView();
399 : // HPVect: camera position
400 : OOGLTranslateModelView(HPVectorToVector(position));
401 : OOGLMultModelView(rotMatrix);
402 : [self drawImmediate:immediate translucent:translucent];
403 :
404 : OOGLPopModelView();
405 : }
406 :
407 :
408 0 : - (void) rescaleBy:(GLfloat)factor
409 : {
410 : if ([self mesh] != nil) {
411 : [self setMesh:[[self mesh] meshRescaledBy:factor]];
412 : }
413 :
414 : // rescale subentities
415 : Entity<OOSubEntity> *se = nil;
416 : foreach (se, [self subEntities])
417 : {
418 : [se setPosition:HPvector_multiply_scalar([se position], factor)];
419 : [se rescaleBy:factor];
420 : }
421 :
422 : collision_radius *= factor;
423 : _profileRadius *= factor;
424 : }
425 :
426 :
427 0 : - (void) rescaleBy:(GLfloat)factor writeToCache:(BOOL)writeToCache
428 : {
429 : /* Do nothing; this is only needed because of OOEntityWithDrawable
430 : implementation requirements */
431 : }
432 :
433 :
434 : - (GLfloat) scaleMax
435 : {
436 : GLfloat scale = 1.0;
437 : if (scaleX > scaleY)
438 : {
439 : if (scaleX > scaleZ)
440 : {
441 : scale *= scaleX;
442 : }
443 : else
444 : {
445 : scale *= scaleZ;
446 : }
447 : }
448 : else if (scaleY > scaleZ)
449 : {
450 : scale *= scaleY;
451 : }
452 : else
453 : {
454 : scale *= scaleZ;
455 : }
456 : return scale;
457 : }
458 :
459 : - (GLfloat) scaleX
460 : {
461 : return scaleX;
462 : }
463 :
464 :
465 : - (void) setScaleX:(GLfloat)factor
466 : {
467 : // rescale subentities
468 : Entity<OOSubEntity> *se = nil;
469 : GLfloat flasher_factor = pow(factor/scaleX,1.0/3.0);
470 : foreach (se, [self subEntities])
471 : {
472 : HPVector move = [se position];
473 : move.x *= factor/scaleX;
474 : [se setPosition:move];
475 : if ([se isVisualEffect])
476 : {
477 : [(OOVisualEffectEntity*)se setScaleX:factor];
478 : }
479 : else
480 : {
481 : [se rescaleBy:flasher_factor];
482 : }
483 : }
484 :
485 : scaleX = factor;
486 : [self setNoDrawDistance];
487 : }
488 :
489 :
490 : - (GLfloat) scaleY
491 : {
492 : return scaleY;
493 : }
494 :
495 :
496 : - (void) setScaleY:(GLfloat)factor
497 : {
498 : // rescale subentities
499 : Entity<OOSubEntity> *se = nil;
500 : GLfloat flasher_factor = pow(factor/scaleY,1.0/3.0);
501 : foreach (se, [self subEntities])
502 : {
503 : HPVector move = [se position];
504 : move.y *= factor/scaleY;
505 : [se setPosition:move];
506 : if ([se isVisualEffect])
507 : {
508 : [(OOVisualEffectEntity*)se setScaleY:factor];
509 : }
510 : else
511 : {
512 : [se rescaleBy:flasher_factor];
513 : }
514 : }
515 :
516 : scaleY = factor;
517 : [self setNoDrawDistance];
518 : }
519 :
520 :
521 : - (GLfloat) scaleZ
522 : {
523 : return scaleZ;
524 : }
525 :
526 :
527 : - (void) setScaleZ:(GLfloat)factor
528 : {
529 : // rescale subentities
530 : Entity<OOSubEntity> *se = nil;
531 : GLfloat flasher_factor = pow(factor/scaleZ,1.0/3.0);
532 : foreach (se, [self subEntities])
533 : {
534 : HPVector move = [se position];
535 : move.z *= factor/scaleZ;
536 : [se setPosition:move];
537 : if ([se isVisualEffect])
538 : {
539 : [(OOVisualEffectEntity*)se setScaleZ:factor];
540 : }
541 : else
542 : {
543 : [se rescaleBy:flasher_factor];
544 : }
545 : }
546 :
547 : scaleZ = factor;
548 : [self setNoDrawDistance];
549 : }
550 :
551 :
552 0 : - (GLfloat) collisionRadius
553 : {
554 : return [self scaleMax] * collision_radius;
555 : }
556 :
557 :
558 : - (void) orientationChanged
559 : {
560 : [super orientationChanged];
561 :
562 : _v_forward = vector_forward_from_quaternion(orientation);
563 : _v_up = vector_up_from_quaternion(orientation);
564 : _v_right = vector_right_from_quaternion(orientation);
565 : }
566 :
567 :
568 : // exposed to shaders
569 : - (Vector) forwardVector
570 : {
571 : return _v_forward;
572 : }
573 :
574 :
575 : // exposed to shaders
576 : - (Vector) upVector
577 : {
578 : return _v_up;
579 : }
580 :
581 :
582 : // exposed to shaders
583 : - (Vector) rightVector
584 : {
585 : return _v_right;
586 : }
587 :
588 :
589 : - (OOColor *)scannerDisplayColor1
590 : {
591 : return [[scanner_display_color1 retain] autorelease];
592 : }
593 :
594 :
595 : - (OOColor *)scannerDisplayColor2
596 : {
597 : return [[scanner_display_color2 retain] autorelease];
598 : }
599 :
600 :
601 : - (void)setScannerDisplayColor1:(OOColor *)color
602 : {
603 : DESTROY(scanner_display_color1);
604 :
605 : if (color == nil) color = [OOColor colorWithDescription:[effectinfoDictionary objectForKey:@"scanner_display_color1"]];
606 : scanner_display_color1 = [color retain];
607 : }
608 :
609 :
610 : - (void)setScannerDisplayColor2:(OOColor *)color
611 : {
612 : DESTROY(scanner_display_color2);
613 :
614 : if (color == nil) color = [OOColor colorWithDescription:[effectinfoDictionary objectForKey:@"scanner_display_color2"]];
615 : scanner_display_color2 = [color retain];
616 : }
617 :
618 0 : static GLfloat default_color[4] = { 0.0, 0.0, 0.0, 0.0};
619 0 : static GLfloat scripted_color[4] = { 0.0, 0.0, 0.0, 0.0};
620 :
621 : - (GLfloat *) scannerDisplayColorForShip:(BOOL)flash :(OOColor *)scannerDisplayColor1 :(OOColor *)scannerDisplayColor2
622 : {
623 :
624 : if (scannerDisplayColor1 || scannerDisplayColor2)
625 : {
626 : if (scannerDisplayColor1 && !scannerDisplayColor2)
627 : {
628 : [scannerDisplayColor1 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
629 : }
630 :
631 : if (!scannerDisplayColor1 && scannerDisplayColor2)
632 : {
633 : [scannerDisplayColor2 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
634 : }
635 :
636 : if (scannerDisplayColor1 && scannerDisplayColor2)
637 : {
638 : if (flash)
639 : [scannerDisplayColor1 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
640 : else
641 : [scannerDisplayColor2 getRed:&scripted_color[0] green:&scripted_color[1] blue:&scripted_color[2] alpha:&scripted_color[3]];
642 : }
643 :
644 : return scripted_color;
645 : }
646 :
647 : return default_color; // transparent black if not specified
648 : }
649 :
650 0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
651 : {
652 : if (no_draw_distance < cam_zero_distance)
653 : {
654 : return; // too far away to draw
655 : }
656 : OOGLPushModelView();
657 : OOGLScaleModelView(make_vector(scaleX,scaleY,scaleZ));
658 :
659 : if ([self mesh] != nil)
660 : {
661 : [super drawImmediate:immediate translucent:translucent];
662 : }
663 : OOGLPopModelView();
664 :
665 : // Draw subentities.
666 : if (!immediate) // TODO: is this relevant any longer?
667 : {
668 : Entity<OOSubEntity> *subEntity = nil;
669 : foreach (subEntity, [self subEntities])
670 : {
671 : [subEntity drawSubEntityImmediate:immediate translucent:translucent];
672 : }
673 : }
674 : }
675 :
676 :
677 0 : - (void) update:(OOTimeDelta)delta_t
678 : {
679 : [super update:delta_t];
680 :
681 : if (!_haveExecutedSpawnAction) {
682 : [self doScriptEvent:OOJSID("effectSpawned")];
683 : _haveExecutedSpawnAction = YES;
684 : }
685 :
686 : Entity *se = nil;
687 : foreach (se, [self subEntities])
688 : {
689 : [se update:delta_t];
690 : }
691 : }
692 :
693 :
694 : - (BOOL) isBreakPattern
695 : {
696 : return isImmuneToBreakPatternHide;
697 : }
698 :
699 :
700 : - (void) setIsBreakPattern:(BOOL)bp
701 : {
702 : isImmuneToBreakPatternHide = bp;
703 : }
704 :
705 :
706 : - (NSDictionary *)effectInfoDictionary
707 : {
708 : return effectinfoDictionary;
709 : }
710 :
711 :
712 : /* scripting */
713 :
714 : - (void) setScript:(NSString *)script_name
715 : {
716 : NSMutableDictionary *properties = nil;
717 :
718 : properties = [NSMutableDictionary dictionary];
719 : [properties setObject:self forKey:@"visualEffect"];
720 :
721 : [script autorelease];
722 : script = [OOScript jsScriptFromFileNamed:script_name properties:properties];
723 : // does not support legacy scripting
724 : if (script == nil) {
725 : script = [OOScript jsScriptFromFileNamed:@"oolite-default-effect-script.js" properties:properties];
726 : }
727 : [script retain];
728 : }
729 :
730 :
731 : - (OOJSScript *)script
732 : {
733 : return script;
734 : }
735 :
736 :
737 : - (NSDictionary *)scriptInfo
738 : {
739 : return (scriptInfo != nil) ? scriptInfo : (NSDictionary *)[NSDictionary dictionary];
740 : }
741 :
742 : // unlikely to need events with arguments
743 : - (void) doScriptEvent:(jsid)message
744 : {
745 : JSContext *context = OOJSAcquireContext();
746 : [script callMethod:message inContext:context withArguments:NULL count:0 result:NULL];
747 : OOJSRelinquishContext(context);
748 : }
749 :
750 :
751 : - (void) remove
752 : {
753 : [self doScriptEvent:OOJSID("effectRemoved")];
754 : [UNIVERSE removeEntity:(Entity*)self];
755 : }
756 :
757 :
758 : /* beacons */
759 :
760 0 : - (NSComparisonResult) compareBeaconCodeWith:(Entity<OOBeaconEntity> *) other
761 : {
762 : return [[self beaconCode] compare:[other beaconCode] options: NSCaseInsensitiveSearch];
763 : }
764 :
765 :
766 0 : - (NSString *) beaconCode
767 : {
768 : return _beaconCode;
769 : }
770 :
771 :
772 0 : - (void) setBeaconCode:(NSString *)bcode
773 : {
774 : if ([bcode length] == 0) bcode = nil;
775 :
776 : if (_beaconCode != bcode)
777 : {
778 : [_beaconCode release];
779 : _beaconCode = [bcode copy];
780 :
781 : DESTROY(_beaconDrawable);
782 : }
783 : // if not blanking code and label is currently blank, default label to code
784 : if (bcode != nil && (_beaconLabel == nil || [_beaconLabel length] == 0))
785 : {
786 : [self setBeaconLabel:bcode];
787 : }
788 :
789 : }
790 :
791 :
792 0 : - (NSString *) beaconLabel
793 : {
794 : return _beaconLabel;
795 : }
796 :
797 :
798 0 : - (void) setBeaconLabel:(NSString *)blabel
799 : {
800 : if ([blabel length] == 0) blabel = nil;
801 :
802 : if (_beaconLabel != blabel)
803 : {
804 : [_beaconLabel release];
805 : _beaconLabel = [OOExpand(blabel) retain];
806 : }
807 : }
808 :
809 :
810 0 : - (BOOL) isBeacon
811 : {
812 : return [self beaconCode] != nil;
813 : }
814 :
815 :
816 0 : - (id <OOHUDBeaconIcon>) beaconDrawable
817 : {
818 : if (_beaconDrawable == nil)
819 : {
820 : NSString *beaconCode = [self beaconCode];
821 : NSUInteger length = [beaconCode length];
822 :
823 : if (length > 1)
824 : {
825 : NSArray *iconData = [[UNIVERSE descriptions] oo_arrayForKey:beaconCode];
826 : if (iconData != nil) _beaconDrawable = [[OOPolygonSprite alloc] initWithDataArray:iconData outlineWidth:0.5 name:beaconCode];
827 : }
828 :
829 : if (_beaconDrawable == nil)
830 : {
831 : if (length > 0) _beaconDrawable = [[beaconCode substringToIndex:1] retain];
832 : else _beaconDrawable = @"";
833 : }
834 : }
835 :
836 : return _beaconDrawable;
837 : }
838 :
839 :
840 0 : - (Entity <OOBeaconEntity> *) prevBeacon
841 : {
842 : return [_prevBeacon weakRefUnderlyingObject];
843 : }
844 :
845 :
846 0 : - (Entity <OOBeaconEntity> *) nextBeacon
847 : {
848 : return [_nextBeacon weakRefUnderlyingObject];
849 : }
850 :
851 :
852 0 : - (void) setPrevBeacon:(Entity <OOBeaconEntity> *)beaconShip
853 : {
854 : if (beaconShip != [self prevBeacon])
855 : {
856 : [_prevBeacon release];
857 : _prevBeacon = [beaconShip weakRetain];
858 : }
859 : }
860 :
861 :
862 0 : - (void) setNextBeacon:(Entity <OOBeaconEntity> *)beaconShip
863 : {
864 : if (beaconShip != [self nextBeacon])
865 : {
866 : [_nextBeacon release];
867 : _nextBeacon = [beaconShip weakRetain];
868 : }
869 : }
870 :
871 :
872 0 : - (BOOL) isJammingScanning
873 : {
874 : return NO;
875 : }
876 :
877 :
878 : /* Shader bindable uniforms */
879 :
880 : // no automatic change of this, but simplifies use of default shader
881 : - (GLfloat)hullHeatLevel
882 : {
883 : return _hullHeatLevel;
884 : }
885 :
886 :
887 : - (void)setHullHeatLevel:(GLfloat)value
888 : {
889 : _hullHeatLevel = OOClamp_0_1_f(value);
890 : }
891 :
892 :
893 : - (GLfloat) shaderFloat1
894 : {
895 : return _shaderFloat1;
896 : }
897 :
898 :
899 : - (void)setShaderFloat1:(GLfloat)value
900 : {
901 : _shaderFloat1 = value;
902 : }
903 :
904 :
905 : - (GLfloat) shaderFloat2
906 : {
907 : return _shaderFloat2;
908 : }
909 :
910 :
911 : - (void)setShaderFloat2:(GLfloat)value
912 : {
913 : _shaderFloat2 = value;
914 : }
915 :
916 :
917 : - (int) shaderInt1
918 : {
919 : return _shaderInt1;
920 : }
921 :
922 :
923 : - (void)setShaderInt1:(int)value
924 : {
925 : _shaderInt1 = value;
926 : }
927 :
928 :
929 : - (int) shaderInt2
930 : {
931 : return _shaderInt2;
932 : }
933 :
934 :
935 : - (void)setShaderInt2:(int)value
936 : {
937 : _shaderInt2 = value;
938 : }
939 :
940 :
941 : - (Vector) shaderVector1
942 : {
943 : return _shaderVector1;
944 : }
945 :
946 :
947 : - (void)setShaderVector1:(Vector)value
948 : {
949 : _shaderVector1 = value;
950 : }
951 :
952 :
953 : - (Vector) shaderVector2
954 : {
955 : return _shaderVector2;
956 : }
957 :
958 :
959 : - (void)setShaderVector2:(Vector)value
960 : {
961 : _shaderVector2 = value;
962 : }
963 :
964 :
965 : @end
966 :
967 : @implementation OOVisualEffectEntity (SubEntityRelationship)
968 :
969 : // a slightly misnamed test now things other than ships can have subents
970 0 : - (BOOL) isShipWithSubEntityShip:(Entity *)other
971 : {
972 : assert ([self isVisualEffect]);
973 :
974 : if (![other isVisualEffect]) return NO;
975 : if (![other isSubEntity]) return NO;
976 : if ([other owner] != self) return NO;
977 :
978 : #ifndef NDEBUG
979 : // Sanity check; this should always be true.
980 : if (![self hasSubEntity:(OOVisualEffectEntity *)other])
981 : {
982 : OOLogERR(@"visualeffect.subentity.sanityCheck.failed", @"%@ thinks it's a subentity of %@, but the supposed parent does not agree. %@", [other shortDescription], [self shortDescription], @"This is an internal error, please report it.");
983 : [other setOwner:nil];
984 : return NO;
985 : }
986 : #endif
987 :
988 : return YES;
989 : }
990 :
991 : @end
|