LCOV - code coverage report
Current view: top level - Core/Entities - WormholeEntity.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 14 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : WormholeEntity.m
       4             : 
       5             : Oolite
       6             : Copyright (C) 2004-2013 Giles C Williams and contributors
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 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"
      31             : #import "ShipEntityLoadRestore.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"
      39             : #import "OOCollectionExtractors.h"
      40             : #import "OOLoggingExtended.h"
      41             : #import "OOSystemDescriptionManager.h"
      42             : 
      43           0 : #define OO_WORMHOLE_COLOR_BOOST 25.0
      44           0 : #define OO_WORMHOLE_COLOR_FVEC4 { 0.067, 0.067, 1.0, 0.25 }
      45             : 
      46             : // Hidden interface
      47             : @interface WormholeEntity (Private)
      48             : 
      49           0 : -(id) init;
      50             : 
      51             : @end
      52             : 
      53             : // Static local functions
      54           0 : static 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             :                         {
     135             :                                 ShipEntity *ship = [ShipEntity shipRestoredFromDictionary:shipInfo
     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;
     376             :                                 quaternion_set_random(&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             : 
     509             : - (OOSystemID) origin
     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           0 : - (void) dealloc
     597             : {
     598             :         [shipsInTransit release];
     599             :         
     600             :         [super dealloc];
     601             : }
     602             : 
     603             : 
     604           0 : - (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           0 : - (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           0 : - (BOOL) checkCloseCollisionWith:(Entity *)other
     660             : {
     661             :         return ![other isEffect];
     662             : }
     663             : 
     664             : 
     665           0 : - (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           0 : - (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             :                 
     720             :                 OOSetOpenGLState(OPENGL_STATE_TRANSLUCENT_PASS);
     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             :         
     736             :         OOVerifyOpenGLState();
     737             :         OOCheckOpenGLErrors(@"WormholeEntity after drawing %@", self);
     738             : }
     739             : 
     740             : 
     741           0 : static 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           0 : - (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           0 : - (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

Generated by: LCOV version 1.14