Oolite 1.91.0.7645-241119-222d325
Loading...
Searching...
No Matches
WormholeEntity.m
Go to the documentation of this file.
1/*
2
3WormholeEntity.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "WormholeEntity.h"
26
27#import "ShipEntity.h"
28#import "OOSunEntity.h"
29#import "OOPlanetEntity.h"
30#import "PlayerEntity.h"
32
33#import "Universe.h"
34#import "AI.h"
35#import "OORoleSet.h"
36#import "OOShipRegistry.h"
37#import "OOShipGroup.h"
38#import "OOStringParsing.h"
40#import "OOLoggingExtended.h"
42
43#define OO_WORMHOLE_COLOR_BOOST 25.0
44#define OO_WORMHOLE_COLOR_FVEC4 { 0.067, 0.067, 1.0, 0.25 }
45
46// Hidden interface
47@interface WormholeEntity (Private)
48
49-(id) init;
50
51@end
52
53// Static local functions
54static void DrawWormholeCorona(GLfloat inner_radius, GLfloat outer_radius, int step, GLfloat z_distance, GLfloat *col4v1);
55
56
57@implementation WormholeEntity (Private)
58
59-(id) init
60{
61 if ((self = [super init]))
62 {
63 witch_mass = 0.0;
64 shipsInTransit = [[NSMutableArray arrayWithCapacity:4] retain];
65 collision_radius = 0.0;
66 [self setStatus:STATUS_EFFECT];
67 scanClass = CLASS_WORMHOLE;
68 isWormhole = YES;
69 scan_info = WH_SCANINFO_NONE;
70 scan_time = 0;
71 hasExitPosition = NO;
72 containsPlayer = NO;
73 exit_speed = 50.0;
74 }
75 return self;
76}
77
78@end // Private interface implementation
79
80
81//
82// Public Wormhole Implementation
83//
84
85@implementation WormholeEntity
86
87- (WormholeEntity*)initWithDict:(NSDictionary*)dict
88{
89 assert(dict != nil);
90
91 if ((self = [self init]))
92 {
93 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
94
95 // wormholes from pre-1.80 savegames using "origin_seed" and "dest_seed"
96 // currently get defaults set; will probably disappear unnoticed
97 origin = [dict oo_intForKey:@"origin_id" defaultValue:0];
98 destination = [dict oo_intForKey:@"dest_id" defaultValue:255];
99
100 originCoords = [[UNIVERSE systemManager] getCoordinatesForSystem:origin inGalaxy:[PLAYER galaxyNumber]];
101 destinationCoords = [[UNIVERSE systemManager] getCoordinatesForSystem:destination inGalaxy:[PLAYER galaxyNumber]];
102
103 // We only ever init from dictionary if we're loaded by the player, so
104 // by definition we have been scanned
105 scan_info = WH_SCANINFO_SCANNED;
106
107 // Remember, times are stored as Ship Clock - but anything
108 // saving/restoring wormholes from dictionaries should know this!
109 expiry_time = [dict oo_doubleForKey:@"expiry_time"];
110 arrival_time = [dict oo_doubleForKey:@"arrival_time"];
111 // just in case an old save game has one with crossed times
112 if (expiry_time > arrival_time)
113 {
114 expiry_time = arrival_time - 1.0;
115 }
116
117 // Since this is new for 1.75.1, we must give it a default values as we could be loading an old savegame
118 estimated_arrival_time = [dict oo_doubleForKey:@"estimated_arrival_time" defaultValue:arrival_time];
119 position = [dict oo_hpvectorForKey:@"position"];
120 _misjump = [dict oo_boolForKey:@"misjump" defaultValue:NO];
121
122
123 // Setup shipsInTransit
124 NSArray * shipDictsArray = [dict oo_arrayForKey:@"ships"];
125 NSEnumerator *shipDicts = [shipDictsArray objectEnumerator];
126 NSDictionary *currShipDict = nil;
127 [shipsInTransit removeAllObjects];
128 NSMutableDictionary *restoreContext = [NSMutableDictionary dictionary];
129
130 while ((currShipDict = [shipDicts nextObject]) != nil)
131 {
132 NSDictionary *shipInfo = [currShipDict oo_dictionaryForKey:@"ship_info"];
133 if (shipInfo != nil)
134 {
136 useFallback:YES
137 context:restoreContext];
138 if (ship != nil)
139 {
140 [shipsInTransit addObject:[NSDictionary dictionaryWithObjectsAndKeys:
141 ship, @"ship",
142 [currShipDict objectForKey:@"time_delta"], @"time",
143 nil]];
144 }
145 else
146 {
147 OOLog(@"wormhole.load.warning", @"Wormhole ship \"%@\" failed to initialize - missing OXP or old-style saved wormhole data.", [shipInfo oo_stringForKey:@"ship_key"]);
148 }
149 }
150 }
151 [pool release];
152 }
153 return self;
154}
155
156- (WormholeEntity*) initWormholeTo:(OOSystemID) s fromShip:(ShipEntity *) ship
157{
158 assert(ship != nil);
159
160 if ((self = [self init]))
161 {
162 double now = [PLAYER clockTimeAdjusted];
163 double distance;
164 OOSunEntity *sun = [UNIVERSE sun];
165
166 _misjump = NO;
167 origin = [UNIVERSE currentSystemID];
168 destination = s;
169 originCoords = [PLAYER galaxy_coordinates];
170 destinationCoords = [[UNIVERSE systemManager] getCoordinatesForSystem:destination inGalaxy:[PLAYER galaxyNumber]];
171 distance = distanceBetweenPlanetPositions(originCoords.x, originCoords.y, destinationCoords.x, destinationCoords.y);
172 distance = fmax(distance, 0.1);
173 witch_mass = 200000.0; // MKW 2010.11.21 - originally the ship's mass was added twice - once here and once in suckInShip. Instead, we give each wormhole a minimum mass.
174 if ([ship isPlayer])
175 witch_mass += [ship mass]; // The player ship never gets sucked in, so add its mass here.
176
177 if (sun && ([sun willGoNova] || [sun goneNova]) && [ship mass] > 240000)
178 shrink_factor = [ship mass] / 240000; // don't allow longstanding wormholes in nova systems. (60 sec * WORMHOLE_SHRINK_RATE = 240 000)
179 else
180 shrink_factor = 1;
181
182 collision_radius = 0.5 * M_PI * pow(witch_mass, 1.0/3.0);
183 expiry_time = now + (witch_mass / WORMHOLE_SHRINK_RATE / shrink_factor);
184 travel_time = (distance * distance * 3600); // Taken from PlayerEntity.h
185 arrival_time = now + travel_time;
186 estimated_arrival_time = arrival_time;
187
188 /* There are a number of bugs where the arrival time is < than the
189 * expiry time (i.e. both ends open at once).
190 * Rather than try to flatten all of them, having been unsuccessful twice
191 * it seems easier to declare as a matter of wormhole physics that it
192 * _can't_ be open at both ends at once. - CIM: 13/12/12 */
193 if (expiry_time > arrival_time)
194 {
195 expiry_time = arrival_time - 1.0;
196 }
197 position = [ship position];
198 zero_distance = HPdistance2([PLAYER position], position);
199 }
200 return self;
201}
202
203
204- (void) setMisjump
205{
206 // Test for misjump first - it's entirely possibly that the wormhole
207 // has already been marked for misjumping when another ship enters it.
208 if (!_misjump)
209 {
210 double distance = distanceBetweenPlanetPositions(originCoords.x, originCoords.y, destinationCoords.x, destinationCoords.y);
211 double time_adjust = distance * distance * (3600 - 2700); // NB: Time adjustment is calculated using original distance. Formula matches the one in [PlayerEntity witchJumpTo]
212 arrival_time -= time_adjust;
213 travel_time -= time_adjust;
214 destinationCoords.x = (originCoords.x + destinationCoords.x) / 2;
215 destinationCoords.y = (originCoords.y + destinationCoords.y) / 2;
216 _misjump = YES;
217 }
218}
219
220
221- (void) setMisjumpWithRange:(GLfloat)range
222{
223 if (range <= 0.0 || range >= 1.0)
224 {
225 range = 0.5; // for safety, though nothing should be setting this
226 }
227 _misjumpRange = range;
228 // Test for misjump first - it's entirely possibly that the wormhole
229 // has already been marked for misjumping when another ship enters it.
230 if (!_misjump)
231 {
232 double distance = distanceBetweenPlanetPositions(originCoords.x, originCoords.y, destinationCoords.x, destinationCoords.y);
233 double time_adjust = (distance * (1-_misjumpRange))*(distance * (1-_misjumpRange))*3600.0;
234 // time adjustment ensures that misjumps not faster than normal jumps
235 // formulae for time and distance by mwerle at http://developer.berlios.de/pm/task.php?func=detailtask&project_task_id=4703&group_id=3577&group_project_id=1753
236 arrival_time -= time_adjust;
237 travel_time -= time_adjust;
238
239 destinationCoords.x = (originCoords.x * (1-_misjumpRange)) + (destinationCoords.x * _misjumpRange);
240 destinationCoords.y = (originCoords.y * (1-_misjumpRange)) + (destinationCoords.y * _misjumpRange);
241 _misjump = YES;
242 }
243}
244
245
246- (BOOL) withMisjump
247{
248 return _misjump;
249}
250
251
252- (GLfloat) misjumpRange
253{
254 return _misjumpRange;
255}
256
257
258- (BOOL) suckInShip:(ShipEntity *) ship
259{
260 if (!ship || [ship status] == STATUS_ENTERING_WITCHSPACE)
261 {
262 return NO;
263 }
264 if (origin != [UNIVERSE currentSystemID])
265 {
266 // if we're no longer in the origin system, can't suck in
267 return NO;
268 }
269 if ([PLAYER galaxy_coordinates].x != originCoords.x || [PLAYER galaxy_coordinates].y != originCoords.y)
270 {
271 // if we're no longer at the origin coordinates, can't suck in (handles interstellar space case)
272 return NO;
273 }
274 double now = [PLAYER clockTimeAdjusted];
275
276/* CIM: removed test. Not valid for wormholes which last longer than their travel time. Most likely for short distances e.g. zero-distance doubles. equal_seeds test above should cover it, with expiry_time test for safety. */
277/* if (now > arrival_time)
278 return NO; // far end of the wormhole! */
279 if( now > expiry_time )
280 return NO;
281 // MKW 2010.11.18 - calculate time it takes for ship to reach wormhole
282 // This is for AI ships which get told to enter the wormhole even though they
283 // may still be some distance from it when the player exits the system
284 float d = HPdistance(position, [ship position]);
285 d -= [ship collisionRadius] + [self collisionRadius];
286 if (d > 0.0f)
287 {
288 float afterburnerFactor = [ship hasFuelInjection] && [ship fuel] > MIN_FUEL ? [ship afterburnerFactor] : 1.0;
289 float shipSpeed = [ship maxFlightSpeed] * afterburnerFactor;
290 // MKW 2011.02.27 - calculate speed based on group leader, if any, to
291 // try and prevent escorts from entering the wormhole before their mother.
292 ShipEntity *leader = [[ship group] leader];
293 if (leader && (leader != ship))
294 {
295 afterburnerFactor = [leader hasFuelInjection] && [leader fuel] > MIN_FUEL ? [leader afterburnerFactor] : 1.0;
296 float leaderShipSpeed = [leader maxFlightSpeed] * afterburnerFactor;
297 if (leaderShipSpeed < shipSpeed ) shipSpeed = leaderShipSpeed;
298 }
299 if (shipSpeed <= 0.0f ) shipSpeed = 0.1f;
300 now += d / shipSpeed;
301 if( now > expiry_time )
302 {
303 return NO;
304 }
305 }
306
307 [shipsInTransit addObject:[NSDictionary dictionaryWithObjectsAndKeys:
308 ship, @"ship",
309 [NSNumber numberWithDouble: now + travel_time - arrival_time], @"time",
310 [ship beaconCode], @"shipBeacon", // in case a beacon code has been set, nil otherwise
311 nil]];
312 witch_mass += [ship mass];
313 expiry_time = now + (witch_mass / WORMHOLE_SHRINK_RATE / shrink_factor);
314 // and, again, cap to be earlier than arrival time
315 if (expiry_time > arrival_time)
316 {
317 expiry_time = arrival_time - 1.0;
318 }
319
320 collision_radius = 0.5 * M_PI * pow(witch_mass, 1.0/3.0);
321
322 [UNIVERSE addWitchspaceJumpEffectForShip:ship];
323
324 // Should probably pass the wormhole, but they have no JS representation
325 [ship setStatus:STATUS_ENTERING_WITCHSPACE];
326 [ship doScriptEvent:OOJSID("shipWillEnterWormhole")];
327 [[ship getAI] message:@"ENTERED_WITCHSPACE"];
328
329 [UNIVERSE removeEntity:ship];
330 [[ship getAI] clearStack]; // get rid of any preserved states
331
332 if ([ship isStation])
333 {
334 if ([PLAYER dockedStation] == (StationEntity*)ship)
335 {
336 // the carrier has jumped while the player is docked
337 [ship retain];
338 [UNIVERSE carryPlayerOn:(StationEntity*)ship inWormhole:self];
339 [ship release];
340 }
341 }
342
343 return YES;
344}
345
346
347- (void) disgorgeShips
348{
349 double now = [PLAYER clockTimeAdjusted];
350 NSMutableArray* shipsStillInTransit = [[NSMutableArray alloc] initWithCapacity:[shipsInTransit count]];
351 BOOL hasShiftedExitPosition = NO;
352 BOOL useExitXYScatter = NO;
353
354 NSDictionary *shipInfo = nil;
355 foreach (shipInfo, shipsInTransit)
356 {
357 ShipEntity *ship = [shipInfo objectForKey:@"ship"];
358 NSString *shipBeacon = [shipInfo objectForKey:@"shipBeacon"];
359 double ship_arrival_time = arrival_time + [shipInfo oo_doubleForKey:@"time"];
360 double time_passed = now - ship_arrival_time;
361
362 if ([ship status] == STATUS_DEAD) continue; // skip dead ships.
363
364 if (ship_arrival_time > now)
365 {
366 [shipsStillInTransit addObject:shipInfo];
367 }
368 else
369 {
370 // Only calculate exit position once so that all ships arrive from the same point
371 if (!hasExitPosition)
372 {
373 position = [UNIVERSE getWitchspaceExitPosition]; // no need to reset PRNG.
374 GLfloat min_d1 = [UNIVERSE safeWitchspaceExitDistance];
375 Quaternion q1;
377 double d1 = SCANNER_MAX_RANGE*((ranrot_rand() % 256)/256.0 - 0.5);
378 Vector v1 = vector_forward_from_quaternion(q1);
379 if (dot_product(v1,kBasisZVector) < -0.99)
380 {
381 // a bit more safe distance if right behind the buoy
382 min_d1 *= 3.0;
383 }
384
385 if (fabs(d1) < min_d1) // no closer than 750m to edge of buoy
386 {
387 d1 += ((d1 > 0.0)? min_d1: -min_d1);
388 }
389 position.x += v1.x * d1; // randomise exit position
390 position.y += v1.y * d1;
391 position.z += v1.z * d1;
392 }
393
394 if (hasExitPosition && (!containsPlayer || useExitXYScatter))
395 {
396 HPVector shippos;
397 Vector exit_vector_x = vector_right_from_quaternion([UNIVERSE getWitchspaceExitRotation]);
398 Vector exit_vector_y = vector_up_from_quaternion([UNIVERSE getWitchspaceExitRotation]);
399// entry wormhole has a radius of around 100m (or perhaps more)
400// so randomise exit positions slightly too for second and subsequent ships
401// helps avoid collisions when two ships enter wormhole at same time
402 double offset_x = randf()*150.0-75.0;
403 double offset_y = randf()*150.0-75.0;
404 shippos.x = position.x + (offset_x*exit_vector_x.x)+(offset_y*exit_vector_y.x);
405 shippos.y = position.y + (offset_x*exit_vector_x.y)+(offset_y*exit_vector_y.y);
406 shippos.z = position.z + (offset_x*exit_vector_x.z)+(offset_y*exit_vector_y.z);
407 [ship setPosition:shippos];
408 }
409 else
410 {
411 // this is the first ship out of the wormhole
412 [self setExitSpeed:[ship maxFlightSpeed]*WORMHOLE_LEADER_SPEED_FACTOR];
413 if (containsPlayer)
414 { // reset the player's speed to the new speed
415 [PLAYER setSpeed:exit_speed];
416 }
417 useExitXYScatter = YES;
418 [ship setPosition:position];
419 }
420
421 if (shipBeacon != nil)
422 {
423 [ship setBeaconCode:shipBeacon];
424 }
425
426 // Don't reduce bounty on misjump. Fixes #17992
427 // - MKW 2011.03.10
428 if (!_misjump) [ship setBounty:[ship bounty]/2 withReason:kOOLegalStatusReasonNewSystem]; // adjust legal status for new system
429
430 // now the cargo is defined in advance, this is unnecessary
431/* if ([ship cargoFlag] == CARGO_FLAG_FULL_PLENTIFUL)
432 {
433 [ship setCargoFlag: CARGO_FLAG_FULL_SCARCE];
434 }*/
435
436 if (time_passed < 2.0)
437 {
438 [ship witchspaceLeavingEffects]; // adds the ship to the universe with effects.
439 }
440 else
441 {
442 // arrived 2 seconds or more before the player. Rings have faded out.
443 [ship setOrientation: [UNIVERSE getWitchspaceExitRotation]];
444 [ship setPitch: 0.0];
445 [ship setRoll: 0.0];
446 [ship setVelocity: kZeroVector];
447 [UNIVERSE addEntity:ship]; // AI and status get initialised here
448 }
449 [ship setSpeed:[self exitSpeed]]; // all ships from this wormhole have same velocity
450
451 // awaken JS-based AIs
452 [ship doScriptEvent:OOJSID("aiStarted")];
453
454 // Wormholes now have a JS representation, so we could provide it
455 // but is it worth it for the exit wormhole?
456 [ship doScriptEvent:OOJSID("shipExitedWormhole") andReactToAIMessage:@"EXITED WITCHSPACE"];
457
458 // update the ships's position
459 if (!hasExitPosition)
460 {
461 hasExitPosition = YES;
462 hasShiftedExitPosition = YES; // exitPosition is shifted towards the lead ship update position.
463 [ship update: time_passed]; // do this only for one ship or the next ships might appear at very different locations.
464 position = [ship position]; // e.g. when the player docks first before following, time_passed is already > 10 minutes.
465 }
466 else if (time_passed > 1) // Only update the ship position if it was some time ago, otherwise we're in 'real time'.
467 {
468 if (hasShiftedExitPosition)
469 {
470 // only update the time delay to the lead ship. Sign is not correct but updating gives a small spacial distribution.
471 [ship update: (ship_arrival_time - arrival_time)];
472 }
473 else
474 {
475 // Exit position was externally set, e.g. by player ship following through this wormhole.
476 // Use the real time difference.
477 [ship update:time_passed];
478 }
479 }
480 }
481 }
482 [shipsInTransit release];
483 shipsInTransit = shipsStillInTransit;
484
485 if (containsPlayer)
486 {
487 // ships exiting the wormhole after now are following the player
488 // so appear behind them
489 position = HPvector_add([PLAYER position], vectorToHPVector(vector_multiply_scalar([PLAYER forwardVector], -500.0f)));
490 containsPlayer = NO;
491 }
492// else, the wormhole doesn't now (or never) contained the player, so
493// no need to move it
494}
495
496
497- (void) setContainsPlayer:(BOOL)val
498{
499 containsPlayer = val;
500}
501
502
503- (void) setExitPosition:(HPVector)pos
504{
505 [self setPosition: pos];
506 hasExitPosition = YES;
507}
508
510{
511 return origin;
512}
513
514- (OOSystemID) destination
515{
516 return destination;
517}
518
519- (NSPoint) originCoordinates
520{
521 return originCoords;
522}
523
524- (NSPoint) destinationCoordinates
525{
526 return destinationCoords;
527}
528
529- (double) exitSpeed
530{
531 return exit_speed;
532}
533
534
535- (void) setExitSpeed:(double) speed
536{
537 exit_speed = speed;
538}
539
540
541- (double) expiryTime
542{
543 return expiry_time;
544}
545
546- (double) arrivalTime
547{
548 return arrival_time;
549}
550
551- (double) estimatedArrivalTime
552{
553 return estimated_arrival_time;
554}
555
556- (double) travelTime
557{
558 return travel_time;
559}
560
561- (double) scanTime
562{
563 return scan_time;
564}
565
566- (BOOL) isScanned
567{
568 return scan_info > WH_SCANINFO_NONE;
569}
570
571- (void) setScannedAt:(double)p_scanTime
572{
573 if( scan_info == WH_SCANINFO_NONE )
574 {
575 scan_time = p_scanTime;
576 scan_info = WH_SCANINFO_SCANNED;
577 }
578 // else we previously scanned this wormhole
579}
580
581- (WORMHOLE_SCANINFO) scanInfo
582{
583 return scan_info;
584}
585
586- (void) setScanInfo:(WORMHOLE_SCANINFO)p_scanInfo
587{
588 scan_info = p_scanInfo;
589}
590
591- (NSArray*) shipsInTransit
592{
593 return shipsInTransit;
594}
595
596- (void) dealloc
597{
598 [shipsInTransit release];
599
600 [super dealloc];
601}
602
603
604- (NSString *) descriptionComponents
605{
606 double now = [PLAYER clockTime];
607 return [NSString stringWithFormat:@"destination: %@ ttl: %.2fs arrival: %@",
608 _misjump ? (NSString *)@"Interstellar Space" : [UNIVERSE getSystemName:destination],
609 expiry_time - now,
610 ClockToString(arrival_time, false)];
611}
612
613
614- (NSString *) identFromShip:(ShipEntity*)ship
615{
616 if ([ship hasEquipmentItem:@"EQ_WORMHOLE_SCANNER"])
617 {
618 if ([self scanInfo] >= WH_SCANINFO_DESTINATION)
619 {
620 return [NSString stringWithFormat:DESC(@"wormhole-to-@"), [UNIVERSE getSystemName:destination]];
621 }
622 else
623 {
624 return DESC(@"wormhole-desc");
625 }
626 }
627 else
628 {
629 OOLogERR(kOOLogInconsistentState, @"%@", @"Wormhole identified when ship has no EQ_WORMHOLE_SCANNER.");
630 /*
631 This was previously an assertion, but a player reported hitting it.
632 http://aegidian.org/bb/viewtopic.php?p=128110#p128110
633 -- Ahruman 2011-01-27
634 */
635 return nil;
636 }
637
638}
639
640
641- (BOOL) canCollide
642{
643 /* Correct test for far end of wormhole */
644 if (origin != [UNIVERSE currentSystemID])
645 {
646 // if we're no longer in the origin system, can't suck in
647 return NO;
648 }
649 if ([PLAYER galaxy_coordinates].x != originCoords.x || [PLAYER galaxy_coordinates].y != originCoords.y)
650 {
651 // if we're no longer at the origin coordinates, can't suck in (handles interstellar space case)
652 return NO;
653 }
654
655 return (witch_mass > 0.0);
656}
657
658
659- (BOOL) checkCloseCollisionWith:(Entity *)other
660{
661 return ![other isEffect];
662}
663
664
665- (void) update:(OOTimeDelta) delta_t
666{
667 [super update:delta_t];
668
669 PlayerEntity *player = PLAYER;
670 assert(player != nil);
671 rotMatrix = OOMatrixForBillboard(position, [player viewpointPosition]);
672 double now = [player clockTimeAdjusted];
673
674 if (witch_mass > 0.0)
675 {
676
677 witch_mass -= WORMHOLE_SHRINK_RATE * delta_t * shrink_factor;
678 witch_mass = fmax(witch_mass, 0.0);
679 collision_radius = 0.5 * M_PI * pow(witch_mass, 1.0/3.0);
680 no_draw_distance = collision_radius * collision_radius * NO_DRAW_DISTANCE_FACTOR * NO_DRAW_DISTANCE_FACTOR;
681 }
682
683 scanClass = (witch_mass > 0.0)? CLASS_WORMHOLE : CLASS_NO_DRAW;
684
685 if (now > expiry_time)
686 {
687 scanClass = CLASS_NO_DRAW; // witch_mass not certain to be limiting factor on extremely short jumps, so make sure now
688
689 // If we're a saved wormhole waiting to disgorge more ships, it's safe
690 // to remove self from UNIVERSE, but we need the current position!
691 [UNIVERSE removeEntity: self];
692 }
693}
694
695
696- (void) drawImmediate:(bool)immediate translucent:(bool)translucent
697{
698 if ([UNIVERSE breakPatternHide])
699 return; // DON'T DRAW DURING BREAK PATTERN
700
701 if (cam_zero_distance > no_draw_distance)
702 return; // TOO FAR AWAY TO SEE
703
704 if (witch_mass <= 0.0)
705 return;
706
707 if (collision_radius <= 0.0)
708 return;
709
710 if ([self scanClass] == CLASS_NO_DRAW)
711 return;
712
713 if (translucent)
714 {
715 // for now, a simple copy of the energy bomb draw routine
716 float srzd = sqrt(cam_zero_distance);
717
718 GLfloat color_fv[4] = OO_WORMHOLE_COLOR_FVEC4;
719
721 OOGL(glDisable(GL_CULL_FACE));
722 OOGL(glEnable(GL_BLEND));
723
724 OOGL(glColor4fv(color_fv));
725 OOGLBEGIN(GL_TRIANGLE_FAN);
726 GLDrawBallBillboard(0.45 * collision_radius, 4, srzd);
727 OOGLEND();
728
729 color_fv[3] = fmin(color_fv[3] * 2.0, 1.0);
730 DrawWormholeCorona(0.45 * collision_radius, collision_radius, 4, srzd, color_fv);
731
732 OOGL(glEnable(GL_CULL_FACE));
733 OOGL(glDisable(GL_BLEND));
734 }
735
737 OOCheckOpenGLErrors(@"WormholeEntity after drawing %@", self);
738}
739
740
741static void DrawWormholeCorona(GLfloat inner_radius, GLfloat outer_radius, int step, GLfloat z_distance, GLfloat *col4v1)
742{
743 if (outer_radius >= z_distance) // inside the sphere
744 return;
745 int i;
746
747 NSRange activity = { 0.34, 1.0 };
748
749 GLfloat s0, c0, s1, c1;
750
751 GLfloat r0, r1;
752 GLfloat rv0, rv1, q;
753
754 GLfloat theta, delta, halfStep;
755
756 r0 = outer_radius * z_distance / sqrt(z_distance * z_distance - outer_radius * outer_radius);
757 r1 = inner_radius * z_distance / sqrt(z_distance * z_distance - inner_radius * inner_radius);
758
759 delta = step * M_PI / 180.0f;
760 halfStep = 0.5f * delta;
761 theta = 0.0f;
762
763 OOGLBEGIN(GL_TRIANGLE_STRIP);
764 for (i = 0; i < 360; i += step )
765 {
766 rv0 = randf();
767 rv1 = randf();
768
769 q = activity.location + rv0 * activity.length;
770
771 s0 = r0 * sin(theta);
772 c0 = r0 * cos(theta);
773 glColor4f(col4v1[0] * q, col4v1[1] * q, col4v1[2] * q, col4v1[3] * rv0);
774 glVertex3f(s0, c0, 0.0);
775
776 s1 = r1 * sin(theta - halfStep) * 0.5 * (1.0 + rv1);
777 c1 = r1 * cos(theta - halfStep) * 0.5 * (1.0 + rv1);
778 glColor4f(col4v1[0] * OO_WORMHOLE_COLOR_BOOST, col4v1[1] * OO_WORMHOLE_COLOR_BOOST, col4v1[2] * OO_WORMHOLE_COLOR_BOOST, col4v1[3] * rv0);
779 glVertex3f(s1, c1, 0.0);
780
781 theta += delta;
782 }
783 // repeat last values to close
784 rv0 = randf();
785 rv1 = randf();
786
787 q = activity.location + rv0 * activity.length;
788
789 s0 = 0.0f; // r0 * sin(0);
790 c0 = r0; // r0 * cos(0);
791 glColor4f(col4v1[0] * q, col4v1[1] * q, col4v1[2] * q, col4v1[3] * rv0);
792 glVertex3f(s0, c0, 0.0);
793
794 s1 = r1 * sin(halfStep) * 0.5 * (1.0 + rv1);
795 c1 = r1 * cos(halfStep) * 0.5 * (1.0 + rv1);
796 glColor4f(col4v1[0] * OO_WORMHOLE_COLOR_BOOST, col4v1[1] * OO_WORMHOLE_COLOR_BOOST, col4v1[2] * OO_WORMHOLE_COLOR_BOOST, col4v1[3] * rv0);
797 glVertex3f(s1, c1, 0.0);
798 OOGLEND();
799}
800
801- (NSDictionary *) getDict
802{
803 NSMutableDictionary *myDict = [NSMutableDictionary dictionary];
804
805 [myDict oo_setInteger:origin forKey:@"origin_id"];
806 [myDict oo_setInteger:destination forKey:@"dest_id"];
807 [myDict setObject:StringFromPoint(originCoords) forKey:@"origin_coords"];
808 [myDict setObject:StringFromPoint(destinationCoords) forKey:@"dest_coords"];
809 // Anything converting a wormhole to a dictionary should already have
810 // modified its time to shipClock time
811 [myDict oo_setFloat:(expiry_time) forKey:@"expiry_time"];
812 [myDict oo_setFloat:(arrival_time) forKey:@"arrival_time"];
813 [myDict oo_setFloat:(estimated_arrival_time) forKey:@"estimated_arrival_time"];
814 [myDict oo_setHPVector:position forKey:@"position"];
815 [myDict oo_setBool:_misjump forKey:@"misjump"];
816
817 NSMutableArray * shipArray = [NSMutableArray arrayWithCapacity:[shipsInTransit count]];
818 NSEnumerator * ships = [shipsInTransit objectEnumerator];
819 NSDictionary * currShipDict = nil;
820 NSMutableDictionary *context = [NSMutableDictionary dictionary];
821 while ((currShipDict = [ships nextObject]) != nil)
822 {
823 id ship = [currShipDict objectForKey:@"ship"];
824 [shipArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:
825 [NSNumber numberWithDouble:[currShipDict oo_doubleForKey:@"time"]], @"time_delta",
826 [ship savedShipDictionaryWithContext:context], @"ship_info",
827 nil]];
828 }
829 [myDict setObject:shipArray forKey:@"ships"];
830
831 return myDict;
832}
833
834- (NSString *) scanInfoString
835{
836 switch(scan_info)
837 {
838 case WH_SCANINFO_NONE: return @"WH_SCANINFO_NONE";
839 case WH_SCANINFO_SCANNED: return @"WH_SCANINFO_SCANNED";
840 case WH_SCANINFO_COLLAPSE_TIME: return @"WH_SCANINFO_COLLAPSE_TIME";
841 case WH_SCANINFO_ARRIVAL_TIME: return @"WH_SCANINFO_ARRIVAL_TIME";
842 case WH_SCANINFO_DESTINATION: return @"WH_SCANINFO_DESTINATION";
843 case WH_SCANINFO_SHIP: return @"WH_SCANINFO_SHIP";
844 }
845 return @"WH_SCANINFO_UNDEFINED"; // should never get here
846}
847
848- (void)dumpSelfState
849{
850 [super dumpSelfState];
851 OOLog(@"dumpState.wormholeEntity", @"Origin : %@", [UNIVERSE getSystemName:origin]);
852 OOLog(@"dumpState.wormholeEntity", @"Destination : %@", [UNIVERSE getSystemName:destination]);
853 OOLog(@"dumpState.wormholeEntity", @"Expiry Time : %@", ClockToString(expiry_time, false));
854 OOLog(@"dumpState.wormholeEntity", @"Arrival Time : %@", ClockToString(arrival_time, false));
855 OOLog(@"dumpState.wormholeEntity", @"Projected Arrival Time : %@", ClockToString(estimated_arrival_time, false));
856 OOLog(@"dumpState.wormholeEntity", @"Scanned Time : %@", ClockToString(scan_time, false));
857 OOLog(@"dumpState.wormholeEntity", @"Scanned State : %@", [self scanInfoString]);
858
859 OOLog(@"dumpState.wormholeEntity", @"Mass : %.2lf", witch_mass);
860 OOLog(@"dumpState.wormholeEntity", @"Ships : %ld", [shipsInTransit count]);
861 unsigned i;
862 for (i = 0; i < [shipsInTransit count]; ++i)
863 {
864 NSDictionary *shipDict = [shipsInTransit oo_dictionaryAtIndex:i];
865 ShipEntity* ship = (ShipEntity*)[shipDict objectForKey:@"ship"];
866 double ship_arrival_time = arrival_time + [shipDict oo_doubleForKey:@"time"];
867 OOLog(@"dumpState.wormholeEntity.ships", @"Ship %d: %@ mass %.2f arrival time %@", i+1, ship, [ship mass], ClockToString(ship_arrival_time, false));
868 }
869}
870
871@end
#define NO_DRAW_DISTANCE_FACTOR
Definition Entity.h:46
#define SCANNER_MAX_RANGE
Definition Entity.h:51
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
NSString *const kOOLogInconsistentState
Definition OOLogging.m:650
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define M_PI
Definition OOMaths.h:73
OOMatrix OOMatrixForBillboard(HPVector bbPos, HPVector eyePos)
Definition OOMatrix.m:191
#define OOGLBEGIN
Definition OOOpenGL.h:253
@ OPENGL_STATE_TRANSLUCENT_PASS
Definition OOOpenGL.h:124
#define OOVerifyOpenGLState()
Definition OOOpenGL.h:136
BOOL OOCheckOpenGLErrors(NSString *format,...)
Definition OOOpenGL.m:39
void GLDrawBallBillboard(GLfloat radius, GLfloat step, GLfloat z_distance)
Definition OOOpenGL.m:111
#define OOSetOpenGLState(STATE)
Definition OOOpenGL.h:135
#define OOGL(statement)
Definition OOOpenGL.h:251
#define OOGLEND
Definition OOOpenGL.h:254
return self
unsigned count
return nil
Vector vector_up_from_quaternion(Quaternion quat)
Vector vector_right_from_quaternion(Quaternion quat)
Vector vector_forward_from_quaternion(Quaternion quat)
void quaternion_set_random(Quaternion *quat)
float y
float x
NSString * ClockToString(double clock, BOOL adjusting)
int16_t OOSystemID
Definition OOTypes.h:211
double OOTimeDelta
Definition OOTypes.h:224
const Vector kBasisZVector
Definition OOVector.m:31
#define PLAYER
#define MIN_FUEL
Definition ShipEntity.h:102
#define UNIVERSE
Definition Universe.h:833
#define DESC(key)
Definition Universe.h:839
#define WORMHOLE_SHRINK_RATE
WORMHOLE_SCANINFO
@ WH_SCANINFO_NONE
@ WH_SCANINFO_SCANNED
@ WH_SCANINFO_SHIP
@ WH_SCANINFO_ARRIVAL_TIME
@ WH_SCANINFO_DESTINATION
@ WH_SCANINFO_COLLAPSE_TIME
#define OO_WORMHOLE_COLOR_FVEC4
#define OO_WORMHOLE_COLOR_BOOST
static void DrawWormholeCorona(GLfloat inner_radius, GLfloat outer_radius, int step, GLfloat z_distance, GLfloat *col4v1)
void clearStack()
Definition AI.m:685
void message:(NSString *ms)
Definition AI.m:600
void setVelocity:(Vector vel)
Definition Entity.m:757
void setOrientation:(Quaternion quat)
Definition Entity.m:725
BOOL isEffect()
Definition Entity.m:196
HPVector position
Definition Entity.h:112
void setStatus:(OOEntityStatus stat)
Definition Entity.m:787
GLfloat mass
Definition Entity.h:146
void setPosition:(HPVector posn)
Definition Entity.m:647
ShipEntity * leader()
double clockTimeAdjusted()
void setStatus:(OOEntityStatus stat)
BOOL witchspaceLeavingEffects()
void setRoll:(double amount)
BOOL hasFuelInjection()
void setSpeed:(double amount)
NSString * beaconCode()
OOShipGroup * group()
void doScriptEvent:(jsid message)
void doScriptEvent:andReactToAIMessage:(jsid scriptEvent,[andReactToAIMessage] NSString *aiMessage)
OOFuelQuantity fuel
Definition ShipEntity.h:288
void setPitch:(double amount)
void setBeaconCode:(NSString *bcode)
void update:(OOTimeDelta delta_t)
float afterburnerFactor()
id shipRestoredFromDictionary:useFallback:context:(NSDictionary *dictionary,[useFallback] BOOL fallback,[context] NSMutableDictionary *context)
GLfloat maxFlightSpeed
Definition ShipEntity.h:239
static void DrawWormholeCorona(GLfloat inner_radius, GLfloat outer_radius, int step, GLfloat z_distance, GLfloat *col4v1)
voidpf uLong int origin
Definition ioapi.h:140
float randf(void)
#define ranrot_rand()
OOINLINE double distanceBetweenPlanetPositions(int x1, int y1, int x2, int y2) INLINE_CONST_FUNC