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

          Line data    Source code
       1           0 : /*
       2             : 
       3             : DockEntity.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 "DockEntity.h"
      26             : #import "StationEntity.h"
      27             : #import "ShipEntityAI.h"
      28             : #import "OOCollectionExtractors.h"
      29             : #import "OOStringParsing.h"
      30             : #import "OOStringExpander.h"
      31             : 
      32             : #import "Universe.h"
      33             : #import "HeadUpDisplay.h"
      34             : 
      35             : #import "PlayerEntityLegacyScriptEngine.h"
      36             : #import "OOLegacyScriptWhitelist.h"
      37             : #import "OOPlanetEntity.h"
      38             : #import "OOShipGroup.h"
      39             : #import "OOQuiriumCascadeEntity.h"
      40             : 
      41             : #import "AI.h"
      42             : #import "OOCharacter.h"
      43             : 
      44             : #import "OOJSScript.h"
      45             : #import "OODebugGLDrawing.h"
      46             : #import "OODebugFlags.h"
      47             : 
      48             : 
      49             : @interface DockEntity (OOPrivate)
      50             : 
      51           0 : - (void) clearIdLocks:(ShipEntity *)ship;
      52           0 : - (void) clearAllIdLocks;
      53           0 : - (void) autoDockShipsInQueue:(NSMutableDictionary *)queue;
      54           0 : - (void) addShipToShipsOnApproach:(ShipEntity *)ship;
      55           0 : - (void) pullInShipIfPermitted:(ShipEntity *)ship;
      56             : 
      57             : @end
      58             : 
      59             : 
      60             : @implementation DockEntity
      61             : 
      62             : - (NSUInteger) pruneAndCountShipsOnApproach
      63             : {
      64             :         // Remove dead entities.
      65             :         // Enumerate over allKeys explicitly because we mutate the dictionary.
      66             :         NSNumber *idObj = nil;
      67             :         foreach (idObj, [shipsOnApproach allKeys])
      68             :         {
      69             :                 ShipEntity *ship = [UNIVERSE entityForUniversalID:[idObj unsignedIntValue]];
      70             :                 /* Remove ships from the approach queue if they are dead, or
      71             :                  * are more than 25.6km from the dock.
      72             :                  */
      73             :                 if (ship == nil || HPmagnitude2(HPvector_subtract([ship position],[self absolutePositionForSubentity])) > SCANNER_MAX_RANGE2)
      74             :                 {
      75             :                         [shipsOnApproach removeObjectForKey:idObj];
      76             :                         if (ship != nil) {
      77             :                                 // notify ship if it's alive
      78             :                                 [ship sendAIMessage:@"DOCKING_ABORTED"];
      79             :                                 [ship doScriptEvent:OOJSID("stationWithdrewDockingClearance")];
      80             :                         }
      81             :                 }
      82             :         }
      83             :         
      84             :         if ([shipsOnApproach count] == 0)
      85             :         {
      86             :                 if (last_launch_time < [UNIVERSE getTime])
      87             :                 {
      88             :                         last_launch_time = [UNIVERSE getTime];
      89             :                 }
      90             :         }
      91             :         
      92             :         return [shipsOnApproach count];
      93             : }
      94             : 
      95             : 
      96             : - (void) abortAllDockings
      97             : {
      98             :         double          playerExtraTime = 0;
      99             :         
     100             :         no_docking_while_launching = YES;
     101             :         
     102             :         NSNumber *idObj = nil;
     103             :         foreach (idObj, [shipsOnApproach allKeys])
     104             :         {
     105             :                 ShipEntity *ship = [UNIVERSE entityForUniversalID:[idObj unsignedIntValue]];
     106             :                 if ([ship isShip])
     107             :                 {
     108             :                         [ship sendAIMessage:@"DOCKING_ABORTED"];
     109             :                         [ship doScriptEvent:OOJSID("stationWithdrewDockingClearance")];
     110             :                 }
     111             :         }
     112             :         [shipsOnApproach removeAllObjects];
     113             :         
     114             :         PlayerEntity *player = PLAYER;
     115             :         StationEntity *station = (StationEntity*)[self parentEntity];
     116             :         BOOL isDockingStation = (station == [player getTargetDockStation]) && ([station playerReservedDock] == self);
     117             :         if (isDockingStation && [player status] == STATUS_IN_FLIGHT &&
     118             :                         [player getDockingClearanceStatus] >= DOCKING_CLEARANCE_STATUS_REQUESTED)
     119             :         {
     120             :                 if (HPmagnitude2(HPvector_subtract([player position], [self absolutePositionForSubentity])) > 2250000) // within 1500m of the dock
     121             :                 {
     122             :                         [station sendExpandedMessage:@"[station-docking-clearance-abort-cancelled]" toShip:player];
     123             :                         [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
     124             :                         [player doScriptEvent:OOJSID("stationWithdrewDockingClearance")];
     125             :                 }
     126             :                 else
     127             :                 {
     128             :                         playerExtraTime = 10; // when very close to the port, give the player a few seconds to react on the abort message.
     129             :                         int seconds = round(playerExtraTime);
     130             :                         [station sendExpandedMessage:OOExpandKey(@"station-docking-clearance-abort-cancelled-in-time", seconds) toShip:player];
     131             :                         [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_TIMING_OUT];
     132             :                 }
     133             : 
     134             :         }
     135             :         
     136             :         // mark docking queue flight pattern as clear
     137             :         [self clearAllIdLocks];
     138             : 
     139             :         last_launch_time = [UNIVERSE getTime] + playerExtraTime;
     140             : }
     141             : 
     142             : 
     143           0 : - (void) abortAllLaunches
     144             : {
     145             :         no_docking_while_launching = NO;
     146             :         [launchQueue removeAllObjects];
     147             : }
     148             : 
     149             : 
     150           0 : - (void) autoDockShipsInQueue:(NSMutableDictionary *)queue
     151             : {       
     152             :         NSNumber *idObj = nil;
     153             :         foreach (idObj, [queue allKeys])
     154             :         {
     155             :                 ShipEntity *ship = [UNIVERSE entityForUniversalID:[idObj unsignedIntValue]];
     156             :                 if ([ship isShip])
     157             :                 {
     158             :                         [self pullInShipIfPermitted:ship];
     159             :                 }
     160             :         }
     161             :         
     162             :         [queue removeAllObjects];
     163             : }
     164             : 
     165             : 
     166             : - (void) autoDockShipsOnApproach
     167             : {
     168             :         [self autoDockShipsInQueue:shipsOnApproach];
     169             : }
     170             : 
     171             : 
     172             : - (BOOL) allowsDocking
     173             : {
     174             :         return allow_docking;
     175             : }
     176             : 
     177             : 
     178             : - (void) setAllowsDocking:(BOOL)allowed
     179             : {
     180             :         if (!allowed && allow_docking) 
     181             :         {
     182             :                 [self abortAllDockings];
     183             :         }
     184             :         allow_docking = allowed;
     185             : }
     186             : 
     187             : 
     188             : - (BOOL) disallowedDockingCollides
     189             : {
     190             :         return disallowed_docking_collides;
     191             : }
     192             : 
     193             : 
     194             : - (BOOL) allowsLaunching
     195             : {
     196             :         return allow_launching;
     197             : }
     198             : 
     199             : 
     200             : - (void) setAllowsLaunching:(BOOL)allowed
     201             : {
     202             :         if (!allowed && allow_launching) 
     203             :         {
     204             :                 [self abortAllLaunches];
     205             :         }
     206             :         allow_launching = allowed;
     207             : }
     208             : 
     209             : 
     210             : - (void) setDisallowedDockingCollides:(BOOL)ddc
     211             : {
     212             :         disallowed_docking_collides = ddc;
     213             : }
     214             : 
     215             : 
     216             : - (void) setVirtual
     217             : {
     218             :         virtual_dock = YES;
     219             : }
     220             : 
     221             : 
     222             : - (NSString*) canAcceptShipForDocking:(ShipEntity *) ship
     223             : {
     224             :         // First test permanent rejection reasons
     225             :         if (!allow_docking)
     226             :         {
     227             :                 return @"DOCK_CLOSED"; // could be temp or perm reject
     228             :         }
     229             :         BoundingBox bb = [ship totalBoundingBox];
     230             :         if ((port_dimensions.x < (bb.max.x - bb.min.x) || port_dimensions.y < (bb.max.y - bb.min.y)) && 
     231             :                 (port_dimensions.y < (bb.max.x - bb.min.x) || port_dimensions.x < (bb.max.y - bb.min.y)))
     232             :         {
     233             :                 return @"TOO_BIG_TO_DOCK";
     234             :         }
     235             : 
     236             :         // callback to allow more complex filtering on accept/reject
     237             :         JSContext       *context = OOJSAcquireContext();
     238             :         jsval           rval = JSVAL_VOID;
     239             :         jsval           args[] = { OOJSValueFromNativeObject(context, ship) };
     240             :         JSBool accept = YES;
     241             :         
     242             :         BOOL OK = [[self script] callMethod:OOJSID("acceptDockingRequestFrom") inContext:context withArguments:args count:1 result:&rval];
     243             :         if (OK)  OK = JS_ValueToBoolean(context, rval, &accept);
     244             :         if (!OK)  accept = YES; // default to permreject
     245             :         OOJSRelinquishContext(context);
     246             : 
     247             :         if (!accept)
     248             :         {
     249             :                 return @"TOO_BIG_TO_DOCK";
     250             :         }
     251             : 
     252             :         // Second test temporary rejection reasons
     253             :         if (no_docking_while_launching)
     254             :         {
     255             :                 return @"TRY_AGAIN_LATER";
     256             :         }
     257             :         // if there are pending launches, temporarily don't accept docking requests
     258             :         if (allow_launching && [launchQueue count])
     259             :         {
     260             :                 return @"TRY_AGAIN_LATER";
     261             :         }
     262             :         
     263             :         return @"DOCKING_POSSIBLE";
     264             : }
     265             : 
     266             : 
     267             : - (BOOL) isOffCentre
     268             : {
     269             :         if (fabs(position.x) + fabs(position.y) > 5.0)
     270             :         {
     271             :                 return YES;
     272             :         }
     273             :         Vector dir = vector_forward_from_quaternion(orientation);
     274             :         if (fabs(dir.x) + fabs(dir.y) > 0.1)
     275             :         {
     276             :                 return YES;
     277             :         }
     278             :         return NO;
     279             : }
     280             : 
     281             : 
     282             : - (NSDictionary *) dockingInstructionsForShip:(ShipEntity *)ship
     283             : {       
     284             :         if (ship == nil)  return nil;
     285             :         
     286             :         OOUniversalID   ship_id = [ship universalID];
     287             :         NSNumber                *shipID = [NSNumber numberWithUnsignedShort:ship_id];
     288             :         StationEntity   *station = (StationEntity *)[self parentEntity];
     289             : 
     290             :         HPVector launchVector = HPvector_forward_from_quaternion(quaternion_multiply(orientation, [station orientation]));
     291             :         HPVector temp = (fabs(launchVector.x) < 0.8)? make_HPvector(1,0,0) : make_HPvector(0,1,0);
     292             :         temp = HPcross_product(launchVector, temp);     // 90 deg to launchVector & temp
     293             :         HPVector vi = HPcross_product(launchVector, temp);
     294             :         HPVector vj = HPcross_product(launchVector, vi);
     295             :         HPVector vk = launchVector;
     296             :         
     297             :         // check if this is a new ship on approach
     298             :         //
     299             :         if (![shipsOnApproach objectForKey:shipID])
     300             :         {
     301             :                 HPVector        delta = HPvector_subtract([ship position], [self absolutePositionForSubentity]);
     302             :                 float   ship_distance = HPmagnitude(delta);
     303             : 
     304             :                 if (ship_distance > SCANNER_MAX_RANGE)
     305             :                 {
     306             :                         // too far away - don't claim a docking slot by not putting on approachlist for now.
     307             :                         return OOMakeDockingInstructions(station, [self absolutePositionForSubentity], [ship maxFlightSpeed], 10000, @"APPROACH", nil, NO, -1);
     308             :                 }
     309             : 
     310             :                 [self addShipToShipsOnApproach: ship];
     311             :                 
     312             :                 if (ship_distance < 1000.0 + [station collisionRadius] + ship->collision_radius)  // too close - back off
     313             :                         return OOMakeDockingInstructions(station, [self absolutePositionForSubentity], [ship maxFlightSpeed], 5000, @"BACK_OFF", nil, NO, -1);
     314             :                 
     315             :                 float dot = HPdot_product(launchVector, delta);
     316             :                 if (dot < 0) // approaching from the wrong side of the station - construct a vector to the side of the station.
     317             :                 {
     318             :                         HPVector approachVector = HPcross_product(HPvector_normal(delta), launchVector);
     319             :                         approachVector = HPcross_product(launchVector, approachVector); // vector, 90 degr rotated from launchVector towards target.
     320             :                         return OOMakeDockingInstructions(station, OOHPVectorTowards([self absolutePositionForSubentity], approachVector, [station collisionRadius] + 5000) , [ship maxFlightSpeed], 1000, @"APPROACH", nil, NO, -1);
     321             :                 }
     322             :                 
     323             :                 if (ship_distance > 12500.0)
     324             :                 {
     325             :                         // long way off - approach more closely
     326             :                         return OOMakeDockingInstructions(station, [self absolutePositionForSubentity], [ship maxFlightSpeed], 10000, @"APPROACH", nil, NO, -1);
     327             :                 }
     328             :         }
     329             :         
     330             :         if (![shipsOnApproach objectForKey:shipID])
     331             :         {
     332             :                 // some error has occurred - log it, and send the try-again message
     333             :                 OOLogERR(@"station.issueDockingInstructions.failed", @"couldn't addShipToShipsOnApproach:%@ in %@, retrying later -- shipsOnApproach:\n%@", ship, self, shipsOnApproach);
     334             :                 
     335             :                 return OOMakeDockingInstructions(station, [ship position], 200, 100, @"TRY_AGAIN_LATER", nil, NO, -1);
     336             :         }
     337             : 
     338             : 
     339             :         //      shipsOnApproach now has an entry for the ship.
     340             :         //
     341             :         NSMutableArray* coordinatesStack = [shipsOnApproach objectForKey:shipID];
     342             : 
     343             :         if ([coordinatesStack count] == 0)
     344             :         {
     345             :                 OOLogERR(@"station.issueDockingInstructions.failed", @" -- coordinatesStack = %@", coordinatesStack);
     346             :                 
     347             :                 return OOMakeDockingInstructions(station, [ship position], 0, 100, @"HOLD_POSITION", nil, NO, -1);
     348             :         }
     349             :         
     350             :         // get the docking information from the instructions    
     351             :         NSMutableDictionary *nextCoords = (NSMutableDictionary *)[coordinatesStack objectAtIndex:0];
     352             :         int docking_stage = [nextCoords oo_intForKey:@"docking_stage"];
     353             :         float speedAdvised = [nextCoords oo_floatForKey:@"speed"];
     354             :         float rangeAdvised = [nextCoords oo_floatForKey:@"range"];
     355             :         
     356             :         // calculate world coordinates from relative coordinates
     357             :         HPVector rel_coords;
     358             :         rel_coords.x = [nextCoords oo_doubleForKey:@"rx"];
     359             :         rel_coords.y = [nextCoords oo_doubleForKey:@"ry"];
     360             :         rel_coords.z = [nextCoords oo_doubleForKey:@"rz"];
     361             :         HPVector coords = [self absolutePositionForSubentity];
     362             :         coords.x += rel_coords.x * vi.x + rel_coords.y * vj.x + rel_coords.z * vk.x;
     363             :         coords.y += rel_coords.x * vi.y + rel_coords.y * vj.y + rel_coords.z * vk.y;
     364             :         coords.z += rel_coords.x * vi.z + rel_coords.y * vj.z + rel_coords.z * vk.z;
     365             :         
     366             :         // check if the ship is at the control point
     367             :         double max_allowed_range = 2.0f * rangeAdvised + ship->collision_radius;     // maximum distance permitted from control point - twice advised range
     368             :         HPVector delta = HPvector_subtract(ship->position, coords);
     369             :         
     370             :         if (HPmagnitude2(delta) > max_allowed_range * max_allowed_range)     // too far from the coordinates - do not remove them from the stack!
     371             :         {
     372             :                 if ((docking_stage == 1) &&(HPmagnitude2(delta) < 1000000.0))        // 1km*1km
     373             :                         speedAdvised *= 0.5;    // half speed
     374             :                 
     375             :                 return OOMakeDockingInstructions(station, coords, speedAdvised, rangeAdvised, @"APPROACH_COORDINATES", nil, NO, docking_stage);
     376             :         }
     377             :         
     378             :         // else, reached the current coordinates okay..
     379             : 
     380             :         // get the NEXT coordinates
     381             :         nextCoords = (NSMutableDictionary *)[coordinatesStack oo_dictionaryAtIndex:1];
     382             :         if (nextCoords == nil)
     383             :         {
     384             :                 return nil;
     385             :         }
     386             :         
     387             :         docking_stage = [nextCoords oo_intForKey:@"docking_stage"];
     388             :         speedAdvised = [nextCoords oo_floatForKey:@"speed"];
     389             :         rangeAdvised = [nextCoords oo_floatForKey:@"range"];
     390             :         BOOL match_rotation = [nextCoords oo_boolForKey:@"match_rotation"];
     391             :         NSString *comms_message = [nextCoords oo_stringForKey:@"comms_message"];
     392             :         
     393             :         if (comms_message)
     394             :         {
     395             :                 [station sendExpandedMessage:comms_message toShip:ship];
     396             :         }
     397             :                         
     398             :         // calculate world coordinates from relative coordinates
     399             :         rel_coords.x = [nextCoords oo_doubleForKey:@"rx"];
     400             :         rel_coords.y = [nextCoords oo_doubleForKey:@"ry"];
     401             :         rel_coords.z = [nextCoords oo_doubleForKey:@"rz"];
     402             :         coords = [self absolutePositionForSubentity];
     403             :         coords.x += rel_coords.x * vi.x + rel_coords.y * vj.x + rel_coords.z * vk.x;
     404             :         coords.y += rel_coords.x * vi.y + rel_coords.y * vj.y + rel_coords.z * vk.y;
     405             :         coords.z += rel_coords.x * vi.z + rel_coords.y * vj.z + rel_coords.z * vk.z;
     406             :         
     407             :         if([id_lock[docking_stage] weakRefUnderlyingObject] == nil &&
     408             :            [id_lock[docking_stage + 1] weakRefUnderlyingObject] == nil &&
     409             :            [id_lock[docking_stage + 2] weakRefUnderlyingObject] == nil) // check three stages ahead
     410             :         {
     411             :                 // approach is clear - move to next position
     412             :                 //
     413             :                 
     414             :                 // clear any previously owned docking stages
     415             :                 [self clearIdLocks:ship];
     416             :                                 
     417             :                 if (docking_stage > 1)       // don't claim first docking stage
     418             :                 {
     419             :                         [id_lock[docking_stage] release];
     420             :                         id_lock[docking_stage] = [ship weakRetain];     // otherwise - claim this docking stage
     421             :                 }
     422             :                 
     423             :                 //remove the previous stage from the stack
     424             :                 [coordinatesStack removeObjectAtIndex:0];
     425             :                 
     426             :                 return OOMakeDockingInstructions(station, coords, speedAdvised, rangeAdvised, @"APPROACH_COORDINATES", nil, match_rotation, docking_stage);
     427             :         }
     428             :         
     429             :         // else, approach isn't clear - hold position..
     430             :         //
     431             :         [[ship getAI] message:@"HOLD_POSITION"];
     432             :         
     433             :         if (![nextCoords objectForKey:@"hold_message_given"])
     434             :         {
     435             :                 // COMM-CHATTER
     436             :                 [UNIVERSE clearPreviousMessage];
     437             :                 [self sendExpandedMessage: @"[station-hold-position]" toShip: ship];
     438             :                 [nextCoords setObject:@"YES" forKey:@"hold_message_given"];
     439             :         }
     440             : 
     441             :         return OOMakeDockingInstructions(station, ship->position, 0, 100, @"HOLD_POSITION", nil, NO, -1);
     442             : }
     443             : 
     444             : 
     445           0 : - (void) addShipToShipsOnApproach:(ShipEntity *) ship
     446             : {               
     447             :         int                     corridor_distance[] =   {       -1,     1,      3,      5,      7,      9,      11,     12,     12};
     448             :         int                     corridor_offset[] =             {       0,      0,      0,      0,      0,      0,      1,      3,      12};
     449             :         /* Eric's improvements to the docking flight code seem to have
     450             :          * made it safer to go quite a bit faster here. With the increased
     451             :          * numbers of ships which might need to dock at the main station,
     452             :          * faster docking will help avoid massive queues. Previous speed
     453             :          * was mostly 48 - CIM: 27/8/2013*/
     454             :         int                     corridor_speed[] =              {       96,     96,     128,    128,    96,     128,    128,    256,    512};   // how fast to approach the next point
     455             :         int                     corridor_range[] =              {       24,     12,     6,      4,      4,      6,      15,     38,     96};    // how close you have to get to the target point
     456             :         int                     corridor_rotate[] =             {       1,      1,      1,      1,      0,      0,      0,      0,      0};             // whether to match the station rotation
     457             :         int                     corridor_count = 9;
     458             :         int                     corridor_final_approach = 3;
     459             :         
     460             :         NSNumber                *shipID = [NSNumber numberWithUnsignedShort:[ship universalID]];
     461             :         StationEntity   *station = (StationEntity *)[self parentEntity];
     462             :         
     463             :         HPVector launchVector = HPvector_forward_from_quaternion(quaternion_multiply(orientation, [station orientation]));
     464             :         HPVector temp = (fabs(launchVector.x) < 0.8)? make_HPvector(1,0,0) : make_HPvector(0,1,0);
     465             :         temp = HPcross_product(launchVector, temp);     // 90 deg to launchVector & temp
     466             :         HPVector rightVector = HPcross_product(launchVector, temp);
     467             :         HPVector upVector = HPcross_product(launchVector, rightVector);
     468             :         
     469             :         // will select a direction for offset based on the entity personality (was ship ID)
     470             :         int offset_id = [ship entityPersonalityInt] & 0xf;  // 16  point compass
     471             :         double c = cos(offset_id * M_PI * ONE_EIGHTH);
     472             :         double s = sin(offset_id * M_PI * ONE_EIGHTH);
     473             :         
     474             :         // test if this points at the ship
     475             :         HPVector point1 = [self absolutePositionForSubentity];
     476             :         point1.x += launchVector.x * corridor_offset[corridor_count - 1];
     477             :         point1.y += launchVector.x * corridor_offset[corridor_count - 1];
     478             :         point1.z += launchVector.x * corridor_offset[corridor_count - 1];
     479             :         HPVector alt1 = point1;
     480             :         point1.x += c * upVector.x * corridor_offset[corridor_count - 1] + s * rightVector.x * corridor_offset[corridor_count - 1];
     481             :         point1.y += c * upVector.y * corridor_offset[corridor_count - 1] + s * rightVector.y * corridor_offset[corridor_count - 1];
     482             :         point1.z += c * upVector.z * corridor_offset[corridor_count - 1] + s * rightVector.z * corridor_offset[corridor_count - 1];
     483             :         alt1.x -= c * upVector.x * corridor_offset[corridor_count - 1] + s * rightVector.x * corridor_offset[corridor_count - 1];
     484             :         alt1.y -= c * upVector.y * corridor_offset[corridor_count - 1] + s * rightVector.y * corridor_offset[corridor_count - 1];
     485             :         alt1.z -= c * upVector.z * corridor_offset[corridor_count - 1] + s * rightVector.z * corridor_offset[corridor_count - 1];
     486             :         if (HPdistance2(alt1, ship->position) < HPdistance2(point1, ship->position))
     487             :         {
     488             :                 s = -s;
     489             :                 c = -c; // turn 180 degrees
     490             :         }
     491             :         
     492             :         //
     493             :         NSMutableArray *coordinatesStack = [NSMutableArray arrayWithCapacity: MAX_DOCKING_STAGES];
     494             :         float port_depth = port_dimensions.z;   // 250m deep standard port.
     495             :         
     496             :         int i;
     497             :         for (i = corridor_count - 1; i >= 0; i--)
     498             :         {
     499             :                 NSMutableDictionary *nextCoords = [NSMutableDictionary dictionaryWithCapacity:3];
     500             :                 int offset = corridor_offset[i];
     501             :                 float corridor_length = port_depth * corridor_distance[i];
     502             :                 
     503             :                 float rx = s * port_depth * offset;
     504             :                 float ry = c * port_depth * offset;
     505             :                 float rz = corridor_length;
     506             :                 // if there are many ships on approach, randomise coordinates a bit
     507             :                 if ((i == corridor_count - 1) && [self countOfShipsInDockingQueue])
     508             :                 {
     509             :                         /* This used to try to just space the ships further out
     510             :                          * along the 16 approach lanes - this had various problems
     511             :                          * with putting ship coordinates on top of each other
     512             :                          * and/or spacing them out all the way back to the
     513             :                          * witchpoint. Instead, use a few more bits of
     514             :                          * entityPersonalityInt to shuffle the holding coordinates
     515             :                          * a bit more. It still doesn't guarantee two ships won't
     516             :                          * want the same space, but it makes it considerably more
     517             :                          * unlikely - I dropped 100 docking ships into the aegis
     518             :                          * at once, and they all got allocated positions far
     519             :                          * enough from the others to avoid collisions or near
     520             :                          * misses - CIM: 27 May 2014 */
     521             : 
     522             :                         int offset_id2 = ([ship entityPersonalityInt] & 0xf0)>>4;     // 16  point compass
     523             :                         int offset_id3 = ([ship entityPersonalityInt] & 0xf00)>>8;    // 16  point step position
     524             :                         float c2 = cos(offset_id2 * M_PI * ONE_EIGHTH);
     525             :                         float s2 = sin(offset_id2 * M_PI * ONE_EIGHTH);
     526             :                         float ssize = MAX(port_depth,1500.0);
     527             :                         rx += c2 * ssize; 
     528             :                         ry += s2 * ssize; 
     529             :                         rz += ssize * ((float)offset_id3 / 4.0);
     530             : 
     531             : //                      OOLog(@"docking.debug",@"Adjusted coordinates by %f x %f x %f",c2 * ssize,s2 * ssize,ssize * ((float)offset_id3 / 4.0));
     532             :                 }
     533             :                 
     534             :                 // add the lenght inside the station to the corridor, except for the final position, inside the dock.
     535             :                 if (corridor_distance[i] > 0)  corridor_length += port_corridor;
     536             :                 
     537             :                 [nextCoords oo_setInteger:corridor_count - i    forKey:@"docking_stage"];
     538             :                 [nextCoords oo_setFloat:rx                                              forKey:@"rx"];
     539             :                 [nextCoords oo_setFloat:ry                                              forKey:@"ry"];
     540             :                 [nextCoords oo_setFloat:rz                                              forKey:@"rz"];
     541             :                 [nextCoords oo_setFloat:corridor_speed[i]               forKey:@"speed"];
     542             :                 [nextCoords oo_setFloat:corridor_range[i]               forKey:@"range"];
     543             :                 
     544             :                 if (corridor_rotate[i])
     545             :                 {
     546             :                         [nextCoords setObject:@"YES" forKey:@"match_rotation"];
     547             :                 }
     548             :                 
     549             :                 if (i == corridor_final_approach)
     550             :                 {
     551             :                         if (station == [UNIVERSE station])
     552             :                         {
     553             :                                 [nextCoords setObject:@"[station-begin-final-aproach]" forKey:@"comms_message"];
     554             :                         }
     555             :                         else
     556             :                         {
     557             :                                 [nextCoords setObject:@"[docking-begin-final-aproach]" forKey:@"comms_message"];
     558             :                         }
     559             :                 }
     560             :                 
     561             :                 [coordinatesStack addObject:nextCoords];
     562             :         }
     563             :         
     564             :         [shipsOnApproach setObject:coordinatesStack forKey:shipID];
     565             :         
     566             :         
     567             :         // COMM-CHATTER
     568             :         if (station == [UNIVERSE station])
     569             :         {
     570             :                 [station sendExpandedMessage:@"[station-welcome]" toShip:ship];
     571             :         }
     572             :         else
     573             :         {
     574             :                 [station sendExpandedMessage:@"[docking-welcome]" toShip:ship];
     575             :         }
     576             : }
     577             : 
     578             : 
     579             : - (void) noteDockingForShip:(ShipEntity *) ship
     580             : {
     581             :         // safe to do this for now, as it just clears the ship from the docking queue
     582             :         [self abortDockingForShip:ship];
     583             :         
     584             :         // avoid clashes with outgoing ships
     585             :         last_launch_time = [UNIVERSE getTime];
     586             : 
     587             : }
     588             : 
     589             : - (void) abortDockingForShip:(ShipEntity *)ship
     590             : {
     591             :         OOUniversalID   ship_id = [ship universalID];
     592             :         NSNumber                *shipID = [NSNumber numberWithUnsignedShort:ship_id];
     593             :         
     594             :         if ([shipsOnApproach objectForKey:shipID])
     595             :         {
     596             :                 [shipsOnApproach removeObjectForKey:shipID];
     597             :         }
     598             :         
     599             :         if ([ship isPlayer])
     600             :         {
     601             :                 PlayerEntity* player = PLAYER;
     602             :                 if ([player status] == STATUS_IN_FLIGHT &&
     603             :                                 [player getDockingClearanceStatus] >= DOCKING_CLEARANCE_STATUS_REQUESTED)
     604             :                 {
     605             :                         if (HPmagnitude2(HPvector_subtract([player position], [self absolutePositionForSubentity])) > 2250000) // within 1500m of the dock
     606             :                         {
     607             :                                 [[self parentEntity] sendExpandedMessage:@"[station-docking-clearance-abort-cancelled]" toShip:player];
     608             :                                 [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
     609             :                         }
     610             :                         else
     611             :                         {
     612             :                                 int seconds = 10; // when very close to the port, give the player a few seconds to react on the abort message.
     613             :                                 [[self parentEntity] sendExpandedMessage:OOExpandKey(@"station-docking-clearance-abort-cancelled-in-time", seconds) toShip:player];
     614             :                                 [player setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_TIMING_OUT];
     615             :                         }
     616             :                 }
     617             :         }
     618             : 
     619             :         // clear any previously owned docking stages
     620             :         [self clearIdLocks:ship];
     621             : }
     622             : 
     623             : 
     624             : - (Vector) portUpVectorForShipsBoundingBox:(BoundingBox)bb
     625             : {
     626             :         BOOL twist = ((port_dimensions.x < port_dimensions.y) ^ (bb.max.x - bb.min.x < bb.max.y - bb.min.y));
     627             : 
     628             :         if (!twist)
     629             :         {
     630             :                 return vector_up_from_quaternion(quaternion_multiply(orientation, [[self parentEntity] orientation]));
     631             :         }
     632             :         else
     633             :         {
     634             :                 return vector_right_from_quaternion(quaternion_multiply(orientation, [[self parentEntity] orientation]));
     635             :         }
     636             : }
     637             : 
     638             : 
     639             : - (BOOL) shipIsInDockingQueue:(ShipEntity *)ship
     640             : {
     641             :         if (![ship isShip])  return NO;
     642             :         if ([ship isPlayer] && [ship status] == STATUS_DEAD)  return NO;
     643             :         
     644             :         OOUniversalID   ship_id = [ship universalID];
     645             :         NSNumber                *shipID = [NSNumber numberWithUnsignedShort:ship_id];
     646             :         
     647             :         if ([shipsOnApproach objectForKey:shipID])
     648             :         {
     649             :                 return YES;
     650             :         }
     651             :         // player docking manually
     652             :         if ([ship isPlayer] && [[self owner] playerReservedDock] == self)
     653             :         {
     654             :                 return YES;
     655             :         }
     656             :         return NO;
     657             : }
     658             : 
     659             : 
     660             : - (NSUInteger) countOfShipsInDockingQueue
     661             : {
     662             :         return [shipsOnApproach count];
     663             : }
     664             : 
     665             : 
     666             : - (NSUInteger) countOfShipsInLaunchQueue
     667             : {
     668             :         return [launchQueue count];
     669             : }
     670             : 
     671             : 
     672             : - (BOOL) shipIsInDockingCorridor:(ShipEntity *)ship
     673             : {
     674             :         if (![ship isShip])  return NO;
     675             :         if ([ship isPlayer] && [ship status] == STATUS_DEAD)  return NO;
     676             : 
     677             :         BOOL allow_docking_thisship = allow_docking || !disallowed_docking_collides;
     678             :         // ships can physically dock here, and this routine is mainly for
     679             :         // collision detection, but will never be directed here by traffic
     680             :         // control, if allow_docking is false but d_d_c is also false
     681             :         
     682             :         StationEntity *station = (StationEntity *)[self parentEntity];
     683             :         if ([station status] == STATUS_DEAD)
     684             :         {
     685             :                 return NO;
     686             :         }
     687             :         
     688             :         Quaternion q0 = quaternion_multiply(orientation, [station orientation]);
     689             :         Vector vi = vector_right_from_quaternion(q0);
     690             :         Vector vj = vector_up_from_quaternion(q0);
     691             :         Vector vk = vector_forward_from_quaternion(q0);
     692             :         
     693             :         HPVector port_pos = [self absolutePositionForSubentity];
     694             :         
     695             :         BoundingBox shipbb = [ship boundingBox];
     696             :         BoundingBox arbb = [ship findBoundingBoxRelativeToPosition: port_pos InVectors: vi : vj : vk];
     697             :         
     698             :         // port dimensions..
     699             :         GLfloat ww = port_dimensions.x;
     700             :         GLfloat hh = port_dimensions.y;
     701             :         GLfloat dd = port_dimensions.z;
     702             : 
     703             :         BOOL rotatedPort = (ww >= hh) ? NO : YES;
     704             :         BOOL rotatedShip = ((shipbb.max.x - shipbb.min.x) >= (shipbb.max.y - shipbb.min.y)) ? NO : YES;
     705             :         BOOL rotationsMatch = (rotatedShip == rotatedPort);
     706             :         if (rotationsMatch)
     707             :         {
     708             :                 // the ship and port are both bigger in x than y
     709             :                 while (shipbb.max.x - shipbb.min.x > ww * 0.90) ww *= 1.25;
     710             :                 while (shipbb.max.y - shipbb.min.y > hh * 0.90) hh *= 1.25;
     711             :         }
     712             :         else
     713             :         {
     714             :                 // the ship and port have different x/y biggerness
     715             :                 while (shipbb.max.y - shipbb.min.y > ww * 0.90) ww *= 1.25;
     716             :                 while (shipbb.max.x - shipbb.min.x > hh * 0.90) hh *= 1.25;
     717             :         }
     718             :         
     719             :         ww *= 0.5;
     720             :         hh *= 0.5;
     721             : 
     722             : #ifndef NDEBUG
     723             :         if ([ship isPlayer] && (gDebugFlags & DEBUG_DOCKING))
     724             :         {
     725             :                 BOOL                    inLane;
     726             :                 float                   range;
     727             :                 unsigned                laneFlags = 0;
     728             :                 
     729             :                 if (arbb.max.x < ww)   laneFlags |= 1;
     730             :                 if (arbb.min.x > -ww)  laneFlags |= 2;
     731             :                 if (arbb.max.y < hh)   laneFlags |= 4;
     732             :                 if (arbb.min.y > -hh)  laneFlags |= 8;
     733             :                 inLane = laneFlags == 0xF;
     734             :                 range = 0.90 * arbb.max.z + 0.10 * arbb.min.z;
     735             :                 
     736             :                 OOLog(@"docking.debug", @"Normalised port dimensions are %g x %g x %g.  Player bounding box is at %@-%@ -- %s (%X), range: %g",
     737             :                         ww * 2.0, hh * 2.0, dd,
     738             :                         VectorDescription(arbb.min), VectorDescription(arbb.max),
     739             :                         inLane ? "in lane" : "out of lane", laneFlags,
     740             :                         range);
     741             :         }
     742             : #endif
     743             : 
     744             :         if ((arbb.max.x < ww * 3.0)&&(arbb.min.x > -ww * 3.0)&&(arbb.max.y < hh * 3.0)&&(arbb.min.y > -hh * 3.0))
     745             :         {
     746             :                 if ([station requiresDockingClearance] && [ship isPlayer] && [ship status] != STATUS_LAUNCHING && [ship status] != STATUS_AUTOPILOT_ENGAGED && [PLAYER getDockingClearanceStatus] < DOCKING_CLEARANCE_STATUS_GRANTED)
     747             :                 {
     748             :                         if ((0.90 * arbb.max.z + 0.10 * arbb.min.z < 3000) && (dot_product(vk,[ship forwardVector]) < -0.9))
     749             :                         {
     750             :                                 // player is in docking corridor and facing dock
     751             :                                 // and within 3km
     752             :                                 [UNIVERSE addMessage:DESC(@"oolite-station-docking-requires-clearance") forCount:3];
     753             :                         }
     754             :                 }
     755             :         }
     756             :         
     757             :         if (arbb.max.z < -dd)
     758             :         {
     759             :                 return NO;
     760             :         }
     761             :         
     762             :         if ((arbb.max.x < ww)&&(arbb.min.x > -ww)&&(arbb.max.y < hh)&&(arbb.min.y > -hh))
     763             :         {
     764             :                 if ([ship status] != STATUS_LAUNCHING && !allow_docking_thisship)
     765             :                 { // launch-only dock: will collide!
     766             :                         if (arbb.min.z < dd)
     767             :                         {
     768             :                                 [ship takeScrapeDamage: 5 * [UNIVERSE getTimeDelta]*[ship flightSpeed] from:station];
     769             :                                 // and bounce
     770             :                                 HPVector rel = HPvector_subtract([ship position],port_pos);
     771             :                                 rel = HPvector_multiply_scalar(HPvector_normal(rel),[ship flightSpeed]*0.4);
     772             :                                 [ship adjustVelocity:HPVectorToVector(rel)];
     773             :                         }
     774             : 
     775             :                         if (arbb.max.z < 0.0)
     776             :                         { // give some warning before exploding...
     777             :                                 return NO;
     778             :                         }
     779             :                 }
     780             : 
     781             :                 // in lane
     782             :                 if (0.90 * arbb.max.z + 0.10 * arbb.min.z < 0.0)     // we're 90% in docking position!
     783             :                 {
     784             :                         [self pullInShipIfPermitted:ship];
     785             :                 }
     786             :                 return YES;
     787             :         }
     788             :         
     789             :         if ([ship status] == STATUS_LAUNCHING)
     790             :         {
     791             :                 return YES;
     792             :         }
     793             :         
     794             :         // if close enough (within 50%) correct and add damage
     795             :         //
     796             :         GLfloat safety = 1.0+(50.0/100.0);
     797             : 
     798             :         if ((arbb.min.x > -safety * ww)&&(arbb.max.x < safety * ww)&&(arbb.min.y > -safety * hh)&&(arbb.max.y < safety * hh))
     799             :         {
     800             :                 if (arbb.min.z < 0.0)        // got our nose inside
     801             :                 {
     802             : 
     803             :                         if ((arbb.min.x < -ww && arbb.max.x > ww) || (arbb.min.y < -hh && arbb.max.y > hh))
     804             :                         {
     805             :                                 /* No matter how much safety margin there is, if the
     806             :                                  * ship is going off opposite edges of the dock at
     807             :                                  * once, that's a fatal collision */
     808             :                                 return NO;
     809             :                         }
     810             : 
     811             :                         GLfloat correction_factor = -arbb.min.z / (arbb.max.z - arbb.min.z);    // proportion of ship inside
     812             :                 
     813             :                         // damage the ship according to velocity - don't send collision messages to AIs to avoid problems.
     814             :                         [ship takeScrapeDamage: 5 * [UNIVERSE getTimeDelta]*[ship flightSpeed] from:station];
     815             :                         [station doScriptEvent:OOJSID("shipCollided") withArgument:ship]; // no COLLISION message to station AI, carriers would move away!
     816             :                         [ship doScriptEvent:OOJSID("shipCollided") withArgument:station]; // no COLLISION message to ship AI, dockingAI.plist would abort.
     817             :                         
     818             :                         Vector delta;
     819             :                         delta.x = 0.5f * (arbb.max.x + arbb.min.x) * correction_factor;
     820             :                         delta.y = 0.5f * (arbb.max.y + arbb.min.y) * correction_factor;
     821             :                         
     822             :                         if (arbb.max.x < ww && arbb.min.x > -ww)
     823             :                         {
     824             :                                 // x is okay - no need to correct
     825             :                                 delta.x = 0.0f;
     826             :                         }
     827             :                         if (arbb.max.y > hh && arbb.min.y > -hh)
     828             :                         {
     829             :                                 // y is okay - no need to correct
     830             :                                 delta.y = 0.0f;
     831             :                         }
     832             :                                 
     833             :                         // adjust the ship back to the center of the port
     834             :                         HPVector pos = [ship position];
     835             :                         pos.x -= delta.y * vj.x + delta.x * vi.x;
     836             :                         pos.y -= delta.y * vj.y + delta.x * vi.y;
     837             :                         pos.z -= delta.y * vj.z + delta.x * vi.z;
     838             :                         [ship setPosition:pos];
     839             :                 }
     840             :                 
     841             :                 // if far enough in - dock
     842             :                 if (0.90f * arbb.max.z + 0.10f * arbb.min.z < 0.0f)
     843             :                 {
     844             :                         [self pullInShipIfPermitted:ship];
     845             :                 }
     846             :                 
     847             :                 return YES;     // okay NOW we're in the docking corridor!
     848             :         }
     849             :         
     850             :         return NO;
     851             : }
     852             : 
     853             : 
     854           0 : - (void) pullInShipIfPermitted:(ShipEntity *)ship
     855             : {
     856             :         // allow_docking: docking permitted and expected
     857             :         // disallowed_docking_collides: unauthorised docking does not result in explosion
     858             :         if (allow_docking || !disallowed_docking_collides)
     859             :         {
     860             :                 [ship enterDock:(StationEntity*)[self parentEntity]];
     861             :         }
     862             : }
     863             : 
     864             : 
     865             : - (void) addShipToLaunchQueue:(ShipEntity *)ship withPriority:(BOOL)priority
     866             : {
     867             :         [self pruneAndCountShipsOnApproach];
     868             :         
     869             :         if (ship == nil)  return;
     870             :         
     871             :         if (launchQueue == nil)
     872             :         {
     873             :                 launchQueue = [[NSMutableArray alloc] init]; // retained
     874             :         }
     875             :         
     876             :         [ship setStatus:STATUS_DOCKED];
     877             :         if (priority)
     878             :         {
     879             :                 [launchQueue insertObject:ship atIndex:0];
     880             :         }
     881             :         else
     882             :         {
     883             :                 [launchQueue addObject:ship];
     884             :         }
     885             : }
     886             : 
     887             : 
     888             : - (void) launchShip:(ShipEntity *) ship
     889             : {
     890             :         if (![ship isShip])  return;
     891             :         
     892             :         BoundingBox             bb = [ship boundingBox];
     893             :         StationEntity   *station = (StationEntity *)[self parentEntity];
     894             :         
     895             :         HPVector launchPos = [self absolutePositionForSubentity];
     896             :         Vector launchVel = [station velocity];
     897             :         double launchSpeed = 0.5 * [ship maxFlightSpeed];
     898             :         if ([station maxFlightSpeed] > 0 && [station flightSpeed] > 0) // is self a carrier in flight.
     899             :         {
     900             :                 launchSpeed = 0.5 * [ship maxFlightSpeed] * (1.0 + [station flightSpeed]/[station maxFlightSpeed]);
     901             :         }
     902             :         Quaternion q1 = [station orientation];
     903             :         q1 = quaternion_multiply(orientation, q1);
     904             :         Vector launchVector = vector_forward_from_quaternion(q1);
     905             :         
     906             :         // launch orientation
     907             :         if ((port_dimensions.x < port_dimensions.y) ^ (bb.max.x - bb.min.x < bb.max.y - bb.min.y))
     908             :         {
     909             :                 quaternion_rotate_about_axis(&q1, launchVector, M_PI*0.5);  // to account for the slot being at 90 degrees to vertical
     910             :         }
     911             :         [ship setNormalOrientation:q1];
     912             :         // launch position
     913             :         [ship setPosition:launchPos];
     914             :         if([ship pendingEscortCount] > 0) [ship setPendingEscortCount:0]; // Make sure no extra escorts are added after launch. (e.g. for miners etc.)
     915             :         if ([ship hasEscorts]) no_docking_while_launching = YES;
     916             :         // launch speed
     917             :         launchVel = vector_add(launchVel, vector_multiply_scalar(launchVector, launchSpeed));
     918             :         launchSpeed = magnitude(launchVel);
     919             :         [ship setSpeed:launchSpeed];
     920             :         [ship setVelocity:launchVel];
     921             :         // launch roll/pitch
     922             :         [ship setRoll:[station flightRoll]];
     923             :         [ship setPitch:0.0];
     924             :         [ship setYaw:0.0];
     925             :         [UNIVERSE addEntity:ship];
     926             :         [ship setStatus: STATUS_LAUNCHING];
     927             :         [ship setDesiredSpeed:launchSpeed]; // must be set after initialising the AI to correct any speed set by AI
     928             :         last_launch_time = [UNIVERSE getTime];
     929             :         double delay = (port_corridor + 2 * port_dimensions.z)/launchSpeed; // pause until 2 portlengths outside of the station.
     930             :         [ship setLaunchDelay:delay];
     931             :         [[ship getAI] setNextThinkTime:last_launch_time + delay]; // pause while launching
     932             :         
     933             :         [ship resetExhaustPlumes];      // resets stuff for tracking/exhausts
     934             :         
     935             :         [ship doScriptEvent:OOJSID("shipWillLaunchFromStation") withArgument:station];
     936             :         [station doScriptEvent:OOJSID("stationLaunchedShip") withArgument:ship andReactToAIMessage: @"STATION_LAUNCHED_SHIP"];
     937             : }
     938             : 
     939             : 
     940             : - (NSUInteger) countOfShipsInLaunchQueueWithPrimaryRole:(NSString *)role
     941             : {
     942             :         NSUInteger count = 0;
     943             :         ShipEntity *ship = nil;
     944             :         foreach (ship, launchQueue)
     945             :         {
     946             :                 if ([ship hasPrimaryRole:role])  count++;
     947             :         }
     948             :         return count;
     949             : }
     950             : 
     951             : 
     952             : - (BOOL) allowsLaunchingOf:(ShipEntity *) ship
     953             : {
     954             :         if (![ship isShip])  return NO;
     955             :         
     956             :         BoundingBox bb = [ship totalBoundingBox];
     957             :         if ((port_dimensions.x < (bb.max.x - bb.min.x) || port_dimensions.y < (bb.max.y - bb.min.y)) && 
     958             :                 (port_dimensions.y < (bb.max.x - bb.min.x) || port_dimensions.x < (bb.max.y - bb.min.y)) && ![ship isPlayer])
     959             :         {
     960             :                 return NO;
     961             :         }
     962             : 
     963             :         // callback to allow more complex filtering on accept/reject
     964             :         JSContext       *context = OOJSAcquireContext();
     965             :         jsval           rval = JSVAL_VOID;
     966             :         jsval           args[] = { OOJSValueFromNativeObject(context, ship) };
     967             :         JSBool accept = YES;
     968             :         
     969             :         BOOL OK = [[self script] callMethod:OOJSID("acceptLaunchingRequestFrom") inContext:context withArguments:args count:1 result:&rval];
     970             :         if (OK)  OK = JS_ValueToBoolean(context, rval, &accept);
     971             :         if (!OK)  accept = YES; // default to permreject
     972             :         OOJSRelinquishContext(context);
     973             :         if (!accept)
     974             :         {
     975             :                 return NO;
     976             :         }
     977             : 
     978             :         return YES;
     979             : }       
     980             : 
     981             : 
     982             : - (void) clear
     983             : {
     984             :         [launchQueue removeAllObjects];
     985             :         [shipsOnApproach removeAllObjects];
     986             : }
     987             : 
     988             : 
     989             : - (BOOL) dockingCorridorIsEmpty
     990             : {
     991             :         double unitime = [UNIVERSE getTime];
     992             :         
     993             :         if (unitime < last_launch_time + STATION_DELAY_BETWEEN_LAUNCHES)
     994             :         {
     995             :                 // leave sufficient pause between launches
     996             :                 return NO;
     997             :         }
     998             :         
     999             : 
    1000             :         StationEntity   *station = (StationEntity *)[self parentEntity];
    1001             :         if ([station playerReservedDock] == self)
    1002             :         {
    1003             :                 // player probably will not appreciate a ship launch right now
    1004             :                 return NO;
    1005             :         }
    1006             : 
    1007             :         // check against all ships
    1008             :         BOOL                    isEmpty = YES;
    1009             :         int                             ent_count =             UNIVERSE->n_entities;
    1010             :         Entity                  **uni_entities =        UNIVERSE->sortedEntities;    // grab the public sorted list
    1011             :         Entity                  *my_entities[ent_count];
    1012             :         int i;
    1013             :         int ship_count = 0;
    1014             :         
    1015             :         for (i = 0; i < ent_count; i++)
    1016             :         {
    1017             :                 //on red alert, launch even if the player is trying block the corridor. Ignore cargopods or other small debris.
    1018             :                 if ([uni_entities[i] isShip] && ([station alertLevel] < STATION_ALERT_LEVEL_RED || ![uni_entities[i] isPlayer]) && [uni_entities[i] mass] > 1000)
    1019             :                 {
    1020             :                         my_entities[ship_count++] = [uni_entities[i] retain];           //      retained
    1021             :                 }
    1022             :         }
    1023             : 
    1024             :         for (i = 0; (i < ship_count)&&(isEmpty); i++)
    1025             :         {
    1026             :                 ShipEntity*     ship = (ShipEntity*)my_entities[i];
    1027             :                 double          d2 = HPdistance2([station position], [ship position]);
    1028             :                 if ((ship != station) && (d2 < 25000000)&&([ship status] != STATUS_DOCKED))  // within 5km
    1029             :                 {
    1030             :                         HPVector ppos = [self absolutePositionForSubentity];
    1031             :                         d2 = HPdistance2(ppos, ship->position);
    1032             :                         if (d2 < 4000000)    // within 2km of the port entrance
    1033             :                         {
    1034             :                                 Quaternion q1 = [station orientation];
    1035             :                                 q1 = quaternion_multiply([self orientation], q1);
    1036             :                                 //
    1037             :                                 HPVector v_out = HPvector_forward_from_quaternion(q1);
    1038             :                                 HPVector r_pos = make_HPvector(ship->position.x - ppos.x, ship->position.y - ppos.y, ship->position.z - ppos.z);
    1039             :                                 if (r_pos.x||r_pos.y||r_pos.z)
    1040             :                                         r_pos = HPvector_normal(r_pos);
    1041             :                                 else
    1042             :                                         r_pos.z = 1.0;
    1043             :                                 //
    1044             :                                 double vdp = HPdot_product(v_out, r_pos); //== cos of the angle between r_pos and v_out
    1045             :                                 //
    1046             :                                 if (vdp > 0.86)
    1047             :                                 {
    1048             :                                         isEmpty = NO;
    1049             :                                         last_launch_time = unitime - STATION_DELAY_BETWEEN_LAUNCHES + STATION_LAUNCH_RETRY_INTERVAL;
    1050             :                                 }
    1051             :                         }
    1052             :                 }
    1053             :         }
    1054             :         
    1055             :         for (i = 0; i < ship_count; i++)
    1056             :         {
    1057             :                 [my_entities[i] release];               //released
    1058             :         }
    1059             : 
    1060             :         return isEmpty;
    1061             : }
    1062             : 
    1063             : 
    1064             : - (void) clearDockingCorridor
    1065             : {
    1066             :         // check against all ships
    1067             :         StationEntity   *station = (StationEntity *)[self parentEntity];
    1068             :         BOOL                    isClear = YES;
    1069             :         int                             ent_count =                     UNIVERSE->n_entities;
    1070             :         Entity                  **uni_entities =        UNIVERSE->sortedEntities;    // grab the public sorted list
    1071             :         Entity                  *my_entities[ent_count];
    1072             :         int i;
    1073             :         int ship_count = 0;
    1074             :         
    1075             :         for (i = 0; i < ent_count; i++)
    1076             :         {
    1077             :                 if (uni_entities[i]->isShip)
    1078             :                 {
    1079             :                         my_entities[ship_count++] = [uni_entities[i] retain];           //      retained
    1080             :                 }
    1081             :         }
    1082             : 
    1083             :         for (i = 0; i < ship_count; i++)
    1084             :         {
    1085             :                 ShipEntity      *ship = (ShipEntity*)my_entities[i];
    1086             :                 double          d2 = HPdistance2([station position], [ship position]);
    1087             :                 if ((ship != station)&&(d2 < 25000000)&&([ship status] != STATUS_DOCKED))    // within 5km
    1088             :                 {
    1089             :                         HPVector ppos = [self absolutePositionForSubentity];
    1090             :                         float time_out = -15.00;        // 15 secs
    1091             :                         do
    1092             :                         {
    1093             :                                 isClear = YES;
    1094             :                                 d2 = HPdistance2(ppos, ship->position);
    1095             :                                 if (d2 < 4000000)    // within 2km of the port entrance
    1096             :                                 {
    1097             :                                         Quaternion q1 = [station orientation];
    1098             :                                         q1 = quaternion_multiply([self orientation], q1);
    1099             :                                         //
    1100             :                                         Vector v_out = vector_forward_from_quaternion(q1);
    1101             :                                         Vector r_pos = make_vector(ship->position.x - ppos.x, ship->position.y - ppos.y, ship->position.z - ppos.z);
    1102             :                                         if (r_pos.x||r_pos.y||r_pos.z)
    1103             :                                                 r_pos = vector_normal(r_pos);
    1104             :                                         else
    1105             :                                                 r_pos.z = 1.0;
    1106             :                                         //
    1107             :                                         float vdp = dot_product(v_out, r_pos); //== cos of the angle between r_pos and v_out
    1108             :                                         //
    1109             :                                         if (vdp > 0.86f)
    1110             :                                         {
    1111             :                                                 isClear = NO;
    1112             :                                                 
    1113             :                                                 // okay it's in the way .. give it a wee nudge (0.25s)
    1114             :                                                 [ship update: 0.25];
    1115             :                                                 time_out += 0.25;
    1116             :                                         }
    1117             :                                         if (time_out > 0)
    1118             :                                         {
    1119             :                                                 HPVector v1 = HPvector_forward_from_quaternion(orientation);
    1120             :                                                 HPVector spos = ship->position;
    1121             :                                                 spos.x += 3000.0 * v1.x;        spos.y += 3000.0 * v1.y;        spos.z += 3000.0 * v1.z; 
    1122             :                                                 [ship setPosition:spos]; // move 3km out of the way
    1123             :                                         }
    1124             :                                 }
    1125             :                         } while (!isClear);
    1126             :                 }
    1127             :         }
    1128             :         
    1129             :         for (i = 0; i < ship_count; i++)
    1130             :         {
    1131             :                 [my_entities[i] release];               //released
    1132             :         }
    1133             : 
    1134             : }
    1135             : 
    1136             : 
    1137             : - (void)setDimensionsAndCorridor:(BOOL)docking :(BOOL)ddc :(BOOL)launching
    1138             : {
    1139             :         StationEntity *station = (StationEntity*)[self parentEntity];
    1140             :         if (virtual_dock)
    1141             :         {
    1142             :                 port_dimensions = [station virtualPortDimensions];
    1143             :         }
    1144             :         else
    1145             :         {
    1146             :                 BoundingBox bb = [self boundingBox];
    1147             :                 port_dimensions = make_vector(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
    1148             :         }
    1149             : 
    1150             :         HPVector vk = HPvector_forward_from_quaternion(orientation);
    1151             :         
    1152             :         BoundingBox stbb = [station boundingBox];
    1153             :         HPVector start = position;
    1154             :         while ((start.x > stbb.min.x)&&(start.x < stbb.max.x) &&
    1155             :                    (start.y > stbb.min.y)&&(start.y < stbb.max.y) &&
    1156             :                    (start.z > stbb.min.z)&&(start.z < stbb.max.z) )
    1157             :         {
    1158             :                 start = HPvector_add(start, HPvector_multiply_scalar(vk, port_dimensions.z));
    1159             :         }
    1160             :         port_corridor = start.z - position.z;
    1161             :         
    1162             :         allow_docking = docking;
    1163             :         disallowed_docking_collides = ddc;
    1164             :         allow_launching = launching;
    1165             : }
    1166             : 
    1167             : 
    1168             : 
    1169             : //////////////////////////////////////////////// from superclass
    1170             : 
    1171           0 : - (BOOL) isDock
    1172             : {
    1173             :         return YES;
    1174             : }
    1175             : 
    1176             : 
    1177           0 : - (id)initWithKey:(NSString *)key definition:(NSDictionary *)dict
    1178             : {
    1179             :         OOJS_PROFILE_ENTER
    1180             :         
    1181             :         self = [super initWithKey:key definition:dict];
    1182             :         if (self != nil)
    1183             :         {
    1184             :                 shipsOnApproach = [[NSMutableDictionary alloc] init];
    1185             :                 launchQueue = [[NSMutableArray alloc] init];
    1186             :                 allow_docking = YES;
    1187             :                 disallowed_docking_collides = NO;
    1188             :                 allow_launching = YES;
    1189             :                 virtual_dock = NO;
    1190             :         }
    1191             :         
    1192             :         return self;
    1193             :         
    1194             :         OOJS_PROFILE_EXIT
    1195             : }
    1196             : 
    1197             : 
    1198           0 : - (void) dealloc
    1199             : {
    1200             :         DESTROY(shipsOnApproach);
    1201             :         DESTROY(launchQueue);
    1202             :         [self clearIdLocks:nil];
    1203             :         
    1204             :         [super dealloc];
    1205             : }
    1206             : 
    1207             : 
    1208           0 : - (void) clearIdLocks:(ShipEntity *)ship
    1209             : {
    1210             :         int i;
    1211             :         for (i = 1; i < MAX_DOCKING_STAGES; i++)
    1212             :         {
    1213             :                 if (ship == nil || ship == [id_lock[i] weakRefUnderlyingObject])
    1214             :                 {
    1215             :                         DESTROY(id_lock[i]);
    1216             :                 }
    1217             :         }
    1218             : }
    1219             : 
    1220             : 
    1221           0 : - (void) clearAllIdLocks
    1222             : {
    1223             :         int i;
    1224             :         for (i = 1; i < MAX_DOCKING_STAGES; i++)
    1225             :         {
    1226             :                 DESTROY(id_lock[i]);
    1227             :         }
    1228             : }
    1229             : 
    1230             : 
    1231           0 : - (BOOL) setUpShipFromDictionary:(NSDictionary *) dict
    1232             : {
    1233             :         OOJS_PROFILE_ENTER
    1234             :         
    1235             :         isShip = YES;
    1236             :         isStation = NO;
    1237             :         
    1238             :         if (![super setUpShipFromDictionary:dict])  return NO;
    1239             :         
    1240             :         return YES;
    1241             :         
    1242             :         OOJS_PROFILE_EXIT
    1243             : }
    1244             : 
    1245             : 
    1246           0 : - (void) update:(OOTimeDelta) delta_t
    1247             : {
    1248             :         [super update:delta_t];
    1249             :         
    1250             :         if (([launchQueue count] > 0)&&([shipsOnApproach count] == 0)&&[self dockingCorridorIsEmpty])
    1251             :         {
    1252             :                 ShipEntity *se=(ShipEntity *)[launchQueue objectAtIndex:0];
    1253             :                 // check to make sure ship has not been destroyed in queue by script
    1254             :                 if ([se status] == STATUS_DOCKED)
    1255             :                 {
    1256             :                         [self launchShip:se];
    1257             :                 }
    1258             :                 [launchQueue removeObjectAtIndex:0];
    1259             :         }
    1260             :         if (([launchQueue count] == 0) && no_docking_while_launching)
    1261             :         {
    1262             :                 no_docking_while_launching = NO;        // launching complete
    1263             :         }
    1264             :         
    1265             : }
    1266             : 
    1267             : 
    1268             : // avoid possibility of shooting the virtual dock damaging the station
    1269           0 : - (void) noteTakingDamage:(double)amount from:(Entity *)entity type:(OOShipDamageType)type
    1270             : {
    1271             :         if (virtual_dock) // can't be damaged
    1272             :         {
    1273             :                 return;
    1274             :         }
    1275             :         [super noteTakingDamage:amount from:entity type:type];
    1276             : }
    1277             : 
    1278             : 
    1279           0 : - (void) takeEnergyDamage:(double)amount from:(Entity *)ent becauseOf:(Entity *)other weaponIdentifier:(NSString *)weaponIdentifier
    1280             : {
    1281             :         if (virtual_dock) // can't be damaged
    1282             :         {
    1283             :                 return;
    1284             :         }
    1285             :         [super takeEnergyDamage:amount from:ent becauseOf:other weaponIdentifier:weaponIdentifier];
    1286             : }
    1287             : 
    1288             : 
    1289             : // virtual docks are invisible
    1290           0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
    1291             : {
    1292             :         if (virtual_dock) // not drawn
    1293             :         {
    1294             :                 return;
    1295             :         }
    1296             :         [super drawImmediate:immediate translucent:translucent];
    1297             : }
    1298             : 
    1299             : @end

Generated by: LCOV version 1.14