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