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

          Line data    Source code
       1           0 : /*
       2             : 
       3             : PlayerEntity.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             : #include <assert.h>
      26             : 
      27             : #import "PlayerEntity.h"
      28             : #import "PlayerEntityLegacyScriptEngine.h"
      29             : #import "PlayerEntityContracts.h"
      30             : #import "PlayerEntityControls.h"
      31             : #import "PlayerEntitySound.h"
      32             : #import "PlayerEntityScriptMethods.h"
      33             : 
      34             : #import "StationEntity.h"
      35             : #import "OOSunEntity.h"
      36             : #import "OOPlanetEntity.h"
      37             : #import "WormholeEntity.h"
      38             : #import "ProxyPlayerEntity.h"
      39             : #import "OOQuiriumCascadeEntity.h"
      40             : #import "OOLaserShotEntity.h"
      41             : #import "OOMesh.h"
      42             : 
      43             : #import "OOMaths.h"
      44             : #import "GameController.h"
      45             : #import "ResourceManager.h"
      46             : #import "Universe.h"
      47             : #import "AI.h"
      48             : #import "ShipEntityAI.h"
      49             : #import "MyOpenGLView.h"
      50             : #import "OOTrumble.h"
      51             : #import "PlayerEntityLoadSave.h"
      52             : #import "OOSound.h"
      53             : #import "OOColor.h"
      54             : #import "Octree.h"
      55             : #import "OOCacheManager.h"
      56             : #import "OOOXZManager.h"
      57             : #import "OOStringExpander.h"
      58             : #import "OOStringParsing.h"
      59             : #import "OOPListParsing.h"
      60             : #import "OOCollectionExtractors.h"
      61             : #import "OOConstToString.h"
      62             : #import "OOTexture.h"
      63             : #import "OORoleSet.h"
      64             : #import "HeadUpDisplay.h"
      65             : #import "OOOpenGLExtensionManager.h"
      66             : #import "OOMusicController.h"
      67             : #import "OOEntityFilterPredicate.h"
      68             : #import "OOShipRegistry.h"
      69             : #import "OOEquipmentType.h"
      70             : #import "NSFileManagerOOExtensions.h"
      71             : #import "OOFullScreenController.h"
      72             : #import "OODebugSupport.h"
      73             : 
      74             : #import "CollisionRegion.h"
      75             : 
      76             : #import "OOJSScript.h"
      77             : #import "OOScriptTimer.h"
      78             : #import "OOJSEngineTimeManagement.h"
      79             : #import "OOJSInterfaceDefinition.h"
      80             : #import "OOJSGuiScreenKeyDefinition.h"
      81             : #import "OOConstToJSString.h"
      82             : 
      83             : #import "OOJoystickManager.h"
      84             : #import "PlayerEntityStickMapper.h"
      85             : #import "PlayerEntityStickProfile.h"
      86             : #import "PlayerEntityKeyMapper.h"
      87             : #import "OOSystemDescriptionManager.h"
      88             : 
      89             : 
      90           0 : #define PLAYER_DEFAULT_NAME                             @"Jameson"
      91             : 
      92           0 : enum
      93             : {
      94             :         // If comm log is kCommLogTrimThreshold or more lines long, it will be cut to kCommLogTrimSize.
      95             :         kCommLogTrimThreshold                           = 15U,
      96             :         kCommLogTrimSize                                        = 10U
      97             : };
      98             : 
      99             : 
     100           0 : static NSString * const kOOLogBuyMountedOK                      = @"equip.buy.mounted";
     101           0 : static NSString * const kOOLogBuyMountedFailed          = @"equip.buy.mounted.failed";
     102           0 : static float const              kDeadResetTime                          = 30.0f;
     103             : 
     104           0 : PlayerEntity            *gOOPlayer = nil;
     105           0 : static GLfloat          sBaseMass = 0.0;
     106             : 
     107             : NSComparisonResult marketSorterByName(id a, id b, void *market);
     108             : NSComparisonResult marketSorterByPrice(id a, id b, void *market);
     109             : NSComparisonResult marketSorterByQuantity(id a, id b, void *market);
     110             : NSComparisonResult marketSorterByMassUnit(id a, id b, void *market);
     111             : 
     112             : 
     113             : @interface PlayerEntity (OOPrivate)
     114             : 
     115           0 : - (void) setExtraEquipmentFromFlags;
     116           0 : - (void) doTradeIn:(OOCreditsQuantity)tradeInValue forPriceFactor:(double)priceFactor;
     117             : 
     118             : // Subs of update:
     119           0 : - (void) updateMovementFlags;
     120           0 : - (void) updateAlertCondition;
     121           0 : - (void) updateFuelScoops:(OOTimeDelta)delta_t;
     122           0 : - (void) updateClocks:(OOTimeDelta)delta_t;
     123           0 : - (void) checkScriptsIfAppropriate;
     124           0 : - (void) updateTrumbles:(OOTimeDelta)delta_t;
     125           0 : - (void) performAutopilotUpdates:(OOTimeDelta)delta_t;
     126           0 : - (void) performInFlightUpdates:(OOTimeDelta)delta_t;
     127           0 : - (void) performWitchspaceCountdownUpdates:(OOTimeDelta)delta_t;
     128           0 : - (void) performWitchspaceExitUpdates:(OOTimeDelta)delta_t;
     129           0 : - (void) performLaunchingUpdates:(OOTimeDelta)delta_t;
     130           0 : - (void) performDockingUpdates:(OOTimeDelta)delta_t;
     131           0 : - (void) performDeadUpdates:(OOTimeDelta)delta_t;
     132           0 : - (void) gameOverFadeToBW;
     133           0 : - (void) updateTargeting;
     134           0 : - (void) showGameOver;
     135           0 : - (void) updateWormholes;
     136             : 
     137           0 : - (void) updateAlertConditionForNearbyEntities;
     138           0 : - (BOOL) checkEntityForMassLock:(Entity *)ent withScanClass:(int)scanClass;
     139             : 
     140             : 
     141             : // Shopping
     142           0 : - (void) showMarketScreenHeaders;
     143           0 : - (void) showMarketScreenDataLine:(OOGUIRow)row forGood:(OOCommodityType)good inMarket:(OOCommodityMarket *)localMarket holdQuantity:(OOCargoQuantity)quantity;
     144           0 : - (void) showMarketCashAndLoadLine;
     145             : 
     146             : 
     147           0 : - (BOOL) tryBuyingItem:(NSString *)eqKey;
     148             : 
     149             : // Cargo & passenger contracts
     150           0 : - (NSArray*) contractsListForScriptingFromArray:(NSArray *)contractsArray forCargo:(BOOL)forCargo;
     151             : 
     152             : 
     153           0 : - (void) prepareMarkedDestination:(NSMutableDictionary *)markers :(NSDictionary *)marker;
     154             : 
     155           0 : - (void) witchStart;
     156           0 : - (void) witchJumpTo:(OOSystemID)sTo misjump:(BOOL)misjump;
     157           0 : - (void) witchEnd;
     158             : 
     159             : // Jump distance/cost calculations for selected target.
     160           0 : - (double) hyperspaceJumpDistance;
     161           0 : - (OOFuelQuantity) fuelRequiredForJump;
     162             : 
     163           0 : - (void) noteCompassLostTarget;
     164             : 
     165             : 
     166             : 
     167             : @end
     168             : 
     169             : 
     170             : @interface ShipEntity (Hax)
     171             : 
     172           0 : - (id) initBypassForPlayer;
     173             : 
     174             : @end
     175             : 
     176             : 
     177             : @implementation PlayerEntity
     178             : 
     179             : + (PlayerEntity *) sharedPlayer
     180             : {
     181             :         if (EXPECT_NOT(gOOPlayer == nil))
     182             :         {
     183             :                 gOOPlayer = [[PlayerEntity alloc] init];
     184             :         }
     185             :         return gOOPlayer;
     186             : }
     187             : 
     188             : 
     189           0 : - (void) setName:(NSString *)inName
     190             : {
     191             :         // Block super method; player ship can't be renamed.
     192             : }
     193             : 
     194             : 
     195             : - (GLfloat) baseMass
     196             : {
     197             :         if (sBaseMass <= 0.0)
     198             :         {
     199             :                 // First call with initialised mass (in [UNIVERSE setUpInitialUniverse]) is always to the cobra 3, even when starting with a savegame.
     200             :                 if ([self mass] > 0.0)       // bootstrap the base mass.
     201             :                 {
     202             :                         OOLog(@"fuelPrices", @"Setting Cobra3 base mass to: %.2f ", [self mass]);
     203             :                         sBaseMass = [self mass];
     204             :                 }
     205             :                 else 
     206             :                 {
     207             :                         // This happened on startup when [UNIVERSE setUpSpace] was called before player init, inside [UNIVERSE setUpInitialUniverse].
     208             :                         OOLog(@"fuelPrices", @"%@", @"Player ship not initialised properly yet, using precalculated base mass.");
     209             :                         return 185580.0;
     210             :                 }
     211             :         }
     212             : 
     213             :         return sBaseMass;
     214             : }
     215             : 
     216             : 
     217             : - (void) unloadAllCargoPodsForType:(OOCommodityType)type toManifest:(OOCommodityMarket *) manifest
     218             : {
     219             :         NSInteger i, cargoCount = [cargo count];
     220             :         if (cargoCount == 0)  return;
     221             :         
     222             :         // step through the cargo pods adding in the quantities 
     223             :         for (i =  cargoCount - 1; i >= 0 ; i--)
     224             :         {
     225             :                 ShipEntity *cargoItem = [cargo objectAtIndex:i];
     226             :                 NSString * commodityType = [cargoItem commodityType];
     227             :                 if (commodityType == nil || [commodityType isEqualToString:type])
     228             :                 {
     229             :                         if ([commodityType isEqualToString:type])
     230             :                         {
     231             :                                 // transfer
     232             :                                 [manifest addQuantity:[cargoItem commodityAmount] forGood:type];
     233             :                         }
     234             :                         else    // undefined
     235             :                         {
     236             :                                 OOLog(@"player.badCargoPod", @"Cargo pod %@ has bad commodity type, rejecting.", cargoItem);
     237             :                                 continue;
     238             :                         }
     239             :                         [cargo removeObjectAtIndex:i];
     240             :                 }
     241             :         }
     242             : }
     243             : 
     244             : 
     245             : - (void) unloadCargoPodsForType:(OOCommodityType)type amount:(OOCargoQuantity)quantity
     246             : {
     247             :         NSInteger                       i, n_cargo = [cargo count];
     248             :         if (n_cargo == 0)  return;
     249             :         
     250             :         ShipEntity                      *cargoItem = nil;
     251             :         OOCommodityType         co_type;
     252             :         OOCargoQuantity         amount;
     253             :         OOCargoQuantity         cargoToGo = quantity;
     254             : 
     255             :         // step through the cargo pods removing pods or quantities      
     256             :         for (i =  n_cargo - 1; (i >= 0 && cargoToGo > 0) ; i--)
     257             :         {
     258             :                 cargoItem = [cargo objectAtIndex:i];
     259             :                 co_type = [cargoItem commodityType];
     260             :                 if (co_type == nil || [co_type isEqualToString:type])
     261             :                 {
     262             :                         if ([co_type isEqualToString:type])
     263             :                         {
     264             :                                 amount =  [cargoItem commodityAmount];
     265             :                                 if (amount <= cargoToGo)
     266             :                                 {
     267             :                                         [cargo removeObjectAtIndex:i];
     268             :                                         cargoToGo -= amount;
     269             :                                 }
     270             :                                 else
     271             :                                 {
     272             :                                         // we only need to remove a part of the cargo to meet our target
     273             :                                         [cargoItem setCommodity:co_type andAmount:(amount - cargoToGo)];
     274             :                                         cargoToGo = 0;
     275             :                                         
     276             :                                 }
     277             :                         }
     278             :                         else    // undefined
     279             :                         {
     280             :                                 OOLog(@"player.badCargoPod", @"Cargo pod %@ has bad commodity type (COMMODITY_UNDEFINED), rejecting.", cargoItem);
     281             :                                 continue;
     282             :                         }
     283             :                 }
     284             :         }
     285             :         
     286             :         // now check if we are ready. When not, proceed with quantities in the manifest.
     287             :         if (cargoToGo > 0)
     288             :         {
     289             :                 [shipCommodityData removeQuantity:cargoToGo forGood:type];
     290             :         }
     291             : }
     292             : 
     293             : 
     294             : - (void) unloadCargoPods
     295             : {
     296             :         NSAssert([self isDocked], @"Cannot unload cargo pods unless docked.");
     297             :         
     298             :         /* loads commodities from the cargo pods onto the ship's manifest */
     299             :         NSString *good = nil;
     300             :         foreach (good, [shipCommodityData goods])
     301             :         {
     302             :                 [self unloadAllCargoPodsForType:good toManifest:shipCommodityData];
     303             :         }
     304             : #ifndef NDEBUG
     305             :         if ([cargo count] > 0)
     306             :         {
     307             :                 OOLog(@"player.unloadCargo",@"Cargo remains in pods after unloading - %@",cargo);
     308             :         }
     309             : #endif
     310             : 
     311             :         [self calculateCurrentCargo];   // work out the correct value for current_cargo
     312             : }
     313             : 
     314             : 
     315             : // TODO: better feedback on the log as to why failing to create player cargo pods causes a CTD?
     316           0 : - (void) createCargoPodWithType:(OOCommodityType)type andAmount:(OOCargoQuantity)amount
     317             : {
     318             :         ShipEntity *container = [UNIVERSE newShipWithRole:@"1t-cargopod"];
     319             :         if (container)
     320             :         {
     321             :                 [container setScanClass: CLASS_CARGO];
     322             :                 [container setStatus:STATUS_IN_HOLD];
     323             :                 [container setCommodity:type andAmount:amount];
     324             :                 [cargo addObject:container];
     325             :                 [container release];
     326             :         }
     327             :         else
     328             :         {
     329             :                 OOLogERR(@"player.loadCargoPods.noContainer", @"%@", @"couldn't create a container in [PlayerEntity loadCargoPods]");
     330             :                 // throw an exception here...
     331             :                 [NSException raise:OOLITE_EXCEPTION_FATAL
     332             :                                                                 format:@"[PlayerEntity loadCargoPods] failed to create a container for cargo with role 'cargopod'"];
     333             :         }
     334             : }
     335             : 
     336             : 
     337             : - (void) loadCargoPodsForType:(OOCommodityType)type fromManifest:(OOCommodityMarket *) manifest
     338             : {
     339             :         // load commodities from the ships manifest into individual cargo pods
     340             :         unsigned j;
     341             :         
     342             :         OOCargoQuantity quantity = [manifest quantityForGood:type];
     343             :         OOMassUnit              units = [manifest massUnitForGood:type];
     344             :         
     345             :         if (quantity > 0)
     346             :         {
     347             :                 if (units == UNITS_TONS)
     348             :                 {
     349             :                         // easy case
     350             :                         for (j = 0; j < quantity; j++)
     351             :                         {
     352             :                                 [self createCargoPodWithType:type andAmount:1];         // or CTD if unsuccesful (!)
     353             :                         }
     354             :                         [manifest setQuantity:0 forGood:type];
     355             :                 }
     356             :                 else
     357             :                 {
     358             :                         OOCargoQuantity podsRequiredForQuantity, amountToLoadInCargopod, tmpQuantity;
     359             :                         // reserve up to 1/2 ton of each commodity for the safe
     360             :                         if (units == UNITS_KILOGRAMS) 
     361             :                         {
     362             :                                 if (quantity <= MAX_KILOGRAMS_IN_SAFE)
     363             :                                 {
     364             :                                         tmpQuantity = quantity;
     365             :                                          quantity = 0;
     366             :                                 }
     367             :                                 else
     368             :                                 {
     369             :                                         tmpQuantity = MAX_KILOGRAMS_IN_SAFE;
     370             :                                         quantity -= tmpQuantity;
     371             :                                 }
     372             :                                 amountToLoadInCargopod = KILOGRAMS_PER_POD;
     373             :                         }
     374             :                         else
     375             :                         {
     376             :                                 if (quantity <= MAX_GRAMS_IN_SAFE) {
     377             :                                         tmpQuantity = quantity;
     378             :                                         quantity = 0;
     379             :                                 }
     380             :                                 else
     381             :                                 {
     382             :                                         tmpQuantity = MAX_GRAMS_IN_SAFE;
     383             :                                         quantity -= tmpQuantity;
     384             :                                 }
     385             :                                 amountToLoadInCargopod = GRAMS_PER_POD;
     386             :                         }
     387             :                         if (quantity > 0)
     388             :                         {
     389             :                                 podsRequiredForQuantity = 1 + (quantity/amountToLoadInCargopod);
     390             :                                 // this check is needed so that initial quantities like 1499kg or 1499999g
     391             :                                 // do not result in generation of an empty cargopod
     392             :                                 if (quantity % amountToLoadInCargopod == 0)  podsRequiredForQuantity--;
     393             :                                 
     394             :                                 // put each ton or part-ton beyond that in a separate container
     395             :                                 for (j = 0; j < podsRequiredForQuantity; j++)
     396             :                                 {
     397             :                                         if (amountToLoadInCargopod > quantity)
     398             :                                         {
     399             :                                                 // last pod gets the dregs. :)
     400             :                                                 amountToLoadInCargopod = quantity;
     401             :                                         }
     402             :                                         [self createCargoPodWithType:type andAmount:amountToLoadInCargopod];    // or CTD if unsuccesful (!)
     403             :                                         quantity -= amountToLoadInCargopod;
     404             :                                 }
     405             :                                 // adjust manifest for this commodity
     406             :                                 [manifest setQuantity:tmpQuantity forGood:type];
     407             :                         }
     408             :                 }
     409             :         }
     410             : }
     411             : 
     412             : 
     413             : - (void) loadCargoPodsForType:(OOCommodityType)type amount:(OOCargoQuantity)quantity
     414             : {
     415             :         OOMassUnit unit = [shipCommodityData massUnitForGood:type];
     416             :         
     417             :         while (quantity)
     418             :         {
     419             :                 if (unit != UNITS_TONS)
     420             :                 {
     421             :                         int amount_per_container = (unit == UNITS_KILOGRAMS)? KILOGRAMS_PER_POD : GRAMS_PER_POD;
     422             :                         while (quantity > 0)
     423             :                         {
     424             :                                 int smaller_quantity = 1 + ((quantity - 1) % amount_per_container);
     425             :                                 if ([cargo count] < [self maxAvailableCargoSpace])
     426             :                                 {
     427             :                                         ShipEntity* container = [UNIVERSE newShipWithRole:@"1t-cargopod"];
     428             :                                         if (container)
     429             :                                         {
     430             :                                                 // the cargopod ship is just being set up. If ejected,  will call UNIVERSE addEntity
     431             :                                                 [container setStatus:STATUS_IN_HOLD];
     432             :                                                 [container setScanClass: CLASS_CARGO];
     433             :                                                 [container setCommodity:type andAmount:smaller_quantity];
     434             :                                                 [cargo addObject:container];
     435             :                                                 [container release];
     436             :                                         }
     437             :                                 }
     438             :                                 else
     439             :                                 {
     440             :                                         // try to squeeze any surplus, up to half a ton, in the manifest.
     441             :                                         int amount = [shipCommodityData quantityForGood:type] + smaller_quantity;
     442             :                                         if (amount > MAX_GRAMS_IN_SAFE && unit == UNITS_GRAMS) amount = MAX_GRAMS_IN_SAFE;
     443             :                                         else if (amount > MAX_KILOGRAMS_IN_SAFE && unit == UNITS_KILOGRAMS) amount = MAX_KILOGRAMS_IN_SAFE;
     444             : 
     445             :                                         [shipCommodityData setQuantity:amount forGood:type];
     446             :                                 }
     447             :                                 quantity -= smaller_quantity;
     448             :                         }
     449             :                 }
     450             :                 else
     451             :                 {
     452             :                         // put each ton in a separate container
     453             :                         while (quantity)
     454             :                         {
     455             :                                 if ([cargo count] < [self maxAvailableCargoSpace])
     456             :                                 {
     457             :                                         ShipEntity* container = [UNIVERSE newShipWithRole:@"1t-cargopod"];
     458             :                                         if (container)
     459             :                                         {
     460             :                                                 // the cargopod ship is just being set up. If ejected, will call UNIVERSE addEntity
     461             :                                                 [container setScanClass: CLASS_CARGO];
     462             :                                                 [container setStatus:STATUS_IN_HOLD];
     463             :                                                 [container setCommodity:type andAmount:1];
     464             :                                                 [cargo addObject:container];
     465             :                                                 [container release];
     466             :                                         }
     467             :                                 }
     468             :                                 quantity--;
     469             :                         }
     470             :                 }
     471             :         }
     472             : }
     473             : 
     474             : 
     475             : - (void) loadCargoPods
     476             : {
     477             :         /* loads commodities from the ships manifest into individual cargo pods */
     478             :         NSString *good = nil;
     479             :         foreach (good, [shipCommodityData goods])
     480             :         {
     481             :                 [self loadCargoPodsForType:good fromManifest:shipCommodityData];
     482             :         }
     483             :         [self calculateCurrentCargo];   // work out the correct value for current_cargo
     484             :         cargo_dump_time = 0;
     485             : }
     486             : 
     487             : 
     488             : - (OOCommodityMarket *) shipCommodityData
     489             : {
     490             :         return shipCommodityData;
     491             : }
     492             : 
     493             : 
     494             : - (OOCreditsQuantity) deciCredits
     495             : {
     496             :         return credits;
     497             : }
     498             : 
     499             : 
     500             : - (int) random_factor
     501             : {
     502             :         return market_rnd;
     503             : }
     504             : 
     505             : 
     506             : - (void) setRandom_factor:(int)rf
     507             : {
     508             :         market_rnd = rf;
     509             : }
     510             : 
     511             : 
     512             : - (OOGalaxyID) galaxyNumber
     513             : {
     514             :         return galaxy_number;
     515             : }
     516             : 
     517             : 
     518             : - (NSPoint) galaxy_coordinates
     519             : {
     520             :         return galaxy_coordinates;
     521             : }
     522             : 
     523             : 
     524             : - (void) setGalaxyCoordinates:(NSPoint)newPosition
     525             : {
     526             :         galaxy_coordinates.x = newPosition.x;
     527             :         galaxy_coordinates.y = newPosition.y;
     528             : }
     529             : 
     530             : 
     531             : - (NSPoint) cursor_coordinates
     532             : {
     533             :         return cursor_coordinates;
     534             : }
     535             : 
     536             : 
     537             : - (NSPoint) chart_centre_coordinates
     538             : {
     539             :         return chart_centre_coordinates;
     540             : }
     541             : 
     542             : 
     543             : - (OOScalar) chart_zoom
     544             : {
     545             :         if(_missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_SHORT ||
     546             :                 _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_SHORT_ANA_QUICKEST ||
     547             :                 _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_SHORT_ANA_SHORTEST)
     548             :         {
     549             :                 return 1.0;
     550             :         }
     551             :         else if(_missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_LONG ||
     552             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_LONG_ANA_SHORTEST ||
     553             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_LONG_ANA_QUICKEST)
     554             :         {
     555             :                 return CHART_MAX_ZOOM;
     556             :         }
     557             :         else if(_missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_CUSTOM ||
     558             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_CUSTOM_ANA_QUICKEST ||
     559             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_CUSTOM_ANA_SHORTEST)
     560             :         {
     561             :                 return custom_chart_zoom;
     562             :         }
     563             :         return chart_zoom;
     564             : }
     565             : 
     566             : - (OOScalar) custom_chart_zoom
     567             : {
     568             :         return custom_chart_zoom;
     569             : }
     570             : 
     571             : - (void) setCustomChartZoom:(OOScalar)zoom
     572             : {
     573             :         custom_chart_zoom = zoom;
     574             : }
     575             : 
     576             : 
     577             : - (NSPoint) custom_chart_centre_coordinates
     578             : {
     579             :         return custom_chart_centre_coordinates;
     580             : }
     581             : 
     582             : 
     583             : - (void) setCustomChartCentre:(NSPoint)coords
     584             : {
     585             :         custom_chart_centre_coordinates.x = coords.x;
     586             :         custom_chart_centre_coordinates.y = coords.y;
     587             : }
     588             : 
     589             : 
     590             : - (NSPoint) adjusted_chart_centre
     591             : {
     592             :         NSPoint acc;            // adjusted chart centre
     593             :         double scroll_pos;      // cursor coordinate at which we'd want to scoll chart in the direction we're currently considering
     594             :         double ecc;             // chart centre coordinate we'd want if the cursor was on the edge of the galaxy in the current direction
     595             : 
     596             :         if(_missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_SHORT ||
     597             :                 _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_SHORT_ANA_QUICKEST || 
     598             :                 _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_SHORT_ANA_SHORTEST)
     599             :         {
     600             :                 return galaxy_coordinates;
     601             :         }
     602             :         else if(_missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_LONG ||
     603             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_LONG_ANA_QUICKEST ||
     604             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_LONG_ANA_SHORTEST)
     605             :         {
     606             :                 return NSMakePoint(128.0, 128.0);
     607             :         }
     608             :         else if (_missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_CUSTOM ||
     609             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_CUSTOM_ANA_QUICKEST ||
     610             :                         _missionBackgroundSpecial == GUI_BACKGROUND_SPECIAL_CUSTOM_ANA_SHORTEST)
     611             :         {
     612             :                 return custom_chart_centre_coordinates;
     613             :         }
     614             :         // When fully zoomed in we want to centre chart on chart_centre_coordinates.  When zoomed out we want the chart centred on
     615             :         // (128.0, 128.0) so the galaxy fits the screen width.  For intermediate zoom we interpolate.
     616             :         acc.x = chart_centre_coordinates.x + (128.0 - chart_centre_coordinates.x) * (chart_zoom - 1.0) / (CHART_MAX_ZOOM - 1.0);
     617             :         acc.y = chart_centre_coordinates.y + (128.0 - chart_centre_coordinates.y) * (chart_zoom - 1.0) / (CHART_MAX_ZOOM - 1.0);
     618             : 
     619             :         // If the cursor is out of the centre non-scrolling part of the screen adjust the chart centre.  If the cursor is just at scroll_pos
     620             :         // we want to return the chart centre as it is, but if it's at the edge of the galaxy we want the centre positioned so the cursor is
     621             :         // at the edge of the screen
     622             :         if (chart_focus_coordinates.x - acc.x <= -CHART_SCROLL_AT_X*chart_zoom)
     623             :         {
     624             :                 scroll_pos = acc.x - CHART_SCROLL_AT_X*chart_zoom;
     625             :                 ecc = CHART_WIDTH_AT_MAX_ZOOM*chart_zoom / 2.0;
     626             :                 if (scroll_pos <= 0)
     627             :                 {
     628             :                         acc.x = ecc;
     629             :                 }
     630             :                 else
     631             :                 {
     632             :                         acc.x = ((scroll_pos-chart_focus_coordinates.x)*ecc + chart_focus_coordinates.x*acc.x)/scroll_pos;
     633             :                 }
     634             :         }
     635             :         else if (chart_focus_coordinates.x - acc.x >= CHART_SCROLL_AT_X*chart_zoom)
     636             :         {
     637             :                 scroll_pos = acc.x + CHART_SCROLL_AT_X*chart_zoom;
     638             :                 ecc = 256.0 - CHART_WIDTH_AT_MAX_ZOOM*chart_zoom / 2.0;
     639             :                 if (scroll_pos >= 256.0)
     640             :                 {
     641             :                         acc.x = ecc;
     642             :                 }
     643             :                 else
     644             :                 {
     645             :                         acc.x = ((chart_focus_coordinates.x-scroll_pos)*ecc + (256.0 - chart_focus_coordinates.x)*acc.x)/(256.0 - scroll_pos);
     646             :                 }
     647             :         }
     648             :         if (chart_focus_coordinates.y - acc.y <= -CHART_SCROLL_AT_Y*chart_zoom)
     649             :         {
     650             :                 scroll_pos = acc.y - CHART_SCROLL_AT_Y*chart_zoom;
     651             :                 ecc = CHART_HEIGHT_AT_MAX_ZOOM*chart_zoom / 2.0;
     652             :                 if (scroll_pos <= 0)
     653             :                 {
     654             :                         acc.y = ecc;
     655             :                 }
     656             :                 else
     657             :                 {
     658             :                         acc.y = ((scroll_pos-chart_focus_coordinates.y)*ecc + chart_focus_coordinates.y*acc.y)/scroll_pos;
     659             :                 }
     660             :         }
     661             :         else if (chart_focus_coordinates.y - acc.y >= CHART_SCROLL_AT_Y*chart_zoom)
     662             :         {
     663             :                 scroll_pos = acc.y + CHART_SCROLL_AT_Y*chart_zoom;
     664             :                 ecc = 256.0 - CHART_HEIGHT_AT_MAX_ZOOM*chart_zoom / 2.0;
     665             :                 if (scroll_pos >= 256.0)
     666             :                 {
     667             :                         acc.y = ecc;
     668             :                 }
     669             :                 else
     670             :                 {
     671             :                         acc.y = ((chart_focus_coordinates.y-scroll_pos)*ecc + (256.0 - chart_focus_coordinates.y)*acc.y)/(256.0 - scroll_pos);
     672             :                 }
     673             :         }
     674             :         return acc;
     675             : }
     676             : 
     677             : 
     678             : - (OORouteType) ANAMode
     679             : {
     680             :         return ANA_mode;
     681             : }
     682             : 
     683             : 
     684             : - (OOSystemID) systemID
     685             : {
     686             :         return system_id;
     687             : }
     688             : 
     689             : 
     690             : - (void) setSystemID:(OOSystemID) sid
     691             : {
     692             :         system_id = sid;
     693             :         galaxy_coordinates = PointFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:sid inGalaxy:galaxy_number]);
     694             :         chart_centre_coordinates = galaxy_coordinates;
     695             :         target_chart_centre = chart_centre_coordinates;
     696             : }
     697             : 
     698             : 
     699             : - (OOSystemID) previousSystemID
     700             : {
     701             :         return previous_system_id;
     702             : }
     703             : 
     704             : 
     705             : - (void) setPreviousSystemID:(OOSystemID) sid
     706             : {
     707             :         previous_system_id = sid;
     708             : }
     709             : 
     710             : 
     711             : - (OOSystemID) targetSystemID
     712             : {
     713             :         return target_system_id;
     714             : }
     715             : 
     716             : 
     717             : - (void) setTargetSystemID:(OOSystemID) sid
     718             : {
     719             :         target_system_id = sid;
     720             :         cursor_coordinates = PointFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystemKey:[UNIVERSE keyForPlanetOverridesForSystem:sid inGalaxy:galaxy_number]]);
     721             : }
     722             : 
     723             : 
     724             : // just return target system id if no valid next hop
     725             : - (OOSystemID) nextHopTargetSystemID
     726             : {
     727             :         // not available if no ANA
     728             :         if (![self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
     729             :         {
     730             :                 return target_system_id;
     731             :         }
     732             :         // not available if ANA is turned off
     733             :         if (ANA_mode == OPTIMIZED_BY_NONE)
     734             :         {
     735             :                 return target_system_id;
     736             :         }
     737             :         // easy case
     738             :         if (system_id == target_system_id)
     739             :         {
     740             :                 return system_id; // no need to calculate
     741             :         }
     742             :         NSDictionary *routeInfo = nil;
     743             :         routeInfo = [UNIVERSE routeFromSystem:system_id toSystem:target_system_id optimizedBy:ANA_mode];
     744             :         // no route to destination
     745             :         if (routeInfo == nil)
     746             :         {
     747             :                 return target_system_id;
     748             :         }
     749             :         return [[routeInfo oo_arrayForKey:@"route"] oo_intAtIndex:1];
     750             : }
     751             : 
     752             : 
     753             : - (OOSystemID) infoSystemID
     754             : {
     755             :         return info_system_id;
     756             : }
     757             : 
     758             : 
     759             : - (void) setInfoSystemID: (OOSystemID) sid moveChart: (BOOL) moveChart
     760             : {
     761             :         if (sid != info_system_id)
     762             :         {
     763             :                 OOSystemID old = info_system_id;
     764             :                 info_system_id = sid;
     765             :                 JSContext *context = OOJSAcquireContext();
     766             :                 ShipScriptEvent(context, self, "infoSystemWillChange", INT_TO_JSVAL(info_system_id), INT_TO_JSVAL(old));
     767             :                 if (gui_screen == GUI_SCREEN_LONG_RANGE_CHART || gui_screen == GUI_SCREEN_SHORT_RANGE_CHART)
     768             :                 {
     769             :                         if(moveChart)
     770             :                         {
     771             :                                 target_chart_focus = [[UNIVERSE systemManager] getCoordinatesForSystem:info_system_id inGalaxy:galaxy_number];
     772             :                         }
     773             :                 }
     774             :                 else
     775             :                 {
     776             :                         if(gui_screen == GUI_SCREEN_SYSTEM_DATA)
     777             :                         {
     778             :                                 [self setGuiToSystemDataScreenRefreshBackground: YES];
     779             :                         }
     780             :                         if(moveChart)
     781             :                         {
     782             :                                 chart_centre_coordinates = [[UNIVERSE systemManager] getCoordinatesForSystem:info_system_id inGalaxy:galaxy_number];
     783             :                                 target_chart_centre = chart_centre_coordinates;
     784             :                                 chart_focus_coordinates = chart_centre_coordinates;
     785             :                                 target_chart_focus = chart_focus_coordinates;
     786             :                         }
     787             :                 }
     788             :                 ShipScriptEvent(context, self, "infoSystemChanged", INT_TO_JSVAL(info_system_id), INT_TO_JSVAL(old));
     789             :                 OOJSRelinquishContext(context);
     790             :         }
     791             : }
     792             : 
     793             : 
     794             : - (void) nextInfoSystem
     795             : {
     796             :         if (ANA_mode == OPTIMIZED_BY_NONE)
     797             :         {
     798             :                 [self setInfoSystemID: target_system_id moveChart: YES];
     799             :                 return;
     800             :         }
     801             :         NSArray *route = [[[UNIVERSE routeFromSystem:system_id toSystem:target_system_id optimizedBy:ANA_mode] oo_arrayForKey: @"route"] retain];
     802             :         NSUInteger i;
     803             :         if (route == nil)
     804             :         {
     805             :                 [self setInfoSystemID: target_system_id moveChart: YES];
     806             :                 return;
     807             :         }
     808             :         for (i = 0; i < [route count]; i++)
     809             :         {
     810             :                 if ([[route objectAtIndex: i] intValue] == info_system_id)
     811             :                 {
     812             :                         if (i + 1 < [route count])
     813             :                         {
     814             :                                 [self setInfoSystemID:[[route objectAtIndex:i + 1] unsignedIntValue] moveChart: YES];
     815             :                                 [route release];
     816             :                                 return;
     817             :                         }
     818             :                         break;
     819             :                 }
     820             :         }
     821             :         [route release];
     822             :         [self setInfoSystemID: target_system_id moveChart: YES];
     823             :         return;
     824             : }
     825             : 
     826             : 
     827             : - (void) previousInfoSystem
     828             : {
     829             :         if (ANA_mode == OPTIMIZED_BY_NONE)
     830             :         {
     831             :                 [self setInfoSystemID: system_id moveChart: YES];
     832             :                 return;
     833             :         }
     834             :         NSArray *route = [[[UNIVERSE routeFromSystem:system_id toSystem:target_system_id optimizedBy:ANA_mode] oo_arrayForKey: @"route"] retain];
     835             :         NSUInteger i;
     836             :         if (route == nil)
     837             :         {
     838             :                 [self setInfoSystemID: system_id moveChart: YES];
     839             :                 return;
     840             :         }
     841             :         for (i = 0; i < [route count]; i++)
     842             :         {
     843             :                 if ([[route objectAtIndex: i] intValue] == info_system_id)
     844             :                 {
     845             :                         if (i > 0)
     846             :                         {
     847             :                                 [self setInfoSystemID: [[route objectAtIndex: i - 1] unsignedIntValue] moveChart: YES];
     848             :                                 [route release];
     849             :                                 return;
     850             :                         }
     851             :                         break;
     852             :                 }
     853             :         }
     854             :         [route release];
     855             :         [self setInfoSystemID: system_id moveChart: YES];
     856             :         return;
     857             : }
     858             : 
     859             : 
     860             : - (void) homeInfoSystem
     861             : {
     862             :         [self setInfoSystemID: system_id moveChart: YES];
     863             :         return;
     864             : }
     865             : 
     866             : 
     867             : - (void) targetInfoSystem
     868             : {
     869             :         [self setInfoSystemID: target_system_id moveChart: YES];
     870             :         return;
     871             : }
     872             : 
     873             : 
     874             : - (BOOL) infoSystemOnRoute
     875             : {
     876             :         NSArray *route = [[UNIVERSE routeFromSystem:system_id toSystem:target_system_id optimizedBy:ANA_mode] oo_arrayForKey: @"route"];
     877             :         NSUInteger i;
     878             :         if (route == nil)
     879             :         {
     880             :                 return NO;
     881             :         }
     882             :         for (i = 0; i < [route count]; i++)
     883             :         {
     884             :                 if ([[route objectAtIndex: i] intValue] == info_system_id)
     885             :                 {
     886             :                         return YES;
     887             :                 }
     888             :         }
     889             :         return NO;
     890             : }
     891             :         
     892             : 
     893             : - (WormholeEntity *) wormhole
     894             : {
     895             :         return wormhole;
     896             : }
     897             : 
     898             : 
     899             : - (void) setWormhole:(WormholeEntity*)newWormhole
     900             : {
     901             :         [wormhole release];
     902             :         if (newWormhole != nil)
     903             :         {
     904             :                 wormhole = [newWormhole retain];
     905             :         }
     906             :         else
     907             :         {
     908             :                 wormhole = nil;
     909             :         }
     910             : }
     911             : 
     912             : 
     913             : - (NSDictionary *) commanderDataDictionary
     914             : {
     915             :         int i;
     916             : 
     917             :         NSMutableDictionary *result = [NSMutableDictionary dictionary];
     918             :         
     919             :         [result setObject:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] forKey:@"written_by_version"];
     920             : 
     921             :         NSString        *gal_id = [NSString stringWithFormat:@"%u", galaxy_number];
     922             :         NSString        *sys_id = [NSString stringWithFormat:@"%d", system_id];
     923             :         NSString        *tgt_id = [NSString stringWithFormat:@"%d", target_system_id];
     924             :         NSString        *prv_id = [NSString stringWithFormat:@"%d", previous_system_id];
     925             : 
     926             :         // Variable requiredCargoSpace not suitable for Oolite as it currently stands: it retroactively changes a savegame cargo space.
     927             :         //unsigned      passenger_space = [[OOEquipmentType equipmentTypeWithIdentifier:@"EQ_PASSENGER_BERTH"] requiredCargoSpace];
     928             :         //if (passenger_space == 0) passenger_space = PASSENGER_BERTH_SPACE;
     929             :         
     930             :         [result setObject:gal_id                forKey:@"galaxy_id"];
     931             :         [result setObject:sys_id        forKey:@"system_id"];
     932             :         [result setObject:tgt_id        forKey:@"target_id"];
     933             :         [result setObject:prv_id        forKey:@"previous_system_id"];
     934             :         [result setObject:[NSNumber numberWithFloat:saved_chart_zoom] forKey:@"chart_zoom"];
     935             :         [result setObject:[NSNumber numberWithInt:ANA_mode] forKey:@"chart_ana_mode"];
     936             :         [result setObject:[NSNumber numberWithInt:longRangeChartMode] forKey:@"chart_colour_mode"];
     937             : 
     938             : 
     939             :         if (found_system_id >= 0)
     940             :         {
     941             :                 NSString *found_id = [NSString stringWithFormat:@"%d", found_system_id];
     942             :                 [result setObject:found_id      forKey:@"found_system_id"];
     943             :         }
     944             :         
     945             :         // Write the name of the current system. Useful for looking up saved game information and for overlapping systems.
     946             :         if (![UNIVERSE inInterstellarSpace])
     947             :         {
     948             :                 [result setObject:[UNIVERSE getSystemName:[self currentSystemID]] forKey:@"current_system_name"];
     949             :                 OOGovernmentID government = [[UNIVERSE currentSystemData] oo_intForKey:KEY_GOVERNMENT];
     950             :                 OOTechLevelID techlevel = [[UNIVERSE currentSystemData] oo_intForKey:KEY_TECHLEVEL];
     951             :                 OOEconomyID economy = [[UNIVERSE currentSystemData] oo_intForKey:KEY_ECONOMY];
     952             :                 [result setObject:[NSNumber numberWithUnsignedShort:government] forKey:@"current_system_government"];
     953             :                 [result setObject:[NSNumber numberWithUnsignedInteger:techlevel] forKey:@"current_system_techlevel"];
     954             :                 [result setObject:[NSNumber numberWithUnsignedShort:economy] forKey:@"current_system_economy"];
     955             :         }
     956             :         
     957             :         [result setObject:[self commanderName] forKey:@"player_name"];
     958             :         [result setObject:[self lastsaveName] forKey:@"player_save_name"];
     959             :         [result setObject:[self shipUniqueName] forKey:@"ship_unique_name"];
     960             :         [result setObject:[self shipClassName] forKey:@"ship_class_name"];
     961             : 
     962             :         /*
     963             :                 BUG: GNUstep truncates integer values to 32 bits when loading XML plists.
     964             :                 Workaround: store credits as a double. 53 bits of precision ought to
     965             :                 be good enough for anybody. Besides, we display credits with double
     966             :                 precision anyway.
     967             :                 -- Ahruman 2011-02-15
     968             :         */
     969             :         [result oo_setFloat:credits                             forKey:@"credits"];
     970             :         [result oo_setUnsignedInteger:fuel              forKey:@"fuel"];
     971             :         
     972             :         [result oo_setInteger:galaxy_number     forKey:@"galaxy_number"];
     973             :         
     974             :         [result oo_setBool:[self weaponsOnline] forKey:@"weapons_online"];
     975             :         
     976             :         if (forward_weapon_type != nil)
     977             :         {
     978             :                 [result setObject:[forward_weapon_type identifier]      forKey:@"forward_weapon"];
     979             :         }
     980             :         if (aft_weapon_type != nil)
     981             :         {
     982             :                 [result setObject:[aft_weapon_type identifier]          forKey:@"aft_weapon"];
     983             :         }
     984             :         if (port_weapon_type != nil)
     985             :         {
     986             :                 [result setObject:[port_weapon_type identifier]         forKey:@"port_weapon"];
     987             :         }
     988             :         if (starboard_weapon_type != nil)
     989             :         {
     990             :                 [result setObject:[starboard_weapon_type identifier]    forKey:@"starboard_weapon"];
     991             :         }
     992             :         [result setObject:[self serializeShipSubEntities] forKey:@"subentities_status"];
     993             :         if (hud != nil && [hud nonlinearScanner])
     994             :         {
     995             :                 [result oo_setFloat: [hud scannerZoom] forKey:@"ship_scanner_zoom"];
     996             :         }
     997             :         
     998             :         [result oo_setInteger:max_cargo + PASSENGER_BERTH_SPACE * max_passengers        forKey:@"max_cargo"];
     999             :         
    1000             :         [result setObject:[shipCommodityData savePlayerAmounts]         forKey:@"shipCommodityData"];
    1001             :         
    1002             :         
    1003             :         NSMutableArray *missileRoles = [NSMutableArray arrayWithCapacity:max_missiles];
    1004             :         
    1005             :         for (i = 0; i < (int)max_missiles; i++)
    1006             :         {
    1007             :                 if (missile_entity[i])
    1008             :                 {
    1009             :                         [missileRoles addObject:[missile_entity[i] primaryRole]];
    1010             :                 }
    1011             :                 else
    1012             :                 {
    1013             :                         [missileRoles addObject:@"NONE"];
    1014             :                 }
    1015             :         }
    1016             :         [result setObject:missileRoles forKey:@"missile_roles"];
    1017             :         
    1018             :         [result oo_setInteger:missiles forKey:@"missiles"];
    1019             :         
    1020             :         [result oo_setInteger:legalStatus forKey:@"legal_status"];
    1021             :         [result oo_setInteger:market_rnd forKey:@"market_rnd"];
    1022             :         [result oo_setInteger:ship_kills forKey:@"ship_kills"];
    1023             : 
    1024             :         // ship depreciation
    1025             :         [result oo_setInteger:ship_trade_in_factor forKey:@"ship_trade_in_factor"];
    1026             : 
    1027             :         // mission variables
    1028             :         if (mission_variables != nil)
    1029             :         {
    1030             :                 [result setObject:[NSDictionary dictionaryWithDictionary:mission_variables] forKey:@"mission_variables"];
    1031             :         }
    1032             : 
    1033             :         // communications log
    1034             :         NSArray *log = [self commLog];
    1035             :         if (log != nil)  [result setObject:log forKey:@"comm_log"];
    1036             :         
    1037             :         [result oo_setUnsignedInteger:entity_personality forKey:@"entity_personality"];
    1038             :         
    1039             :         // extra equipment flags
    1040             :         NSMutableDictionary     *equipment = [NSMutableDictionary dictionary];
    1041             :         NSEnumerator            *eqEnum = nil;
    1042             :         NSString                        *eqDesc = nil;
    1043             :         for (eqEnum = [self equipmentEnumerator]; (eqDesc = [eqEnum nextObject]); )
    1044             :         {
    1045             :                 [equipment oo_setInteger:[self countEquipmentItem:eqDesc] forKey:eqDesc];
    1046             :         }
    1047             :         if ([equipment count] != 0)
    1048             :         {
    1049             :                 [result setObject:equipment forKey:@"extra_equipment"];
    1050             :         }
    1051             :         if (primedEquipment < [eqScripts count]) [result setObject:[[eqScripts oo_arrayAtIndex:primedEquipment] oo_stringAtIndex:0] forKey:@"primed_equipment"];
    1052             :         
    1053             :         [result setObject:[self fastEquipmentA] forKey:@"primed_equipment_a"];
    1054             :         [result setObject:[self fastEquipmentB] forKey:@"primed_equipment_b"];
    1055             : 
    1056             :         // roles
    1057             :         [result setObject:roleWeights forKey:@"role_weights"];
    1058             : 
    1059             :         // role information
    1060             :         [result setObject:roleWeightFlags forKey:@"role_weight_flags"];
    1061             : 
    1062             :         // role information
    1063             :         [result setObject:roleSystemList forKey:@"role_system_memory"];
    1064             : 
    1065             :         // reputation
    1066             :         [result setObject:reputation forKey:@"reputation"];
    1067             :         
    1068             :         // initialise parcel reputations in dictionary if not set
    1069             :         int pGood = [reputation oo_intForKey:PARCEL_GOOD_KEY];
    1070             :         int pBad = [reputation oo_intForKey:PARCEL_BAD_KEY];
    1071             :         int pUnknown = [reputation oo_intForKey:PARCEL_UNKNOWN_KEY];
    1072             :         if (pGood+pBad+pUnknown != MAX_CONTRACT_REP)
    1073             :         {
    1074             :                 [reputation oo_setInteger:0 forKey:PARCEL_GOOD_KEY];
    1075             :                 [reputation oo_setInteger:0 forKey:PARCEL_BAD_KEY];
    1076             :                 [reputation oo_setInteger:MAX_CONTRACT_REP forKey:PARCEL_UNKNOWN_KEY];
    1077             :         }
    1078             : 
    1079             :         // passengers
    1080             :         [result oo_setInteger:max_passengers forKey:@"max_passengers"];
    1081             :         [result setObject:passengers forKey:@"passengers"];
    1082             :         [result setObject:passenger_record forKey:@"passenger_record"];
    1083             : 
    1084             :         // parcels
    1085             :         [result setObject:parcels forKey:@"parcels"];
    1086             :         [result setObject:parcel_record forKey:@"parcel_record"];
    1087             :         
    1088             :         //specialCargo
    1089             :         if (specialCargo)  [result setObject:specialCargo forKey:@"special_cargo"];
    1090             :         
    1091             :         // contracts
    1092             :         [result setObject:contracts forKey:@"contracts"];
    1093             :         [result setObject:contract_record forKey:@"contract_record"];
    1094             : 
    1095             :         [result setObject:missionDestinations forKey:@"mission_destinations"];
    1096             : 
    1097             :         //shipyard
    1098             :         [result setObject:shipyard_record forKey:@"shipyard_record"];
    1099             : 
    1100             :         //ship's clock
    1101             :         [result setObject:[NSNumber numberWithDouble:ship_clock] forKey:@"ship_clock"];
    1102             : 
    1103             :         //speech
    1104             :         [result setObject:[NSNumber numberWithInt:isSpeechOn] forKey:@"speech_on"];
    1105             : #if OOLITE_ESPEAK
    1106             :         [result setObject:[UNIVERSE voiceName:voice_no] forKey:@"speech_voice"];
    1107             :         [result setObject:[NSNumber numberWithBool:voice_gender_m] forKey:@"speech_gender"];
    1108             : #endif
    1109             :         
    1110             :         // docking clearance
    1111             :         [result setObject:[NSNumber numberWithBool:[UNIVERSE dockingClearanceProtocolActive]] forKey:@"docking_clearance_protocol"];
    1112             : 
    1113             :         //base ship description
    1114             :         [result setObject:[self shipDataKey] forKey:@"ship_desc"];
    1115             :         [result setObject:[[self shipInfoDictionary] oo_stringForKey:KEY_NAME] forKey:@"ship_name"];
    1116             : 
    1117             :         //custom view no.
    1118             :         [result oo_setUnsignedInteger:_customViewIndex forKey:@"custom_view_index"];
    1119             : 
    1120             :         // escape pod rescue time
    1121             :         [result oo_setFloat:[self escapePodRescueTime] forKey:@"escape_pod_rescue_time"];
    1122             : 
    1123             :         //local market for main station
    1124             :         if ([[UNIVERSE station] localMarket])  [result setObject:[[[UNIVERSE station] localMarket] saveStationAmounts] forKey:@"localMarket"];
    1125             : 
    1126             :         // Scenario restriction on OXZs
    1127             :         [result setObject:[UNIVERSE useAddOns] forKey:@"scenario_restriction"];
    1128             : 
    1129             :         [result setObject:[[UNIVERSE systemManager] exportScriptedChanges] forKey:@"scripted_planetinfo_overrides"];
    1130             : 
    1131             :         // trumble information
    1132             :         [result setObject:[self trumbleValue] forKey:@"trumbles"];
    1133             : 
    1134             :         // wormhole information
    1135             :         NSMutableArray *wormholeDicts = [NSMutableArray arrayWithCapacity:[scannedWormholes count]];
    1136             :         NSEnumerator *wormholes = [scannedWormholes objectEnumerator];
    1137             :         WormholeEntity *wh = nil;
    1138             :         foreach(wh, wormholes)
    1139             :         {
    1140             :                 [wormholeDicts addObject:[wh getDict]];
    1141             :         }
    1142             :         [result setObject:wormholeDicts forKey:@"wormholes"];
    1143             : 
    1144             :         // docked station
    1145             :         StationEntity *dockedStation = [self dockedStation];
    1146             :         [result setObject:dockedStation != nil ? [dockedStation primaryRole]:(NSString *)@"" forKey:@"docked_station_role"];
    1147             :         if (dockedStation)
    1148             :         {
    1149             :                 HPVector dpos = [dockedStation position];
    1150             :                 [result setObject:ArrayFromHPVector(dpos) forKey:@"docked_station_position"];
    1151             :         }
    1152             :         else
    1153             :         {
    1154             :                 [result setObject:[NSArray array] forKey:@"docked_station_position"];
    1155             :         }
    1156             :         [result setObject:[UNIVERSE getStationMarkets] forKey:@"station_markets"];
    1157             : 
    1158             :         // scenario information
    1159             :         if (scenarioKey != nil)
    1160             :         {
    1161             :                 [result setObject:scenarioKey forKey:@"scenario"];
    1162             :         }
    1163             : 
    1164             :         // create checksum
    1165             :         clear_checksum();
    1166             : // TODO: should checksum checks be removed?
    1167             : //      munge_checksum(galaxy_seed.a);  munge_checksum(galaxy_seed.b);  munge_checksum(galaxy_seed.c);
    1168             : //      munge_checksum(galaxy_seed.d);  munge_checksum(galaxy_seed.e);  munge_checksum(galaxy_seed.f);
    1169             :         munge_checksum(galaxy_coordinates.x);   munge_checksum(galaxy_coordinates.y);
    1170             :         munge_checksum(credits);                munge_checksum(fuel);
    1171             :         munge_checksum(max_cargo);              munge_checksum(missiles);
    1172             :         munge_checksum(legalStatus);    munge_checksum(market_rnd);             munge_checksum(ship_kills);
    1173             :         
    1174             :         if (mission_variables != nil)
    1175             :         {
    1176             :                 munge_checksum([[mission_variables description] length]);
    1177             :         }
    1178             :         if (equipment != nil)
    1179             :         {
    1180             :                 munge_checksum([[equipment description] length]);
    1181             :         }
    1182             :         
    1183             :         int final_checksum = munge_checksum([[self shipDataKey] length]);
    1184             : 
    1185             :         //set checksum
    1186             :         [result oo_setInteger:final_checksum forKey:@"checksum"];
    1187             :         
    1188             :         return result;
    1189             : }
    1190             : 
    1191             : 
    1192             : - (BOOL)setCommanderDataFromDictionary:(NSDictionary *) dict
    1193             : {
    1194             :         // multi-function displays
    1195             :         // must be reset before ship setup
    1196             :         [multiFunctionDisplayText release];
    1197             :         multiFunctionDisplayText = [[NSMutableDictionary alloc] init];
    1198             : 
    1199             :         [multiFunctionDisplaySettings release];
    1200             :         multiFunctionDisplaySettings = [[NSMutableArray alloc] init];
    1201             : 
    1202             :         [customDialSettings release];
    1203             :         customDialSettings = [[NSMutableDictionary alloc] init];
    1204             : 
    1205             :         [[UNIVERSE gameView] resetTypedString];
    1206             : 
    1207             :         // Required keys
    1208             :         if ([dict oo_stringForKey:@"ship_desc"] == nil)  return NO;
    1209             :         // galaxy_seed is used is 1.80 or earlier
    1210             :         if ([dict oo_stringForKey:@"galaxy_seed"] == nil && [dict oo_stringForKey:@"galaxy_id"] == nil)  return NO;
    1211             :         // galaxy_coordinates is used is 1.80 or earlier
    1212             :         if ([dict oo_stringForKey:@"galaxy_coordinates"] == nil && [dict oo_stringForKey:@"system_id"] == nil)  return NO;
    1213             :         
    1214             :         NSString *scenarioRestrict = [dict oo_stringForKey:@"scenario_restriction" defaultValue:nil];
    1215             :         if (scenarioRestrict == nil)
    1216             :         {
    1217             :                 // older save game - use the 'strict' key instead
    1218             :                 BOOL strict = [dict oo_boolForKey:@"strict" defaultValue:NO];
    1219             :                 if (strict)
    1220             :                 {
    1221             :                         scenarioRestrict = SCENARIO_OXP_DEFINITION_NONE;
    1222             :                 }
    1223             :                 else
    1224             :                 {
    1225             :                         scenarioRestrict = SCENARIO_OXP_DEFINITION_ALL;
    1226             :                 }
    1227             :         }
    1228             : 
    1229             :         if (![UNIVERSE setUseAddOns:scenarioRestrict fromSaveGame:YES]) 
    1230             :         {
    1231             :                 return NO;
    1232             :         } 
    1233             : 
    1234             :         
    1235             :         //base ship description
    1236             :         [self setShipDataKey:[dict oo_stringForKey:@"ship_desc"]];
    1237             :         
    1238             :         NSDictionary *shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:[self shipDataKey]];
    1239             :         if (shipDict == nil)  return NO;
    1240             :         if (![self setUpShipFromDictionary:shipDict])  return NO;
    1241             :         OOLog(@"fuelPrices", @"Got \"%@\", fuel charge rate: %.2f", [self shipDataKey],[self fuelChargeRate]);
    1242             :         
    1243             :         // ship depreciation
    1244             :         ship_trade_in_factor = [dict oo_intForKey:@"ship_trade_in_factor" defaultValue:95];
    1245             :         
    1246             :         // newer savegames use galaxy_id
    1247             :         if ([dict oo_stringForKey:@"galaxy_id"] != nil)
    1248             :         {
    1249             :                 galaxy_number = [dict oo_unsignedIntegerForKey:@"galaxy_id"];
    1250             :                 if (galaxy_number >= OO_GALAXIES_AVAILABLE)
    1251             :                 {
    1252             :                         return NO;
    1253             :                 }
    1254             :                 [UNIVERSE setGalaxyTo:galaxy_number andReinit:YES];
    1255             : 
    1256             :                 system_id = [dict oo_intForKey:@"system_id"];
    1257             :                 if (system_id < 0 || system_id >= OO_SYSTEMS_PER_GALAXY)
    1258             :                 {
    1259             :                         return NO;
    1260             :                 }
    1261             : 
    1262             :                 [UNIVERSE setSystemTo:system_id];
    1263             : 
    1264             :                 NSArray *coord_vals = ScanTokensFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:system_id inGalaxy:galaxy_number]);
    1265             :                 galaxy_coordinates.x = [coord_vals oo_unsignedCharAtIndex:0];
    1266             :                 galaxy_coordinates.y = [coord_vals oo_unsignedCharAtIndex:1];
    1267             :                 chart_centre_coordinates = galaxy_coordinates;
    1268             :                 target_chart_centre = chart_centre_coordinates;
    1269             :                 cursor_coordinates = galaxy_coordinates;
    1270             :                 chart_zoom = [dict oo_floatForKey:@"chart_zoom" defaultValue:1.0];
    1271             :                 target_chart_zoom = chart_zoom;
    1272             :                 saved_chart_zoom = chart_zoom;
    1273             :                 ANA_mode = [dict oo_intForKey:@"chart_ana_mode" defaultValue:OPTIMIZED_BY_NONE];
    1274             :                 longRangeChartMode = [dict oo_intForKey:@"chart_colour_mode" defaultValue:OOLRC_MODE_SUNCOLOR];
    1275             :                 if (longRangeChartMode == OOLRC_MODE_UNKNOWN) longRangeChartMode = OOLRC_MODE_SUNCOLOR;
    1276             : 
    1277             :                 target_system_id = [dict oo_intForKey:@"target_id" defaultValue:system_id];
    1278             :                 previous_system_id = [dict oo_intForKey:@"previous_system_id" defaultValue:system_id];
    1279             :                 info_system_id = target_system_id;
    1280             :                 coord_vals = ScanTokensFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:target_system_id inGalaxy:galaxy_number]);           
    1281             :                 cursor_coordinates.x = [coord_vals oo_unsignedCharAtIndex:0];
    1282             :                 cursor_coordinates.y = [coord_vals oo_unsignedCharAtIndex:1];
    1283             : 
    1284             :                 chart_focus_coordinates = chart_centre_coordinates;
    1285             :                 target_chart_focus = chart_focus_coordinates;
    1286             : 
    1287             :                 found_system_id = [dict oo_intForKey:@"found_system_id" defaultValue:-1];
    1288             :         }
    1289             :         else
    1290             :                 // compatibility for loading 1.80 savegames
    1291             :         {
    1292             :                 galaxy_number = [dict oo_unsignedIntegerForKey:@"galaxy_number"];
    1293             : 
    1294             :                 [UNIVERSE setGalaxyTo: galaxy_number andReinit:YES];
    1295             :         
    1296             :                 NSArray *coord_vals = ScanTokensFromString([dict oo_stringForKey:@"galaxy_coordinates"]);
    1297             :                 galaxy_coordinates.x = [coord_vals oo_unsignedCharAtIndex:0];
    1298             :                 galaxy_coordinates.y = [coord_vals oo_unsignedCharAtIndex:1];
    1299             :                 chart_centre_coordinates = galaxy_coordinates;
    1300             :                 target_chart_centre = chart_centre_coordinates;
    1301             :                 cursor_coordinates = galaxy_coordinates;
    1302             :                 chart_zoom = 1.0;
    1303             :                 target_chart_zoom = 1.0;
    1304             :                 saved_chart_zoom = 1.0;
    1305             :                 ANA_mode = OPTIMIZED_BY_NONE;
    1306             :                 
    1307             :                 NSString *keyStringValue = [dict oo_stringForKey:@"target_coordinates"];
    1308             : 
    1309             :                 if (keyStringValue != nil)
    1310             :                 {
    1311             :                         coord_vals = ScanTokensFromString(keyStringValue);
    1312             :                         cursor_coordinates.x = [coord_vals oo_unsignedCharAtIndex:0];
    1313             :                         cursor_coordinates.y = [coord_vals oo_unsignedCharAtIndex:1];
    1314             :                 }
    1315             :                 chart_focus_coordinates = chart_centre_coordinates;
    1316             :                 target_chart_focus = chart_focus_coordinates;
    1317             : 
    1318             :                 // calculate system ID, target ID
    1319             :                 if ([dict objectForKey:@"current_system_name"])
    1320             :                 {
    1321             :                         system_id = [UNIVERSE findSystemFromName:[dict oo_stringForKey:@"current_system_name"]];
    1322             :                         if (system_id == -1)  system_id = [UNIVERSE findSystemNumberAtCoords:galaxy_coordinates withGalaxy:galaxy_number includingHidden:YES];
    1323             :                 }
    1324             :                 else
    1325             :                 {
    1326             :                         // really old save games don't have system name saved
    1327             :                         // use coordinates instead - unreliable in zero-distance pairs.
    1328             :                         system_id = [UNIVERSE findSystemNumberAtCoords:galaxy_coordinates withGalaxy:galaxy_number includingHidden:YES];
    1329             :                 }
    1330             :                 // and current_system_name and target_system_name
    1331             :                 // were introduced at different times, too
    1332             :                 if ([dict objectForKey:@"target_system_name"])
    1333             :                 {
    1334             :                         target_system_id = [UNIVERSE findSystemFromName:[dict oo_stringForKey:@"target_system_name"]];
    1335             :                         if (target_system_id == -1)  target_system_id = [UNIVERSE findSystemNumberAtCoords:cursor_coordinates withGalaxy:galaxy_number includingHidden:YES];
    1336             :                 }
    1337             :                 else
    1338             :                 {
    1339             :                         target_system_id = [UNIVERSE findSystemNumberAtCoords:cursor_coordinates withGalaxy:galaxy_number includingHidden:YES];
    1340             :                 }
    1341             :                 info_system_id = target_system_id;
    1342             :                 found_system_id = -1;
    1343             :         }               
    1344             : 
    1345             :         NSString *cname = [dict oo_stringForKey:@"player_name" defaultValue:PLAYER_DEFAULT_NAME];
    1346             :         [self setCommanderName:cname];
    1347             :         [self setLastsaveName:[dict oo_stringForKey:@"player_save_name" defaultValue:cname]];
    1348             : 
    1349             :         [self setShipUniqueName:[dict oo_stringForKey:@"ship_unique_name" defaultValue:@""]];
    1350             :         [self setShipClassName:[dict oo_stringForKey:@"ship_class_name" defaultValue:[shipDict oo_stringForKey:@"name"]]];
    1351             :         
    1352             :         [shipCommodityData loadPlayerAmounts:[dict oo_arrayForKey:@"shipCommodityData"]];
    1353             :         
    1354             :         // extra equipment flags
    1355             :         [self removeAllEquipment];
    1356             :         NSMutableDictionary *equipment = [NSMutableDictionary dictionaryWithDictionary:[dict oo_dictionaryForKey:@"extra_equipment"]];
    1357             :         
    1358             :         // Equipment flags      (deprecated in favour of equipment dictionary, keep for compatibility)
    1359             :         if ([dict oo_boolForKey:@"has_docking_computer"])             [equipment oo_setInteger:1 forKey:@"EQ_DOCK_COMP"];
    1360             :         if ([dict oo_boolForKey:@"has_galactic_hyperdrive"])  [equipment oo_setInteger:1 forKey:@"EQ_GAL_DRIVE"];
    1361             :         if ([dict oo_boolForKey:@"has_escape_pod"])                           [equipment oo_setInteger:1 forKey:@"EQ_ESCAPE_POD"];
    1362             :         if ([dict oo_boolForKey:@"has_ecm"])                                  [equipment oo_setInteger:1 forKey:@"EQ_ECM"];
    1363             :         if ([dict oo_boolForKey:@"has_scoop"])                                        [equipment oo_setInteger:1 forKey:@"EQ_FUEL_SCOOPS"];
    1364             :         if ([dict oo_boolForKey:@"has_energy_bomb"])                  [equipment oo_setInteger:1 forKey:@"EQ_ENERGY_BOMB"];
    1365             :         if ([dict oo_boolForKey:@"has_fuel_injection"])               [equipment oo_setInteger:1 forKey:@"EQ_FUEL_INJECTION"];
    1366             :         
    1367             :         
    1368             :         // Legacy energy unit type -> energy unit equipment item
    1369             :         if ([dict oo_boolForKey:@"has_energy_unit"] && [self installedEnergyUnitType] == ENERGY_UNIT_NONE)
    1370             :         {
    1371             :                 OOEnergyUnitType eType = [dict oo_intForKey:@"energy_unit" defaultValue:ENERGY_UNIT_NORMAL];
    1372             :                 switch (eType)
    1373             :                 {
    1374             :                         // look for NEU first!
    1375             :                         case OLD_ENERGY_UNIT_NAVAL:
    1376             :                                 [equipment oo_setInteger:1 forKey:@"EQ_NAVAL_ENERGY_UNIT"];
    1377             :                                 break;
    1378             :                         
    1379             :                         case OLD_ENERGY_UNIT_NORMAL:
    1380             :                                 [equipment oo_setInteger:1 forKey:@"EQ_ENERGY_UNIT"];
    1381             :                                 break;
    1382             : 
    1383             :                         default:
    1384             :                                 break;
    1385             :                 }
    1386             :         }
    1387             :         
    1388             :         custom_chart_zoom = 1.0;
    1389             :         custom_chart_centre_coordinates = NSMakePoint(galaxy_coordinates.y, galaxy_coordinates.y);
    1390             : 
    1391             :         /*      Energy bombs are no longer supported without OXPs. As compensation,
    1392             :                 we'll award either a Q-mine or some cash. We can't determine what to
    1393             :                 award until we've handled missiles later on, though.
    1394             :         */
    1395             :         BOOL energyBombCompensation = NO;
    1396             :         if ([equipment oo_boolForKey:@"EQ_ENERGY_BOMB"] && [OOEquipmentType equipmentTypeWithIdentifier:@"EQ_ENERGY_BOMB"] == nil)
    1397             :         {
    1398             :                 energyBombCompensation = YES;
    1399             :                 [equipment removeObjectForKey:@"EQ_ENERGY_BOMB"];
    1400             :         }
    1401             :         
    1402             :         eqScripts = [[NSMutableArray alloc] init];
    1403             :         [self addEquipmentFromCollection:equipment];
    1404             :         primedEquipment = [self eqScriptIndexForKey:[dict oo_stringForKey:@"primed_equipment"]];      // if key not found primedEquipment is set to primed-none
    1405             :         
    1406             :         [self setFastEquipmentA:[dict oo_stringForKey:@"primed_equipment_a" defaultValue:@"EQ_CLOAKING_DEVICE"]];
    1407             :         [self setFastEquipmentB:[dict oo_stringForKey:@"primed_equipment_b" defaultValue:@"EQ_ENERGY_BOMB"]]; // even though there isn't one, for compatibility.
    1408             : 
    1409             :         if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"])  compassMode = COMPASS_MODE_PLANET;
    1410             :         else  compassMode = COMPASS_MODE_BASIC;
    1411             :         DESTROY(compassTarget);
    1412             :         
    1413             :         // speech
    1414             :         isSpeechOn = [dict oo_intForKey:@"speech_on"];
    1415             : #if OOLITE_ESPEAK
    1416             :         voice_gender_m = [dict oo_boolForKey:@"speech_gender" defaultValue:YES];
    1417             :         voice_no = [UNIVERSE setVoice:[UNIVERSE voiceNumber:[dict oo_stringForKey:@"speech_voice" defaultValue:nil]] withGenderM:voice_gender_m];
    1418             : #endif
    1419             :         
    1420             :         // reputation
    1421             :         [reputation release];
    1422             :         reputation = [[dict oo_dictionaryForKey:@"reputation"] mutableCopy];
    1423             :         if (reputation == nil)  reputation = [[NSMutableDictionary alloc] init];
    1424             :         [self normaliseReputation];
    1425             : 
    1426             :         // passengers and contracts
    1427             :         [parcels release];
    1428             :         [parcel_record release];
    1429             :         [passengers release];
    1430             :         [passenger_record release];
    1431             :         [contracts release];
    1432             :         [contract_record release];
    1433             :         
    1434             :         max_passengers = [dict oo_intForKey:@"max_passengers" defaultValue:0];
    1435             :         passengers = [[dict oo_arrayForKey:@"passengers"] mutableCopy];
    1436             :         passenger_record = [[dict oo_dictionaryForKey:@"passenger_record"] mutableCopy];
    1437             :         /* Note: contracts from older savegames will have ints in the commodity.
    1438             :          * Need to fix this up */
    1439             :         contracts = [[dict oo_arrayForKey:@"contracts"] mutableCopy];
    1440             :         NSMutableDictionary *contractInfo = nil;
    1441             : 
    1442             :         // iterate downwards; lets us remove invalid ones as we go
    1443             :         for (NSInteger i = (NSInteger)[contracts count] - 1; i >= 0; i--)
    1444             :         {
    1445             :                 contractInfo = [[[contracts oo_dictionaryAtIndex:i] mutableCopy] autorelease];
    1446             :                 // if the trade good ID is an int
    1447             :                 if ([[contractInfo objectForKey:CARGO_KEY_TYPE] isKindOfClass:[NSNumber class]])
    1448             :                 {
    1449             :                         // look it up, and replace with a string
    1450             :                         NSUInteger legacy_type = [contractInfo oo_unsignedIntegerForKey:CARGO_KEY_TYPE];
    1451             :                         [contractInfo setObject:[OOCommodities legacyCommodityType:legacy_type] forKey:CARGO_KEY_TYPE];
    1452             :                         [contracts replaceObjectAtIndex:i withObject:[[contractInfo copy] autorelease]];
    1453             :                 }
    1454             :                 else
    1455             :                 {
    1456             :                         OOCommodityType new_type = [contractInfo oo_stringForKey:CARGO_KEY_TYPE];
    1457             :                         // check that that the type still exists
    1458             :                         if (![[UNIVERSE commodities] goodDefined:new_type])
    1459             :                         {
    1460             :                                 OOLog(@"setCommanderDataFromDictionary.warning.contract",@"Cargo contract to deliver %@ could not be loaded from the saved game, as the commodity is no longer defined",new_type);
    1461             :                                 [contracts removeObjectAtIndex:i];
    1462             :                         }
    1463             :                 }
    1464             :         }
    1465             : 
    1466             :         contract_record = [[dict oo_dictionaryForKey:@"contract_record"] mutableCopy];
    1467             :         parcels = [[dict oo_arrayForKey:@"parcels"] mutableCopy];
    1468             :         parcel_record = [[dict oo_dictionaryForKey:@"parcel_record"] mutableCopy];
    1469             : 
    1470             :         
    1471             :         
    1472             :         if (passengers == nil)  passengers = [[NSMutableArray alloc] init];
    1473             :         if (passenger_record == nil)  passenger_record = [[NSMutableDictionary alloc] init];
    1474             :         if (contracts == nil)  contracts = [[NSMutableArray alloc] init];
    1475             :         if (contract_record == nil)  contract_record = [[NSMutableDictionary alloc] init];
    1476             :         if (parcels == nil)  parcels = [[NSMutableArray alloc] init];
    1477             :         if (parcel_record == nil)  parcel_record = [[NSMutableDictionary alloc] init];
    1478             :         
    1479             :         //specialCargo
    1480             :         [specialCargo release];
    1481             :         specialCargo = [[dict oo_stringForKey:@"special_cargo"] copy];
    1482             :         
    1483             :         // mission destinations
    1484             :         NSArray *legacyDestinations = [dict oo_arrayForKey:@"missionDestinations"];
    1485             : 
    1486             :         NSDictionary *newDestinations = [dict oo_dictionaryForKey:@"mission_destinations"];
    1487             :         [self initialiseMissionDestinations:newDestinations andLegacy:legacyDestinations];
    1488             :         
    1489             :         // shipyard
    1490             :         DESTROY(shipyard_record);
    1491             :         shipyard_record = [[dict oo_dictionaryForKey:@"shipyard_record"] mutableCopy];
    1492             :         if (shipyard_record == nil)  shipyard_record = [[NSMutableDictionary alloc] init];
    1493             :         
    1494             :         // Normalize cargo capacity
    1495             :         unsigned        original_hold_size = [UNIVERSE maxCargoForShip:[self shipDataKey]];
    1496             :         // Not Suitable For Oolite
    1497             :         //unsigned      passenger_space = [[OOEquipmentType equipmentTypeWithIdentifier:@"EQ_PASSENGER_BERTH"] requiredCargoSpace];
    1498             :         //if (passenger_space == 0) passenger_space = PASSENGER_BERTH_SPACE;
    1499             :         
    1500             :         max_cargo = [dict oo_unsignedIntForKey:@"max_cargo" defaultValue:max_cargo];
    1501             :         if (max_cargo > original_hold_size)  [self addEquipmentItem:@"EQ_CARGO_BAY" inContext:@"loading"];
    1502             :         max_cargo = original_hold_size + ([self hasExpandedCargoBay] ? extra_cargo : 0);
    1503             :         if (max_cargo < max_passengers * PASSENGER_BERTH_SPACE)
    1504             :         {
    1505             :                 // Something went wrong. Possibly the save file was hacked to contain more passenger cabins than the available cargo space would allow - Nikos 20110731
    1506             :                 unsigned originalMaxPassengers = max_passengers;
    1507             :                 max_passengers = (unsigned)(max_cargo / PASSENGER_BERTH_SPACE);
    1508             :                 OOLogWARN(@"setCommanderDataFromDictionary.inconsistency.max_passengers", @"player ship %@ had max_passengers set to a value requiring more cargo space than currently available (%u). Setting max_passengers to maximum possible value (%u).", [self name], originalMaxPassengers, max_passengers);
    1509             :         }
    1510             :         max_cargo -= max_passengers * PASSENGER_BERTH_SPACE;
    1511             :         
    1512             :         // Do we have extra passengers?
    1513             :         if (passengers && ([passengers count] > max_passengers))
    1514             :         {
    1515             :                 OOLogWARN(@"setCommanderDataFromDictionary.inconsistency.passengers", @"player ship %@ had more passengers (%lu) than passenger berths (%u). Removing extra passengers.", [self name], [passengers count], max_passengers);
    1516             :                 for (NSInteger i = (NSInteger)[passengers count] - 1; i >= max_passengers; i--)
    1517             :                 {
    1518             :                         [passenger_record removeObjectForKey:[[passengers oo_dictionaryAtIndex:i] oo_stringForKey:PASSENGER_KEY_NAME]];
    1519             :                         [passengers removeObjectAtIndex:i];
    1520             :                 }
    1521             :         }
    1522             :         
    1523             :         // too much cargo?      
    1524             :         NSInteger excessCargo = (NSInteger)[self cargoQuantityOnBoard] - (NSInteger)[self maxAvailableCargoSpace];
    1525             :         if (excessCargo > 0)
    1526             :         {
    1527             :                 OOLogWARN(@"setCommanderDataFromDictionary.inconsistency.cargo", @"player ship %@ had more cargo (%i) than it can hold (%u). Removing extra cargo.", [self name], [self cargoQuantityOnBoard], [self maxAvailableCargoSpace]);
    1528             :                 
    1529             :                 OOCommodityType         type;
    1530             :                 OOMassUnit                      units;
    1531             :                 OOCargoQuantity         oldAmount, toRemove;
    1532             :                 
    1533             :                 OOCargoQuantity remainingExcess = (OOCargoQuantity)excessCargo;
    1534             :                 
    1535             :                 // manifest always contains entries for all 17 commodities, even if their quantity is 0.
    1536             :                 foreach (type, [shipCommodityData goods])
    1537             :                 {
    1538             :                         units = [shipCommodityData massUnitForGood:type];
    1539             : 
    1540             :                         oldAmount = [shipCommodityData quantityForGood:type];
    1541             :                         BOOL roundedTon = (units != UNITS_TONS) && ((units == UNITS_KILOGRAMS && oldAmount > MAX_KILOGRAMS_IN_SAFE) || (units == UNITS_GRAMS && oldAmount > MAX_GRAMS_IN_SAFE));
    1542             :                         if (roundedTon || (units == UNITS_TONS && oldAmount > 0))
    1543             :                         {
    1544             :                                 // let's remove stuff
    1545             :                                 OOCargoQuantity partAmount = oldAmount;
    1546             :                                 toRemove = 0;
    1547             :                                 while (remainingExcess > 0 && partAmount > 0)
    1548             :                                 {
    1549             :                                         if (EXPECT_NOT(roundedTon && ((units == UNITS_KILOGRAMS && partAmount > MAX_KILOGRAMS_IN_SAFE) || (units == UNITS_GRAMS && partAmount > MAX_GRAMS_IN_SAFE))))
    1550             :                                         {
    1551             :                                                 toRemove += (units == UNITS_KILOGRAMS) ? (partAmount > (KILOGRAMS_PER_POD + MAX_KILOGRAMS_IN_SAFE) ? KILOGRAMS_PER_POD : partAmount - MAX_KILOGRAMS_IN_SAFE)
    1552             :                                                                         : (partAmount > (GRAMS_PER_POD + MAX_GRAMS_IN_SAFE) ? GRAMS_PER_POD : partAmount - MAX_GRAMS_IN_SAFE);
    1553             :                                                 partAmount = oldAmount - toRemove;
    1554             :                                                 remainingExcess--;
    1555             :                                         }
    1556             :                                         else if (!roundedTon)
    1557             :                                         {
    1558             :                                                 toRemove++;
    1559             :                                                 partAmount--;
    1560             :                                                 remainingExcess--;
    1561             :                                         }
    1562             :                                         else
    1563             :                                         {
    1564             :                                                 partAmount = 0;
    1565             :                                         }
    1566             :                                 }
    1567             :                                 [shipCommodityData removeQuantity:toRemove forGood:type];
    1568             :                         }
    1569             :                 }
    1570             :         }
    1571             :         
    1572             :         credits = OODeciCreditsFromObject([dict objectForKey:@"credits"]);
    1573             :         
    1574             :         fuel = [dict oo_unsignedIntForKey:@"fuel" defaultValue:fuel];
    1575             :         galaxy_number = [dict oo_intForKey:@"galaxy_number"];
    1576             : //
    1577             :         NSDictionary *shipyard_info = [[OOShipRegistry sharedRegistry] shipyardInfoForKey:[self shipDataKey]];
    1578             :         OOWeaponFacingSet available_facings = [shipyard_info oo_unsignedIntForKey:KEY_WEAPON_FACINGS defaultValue:[self weaponFacings]];
    1579             : 
    1580             :         if (available_facings & WEAPON_FACING_FORWARD)
    1581             :                 forward_weapon_type = OOWeaponTypeFromEquipmentIdentifierLegacy([dict oo_stringForKey:@"forward_weapon"]);
    1582             :         else
    1583             :                 forward_weapon_type = OOWeaponTypeFromEquipmentIdentifierSloppy(@"EQ_WEAPON_NONE");
    1584             : 
    1585             :         if (available_facings & WEAPON_FACING_AFT)
    1586             :                 aft_weapon_type = OOWeaponTypeFromEquipmentIdentifierLegacy([dict oo_stringForKey:@"aft_weapon"]);
    1587             :         else
    1588             :                 aft_weapon_type = OOWeaponTypeFromEquipmentIdentifierSloppy(@"EQ_WEAPON_NONE");
    1589             : 
    1590             :         if (available_facings & WEAPON_FACING_PORT)
    1591             :                 port_weapon_type = OOWeaponTypeFromEquipmentIdentifierLegacy([dict oo_stringForKey:@"port_weapon"]);
    1592             :         else
    1593             :                 port_weapon_type = OOWeaponTypeFromEquipmentIdentifierSloppy(@"EQ_WEAPON_NONE");
    1594             : 
    1595             :         if (available_facings & WEAPON_FACING_STARBOARD)
    1596             :                 starboard_weapon_type = OOWeaponTypeFromEquipmentIdentifierLegacy([dict oo_stringForKey:@"starboard_weapon"]);
    1597             :         else
    1598             :                 starboard_weapon_type = OOWeaponTypeFromEquipmentIdentifierSloppy(@"EQ_WEAPON_NONE");
    1599             : 
    1600             :         [self setWeaponDataFromType:forward_weapon_type];
    1601             : 
    1602             :         if (hud != nil && [hud nonlinearScanner])
    1603             :         {
    1604             :                 [hud setScannerZoom: [dict oo_floatForKey:@"ship_scanner_zoom" defaultValue: 1.0]];
    1605             :         }
    1606             :         
    1607             :         weapons_online = [dict oo_boolForKey:@"weapons_online" defaultValue:YES];
    1608             :         
    1609             :         legalStatus = [dict oo_intForKey:@"legal_status"];
    1610             :         market_rnd = [dict oo_intForKey:@"market_rnd"];
    1611             :         ship_kills = [dict oo_intForKey:@"ship_kills"];
    1612             :         
    1613             :         ship_clock = [dict oo_doubleForKey:@"ship_clock" defaultValue:PLAYER_SHIP_CLOCK_START];
    1614             :         fps_check_time = ship_clock;
    1615             :         
    1616             :         escape_pod_rescue_time = [dict oo_doubleForKey:@"escape_pod_rescue_time" defaultValue:0.0];
    1617             :         
    1618             :         // role weights
    1619             :         [roleWeights release];
    1620             :         roleWeights = [[dict oo_arrayForKey:@"role_weights"] mutableCopy];
    1621             :         NSUInteger rc = [self maxPlayerRoles];
    1622             :         if (roleWeights == nil)
    1623             :         {
    1624             :                 roleWeights = [[NSMutableArray alloc] initWithCapacity:rc];
    1625             :                 while (rc-- > 0)
    1626             :                 {
    1627             :                         [roleWeights addObject:@"player-unknown"];
    1628             :                 }
    1629             :         }
    1630             :         else
    1631             :         {
    1632             :                 if ([roleWeights count] > rc)
    1633             :                 {
    1634             :                         [roleWeights removeObjectsInRange:(NSRange) {rc,[roleWeights count]-rc}];
    1635             :                 }
    1636             :         }
    1637             : 
    1638             :         roleWeightFlags = [[dict oo_dictionaryForKey:@"role_weight_flags"] mutableCopy];
    1639             :         if (roleWeightFlags == nil)
    1640             :         {
    1641             :                 roleWeightFlags = [[NSMutableDictionary alloc] init];
    1642             :         }
    1643             : 
    1644             :         roleSystemList = [[dict oo_arrayForKey:@"role_system_memory"] mutableCopy];
    1645             :         if (roleSystemList == nil)
    1646             :         {
    1647             :                 roleSystemList = [[NSMutableArray alloc] initWithCapacity:32];
    1648             :         }
    1649             : 
    1650             : 
    1651             :         // mission_variables
    1652             :         [mission_variables release];
    1653             :         mission_variables = [[dict oo_dictionaryForKey:@"mission_variables"] mutableCopy];
    1654             :         if (mission_variables == nil)  mission_variables = [[NSMutableDictionary alloc] init];
    1655             :         
    1656             :         // persistant UNIVERSE info
    1657             :         NSDictionary *planetInfoOverrides = [dict oo_dictionaryForKey:@"scripted_planetinfo_overrides"];
    1658             :         if (planetInfoOverrides != nil)
    1659             :         {
    1660             :                 [[UNIVERSE systemManager] importScriptedChanges:planetInfoOverrides];   
    1661             :         } 
    1662             :         else
    1663             :         {
    1664             :                 // no scripted overrides? What about 1.80-style local overrides?
    1665             :                 planetInfoOverrides = [dict oo_dictionaryForKey:@"local_planetinfo_overrides"];
    1666             :                 if (planetInfoOverrides != nil)
    1667             :                 {
    1668             :                         [[UNIVERSE systemManager] importLegacyScriptedChanges:planetInfoOverrides];
    1669             :                 }
    1670             :         }
    1671             :         
    1672             :         // communications log
    1673             :         [commLog release];
    1674             :         commLog = [[NSMutableArray alloc] initWithCapacity:kCommLogTrimThreshold];
    1675             :         
    1676             :         NSArray *savedCommLog = [dict oo_arrayForKey:@"comm_log"];
    1677             :         NSUInteger commCount = [savedCommLog count];
    1678             :         for (NSUInteger i = 0; i < commCount; i++)
    1679             :         {
    1680             :                 [UNIVERSE addCommsMessage:[savedCommLog objectAtIndex:i] forCount:0 andShowComms:NO logOnly:YES];
    1681             :         }
    1682             : 
    1683             :         /*      entity_personality for scripts and shaders. If undefined, we fall back
    1684             :                 to old behaviour of using a random value each time game is loaded (set
    1685             :                 up in -setUp). Saving of entity_personality was added in 1.74.
    1686             :                 -- Ahruman 2009-09-13
    1687             :         */
    1688             :         entity_personality = [dict oo_unsignedShortForKey:@"entity_personality" defaultValue:entity_personality];
    1689             :         
    1690             :         // set up missiles
    1691             :         [self setActiveMissile:0];
    1692             :         for (NSUInteger i = 0; i < PLAYER_MAX_MISSILES; i++)
    1693             :         {
    1694             :                 [missile_entity[i] release];
    1695             :                 missile_entity[i] = nil;
    1696             :         }
    1697             :         NSArray *missileRoles = [dict oo_arrayForKey:@"missile_roles"];
    1698             :         if (missileRoles != nil)
    1699             :         {
    1700             :                 unsigned missileCount = 0;
    1701             :                 for (NSUInteger roleIndex = 0; roleIndex < [missileRoles count] && missileCount < max_missiles; roleIndex++)
    1702             :                 {
    1703             :                         NSString *missile_desc = [missileRoles oo_stringAtIndex:roleIndex];
    1704             :                         if (missile_desc != nil && ![missile_desc isEqualToString:@"NONE"])
    1705             :                         {
    1706             :                                 ShipEntity *amiss = [UNIVERSE newShipWithRole:missile_desc];
    1707             :                                 if (amiss)
    1708             :                                 {
    1709             :                                         missile_list[missileCount] = [OOEquipmentType equipmentTypeWithIdentifier:missile_desc];
    1710             :                                         missile_entity[missileCount] = amiss;   // retain count = 1
    1711             :                                         missileCount++;
    1712             :                                 }
    1713             :                                 else
    1714             :                                 {
    1715             :                                         OOLogWARN(@"load.failed.missileNotFound", @"couldn't find missile with role '%@' in [PlayerEntity setCommanderDataFromDictionary:], missile entry discarded.", missile_desc);
    1716             :                                 }
    1717             :                         }
    1718             :                         missiles = missileCount;
    1719             :                 }
    1720             :         }
    1721             :         else    // no missile_roles
    1722             :         {
    1723             :                 for (NSUInteger i = 0; i < missiles; i++)
    1724             :                 {
    1725             :                         missile_list[i] = [OOEquipmentType equipmentTypeWithIdentifier:@"EQ_MISSILE"];
    1726             :                         missile_entity[i] = [UNIVERSE newShipWithRole:@"EQ_MISSILE"]; // retain count = 1 - should be okay as long as we keep a missile with this role
    1727             :                                                                                                                                                         // in the base package.
    1728             :                 }
    1729             :         }
    1730             :         
    1731             :         if (energyBombCompensation)
    1732             :         {
    1733             :                 /*
    1734             :                         Compensate energy bomb with either a QC mine or the cost of an
    1735             :                         energy bomb (900 credits). This must be done after missiles are
    1736             :                         set up.
    1737             :                 */
    1738             :                 if ([self mountMissileWithRole:@"EQ_QC_MINE"])
    1739             :                 {
    1740             :                         OOLog(@"load.upgrade.replacedEnergyBomb", @"%@", @"Replaced legacy energy bomb with Quirium cascade mine.");
    1741             :                 }
    1742             :                 else
    1743             :                 {
    1744             :                         credits += 9000;
    1745             :                         OOLog(@"load.upgrade.replacedEnergyBomb", @"%@", @"Compensated legacy energy bomb with 900 credits.");
    1746             :                 }
    1747             :         }
    1748             :         
    1749             :         [self setActiveMissile:0];
    1750             :         
    1751             :         [self setHeatInsulation:1.0];
    1752             : 
    1753             :         max_forward_shield                              = BASELINE_SHIELD_LEVEL;
    1754             :         max_aft_shield                                  = BASELINE_SHIELD_LEVEL;
    1755             : 
    1756             :         forward_shield_recharge_rate    = 2.0;
    1757             :         aft_shield_recharge_rate                = 2.0;
    1758             : 
    1759             :         forward_shield = [self maxForwardShieldLevel];
    1760             :         aft_shield = [self maxAftShieldLevel];
    1761             :         
    1762             :         // used to get current_system and target_system here,
    1763             :         // but stores the ID in the save file instead
    1764             :         
    1765             :         // restore subentities status
    1766             :         [self deserializeShipSubEntitiesFrom:[dict oo_stringForKey:@"subentities_status"]];
    1767             :         
    1768             :         // wormholes
    1769             :         NSArray * whArray;
    1770             :         whArray = [dict objectForKey:@"wormholes"];
    1771             :         NSEnumerator * whDicts = [whArray objectEnumerator];
    1772             :         NSDictionary * whCurrDict;
    1773             :         [scannedWormholes release];
    1774             :         scannedWormholes = [[NSMutableArray alloc] initWithCapacity:[whArray count]];
    1775             :         while ((whCurrDict = [whDicts nextObject]) != nil)
    1776             :         {
    1777             :                 WormholeEntity * wh = [[WormholeEntity alloc] initWithDict:whCurrDict];
    1778             :                 [scannedWormholes addObject:wh];
    1779             :                 /* TODO - add to Universe if the wormhole hasn't expired yet; but in this case
    1780             :                  * we need to save/load position and mass as well, which we currently 
    1781             :                  * don't
    1782             :                 if (equal_seeds([wh origin], system_seed))
    1783             :                 {
    1784             :                         [UNIVERSE addEntity:wh];
    1785             :                 }
    1786             :                 */
    1787             :         }
    1788             :         
    1789             :         // custom view no.
    1790             :         if (_customViews != nil)
    1791             :                 _customViewIndex = [dict oo_unsignedIntForKey:@"custom_view_index"] % [_customViews count];
    1792             : 
    1793             : 
    1794             :         // docking clearance protocol
    1795             :         [UNIVERSE setDockingClearanceProtocolActive:[dict oo_boolForKey:@"docking_clearance_protocol" defaultValue:NO]];
    1796             :         
    1797             :         // trumble information
    1798             :         [self setUpTrumbles];
    1799             :         [self setTrumbleValueFrom:[dict objectForKey:@"trumbles"]];   // if it doesn't exist we'll check user-defaults
    1800             : 
    1801             :         return YES;
    1802             : }
    1803             : 
    1804             : 
    1805             : 
    1806             : /////////////////////////////////////////////////////////
    1807             : 
    1808             : 
    1809             : /*      Nasty initialization mechanism:
    1810             :         PlayerEntity is alloced and inited on demand by +sharedPlayer. This
    1811             :         initialization doesn't actually set anything up -- apart from the
    1812             :         assertion, it's like doing a bare alloc. -deferredInit does the work
    1813             :         that -init "should" be doing. It assumes that -[ShipEntity initWithKey:
    1814             :         definition:] will not return an object other than self.
    1815             :         This is necessary because we need a pointer to the PlayerEntity early in
    1816             :         startup, when ship data hasn't been loaded yet. In particular, we need
    1817             :         a pointer to the player to set up the JavaScript environment, we need the
    1818             :         JavaScript environment to set up OpenGL, and we need OpenGL set up to load
    1819             :         ships.
    1820             : */
    1821           0 : - (id) init
    1822             : {
    1823             :         NSAssert(gOOPlayer == nil, @"Expected only one PlayerEntity to exist at a time.");
    1824             :         return [super initBypassForPlayer];
    1825             : }
    1826             : 
    1827             : 
    1828             : - (void) deferredInit
    1829             : {
    1830             :         NSAssert(gOOPlayer == self, @"Expected only one PlayerEntity to exist at a time.");
    1831             :         NSAssert([super initWithKey:PLAYER_SHIP_DESC definition:[NSDictionary dictionary]] == self, @"PlayerEntity requires -[ShipEntity initWithKey:definition:] to return unmodified self.");
    1832             : 
    1833             :         maxFieldOfView = MAX_FOV;
    1834             : #if OO_FOV_INFLIGHT_CONTROL_ENABLED
    1835             :         fov_delta = 2.0; // multiply by 2 each second
    1836             : #endif
    1837             :         
    1838             :         compassMode = COMPASS_MODE_BASIC;
    1839             :         
    1840             :         afterburnerSoundLooping = NO;
    1841             :         
    1842             :         isPlayer = YES;
    1843             :         
    1844             :         [self setStatus:STATUS_START_GAME];
    1845             : 
    1846             :         int i;
    1847             :         for (i = 0; i < PLAYER_MAX_MISSILES; i++)
    1848             :         {
    1849             :                 missile_entity[i] = nil;
    1850             :         }
    1851             :         [self setUpAndConfirmOK:NO];
    1852             :         
    1853             :         save_path = nil;
    1854             :         
    1855             :         scoopsActive = NO;
    1856             :         
    1857             :         target_memory_index = 0;
    1858             :         
    1859             :         DESTROY(dockingReport);
    1860             :         dockingReport = [[NSMutableString alloc] init];
    1861             :         [hud resetGuis:[NSDictionary dictionaryWithObjectsAndKeys:[NSDictionary dictionary], @"message_gui",
    1862             :                                                                                                                 [NSDictionary dictionary], @"comm_log_gui", nil]];
    1863             :         
    1864             :         [self initControls];
    1865             : }
    1866             : 
    1867             : 
    1868             : - (BOOL) setUpAndConfirmOK:(BOOL)stopOnError
    1869             : {
    1870             :         return [self setUpAndConfirmOK:stopOnError saveGame:NO];
    1871             : }
    1872             : 
    1873             : 
    1874             : - (BOOL) setUpAndConfirmOK:(BOOL)stopOnError saveGame:(BOOL)saveGame
    1875             : {
    1876             :         fieldOfView = [[UNIVERSE gameView] fov:YES];
    1877             :         unsigned i;
    1878             :         
    1879             :         showDemoShips = NO;
    1880             :         show_info_flag = NO;
    1881             :         DESTROY(marketSelectedCommodity);
    1882             :         
    1883             :         // Reset JavaScript.
    1884             :         [OOScriptTimer noteGameReset];
    1885             :         [OOScriptTimer updateTimers];
    1886             :         
    1887             :         GameController          *gc = [[UNIVERSE gameView] gameController];
    1888             :         
    1889             :         if (![gc inFullScreenMode] && stopOnError)      [gc stopAnimationTimer];        // start of critical section
    1890             :         
    1891             :         if (EXPECT_NOT(![[OOJavaScriptEngine sharedEngine] reset] && stopOnError)) // always (try to) reset the engine, then find out if we need to stop.
    1892             :         {
    1893             :                 /*
    1894             :                         Occasionally there's a racing condition between timers being deleted
    1895             :                         and the js engine needing to be reset: the engine reset stops the timers
    1896             :                         from being deleted, and undeleted timers don't allow the engine to reset
    1897             :                         itself properly.
    1898             :                         
    1899             :                         If the engine can't reset, let's give ourselves an extra 20ms to allow the
    1900             :                         timers to delete themselves.
    1901             :                         
    1902             :                         We'll piggyback performDeadUpdates: when STATUS_DEAD, the engine waits until 
    1903             :                         kDeadResetTime then restarts Oolite via [UNIVERSE updateGameOver]
    1904             :                         The variable shot_time is used to keep track of how long ago we were
    1905             :                         shot.
    1906             :                         
    1907             :                         If we're loading a savegame the code will try a new JS reset immediately
    1908             :                         after failing this reset...
    1909             :                 */
    1910             :                 
    1911             :                 // set up STATUS_DEAD
    1912             :                 [self setDockedStation:nil];    // needed for STATUS_DEAD
    1913             :                 [self setStatus:STATUS_DEAD];
    1914             :                 OOLog(@"script.javascript.init.error", @"%@", @"Scheduling new JavaScript reset.");
    1915             :                 shot_time = kDeadResetTime - 0.02f;     // schedule reinit 20 milliseconds from now.
    1916             :                 
    1917             :                 if (![gc inFullScreenMode])     [gc startAnimationTimer];       // keep the game ticking over.
    1918             :                 return NO;
    1919             :         }
    1920             :         
    1921             :         // end of critical section
    1922             :         if (![gc inFullScreenMode] && stopOnError)      [gc startAnimationTimer];
    1923             :         
    1924             :         // Load locale script before any regular scripts.
    1925             :         [OOJSScript jsScriptFromFileNamed:@"oolite-locale-functions.js"
    1926             :                                                    properties:nil];
    1927             :         
    1928             :         [[GameController sharedController] logProgress:DESC(@"loading-scripts")];
    1929             :         
    1930             :         [UNIVERSE setBlockJSPlayerShipProps:NO];        // full access to player.ship properties!
    1931             :         DESTROY(worldScripts);
    1932             :         DESTROY(worldScriptsRequiringTickle);
    1933             :         DESTROY(commodityScripts);
    1934             : 
    1935             : #if OOLITE_WINDOWS
    1936             :         if (saveGame)
    1937             :         {
    1938             :                 [UNIVERSE preloadSounds];
    1939             :                 [self setUpSound];
    1940             :                 worldScripts = [[ResourceManager loadScripts] retain];
    1941             :                 [UNIVERSE loadConditionScripts];
    1942             :                 commodityScripts = [[NSMutableDictionary alloc] init];
    1943             :         }
    1944             : #else
    1945             :         /* on OSes that allow safe deletion of open files, can use sounds
    1946             :          * on the OXZ screen and other start screens */
    1947             :         [UNIVERSE preloadSounds];
    1948             :         [self setUpSound];
    1949             :         if (saveGame)
    1950             :         {
    1951             :                 worldScripts = [[ResourceManager loadScripts] retain];
    1952             :                 [UNIVERSE loadConditionScripts];
    1953             :                 commodityScripts = [[NSMutableDictionary alloc] init];
    1954             :         }
    1955             : #endif
    1956             : 
    1957             :         // make sure extraGuiScreenKeys is clear
    1958             :         DESTROY(extraGuiScreenKeys);
    1959             : 
    1960             :         [[GameController sharedController] logProgress:OOExpandKeyRandomized(@"loading-miscellany")];
    1961             :         
    1962             :         // if there is cargo remaining from previously (e.g. a game restart), remove it
    1963             :         if ([self cargoList] != nil)
    1964             :         {
    1965             :                 [self removeAllCargo:YES];              // force removal of cargo
    1966             :         }
    1967             :         
    1968             :         [self setShipDataKey:PLAYER_SHIP_DESC];
    1969             :         ship_trade_in_factor = 95;
    1970             :         
    1971             :         // reset HUD & default commlog behaviour
    1972             :         [UNIVERSE setAutoCommLog:YES];
    1973             :         [UNIVERSE setPermanentCommLog:NO];
    1974             :         
    1975             :         [multiFunctionDisplayText release];
    1976             :         multiFunctionDisplayText = [[NSMutableDictionary alloc] init];
    1977             : 
    1978             :         [multiFunctionDisplaySettings release];
    1979             :         multiFunctionDisplaySettings = [[NSMutableArray alloc] init];
    1980             : 
    1981             :         [customDialSettings release];
    1982             :         customDialSettings = [[NSMutableDictionary alloc] init];
    1983             : 
    1984             :         [self switchHudTo:@"hud.plist"];      
    1985             :         scanner_zoom_rate = 0.0f;
    1986             :         longRangeChartMode = OOLRC_MODE_SUNCOLOR;
    1987             : 
    1988             :         [mission_variables release];
    1989             :         mission_variables = [[NSMutableDictionary alloc] init];
    1990             :         
    1991             :         [localVariables release];
    1992             :         localVariables = [[NSMutableDictionary alloc] init];
    1993             :         
    1994             :         [self setScriptTarget:nil];
    1995             :         [self resetMissionChoice];
    1996             :         [[UNIVERSE gameView] resetTypedString];
    1997             :         found_system_id = -1;
    1998             :         
    1999             :         [reputation release];
    2000             :         reputation = [[NSMutableDictionary alloc] initWithCapacity:6];
    2001             :         [reputation oo_setInteger:0 forKey:CONTRACTS_GOOD_KEY];
    2002             :         [reputation oo_setInteger:0 forKey:CONTRACTS_BAD_KEY];
    2003             :         [reputation oo_setInteger:MAX_CONTRACT_REP forKey:CONTRACTS_UNKNOWN_KEY];
    2004             :         [reputation oo_setInteger:0 forKey:PASSAGE_GOOD_KEY];
    2005             :         [reputation oo_setInteger:0 forKey:PASSAGE_BAD_KEY];
    2006             :         [reputation oo_setInteger:MAX_CONTRACT_REP forKey:PASSAGE_UNKNOWN_KEY];
    2007             :         [reputation oo_setInteger:0 forKey:PARCEL_GOOD_KEY];
    2008             :         [reputation oo_setInteger:0 forKey:PARCEL_BAD_KEY];
    2009             :         [reputation oo_setInteger:MAX_CONTRACT_REP forKey:PARCEL_UNKNOWN_KEY];
    2010             :         
    2011             :         DESTROY(roleWeights);
    2012             :         roleWeights = [[NSMutableArray alloc] initWithCapacity:8];
    2013             :         for (i = 0 ; i < 8 ; i++)
    2014             :         {
    2015             :                 [roleWeights addObject:@"player-unknown"];
    2016             :         }
    2017             :         DESTROY(roleWeightFlags);
    2018             :         roleWeightFlags = [[NSMutableDictionary alloc] init];
    2019             : 
    2020             :         DESTROY(roleSystemList);
    2021             :         roleSystemList = [[NSMutableArray alloc] initWithCapacity:32];
    2022             : 
    2023             :         energy                                  = 256;
    2024             :         weapon_temp                             = 0.0f;
    2025             :         forward_weapon_temp             = 0.0f;
    2026             :         aft_weapon_temp                 = 0.0f;
    2027             :         port_weapon_temp                = 0.0f;
    2028             :         starboard_weapon_temp   = 0.0f;
    2029             :         lastShot = nil;
    2030             :         forward_shot_time               = INITIAL_SHOT_TIME;
    2031             :         aft_shot_time                   = INITIAL_SHOT_TIME;
    2032             :         port_shot_time                  = INITIAL_SHOT_TIME;
    2033             :         starboard_shot_time             = INITIAL_SHOT_TIME;
    2034             :         ship_temperature                = 60.0f;
    2035             :         alertFlags                              = 0;
    2036             :         hyperspeed_engaged              = NO;
    2037             :         autopilot_engaged = NO;
    2038             :         velocity = kZeroVector;
    2039             :         
    2040             :         flightRoll = 0.0f;
    2041             :         flightPitch = 0.0f;
    2042             :         flightYaw = 0.0f;
    2043             : 
    2044             :         max_passengers = 0;
    2045             :         [passengers release];
    2046             :         passengers = [[NSMutableArray alloc] init];
    2047             :         [passenger_record release];
    2048             :         passenger_record = [[NSMutableDictionary alloc] init];
    2049             :         
    2050             :         [contracts release];
    2051             :         contracts = [[NSMutableArray alloc] init];
    2052             :         [contract_record release];
    2053             :         contract_record = [[NSMutableDictionary alloc] init];
    2054             : 
    2055             :         [parcels release];
    2056             :         parcels = [[NSMutableArray alloc] init];
    2057             :         [parcel_record release];
    2058             :         parcel_record = [[NSMutableDictionary alloc] init];
    2059             :         
    2060             :         [missionDestinations release];
    2061             :         missionDestinations = [[NSMutableDictionary alloc] init];
    2062             :         
    2063             :         [shipyard_record release];
    2064             :         shipyard_record = [[NSMutableDictionary alloc] init];
    2065             :         
    2066             :         [target_memory release];
    2067             :         target_memory = [[NSMutableArray alloc] initWithCapacity:PLAYER_TARGET_MEMORY_SIZE];
    2068             :         [self clearTargetMemory]; // also does first-time initialisation
    2069             : 
    2070             :         [self setMissionOverlayDescriptor:nil];
    2071             :         [self setMissionBackgroundDescriptor:nil];
    2072             :         [self setMissionBackgroundSpecial:nil];
    2073             :         [self setEquipScreenBackgroundDescriptor:nil];
    2074             :         marketOffset = 0;
    2075             :         DESTROY(marketSelectedCommodity);
    2076             : 
    2077             :         script_time = 0.0;
    2078             :         script_time_check = SCRIPT_TIMER_INTERVAL;
    2079             :         script_time_interval = SCRIPT_TIMER_INTERVAL;
    2080             :         
    2081             :         NSCalendarDate *nowDate = [NSCalendarDate calendarDate];
    2082             :         ship_clock = PLAYER_SHIP_CLOCK_START;
    2083             :         ship_clock += [nowDate hourOfDay] * 3600.0;
    2084             :         ship_clock += [nowDate minuteOfHour] * 60.0;
    2085             :         ship_clock += [nowDate secondOfMinute];
    2086             :         fps_check_time = ship_clock;
    2087             :         ship_clock_adjust = 0.0;
    2088             :         escape_pod_rescue_time = 0.0;
    2089             : 
    2090             :         isSpeechOn = OOSPEECHSETTINGS_OFF;
    2091             : #if OOLITE_ESPEAK
    2092             :         voice_gender_m = YES;
    2093             :         voice_no = [UNIVERSE setVoice:-1 withGenderM:voice_gender_m];
    2094             : #endif
    2095             :         
    2096             :         [_customViews release];
    2097             :         _customViews = nil;
    2098             :         _customViewIndex = 0;
    2099             :         
    2100             :         mouse_control_on = NO;
    2101             :         
    2102             :         // player commander data
    2103             :         // Most of this is probably also set more than once
    2104             :         
    2105             :         [self setCommanderName:PLAYER_DEFAULT_NAME];
    2106             :         [self setLastsaveName:PLAYER_DEFAULT_NAME];
    2107             :         
    2108             :         galaxy_coordinates              = NSMakePoint(0x14,0xAD);       // 20,173
    2109             : 
    2110             :         credits                                 = 1000;
    2111             :         fuel                                    = PLAYER_MAX_FUEL;
    2112             :         fuel_accumulator                = 0.0f;
    2113             :         fuel_leak_rate                  = 0.0f;
    2114             :         
    2115             :         galaxy_number                   = 0;
    2116             :         // will load real weapon data later
    2117             :         forward_weapon_type             = nil;
    2118             :         aft_weapon_type                 = nil;
    2119             :         port_weapon_type                = nil;
    2120             :         starboard_weapon_type   = nil;
    2121             :         scannerRange = (float)SCANNER_MAX_RANGE; 
    2122             :         
    2123             :         weapons_online                  = YES;
    2124             :         
    2125             :         ecm_in_operation = NO;
    2126             :         last_ecm_time = [UNIVERSE getTime];
    2127             :         compassMode = COMPASS_MODE_BASIC;
    2128             :         ident_engaged = NO;
    2129             :         
    2130             :         max_cargo                               = 20; // will be reset later
    2131             :         marketFilterMode                = MARKET_FILTER_MODE_OFF;
    2132             :         
    2133             :         DESTROY(shipCommodityData);
    2134             :         shipCommodityData = [[[UNIVERSE commodities] generateManifestForPlayer] retain];
    2135             :         
    2136             :         // set up missiles
    2137             :         missiles                                = PLAYER_STARTING_MISSILES;
    2138             :         max_missiles                    = PLAYER_STARTING_MAX_MISSILES;
    2139             :         
    2140             :         [eqScripts release];
    2141             :         eqScripts = [[NSMutableArray alloc] init];
    2142             :         primedEquipment = 0;
    2143             :         [self setFastEquipmentA:@"EQ_CLOAKING_DEVICE"];
    2144             :         [self setFastEquipmentB:@"EQ_ENERGY_BOMB"]; // for compatibility purposes
    2145             : 
    2146             :         [self setActiveMissile:0];
    2147             :         for (i = 0; i < missiles; i++)
    2148             :         {
    2149             :                 [missile_entity[i] release];
    2150             :                 missile_entity[i] = nil;
    2151             :         }
    2152             :         [self safeAllMissiles];
    2153             :         
    2154             :         [self clearSubEntities];
    2155             :         
    2156             :         legalStatus                             = 0;
    2157             :         
    2158             :         market_rnd                              = 0;
    2159             :         ship_kills                              = 0;
    2160             :         chart_centre_coordinates        = galaxy_coordinates;
    2161             :         target_chart_centre             = chart_centre_coordinates;
    2162             :         cursor_coordinates              = galaxy_coordinates;
    2163             :         chart_focus_coordinates         = cursor_coordinates;
    2164             :         target_chart_focus              = chart_focus_coordinates;
    2165             :         chart_zoom                      = 1.0;
    2166             :         target_chart_zoom               = 1.0;
    2167             :         saved_chart_zoom                = 1.0;
    2168             :         ANA_mode                        = OPTIMIZED_BY_NONE;
    2169             : 
    2170             :         
    2171             :         scripted_misjump                = NO;
    2172             :         _scriptedMisjumpRange   = 0.5;
    2173             :         scoopOverride                   = NO;
    2174             :         
    2175             :         max_forward_shield              = BASELINE_SHIELD_LEVEL;
    2176             :         max_aft_shield                  = BASELINE_SHIELD_LEVEL;
    2177             : 
    2178             :         forward_shield_recharge_rate    = 2.0;
    2179             :         aft_shield_recharge_rate                = 2.0;
    2180             : 
    2181             :         forward_shield                  = [self maxForwardShieldLevel];
    2182             :         aft_shield                              = [self maxAftShieldLevel];
    2183             :         
    2184             :         scanClass                               = CLASS_PLAYER;
    2185             :         
    2186             :         [UNIVERSE clearGUIs];
    2187             :         
    2188             :         dockingClearanceStatus = DOCKING_CLEARANCE_STATUS_GRANTED;
    2189             :         targetDockStation = nil;
    2190             :         
    2191             :         [self setDockedStation:[UNIVERSE station]];
    2192             :         
    2193             :         [commLog release];
    2194             :         commLog = nil;
    2195             :         
    2196             :         [specialCargo release];
    2197             :         specialCargo = nil;
    2198             :         
    2199             :         // views
    2200             :         forwardViewOffset               = kZeroVector;
    2201             :         aftViewOffset                   = kZeroVector;
    2202             :         portViewOffset                  = kZeroVector;
    2203             :         starboardViewOffset             = kZeroVector;
    2204             :         customViewOffset                = kZeroVector;
    2205             :         
    2206             :         currentWeaponFacing             = WEAPON_FACING_FORWARD;
    2207             :         [self currentWeaponStats];
    2208             :         
    2209             :         [save_path autorelease];
    2210             :         save_path = nil;
    2211             :         
    2212             :         [scannedWormholes release];
    2213             :         scannedWormholes = [[NSMutableArray alloc] init];
    2214             :         
    2215             :         [self setUpTrumbles];
    2216             :         
    2217             :         suppressTargetLost = NO;
    2218             :         
    2219             :         scoopsActive = NO;
    2220             :         
    2221             :         [dockingReport release];
    2222             :         dockingReport = [[NSMutableString alloc] init];
    2223             :         
    2224             :         [shipAI release];
    2225             :         shipAI = [[AI alloc] initWithStateMachine:PLAYER_DOCKING_AI_NAME andState:@"GLOBAL"];
    2226             :         [self resetAutopilotAI];
    2227             :         
    2228             :         lastScriptAlertCondition = [self alertCondition];
    2229             :         
    2230             :         entity_personality = ranrot_rand() & 0x7FFF;
    2231             :         
    2232             :         [self setSystemID:[UNIVERSE findSystemNumberAtCoords:[self galaxy_coordinates] withGalaxy:galaxy_number includingHidden:YES]];
    2233             :         [UNIVERSE setGalaxyTo:galaxy_number];
    2234             :         [UNIVERSE setSystemTo:system_id];
    2235             : 
    2236             :         [self setUpWeaponSounds];
    2237             :         
    2238             :         [self setGalacticHyperspaceBehaviourTo:[[UNIVERSE globalSettings] oo_stringForKey:@"galactic_hyperspace_behaviour" defaultValue:@"BEHAVIOUR_STANDARD"]];
    2239             :         [self setGalacticHyperspaceFixedCoordsTo:[[UNIVERSE globalSettings] oo_stringForKey:@"galactic_hyperspace_fixed_coords" defaultValue:@"96 96"]];
    2240             :         
    2241             :         cloaking_device_active = NO;
    2242             : 
    2243             :         demoShip = nil;
    2244             :         
    2245             :         [[OOMusicController sharedController] justStop];
    2246             :         [stickProfileScreen release];
    2247             :         stickProfileScreen = [[StickProfileScreen alloc] init];
    2248             :         return YES;
    2249             : }
    2250             : 
    2251             : 
    2252             : - (void) completeSetUp
    2253             : {
    2254             :         [self completeSetUpAndSetTarget:YES];
    2255             : }
    2256             : 
    2257             : 
    2258             : - (void) completeSetUpAndSetTarget:(BOOL)setTarget
    2259             : {
    2260             :         [OOSoundSource stopAll];
    2261             : 
    2262             :         [self setDockedStation:[UNIVERSE station]];
    2263             :         [self setLastAegisLock:[UNIVERSE planet]];
    2264             :         [self validateCustomEquipActivationArray];
    2265             : 
    2266             :         JSContext *context = OOJSAcquireContext();
    2267             :         [self doWorldScriptEvent:OOJSID("startUp") inContext:context withArguments:NULL count:0 timeLimit:MAX(0.0, [[NSUserDefaults standardUserDefaults] oo_floatForKey:@"start-script-limit-value" defaultValue:kOOJSLongTimeLimit])];
    2268             :         OOJSRelinquishContext(context);
    2269             : }
    2270             : 
    2271             : 
    2272             : - (void) startUpComplete
    2273             : {
    2274             :         JSContext *context = OOJSAcquireContext();
    2275             :         [self doWorldScriptEvent:OOJSID("startUpComplete") inContext:context withArguments:NULL count:0 timeLimit:kOOJSLongTimeLimit];
    2276             :         OOJSRelinquishContext(context);
    2277             : }
    2278             : 
    2279             : 
    2280           0 : - (BOOL) setUpShipFromDictionary:(NSDictionary *)shipDict
    2281             : {
    2282             :         DESTROY(compassTarget);
    2283             :         [UNIVERSE setBlockJSPlayerShipProps:NO];        // full access to player.ship properties!
    2284             :         
    2285             :         if (![super setUpFromDictionary:shipDict]) return NO;
    2286             :         
    2287             :         DESTROY(cargo);
    2288             :         cargo = [[NSMutableArray alloc] initWithCapacity:max_cargo];
    2289             : 
    2290             :         // Player-only settings.
    2291             :         //
    2292             :         // set control factors..
    2293             :         roll_delta =            2.0f * max_flight_roll;
    2294             :         pitch_delta =           2.0f * max_flight_pitch;
    2295             :         yaw_delta =                     2.0f * max_flight_yaw;
    2296             :         
    2297             :         energy = maxEnergy;
    2298             :         //if (forward_weapon_type == WEAPON_NONE) [self setWeaponDataFromType:forward_weapon_type]; 
    2299             :         scannerRange = (float)SCANNER_MAX_RANGE; 
    2300             :         
    2301             :         [roleSet release];
    2302             :         roleSet = nil;
    2303             :         [self setPrimaryRole:@"player"];
    2304             :         
    2305             :         [self removeAllEquipment];
    2306             :         [self addEquipmentFromCollection:[shipDict objectForKey:@"extra_equipment"]];
    2307             : 
    2308             :         [self resetHud];
    2309             :         [hud setHidden:NO];
    2310             :         
    2311             :         // set up missiles
    2312             :         // sanity check the number of missiles...
    2313             :         if (max_missiles > PLAYER_MAX_MISSILES)  max_missiles = PLAYER_MAX_MISSILES;
    2314             :         if (missiles > max_missiles)  missiles = max_missiles;
    2315             :         // end sanity check
    2316             : 
    2317             :         unsigned i;
    2318             :         for (i = 0; i < PLAYER_MAX_MISSILES; i++)
    2319             :         {
    2320             :                 [missile_entity[i] release];
    2321             :                 missile_entity[i] = nil;
    2322             :         }
    2323             :         for (i = 0; i < missiles; i++)
    2324             :         {
    2325             :                 missile_list[i] = [OOEquipmentType equipmentTypeWithIdentifier:@"EQ_MISSILE"];
    2326             :                 missile_entity[i] = [UNIVERSE newShipWithRole:@"EQ_MISSILE"];   // retain count = 1
    2327             :         }
    2328             :         
    2329             :         DESTROY(_primaryTarget);
    2330             :         [self safeAllMissiles];
    2331             :         [self setActiveMissile:0];
    2332             :         
    2333             :         // set view offsets
    2334             :         [self setDefaultViewOffsets];
    2335             :         
    2336             :         if (EXPECT(_scaleFactor == 1.0f))
    2337             :         {
    2338             :                 forwardViewOffset = [shipDict oo_vectorForKey:@"view_position_forward" defaultValue:forwardViewOffset];
    2339             :                 aftViewOffset = [shipDict oo_vectorForKey:@"view_position_aft" defaultValue:aftViewOffset];
    2340             :                 portViewOffset = [shipDict oo_vectorForKey:@"view_position_port" defaultValue:portViewOffset];
    2341             :                 starboardViewOffset = [shipDict oo_vectorForKey:@"view_position_starboard" defaultValue:starboardViewOffset];
    2342             :         }
    2343             :         else
    2344             :         {
    2345             :                 forwardViewOffset = vector_multiply_scalar([shipDict oo_vectorForKey:@"view_position_forward" defaultValue:forwardViewOffset],_scaleFactor);
    2346             :                 aftViewOffset = vector_multiply_scalar([shipDict oo_vectorForKey:@"view_position_aft" defaultValue:aftViewOffset],_scaleFactor);
    2347             :                 portViewOffset = vector_multiply_scalar([shipDict oo_vectorForKey:@"view_position_port" defaultValue:portViewOffset],_scaleFactor);
    2348             :                 starboardViewOffset = vector_multiply_scalar([shipDict oo_vectorForKey:@"view_position_starboard" defaultValue:starboardViewOffset],_scaleFactor);
    2349             :         }
    2350             : 
    2351             :         [self setDefaultCustomViews];
    2352             :         
    2353             :         NSArray *customViews = [shipDict oo_arrayForKey:@"custom_views"];
    2354             :         if (customViews != nil)
    2355             :         {
    2356             :                 [_customViews release];
    2357             :                 _customViews = [customViews retain];
    2358             :                 _customViewIndex = 0;
    2359             :         }
    2360             :         
    2361             :         massLockable = [shipDict oo_boolForKey:@"mass_lockable" defaultValue:YES];
    2362             :         
    2363             :         // Load js script
    2364             :         [script autorelease];
    2365             :         NSDictionary *scriptProperties = [NSDictionary dictionaryWithObject:self forKey:@"ship"];
    2366             :         script = [OOScript jsScriptFromFileNamed:[shipDict oo_stringForKey:@"script"] 
    2367             :                                                                                  properties:scriptProperties];
    2368             :         if (script == nil)
    2369             :         {
    2370             :                 // Do not switch to using a default value above; we want to use the default script if loading fails.
    2371             :                 script = [OOScript jsScriptFromFileNamed:@"oolite-default-player-script.js"
    2372             :                                                                                          properties:scriptProperties];
    2373             :         }
    2374             :         [script retain];
    2375             :         
    2376             :         return YES;
    2377             : }
    2378             : 
    2379           0 : - (void) dealloc
    2380             : {
    2381             :         DESTROY(compassTarget);
    2382             :         DESTROY(hud);
    2383             :         DESTROY(multiFunctionDisplayText);
    2384             :         DESTROY(multiFunctionDisplaySettings);
    2385             :         DESTROY(customDialSettings);
    2386             : 
    2387             :         DESTROY(commLog);
    2388             :         DESTROY(keyconfig2_settings);
    2389             :         DESTROY(target_memory);
    2390             :         
    2391             :         DESTROY(_fastEquipmentA);
    2392             :         DESTROY(_fastEquipmentB);
    2393             : 
    2394             :         DESTROY(eqScripts);
    2395             :         DESTROY(worldScripts);
    2396             :         DESTROY(worldScriptsRequiringTickle);
    2397             :         DESTROY(commodityScripts);
    2398             :         DESTROY(mission_variables);
    2399             :         
    2400             :         DESTROY(localVariables);
    2401             :         
    2402             :         DESTROY(lastTextKey);
    2403             :         
    2404             :         DESTROY(marketSelectedCommodity);
    2405             :         DESTROY(reputation);
    2406             :         DESTROY(roleWeights);
    2407             :         DESTROY(roleWeightFlags);
    2408             :         DESTROY(roleSystemList);
    2409             :         DESTROY(passengers);
    2410             :         DESTROY(passenger_record);
    2411             :         DESTROY(contracts);
    2412             :         DESTROY(contract_record);
    2413             :         DESTROY(parcels);
    2414             :         DESTROY(parcel_record);
    2415             :         DESTROY(missionDestinations);
    2416             :         DESTROY(shipyard_record);
    2417             :         
    2418             :         DESTROY(_missionOverlayDescriptor);
    2419             :         DESTROY(_missionBackgroundDescriptor);
    2420             :         DESTROY(_equipScreenBackgroundDescriptor);
    2421             :         
    2422             :         DESTROY(_commanderName);
    2423             :         DESTROY(_lastsaveName);
    2424             :         DESTROY(shipCommodityData);
    2425             :         
    2426             :         DESTROY(specialCargo);
    2427             :         
    2428             :         DESTROY(save_path);
    2429             :         DESTROY(scenarioKey);
    2430             :         
    2431             :         DESTROY(_customViews);
    2432             :         DESTROY(lastShot);
    2433             : 
    2434             :         DESTROY(dockingReport);
    2435             :         
    2436             :         DESTROY(_jumpCause);
    2437             : 
    2438             :         [self destroySound];
    2439             :         
    2440             :         DESTROY(scannedWormholes);
    2441             :         DESTROY(wormhole);
    2442             :         
    2443             :         int i;
    2444             :         for (i = 0; i < PLAYER_MAX_MISSILES; i++)  DESTROY(missile_entity[i]);
    2445             :         for (i = 0; i < PLAYER_MAX_TRUMBLES; i++)  DESTROY(trumble[i]);
    2446             :         
    2447             :         DESTROY(keyShiftText);
    2448             :         DESTROY(keyMod1Text);
    2449             :         DESTROY(keyMod2Text);
    2450             :         DESTROY(stickFunctions);
    2451             :         DESTROY(keyFunctions);
    2452             :         DESTROY(kbdLayouts);
    2453             : 
    2454             :         DESTROY(customEquipActivation);
    2455             :         DESTROY(customActivatePressed);
    2456             :         DESTROY(customModePressed);
    2457             : 
    2458             :         DESTROY(extraGuiScreenKeys);
    2459             : 
    2460             :         [super dealloc];
    2461             : }
    2462             : 
    2463             : 
    2464           0 : - (NSUInteger) sessionID
    2465             : {
    2466             :         // The player ship always belongs to the current session.
    2467             :         return [UNIVERSE sessionID];
    2468             : } 
    2469             : 
    2470             : 
    2471             : - (void) warnAboutHostiles
    2472             : {
    2473             :         [self playHostileWarning];
    2474             : }
    2475             : 
    2476             : 
    2477           0 : - (BOOL) canCollide
    2478             : {
    2479             :         switch ([self status])
    2480             :         {
    2481             :                 case STATUS_START_GAME:
    2482             :                 case STATUS_DOCKING:
    2483             :                 case STATUS_DOCKED:
    2484             :                 case STATUS_DEAD:
    2485             :                 case STATUS_ESCAPE_SEQUENCE:
    2486             :                         return NO;
    2487             :                 
    2488             :                 default:
    2489             :                         return YES;
    2490             :         }
    2491             : }
    2492             : 
    2493             : 
    2494           0 : - (NSComparisonResult) compareZeroDistance:(Entity *)otherEntity
    2495             : {
    2496             :         return NSOrderedDescending;  // always the most near
    2497             : }
    2498             : 
    2499             : 
    2500           0 : - (BOOL) validForAddToUniverse
    2501             : {
    2502             :         return YES;
    2503             : }
    2504             : 
    2505             : 
    2506           0 : - (GLfloat) lookingAtSunWithThresholdAngleCos:(GLfloat) thresholdAngleCos
    2507             : {
    2508             :         OOSunEntity     *sun = [UNIVERSE sun];
    2509             :         GLfloat measuredCos = 999.0f, measuredCosAbs;
    2510             :         GLfloat sunBrightness = 0.0f;
    2511             :         Vector relativePosition, unitRelativePosition;
    2512             :         
    2513             :         if (EXPECT_NOT(!sun))  return 0.0f;
    2514             :         
    2515             :         // check if camera position is shadowed
    2516             :         OOViewID vdir = [UNIVERSE viewDirection];
    2517             :         unsigned i;
    2518             :         unsigned        ent_count =     UNIVERSE->n_entities;
    2519             :         Entity          **uni_entities = UNIVERSE->sortedEntities;   // grab the public sorted list
    2520             :         for (i = 0; i < ent_count; i++)
    2521             :         {
    2522             :                 if (uni_entities[i]->isSunlit)
    2523             :                 {
    2524             :                         if ([uni_entities[i] isPlanet] || 
    2525             :                                 ([uni_entities[i] isShip] &&
    2526             :                                  [uni_entities[i] isVisible]))
    2527             :                         {
    2528             :                                 // the player ship can't shadow internal views
    2529             :                                 if (EXPECT(vdir > VIEW_STARBOARD || ![uni_entities[i] isPlayer]))
    2530             :                                 {
    2531             :                                         float shadow = 1.5f;
    2532             :                                         shadowAtPointOcclusionToValue([self viewpointPosition],1.0f,uni_entities[i],sun,&shadow);
    2533             :                                         /* BUG: if the shadowing entity is not spherical, this gives over-shadowing. True elsewhere as well, but not so obvious there. */
    2534             :                                         if (shadow < 1) {
    2535             :                                                 return 0.0f;
    2536             :                                         }
    2537             :                                 }
    2538             :                         }
    2539             :                 }
    2540             :         }
    2541             : 
    2542             : 
    2543             :         relativePosition = HPVectorToVector(HPvector_subtract([self viewpointPosition], [sun position]));
    2544             :         unitRelativePosition = vector_normal_or_zbasis(relativePosition);
    2545             :         switch (vdir)
    2546             :         {
    2547             :                 case VIEW_FORWARD:
    2548             :                         measuredCos = -dot_product(unitRelativePosition, v_forward);
    2549             :                         break;
    2550             :                 case VIEW_AFT:
    2551             :                         measuredCos = +dot_product(unitRelativePosition, v_forward);
    2552             :                         break;
    2553             :                 case VIEW_PORT:
    2554             :                         measuredCos = +dot_product(unitRelativePosition, v_right);
    2555             :                         break;
    2556             :                 case VIEW_STARBOARD:
    2557             :                         measuredCos = -dot_product(unitRelativePosition, v_right);
    2558             :                         break;
    2559             :                 case VIEW_CUSTOM:
    2560             :                         {
    2561             :                                 Vector relativeView = [self customViewForwardVector];
    2562             :                                 Vector absoluteView = quaternion_rotate_vector(quaternion_conjugate([self orientation]),relativeView);
    2563             :                                 measuredCos = -dot_product(unitRelativePosition, absoluteView);
    2564             :                         }
    2565             :                         break;
    2566             :                         
    2567             :                 default:
    2568             :                         break;
    2569             :         }
    2570             :         measuredCosAbs = fabs(measuredCos);
    2571             :         /*
    2572             :           Bugfix: 1.1f - floating point errors can mean the dot product of two
    2573             :           normalised vectors can be very slightly more than 1, which can
    2574             :           cause extreme flickering of the glare at certain ranges to the
    2575             :           sun. The real test is just that it's not still 999 - CIM
    2576             :          */
    2577             :         if (thresholdAngleCos <= measuredCosAbs && measuredCosAbs <= 1.1f)        // angle from viewpoint to sun <= desired threshold
    2578             :         {
    2579             :                 sunBrightness =  (measuredCos - thresholdAngleCos) / (1.0f - thresholdAngleCos);
    2580             : //              OOLog(@"glare.debug",@"raw brightness = %f",sunBrightness);
    2581             :                 if (sunBrightness < 0.0f)  sunBrightness = 0.0f;
    2582             :                 else if (sunBrightness > 1.0f)  sunBrightness = 1.0f;
    2583             :         }
    2584             : //      OOLog(@"glare.debug",@"cos=%f, threshold = %f, brightness = %f",measuredCosAbs,thresholdAngleCos,sunBrightness);
    2585             :         return sunBrightness * sunBrightness * sunBrightness;
    2586             : }
    2587             : 
    2588             : 
    2589             : - (GLfloat) insideAtmosphereFraction
    2590             : {
    2591             :         GLfloat insideAtmoFrac = 0.0f;
    2592             :         
    2593             :         if ([UNIVERSE airResistanceFactor] > 0.01)  // player is inside planetary atmosphere
    2594             :         {
    2595             :                 insideAtmoFrac = 1.0f - ([self dialAltitude] *  (GLfloat)PLAYER_DIAL_MAX_ALTITUDE / (10.0f * (GLfloat)ATMOSPHERE_DEPTH));
    2596             :         }
    2597             :         
    2598             :         return insideAtmoFrac;
    2599             : }
    2600             : 
    2601             : 
    2602             : #ifndef NDEBUG
    2603           0 : #define STAGE_TRACKING_BEGIN    { \
    2604             :                                                                         NSString * volatile updateStage = @"initialisation"; \
    2605             :                                                                         @try {
    2606           0 : #define STAGE_TRACKING_END                      } \
    2607             :                                                                         @catch (NSException *exception) \
    2608             :                                                                         { \
    2609             :                                                                                 OOLog(kOOLogException, @"***** Exception during [%@] in %s : %@ : %@ *****", updateStage, __PRETTY_FUNCTION__, [exception name], [exception reason]); \
    2610             :                                                                                 @throw exception; \
    2611             :                                                                         } \
    2612             :                                                                 }
    2613           0 : #define UPDATE_STAGE(x) do { updateStage = (x); } while (0)
    2614             : #else
    2615             : #define STAGE_TRACKING_BEGIN    {
    2616             : #define STAGE_TRACKING_END              }
    2617             : #define UPDATE_STAGE(x) do { (void) (x); } while (0);
    2618             : #endif
    2619             : 
    2620             : 
    2621           0 : - (void) update:(OOTimeDelta)delta_t
    2622             : {
    2623             :         STAGE_TRACKING_BEGIN
    2624             :         
    2625             :         UPDATE_STAGE(@"updateMovementFlags");
    2626             :         [self updateMovementFlags];
    2627             :         UPDATE_STAGE(@"updateAlertCondition");
    2628             :         [self updateAlertCondition];
    2629             :         UPDATE_STAGE(@"updateFuelScoops:");
    2630             :         [self updateFuelScoops:delta_t];
    2631             :         
    2632             :         UPDATE_STAGE(@"updateClocks:");
    2633             :         [self updateClocks:delta_t];
    2634             :         
    2635             :         // scripting
    2636             :         UPDATE_STAGE(@"updateTimers");
    2637             :         [OOScriptTimer updateTimers];
    2638             :         UPDATE_STAGE(@"checkScriptsIfAppropriate");
    2639             :         [self checkScriptsIfAppropriate];
    2640             :         
    2641             :         // deal with collisions
    2642             :         UPDATE_STAGE(@"manageCollisions");
    2643             :         [self manageCollisions];
    2644             :         
    2645             :         UPDATE_STAGE(@"pollControls:");
    2646             :         [self pollControls:delta_t];
    2647             :         
    2648             :         UPDATE_STAGE(@"updateTrumbles:");
    2649             :         [self updateTrumbles:delta_t];
    2650             :         
    2651             :         OOEntityStatus status = [self status];
    2652             :         /* Validate that if the status is STATUS_START_GAME we're on one
    2653             :          * of the few GUI screens which that makes sense for */
    2654             :         if (EXPECT_NOT(status == STATUS_START_GAME && 
    2655             :                                    gui_screen != GUI_SCREEN_INTRO1 && 
    2656             :                                    gui_screen != GUI_SCREEN_SHIPLIBRARY && 
    2657             :                                    gui_screen != GUI_SCREEN_GAMEOPTIONS && 
    2658             :                                    gui_screen != GUI_SCREEN_STICKMAPPER && 
    2659             :                                    gui_screen != GUI_SCREEN_STICKPROFILE && 
    2660             :                                    gui_screen != GUI_SCREEN_NEWGAME && 
    2661             :                                    gui_screen != GUI_SCREEN_OXZMANAGER && 
    2662             :                                    gui_screen != GUI_SCREEN_LOAD && 
    2663             :                                    gui_screen != GUI_SCREEN_KEYBOARD && 
    2664             :                                    gui_screen != GUI_SCREEN_KEYBOARD_CONFIRMCLEAR &&
    2665             :                                    gui_screen != GUI_SCREEN_KEYBOARD_CONFIG &&
    2666             :                                    gui_screen != GUI_SCREEN_KEYBOARD_ENTRY &&
    2667             :                                    gui_screen != GUI_SCREEN_KEYBOARD_LAYOUT))
    2668             :         {
    2669             :                 // and if not, do a restart of the GUI
    2670             :                 UPDATE_STAGE(@"setGuiToIntroFirstGo:");
    2671             :                 [self setGuiToIntroFirstGo:YES];        //set up demo mode
    2672             :         }
    2673             :         
    2674             :         if (status == STATUS_AUTOPILOT_ENGAGED || status == STATUS_ESCAPE_SEQUENCE)
    2675             :         {
    2676             :                 UPDATE_STAGE(@"performAutopilotUpdates:");
    2677             :                 [self performAutopilotUpdates:delta_t];
    2678             :         }
    2679             :         else  if (![self isDocked])
    2680             :         {
    2681             :                 UPDATE_STAGE(@"performInFlightUpdates:");
    2682             :                 [self performInFlightUpdates:delta_t];
    2683             :         }
    2684             :         
    2685             :         /*      NOTE: status-contingent updates are not a switch since they can
    2686             :                 cascade when status changes.
    2687             :         */
    2688             :         if (status == STATUS_IN_FLIGHT)
    2689             :         {
    2690             :                 UPDATE_STAGE(@"doBookkeeping:");
    2691             :                 [self doBookkeeping:delta_t];
    2692             :         }
    2693             :         if (status == STATUS_WITCHSPACE_COUNTDOWN)
    2694             :         {
    2695             :                 UPDATE_STAGE(@"performWitchspaceCountdownUpdates:");
    2696             :                 [self performWitchspaceCountdownUpdates:delta_t];
    2697             :         }
    2698             :         if (status == STATUS_EXITING_WITCHSPACE)
    2699             :         {
    2700             :                 UPDATE_STAGE(@"performWitchspaceExitUpdates:");
    2701             :                 [self performWitchspaceExitUpdates:delta_t];
    2702             :         }
    2703             :         if (status == STATUS_LAUNCHING)
    2704             :         {
    2705             :                 UPDATE_STAGE(@"performLaunchingUpdates:");
    2706             :                 [self performLaunchingUpdates:delta_t];
    2707             :         }
    2708             :         if (status == STATUS_DOCKING)
    2709             :         {
    2710             :                 UPDATE_STAGE(@"performDockingUpdates:");
    2711             :                 [self performDockingUpdates:delta_t];
    2712             :         }
    2713             :         if (status == STATUS_DEAD)
    2714             :         {
    2715             :                 UPDATE_STAGE(@"performDeadUpdates:");
    2716             :                 [self performDeadUpdates:delta_t];
    2717             :         }
    2718             :         
    2719             :         UPDATE_STAGE(@"updateWormholes");
    2720             :         [self updateWormholes];
    2721             :         
    2722             :         STAGE_TRACKING_END
    2723             : }
    2724             : 
    2725             : 
    2726             : - (void) doBookkeeping:(double) delta_t
    2727             : {
    2728             :         STAGE_TRACKING_BEGIN
    2729             :         
    2730             :         double speed_delta = SHIP_THRUST_FACTOR * thrust;
    2731             : 
    2732             :         static BOOL             gettingInterference = NO;
    2733             :         
    2734             :         OOSunEntity     *sun = [UNIVERSE sun];
    2735             :         double          external_temp = 0;
    2736             :         GLfloat         air_friction = 0.0f;
    2737             :         air_friction = 0.5f * [UNIVERSE airResistanceFactor];
    2738             :         if (air_friction < 0.005f) // aRF < 0.01
    2739             :         {
    2740             :                 // stops mysteriously overheating and exploding in the middle of empty space
    2741             :                 air_friction = 0;
    2742             :         }
    2743             : 
    2744             :         UPDATE_STAGE(@"updating weapon temperatures and shot times");
    2745             :         // cool all weapons.
    2746             :         float coolAmount = WEAPON_COOLING_FACTOR * delta_t;
    2747             :         forward_weapon_temp = fdim(forward_weapon_temp, coolAmount);
    2748             :         aft_weapon_temp = fdim(aft_weapon_temp, coolAmount);
    2749             :         port_weapon_temp = fdim(port_weapon_temp, coolAmount);
    2750             :         starboard_weapon_temp = fdim(starboard_weapon_temp, coolAmount);
    2751             :         
    2752             :         // update shot times.
    2753             :         forward_shot_time += delta_t;
    2754             :         aft_shot_time += delta_t;
    2755             :         port_shot_time += delta_t;
    2756             :         starboard_shot_time += delta_t;
    2757             :                 
    2758             :         // copy new temp & shot time to main temp & shot time
    2759             :         switch (currentWeaponFacing)
    2760             :         {
    2761             :                 case WEAPON_FACING_FORWARD:
    2762             :                         weapon_temp = forward_weapon_temp;
    2763             :                         shot_time = forward_shot_time;
    2764             :                         break;
    2765             :                 case WEAPON_FACING_AFT:
    2766             :                         weapon_temp = aft_weapon_temp;
    2767             :                         shot_time = aft_shot_time;
    2768             :                         break;
    2769             :                 case WEAPON_FACING_PORT:
    2770             :                         weapon_temp = port_weapon_temp;
    2771             :                         shot_time = port_shot_time;
    2772             :                         break;
    2773             :                 case WEAPON_FACING_STARBOARD:
    2774             :                         weapon_temp = starboard_weapon_temp;
    2775             :                         shot_time = starboard_shot_time;
    2776             :                         break;
    2777             :                         
    2778             :                 case WEAPON_FACING_NONE:
    2779             :                         break;
    2780             :         }
    2781             :         
    2782             :         // cloaking device
    2783             :         if ([self hasCloakingDevice] && cloaking_device_active)
    2784             :         {
    2785             :                 UPDATE_STAGE(@"updating cloaking device");
    2786             :                 
    2787             :                 energy -= (float)delta_t * CLOAKING_DEVICE_ENERGY_RATE;
    2788             :                 if (energy < CLOAKING_DEVICE_MIN_ENERGY)
    2789             :                         [self deactivateCloakingDevice];
    2790             :         }
    2791             :         
    2792             :         // military_jammer
    2793             :         if ([self hasMilitaryJammer])
    2794             :         {
    2795             :                 UPDATE_STAGE(@"updating military jammer");
    2796             :                 
    2797             :                 if (military_jammer_active)
    2798             :                 {
    2799             :                         energy -= (float)delta_t * MILITARY_JAMMER_ENERGY_RATE;
    2800             :                         if (energy < MILITARY_JAMMER_MIN_ENERGY)
    2801             :                                 military_jammer_active = NO;
    2802             :                 }
    2803             :                 else
    2804             :                 {
    2805             :                         if (energy > 1.5 * MILITARY_JAMMER_MIN_ENERGY)
    2806             :                                 military_jammer_active = YES;
    2807             :                 }
    2808             :         }
    2809             :         
    2810             :         // ecm
    2811             :         if (ecm_in_operation)
    2812             :         {
    2813             :                 UPDATE_STAGE(@"updating ECM");
    2814             :                 
    2815             :                 if (energy > 0.0)
    2816             :                         energy -= (float)(ECM_ENERGY_DRAIN_FACTOR * delta_t);           // drain energy because of the ECM
    2817             :                 else
    2818             :                 {
    2819             :                         ecm_in_operation = NO;
    2820             :                         [UNIVERSE addMessage:DESC(@"ecm-out-of-juice") forCount:3.0];
    2821             :                 }
    2822             :                 if ([UNIVERSE getTime] > ecm_start_time + ECM_DURATION)
    2823             :                 {
    2824             :                         ecm_in_operation = NO;
    2825             :                 }
    2826             :         }
    2827             : 
    2828             :         // ecm interference visual effect
    2829             :         if ([UNIVERSE useShaders] && [UNIVERSE ECMVisualFXEnabled])
    2830             :         {
    2831             :                 // we want to start and stop the effect exactly once, not start it
    2832             :                 // or stop it on every frame
    2833             :                 if ([self scannerFuzziness] > 0.0)
    2834             :                 {
    2835             :                         if (!gettingInterference)
    2836             :                         {
    2837             :                                 [UNIVERSE setCurrentPostFX:OO_POSTFX_CRTBADSIGNAL];
    2838             :                                 gettingInterference = YES;
    2839             :                         }
    2840             :                 }
    2841             :                 else
    2842             :                 {
    2843             :                         if (gettingInterference)
    2844             :                         {
    2845             :                                 [UNIVERSE terminatePostFX:OO_POSTFX_CRTBADSIGNAL];
    2846             :                                 gettingInterference = NO;
    2847             :                         }
    2848             :                 }       
    2849             :         }
    2850             :         
    2851             :         // Energy Banks and Shields
    2852             :         
    2853             :         /* Shield-charging behaviour, as per Eric's proposal:
    2854             :            1. If shields are less than a threshold, recharge with all available energy
    2855             :            2. If energy banks are below threshold, recharge with generated energy
    2856             :            3. Charge shields with any surplus energy
    2857             :         */
    2858             :         UPDATE_STAGE(@"updating energy and shield charges");
    2859             :         
    2860             :         // 1. (Over)charge energy banks (will get normalised later)
    2861             :         energy += [self energyRechargeRate] * delta_t;
    2862             :         
    2863             :         // 2. Calculate shield recharge rates
    2864             :         float fwdMax = [self maxForwardShieldLevel];
    2865             :         float aftMax = [self maxAftShieldLevel];
    2866             :         float shieldRechargeFwd = [self forwardShieldRechargeRate] * delta_t;
    2867             :         float shieldRechargeAft = [self aftShieldRechargeRate] * delta_t;
    2868             :         /* there is potential for negative rechargeFwd and rechargeAFt values here
    2869             :            (e.g. getting shield boosters damaged while shields are full). This may
    2870             :            lead to energy being gained rather than consumed when recharging. Leaving
    2871             :            as-is for now, as there might be OXPs that rely in such behaviour.
    2872             :            Boosters case example mentioned above is the only known core equipment
    2873             :            occurrence at this time and it has been fixed inside the
    2874             :            oolite-equipment-control.js script. - Nikos 20160104.
    2875             :          */
    2876             :         float rechargeFwd = MIN(shieldRechargeFwd, fwdMax - forward_shield);
    2877             :         float rechargeAft = MIN(shieldRechargeAft, aftMax - aft_shield);
    2878             :         
    2879             :         // Note: we've simplified this a little, so if either shield is below
    2880             :         //       the critical threshold, we allocate all energy.  Ideally we
    2881             :         //       would only allocate the full recharge to the critical shield,
    2882             :         //       but doing so would add another few levels of if-then below.
    2883             :         float energyForShields = energy;
    2884             :         if( (forward_shield > fwdMax * 0.25) && (aft_shield > aftMax * 0.25) )
    2885             :         {
    2886             :                 // TODO: Can this be cached anywhere sensibly (without adding another member variable)?
    2887             :                 float minEnergyBankLevel = [[UNIVERSE globalSettings] oo_floatForKey:@"shield_charge_energybank_threshold" defaultValue:0.25];
    2888             :                 energyForShields = MAX(0.0, energy -0.1 - (maxEnergy * minEnergyBankLevel)); // NB: The - 0.1 ensures the energy value does not 'bounce' across the critical energy message and causes spurious energy-low warnings
    2889             :         }
    2890             :         
    2891             :         if( forward_shield < aft_shield )
    2892             :         {
    2893             :                 rechargeFwd = MIN(rechargeFwd, energyForShields);
    2894             :                 rechargeAft = MIN(rechargeAft, energyForShields - rechargeFwd);
    2895             :         }
    2896             :         else
    2897             :         {
    2898             :                 rechargeAft = MIN(rechargeAft, energyForShields);
    2899             :                 rechargeFwd = MIN(rechargeFwd, energyForShields - rechargeAft);
    2900             :         }
    2901             :         
    2902             :         // 3. Recharge shields, drain banks, and clamp values
    2903             :         forward_shield += rechargeFwd;
    2904             :         aft_shield += rechargeAft;
    2905             :         energy -= rechargeFwd + rechargeAft;
    2906             :         
    2907             :         forward_shield = OOClamp_0_max_f(forward_shield, fwdMax);
    2908             :         aft_shield = OOClamp_0_max_f(aft_shield, aftMax);
    2909             :         energy = OOClamp_0_max_f(energy, maxEnergy);
    2910             :         
    2911             :         if (sun)
    2912             :         {
    2913             :                 UPDATE_STAGE(@"updating sun effects");
    2914             :                 
    2915             :                 // set the ambient temperature here
    2916             :                 double  sun_zd = sun->zero_distance; // square of distance
    2917             :                 double  sun_cr = sun->collision_radius;
    2918             :                 double  alt1 = sun_cr * sun_cr / sun_zd;
    2919             :                 external_temp = SUN_TEMPERATURE * alt1;
    2920             : 
    2921             :                 if ([sun goneNova])
    2922             :                         external_temp *= 100;
    2923             :                 // fuel scooping during the nova mission very unlikely
    2924             :                 if ([sun willGoNova])
    2925             :                         external_temp *= 3;
    2926             :                         
    2927             :                 // do Revised sun-skimming check here...
    2928             :                 if ([self hasFuelScoop] && alt1 > 0.75 && [self fuel] < [self fuelCapacity])
    2929             :                 {
    2930             :                         fuel_accumulator += (float)(delta_t * flightSpeed * 0.010 / [self fuelChargeRate]);
    2931             :                         // are we fast enough to collect any fuel?
    2932             :                         scoopsActive = YES && flightSpeed > 0.1f;
    2933             :                         while (fuel_accumulator > 1.0f)
    2934             :                         {
    2935             :                                 [self setFuel:[self fuel] + 1];
    2936             :                                 fuel_accumulator -= 1.0f;
    2937             :                                 [self doScriptEvent:OOJSID("shipScoopedFuel")];
    2938             :                         }
    2939             :                         [UNIVERSE displayCountdownMessage:DESC(@"fuel-scoop-active") forCount:1.0];
    2940             :                 }
    2941             :         }
    2942             :         
    2943             :         //Bug #11692 CmdrJames added Status entering witchspace
    2944             :         OOEntityStatus status = [self status];
    2945             :         if ((status != STATUS_ESCAPE_SEQUENCE) && (status != STATUS_ENTERING_WITCHSPACE))
    2946             :         {
    2947             :                 UPDATE_STAGE(@"updating cabin temperature");
    2948             :                 
    2949             :                 // work on the cabin temperature
    2950             :                 float heatInsulation = [self heatInsulation]; // Optimisation, suggested by EricW
    2951             :                 float deltaInsulation = delta_t/heatInsulation;
    2952             :                 float heatThreshold = heatInsulation * 100.0f;
    2953             :                 ship_temperature += (float)( flightSpeed * air_friction * deltaInsulation);     // wind_speed
    2954             :                 
    2955             :                 if (external_temp > heatThreshold && external_temp > ship_temperature)
    2956             :                         ship_temperature += (float)((external_temp - ship_temperature) * SHIP_INSULATION_FACTOR  * deltaInsulation);
    2957             :                 else
    2958             :                 {
    2959             :                         if (ship_temperature > SHIP_MIN_CABIN_TEMP)
    2960             :                                 ship_temperature += (float)((external_temp - heatThreshold - ship_temperature) * SHIP_COOLING_FACTOR  * deltaInsulation);
    2961             :                 }
    2962             :                 
    2963             :                 if (ship_temperature > SHIP_MAX_CABIN_TEMP)
    2964             :                         [self takeHeatDamage: delta_t * ship_temperature];
    2965             :         }
    2966             :         
    2967             :         if ((status == STATUS_ESCAPE_SEQUENCE)&&(shot_time > ESCAPE_SEQUENCE_TIME))
    2968             :         {
    2969             :                 UPDATE_STAGE(@"resetting after escape");
    2970             :                 ShipEntity      *doppelganger = (ShipEntity*)[self foundTarget];
    2971             :                 // reset legal status again! Could have changed if a previously launched missile hit a clean NPC while in the escape pod.
    2972             :                 [self setBounty:0 withReason:kOOLegalStatusReasonEscapePod];
    2973             :                 bounty = 0;
    2974             :                 thrust = max_thrust; // re-enable inertialess drives
    2975             :                 // no access to all player.ship properties while inside the escape pod,
    2976             :                 // we're not supposed to be inside our ship anymore! 
    2977             :                 [self doScriptEvent:OOJSID("escapePodSequenceOver")]; // allow oxps to override the escape pod target
    2978             :                 /**
    2979             :                  * This code branch doesn't seem to be used any more - see ~line 6000
    2980             :                  * Should we remove it? - CIM
    2981             :                  */
    2982             :                 if (EXPECT_NOT(target_system_id != system_id)) // overridden: we're going to a nearby system!
    2983             :                 {
    2984             :                         system_id = target_system_id;
    2985             :                         info_system_id = target_system_id;
    2986             :                         [UNIVERSE setSystemTo:system_id];
    2987             :                         galaxy_coordinates = PointFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:system_id inGalaxy:galaxy_number]);
    2988             :                         
    2989             :                         [UNIVERSE setUpSpace];
    2990             :                         // run initial system population
    2991             :                         [UNIVERSE populateNormalSpace];
    2992             : 
    2993             :                         [self setDockTarget:[UNIVERSE station]];
    2994             :                         // send world script events to let oxps know we're in a new system.
    2995             :                         // all player.ship properties are still disabled at this stage.
    2996             :                         [UNIVERSE setWitchspaceBreakPattern:YES];
    2997             :                         [self doScriptEvent:OOJSID("shipWillExitWitchspace")];
    2998             :                         [self doScriptEvent:OOJSID("shipExitedWitchspace")];
    2999             :                         
    3000             :                         [[UNIVERSE planet] update: 2.34375 * market_rnd];       // from 0..10 minutes
    3001             :                         [[UNIVERSE station] update: 2.34375 * market_rnd];      // from 0..10 minutes
    3002             :                 }
    3003             :                 
    3004             :                 Entity  *dockTargetEntity = [UNIVERSE entityForUniversalID:_dockTarget];        // main station in the original system, unless overridden.
    3005             :                 if ([dockTargetEntity isStation]) // fails if _dockTarget is NO_TARGET
    3006             :                 {
    3007             :                         [doppelganger becomeExplosion]; // blow up the doppelganger
    3008             :                         // restore player ship
    3009             :                         ShipEntity *player_ship = [UNIVERSE newShipWithName:[self shipDataKey]];        // retained
    3010             :                         if (player_ship)
    3011             :                         {
    3012             :                                 // FIXME: this should use OOShipType, which should exist. -- Ahruman
    3013             :                                 [self setMesh:[player_ship mesh]];
    3014             :                                 [player_ship release];                                          // we only wanted it for its polygons!
    3015             :                         }
    3016             :                         [UNIVERSE setViewDirection:VIEW_FORWARD];
    3017             :                         [UNIVERSE setBlockJSPlayerShipProps:NO];        // re-enable player.ship!
    3018             :                         [self enterDock:(StationEntity *)dockTargetEntity];
    3019             :                 }
    3020             :                 else    // no dock target? dock target is not a station? game over!
    3021             :                 {
    3022             :                         [self setStatus:STATUS_DEAD];
    3023             :                         //[self playGameOver];  // no death explosion sounds for player pods
    3024             :                         // no shipDied events for player pods, either
    3025             :                         [UNIVERSE displayMessage:DESC(@"gameoverscreen-escape-pod") forCount:kDeadResetTime];
    3026             :                         [UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
    3027             :                         [self showGameOver];
    3028             :                 }
    3029             :         }
    3030             :         
    3031             :         
    3032             :         // MOVED THE FOLLOWING FROM PLAYERENTITY POLLFLIGHTCONTROLS:
    3033             :         travelling_at_hyperspeed = (flightSpeed > maxFlightSpeed);
    3034             :         if (hyperspeed_engaged)
    3035             :         {
    3036             :                 UPDATE_STAGE(@"updating hyperspeed");
    3037             :                 
    3038             :                 // increase speed up to maximum hyperspeed
    3039             :                 if (flightSpeed < maxFlightSpeed * HYPERSPEED_FACTOR)
    3040             :                         flightSpeed += (float)(speed_delta * delta_t * HYPERSPEED_FACTOR);
    3041             :                 if (flightSpeed > maxFlightSpeed * HYPERSPEED_FACTOR)
    3042             :                         flightSpeed = (float)(maxFlightSpeed * HYPERSPEED_FACTOR);
    3043             :                 
    3044             :                 // check for mass lock
    3045             :                 hyperspeed_locked = [self massLocked];
    3046             :                 // check for mass lock & external temperature?
    3047             :                 //hyperspeed_locked = flightSpeed * air_friction > 40.0f+(ship_temperature - external_temp ) * SHIP_COOLING_FACTOR || [self massLocked];
    3048             :                 
    3049             :                 if (hyperspeed_locked)
    3050             :                 {
    3051             :                         [self playJumpMassLocked];
    3052             :                         [UNIVERSE addMessage:DESC(@"jump-mass-locked") forCount:4.5];
    3053             :                         hyperspeed_engaged = NO;
    3054             :                 }
    3055             :         }
    3056             :         else
    3057             :         {
    3058             :                 if (afterburner_engaged)
    3059             :                 {
    3060             :                         UPDATE_STAGE(@"updating afterburner");
    3061             :                         
    3062             :                         float abFactor = [self afterburnerFactor];
    3063             :                         float maxInjectionSpeed = maxFlightSpeed * abFactor;
    3064             :                         if (flightSpeed > maxInjectionSpeed)
    3065             :                         {
    3066             :                                 // decellerate to maxInjectionSpeed but slower than without afterburner.
    3067             :                                 flightSpeed -= (float)(speed_delta * delta_t * abFactor);
    3068             :                         }
    3069             :                         else
    3070             :                         {
    3071             :                                 if (flightSpeed < maxInjectionSpeed)
    3072             :                                         flightSpeed += (float)(speed_delta * delta_t * abFactor);
    3073             :                                 if (flightSpeed > maxInjectionSpeed)
    3074             :                                         flightSpeed = maxInjectionSpeed;
    3075             :                         }
    3076             :                         fuel_accumulator -= (float)(delta_t * afterburner_rate);
    3077             :                         while ((fuel_accumulator < 0)&&(fuel > 0))
    3078             :                         {
    3079             :                                 fuel_accumulator += 1.0f;
    3080             :                                 if (--fuel <= MIN_FUEL)
    3081             :                                         afterburner_engaged = NO;
    3082             :                         }
    3083             :                 }
    3084             :                 else
    3085             :                 {
    3086             :                         UPDATE_STAGE(@"slowing from hyperspeed");
    3087             :                         
    3088             :                         // slow back down...
    3089             :                         if (travelling_at_hyperspeed)
    3090             :                         {
    3091             :                                 // decrease speed to maximum normal speed
    3092             :                                 float deceleration = (speed_delta * delta_t * HYPERSPEED_FACTOR);
    3093             :                                 if (alertFlags & ALERT_FLAG_MASS_LOCK)
    3094             :                                 {
    3095             :                                         // decelerate much quicker in masslocks
    3096             :                                         // this does also apply to injector deceleration
    3097             :                                         // but it's not very noticeable
    3098             :                                         deceleration *= 3;
    3099             :                                 }
    3100             :                                 flightSpeed -= deceleration;
    3101             :                                 if (flightSpeed < maxFlightSpeed)
    3102             :                                         flightSpeed = maxFlightSpeed;
    3103             :                         }
    3104             :                 }
    3105             :         }
    3106             :         
    3107             :         
    3108             :         
    3109             :         // fuel leakage
    3110             :         if ((fuel_leak_rate > 0.0)&&(fuel > 0))
    3111             :         {
    3112             :                 UPDATE_STAGE(@"updating fuel leakage");
    3113             :                 
    3114             :                 fuel_accumulator -= (float)(fuel_leak_rate * delta_t);
    3115             :                 while ((fuel_accumulator < 0)&&(fuel > 0))
    3116             :                 {
    3117             :                         fuel_accumulator += 1.0f;
    3118             :                         fuel--;
    3119             :                 }
    3120             :                 if (fuel == 0)
    3121             :                         fuel_leak_rate = 0;
    3122             :         }
    3123             :         
    3124             :         // smart_zoom
    3125             :         UPDATE_STAGE(@"updating scanner zoom");
    3126             :         if (scanner_zoom_rate)
    3127             :         {
    3128             :                 double z = [hud scannerZoom];
    3129             :                 double z1 = z + scanner_zoom_rate * delta_t;
    3130             :                 if (scanner_zoom_rate > 0.0)
    3131             :                 {
    3132             :                         if (floor(z1) > floor(z))
    3133             :                         {
    3134             :                                 z1 = floor(z1);
    3135             :                                 scanner_zoom_rate = 0.0f;
    3136             :                         }
    3137             :                 }
    3138             :                 else
    3139             :                 {
    3140             :                         if (z1 < 1.0)
    3141             :                         {
    3142             :                                 z1 = 1.0;
    3143             :                                 scanner_zoom_rate = 0.0f;
    3144             :                         }
    3145             :                 }
    3146             :                 [hud setScannerZoom:z1];
    3147             :         }
    3148             : 
    3149             :         [[UNIVERSE gameView] setFov:fieldOfView fromFraction:YES];
    3150             :         
    3151             :         // scanner sanity check - lose any targets further than maximum scanner range
    3152             :         ShipEntity *primeTarget = [self primaryTarget];
    3153             :         if (primeTarget && HPdistance2([primeTarget position], [self position]) > SCANNER_MAX_RANGE2 && !autopilot_engaged)
    3154             :         {
    3155             :                 [UNIVERSE addMessage:DESC(@"target-lost") forCount:3.0];
    3156             :                 [self removeTarget:primeTarget];
    3157             :         }
    3158             :         // compass sanity check and update target for changed mode
    3159             :         [self validateCompassTarget];
    3160             :         
    3161             :         // update subentities
    3162             :         UPDATE_STAGE(@"updating subentities");
    3163             :         totalBoundingBox = boundingBox; //      reset totalBoundingBox
    3164             :         ShipEntity *se = nil;
    3165             :         foreach (se, [self subEntities])
    3166             :         {
    3167             :                 [se update:delta_t];
    3168             :                 if ([se isShip])
    3169             :                 {
    3170             :                         BoundingBox sebb = [se findSubentityBoundingBox];
    3171             :                         bounding_box_add_vector(&totalBoundingBox, sebb.max);
    3172             :                         bounding_box_add_vector(&totalBoundingBox, sebb.min);
    3173             :                 }
    3174             :         }
    3175             :         // and one thing which isn't a subentity. Fixes bug with
    3176             :         // mispositioned laser beams particularly noticeable on side view.
    3177             :         if (lastShot != nil)
    3178             :         {
    3179             :                 OOLaserShotEntity *lse = nil;
    3180             :                 foreach (lse, lastShot)
    3181             :                 {
    3182             :                         [lse update:0.0];
    3183             :                 }
    3184             :                 DESTROY(lastShot);
    3185             :         }
    3186             :         
    3187             :         // update mousewheel status
    3188             :         UPDATE_STAGE(@"updating mousewheel delta");
    3189             :         MyOpenGLView *gView = [UNIVERSE gameView];
    3190             :         float mouseWheelDelta = [gView mouseWheelDelta];
    3191             :         if (mouseWheelDelta > 0.0f)
    3192             :         {
    3193             :                 if (mouseWheelDelta < delta_t)  [gView setMouseWheelDelta:0.0f];
    3194             :                 else  [gView setMouseWheelDelta:mouseWheelDelta - delta_t];
    3195             :         }
    3196             :         else if (mouseWheelDelta < 0.0f)
    3197             :         {
    3198             :                 if (mouseWheelDelta > -delta_t)  [gView setMouseWheelDelta:0.0f];
    3199             :                 else  [gView setMouseWheelDelta:mouseWheelDelta + delta_t];
    3200             :         }
    3201             :         
    3202             :         STAGE_TRACKING_END
    3203             : }
    3204             : 
    3205             : 
    3206           0 : - (void) updateMovementFlags
    3207             : {
    3208             :         hasMoved = !HPvector_equal(position, lastPosition);
    3209             :         hasRotated = !quaternion_equal(orientation, lastOrientation);
    3210             :         lastPosition = position;
    3211             :         lastOrientation = orientation;
    3212             : }
    3213             : 
    3214             : 
    3215           0 : - (void) updateAlertConditionForNearbyEntities
    3216             : {
    3217             :         if (![self isInSpace] || [self status] == STATUS_DOCKING)
    3218             :         {
    3219             :                 [self clearAlertFlags];
    3220             :                 // not needed while docked
    3221             :                 return;
    3222             :         }
    3223             : 
    3224             :         int                             i, ent_count    = UNIVERSE->n_entities;
    3225             :         Entity                  **uni_entities  = UNIVERSE->sortedEntities;  // grab the public sorted list
    3226             :         Entity                  *my_entities[ent_count];
    3227             :         Entity                  *scannedEntity = nil;
    3228             :         for (i = 0; i < ent_count; i++)
    3229             :         {
    3230             :                 my_entities[i] = [uni_entities[i] retain];      // retained
    3231             :         }
    3232             :         BOOL massLocked = NO;
    3233             :         BOOL foundHostiles = NO;
    3234             : #if OO_VARIABLE_TORUS_SPEED
    3235             :         BOOL needHyperspeedNearest = YES;
    3236             :         double hsnDistance = 0;
    3237             : #endif
    3238             :         for (i = 0; i < ent_count; i++)  // scanner lollypops
    3239             :         {
    3240             :                 scannedEntity = my_entities[i];
    3241             : 
    3242             : #if OO_VARIABLE_TORUS_SPEED
    3243             :                 if (EXPECT_NOT(needHyperspeedNearest))
    3244             :                 {
    3245             :                         // not visual effects, waypoints, ships, etc.
    3246             :                         if (scannedEntity != self && [scannedEntity canCollide] && (![scannedEntity isShip] || ![self collisionExceptedFor:(ShipEntity *) scannedEntity]))
    3247             :                         {
    3248             :                                 hsnDistance = sqrt(scannedEntity->zero_distance)-[scannedEntity collisionRadius];
    3249             :                                 needHyperspeedNearest = NO;
    3250             :                         }
    3251             :                 } 
    3252             :                 else if ([scannedEntity isStellarObject])
    3253             :                 {
    3254             :                         // planets, stars might be closest surface even if not
    3255             :                         // closest centre. That could be true of others, but the
    3256             :                         // error is negligible there.
    3257             :                         double thisHSN = sqrt(scannedEntity->zero_distance)-[scannedEntity collisionRadius];
    3258             :                         if (thisHSN < hsnDistance)
    3259             :                         {
    3260             :                                 hsnDistance = thisHSN;
    3261             :                         }
    3262             :                 }
    3263             : #endif
    3264             :                 
    3265             :                 if (scannedEntity->zero_distance < SCANNER_MAX_RANGE2 || !scannedEntity->isShip)
    3266             :                 {
    3267             :                         int theirClass = [scannedEntity scanClass];
    3268             :                         // here we could also force masslock for higher than yellow alert, but
    3269             :                         // if we are going to hand over masslock control to scripting, might as well
    3270             :                         // hand it over fully
    3271             :                         if ([self massLockable] /*|| alertFlags > ALERT_FLAG_YELLOW_LIMIT*/)
    3272             :                         {
    3273             :                                 massLocked |= [self checkEntityForMassLock:scannedEntity withScanClass:theirClass];     // we just need one masslocker..
    3274             :                         }
    3275             :                         if (theirClass != CLASS_NO_DRAW)
    3276             :                         {
    3277             :                                 if (theirClass == CLASS_THARGOID || [scannedEntity isCascadeWeapon])
    3278             :                                 {
    3279             :                                         foundHostiles = YES;
    3280             :                                 }
    3281             :                                 else if ([scannedEntity isShip])
    3282             :                                 {
    3283             :                                         ShipEntity *ship = (ShipEntity *)scannedEntity;
    3284             :                                         foundHostiles |= (([ship hasHostileTarget])&&([ship primaryTarget] == self));
    3285             :                                 }
    3286             :                         }
    3287             :                 }
    3288             :         }
    3289             : #if OO_VARIABLE_TORUS_SPEED
    3290             :         if (EXPECT_NOT(needHyperspeedNearest))
    3291             :         {
    3292             :                 // this case should only occur in an otherwise empty
    3293             :                 // interstellar space - unlikely but possible
    3294             :                 hyperspeedFactor = MIN_HYPERSPEED_FACTOR;
    3295             :         }
    3296             :         else
    3297             :         {
    3298             :                 // once nearest object is >4x scanner range
    3299             :                 // start increasing torus speed
    3300             :                 double factor = hsnDistance/(4*SCANNER_MAX_RANGE);
    3301             :                 if (factor < 1.0)
    3302             :                 {
    3303             :                         hyperspeedFactor = MIN_HYPERSPEED_FACTOR;
    3304             :                 }
    3305             :                 else
    3306             :                 {
    3307             :                         hyperspeedFactor = MIN_HYPERSPEED_FACTOR * sqrt(factor);
    3308             :                         if (hyperspeedFactor > MAX_HYPERSPEED_FACTOR)
    3309             :                         {
    3310             :                                 // caps out at ~10^8m from nearest object
    3311             :                                 // which takes ~10 minutes of flying
    3312             :                                 hyperspeedFactor = MAX_HYPERSPEED_FACTOR;
    3313             :                         }
    3314             :                 }
    3315             :         }
    3316             : #endif
    3317             : 
    3318             :         [self setAlertFlag:ALERT_FLAG_MASS_LOCK to:massLocked];
    3319             :                 
    3320             :         [self setAlertFlag:ALERT_FLAG_HOSTILES to:foundHostiles];
    3321             : 
    3322             :         for (i = 0; i < ent_count; i++)
    3323             :         {
    3324             :                 [my_entities[i] release];       //      released
    3325             :         }
    3326             :         
    3327             :         BOOL energyCritical = NO;
    3328             :         if (energy < 64 && energy < maxEnergy * 0.8)
    3329             :         {
    3330             :                 energyCritical = YES;
    3331             :         }
    3332             :         [self setAlertFlag:ALERT_FLAG_ENERGY to:energyCritical];
    3333             : 
    3334             :         [self setAlertFlag:ALERT_FLAG_TEMP to:([self hullHeatLevel] > .90)];
    3335             : 
    3336             :         [self setAlertFlag:ALERT_FLAG_ALT to:([self dialAltitude] < .10)];
    3337             : 
    3338             : }
    3339             : 
    3340             : 
    3341           0 : - (void) setMaxFlightPitch:(GLfloat)new
    3342             : {
    3343             :         max_flight_pitch = new;
    3344             :         pitch_delta = 2.0 * new;
    3345             : }
    3346             : 
    3347             : 
    3348           0 : - (void) setMaxFlightRoll:(GLfloat)new
    3349             : {
    3350             :         max_flight_roll = new;
    3351             :         roll_delta = 2.0 * new;
    3352             : }
    3353             : 
    3354             : 
    3355           0 : - (void) setMaxFlightYaw:(GLfloat)new
    3356             : {
    3357             :         max_flight_yaw = new;
    3358             :         yaw_delta = 2.0 * new;
    3359             : }
    3360             : 
    3361             : 
    3362           0 : - (BOOL) checkEntityForMassLock:(Entity *)ent withScanClass:(int)theirClass
    3363             : {
    3364             :         BOOL massLocked = NO;
    3365             :         BOOL entIsCloakedShip = [ent isShip] && [(ShipEntity *)ent isCloaked];
    3366             :         
    3367             :         if (EXPECT_NOT([ent isStellarObject]))
    3368             :         {
    3369             :                 Entity<OOStellarBody> *stellar = (Entity<OOStellarBody> *)ent;
    3370             :                 if (EXPECT([stellar planetType] != STELLAR_TYPE_MINIATURE))
    3371             :                 {
    3372             :                         double dist = stellar->zero_distance;
    3373             :                         double rad = stellar->collision_radius;
    3374             :                         double factor = ([stellar isSun]) ? 2.0 : 4.0;
    3375             :                         // plus ensure mass lock when 25 km or less from the surface of small stellar bodies
    3376             :                         // dist is a square distance so it needs to be compared to (rad+25000) * (rad+25000)!
    3377             :                         if (dist < rad*rad*factor || dist < rad*rad + 50000*rad + 625000000 ) 
    3378             :                         {
    3379             :                                 massLocked = YES;
    3380             :                         }
    3381             :                 }
    3382             :         }
    3383             :         else if (theirClass != CLASS_NO_DRAW)
    3384             :         {
    3385             :                 if (EXPECT_NOT (entIsCloakedShip))
    3386             :                 {
    3387             :                         theirClass = CLASS_NO_DRAW;
    3388             :                 }
    3389             :         }
    3390             : 
    3391             :         if (!massLocked && ent->zero_distance <= SCANNER_MAX_RANGE2)
    3392             :         {
    3393             :                 switch (theirClass)
    3394             :                 {
    3395             :                         case CLASS_NO_DRAW:
    3396             :                                 // cloaked ships do mass lock! - Nikos 20200718
    3397             :                                 if (entIsCloakedShip && ![ent isPlayer])
    3398             :                                 {
    3399             :                                         massLocked = YES;
    3400             :                                 }
    3401             :                                 break;
    3402             :                         case CLASS_PLAYER:
    3403             :                         case CLASS_BUOY:
    3404             :                         case CLASS_ROCK:
    3405             :                         case CLASS_CARGO:
    3406             :                         case CLASS_MINE:
    3407             :                         case CLASS_VISUAL_EFFECT:
    3408             :                                 break;
    3409             :                                 
    3410             :                         case CLASS_THARGOID:
    3411             :                         case CLASS_MISSILE:
    3412             :                         case CLASS_STATION:
    3413             :                         case CLASS_POLICE:
    3414             :                         case CLASS_MILITARY:
    3415             :                         case CLASS_WORMHOLE:
    3416             :                         default:
    3417             :                                 massLocked = YES;
    3418             :                                 break;
    3419             :                 }
    3420             :         }
    3421             :         
    3422             :         return massLocked;
    3423             : }
    3424             : 
    3425             : 
    3426           0 : - (void) updateAlertCondition
    3427             : {
    3428             :         [self updateAlertConditionForNearbyEntities];
    3429             :         /*      TODO: update alert condition once per frame. Tried this before, but
    3430             :                 there turned out to be complications. See mailing list archive.
    3431             :                 -- Ahruman 20070802
    3432             :          */
    3433             :         OOAlertCondition cond = [self alertCondition];
    3434             :         OOTimeAbsolute t = [UNIVERSE getTime];
    3435             :         if (cond != lastScriptAlertCondition)
    3436             :         {
    3437             :                 ShipScriptEventNoCx(self, "alertConditionChanged", INT_TO_JSVAL(cond), INT_TO_JSVAL(lastScriptAlertCondition));
    3438             :                 lastScriptAlertCondition = cond;
    3439             :         }
    3440             :         /* Update heuristic assessment of whether player is fleeing */
    3441             :         if (cond == ALERT_CONDITION_DOCKED || cond == ALERT_CONDITION_GREEN || (cond == ALERT_CONDITION_YELLOW && energy == maxEnergy))
    3442             :         {
    3443             :                 fleeing_status = PLAYER_FLEEING_NONE;
    3444             :         }
    3445             :         else if (fleeing_status == PLAYER_FLEEING_UNLIKELY && (energy > maxEnergy*0.6 || cond != ALERT_CONDITION_RED))
    3446             :         {
    3447             :                 fleeing_status = PLAYER_FLEEING_NONE;
    3448             :         }
    3449             :         else if ((fleeing_status == PLAYER_FLEEING_MAYBE || fleeing_status == PLAYER_FLEEING_UNLIKELY) && cargo_dump_time > last_shot_time)
    3450             :         {
    3451             :                 fleeing_status = PLAYER_FLEEING_CARGO;
    3452             :         }
    3453             :         else if (fleeing_status == PLAYER_FLEEING_MAYBE && last_shot_time + 10 > t)
    3454             :         {
    3455             :                 fleeing_status = PLAYER_FLEEING_NONE;
    3456             :         }
    3457             :         else if (fleeing_status == PLAYER_FLEEING_LIKELY && last_shot_time + 10 > t)
    3458             :         {
    3459             :                 fleeing_status = PLAYER_FLEEING_UNLIKELY;
    3460             :         }
    3461             :         else if (fleeing_status == PLAYER_FLEEING_NONE && cond == ALERT_CONDITION_RED && last_shot_time + 10 < t && flightSpeed > 0.75*maxFlightSpeed)
    3462             :         {
    3463             :                 fleeing_status = PLAYER_FLEEING_MAYBE;
    3464             :         }
    3465             :         else if ((fleeing_status == PLAYER_FLEEING_MAYBE || fleeing_status == PLAYER_FLEEING_CARGO) && cond == ALERT_CONDITION_RED && last_shot_time + 10 < t && flightSpeed > 0.75*maxFlightSpeed && energy < maxEnergy * 0.5 && (forward_shield < [self maxForwardShieldLevel]*0.25 || aft_shield < [self maxAftShieldLevel]*0.25))
    3466             :         {
    3467             :                 fleeing_status = PLAYER_FLEEING_LIKELY;
    3468             :         }
    3469             : }
    3470             : 
    3471             : 
    3472           0 : - (void) updateFuelScoops:(OOTimeDelta)delta_t
    3473             : {
    3474             :         if (scoopsActive)
    3475             :         {
    3476             :                 [self updateFuelScoopSoundWithInterval:delta_t];
    3477             :                 if (![self scoopOverride])
    3478             :                 {
    3479             :                         scoopsActive = NO;
    3480             :                         [self updateFuelScoopSoundWithInterval:delta_t];
    3481             :                 }
    3482             :         }
    3483             : }
    3484             : 
    3485             : 
    3486           0 : - (void) updateClocks:(OOTimeDelta)delta_t
    3487             : {
    3488             :         // shot time updates are still needed here for STATUS_DEAD!
    3489             :         shot_time += delta_t;
    3490             :         script_time += delta_t;
    3491             :         unsigned prev_day = floor(ship_clock / 86400);
    3492             :         ship_clock += delta_t;
    3493             :         if (ship_clock_adjust > 0.0)                         // adjust for coming out of warp (add LY * LY hrs)
    3494             :         {
    3495             :                 double fine_adjust = delta_t * 7200.0;
    3496             :                 if (ship_clock_adjust > 86400)                       // more than a day
    3497             :                         fine_adjust = delta_t * 115200.0;       // 16 times faster
    3498             :                 if (ship_clock_adjust > 0)
    3499             :                 {
    3500             :                         if (fine_adjust > ship_clock_adjust)
    3501             :                                 fine_adjust = ship_clock_adjust;
    3502             :                         ship_clock += fine_adjust;
    3503             :                         ship_clock_adjust -= fine_adjust;
    3504             :                 }
    3505             :                 else
    3506             :                 {
    3507             :                         if (fine_adjust < ship_clock_adjust)
    3508             :                                 fine_adjust = ship_clock_adjust;
    3509             :                         ship_clock -= fine_adjust;
    3510             :                         ship_clock_adjust += fine_adjust;
    3511             :                 }
    3512             :         }
    3513             :         else 
    3514             :                 ship_clock_adjust = 0.0;
    3515             :         
    3516             :         unsigned now_day = floor(ship_clock / 86400.0);
    3517             :         while (prev_day < now_day)
    3518             :         {
    3519             :                 prev_day++;
    3520             :                 [self doScriptEvent:OOJSID("dayChanged") withArgument:[NSNumber numberWithUnsignedInt:prev_day]];
    3521             :                 // not impossible that at ultra-low frame rates two of these will
    3522             :                 // happen in a single update.
    3523             :         }
    3524             : 
    3525             :         //fps
    3526             :         if (ship_clock > fps_check_time)
    3527             :         {
    3528             :                 if (![self clockAdjusting])
    3529             :                 {
    3530             :                         fps_counter = (int)([UNIVERSE timeAccelerationFactor] * floor([UNIVERSE framesDoneThisUpdate] / (fps_check_time - last_fps_check_time)));
    3531             :                         last_fps_check_time = fps_check_time;
    3532             :                         fps_check_time = ship_clock + MINIMUM_GAME_TICK;
    3533             :                 }
    3534             :                 else
    3535             :                 {
    3536             :                         // Good approximation for when the clock is adjusting and proper fps calculation
    3537             :                         // cannot be performed.
    3538             :                         fps_counter = (int)([UNIVERSE timeAccelerationFactor] * floor(1.0 / delta_t));
    3539             :                         fps_check_time = ship_clock + MINIMUM_GAME_TICK;
    3540             :                 }
    3541             :                 [UNIVERSE resetFramesDoneThisUpdate];   // Reset frame counter
    3542             :         }
    3543             : }
    3544             : 
    3545             : 
    3546           0 : - (void) checkScriptsIfAppropriate
    3547             : {
    3548             :         if (script_time <= script_time_check)  return;
    3549             :         
    3550             :         if ([self status] != STATUS_IN_FLIGHT)
    3551             :         {
    3552             :                 switch (gui_screen)
    3553             :                 {
    3554             :                         // Screens where no world script tickles are performed
    3555             :                         case GUI_SCREEN_MAIN:
    3556             :                         case GUI_SCREEN_INTRO1:
    3557             :                         case GUI_SCREEN_SHIPLIBRARY:
    3558             :                         case GUI_SCREEN_KEYBOARD:
    3559             :                         case GUI_SCREEN_NEWGAME:
    3560             :                         case GUI_SCREEN_OXZMANAGER:
    3561             :                         case GUI_SCREEN_MARKET:
    3562             :                         case GUI_SCREEN_MARKETINFO:
    3563             :                         case GUI_SCREEN_OPTIONS:
    3564             :                         case GUI_SCREEN_GAMEOPTIONS:
    3565             :                         case GUI_SCREEN_LOAD:
    3566             :                         case GUI_SCREEN_SAVE:
    3567             :                         case GUI_SCREEN_SAVE_OVERWRITE:
    3568             :                         case GUI_SCREEN_STICKMAPPER:
    3569             :                         case GUI_SCREEN_STICKPROFILE:
    3570             :                         case GUI_SCREEN_MISSION:
    3571             :                         case GUI_SCREEN_REPORT:
    3572             :                         case GUI_SCREEN_KEYBOARD_CONFIRMCLEAR:
    3573             :                         case GUI_SCREEN_KEYBOARD_CONFIG:
    3574             :                         case GUI_SCREEN_KEYBOARD_ENTRY:
    3575             :                         case GUI_SCREEN_KEYBOARD_LAYOUT:
    3576             :                                 return;
    3577             :                         
    3578             :                         // Screens from which it's safe to jump to the mission screen
    3579             : //                      case GUI_SCREEN_CONTRACTS:
    3580             :                         case GUI_SCREEN_EQUIP_SHIP:
    3581             :                         case GUI_SCREEN_INTERFACES:
    3582             :                         case GUI_SCREEN_MANIFEST:
    3583             :                         case GUI_SCREEN_SHIPYARD:
    3584             :                         case GUI_SCREEN_LONG_RANGE_CHART:
    3585             :                         case GUI_SCREEN_SHORT_RANGE_CHART:
    3586             :                         case GUI_SCREEN_STATUS:
    3587             :                         case GUI_SCREEN_SYSTEM_DATA:
    3588             :                                 // Test passed, we can run scripts. Nothing to do here.
    3589             :                                 break;
    3590             :                 }
    3591             :         }
    3592             :         
    3593             :         // Test either passed or never ran, run scripts.
    3594             :         [self checkScript];
    3595             :         script_time_check += script_time_interval;
    3596             : }
    3597             : 
    3598             : 
    3599           0 : - (void) updateTrumbles:(OOTimeDelta)delta_t
    3600             : {
    3601             :         OOTrumble       **trumbles = [self trumbleArray];
    3602             :         NSUInteger      i;
    3603             :         
    3604             :         for (i = [self trumbleCount] ; i > 0; i--)
    3605             :         {
    3606             :                 OOTrumble* trum = trumbles[i - 1];
    3607             :                 [trum updateTrumble:delta_t];
    3608             :         }
    3609             : }
    3610             : 
    3611             : 
    3612           0 : - (void) performAutopilotUpdates:(OOTimeDelta)delta_t
    3613             : {
    3614             :         [self processBehaviour:delta_t];
    3615             :         [self applyVelocity:delta_t];
    3616             :         [self doBookkeeping:delta_t];
    3617             : }
    3618             : 
    3619             : - (void) performDockingRequest:(StationEntity *)stationForDocking
    3620             : {
    3621             :         if (stationForDocking == nil) return;
    3622             :         if (![stationForDocking isStation] || ![stationForDocking isKindOfClass:[StationEntity class]]) return;
    3623             :         if ([self isDocked])  return;
    3624             :         if (autopilot_engaged && [self targetStation] == stationForDocking)     return;
    3625             :         if (autopilot_engaged && [self targetStation] != stationForDocking)
    3626             :         {
    3627             :                 [self disengageAutopilot];
    3628             :         }
    3629             :         NSString *stationDockingClearanceStatus = [stationForDocking acceptDockingClearanceRequestFrom:self];
    3630             :         if (stationDockingClearanceStatus != nil)
    3631             :         {
    3632             :                 [self doScriptEvent:OOJSID("playerRequestedDockingClearance") withArgument:stationDockingClearanceStatus];
    3633             :                 if ([stationDockingClearanceStatus isEqualToString:@"DOCKING_CLEARANCE_GRANTED"]) 
    3634             :                 {
    3635             :                         [self doScriptEvent:OOJSID("playerDockingClearanceGranted")];
    3636             :                 }
    3637             :         } 
    3638             : }
    3639             : 
    3640             : - (void) requestDockingClearance:(StationEntity *)stationForDocking
    3641             : {
    3642             :         if (dockingClearanceStatus != DOCKING_CLEARANCE_STATUS_REQUESTED && dockingClearanceStatus != DOCKING_CLEARANCE_STATUS_GRANTED)
    3643             :         {
    3644             :                 [self performDockingRequest:stationForDocking];
    3645             :         }
    3646             : }
    3647             : 
    3648             : - (void) cancelDockingRequest:(StationEntity *)stationForDocking
    3649             : {
    3650             :         if (stationForDocking == nil) return;
    3651             :         if (![stationForDocking isStation] || ![stationForDocking isKindOfClass:[StationEntity class]]) return;
    3652             :         if ([self isDocked])  return;
    3653             :         if (autopilot_engaged && [self targetStation] == stationForDocking)     return;
    3654             :         if (autopilot_engaged && [self targetStation] != stationForDocking)
    3655             :         {
    3656             :                 [self disengageAutopilot];
    3657             :         }
    3658             :         if (dockingClearanceStatus == DOCKING_CLEARANCE_STATUS_GRANTED || dockingClearanceStatus == DOCKING_CLEARANCE_STATUS_REQUESTED)
    3659             :         {
    3660             :                 NSString *stationDockingClearanceStatus = [stationForDocking acceptDockingClearanceRequestFrom:self];
    3661             :                 if (stationDockingClearanceStatus != nil && [stationDockingClearanceStatus isEqualToString:@"DOCKING_CLEARANCE_CANCELLED"])
    3662             :                 {
    3663             :                         [self doScriptEvent:OOJSID("playerDockingClearanceCancelled")];
    3664             :                 } 
    3665             :         }
    3666             : }
    3667             : 
    3668             : - (BOOL) engageAutopilotToStation:(StationEntity *)stationForDocking
    3669             : {
    3670             :         if (stationForDocking == nil)   return NO;
    3671             :         if ([self isDocked])  return NO;
    3672             :         
    3673             :         if (autopilot_engaged && [self targetStation] == stationForDocking)
    3674             :         {       
    3675             :                 return YES;
    3676             :         }
    3677             :                 
    3678             :         [self setTargetStation:stationForDocking];
    3679             :         DESTROY(_primaryTarget);
    3680             :         autopilot_engaged = YES;
    3681             :         ident_engaged = NO;
    3682             :         [self safeAllMissiles];
    3683             :         velocity = kZeroVector;
    3684             :         if ([self status] == STATUS_WITCHSPACE_COUNTDOWN) [self cancelWitchspaceCountdown]; // cancel witchspace countdown properly
    3685             :         [self setStatus:STATUS_AUTOPILOT_ENGAGED];
    3686             :         [self resetAutopilotAI];
    3687             :         [shipAI setState:@"BEGIN_DOCKING"];   // reboot the AI
    3688             :         [self playAutopilotOn];
    3689             :         [[OOMusicController sharedController] playDockingMusic];
    3690             :         [self doScriptEvent:OOJSID("playerStartedAutoPilot") withArgument:stationForDocking];
    3691             :         [self setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_GRANTED];
    3692             :                 
    3693             :         if (afterburner_engaged)
    3694             :         {
    3695             :                 afterburner_engaged = NO;
    3696             :                 if (afterburnerSoundLooping)  [self stopAfterburnerSound];
    3697             :         }
    3698             :         return YES;
    3699             : }
    3700             : 
    3701             : 
    3702             : 
    3703             : - (void) disengageAutopilot
    3704             : {
    3705             :         if (autopilot_engaged)
    3706             :         {
    3707             :                 [self abortDocking];                    // let the station know that you are no longer on approach
    3708             :                 behaviour = BEHAVIOUR_IDLE;
    3709             :                 frustration = 0.0;
    3710             :                 autopilot_engaged = NO;
    3711             :                 DESTROY(_primaryTarget);
    3712             :                 [self setTargetStation:nil];
    3713             :                 [self setStatus:STATUS_IN_FLIGHT];
    3714             :                 [self playAutopilotOff];
    3715             :                 [self setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
    3716             :                 [[OOMusicController sharedController] stopDockingMusic];
    3717             :                 [self doScriptEvent:OOJSID("playerCancelledAutoPilot")];
    3718             :                 
    3719             :                 [self resetAutopilotAI];
    3720             :         }
    3721             : }
    3722             : 
    3723             : 
    3724             : - (void) resetAutopilotAI
    3725             : {
    3726             :         AI *myAI = [self getAI];
    3727             :         // JSAI: will need changing if oolite-dockingAI.js written
    3728             :         if (![[myAI name] isEqualToString:PLAYER_DOCKING_AI_NAME])
    3729             :         {
    3730             :                 [self setAITo:PLAYER_DOCKING_AI_NAME ];
    3731             :         }
    3732             :         [myAI clearAllData];
    3733             :         [myAI setState:@"GLOBAL"];
    3734             :         [myAI setNextThinkTime:[UNIVERSE getTime] + 2];
    3735             :         [myAI setOwner:self];
    3736             : }
    3737             : 
    3738             : 
    3739           0 : #define VELOCITY_CLEANUP_MIN    2000.0f // Minimum speed for "power braking".
    3740           0 : #define VELOCITY_CLEANUP_FULL   5000.0f // Speed at which full "power braking" factor is used.
    3741           0 : #define VELOCITY_CLEANUP_RATE   0.001f  // Factor for full "power braking".
    3742             : 
    3743             : 
    3744             : #if OO_VARIABLE_TORUS_SPEED
    3745             : - (GLfloat) hyperspeedFactor
    3746             : {
    3747             :         return hyperspeedFactor;
    3748             : }
    3749             : #endif
    3750             : 
    3751             : 
    3752             : - (BOOL) injectorsEngaged
    3753             : {
    3754             :         return afterburner_engaged;
    3755             : }
    3756             : 
    3757             : 
    3758             : - (BOOL) hyperspeedEngaged
    3759             : {
    3760             :         return hyperspeed_engaged;
    3761             : }
    3762             : 
    3763             : 
    3764           0 : - (void) performInFlightUpdates:(OOTimeDelta)delta_t
    3765             : {
    3766             :         STAGE_TRACKING_BEGIN
    3767             :         
    3768             :         // do flight routines
    3769             :         //// velocity stuff
    3770             :         UPDATE_STAGE(@"applying newtonian drift");
    3771             :         assert(VELOCITY_CLEANUP_FULL > VELOCITY_CLEANUP_MIN);
    3772             :         
    3773             :         [self applyVelocity:delta_t];
    3774             :         
    3775             :         GLfloat thrust_factor = 1.0;
    3776             :         if (flightSpeed > maxFlightSpeed)
    3777             :         {
    3778             :                 if (afterburner_engaged)
    3779             :                 {
    3780             :                         thrust_factor = [self afterburnerFactor];
    3781             :                 }
    3782             :                 else
    3783             :                 {
    3784             :                         thrust_factor = HYPERSPEED_FACTOR;
    3785             :                 }
    3786             :         }
    3787             :         
    3788             : 
    3789             :         GLfloat velmag = magnitude(velocity);
    3790             :         GLfloat velmag2 = velmag - (float)delta_t * thrust * thrust_factor;
    3791             :         if (velmag > 0)
    3792             :         {
    3793             :                 UPDATE_STAGE(@"applying power braking");
    3794             :                 
    3795             :                 if (velmag > VELOCITY_CLEANUP_MIN)
    3796             :                 {
    3797             :                         GLfloat rate;
    3798             :                         // Fix up extremely ridiculous speeds that can happen in collisions or explosions
    3799             :                         if (velmag > VELOCITY_CLEANUP_FULL)  rate = VELOCITY_CLEANUP_RATE;
    3800             :                         else  rate = (velmag - VELOCITY_CLEANUP_MIN) / (VELOCITY_CLEANUP_FULL - VELOCITY_CLEANUP_MIN) * VELOCITY_CLEANUP_RATE;
    3801             :                         velmag2 -= velmag * rate;
    3802             :                 }
    3803             :                 if (velmag2 < 0.0f)  velocity = kZeroVector;
    3804             :                 else  velocity = vector_multiply_scalar(velocity, velmag2 / velmag);
    3805             :                 
    3806             :         }
    3807             :         
    3808             :         UPDATE_STAGE(@"updating joystick");
    3809             :         [self applyRoll:(float)delta_t*flightRoll andClimb:(float)delta_t*flightPitch];
    3810             :         if (flightYaw != 0.0)
    3811             :         {
    3812             :                 [self applyYaw:(float)delta_t*flightYaw];
    3813             :         }
    3814             :         
    3815             :         UPDATE_STAGE(@"applying para-newtonian thrust");
    3816             :         [self moveForward:delta_t*flightSpeed];
    3817             :         
    3818             :         UPDATE_STAGE(@"updating targeting");
    3819             :         [self updateTargeting];
    3820             :         
    3821             :         STAGE_TRACKING_END
    3822             : }
    3823             : 
    3824             : 
    3825           0 : - (void) performWitchspaceCountdownUpdates:(OOTimeDelta)delta_t
    3826             : {
    3827             :         STAGE_TRACKING_BEGIN
    3828             :         
    3829             :         UPDATE_STAGE(@"doing bookkeeping");
    3830             :         [self doBookkeeping:delta_t];
    3831             :         
    3832             :         UPDATE_STAGE(@"updating countdown timer");
    3833             :         witchspaceCountdown = fdim(witchspaceCountdown, delta_t);
    3834             :         
    3835             :         // damaged gal drive? abort!
    3836             :         /* TODO: this check should possibly be hasEquipmentItemProviding:,
    3837             :          * but if it was we'd need to know which item was actually doing
    3838             :          * the providing so it could be removed. */
    3839             :         if (EXPECT_NOT(galactic_witchjump && ![self hasEquipmentItem:@"EQ_GAL_DRIVE"]))
    3840             :         {
    3841             :                 galactic_witchjump = NO;
    3842             :                 [self setStatus:STATUS_IN_FLIGHT];
    3843             :                 [self playHyperspaceAborted];
    3844             :                 ShipScriptEventNoCx(self, "playerJumpFailed", OOJSSTR("malfunction"));
    3845             :                 return;
    3846             :         }
    3847             :         
    3848             :         int seconds = round(witchspaceCountdown);
    3849             :         if (galactic_witchjump)
    3850             :         {
    3851             :                 [UNIVERSE displayCountdownMessage:OOExpandKey(@"witch-galactic-in-x-seconds", seconds) forCount:1.0];
    3852             :         }
    3853             :         else
    3854             :         {
    3855             :                 NSString *destination = [UNIVERSE getSystemName:[self nextHopTargetSystemID]];
    3856             :                 [UNIVERSE displayCountdownMessage:OOExpandKey(@"witch-to-x-in-y-seconds", seconds, destination) forCount:1.0];
    3857             :         }
    3858             :         
    3859             :         if (witchspaceCountdown == 0.0)
    3860             :         {
    3861             :                 UPDATE_STAGE(@"preloading planet textures");
    3862             :                 if (!galactic_witchjump)
    3863             :                 {
    3864             :                         /*      Note: planet texture preloading is done twice for hyperspace jumps:
    3865             :                                 once when starting the countdown and once at the beginning of the
    3866             :                                 jump. The reason is that the preloading may have been skipped the
    3867             :                                 first time because of rate limiting (see notes at
    3868             :                                 -preloadPlanetTexturesForSystem:). There is no significant overhead
    3869             :                                 from doing it twice thanks to the texture cache.
    3870             :                                 -- Ahruman 2009-12-19
    3871             :                         */
    3872             :                         [UNIVERSE preloadPlanetTexturesForSystem:target_system_id];
    3873             :                 }
    3874             :                 else
    3875             :                 {
    3876             :                         // FIXME: preload target system for galactic jump?
    3877             :                 }
    3878             : 
    3879             :                 UPDATE_STAGE(@"JUMP!");
    3880             :                 if (galactic_witchjump)  [self enterGalacticWitchspace];
    3881             :                 else  [self enterWitchspace];
    3882             :                 galactic_witchjump = NO;
    3883             :         }
    3884             :         
    3885             :         STAGE_TRACKING_END
    3886             : }
    3887             : 
    3888             : 
    3889           0 : - (void) performWitchspaceExitUpdates:(OOTimeDelta)delta_t
    3890             : {
    3891             :         if ([UNIVERSE breakPatternOver])
    3892             :         {
    3893             :                 [self resetExhaustPlumes];
    3894             :                 // time to check the script!
    3895             :                 [self checkScript];
    3896             :                 // next check in 10s
    3897             :                 [self resetScriptTimer];        // reset the in-system timer
    3898             :                 
    3899             :                 // announce arrival
    3900             :                 if ([UNIVERSE planet])
    3901             :                 {
    3902             :                         [UNIVERSE addMessage:[NSString stringWithFormat:@" %@. ",[UNIVERSE getSystemName:system_id]] forCount:3.0];
    3903             :                         // and reset the compass
    3904             :                         if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"])
    3905             :                                 compassMode = COMPASS_MODE_PLANET;
    3906             :                         else
    3907             :                                 compassMode = COMPASS_MODE_BASIC;
    3908             :                 }
    3909             :                 else
    3910             :                 {
    3911             :                         if ([UNIVERSE inInterstellarSpace])  [UNIVERSE addMessage:DESC(@"witch-engine-malfunction") forCount:3.0]; // if sun gone nova, print nothing
    3912             :                 }
    3913             :                 
    3914             :                 [self setStatus:STATUS_IN_FLIGHT];
    3915             :                 
    3916             :                 // If we are exiting witchspace after a scripted misjump. then make sure it gets reset now.
    3917             :                 // Scripted misjump situations should have a lifespan of one jump only, to keep things
    3918             :                 // simple - Nikos 20090728
    3919             :                 if ([self scriptedMisjump])  [self setScriptedMisjump:NO];
    3920             :                 // similarly reset the misjump range to the traditional 0.5
    3921             :                 [self setScriptedMisjumpRange:0.5];
    3922             : 
    3923             :                 [self doScriptEvent:OOJSID("shipExitedWitchspace") withArgument:[self jumpCause]];
    3924             : 
    3925             :                 [self doBookkeeping:delta_t]; // arrival frame updates
    3926             : 
    3927             :                 suppressAegisMessages=NO;
    3928             :         }
    3929             : }
    3930             : 
    3931             : 
    3932           0 : - (void) performLaunchingUpdates:(OOTimeDelta)delta_t
    3933             : {
    3934             :         if (![UNIVERSE breakPatternHide])
    3935             :         {
    3936             :                 flightRoll = launchRoll;        // synchronise player's & launching station's spins.
    3937             :                 [self doBookkeeping:delta_t];   // don't show ghost exhaust plumes from previous docking!
    3938             :         }
    3939             :         
    3940             :         if ([UNIVERSE breakPatternOver])
    3941             :         {
    3942             :                 // time to check the legacy scripts!
    3943             :                 [self checkScript];
    3944             :                 // next check in 10s
    3945             :                 
    3946             :                 [self setStatus:STATUS_IN_FLIGHT];
    3947             : 
    3948             :                 [self setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
    3949             :                 StationEntity *stationLaunchedFrom = [UNIVERSE nearestEntityMatchingPredicate:IsStationPredicate parameter:NULL relativeToEntity:self];
    3950             :                 [self doScriptEvent:OOJSID("shipLaunchedFromStation") withArgument:stationLaunchedFrom];
    3951             :         }
    3952             : }
    3953             : 
    3954             : 
    3955           0 : - (void) performDockingUpdates:(OOTimeDelta)delta_t
    3956             : {
    3957             :         if ([UNIVERSE breakPatternOver])
    3958             :         {
    3959             :                 [self docked];          // bookkeeping for docking
    3960             :         }
    3961             : 
    3962             :         // if cloak or ecm visual effects are playing while docking, terminate them
    3963             :         [UNIVERSE terminatePostFX:OO_POSTFX_CLOAK];
    3964             :         if ([UNIVERSE ECMVisualFXEnabled])  [UNIVERSE terminatePostFX:OO_POSTFX_CRTBADSIGNAL];
    3965             : }
    3966             : 
    3967             : 
    3968           0 : - (void) performDeadUpdates:(OOTimeDelta)delta_t
    3969             : {
    3970             :         [UNIVERSE terminatePostFX:OO_POSTFX_CLOAK];
    3971             :         if ([UNIVERSE ECMVisualFXEnabled])  [UNIVERSE terminatePostFX:OO_POSTFX_CRTBADSIGNAL];
    3972             :         
    3973             :         [self gameOverFadeToBW];
    3974             :         
    3975             :         if ([self shotTime] > kDeadResetTime)
    3976             :         {
    3977             :                 BOOL was_mouse_control_on = mouse_control_on;
    3978             :                 [UNIVERSE handleGameOver];                              //  we restart the UNIVERSE
    3979             :                 mouse_control_on = was_mouse_control_on;
    3980             :         }
    3981             : }
    3982             : 
    3983             : 
    3984           0 : - (void) gameOverFadeToBW
    3985             : {
    3986             :         float secondsToBWFadeOut = [[NSUserDefaults standardUserDefaults] oo_floatForKey:@"gameover-seconds-to-bw-fadeout" defaultValue:5.0f];
    3987             :         if ([UNIVERSE detailLevel] >= DETAIL_LEVEL_SHADERS && secondsToBWFadeOut > 0.0f)
    3988             :         {
    3989             :                 MyOpenGLView *gameView = [UNIVERSE gameView];
    3990             :                 static float originalColorSaturation = -1.0f;
    3991             :                 if (originalColorSaturation == -1.0f)  originalColorSaturation = [gameView colorSaturation];
    3992             :                 if ([self shotTime] < secondsToBWFadeOut)
    3993             :                 {
    3994             :                         // fade to black & white within secondsToBWFadeOut, independently of
    3995             :                         // frame rate and original color saturation
    3996             :                         if (fps_counter != 0)
    3997             :                         {
    3998             :                                 [gameView adjustColorSaturation:-(originalColorSaturation * (1.0f / secondsToBWFadeOut) * [UNIVERSE timeAccelerationFactor] / fps_counter)];
    3999             :                         }
    4000             :                 }
    4001             :                 
    4002             :                 if ([self shotTime] > kDeadResetTime)
    4003             :                 {
    4004             :                         // make sure to subtract the current saturation because if the user presses space to skip
    4005             :                         // the game over screen before the transition to b/w has been completed, whatever is left
    4006             :                         // will be added to the original saturation, resulting in an oversaturated image
    4007             :                         [gameView adjustColorSaturation:originalColorSaturation - [gameView colorSaturation]];
    4008             :                         originalColorSaturation = -1.0f;
    4009             :                 }
    4010             :         }
    4011             : }
    4012             : 
    4013             : 
    4014             : // Target is valid if it's within Scanner range, AND
    4015             : // Target is a ship AND is not cloaked or jamming, OR
    4016             : // Target is a wormhole AND player has the Wormhole Scanner
    4017             : - (BOOL)isValidTarget:(Entity*)target
    4018             : {
    4019             :         // Just in case we got called with a bad target.
    4020             :         if (!target)
    4021             :                 return NO;
    4022             : 
    4023             :         // If target is beyond scanner range, it's lost
    4024             :         if(target->zero_distance > SCANNER_MAX_RANGE2)
    4025             :                 return NO;
    4026             : 
    4027             :         // If target is a ship, check whether it's cloaked or is actively jamming our scanner
    4028             :         if ([target isShip])
    4029             :         {
    4030             :                 ShipEntity *targetShip = (ShipEntity*)target;
    4031             :                 if ([targetShip isCloaked] ||   // checks for cloaked ships
    4032             :                         ([targetShip isJammingScanning] && ![self hasMilitaryScannerFilter]))   // checks for activated jammer
    4033             :                 {
    4034             :                         return NO;
    4035             :                 }
    4036             :                 OOEntityStatus tstatus = [targetShip status];
    4037             :                 if (tstatus == STATUS_ENTERING_WITCHSPACE || tstatus == STATUS_IN_HOLD || tstatus == STATUS_DOCKED)
    4038             :                 { // checks for ships entering wormholes, docking, or been scooped
    4039             :                         return NO;
    4040             :                 }
    4041             :                 return YES;
    4042             :         }
    4043             : 
    4044             :         // If target is an unexpired wormhole and the player has bought the Wormhole Scanner and we're in ID mode
    4045             :         if ([target isWormhole] && [target scanClass] != CLASS_NO_DRAW && 
    4046             :                 [self hasEquipmentItemProviding:@"EQ_WORMHOLE_SCANNER"] && ident_engaged)
    4047             :         {
    4048             :                 return YES;
    4049             :         }
    4050             :         
    4051             :         // Target is neither a wormhole nor a ship
    4052             :         return NO;
    4053             : }
    4054             : 
    4055             : 
    4056           0 : - (void) showGameOver
    4057             : {
    4058             :         [hud resetGuis:[NSDictionary dictionaryWithObject:[NSDictionary dictionary] forKey:@"message_gui"]];
    4059             :         NSString *scoreMS = [NSString stringWithFormat:OOExpandKey(@"gameoverscreen-score-@"),
    4060             :                                                         KillCountToRatingAndKillString(ship_kills)];
    4061             :         
    4062             :         [UNIVERSE displayMessage:OOExpandKey(@"gameoverscreen-game-over") forCount:kDeadResetTime];
    4063             :         [UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
    4064             :         [UNIVERSE displayMessage:scoreMS forCount:kDeadResetTime];
    4065             :         [UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
    4066             :         [UNIVERSE displayMessage:OOExpandKey(@"gameoverscreen-press-space") forCount:kDeadResetTime];
    4067             :         [UNIVERSE displayMessage:@" " forCount:kDeadResetTime];
    4068             :         [UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
    4069             :         [self resetShotTime];
    4070             : }
    4071             : 
    4072             : 
    4073             : - (void) showShipModelWithKey:(NSString *)shipKey shipData:(NSDictionary *)shipData personality:(uint16_t)personality factorX:(GLfloat)factorX factorY:(GLfloat)factorY factorZ:(GLfloat)factorZ inContext:(NSString *)context
    4074             : {
    4075             :         if (shipKey == nil)  return;
    4076             :         if (shipData == nil)  shipData = [[OOShipRegistry sharedRegistry] shipInfoForKey:shipKey];
    4077             :         if (shipData == nil)  return;
    4078             :         
    4079             :         Quaternion              q2 = { (GLfloat)M_SQRT1_2, (GLfloat)M_SQRT1_2, (GLfloat)0.0f, (GLfloat)0.0f };
    4080             :         // MKW - retrieve last demo ships' orientation and release it
    4081             :         if( demoShip != nil )
    4082             :         {
    4083             :                 q2 = [demoShip orientation];
    4084             :                 [demoShip release];
    4085             :         }
    4086             :         
    4087             :         ShipEntity *ship = [[ProxyPlayerEntity alloc] initWithKey:shipKey definition:shipData];
    4088             :         if (personality != ENTITY_PERSONALITY_INVALID)  [ship setEntityPersonalityInt:personality];
    4089             :         
    4090             :         [ship wasAddedToUniverse];
    4091             :         
    4092             :         if (context)  OOLog(@"script.debug.note.showShipModel", @"::::: showShipModel:'%@' in context: %@.", [ship name], context);
    4093             :         
    4094             :         GLfloat cr = [ship collisionRadius];
    4095             :         [ship setOrientation: q2];
    4096             :         [ship setPositionX:factorX * cr y:factorY * cr z:factorZ * cr];
    4097             :         [ship setScanClass: CLASS_NO_DRAW];
    4098             :         [ship setDemoShip: 0.6];
    4099             :         [ship setDemoStartTime: [UNIVERSE getTime]];
    4100             :         if([ship pendingEscortCount] > 0) [ship setPendingEscortCount:0];
    4101             :         [ship setAITo: @"nullAI.plist"];
    4102             :         id subEntStatus = [shipData objectForKey:@"subentities_status"];
    4103             :         // show missing subentities if there's a subentities_status key
    4104             :         if (subEntStatus != nil) [ship deserializeShipSubEntitiesFrom:(NSString *)subEntStatus];
    4105             :         [UNIVERSE addEntity: ship];
    4106             :         // MKW - save demo ship for its rotation
    4107             :         demoShip = [ship retain];
    4108             :         
    4109             :         [ship setStatus: STATUS_COCKPIT_DISPLAY];
    4110             :         
    4111             :         [ship release];
    4112             : }
    4113             : 
    4114             : 
    4115             : // Game options and status screens (for now) may require an immediate window redraw after
    4116             : // said window has been resized. This method must be called after such resize events, including
    4117             : // toggle to/from full screen - Nikos 20140129
    4118             : - (void) doGuiScreenResizeUpdates
    4119             : {
    4120             :         switch ([self guiScreen])
    4121             :         {
    4122             :                 case GUI_SCREEN_GAMEOPTIONS:
    4123             :                         //refresh play windowed / full screen
    4124             :                         [self setGuiToGameOptionsScreen];
    4125             :                         break;
    4126             :                 case GUI_SCREEN_STATUS:
    4127             :                         // status screen must be redone in order to possibly
    4128             :                         // refresh displayed model's draw position
    4129             :                         [self setGuiToStatusScreen];
    4130             :                         break;
    4131             :                 default:
    4132             :                         break;
    4133             :         }
    4134             : 
    4135             : 
    4136             :         [hud resetGuiPositions];
    4137             : }
    4138             : 
    4139             : 
    4140             : // Check for lost targeting - both on the ships' main target as well as each
    4141             : // missile.
    4142             : // If we're actively scanning and we don't have a current target, then check
    4143             : // to see if we've locked onto a new target.
    4144             : // Finally, if we have a target and it's a wormhole, check whether we have more
    4145             : // information
    4146           0 : - (void) updateTargeting
    4147             : {
    4148             :         STAGE_TRACKING_BEGIN
    4149             :         
    4150             :         // check for lost ident target and ensure the ident system is actually scanning
    4151             :         UPDATE_STAGE(@"checking ident target");
    4152             :         if (ident_engaged && [self primaryTarget] != nil)
    4153             :         {
    4154             :                 if (![self isValidTarget:[self primaryTarget]])
    4155             :                 {
    4156             :                         if (!suppressTargetLost)
    4157             :                         {
    4158             :                                 [UNIVERSE addMessage:DESC(@"target-lost") forCount:3.0];
    4159             :                                 [self playTargetLost];
    4160             :                                 [self noteLostTarget];
    4161             :                         }
    4162             :                         else
    4163             :                         {
    4164             :                                 suppressTargetLost = NO;
    4165             :                         }
    4166             : 
    4167             :                         DESTROY(_primaryTarget);
    4168             :                 }
    4169             :         }
    4170             : 
    4171             :         // check each unlaunched missile's target still exists and is in-range
    4172             :         UPDATE_STAGE(@"checking missile targets");
    4173             :         if (missile_status != MISSILE_STATUS_SAFE)
    4174             :         {
    4175             :                 unsigned i;
    4176             :                 for (i = 0; i < max_missiles; i++)
    4177             :                 {
    4178             :                         if ([missile_entity[i] primaryTarget] != nil &&
    4179             :                                         ![self isValidTarget:[missile_entity[i] primaryTarget]])
    4180             :                         {
    4181             :                                 [UNIVERSE addMessage:DESC(@"target-lost") forCount:3.0];
    4182             :                                 [self playTargetLost];
    4183             :                                 [missile_entity[i] removeTarget:nil];
    4184             :                                 if (i == activeMissile)
    4185             :                                 {
    4186             :                                         [self noteLostTarget];
    4187             :                                         DESTROY(_primaryTarget);
    4188             :                                         missile_status = MISSILE_STATUS_ARMED;
    4189             :                                 }
    4190             :                         } else if (i == activeMissile && [missile_entity[i] primaryTarget] == nil) {
    4191             :                                 missile_status = MISSILE_STATUS_ARMED;
    4192             :                         }
    4193             :                 }
    4194             :         }
    4195             : 
    4196             :         // if we don't have a primary target, and we're scanning, then check for a new
    4197             :         // target to lock on to
    4198             :         UPDATE_STAGE(@"looking for new target");
    4199             :         if ([self primaryTarget] == nil && 
    4200             :                         (ident_engaged || missile_status != MISSILE_STATUS_SAFE) &&
    4201             :                         ([self status] == STATUS_IN_FLIGHT || [self status] == STATUS_WITCHSPACE_COUNTDOWN))
    4202             :         {
    4203             :                 Entity *target = [UNIVERSE firstEntityTargetedByPlayer];
    4204             :                 if ([self isValidTarget:target])
    4205             :                 {
    4206             :                         [self addTarget:target];
    4207             :                 }
    4208             :         }
    4209             :         
    4210             :         // If our primary target is a wormhole, check to see if we have additional
    4211             :         // information
    4212             :         UPDATE_STAGE(@"checking for additional wormhole information");
    4213             :         if ([[self primaryTarget] isWormhole])
    4214             :         {
    4215             :                 WormholeEntity *wh = [self primaryTarget];
    4216             :                 switch ([wh scanInfo])
    4217             :                 {
    4218             :                         case WH_SCANINFO_NONE:
    4219             :                                 OOLog(kOOLogInconsistentState, @"%@", @"Internal Error - WH_SCANINFO_NONE reached in [PlayerEntity updateTargeting:]");
    4220             :                                 [self dumpState];
    4221             :                                 [wh dumpState];
    4222             :                                 // Workaround a reported hit of the assert here.  We really
    4223             :                                 // should work out how/why this could happen though and fix
    4224             :                                 // the underlying cause.
    4225             :                                 // - MKW 2011.03.11
    4226             :                                 //assert([wh scanInfo] != WH_SCANINFO_NONE);
    4227             :                                 [wh setScannedAt:[self clockTimeAdjusted]];
    4228             :                                 break;
    4229             :                         case WH_SCANINFO_SCANNED:
    4230             :                                 if ([self clockTimeAdjusted] > [wh scanTime] + 2)
    4231             :                                 {
    4232             :                                         [wh setScanInfo:WH_SCANINFO_COLLAPSE_TIME];
    4233             :                                         //[UNIVERSE addCommsMessage:[NSString stringWithFormat:DESC(@"wormhole-collapse-time-computed"),
    4234             :                                         //                                                 [UNIVERSE getSystemName:[wh destination]]] forCount:5.0];
    4235             :                                 }
    4236             :                                 break;
    4237             :                         case WH_SCANINFO_COLLAPSE_TIME:
    4238             :                                 if([self clockTimeAdjusted] > [wh scanTime] + 4)
    4239             :                                 {
    4240             :                                         [wh setScanInfo:WH_SCANINFO_ARRIVAL_TIME];
    4241             :                                         [UNIVERSE addCommsMessage:[NSString stringWithFormat:DESC(@"wormhole-arrival-time-computed-@"),
    4242             :                                                                                            ClockToString([wh estimatedArrivalTime], NO)] forCount:5.0];
    4243             :                                 }
    4244             :                                 break;
    4245             :                         case WH_SCANINFO_ARRIVAL_TIME:
    4246             :                                 if ([self clockTimeAdjusted] > [wh scanTime] + 7)
    4247             :                                 {
    4248             :                                         [wh setScanInfo:WH_SCANINFO_DESTINATION];
    4249             :                                         [UNIVERSE addCommsMessage:[NSString stringWithFormat:DESC(@"wormhole-destination-computed-@"),
    4250             :                                                                                            [UNIVERSE getSystemName:[wh destination]]] forCount:5.0];
    4251             :                                 }
    4252             :                                 break;
    4253             :                         case WH_SCANINFO_DESTINATION:
    4254             :                                 if ([self clockTimeAdjusted] > [wh scanTime] + 10)
    4255             :                                 {
    4256             :                                         [wh setScanInfo:WH_SCANINFO_SHIP];
    4257             :                                         // TODO: Extract last ship from wormhole and display its name
    4258             :                                 }
    4259             :                                 break;
    4260             :                         case WH_SCANINFO_SHIP:
    4261             :                                 break;
    4262             :                 }
    4263             :         }
    4264             :         
    4265             :         STAGE_TRACKING_END
    4266             : }
    4267             : 
    4268             : 
    4269           0 : - (void) orientationChanged
    4270             : {
    4271             :         quaternion_normalize(&orientation);
    4272             :         rotMatrix = OOMatrixForQuaternionRotation(orientation);
    4273             :         OOMatrixGetBasisVectors(rotMatrix, &v_right, &v_up, &v_forward);
    4274             :         
    4275             :         orientation.w = -orientation.w;
    4276             :         playerRotMatrix = OOMatrixForQuaternionRotation(orientation);   // this is the rotation similar to ordinary ships
    4277             :         orientation.w = -orientation.w;
    4278             : }
    4279             : 
    4280             : 
    4281           0 : - (void) applyAttitudeChanges:(double) delta_t
    4282             : {
    4283             :         [self applyRoll:flightRoll*delta_t andClimb:flightPitch*delta_t];
    4284             :         [self applyYaw:flightYaw*delta_t];
    4285             : }
    4286             : 
    4287             : 
    4288           0 : - (void) applyRoll:(GLfloat) roll1 andClimb:(GLfloat) climb1
    4289             : {
    4290             :         if (roll1 == 0.0 && climb1 == 0.0 && hasRotated == NO)
    4291             :                 return;
    4292             : 
    4293             :         if (roll1)
    4294             :                 quaternion_rotate_about_z(&orientation, -roll1);
    4295             :         if (climb1)
    4296             :                 quaternion_rotate_about_x(&orientation, -climb1);
    4297             :         
    4298             :         /*      Bugginess may put us in a state where the orientation quat is all
    4299             :                 zeros, at which point it’s impossible to move.
    4300             :         */
    4301             :         if (EXPECT_NOT(quaternion_equal(orientation, kZeroQuaternion)))
    4302             :         {
    4303             :                 if (!quaternion_equal(lastOrientation, kZeroQuaternion))
    4304             :                 {
    4305             :                         orientation = lastOrientation;
    4306             :                 }
    4307             :                 else
    4308             :                 {
    4309             :                         orientation = kIdentityQuaternion;
    4310             :                 }
    4311             :         }
    4312             :         
    4313             :         [self orientationChanged];
    4314             : }
    4315             : 
    4316             : /*
    4317             :  * This method should not be necessary, but when I replaced the above with applyRoll:andClimb:andYaw, the
    4318             :  * ship went crazy. Perhaps applyRoll:andClimb is called from one of the subclasses and that was messing
    4319             :  * things up.
    4320             :  */
    4321             : - (void) applyYaw:(GLfloat) yaw
    4322             : {
    4323             :         quaternion_rotate_about_y(&orientation, -yaw);
    4324             :         
    4325             :         [self orientationChanged];
    4326             : }
    4327             : 
    4328             : 
    4329           0 : - (OOMatrix) drawRotationMatrix // override to provide the 'correct' drawing matrix
    4330             : {
    4331             :         return playerRotMatrix;
    4332             : }
    4333             : 
    4334             : 
    4335           0 : - (OOMatrix) drawTransformationMatrix
    4336             : {
    4337             :         OOMatrix result = playerRotMatrix;
    4338             :         // HPVect: modify to use camera-relative positioning
    4339             :         return OOMatrixTranslate(result, HPVectorToVector(position));
    4340             : }
    4341             : 
    4342             : 
    4343           0 : - (Quaternion) normalOrientation
    4344             : {
    4345             :         return make_quaternion(-orientation.w, orientation.x, orientation.y, orientation.z);
    4346             : }
    4347             : 
    4348             : 
    4349           0 : - (void) setNormalOrientation:(Quaternion) quat
    4350             : {
    4351             :         [self setOrientation:make_quaternion(-quat.w, quat.x, quat.y, quat.z)];
    4352             : }
    4353             : 
    4354             : 
    4355           0 : - (void) moveForward:(double) amount
    4356             : {
    4357             :         distanceTravelled += (float)amount;
    4358             :         [self setPosition:HPvector_add(position, vectorToHPVector(vector_multiply_scalar(v_forward, (float)amount)))];
    4359             : }
    4360             : 
    4361             : 
    4362             : - (HPVector) breakPatternPosition
    4363             : {
    4364             :         return HPvector_add(position,vectorToHPVector(quaternion_rotate_vector(quaternion_conjugate(orientation),forwardViewOffset)));
    4365             : }
    4366             : 
    4367             : 
    4368             : - (Vector) viewpointOffset
    4369             : {
    4370             : //      if ([UNIVERSE breakPatternHide])
    4371             : //              return kZeroVector;     // center view for break pattern
    4372             :         // now done by positioning break pattern correctly
    4373             : 
    4374             :         switch ([UNIVERSE viewDirection])
    4375             :         {
    4376             :                 case VIEW_FORWARD:
    4377             :                         return forwardViewOffset;
    4378             :                 case VIEW_AFT:
    4379             :                         return aftViewOffset;
    4380             :                 case VIEW_PORT:
    4381             :                         return portViewOffset;
    4382             :                 case VIEW_STARBOARD:
    4383             :                         return starboardViewOffset;
    4384             :                 /* GILES custom viewpoints */
    4385             :                 case VIEW_CUSTOM:
    4386             :                         return customViewOffset;
    4387             :                 /* -- */
    4388             :                 
    4389             :                 default:
    4390             :                         break;
    4391             :         }
    4392             : 
    4393             :         return kZeroVector;
    4394             : }
    4395             : 
    4396             : 
    4397             : - (Vector) viewpointOffsetAft
    4398             : {
    4399             :         return aftViewOffset;
    4400             : }
    4401             : 
    4402             : - (Vector) viewpointOffsetForward
    4403             : {
    4404             :         return forwardViewOffset;
    4405             : }
    4406             : 
    4407             : - (Vector) viewpointOffsetPort
    4408             : {
    4409             :         return portViewOffset;
    4410             : }
    4411             : 
    4412             : - (Vector) viewpointOffsetStarboard
    4413             : {
    4414             :         return starboardViewOffset;
    4415             : }
    4416             : 
    4417             : 
    4418             : /* TODO post 1.78: profiling suggests this gets called often enough
    4419             :  * that it's worth caching the result per-frame - CIM */
    4420             : - (HPVector) viewpointPosition
    4421             : {
    4422             :         HPVector                viewpoint = position;
    4423             :         if (showDemoShips)
    4424             :         {
    4425             :                 viewpoint = kZeroHPVector;
    4426             :         }
    4427             :         Vector          offset = [self viewpointOffset];
    4428             :         
    4429             :         // FIXME: this ought to be done with matrix or quaternion functions.
    4430             :         OOMatrix r = rotMatrix;
    4431             :         
    4432             :         viewpoint.x += offset.x * r.m[0][0];    viewpoint.y += offset.x * r.m[1][0];    viewpoint.z += offset.x * r.m[2][0];
    4433             :         viewpoint.x += offset.y * r.m[0][1];    viewpoint.y += offset.y * r.m[1][1];    viewpoint.z += offset.y * r.m[2][1];
    4434             :         viewpoint.x += offset.z * r.m[0][2];    viewpoint.y += offset.z * r.m[1][2];    viewpoint.z += offset.z * r.m[2][2];
    4435             :         
    4436             :         return viewpoint;
    4437             : }
    4438             : 
    4439             : 
    4440           0 : - (void) drawImmediate:(bool)immediate translucent:(bool)translucent
    4441             : {
    4442             :         switch ([self status])
    4443             :         {
    4444             :                 case STATUS_DEAD:
    4445             :                 case STATUS_COCKPIT_DISPLAY:
    4446             :                 case STATUS_DOCKED:
    4447             :                 case STATUS_START_GAME:
    4448             :                         return;
    4449             :                         
    4450             :                 default:
    4451             :                         if ([UNIVERSE breakPatternHide])  return;
    4452             :         }
    4453             :         
    4454             :         [super drawImmediate:immediate translucent:translucent];
    4455             : }
    4456             : 
    4457             : 
    4458             : - (void) setMassLockable:(BOOL)newValue
    4459             : {
    4460             :         massLockable = !!newValue;
    4461             :         [self updateAlertCondition];
    4462             : }
    4463             : 
    4464             : 
    4465             : - (BOOL) massLockable
    4466             : {
    4467             :         return massLockable;
    4468             : }
    4469             : 
    4470             : 
    4471             : - (BOOL) massLocked
    4472             : {
    4473             :         return ((alertFlags & ALERT_FLAG_MASS_LOCK) != 0);
    4474             : }
    4475             : 
    4476             : 
    4477             : - (BOOL) atHyperspeed
    4478             : {
    4479             :         return travelling_at_hyperspeed;
    4480             : }
    4481             : 
    4482             : 
    4483             : - (float) occlusionLevel
    4484             : {
    4485             :         return occlusion_dial;
    4486             : }
    4487             : 
    4488             : 
    4489             : - (void) setOcclusionLevel:(float)level
    4490             : {
    4491             :         occlusion_dial = level;
    4492             : }
    4493             : 
    4494             : 
    4495             : - (void) setDockedAtMainStation
    4496             : {
    4497             :         [self setDockedStation:[UNIVERSE station]];
    4498             :         if (_dockedStation != nil)  [self setStatus:STATUS_DOCKED];
    4499             : }
    4500             : 
    4501             : 
    4502             : - (StationEntity *) dockedStation
    4503             : {
    4504             :         return [_dockedStation weakRefUnderlyingObject];
    4505             : }
    4506             : 
    4507             : 
    4508             : - (void) setDockedStation:(StationEntity *)station
    4509             : {
    4510             :         [_dockedStation release];
    4511             :         _dockedStation = [station weakRetain];
    4512             : }
    4513             : 
    4514             : 
    4515             : - (void) setTargetDockStationTo:(StationEntity *) value
    4516             : {
    4517             :         targetDockStation = value;
    4518             : }
    4519             : 
    4520             : 
    4521             : - (StationEntity *) getTargetDockStation
    4522             : {
    4523             :         return targetDockStation;
    4524             : }
    4525             : 
    4526             : 
    4527             : - (HeadUpDisplay *) hud
    4528             : {
    4529             :         return hud;
    4530             : }
    4531             : 
    4532             : 
    4533             : - (void) resetHud
    4534             : {
    4535             :         // set up defauld HUD for the ship
    4536             :         NSDictionary *shipDict = [[OOShipRegistry sharedRegistry] shipInfoForKey:[self shipDataKey]];
    4537             :         NSString *hud_desc = [shipDict oo_stringForKey:@"hud" defaultValue:@"hud.plist"];
    4538             :         if (![self switchHudTo:hud_desc])  [self switchHudTo:@"hud.plist"];   // ensure we have a HUD to fall back to
    4539             : }
    4540             : 
    4541             : 
    4542             : - (BOOL) switchHudTo:(NSString *)hudFileName
    4543             : {
    4544             :         NSDictionary    *hudDict = nil;
    4545             :         BOOL                    wasHidden = NO;
    4546             :         BOOL                    wasCompassActive = YES;
    4547             :         double                  scannerZoom = 1.0;
    4548             :         NSUInteger              lastMFD = 0;
    4549             :         NSUInteger              i;
    4550             : 
    4551             :         if (!hudFileName)  return NO;
    4552             :         
    4553             :         // is the HUD in the process of being rendered? If yes, set it to defer state and abort the switching now
    4554             :         if (hud != nil && [hud isUpdating])
    4555             :         {
    4556             :                 [hud setDeferredHudName:hudFileName];
    4557             :                 return NO;
    4558             :         }
    4559             :         
    4560             :         hudDict = [ResourceManager dictionaryFromFilesNamed:hudFileName inFolder:@"Config" andMerge:YES];
    4561             :         // hud defined, but buggy?
    4562             :         if (hudDict == nil)
    4563             :         {
    4564             :                 OOLog(@"PlayerEntity.switchHudTo.failed", @"HUD dictionary file %@ to switch to not found or invalid.", hudFileName);
    4565             :                 return NO;
    4566             :         }
    4567             :         
    4568             :         if (hud != nil)
    4569             :         {
    4570             :                 // remember these values
    4571             :                 wasHidden = [hud isHidden];
    4572             :                 wasCompassActive = [hud isCompassActive];
    4573             :                 scannerZoom = [hud scannerZoom];
    4574             :                 lastMFD = activeMFD;
    4575             :         }
    4576             :         
    4577             :         // buggy oxp could override hud.plist with a non-dictionary.
    4578             :         if (hudDict != nil)
    4579             :         {
    4580             :                 [hud setHidden:YES];    // hide the hud while rebuilding it.
    4581             :                 DESTROY(hud);
    4582             :                 hud = [[HeadUpDisplay alloc] initWithDictionary:hudDict inFile:hudFileName];
    4583             :                 [hud resetGuis:hudDict];
    4584             :                 // reset zoom & hidden to what they were before the swich
    4585             :                 [hud setScannerZoom:scannerZoom];
    4586             :                 [hud setCompassActive:wasCompassActive];
    4587             :                 [hud setHidden:wasHidden];
    4588             :                 activeMFD = 0;
    4589             :                 NSArray *savedMFDs = [NSArray arrayWithArray:multiFunctionDisplaySettings];
    4590             :                 [multiFunctionDisplaySettings removeAllObjects];
    4591             :                 for (i = 0; i < [hud mfdCount] ; i++)
    4592             :                 {
    4593             :                         if ([savedMFDs count] > i)
    4594             :                         {
    4595             :                                 [multiFunctionDisplaySettings addObject:[savedMFDs objectAtIndex:i]];
    4596             :                         }
    4597             :                         else
    4598             :                         {
    4599             :                                 [multiFunctionDisplaySettings addObject:[NSNull null]];
    4600             :                         }
    4601             :                 }
    4602             :                 if (lastMFD < [hud mfdCount]) activeMFD = lastMFD;
    4603             :         }
    4604             :         
    4605             :         return YES;
    4606             : }
    4607             : 
    4608             : 
    4609             : - (float) dialCustomFloat:(NSString *)dialKey
    4610             : {
    4611             :         return [customDialSettings oo_floatForKey:dialKey defaultValue:0.0];
    4612             : }
    4613             : 
    4614             : 
    4615             : - (NSString *) dialCustomString:(NSString *)dialKey
    4616             : {
    4617             :         return [customDialSettings oo_stringForKey:dialKey defaultValue:@""];
    4618             : }
    4619             : 
    4620             : 
    4621             : - (OOColor *) dialCustomColor:(NSString *)dialKey
    4622             : {
    4623             :         return [OOColor colorWithDescription:[customDialSettings objectForKey:dialKey]];
    4624             : }
    4625             : 
    4626             : 
    4627             : - (void) setDialCustom:(id)value forKey:(NSString *)dialKey
    4628             : {
    4629             :         [customDialSettings setObject:value forKey:dialKey];
    4630             : }
    4631             : 
    4632             : 
    4633             : - (void) setShowDemoShips:(BOOL)value
    4634             : {
    4635             :         showDemoShips = value;
    4636             : }
    4637             : 
    4638             : 
    4639             : - (BOOL) showDemoShips
    4640             : {
    4641             :         return showDemoShips;
    4642             : }
    4643             : 
    4644             : 
    4645           0 : - (float) maxForwardShieldLevel
    4646             : {
    4647             :         return max_forward_shield;
    4648             : }
    4649             : 
    4650             : 
    4651           0 : - (float) maxAftShieldLevel
    4652             : {
    4653             :         return max_aft_shield;
    4654             : }
    4655             : 
    4656             : 
    4657             : - (float) forwardShieldRechargeRate
    4658             : {
    4659             :         return forward_shield_recharge_rate;
    4660             : }
    4661             : 
    4662             : 
    4663             : - (float) aftShieldRechargeRate
    4664             : {
    4665             :         return aft_shield_recharge_rate;
    4666             : }
    4667             : 
    4668             : 
    4669             : - (void) setMaxForwardShieldLevel:(float)new
    4670             : {
    4671             :         max_forward_shield = new;
    4672             : }
    4673             : 
    4674             : 
    4675             : - (void) setMaxAftShieldLevel:(float)new
    4676             : {
    4677             :         max_aft_shield = new;
    4678             : }
    4679             : 
    4680             : 
    4681             : - (void) setForwardShieldRechargeRate:(float)new
    4682             : {
    4683             :         forward_shield_recharge_rate = new;
    4684             : }
    4685             : 
    4686             : 
    4687             : - (void) setAftShieldRechargeRate:(float)new
    4688             : {
    4689             :         aft_shield_recharge_rate = new;
    4690             : }
    4691             : 
    4692             : 
    4693             : - (GLfloat) forwardShieldLevel
    4694             : {
    4695             :         return forward_shield;
    4696             : }
    4697             : 
    4698             : 
    4699             : - (GLfloat) aftShieldLevel
    4700             : {
    4701             :         return aft_shield;
    4702             : }
    4703             : 
    4704             : 
    4705             : - (void) setForwardShieldLevel:(GLfloat)level
    4706             : {
    4707             :         forward_shield = OOClamp_0_max_f(level, [self maxForwardShieldLevel]);
    4708             : }
    4709             : 
    4710             : 
    4711             : - (void) setAftShieldLevel:(GLfloat)level
    4712             : {
    4713             :         aft_shield = OOClamp_0_max_f(level, [self maxAftShieldLevel]);
    4714             : }
    4715             : 
    4716             : 
    4717             : - (NSDictionary *) keyConfig
    4718             : {
    4719             :         //return keyconfig_settings;
    4720             :         return keyconfig2_settings;
    4721             : }
    4722             : 
    4723             : 
    4724             : - (BOOL) isMouseControlOn
    4725             : {
    4726             :         return mouse_control_on;
    4727             : }
    4728             : 
    4729             : 
    4730             : - (GLfloat) dialRoll
    4731             : {
    4732             :         GLfloat result = flightRoll / max_flight_roll;
    4733             :         if ((result < 1.0f)&&(result > -1.0f))
    4734             :                 return result;
    4735             :         if (result > 0.0f)
    4736             :                 return 1.0f;
    4737             :         return -1.0f;
    4738             : }
    4739             : 
    4740             : 
    4741             : - (GLfloat) dialPitch
    4742             : {
    4743             :         GLfloat result = flightPitch / max_flight_pitch;
    4744             :         if ((result < 1.0f)&&(result > -1.0f))
    4745             :                 return result;
    4746             :         if (result > 0.0f)
    4747             :                 return 1.0f;
    4748             :         return -1.0f;
    4749             : }
    4750             : 
    4751             : 
    4752             : - (GLfloat) dialYaw
    4753             : {
    4754             :         GLfloat result = -flightYaw / max_flight_yaw;
    4755             :         if ((result < 1.0f)&&(result > -1.0f))
    4756             :         return result;
    4757             :         if (result > 0.0f)
    4758             :                 return 1.0f;
    4759             :         return -1.0f;
    4760             : }
    4761             : 
    4762             : 
    4763             : - (GLfloat) dialSpeed
    4764             : {
    4765             :         GLfloat result = flightSpeed / maxFlightSpeed;
    4766             :         return OOClamp_0_1_f(result);
    4767             : }
    4768             : 
    4769             : 
    4770             : - (GLfloat) dialHyperSpeed
    4771             : {
    4772             :         return flightSpeed / maxFlightSpeed;
    4773             : }
    4774             : 
    4775             : 
    4776             : - (GLfloat) dialForwardShield
    4777             : {
    4778             :         if (EXPECT_NOT([self maxForwardShieldLevel] <= 0))
    4779             :         {
    4780             :                 return 0.0;
    4781             :         }
    4782             :         GLfloat result = forward_shield / [self maxForwardShieldLevel];
    4783             :         return OOClamp_0_1_f(result);
    4784             : }
    4785             : 
    4786             : 
    4787             : - (GLfloat) dialAftShield
    4788             : {
    4789             :         if (EXPECT_NOT([self maxAftShieldLevel] <= 0))
    4790             :         {
    4791             :                 return 0.0;
    4792             :         }
    4793             :         GLfloat result = aft_shield / [self maxAftShieldLevel];
    4794             :         return OOClamp_0_1_f(result);
    4795             : }
    4796             : 
    4797             : 
    4798             : - (GLfloat) dialEnergy
    4799             : {
    4800             :         GLfloat result = energy / maxEnergy;
    4801             :         return OOClamp_0_1_f(result);
    4802             : }
    4803             : 
    4804             : 
    4805             : - (GLfloat) dialMaxEnergy
    4806             : {
    4807             :         return maxEnergy;
    4808             : }
    4809             : 
    4810             : 
    4811             : - (GLfloat) dialFuel
    4812             : {
    4813             :         if (fuel <= 0.0f)
    4814             :                 return 0.0f;
    4815             :         if (fuel > [self fuelCapacity])
    4816             :                 return 1.0f;
    4817             :         return (GLfloat)fuel / (GLfloat)[self fuelCapacity];
    4818             : }
    4819             : 
    4820             : 
    4821             : - (GLfloat) dialHyperRange
    4822             : {
    4823             :         if (target_system_id == system_id && ![UNIVERSE inInterstellarSpace])  return 0.0f;
    4824             :         return [self fuelRequiredForJump] / (GLfloat)PLAYER_MAX_FUEL;
    4825             : }
    4826             : 
    4827             : 
    4828           0 : - (GLfloat) laserHeatLevel
    4829             : {
    4830             :         GLfloat result = (GLfloat)weapon_temp / (GLfloat)PLAYER_MAX_WEAPON_TEMP;
    4831             :         return OOClamp_0_1_f(result);
    4832             : }
    4833             : 
    4834             : 
    4835           0 : - (GLfloat)laserHeatLevelAft
    4836             : {
    4837             :         GLfloat result = aft_weapon_temp / (GLfloat)PLAYER_MAX_WEAPON_TEMP;
    4838             :         return OOClamp_0_1_f(result);
    4839             : }
    4840             : 
    4841             : 
    4842           0 : - (GLfloat)laserHeatLevelForward
    4843             : {
    4844             :         GLfloat result = forward_weapon_temp / (GLfloat)PLAYER_MAX_WEAPON_TEMP;
    4845             : // no need to check subents here
    4846             :         return OOClamp_0_1_f(result);
    4847             : }
    4848             : 
    4849             : 
    4850           0 : - (GLfloat)laserHeatLevelPort
    4851             : {
    4852             :         GLfloat result = port_weapon_temp / PLAYER_MAX_WEAPON_TEMP;
    4853             :         return OOClamp_0_1_f(result);
    4854             : }
    4855             : 
    4856             : 
    4857           0 : - (GLfloat)laserHeatLevelStarboard
    4858             : {
    4859             :         GLfloat result = starboard_weapon_temp / PLAYER_MAX_WEAPON_TEMP;
    4860             :         return OOClamp_0_1_f(result);
    4861             : }
    4862             : 
    4863             : 
    4864             : 
    4865             : 
    4866             : - (GLfloat) dialAltitude
    4867             : {
    4868             :         if ([self isDocked])  return 0.0f;
    4869             :         
    4870             :         // find nearest planet type entity...
    4871             :         assert(UNIVERSE != nil);
    4872             :         
    4873             :         Entity  *nearestPlanet = [self findNearestStellarBody];
    4874             :         if (nearestPlanet == nil)  return 1.0f;
    4875             :         
    4876             :         GLfloat zd = nearestPlanet->zero_distance;
    4877             :         GLfloat cr = nearestPlanet->collision_radius;
    4878             :         GLfloat alt = sqrt(zd) - cr;
    4879             :         
    4880             :         return OOClamp_0_1_f(alt / (GLfloat)PLAYER_DIAL_MAX_ALTITUDE);
    4881             : }
    4882             : 
    4883             : 
    4884             : - (double) clockTime
    4885             : {
    4886             :         return ship_clock;
    4887             : }
    4888             : 
    4889             : 
    4890             : - (double) clockTimeAdjusted
    4891             : {
    4892             :         return ship_clock + ship_clock_adjust;
    4893             : }
    4894             : 
    4895             : 
    4896             : - (BOOL) clockAdjusting
    4897             : {
    4898             :         return ship_clock_adjust > 0;
    4899             : }
    4900             : 
    4901             : 
    4902             : - (void) addToAdjustTime:(double)seconds
    4903             : {
    4904             :         ship_clock_adjust += seconds;
    4905             : }
    4906             : 
    4907             : 
    4908             : - (double) escapePodRescueTime
    4909             : {
    4910             :         return escape_pod_rescue_time;
    4911             : }
    4912             : 
    4913             : 
    4914             : - (void) setEscapePodRescueTime:(double)seconds
    4915             : {
    4916             :         escape_pod_rescue_time = seconds;
    4917             : }
    4918             : 
    4919             : - (NSString *) dial_clock
    4920             : {
    4921             :         return ClockToString(ship_clock, ship_clock_adjust > 0);
    4922             : }
    4923             : 
    4924             : 
    4925             : - (NSString *) dial_clock_adjusted
    4926             : {
    4927             :         return ClockToString(ship_clock + ship_clock_adjust, NO);
    4928             : }
    4929             : 
    4930             : 
    4931             : - (NSString *) dial_fpsinfo
    4932             : {
    4933             :         unsigned fpsVal = fps_counter;  
    4934             :         return [NSString stringWithFormat:@"FPS: %3d", fpsVal];
    4935             : }
    4936             : 
    4937             : 
    4938             : - (NSString *) dial_objinfo
    4939             : {
    4940             :         NSString *result = [NSString stringWithFormat:@"Entities: %3ld", [UNIVERSE entityCount]];
    4941             : #ifndef NDEBUG
    4942             :         result = [NSString stringWithFormat:@"%@ (%d, %zu KiB, avg %lu bytes)", result, gLiveEntityCount, gTotalEntityMemory >> 10, gTotalEntityMemory / gLiveEntityCount];
    4943             : #endif
    4944             :         
    4945             :         return result;
    4946             : }
    4947             : 
    4948             : 
    4949             : - (unsigned) countMissiles
    4950             : {
    4951             :         unsigned n_missiles = 0;
    4952             :         unsigned i;
    4953             :         for (i = 0; i < max_missiles; i++)
    4954             :         {
    4955             :                 if (missile_entity[i])
    4956             :                         n_missiles++;
    4957             :         }
    4958             :         return n_missiles;
    4959             : }
    4960             : 
    4961             : 
    4962             : - (OOMissileStatus) dialMissileStatus
    4963             : {
    4964             :         if ([self weaponsOnline])
    4965             :         {
    4966             :                 return missile_status;
    4967             :         }
    4968             :         else
    4969             :         {
    4970             :                 // Invariant/safety interlock: weapons offline implies missiles safe. -- Ahruman 2012-07-21
    4971             :                 if (missile_status != MISSILE_STATUS_SAFE)
    4972             :                 {
    4973             :                         OOLogERR(@"player.missilesUnsafe", @"%@", @"Missile state is not SAFE when weapons are offline. This is a bug, please report it.");
    4974             :                         [self safeAllMissiles];
    4975             :                 }
    4976             :                 return MISSILE_STATUS_SAFE;
    4977             :         }
    4978             : }
    4979             : 
    4980             : 
    4981           0 : - (BOOL) canScoop:(ShipEntity *)other
    4982             : {
    4983             :         if (specialCargo)       return NO;
    4984             :         return [super canScoop:other];
    4985             : }
    4986             : 
    4987             : 
    4988             : - (OOFuelScoopStatus) dialFuelScoopStatus
    4989             : {
    4990             :         // need to account for the different ways of calculating cargo on board when docked/in-flight
    4991             :         OOCargoQuantity cargoOnBoard = [self status] == STATUS_DOCKED ? current_cargo : (OOCargoQuantity)[cargo count];
    4992             :         if ([self hasScoop])
    4993             :         {
    4994             :                 if (scoopsActive)
    4995             :                         return SCOOP_STATUS_ACTIVE;
    4996             :                 if (cargoOnBoard >= [self maxAvailableCargoSpace] || specialCargo)
    4997             :                         return SCOOP_STATUS_FULL_HOLD;
    4998             :                 return SCOOP_STATUS_OKAY;
    4999             :         }
    5000             :         else
    5001             :         {
    5002             :                 return SCOOP_STATUS_NOT_INSTALLED;
    5003             :         }
    5004             : }
    5005             : 
    5006             : 
    5007             : - (float) fuelLeakRate
    5008             : {
    5009             :         return fuel_leak_rate;
    5010             : }
    5011             : 
    5012             : 
    5013             : - (void) setFuelLeakRate:(float)value
    5014             : {
    5015             :         fuel_leak_rate = fmax(value, 0.0f);
    5016             : }
    5017             : 
    5018             : 
    5019             : - (NSMutableArray *) commLog
    5020             : {
    5021             :         assert(kCommLogTrimSize < kCommLogTrimThreshold);
    5022             :         
    5023             :         if (commLog != nil)
    5024             :         {
    5025             :                 NSUInteger count = [commLog count];
    5026             :                 if (count >= kCommLogTrimThreshold)
    5027             :                 {
    5028             :                         [commLog removeObjectsInRange:NSMakeRange(0, count - kCommLogTrimSize)];
    5029             :                 }
    5030             :         }
    5031             :         else
    5032             :         {
    5033             :                 commLog = [[NSMutableArray alloc] init];
    5034             :         }
    5035             :         
    5036             :         return commLog;
    5037             : }
    5038             : 
    5039             : 
    5040             : - (NSMutableArray *) roleWeights
    5041             : {
    5042             :         return roleWeights;
    5043             : }
    5044             : 
    5045             : 
    5046             : - (void) addRoleForAggression:(ShipEntity *)victim
    5047             : {
    5048             :         if ([victim isExplicitlyUnpiloted] || [victim isHulk] || [victim hasHostileTarget] || [[victim primaryAggressor] isPlayer])
    5049             :         {
    5050             :                 return;
    5051             :         }
    5052             :         NSString *role = nil;
    5053             :         if ([[victim primaryRole] isEqualToString:@"escape-capsule"])
    5054             :         {
    5055             :                 role = @"assassin-player";
    5056             :         }
    5057             :         else if ([victim bounty] > 0)
    5058             :         {
    5059             :                 role = @"hunter";
    5060             :         }
    5061             :         else if ([victim isPirateVictim])
    5062             :         {
    5063             :                 role = @"pirate";
    5064             :         }
    5065             :         else if ([UNIVERSE role:[self primaryRole] isInCategory:@"oolite-hunter"] || [victim scanClass] == CLASS_POLICE)
    5066             :         {
    5067             :                 role = @"pirate-interceptor";
    5068             :         }
    5069             :         if (role == nil)
    5070             :         {
    5071             :                 return;
    5072             :         }
    5073             :         NSUInteger times = [roleWeightFlags oo_intForKey:role defaultValue:0];
    5074             :         times++;
    5075             :         [roleWeightFlags setObject:[NSNumber numberWithUnsignedInteger:times] forKey:role];
    5076             :         if ((times & (times-1)) == 0) // is power of 2
    5077             :         {
    5078             :                 [self addRoleToPlayer:role];
    5079             :         }
    5080             : }
    5081             : 
    5082             : 
    5083             : - (void) addRoleForMining
    5084             : {
    5085             :         NSString *role = @"miner";
    5086             :         NSUInteger times = [roleWeightFlags oo_intForKey:role defaultValue:0];
    5087             :         times++;
    5088             :         [roleWeightFlags setObject:[NSNumber numberWithUnsignedInteger:times] forKey:role];
    5089             :         if ((times & (times-1)) == 0) // is power of 2
    5090             :         {
    5091             :                 [self addRoleToPlayer:role];
    5092             :         }
    5093             : }
    5094             : 
    5095             : 
    5096             : - (void) addRoleToPlayer:(NSString *)role
    5097             : {
    5098             :         NSUInteger slot = Ranrot() & ([self maxPlayerRoles]-1);
    5099             :         [self addRoleToPlayer:role inSlot:slot];
    5100             : }
    5101             : 
    5102             : 
    5103             : - (void) addRoleToPlayer:(NSString *)role inSlot:(NSUInteger)slot
    5104             : {
    5105             :         if (slot >= [self maxPlayerRoles])
    5106             :         {
    5107             :                 slot = [self maxPlayerRoles]-1;
    5108             :         }
    5109             :         if (slot >= [roleWeights count])
    5110             :         {
    5111             :                 [roleWeights addObject:role];
    5112             :         }
    5113             :         else
    5114             :         {
    5115             :                 [roleWeights replaceObjectAtIndex:slot withObject:role];
    5116             :         }
    5117             : }
    5118             : 
    5119             : 
    5120             : - (void) clearRoleFromPlayer:(BOOL)includingLongRange
    5121             : {
    5122             :         NSUInteger slot = Ranrot() % [roleWeights count];
    5123             :         if (!includingLongRange)
    5124             :         {
    5125             :                 NSString *role = [roleWeights objectAtIndex:slot];
    5126             :                 // long range roles cleared at 1/2 normal rate
    5127             :                 if ([role hasSuffix:@"+"] && randf() > 0.5)
    5128             :                 {
    5129             :                         return;
    5130             :                 }
    5131             :         }
    5132             :         [roleWeights replaceObjectAtIndex:slot withObject:@"player-unknown"];
    5133             : }
    5134             : 
    5135             : 
    5136             : - (void) clearRolesFromPlayer:(float)chance
    5137             : {
    5138             :         NSUInteger i, count=[roleWeights count];
    5139             :         for (i = 0; i < count; i++)
    5140             :         {
    5141             :                 if (randf() < chance)
    5142             :                 {
    5143             :                         [roleWeights replaceObjectAtIndex:i withObject:@"player-unknown"];
    5144             :                 }
    5145             :         }
    5146             : }
    5147             : 
    5148             : 
    5149             : - (NSUInteger) maxPlayerRoles
    5150             : {
    5151             :         if (ship_kills >= 6400)
    5152             :         {
    5153             :                 return 32;
    5154             :         }
    5155             :         else if (ship_kills >= 128)
    5156             :         {
    5157             :                 return 16;
    5158             :         }
    5159             :         else
    5160             :         {
    5161             :                 return 8;
    5162             :         }
    5163             : }
    5164             : 
    5165             : 
    5166             : - (void) updateSystemMemory
    5167             : {
    5168             :         OOSystemID sys = [self currentSystemID];
    5169             :         if (sys < 0)
    5170             :         {
    5171             :                 return;
    5172             :         }
    5173             :         NSUInteger memory = 4;
    5174             :         if (ship_kills >= 6400)
    5175             :         {
    5176             :                 memory = 32;
    5177             :         }
    5178             :         else if (ship_kills >= 256)
    5179             :         {
    5180             :                 memory = 16;
    5181             :         }
    5182             :         else if (ship_kills >= 64)
    5183             :         {
    5184             :                 memory = 8;
    5185             :         }
    5186             :         if ([roleSystemList count] >= memory)
    5187             :         {
    5188             :                 [roleSystemList removeObjectAtIndex:0];
    5189             :         }
    5190             :         [roleSystemList addObject:[NSNumber numberWithInt:sys]];
    5191             : }
    5192             : 
    5193             : 
    5194             : - (Entity *) compassTarget
    5195             : {
    5196             :         Entity *result = [compassTarget weakRefUnderlyingObject];
    5197             :         if (result == nil)
    5198             :         {
    5199             :                 DESTROY(compassTarget);
    5200             :                 return nil;
    5201             :         }
    5202             :         return result;
    5203             : }
    5204             : 
    5205             : 
    5206             : - (void) setCompassTarget:(Entity *)value
    5207             : {
    5208             :         [compassTarget release];
    5209             :         compassTarget = [value weakRetain];
    5210             : }
    5211             : 
    5212             : 
    5213             : - (void) validateCompassTarget
    5214             : {
    5215             :         OOSunEntity             *the_sun = [UNIVERSE sun];
    5216             :         OOPlanetEntity  *the_planet = [UNIVERSE planet];
    5217             :         StationEntity   *the_station = [UNIVERSE station];
    5218             :         Entity                  *the_target = [self primaryTarget];
    5219             :         Entity <OOBeaconEntity>           *beacon = [self nextBeacon];
    5220             :         if ([self isInSpace] && the_sun && the_planet           // be in a system
    5221             :                 && ![the_sun goneNova])                 // and the system has not been novabombed
    5222             :         {
    5223             :                 Entity *new_target = nil;
    5224             :                 OOAegisStatus   aegis = [self checkForAegis];
    5225             :                 
    5226             :                 switch ([self compassMode])
    5227             :                 {
    5228             :                         case COMPASS_MODE_INACTIVE:
    5229             :                                 break;
    5230             :                         
    5231             :                         case COMPASS_MODE_BASIC:
    5232             :                                 if ((aegis == AEGIS_CLOSE_TO_MAIN_PLANET || aegis == AEGIS_IN_DOCKING_RANGE) && the_station)
    5233             :                                 {
    5234             :                                         new_target = the_station;
    5235             :                                 }
    5236             :                                 else
    5237             :                                 {
    5238             :                                         new_target = the_planet;
    5239             :                                 }
    5240             :                                 break;
    5241             :                                 
    5242             :                         case COMPASS_MODE_PLANET:
    5243             :                                 new_target = the_planet;
    5244             :                                 break;
    5245             :                                 
    5246             :                         case COMPASS_MODE_STATION:
    5247             :                                 new_target = the_station;
    5248             :                                 break;
    5249             :                                 
    5250             :                         case COMPASS_MODE_SUN:
    5251             :                                 new_target = the_sun;
    5252             :                                 break;
    5253             :                                 
    5254             :                         case COMPASS_MODE_TARGET:
    5255             :                                 new_target = the_target;
    5256             :                                 break;
    5257             :                                 
    5258             :                         case COMPASS_MODE_BEACONS:
    5259             :                                 new_target = beacon;
    5260             :                                 break;
    5261             :                 }
    5262             :                 
    5263             :                 if (new_target == nil || [new_target status] < STATUS_ACTIVE || [new_target status] == STATUS_IN_HOLD)
    5264             :                 {
    5265             :                         [self setCompassMode:COMPASS_MODE_PLANET];
    5266             :                         new_target = the_planet;
    5267             :                 }
    5268             :                 
    5269             :                 if (EXPECT_NOT(new_target != [self compassTarget]))
    5270             :                 {
    5271             :                         [self setCompassTarget:new_target];
    5272             :                         [self doScriptEvent:OOJSID("compassTargetChanged") withArguments:[NSArray arrayWithObjects:new_target, OOStringFromCompassMode([self compassMode]), nil]];
    5273             :                 }
    5274             :         }
    5275             : }
    5276             : 
    5277             : 
    5278             : - (NSString *) compassTargetLabel
    5279             : {
    5280             :         switch (compassMode)
    5281             :         {
    5282             :         case COMPASS_MODE_INACTIVE:
    5283             :                 return @"";
    5284             :         case COMPASS_MODE_BASIC:
    5285             :                 return @"";
    5286             :         case COMPASS_MODE_BEACONS:
    5287             :         {
    5288             :                 Entity *target = [self compassTarget];
    5289             :                 if (target)
    5290             :                 {
    5291             :                         return [(Entity <OOBeaconEntity> *)target beaconLabel];
    5292             :                 }
    5293             :                 return @"";
    5294             :         }
    5295             :         case COMPASS_MODE_PLANET:
    5296             :                 return [[UNIVERSE planet] name];
    5297             :         case COMPASS_MODE_SUN:
    5298             :                 return [[UNIVERSE sun] name];
    5299             :         case COMPASS_MODE_STATION:
    5300             :                 return [[UNIVERSE station] displayName];
    5301             :         case COMPASS_MODE_TARGET:
    5302             :                 return DESC(@"oolite-beacon-label-target");
    5303             :         }
    5304             :         return @"";
    5305             : }
    5306             : 
    5307             : 
    5308             : - (OOCompassMode) compassMode
    5309             : {
    5310             :         return compassMode;
    5311             : }
    5312             : 
    5313             : 
    5314             : - (void) setCompassMode:(OOCompassMode) value
    5315             : {
    5316             :         compassMode = value;
    5317             : }
    5318             : 
    5319             : 
    5320             : - (void) setPrevCompassMode
    5321             : {
    5322             :         OOAegisStatus   aegis = AEGIS_NONE;
    5323             :         Entity <OOBeaconEntity>           *beacon = nil;
    5324             :         
    5325             :         switch (compassMode)
    5326             :         {
    5327             :                 case COMPASS_MODE_INACTIVE:
    5328             :                 case COMPASS_MODE_BASIC:
    5329             :                 case COMPASS_MODE_PLANET:
    5330             :                         beacon = [UNIVERSE lastBeacon];
    5331             :                         while (beacon != nil && [beacon isJammingScanning])
    5332             :                         {
    5333             :                                 beacon = [beacon prevBeacon];
    5334             :                         }
    5335             :                         [self setNextBeacon:beacon];
    5336             :                         
    5337             :                         if (beacon != nil)
    5338             :                         {
    5339             :                                 [self setCompassMode:COMPASS_MODE_BEACONS];
    5340             :                                 break;
    5341             :                         }
    5342             :                         // else fall through to switch to target mode.
    5343             : 
    5344             :                 case COMPASS_MODE_BEACONS:
    5345             :                         beacon = [self nextBeacon];
    5346             :                         do
    5347             :                         {
    5348             :                                 beacon = [beacon prevBeacon];
    5349             :                         } while (beacon != nil && [beacon isJammingScanning]);
    5350             :                         [self setNextBeacon:beacon];
    5351             :                         
    5352             :                         if (beacon == nil)
    5353             :                         {
    5354             :                                 if ([self primaryTarget])
    5355             :                                 {
    5356             :                                         [self setCompassMode:COMPASS_MODE_TARGET];
    5357             :                                 }
    5358             :                                 else
    5359             :                                 {
    5360             :                                         [self setCompassMode:COMPASS_MODE_SUN];
    5361             :                                 }
    5362             :                                 break;
    5363             :                         }
    5364             :                         break;
    5365             : 
    5366             :                 case COMPASS_MODE_TARGET:
    5367             :                         [self setCompassMode:COMPASS_MODE_SUN];
    5368             :                         break;
    5369             : 
    5370             :                 case COMPASS_MODE_SUN:
    5371             :                         aegis = [self checkForAegis];
    5372             :                         if (aegis == AEGIS_CLOSE_TO_MAIN_PLANET || aegis == AEGIS_IN_DOCKING_RANGE)
    5373             :                         { 
    5374             :                                 [self setCompassMode:COMPASS_MODE_STATION];
    5375             :                         }
    5376             :                         else
    5377             :                         {
    5378             :                                 [self setCompassMode:COMPASS_MODE_PLANET];
    5379             :                         } 
    5380             :                         break;
    5381             : 
    5382             :                 case COMPASS_MODE_STATION:
    5383             :                         [self setCompassMode:COMPASS_MODE_PLANET];
    5384             :                         break;
    5385             :         }
    5386             : }
    5387             : 
    5388             : 
    5389             : - (void) setNextCompassMode
    5390             : {
    5391             :         OOAegisStatus   aegis = AEGIS_NONE;
    5392             :         Entity <OOBeaconEntity>           *beacon = nil;
    5393             :         
    5394             :         switch (compassMode)
    5395             :         {
    5396             :                 case COMPASS_MODE_INACTIVE:
    5397             :                 case COMPASS_MODE_BASIC:
    5398             :                 case COMPASS_MODE_PLANET:
    5399             :                         aegis = [self checkForAegis];
    5400             :                         if ([UNIVERSE station] && (aegis == AEGIS_CLOSE_TO_MAIN_PLANET || aegis == AEGIS_IN_DOCKING_RANGE))
    5401             :                         { 
    5402             :                                 [self setCompassMode:COMPASS_MODE_STATION];
    5403             :                         }
    5404             :                         else
    5405             :                         {
    5406             :                                 [self setCompassMode:COMPASS_MODE_SUN];
    5407             :                         }
    5408             :                         break;
    5409             :                         
    5410             :                 case COMPASS_MODE_STATION:
    5411             :                         [self setCompassMode:COMPASS_MODE_SUN];
    5412             :                         break;
    5413             :                         
    5414             :                 case COMPASS_MODE_SUN:
    5415             :                         if ([self primaryTarget])
    5416             :                         {
    5417             :                                 [self setCompassMode:COMPASS_MODE_TARGET];
    5418             :                                 break;
    5419             :                         }
    5420             :                         // else fall through to switch to beacon mode.
    5421             :                         
    5422             :                 case COMPASS_MODE_TARGET:
    5423             :                         beacon = [UNIVERSE firstBeacon];
    5424             :                         while (beacon != nil && [beacon isJammingScanning])
    5425             :                         {
    5426             :                                 beacon = [beacon nextBeacon];
    5427             :                         }
    5428             :                         [self setNextBeacon:beacon];
    5429             :                         
    5430             :                         if (beacon != nil)  [self setCompassMode:COMPASS_MODE_BEACONS];
    5431             :                         else  [self setCompassMode:COMPASS_MODE_PLANET];
    5432             :                         break;
    5433             : 
    5434             :                 case COMPASS_MODE_BEACONS:
    5435             :                         beacon = [self nextBeacon];
    5436             :                         do
    5437             :                         {
    5438             :                                 beacon = [beacon nextBeacon];
    5439             :                         } while (beacon != nil && [beacon isJammingScanning]);
    5440             :                         [self setNextBeacon:beacon];
    5441             :                         
    5442             :                         if (beacon == nil)
    5443             :                         {
    5444             :                                 [self setCompassMode:COMPASS_MODE_PLANET];
    5445             :                         }
    5446             :                         break;
    5447             :         }
    5448             : }
    5449             : 
    5450             : 
    5451             : - (NSUInteger) activeMissile
    5452             : {
    5453             :         return activeMissile;
    5454             : }
    5455             : 
    5456             : 
    5457             : - (void) setActiveMissile:(NSUInteger)value
    5458             : {
    5459             :         activeMissile = value;
    5460             : }
    5461             : 
    5462             : 
    5463             : - (NSUInteger) dialMaxMissiles
    5464             : {
    5465             :         return max_missiles;
    5466             : }
    5467             : 
    5468             : 
    5469             : - (BOOL) dialIdentEngaged
    5470             : {
    5471             :         return ident_engaged;
    5472             : }
    5473             : 
    5474             : 
    5475             : - (void) setDialIdentEngaged:(BOOL)newValue
    5476             : {
    5477             :         ident_engaged = !!newValue;
    5478             : }
    5479             : 
    5480             : 
    5481             : - (NSString *) specialCargo
    5482             : {
    5483             :         return specialCargo;
    5484             : }
    5485             : 
    5486             : 
    5487             : - (NSString *) dialTargetName
    5488             : {
    5489             :         Entity          *target_entity = [self primaryTarget];
    5490             :         NSString        *result = nil;
    5491             :         
    5492             :         if (target_entity == nil)
    5493             :         {
    5494             :                 result = DESC(@"no-target-string");
    5495             :         }
    5496             :         
    5497             :         if ([target_entity respondsToSelector:@selector(identFromShip:)])
    5498             :         {
    5499             :                 result = [(ShipEntity*)target_entity identFromShip:self];
    5500             :         }
    5501             :         
    5502             :         if (result == nil)  result = DESC(@"unknown-target");
    5503             :         
    5504             :         return result;
    5505             : }
    5506             : 
    5507             : 
    5508             : - (NSArray *) multiFunctionDisplayList
    5509             : {
    5510             :         return multiFunctionDisplaySettings;
    5511             : }
    5512             : 
    5513             : 
    5514             : - (NSString *) multiFunctionText:(NSUInteger)i
    5515             : {
    5516             :         NSString *key = [multiFunctionDisplaySettings oo_stringAtIndex:i defaultValue:nil];
    5517             :         if (key == nil)
    5518             :         {
    5519             :                 return nil;
    5520             :         }
    5521             :         NSString *text = [multiFunctionDisplayText oo_stringForKey:key defaultValue:nil];
    5522             :         return text;
    5523             : }
    5524             : 
    5525             : 
    5526             : - (void) setMultiFunctionText:(NSString *)text forKey:(NSString *)key
    5527             : {
    5528             :         if (text != nil)
    5529             :         {
    5530             :                 [multiFunctionDisplayText setObject:text forKey:key];
    5531             :         }
    5532             :         else if (key != nil)
    5533             :         {
    5534             :                 [multiFunctionDisplayText removeObjectForKey:key];
    5535             :                 // and blank any MFDs currently using it
    5536             :                 NSUInteger index;
    5537             :                 while ((index = [multiFunctionDisplaySettings indexOfObject:key]) != NSNotFound)
    5538             :                 {
    5539             :                         [multiFunctionDisplaySettings replaceObjectAtIndex:index withObject:[NSNull null]];
    5540             :                 }
    5541             :         }
    5542             : }
    5543             : 
    5544             : 
    5545             : - (BOOL) setMultiFunctionDisplay:(NSUInteger)index toKey:(NSString *)key
    5546             : {
    5547             :         if (index >= [hud mfdCount])
    5548             :         {
    5549             :                 // is first inactive display
    5550             :                 index = [multiFunctionDisplaySettings indexOfObject:[NSNull null]];
    5551             :                 if (index == NSNotFound)
    5552             :                 {
    5553             :                         return NO;
    5554             :                 }
    5555             :         }
    5556             : 
    5557             :         if (index < [hud mfdCount])
    5558             :         {
    5559             :                 if (key == nil)
    5560             :                 {
    5561             :                         [multiFunctionDisplaySettings replaceObjectAtIndex:index withObject:[NSNull null]];
    5562             :                 }
    5563             :                 else
    5564             :                 {
    5565             :                         [multiFunctionDisplaySettings replaceObjectAtIndex:index withObject:key];
    5566             :                 }
    5567             :                 return YES;
    5568             :         }
    5569             :         else
    5570             :         {
    5571             :                 return NO;
    5572             :         }
    5573             : }
    5574             : 
    5575             : 
    5576             : - (void) cycleNextMultiFunctionDisplay:(NSUInteger) index
    5577             : {
    5578             :         if ([[self hud] mfdCount] == 0) return;
    5579             :         NSArray *keys = [multiFunctionDisplayText allKeys];
    5580             :         NSString *key = nil;
    5581             :         if ([keys count] == 0)
    5582             :         {
    5583             :                 [self setMultiFunctionDisplay:index toKey:nil];
    5584             :                 return;
    5585             :         }
    5586             :         id current = [multiFunctionDisplaySettings objectAtIndex:index];
    5587             :         if (current == [NSNull null])
    5588             :         {
    5589             :                 key = [keys objectAtIndex:0];
    5590             :                 [self setMultiFunctionDisplay:index toKey:key];
    5591             :         }
    5592             :         else
    5593             :         {
    5594             :                 NSUInteger cIndex = [keys indexOfObject:current];
    5595             :                 if (cIndex == NSNotFound || cIndex + 1 >= [keys count])
    5596             :                 {
    5597             :                         key = nil;
    5598             :                         [self setMultiFunctionDisplay:index toKey:nil];
    5599             :                 }
    5600             :                 else 
    5601             :                 {
    5602             :                         key = [keys objectAtIndex:(cIndex+1)];
    5603             :                         [self setMultiFunctionDisplay:index toKey:key];
    5604             :                 }
    5605             :         }
    5606             :         JSContext *context = OOJSAcquireContext();
    5607             :         jsval keyVal = OOJSValueFromNativeObject(context,key);
    5608             :         ShipScriptEvent(context, self, "mfdKeyChanged", INT_TO_JSVAL(activeMFD), keyVal);
    5609             :         OOJSRelinquishContext(context);
    5610             : }
    5611             : 
    5612             : 
    5613             : - (void) cyclePreviousMultiFunctionDisplay:(NSUInteger) index
    5614             : {
    5615             :         if ([[self hud] mfdCount] == 0) return;
    5616             :         NSArray *keys = [multiFunctionDisplayText allKeys];
    5617             :         NSString *key = nil;
    5618             :         if ([keys count] == 0)
    5619             :         {
    5620             :                 [self setMultiFunctionDisplay:index toKey:nil];
    5621             :                 return;
    5622             :         }
    5623             :         id current = [multiFunctionDisplaySettings objectAtIndex:index];
    5624             :         if (current == [NSNull null])
    5625             :         {
    5626             :                 key = [keys objectAtIndex:([keys count]-1)];
    5627             :                 [self setMultiFunctionDisplay:index toKey:key];
    5628             :         }
    5629             :         else
    5630             :         {
    5631             :                 NSUInteger cIndex = [keys indexOfObject:current];
    5632             :                 if (cIndex == NSNotFound || cIndex == 0)
    5633             :                 {
    5634             :                         key = nil;
    5635             :                         [self setMultiFunctionDisplay:index toKey:nil];
    5636             :                 }
    5637             :                 else 
    5638             :                 {
    5639             :                         key = [keys objectAtIndex:(cIndex-1)];
    5640             :                         [self setMultiFunctionDisplay:index toKey:key];
    5641             :                 }
    5642             :         }
    5643             :         JSContext *context = OOJSAcquireContext();
    5644             :         jsval keyVal = OOJSValueFromNativeObject(context,key);
    5645             :         ShipScriptEvent(context, self, "mfdKeyChanged", INT_TO_JSVAL(activeMFD), keyVal);
    5646             :         OOJSRelinquishContext(context);
    5647             : }
    5648             : 
    5649             : 
    5650             : - (void) selectNextMultiFunctionDisplay
    5651             : {
    5652             :         if ([[self hud] mfdCount] == 0) return;
    5653             :         activeMFD = (activeMFD + 1) % [[self hud] mfdCount];
    5654             :         NSUInteger mfdID = activeMFD + 1;
    5655             :         [UNIVERSE addMessage:OOExpandKey(@"mfd-N-selected", mfdID) forCount:3.0 ];
    5656             :         JSContext *context = OOJSAcquireContext();
    5657             :         ShipScriptEvent(context, self, "selectedMFDChanged", INT_TO_JSVAL(activeMFD));
    5658             :         OOJSRelinquishContext(context);
    5659             : }
    5660             : 
    5661             : 
    5662             : - (void) selectPreviousMultiFunctionDisplay
    5663             : {
    5664             :         if ([[self hud] mfdCount] == 0) return;
    5665             :         if (activeMFD == 0) 
    5666             :         {
    5667             :                 activeMFD = ([[self hud] mfdCount] - 1);
    5668             :         }
    5669             :         else
    5670             :         {
    5671             :                 activeMFD = (activeMFD - 1);
    5672             :         }
    5673             :         NSUInteger mfdID = activeMFD + 1;
    5674             :         [UNIVERSE addMessage:OOExpandKey(@"mfd-N-selected", mfdID) forCount:3.0 ];
    5675             :         JSContext *context = OOJSAcquireContext();
    5676             :         ShipScriptEvent(context, self, "selectedMFDChanged", INT_TO_JSVAL(activeMFD));
    5677             :         OOJSRelinquishContext(context);
    5678             : }
    5679             : 
    5680             : 
    5681             : - (NSUInteger) activeMFD
    5682             : {
    5683             :         return activeMFD;
    5684             : }
    5685             : 
    5686             : 
    5687             : - (ShipEntity *) missileForPylon:(NSUInteger)value
    5688             : {
    5689             :         if (value < max_missiles)  return missile_entity[value];
    5690             :         return nil;
    5691             : }
    5692             : 
    5693             : 
    5694             : 
    5695             : - (void) safeAllMissiles
    5696             : {
    5697             :         //      sets all missile targets to NO_TARGET
    5698             :         
    5699             :         unsigned i;
    5700             :         for (i = 0; i < max_missiles; i++)
    5701             :         {
    5702             :                 if (missile_entity[i] && [missile_entity[i] primaryTarget] != nil)
    5703             :                         [missile_entity[i] removeTarget:nil];
    5704             :         }
    5705             :         missile_status = MISSILE_STATUS_SAFE;
    5706             : }
    5707             : 
    5708             : 
    5709             : - (void) tidyMissilePylons
    5710             : {
    5711             :         // Make sure there's no gaps between missiles, synchronise missile_entity & missile_list.
    5712             :         int i, pylon = 0;
    5713             :         OOLog(@"missile.tidying.debug",@"Tidying fitted %d of possible %d missiles",missiles,PLAYER_MAX_MISSILES);
    5714             :         for(i = 0; i < PLAYER_MAX_MISSILES; i++)
    5715             :         {
    5716             :                 OOLog(@"missile.tidying.debug",@"%d %@ %@",i,missile_entity[i],missile_list[i]);
    5717             :                 if(missile_entity[i] != nil)
    5718             :                 {
    5719             :                         missile_entity[pylon] = missile_entity[i];
    5720             :                         missile_list[pylon] = [OOEquipmentType equipmentTypeWithIdentifier:[missile_entity[i] primaryRole]];
    5721             :                         pylon++;
    5722             :                 }
    5723             :         }
    5724             : 
    5725             :         // Now clean up the remainder of the pylons.
    5726             :         for(i = pylon; i < PLAYER_MAX_MISSILES; i++)
    5727             :         {
    5728             :                 missile_entity[i] = nil;
    5729             :                 // not strictly needed, but helps clear things up
    5730             :                 missile_list[i] = nil;
    5731             :         }
    5732             : }
    5733             : 
    5734             : 
    5735             : - (void) selectNextMissile
    5736             : {
    5737             :         if (![self weaponsOnline])  return;
    5738             :         
    5739             :         unsigned i;
    5740             :         for (i = 1; i < max_missiles; i++)
    5741             :         {
    5742             :                 int next_missile = (activeMissile + i) % max_missiles;
    5743             :                 if (missile_entity[next_missile])
    5744             :                 {
    5745             :                         // If we don't have the multi-targeting module installed, clear the active missiles' target
    5746             :                         if( ![self hasEquipmentItemProviding:@"EQ_MULTI_TARGET"] && [missile_entity[activeMissile] isMissile] )
    5747             :                         {
    5748             :                                 [missile_entity[activeMissile] removeTarget:nil];
    5749             :                         }
    5750             : 
    5751             :                         // Set next missile to active
    5752             :                         [self setActiveMissile:next_missile];
    5753             : 
    5754             :                         if (missile_status != MISSILE_STATUS_SAFE)
    5755             :                         {
    5756             :                                 missile_status = MISSILE_STATUS_ARMED;
    5757             : 
    5758             :                                 // If the newly active pylon contains a missile then work out its target, if any
    5759             :                                 if( [missile_entity[activeMissile] isMissile] )
    5760             :                                 {
    5761             :                                         if( [self hasEquipmentItemProviding:@"EQ_MULTI_TARGET"] &&
    5762             :                                                         ([missile_entity[next_missile] primaryTarget] != nil))
    5763             :                                         {
    5764             :                                                 // copy the missile's target
    5765             :                                                 [self addTarget:[missile_entity[next_missile] primaryTarget]];
    5766             :                                                 missile_status = MISSILE_STATUS_TARGET_LOCKED;
    5767             :                                         }
    5768             :                                         else if ([self primaryTarget] != nil)
    5769             :                                         {
    5770             :                                                 // never inherit target if we have EQ_MULTI_TARGET installed! [ Bug #16221 : Targeting enhancement regression ]
    5771             :                                                 /* CIM: seems okay to do this when launching a
    5772             :                                                  * missile to stop multi-target being a bit
    5773             :                                                  * irritating in a fight - 20/8/2014 */
    5774             :                                                 if([self hasEquipmentItemProviding:@"EQ_MULTI_TARGET"] && !launchingMissile)
    5775             :                                                 {
    5776             :                                                         [self noteLostTarget];
    5777             :                                                         DESTROY(_primaryTarget);
    5778             :                                                 }
    5779             :                                                 else
    5780             :                                                 {
    5781             :                                                         [missile_entity[activeMissile] addTarget:[self primaryTarget]];
    5782             :                                                         missile_status = MISSILE_STATUS_TARGET_LOCKED;
    5783             :                                                 }
    5784             :                                         }
    5785             :                                 }
    5786             :                         }
    5787             :                         return;
    5788             :                 }
    5789             :         }
    5790             : }
    5791             : 
    5792             : 
    5793             : - (void) clearAlertFlags
    5794             : {
    5795             :         alertFlags = 0;
    5796             : }
    5797             : 
    5798             : 
    5799             : - (int) alertFlags
    5800             : {
    5801             :         return alertFlags;
    5802             : }
    5803             : 
    5804             : 
    5805             : - (void) setAlertFlag:(int)flag to:(BOOL)value
    5806             : {
    5807             :         if (value)
    5808             :         {
    5809             :                 alertFlags |= flag;
    5810             :         }
    5811             :         else
    5812             :         {
    5813             :                 int comp = ~flag;
    5814             :                 alertFlags &= comp;
    5815             :         }
    5816             : }
    5817             : 
    5818             : 
    5819             : // used by Javascript and the distinction is important for NPCs
    5820           0 : - (OOAlertCondition) realAlertCondition
    5821             : {
    5822             :         return [self alertCondition];
    5823             : }
    5824             : 
    5825             : 
    5826             : - (OOAlertCondition) alertCondition
    5827             : {
    5828             :         OOAlertCondition old_alert_condition = alertCondition;
    5829             :         alertCondition = ALERT_CONDITION_GREEN;
    5830             :         
    5831             :         [self setAlertFlag:ALERT_FLAG_DOCKED to:[self status] == STATUS_DOCKED];
    5832             :         
    5833             :         if (alertFlags & ALERT_FLAG_DOCKED)
    5834             :         {
    5835             :                 alertCondition = ALERT_CONDITION_DOCKED;
    5836             :         }
    5837             :         else
    5838             :         {
    5839             :                 if (alertFlags != 0)
    5840             :                 {
    5841             :                         alertCondition = ALERT_CONDITION_YELLOW;
    5842             :                 }
    5843             :                 if (alertFlags > ALERT_FLAG_YELLOW_LIMIT)
    5844             :                 {
    5845             :                         alertCondition = ALERT_CONDITION_RED;
    5846             :                 }
    5847             :         }
    5848             :         if ((alertCondition == ALERT_CONDITION_RED)&&(old_alert_condition < ALERT_CONDITION_RED))
    5849             :         {
    5850             :                 [self playAlertConditionRed];
    5851             :         }
    5852             :         
    5853             :         return alertCondition;
    5854             : }
    5855             : 
    5856             : 
    5857             : - (OOPlayerFleeingStatus) fleeingStatus
    5858             : {
    5859             :         return fleeing_status;
    5860             : }
    5861             : 
    5862             : /////////////////////////////////////////////////////////////////////
    5863             : 
    5864             : 
    5865           0 : - (void) interpretAIMessage:(NSString *)ms
    5866             : {
    5867             :         if ([ms isEqual:@"HOLD_FULL"])
    5868             :         {
    5869             :                 [self playHoldFull];
    5870             :                 [UNIVERSE addMessage:DESC(@"hold-full") forCount:4.5];
    5871             :         }
    5872             : 
    5873             :         if ([ms isEqual:@"INCOMING_MISSILE"])
    5874             :         {
    5875             :                 if ([self primaryAggressor] != nil)
    5876             :                 {
    5877             :                         [self playIncomingMissile:HPVectorToVector([[self primaryAggressor] position])];
    5878             :                 }
    5879             :                 else
    5880             :                 {
    5881             :                         [self playIncomingMissile:kZeroVector];
    5882             :                 }
    5883             :                 [UNIVERSE addMessage:DESC(@"incoming-missile") forCount:4.5];
    5884             :         }
    5885             : 
    5886             :         if ([ms isEqual:@"ENERGY_LOW"])
    5887             :         {
    5888             :                 [UNIVERSE addMessage:DESC(@"energy-low") forCount:6.0];
    5889             :         }
    5890             : 
    5891             :         if ([ms isEqual:@"ECM"] && ![self isDocked])  [self playHitByECMSound];
    5892             : 
    5893             :         if ([ms isEqual:@"DOCKING_REFUSED"] && [self status] == STATUS_AUTOPILOT_ENGAGED)
    5894             :         {
    5895             :                 [self playDockingDenied];
    5896             :                 [UNIVERSE addMessage:DESC(@"autopilot-denied") forCount:4.5];
    5897             :                 autopilot_engaged = NO;
    5898             :                 [self resetAutopilotAI];
    5899             :                 DESTROY(_primaryTarget);
    5900             :                 [self setStatus:STATUS_IN_FLIGHT];
    5901             :                 [[OOMusicController sharedController] stopDockingMusic];
    5902             :                 [self doScriptEvent:OOJSID("playerDockingRefused")];
    5903             :         }
    5904             : 
    5905             :         // aegis messages to advanced compass so in planet mode it behaves like the old compass
    5906             :         if (compassMode != COMPASS_MODE_BASIC)
    5907             :         {
    5908             :                 if ([ms isEqual:@"AEGIS_CLOSE_TO_MAIN_PLANET"]&&(compassMode == COMPASS_MODE_PLANET))
    5909             :                 {
    5910             :                         [self playAegisCloseToPlanet];
    5911             :                         [self setCompassMode:COMPASS_MODE_STATION];
    5912             :                 }
    5913             :                 if ([ms isEqual:@"AEGIS_IN_DOCKING_RANGE"]&&(compassMode == COMPASS_MODE_PLANET))
    5914             :                 {
    5915             :                         [self playAegisCloseToStation];
    5916             :                         [self setCompassMode:COMPASS_MODE_STATION];
    5917             :                 }
    5918             :                 if ([ms isEqual:@"AEGIS_NONE"]&&(compassMode == COMPASS_MODE_STATION))
    5919             :                 {
    5920             :                         [self setCompassMode:COMPASS_MODE_PLANET];
    5921             :                 }
    5922             :         }
    5923             : }
    5924             : 
    5925             : 
    5926             : - (BOOL) mountMissile:(ShipEntity *)missile
    5927             : {
    5928             :         if (missile == nil)  return NO;
    5929             :         
    5930             :         unsigned i;
    5931             :         for (i = 0; i < max_missiles; i++)
    5932             :         {
    5933             :                 if (missile_entity[i] == nil)
    5934             :                 {
    5935             :                         missile_entity[i] = [missile retain];
    5936             :                         missile_list[missiles] = [OOEquipmentType equipmentTypeWithIdentifier:[missile primaryRole]];
    5937             :                         missiles++;
    5938             :                         if (missiles == 1) [self setActiveMissile:0];   // auto select the first purchased missile
    5939             :                         return YES;
    5940             :                 }
    5941             :         }
    5942             :         
    5943             :         return NO;
    5944             : }
    5945             : 
    5946             : 
    5947             : - (BOOL) mountMissileWithRole:(NSString *)role
    5948             : {
    5949             :         if ([self missileCount] >= [self missileCapacity]) return NO;
    5950             :         return [self mountMissile:[[UNIVERSE newShipWithRole:role] autorelease]];
    5951             : }
    5952             : 
    5953             : 
    5954           0 : - (ShipEntity *) fireMissile
    5955             : {
    5956             :         ShipEntity      *missile = missile_entity[activeMissile];       // retain count is 1
    5957             :         NSString        *identifier = [missile primaryRole];
    5958             :         ShipEntity      *firedMissile = nil;
    5959             : 
    5960             :         if (missile == nil) return nil;
    5961             :         
    5962             :         if (![self weaponsOnline])  return nil;
    5963             :         
    5964             :         // check if we were cloaked before firing the missile - can't use
    5965             :         // cloaking_device_active directly because fireMissilewithIdentifier: andTarget:
    5966             :         // will reset it in case passive cloak is set - Nikos 20130313
    5967             :         BOOL cloakedPriorToFiring = cloaking_device_active;
    5968             :         
    5969             :         launchingMissile = YES;
    5970             :         replacingMissile = NO;
    5971             : 
    5972             :         if ([missile isMine] && (missile_status != MISSILE_STATUS_SAFE))
    5973             :         {
    5974             :                 firedMissile = [self launchMine:missile];
    5975             :                 if (!replacingMissile) [self removeFromPylon:activeMissile];
    5976             :                 if (firedMissile != nil) [self playMineLaunched:[self missileLaunchPosition] weaponIdentifier:identifier];
    5977             :         }
    5978             :         else
    5979             :         {
    5980             :                 if (missile_status != MISSILE_STATUS_TARGET_LOCKED) return nil;
    5981             :                 //  release this before creating it anew in fireMissileWithIdentifier
    5982             :                 firedMissile = [self fireMissileWithIdentifier:identifier andTarget:[missile primaryTarget]];
    5983             : 
    5984             :                 if (firedMissile != nil)
    5985             :                 {
    5986             :                         if (!replacingMissile) [self removeFromPylon:activeMissile];
    5987             :                         [self playMissileLaunched:[self missileLaunchPosition] weaponIdentifier:identifier];
    5988             :                 }
    5989             :         }
    5990             :         
    5991             :         if (cloakedPriorToFiring && cloakPassive)
    5992             :         {
    5993             :                 // fireMissilewithIdentifier: andTarget: has already taken care of deactivating
    5994             :                 // the cloak in the case of missiles by the time we get here, but explicitly
    5995             :                 // calling deactivateCloakingDevice is needed in order to be covered fully with mines too
    5996             :                 [self deactivateCloakingDevice];
    5997             :         }
    5998             :         
    5999             :         replacingMissile = NO;
    6000             :         launchingMissile = NO;
    6001             :         
    6002             :         return firedMissile;
    6003             : }
    6004             : 
    6005             : 
    6006             : - (ShipEntity *) launchMine:(ShipEntity*) mine
    6007             : {
    6008             :         if (!mine)
    6009             :                 return nil;
    6010             :                 
    6011             :         if (![self weaponsOnline])
    6012             :                 return nil;
    6013             :                 
    6014             :         [mine setOwner: self];
    6015             :         [mine setBehaviour: BEHAVIOUR_IDLE];
    6016             :         [self dumpItem: mine];  // includes UNIVERSE addEntity: CLASS_CARGO, STATUS_IN_FLIGHT, AI state GLOBAL ( the last one starts the timer !)
    6017             :         [mine setScanClass: CLASS_MINE];
    6018             :         
    6019             :         float  mine_speed = 500.0f;
    6020             :         Vector mvel = vector_subtract([mine velocity], vector_multiply_scalar(v_forward, mine_speed));
    6021             :         [mine setVelocity: mvel];
    6022             :         [self doScriptEvent:OOJSID("shipReleasedEquipment") withArgument:mine];
    6023             :         return mine;
    6024             : }
    6025             : 
    6026             : 
    6027             : - (BOOL) assignToActivePylon:(NSString *)equipmentKey
    6028             : {
    6029             :         if (!launchingMissile) return NO;
    6030             :         
    6031             :         OOEquipmentType                 *eqType = nil;
    6032             :         
    6033             :         if ([equipmentKey hasSuffix:@"_DAMAGED"])
    6034             :         {
    6035             :                 return NO;
    6036             :         }
    6037             :         else
    6038             :         {
    6039             :                 eqType = [OOEquipmentType equipmentTypeWithIdentifier:equipmentKey];
    6040             :         }
    6041             :         
    6042             :         // missiles with techlevel above 99 (kOOVariableTechLevel) are never available to the player
    6043             :         if (![eqType isMissileOrMine] || [eqType effectiveTechLevel] > kOOVariableTechLevel)
    6044             :         {
    6045             :                 return NO;
    6046             :         }
    6047             : 
    6048             :         ShipEntity *amiss = [UNIVERSE newShipWithRole:equipmentKey];
    6049             :         
    6050             :         if (!amiss) return NO;
    6051             : 
    6052             :         // replace the missile now.
    6053             :         [missile_entity[activeMissile] release];
    6054             :         missile_entity[activeMissile] = amiss;
    6055             :         missile_list[activeMissile] = eqType;
    6056             :         
    6057             :         // make sure the new missile is properly activated.
    6058             :         if (activeMissile > 0) activeMissile--;
    6059             :         else activeMissile = max_missiles - 1;
    6060             :         [self selectNextMissile];
    6061             :         
    6062             :         replacingMissile = YES;
    6063             :         
    6064             :         return YES;
    6065             : }
    6066             : 
    6067             : 
    6068             : - (BOOL) activateCloakingDevice
    6069             : {
    6070             :         if (![self hasCloakingDevice])  return NO;
    6071             :         
    6072             :         if ([super activateCloakingDevice])
    6073             :         {
    6074             :                 [UNIVERSE setCurrentPostFX:OO_POSTFX_CLOAK];
    6075             :                 [UNIVERSE addMessage:DESC(@"cloak-on") forCount:2];
    6076             :                 [self playCloakingDeviceOn];
    6077             :                 return YES;
    6078             :         }
    6079             :         else
    6080             :         {
    6081             :                 [UNIVERSE addMessage:DESC(@"cloak-low-juice") forCount:3];
    6082             :                 [self playCloakingDeviceInsufficientEnergy];
    6083             :                 return NO;
    6084             :         }
    6085             : }
    6086             : 
    6087             : 
    6088             : - (void) deactivateCloakingDevice
    6089             : {
    6090             :         if (![self hasCloakingDevice])  return;
    6091             : 
    6092             :         [super deactivateCloakingDevice];
    6093             :         [UNIVERSE terminatePostFX:OO_POSTFX_CLOAK];
    6094             :         [UNIVERSE addMessage:DESC(@"cloak-off") forCount:2];
    6095             :         [self playCloakingDeviceOff];
    6096             : }
    6097             : 
    6098             : 
    6099             : /* Scanner fuzziness is entirely cosmetic - it doesn't affect the
    6100             :  * player's actual target locks */
    6101             : - (double) scannerFuzziness
    6102             : {
    6103             :         double fuzz = 0.0;
    6104             :         
    6105             :         /* Fuzziness from ECM bursts */
    6106             :         if (last_ecm_time > 0.0)
    6107             :         {
    6108             :                 double since = [UNIVERSE getTime] - last_ecm_time;
    6109             :                 if (since < SCANNER_ECM_FUZZINESS)
    6110             :                 {
    6111             :                         fuzz += (SCANNER_ECM_FUZZINESS - since) * (SCANNER_ECM_FUZZINESS - since) * 500.0;
    6112             :                 }
    6113             :         }
    6114             :         /* Other causes could go here */
    6115             :         
    6116             :         return fuzz;
    6117             : }
    6118             : 
    6119             : 
    6120           0 : - (void) noticeECM
    6121             : {
    6122             :         last_ecm_time = [UNIVERSE getTime];
    6123             : }
    6124             : 
    6125             : 
    6126           0 : - (BOOL) fireECM
    6127             : {
    6128             :         if ([super fireECM])
    6129             :         {
    6130             :                 ecm_in_operation = YES;
    6131             :                 ecm_start_time = [UNIVERSE getTime];
    6132             :                 return YES;
    6133             :         }
    6134             :         else
    6135             :         {
    6136             :                 return NO;
    6137             :         }
    6138             : }
    6139             : 
    6140             : 
    6141             : - (OOEnergyUnitType) installedEnergyUnitType
    6142             : {
    6143             :         if ([self hasEquipmentItem:@"EQ_NAVAL_ENERGY_UNIT"])  return ENERGY_UNIT_NAVAL;
    6144             :         if ([self hasEquipmentItem:@"EQ_ENERGY_UNIT"])  return ENERGY_UNIT_NORMAL;
    6145             :         return ENERGY_UNIT_NONE;
    6146             : }
    6147             : 
    6148             : 
    6149             : - (OOEnergyUnitType) energyUnitType
    6150             : {
    6151             :         if ([self hasEquipmentItem:@"EQ_NAVAL_ENERGY_UNIT"])  return ENERGY_UNIT_NAVAL;
    6152             :         if ([self hasEquipmentItem:@"EQ_ENERGY_UNIT"])  return ENERGY_UNIT_NORMAL;
    6153             :         if ([self hasEquipmentItem:@"EQ_NAVAL_ENERGY_UNIT_DAMAGED"])  return ENERGY_UNIT_NAVAL_DAMAGED;
    6154             :         if ([self hasEquipmentItem:@"EQ_ENERGY_UNIT_DAMAGED"])  return ENERGY_UNIT_NORMAL_DAMAGED;
    6155             :         return ENERGY_UNIT_NONE;
    6156             : }
    6157             : 
    6158             : 
    6159             : - (void) currentWeaponStats
    6160             : {
    6161             :         OOWeaponType currentWeapon = [self currentWeapon];
    6162             :         // Did find & correct a minor mismatch between player and NPC weapon stats. This is the resulting code - Kaks 20101027
    6163             :         
    6164             :         // Basic stats: weapon_damage & weaponRange (weapon_recharge_rate is not used by the player)
    6165             :         [self setWeaponDataFromType:currentWeapon];
    6166             : }
    6167             : 
    6168             : 
    6169             : - (BOOL) weaponsOnline
    6170             : {
    6171             :         return weapons_online;
    6172             : }
    6173             : 
    6174             : 
    6175             : - (void) setWeaponsOnline:(BOOL)newValue
    6176             : {
    6177             :         weapons_online = !!newValue;
    6178             :         if (!weapons_online)  [self safeAllMissiles];
    6179             : }
    6180             : 
    6181             : 
    6182             : - (NSArray *) currentLaserOffset
    6183             : {
    6184             :         return [self laserPortOffset:currentWeaponFacing];
    6185             : }
    6186             : 
    6187             : 
    6188             : - (BOOL) fireMainWeapon
    6189             : {
    6190             :         OOWeaponType weapon_to_be_fired = [self currentWeapon];
    6191             : 
    6192             :         if (![self weaponsOnline])
    6193             :         {
    6194             :                 return NO;
    6195             :         }
    6196             :         
    6197             :         if (weapon_temp / PLAYER_MAX_WEAPON_TEMP >= WEAPON_COOLING_CUTOUT)
    6198             :         {
    6199             :                 [self playWeaponOverheated:[[self currentLaserOffset] oo_vectorAtIndex:0]];
    6200             :                 [UNIVERSE addMessage:DESC(@"weapon-overheat") forCount:3.0];
    6201             :                 return NO;
    6202             :         }
    6203             : 
    6204             :         if (isWeaponNone(weapon_to_be_fired))
    6205             :         {
    6206             :                 return NO;
    6207             :         }
    6208             : 
    6209             :         [self currentWeaponStats];
    6210             : 
    6211             :         NSUInteger multiplier = 1;
    6212             :         if (_multiplyWeapons)
    6213             :         {
    6214             :                 // multiple fitted
    6215             :                 multiplier = [[self laserPortOffset:currentWeaponFacing] count];
    6216             :         }
    6217             :         
    6218             :         if (energy <= weapon_energy_use * multiplier)
    6219             :         {
    6220             :                 [UNIVERSE addMessage:DESC(@"weapon-out-of-juice") forCount:3.0];
    6221             :                 return NO;
    6222             :         }
    6223             : 
    6224             :         using_mining_laser = [weapon_to_be_fired isMiningLaser];
    6225             : 
    6226             :         energy -= weapon_energy_use * multiplier;
    6227             : 
    6228             :         switch (currentWeaponFacing)
    6229             :         {
    6230             :                 case WEAPON_FACING_FORWARD:
    6231             :                         forward_weapon_temp += weapon_shot_temperature * multiplier;
    6232             :                         forward_shot_time = 0.0;
    6233             :                         break;
    6234             :                         
    6235             :                 case WEAPON_FACING_AFT:
    6236             :                         aft_weapon_temp += weapon_shot_temperature * multiplier;
    6237             :                         aft_shot_time = 0.0;
    6238             :                         break;
    6239             :                         
    6240             :                 case WEAPON_FACING_PORT:
    6241             :                         port_weapon_temp += weapon_shot_temperature * multiplier;
    6242             :                         port_shot_time = 0.0;
    6243             :                         break;
    6244             :                         
    6245             :                 case WEAPON_FACING_STARBOARD:
    6246             :                         starboard_weapon_temp += weapon_shot_temperature * multiplier;
    6247             :                         starboard_shot_time = 0.0;
    6248             :                         break;
    6249             :                         
    6250             :                 case WEAPON_FACING_NONE:
    6251             :                         break;
    6252             :         }
    6253             :         
    6254             :         BOOL    weaponFired = NO;
    6255             :         if (!isWeaponNone(weapon_to_be_fired))
    6256             :         {
    6257             :                 if (![weapon_to_be_fired isTurretLaser])
    6258             :                 {
    6259             :                         [self fireLaserShotInDirection:currentWeaponFacing weaponIdentifier:[[self currentWeapon] identifier]];
    6260             :                         weaponFired = YES;
    6261             :                 }
    6262             :                 else
    6263             :                 {
    6264             :                         // nothing: compatible with previous versions
    6265             :                 }
    6266             :         }
    6267             :         
    6268             :         if (weaponFired && cloaking_device_active && cloakPassive)
    6269             :         {
    6270             :                 [self deactivateCloakingDevice];
    6271             :         }       
    6272             :         
    6273             :         return weaponFired;
    6274             : }
    6275             : 
    6276             : 
    6277             : - (OOWeaponType) weaponForFacing:(OOWeaponFacing)facing
    6278             : {
    6279             :         switch (facing)
    6280             :         {
    6281             :                 case WEAPON_FACING_FORWARD:
    6282             :                         return forward_weapon_type;
    6283             :                         
    6284             :                 case WEAPON_FACING_AFT:
    6285             :                         return aft_weapon_type;
    6286             :                         
    6287             :                 case WEAPON_FACING_PORT:
    6288             :                         return port_weapon_type;
    6289             :                         
    6290             :                 case WEAPON_FACING_STARBOARD:
    6291             :                         return starboard_weapon_type;
    6292             :                         
    6293             :                 case WEAPON_FACING_NONE:
    6294             :                         break;
    6295             :         }
    6296             :         return nil;
    6297             : }
    6298             : 
    6299             : 
    6300             : - (OOWeaponType) currentWeapon
    6301             : {
    6302             :         return [self weaponForFacing:currentWeaponFacing];
    6303             : }
    6304             : 
    6305             : 
    6306             : // override ShipEntity definition to ensure that 
    6307             : // if shields are still up, always hit the main entity and take the damage
    6308             : // on the shields
    6309           0 : - (GLfloat) doesHitLine:(HPVector)v0 :(HPVector)v1 :(ShipEntity **)hitEntity
    6310             : {
    6311             :         if (hitEntity)
    6312             :                 hitEntity[0] = (ShipEntity*)nil;
    6313             :         Vector u0 = HPVectorToVector(HPvector_between(position, v0));   // relative to origin of model / octree
    6314             :         Vector u1 = HPVectorToVector(HPvector_between(position, v1));
    6315             :         Vector w0 = make_vector(dot_product(u0, v_right), dot_product(u0, v_up), dot_product(u0, v_forward));   // in ijk vectors
    6316             :         Vector w1 = make_vector(dot_product(u1, v_right), dot_product(u1, v_up), dot_product(u1, v_forward));
    6317             :         GLfloat hit_distance = [octree isHitByLine:w0 :w1];
    6318             :         if (hit_distance)
    6319             :         {
    6320             :                 if (hitEntity)
    6321             :                         hitEntity[0] = self;
    6322             :         }
    6323             : 
    6324             :         bool shields = false;
    6325             :         if ((w0.z >= 0 && forward_shield > 1) || (w0.z <= 0 && aft_shield > 1))
    6326             :         {
    6327             :                 shields = true;
    6328             :         }
    6329             :         
    6330             :         NSEnumerator    *subEnum = nil;
    6331             :         ShipEntity              *se = nil;
    6332             :         for (subEnum = [self shipSubEntityEnumerator]; (se = [subEnum nextObject]); )
    6333             :         {
    6334             :                 HPVector p0 = [se absolutePositionForSubentity];
    6335             :                 Triangle ijk = [se absoluteIJKForSubentity];
    6336             :                 u0 = HPVectorToVector(HPvector_between(p0, v0));
    6337             :                 u1 = HPVectorToVector(HPvector_between(p0, v1));
    6338             :                 w0 = resolveVectorInIJK(u0, ijk);
    6339             :                 w1 = resolveVectorInIJK(u1, ijk);
    6340             :                 
    6341             :                 GLfloat hitSub = [se->octree isHitByLine:w0 :w1];
    6342             :                 if (hitSub && (hit_distance == 0 || hit_distance > hitSub))
    6343             :                 {       
    6344             :                         hit_distance = hitSub;
    6345             :                         if (hitEntity && !shields)
    6346             :                         {
    6347             :                                 *hitEntity = se;
    6348             :                         }
    6349             :                 }
    6350             :         }
    6351             :         
    6352             :         return hit_distance;
    6353             : }
    6354             : 
    6355             : 
    6356             : 
    6357           0 : - (void) takeEnergyDamage:(double)amount from:(Entity *)ent becauseOf:(Entity *)other weaponIdentifier:(NSString *)weaponIdentifier
    6358             : {
    6359             :         HPVector                rel_pos;
    6360             :         OOScalar                d_forward, d_right, d_up;
    6361             :         BOOL            internal_damage = NO;   // base chance
    6362             :         
    6363             :         OOLog(@"player.ship.damage",  @"Player took damage from %@ becauseOf %@", ent, other);
    6364             :         
    6365             :         if ([self status] == STATUS_DEAD)  return;
    6366             :         if ([self status] == STATUS_ESCAPE_SEQUENCE) return; // if the player has ejected, don't deal more damage
    6367             :         if (amount == 0.0)  return;
    6368             :         
    6369             :         BOOL cascadeWeapon = [ent isCascadeWeapon];
    6370             :         BOOL cascading = NO;
    6371             :         if (cascadeWeapon)
    6372             :         {
    6373             :                 cascading = [self cascadeIfAppropriateWithDamageAmount:amount cascadeOwner:[ent owner]];
    6374             :         }
    6375             :         
    6376             :         // make sure ent (& its position) is the attacking _ship_/missile !
    6377             :         if (ent && [ent isSubEntity]) ent = [ent owner];
    6378             :         
    6379             :         [[ent retain] autorelease];
    6380             :         [[other retain] autorelease];
    6381             :         
    6382             :         rel_pos = (ent != nil) ? [ent position] : kZeroHPVector;
    6383             :         rel_pos = HPvector_subtract(rel_pos, position);
    6384             :         
    6385             :         [self doScriptEvent:OOJSID("shipBeingAttacked") withArgument:ent];
    6386             :         if ([ent isShip]) [(ShipEntity *)ent doScriptEvent:OOJSID("shipAttackedOther") withArgument:self];
    6387             : 
    6388             :         d_forward = dot_product(HPVectorToVector(rel_pos), v_forward);
    6389             :         d_right = dot_product(HPVectorToVector(rel_pos), v_right);
    6390             :         d_up = dot_product(HPVectorToVector(rel_pos), v_up);
    6391             :         Vector relative = make_vector(d_right,d_up,d_forward);
    6392             : 
    6393             :         [self playShieldHit:relative weaponIdentifier:weaponIdentifier];
    6394             : 
    6395             :         // firing on an innocent ship is an offence
    6396             :         if ([other isShip])
    6397             :         {
    6398             :                 [self broadcastHitByLaserFrom:(ShipEntity*) other];
    6399             :         }
    6400             : 
    6401             :         if (d_forward >= 0)
    6402             :         {
    6403             :                 forward_shield -= amount;
    6404             :                 if (forward_shield < 0.0)
    6405             :                 {
    6406             :                         amount = -forward_shield;
    6407             :                         forward_shield = 0.0f;
    6408             :                 }
    6409             :                 else
    6410             :                 {
    6411             :                         amount = 0.0;
    6412             :                 }
    6413             :         }
    6414             :         else
    6415             :         {
    6416             :                 aft_shield -= amount;
    6417             :                 if (aft_shield < 0.0)
    6418             :                 {
    6419             :                         amount = -aft_shield;
    6420             :                         aft_shield = 0.0f;
    6421             :                 }
    6422             :                 else
    6423             :                 {
    6424             :                         amount = 0.0;
    6425             :                 }
    6426             :         }
    6427             :         
    6428             :         OOShipDamageType damageType = cascadeWeapon ? kOODamageTypeCascadeWeapon : kOODamageTypeEnergy;
    6429             :         
    6430             :         if (amount > 0.0)
    6431             :         {
    6432             :                 energy -= amount;
    6433             :                 [self playDirectHit:relative weaponIdentifier:weaponIdentifier];
    6434             :                 if (ship_temperature < SHIP_MAX_CABIN_TEMP)
    6435             :                 {
    6436             :                         /* Heat increase from energy impacts will never directly cause
    6437             :                          * overheating - too easy for missile hits to cause an uncredited
    6438             :                          * death by overheating against NPCs, so same rules for player */
    6439             :                         ship_temperature += amount * SHIP_ENERGY_DAMAGE_TO_HEAT_FACTOR / [self heatInsulation];
    6440             :                         if (ship_temperature > SHIP_MAX_CABIN_TEMP)
    6441             :                         {
    6442             :                                 ship_temperature = SHIP_MAX_CABIN_TEMP;
    6443             :                         }
    6444             :                 }
    6445             :         }
    6446             :         [self noteTakingDamage:amount from:other type:damageType];
    6447             :         if (cascading) energy = 0.0; // explicitly set energy to zero when cascading, in case an oxp raised the energy in noteTakingDamage.
    6448             :         
    6449             :         if (energy <= 0.0) //use normal ship temperature calculations for heat damage
    6450             :         {
    6451             :                 if ([other isShip])
    6452             :                 {
    6453             :                         [(ShipEntity *)other noteTargetDestroyed:self];
    6454             :                 }
    6455             :                 
    6456             :                 [self getDestroyedBy:other damageType:damageType];
    6457             :         }
    6458             :         else
    6459             :         {
    6460             :                 while (amount > 0.0)
    6461             :                 {
    6462             :                         internal_damage = ((ranrot_rand() & PLAYER_INTERNAL_DAMAGE_FACTOR) < amount);    // base chance of damage to systems
    6463             :                         if (internal_damage)
    6464             :                         {
    6465             :                                 [self takeInternalDamage];
    6466             :                         }
    6467             :                         amount -= (PLAYER_INTERNAL_DAMAGE_FACTOR + 1);
    6468             :                 }
    6469             :         }
    6470             : }
    6471             : 
    6472             : 
    6473           0 : - (void) takeScrapeDamage:(double) amount from:(Entity *) ent
    6474             : {
    6475             :         HPVector  rel_pos;
    6476             :         OOScalar  d_forward, d_right, d_up;
    6477             :         BOOL    internal_damage = NO;   // base chance
    6478             :         
    6479             :         if ([self status] == STATUS_DEAD)  return;
    6480             :         
    6481             :         if (amount < 0) 
    6482             :         {
    6483             :                 OOLog(@"player.ship.damage",  @"Player took negative scrape damage %.3f so we made it positive", amount);
    6484             :                 amount = -amount;
    6485             :         }
    6486             :         OOLog(@"player.ship.damage",  @"Player took %.3f scrape damage from %@", amount, ent);
    6487             :         
    6488             :         [[ent retain] autorelease];
    6489             :         rel_pos = ent ? [ent position] : kZeroHPVector;
    6490             :         rel_pos = HPvector_subtract(rel_pos, position);
    6491             :         // rel_pos is now small
    6492             :         d_forward = dot_product(HPVectorToVector(rel_pos), v_forward);
    6493             :         d_right = dot_product(HPVectorToVector(rel_pos), v_right);
    6494             :         d_up = dot_product(HPVectorToVector(rel_pos), v_up);
    6495             :         Vector relative = make_vector(d_right,d_up,d_forward);
    6496             : 
    6497             :         [self playScrapeDamage:relative];
    6498             :         if (d_forward >= 0)
    6499             :         {
    6500             :                 forward_shield -= amount;
    6501             :                 if (forward_shield < 0.0)
    6502             :                 {
    6503             :                         amount = -forward_shield;
    6504             :                         forward_shield = 0.0f;
    6505             :                 }
    6506             :                 else
    6507             :                 {
    6508             :                         amount = 0.0;
    6509             :                 }
    6510             :         }
    6511             :         else
    6512             :         {
    6513             :                 aft_shield -= amount;
    6514             :                 if (aft_shield < 0.0)
    6515             :                 {
    6516             :                         amount = -aft_shield;
    6517             :                         aft_shield = 0.0f;
    6518             :                 }
    6519             :                 else
    6520             :                 {
    6521             :                         amount = 0.0;
    6522             :                 }
    6523             :         }
    6524             :         
    6525             :         [super takeScrapeDamage:amount from:ent];
    6526             :         
    6527             :         while (amount > 0.0)
    6528             :         {
    6529             :                 internal_damage = ((ranrot_rand() & PLAYER_INTERNAL_DAMAGE_FACTOR) < amount);    // base chance of damage to systems
    6530             :                 if (internal_damage)
    6531             :                 {
    6532             :                         [self takeInternalDamage];
    6533             :                 }
    6534             :                 amount -= (PLAYER_INTERNAL_DAMAGE_FACTOR + 1);
    6535             :         }
    6536             : }
    6537             : 
    6538             : 
    6539           0 : - (void) takeHeatDamage:(double) amount
    6540             : {
    6541             :         if ([self status] == STATUS_DEAD || amount < 0)  return;
    6542             :         
    6543             :         // hit the shields first!
    6544             :         float fwd_amount = (float)(0.5 * amount);
    6545             :         float aft_amount = (float)(0.5 * amount);
    6546             : 
    6547             :         forward_shield -= fwd_amount;
    6548             :         if (forward_shield < 0.0)
    6549             :         {
    6550             :                 fwd_amount = -forward_shield;
    6551             :                 forward_shield = 0.0f;
    6552             :         }
    6553             :         else
    6554             :         {
    6555             :                 fwd_amount = 0.0f;
    6556             :         }
    6557             : 
    6558             :         aft_shield -= aft_amount;
    6559             :         if (aft_shield < 0.0)
    6560             :         {
    6561             :                 aft_amount = -aft_shield;
    6562             :                 aft_shield = 0.0f;
    6563             :         }
    6564             :         else
    6565             :         {
    6566             :                 aft_amount = 0.0f;
    6567             :         }
    6568             : 
    6569             :         double residual_amount = fwd_amount + aft_amount;
    6570             :         
    6571             :         [super takeHeatDamage:residual_amount];
    6572             : }
    6573             : 
    6574             : 
    6575           0 : - (ProxyPlayerEntity *) createDoppelganger
    6576             : {
    6577             :         ProxyPlayerEntity *result = (ProxyPlayerEntity *)[[UNIVERSE newShipWithName:[self shipDataKey] usePlayerProxy:YES] autorelease];
    6578             :         
    6579             :         if (result != nil)
    6580             :         {
    6581             :                 [result setPosition:[self position]];
    6582             :                 [result setScanClass:CLASS_NEUTRAL];
    6583             :                 [result setOrientation:[self normalOrientation]];
    6584             :                 [result setVelocity:[self velocity]];
    6585             :                 [result setSpeed:[self flightSpeed]];
    6586             :                 [result setDesiredSpeed:[self flightSpeed]];
    6587             :                 [result setRoll:flightRoll];
    6588             :                 [result setBehaviour:BEHAVIOUR_IDLE];
    6589             :                 [result switchAITo:@"nullAI.plist"];  // fly straight on
    6590             :                 [result setTemperature:[self temperature]];
    6591             :                 [result copyValuesFromPlayer:self];
    6592             :         }
    6593             :         
    6594             :         return result;
    6595             : }
    6596             : 
    6597             : 
    6598           0 : - (ShipEntity *) launchEscapeCapsule
    6599             : {
    6600             :         ShipEntity              *doppelganger = nil;
    6601             :         ShipEntity              *escapePod = nil;
    6602             :         
    6603             :         if ([UNIVERSE displayGUI]) [self switchToMainView];     // Clear the F7 screen!
    6604             :         [UNIVERSE setViewDirection:VIEW_FORWARD];
    6605             :         
    6606             :         if ([self status] == STATUS_DEAD)  return nil;
    6607             :         
    6608             :         /*
    6609             :                 While inside the escape pod, we need to block access to all player.ship properties,
    6610             :                 since we're not supposed to be inside our ship anymore! -- Kaks 20101114
    6611             :         */
    6612             :         
    6613             :         [UNIVERSE setBlockJSPlayerShipProps:YES];       // no player.ship properties while inside the pod!
    6614             :         // if a specific amount of time has been provided for the rescue, use it now
    6615             :         if (escape_pod_rescue_time > 0) 
    6616             :         {
    6617             :                 ship_clock_adjust += escape_pod_rescue_time;
    6618             :                 escape_pod_rescue_time = 0; // reset value
    6619             :         } 
    6620             :         else 
    6621             :         {
    6622             :                 // otherwise, use the default time calc
    6623             :                 ship_clock_adjust += 43200 + 5400 * (ranrot_rand() & 127);  // add up to 8 days until rescue!
    6624             :         }
    6625             :         dockingClearanceStatus = DOCKING_CLEARANCE_STATUS_NOT_REQUIRED;
    6626             :         flightSpeed = fmin(flightSpeed, maxFlightSpeed);
    6627             :         
    6628             :         doppelganger = [self createDoppelganger];
    6629             :         if (doppelganger)
    6630             :         {
    6631             :                 [doppelganger setVelocity:vector_multiply_scalar(v_forward, flightSpeed)];
    6632             :                 [doppelganger setSpeed:0.0];
    6633             :                 [doppelganger setDesiredSpeed:0.0];
    6634             :                 [doppelganger setRoll:0.2 * (randf() - 0.5)];
    6635             :                 [doppelganger setOwner:self];
    6636             :                 [doppelganger setThrust:0]; // drifts
    6637             :                 [UNIVERSE addEntity:doppelganger];
    6638             :         }
    6639             : 
    6640             :         [self setFoundTarget:doppelganger]; // must do this before setting status
    6641             :         [self setStatus:STATUS_ESCAPE_SEQUENCE];        // now set up the escape sequence.
    6642             : 
    6643             : 
    6644             :         // must do this before next step or uses BBox of pod, not old ship!
    6645             :         float sheight = (float)(boundingBox.max.y - boundingBox.min.y);
    6646             :         position = HPvector_subtract(position, vectorToHPVector(vector_multiply_scalar(v_up, sheight)));
    6647             :         float sdepth = (float)(boundingBox.max.z - boundingBox.min.z);
    6648             :         position = HPvector_subtract(position, vectorToHPVector(vector_multiply_scalar(v_forward, sdepth/2.0)));
    6649             : 
    6650             :         // set up you
    6651             :         escapePod = [UNIVERSE newShipWithName:@"escape-capsule"];     // retained
    6652             :         if (escapePod != nil)
    6653             :         {
    6654             :                 // FIXME: this should use OOShipType, which should exist. -- Ahruman
    6655             :                 [self setMesh:[escapePod mesh]];
    6656             :         }
    6657             :         
    6658             :         /* These used to be non-zero, but BEHAVIOUR_IDLE levels off flight
    6659             :          * anyway, and inertial velocity is used instead of inertialess
    6660             :          * thrust - CIM */
    6661             :         flightSpeed = 0.0f;
    6662             :         flightPitch = 0.0f;
    6663             :         flightRoll = 0.0f;
    6664             :         flightYaw = 0.0f;
    6665             :         // and turn off inertialess drive
    6666             :         thrust = 0.0f;
    6667             :         
    6668             : 
    6669             :         /*      Add an impulse upwards and backwards to the escape pod. This avoids
    6670             :                 flying straight through the doppelganger in interstellar space or when
    6671             :                 facing the main station/escape target, and generally looks cool.
    6672             :                 -- Ahruman 2011-04-02
    6673             :         */
    6674             :         Vector launchVector = vector_add([doppelganger velocity],
    6675             :                                                         vector_add(vector_multiply_scalar(v_up, 15.0f),
    6676             :                                                                            vector_multiply_scalar(v_forward, -90.0f)));
    6677             :         [self setVelocity:launchVector];
    6678             :         
    6679             : 
    6680             : 
    6681             :         // if multiple items providing escape pod, remove the first one
    6682             :         [self removeEquipmentItem:[self equipmentItemProviding:@"EQ_ESCAPE_POD"]];
    6683             : 
    6684             :         
    6685             :         // set up the standard location where the escape pod will dock.
    6686             :         target_system_id = system_id;                   // we're staying in this system
    6687             :         info_system_id = system_id;
    6688             :         [self setDockTarget:[UNIVERSE station]];        // we're docking at the main station, if there is one
    6689             :         
    6690             :         [self doScriptEvent:OOJSID("shipLaunchedEscapePod") withArgument:escapePod];  // no player.ship properties should be available to script
    6691             : 
    6692             :         // reset legal status
    6693             :         [self setBounty:0 withReason:kOOLegalStatusReasonEscapePod];
    6694             :         bounty = 0;
    6695             : 
    6696             :         // new ship, so lose some memory of player actions
    6697             :         if (ship_kills >= 6400)
    6698             :         {
    6699             :                 [self clearRolesFromPlayer:0.1];
    6700             :         }
    6701             :         else if (ship_kills >= 2560)
    6702             :         {
    6703             :                 [self clearRolesFromPlayer:0.25];
    6704             :         }
    6705             :         else
    6706             :         {
    6707             :                 [self clearRolesFromPlayer:0.5];
    6708             :         }       
    6709             : 
    6710             :         // reset trumbles
    6711             :         if (trumbleCount != 0)  trumbleCount = 1;
    6712             :         
    6713             :         // remove cargo
    6714             :         [cargo removeAllObjects];
    6715             :         
    6716             :         energy = 25;
    6717             :         [UNIVERSE addMessage:DESC(@"escape-sequence") forCount:4.5];
    6718             :         [self resetShotTime];
    6719             :         
    6720             :         // need to zero out all facings shot_times too, otherwise we may end up
    6721             :         // with a broken escape pod sequence - Nikos 20100909
    6722             :         forward_shot_time = 0.0;
    6723             :         aft_shot_time = 0.0;
    6724             :         port_shot_time = 0.0;
    6725             :         starboard_shot_time = 0.0;
    6726             :         
    6727             :         [escapePod release];
    6728             :         
    6729             :         return doppelganger;
    6730             : }
    6731             : 
    6732             : 
    6733           0 : - (OOCommodityType) dumpCargo
    6734             : {
    6735             :         if (flightSpeed > 4.0 * maxFlightSpeed)
    6736             :         {
    6737             :                 [UNIVERSE addMessage:OOExpandKey(@"hold-locked") forCount:3.0];
    6738             :                 return nil;
    6739             :         }
    6740             : 
    6741             :         OOCommodityType result = [super dumpCargo];
    6742             :         if (result != nil)
    6743             :         {
    6744             :                 NSString *commodity = [UNIVERSE displayNameForCommodity:result];
    6745             :                 [UNIVERSE addMessage:OOExpandKey(@"commodity-ejected", commodity) forCount:3.0 forceDisplay:YES];
    6746             :                 [self playCargoJettisioned];
    6747             :         }
    6748             :         return result;
    6749             : }
    6750             : 
    6751             : 
    6752             : - (void) rotateCargo
    6753             : {
    6754             :         NSInteger i, n_cargo = [cargo count];
    6755             :         if (n_cargo == 0)  return;
    6756             :         
    6757             :         ShipEntity *pod = (ShipEntity *)[[cargo objectAtIndex:0] retain];
    6758             :         OOCommodityType current_contents = [pod commodityType];
    6759             :         OOCommodityType contents;
    6760             :         NSInteger rotates = 0;
    6761             :         
    6762             :         do
    6763             :         {
    6764             :                 [cargo removeObjectAtIndex:0];  // take it from the eject position
    6765             :                 [cargo addObject:pod];  // move it to the last position
    6766             :                 [pod release];
    6767             :                 pod = (ShipEntity*)[[cargo objectAtIndex:0] retain];
    6768             :                 contents = [pod commodityType];
    6769             :                 rotates++;
    6770             :         } while ([contents isEqualToString:current_contents]&&(rotates < n_cargo));
    6771             :         [pod release];
    6772             :         
    6773             :         NSString *commodity = [UNIVERSE displayNameForCommodity:contents];
    6774             :         [UNIVERSE addMessage:OOExpandKey(@"ready-to-eject-commodity", commodity) forCount:3.0];
    6775             : 
    6776             :         // now scan through the remaining 1..(n_cargo - rotates) places moving similar cargo to the last place
    6777             :         // this means the cargo gets to be sorted as it is rotated through
    6778             :         for (i = 1; i < (n_cargo - rotates); i++)
    6779             :         {
    6780             :                 pod = [cargo objectAtIndex:i];
    6781             :                 if ([[pod commodityType] isEqualToString:current_contents])
    6782             :                 {
    6783             :                         [pod retain];
    6784             :                         [cargo removeObjectAtIndex:i--];
    6785             :                         [cargo addObject:pod];
    6786             :                         [pod release];
    6787             :                         rotates++;
    6788             :                 }
    6789             :         }
    6790             : }
    6791             : 
    6792             : 
    6793           0 : - (void) setBounty:(OOCreditsQuantity) amount
    6794             : {
    6795             :         [self setBounty:amount withReason:kOOLegalStatusReasonUnknown];
    6796             : }
    6797             : 
    6798             : 
    6799           0 : - (void) setBounty:(OOCreditsQuantity)amount withReason:(OOLegalStatusReason)reason
    6800             : {
    6801             :         NSString *nReason = OOStringFromLegalStatusReason(reason);
    6802             :         [self setBounty:amount withReasonAsString:nReason];
    6803             : }
    6804             : 
    6805             : 
    6806           0 : - (void) setBounty:(OOCreditsQuantity)amount withReasonAsString:(NSString *)reason
    6807             : {
    6808             :         JSContext *context = OOJSAcquireContext();
    6809             :         
    6810             :         jsval amountVal = JSVAL_VOID;
    6811             :         int amountVal2 = (int)amount-(int)legalStatus;
    6812             :         JS_NewNumberValue(context, amountVal2, &amountVal);
    6813             : 
    6814             :         legalStatus = (int)amount; // can't set the new bounty until the size of the change is known
    6815             : 
    6816             :         jsval reasonVal = OOJSValueFromNativeObject(context,reason);
    6817             :                 
    6818             :         ShipScriptEvent(context, self, "shipBountyChanged", amountVal, reasonVal);
    6819             :                 
    6820             :         OOJSRelinquishContext(context);
    6821             : }
    6822             : 
    6823             : 
    6824           0 : - (OOCreditsQuantity) bounty            // overrides returning 'bounty'
    6825             : {
    6826             :         return legalStatus;
    6827             : }
    6828             : 
    6829             : 
    6830             : - (int) legalStatus
    6831             : {
    6832             :         return legalStatus;
    6833             : }
    6834             : 
    6835             : 
    6836           0 : - (void) markAsOffender:(int)offence_value
    6837             : {
    6838             :         [self markAsOffender:offence_value withReason:kOOLegalStatusReasonUnknown];
    6839             : }
    6840             : 
    6841             : 
    6842           0 : - (void) markAsOffender:(int)offence_value withReason:(OOLegalStatusReason)reason
    6843             : {
    6844             :         if (![self isCloaked])
    6845             :         {
    6846             :                 JSContext *context = OOJSAcquireContext();
    6847             :         
    6848             :                 jsval amountVal = JSVAL_VOID;
    6849             :                 int amountVal2 = (legalStatus | offence_value) - legalStatus;
    6850             :                 JS_NewNumberValue(context, amountVal2, &amountVal);
    6851             : 
    6852             :                 legalStatus |= offence_value; // can't set the new bounty until the size of the change is known
    6853             : 
    6854             :                 jsval reasonVal = OOJSValueFromLegalStatusReason(context, reason);
    6855             :                 
    6856             :                 ShipScriptEvent(context, self, "shipBountyChanged", amountVal, reasonVal);
    6857             :                 
    6858             :                 OOJSRelinquishContext(context);
    6859             : 
    6860             :         }
    6861             : }
    6862             : 
    6863             : 
    6864           0 : - (void) collectBountyFor:(ShipEntity *)other
    6865             : {
    6866             :         if ([self status] == STATUS_DEAD)  return; // no bounty if we died while trying
    6867             :         
    6868             :         if (other == nil || [other isSubEntity])  return;
    6869             :         
    6870             :         if (other == [UNIVERSE station])
    6871             :         {
    6872             :                 // there is no way the player can destroy the main station
    6873             :                 // and so the explosion will be cancelled, so there shouldn't
    6874             :                 // be a kill award
    6875             :                 return;
    6876             :         }
    6877             : 
    6878             :         if ([self isCloaked])
    6879             :         {
    6880             :                 // no-one knows about it; no award
    6881             :                 return;
    6882             :         }
    6883             : 
    6884             :         OOCreditsQuantity       score = 10 * [other bounty];
    6885             :         OOScanClass                     killClass = [other scanClass]; // **tgape** change (+line)
    6886             :         BOOL                            killAward = [other countsAsKill];
    6887             :         
    6888             :         if ([other isPolice])   // oops, we shot a copper!
    6889             :         {
    6890             :                 [self markAsOffender:64 withReason:kOOLegalStatusReasonAttackedPolice];
    6891             :         }
    6892             :         
    6893             :         BOOL killIsCargo = ((killClass == CLASS_CARGO) && ([other commodityAmount] > 0) && ![other isHulk]);
    6894             :         if ((killIsCargo) || (killClass == CLASS_BUOY) || (killClass == CLASS_ROCK))
    6895             :         {
    6896             :                 // EMMSTRAN: no killaward (but full bounty) for tharglets?
    6897             :                 if (![other hasRole:@"tharglet"])     // okay, we'll count tharglets as proper kills
    6898             :                 {
    6899             :                         score /= 10;    // reduce bounty awarded
    6900             :                         killAward = NO; // don't award a kill
    6901             :                 }
    6902             :         }
    6903             :         
    6904             :         credits += score;
    6905             :         
    6906             :         if (score > 9)
    6907             :         {
    6908             :                 NSString *bonusMessage = OOExpandKey(@"bounty-awarded", score, credits);
    6909             :                 [UNIVERSE addDelayedMessage:bonusMessage forCount:6 afterDelay:0.15];
    6910             :         }
    6911             :         
    6912             :         if (killAward)
    6913             :         {
    6914             :                 ship_kills++;
    6915             :                 if ((ship_kills % 256) == 0)
    6916             :                 {
    6917             :                         // congratulations method needs to be delayed a fraction of a second
    6918             :                         [UNIVERSE addDelayedMessage:DESC(@"right-on-commander") forCount:4 afterDelay:0.2];
    6919             :                 }
    6920             :         }
    6921             : }
    6922             : 
    6923             : 
    6924             : - (BOOL) takeInternalDamage
    6925             : {
    6926             :         unsigned n_cargo = [self maxAvailableCargoSpace];
    6927             :         unsigned n_mass = [self mass] / 10000;
    6928             :         unsigned n_considered = (n_cargo + n_mass) * ship_trade_in_factor / 100; // a lower value of n_considered means more vulnerable to damage.
    6929             :         unsigned damage_to = n_considered ? (ranrot_rand() % n_considered) : 0; // n_considered can be 0 for small ships.
    6930             :         BOOL     result = NO;
    6931             :         // cargo damage
    6932             :         if (damage_to < [cargo count])
    6933             :         {
    6934             :                 ShipEntity* pod = (ShipEntity*)[cargo objectAtIndex:damage_to];
    6935             :                 NSString* cargo_desc = [UNIVERSE displayNameForCommodity:[pod commodityType]];
    6936             :                 if (!cargo_desc)
    6937             :                         return NO;
    6938             :                 [UNIVERSE clearPreviousMessage];
    6939             :                 [UNIVERSE addMessage:[NSString stringWithFormat:DESC(@"@-destroyed"), cargo_desc] forCount:4.5];
    6940             :                 [cargo removeObject:pod];
    6941             :                 return YES;
    6942             :         }
    6943             :         else
    6944             :         {
    6945             :                 damage_to = n_considered - (damage_to + 1);     // reverse the die-roll
    6946             :         }
    6947             :         // equipment damage
    6948             :         NSEnumerator *eqEnum = [self equipmentEnumerator];
    6949             :         OOEquipmentType *eqType = nil;
    6950             :         NSString                *system_key;
    6951             :         unsigned damageableCounter = 0;
    6952             :         GLfloat damageableOdds = 0.0;
    6953             :         while ((system_key = [eqEnum nextObject]) != nil)
    6954             :         {
    6955             :                 eqType = [OOEquipmentType equipmentTypeWithIdentifier:system_key];
    6956             :                 if ([eqType canBeDamaged])
    6957             :                 {
    6958             :                         damageableCounter++;
    6959             :                         damageableOdds += [eqType damageProbability];
    6960             :                 }
    6961             :         }
    6962             : 
    6963             :         if (damage_to < damageableCounter)
    6964             :         {
    6965             :                 GLfloat target = randf() * damageableOdds;
    6966             :                 GLfloat accumulator = 0.0;
    6967             :                 eqEnum = [self equipmentEnumerator];
    6968             :                 while ((system_key = [eqEnum nextObject]) != nil)
    6969             :                 {
    6970             :                         eqType = [OOEquipmentType equipmentTypeWithIdentifier:system_key];
    6971             :                         accumulator += [eqType damageProbability];
    6972             :                         if (accumulator > target) 
    6973             :                         {
    6974             :                                 [system_key retain];
    6975             :                                 break;
    6976             :                         }
    6977             :                 }
    6978             :                 if (system_key == nil)
    6979             :                 {
    6980             :                         [system_key release];
    6981             :                         return NO;
    6982             :                 }
    6983             : 
    6984             :                 NSString                *system_name = [eqType name];
    6985             :                 if (![eqType canBeDamaged] || system_name == nil)
    6986             :                 {
    6987             :                         [system_key release];
    6988             :                         return NO;
    6989             :                 }
    6990             : 
    6991             :                 // set the following so removeEquipment works on the right entity
    6992             :                 [self setScriptTarget:self];
    6993             :                 [UNIVERSE clearPreviousMessage];
    6994             :                 [self removeEquipmentItem:system_key];
    6995             : 
    6996             :                 NSString *damagedKey = [NSString stringWithFormat:@"%@_DAMAGED", system_key];
    6997             :                 [self addEquipmentItem:damagedKey withValidation: NO inContext:@"damage"];    // for possible future repair.
    6998             :                 [self doScriptEvent:OOJSID("equipmentDamaged") withArgument:system_key];
    6999             :                         
    7000             :                 if (![self hasEquipmentItem:system_name] && [self hasEquipmentItem:damagedKey])
    7001             :                 {
    7002             :                         /*
    7003             :                                 Display "foo damaged" message only if no script has
    7004             :                                 repaired or removed the equipment item. (If a script does
    7005             :                                 either of those and wants a message, it can write it
    7006             :                                 itself.)
    7007             :                         */
    7008             :                         [UNIVERSE addMessage:[NSString stringWithFormat:DESC(@"@-damaged"), system_name] forCount:4.5];
    7009             :                 }
    7010             :                 
    7011             :                 /* There used to be a check for docking computers here, but
    7012             :                  * that didn't cover other ways they might fail in flight, so
    7013             :                  * it has been moved to the removeEquipment method. */
    7014             :                 [system_key release];
    7015             :                 return YES;
    7016             :         }
    7017             :         //cosmetic damage
    7018             :         if (((damage_to & 7) == 7)&&(ship_trade_in_factor > 75))
    7019             :         {
    7020             :                 ship_trade_in_factor--;
    7021             :                 result = YES;
    7022             :         }
    7023             :         return result;
    7024             : }
    7025             : 
    7026             : 
    7027           0 : - (void) getDestroyedBy:(Entity *)whom damageType:(OOShipDamageType)type
    7028             : {
    7029             :         if ([self isDocked])  return;   // Can't die while docked. (Doing so would cause breakage elsewhere.)
    7030             :         
    7031             :         OOLog(@"player.ship.damage",  @"Player destroyed by %@ due to %@", whom, OOStringFromShipDamageType(type)); 
    7032             :         
    7033             :         if (![[UNIVERSE gameController] playerFileToLoad])
    7034             :         {
    7035             :                 [[UNIVERSE gameController] setPlayerFileToLoad: save_path];     // make sure we load the correct game
    7036             :         }
    7037             :         
    7038             :         energy = 0.0f;
    7039             :         afterburner_engaged = NO;
    7040             :         [self disengageAutopilot];
    7041             : 
    7042             :         [UNIVERSE setDisplayText:NO];
    7043             :         [UNIVERSE setViewDirection:VIEW_AFT];
    7044             :         
    7045             :         // Let scripts know the player died.
    7046             :         [self noteKilledBy:whom damageType:type]; // called before exploding, consistant with npc ships.
    7047             :         
    7048             :         [self becomeLargeExplosion:4.0]; // also sets STATUS_DEAD
    7049             :         [self moveForward:100.0];
    7050             :         
    7051             :         flightSpeed = 160.0f;
    7052             :         velocity = kZeroVector;
    7053             :         flightRoll = 0.0;
    7054             :         flightPitch = 0.0;
    7055             :         flightYaw = 0.0;
    7056             :         [[UNIVERSE messageGUI] clear];          // No messages for the dead.
    7057             :         [self suppressTargetLost];                      // No target lost messages when dead.
    7058             :         [self playGameOver];
    7059             :         [UNIVERSE setBlockJSPlayerShipProps:YES];       // Treat JS player as stale entity.
    7060             :         [self removeAllEquipment];                      // No scooping / equipment damage when dead.
    7061             :         [self loseTargetStatus];
    7062             :         [self showGameOver];
    7063             : }
    7064             : 
    7065             : 
    7066             : - (void) loseTargetStatus
    7067             : {
    7068             :         if (!UNIVERSE)
    7069             :                 return;
    7070             :         int                     ent_count =             UNIVERSE->n_entities;
    7071             :         Entity**        uni_entities =  UNIVERSE->sortedEntities;    // grab the public sorted list
    7072             :         Entity*         my_entities[ent_count];
    7073             :         int i;
    7074             :         for (i = 0; i < ent_count; i++)
    7075             :                 my_entities[i] = [uni_entities[i] retain];              //      retained
    7076             :         for (i = 0; i < ent_count ; i++)
    7077             :         {
    7078             :                 Entity* thing = my_entities[i];
    7079             :                 if (thing->isShip)
    7080             :                 {
    7081             :                         ShipEntity* ship = (ShipEntity *)thing;
    7082             :                         if (self == [ship primaryTarget])
    7083             :                         {
    7084             :                                 [ship noteLostTarget];
    7085             :                         }
    7086             :                 }
    7087             :         }
    7088             :         for (i = 0; i < ent_count; i++)
    7089             :         {
    7090             :                 [my_entities[i] release];               //      released
    7091             :         }
    7092             : }
    7093             : 
    7094             : 
    7095             : - (BOOL) endScenario:(NSString *)key
    7096             : {
    7097             :         if (scenarioKey != nil && [key isEqualToString:scenarioKey])
    7098             :         {
    7099             :                 [self setStatus:STATUS_RESTART_GAME];
    7100             :                 return YES;
    7101             :         }
    7102             :         return NO;
    7103             : }
    7104             : 
    7105             : 
    7106           0 : - (void) enterDock:(StationEntity *)station
    7107             : {
    7108             :         NSParameterAssert(station != nil);
    7109             :         if ([self status] == STATUS_DEAD)  return;
    7110             :         
    7111             :         [self setStatus:STATUS_DOCKING];
    7112             :         [self setDockedStation:station];
    7113             :         [self doScriptEvent:OOJSID("shipWillDockWithStation") withArgument:station];
    7114             : 
    7115             :         if (![hud nonlinearScanner])
    7116             :         {
    7117             :                 [hud setScannerZoom: 1.0];
    7118             :         }
    7119             :         ident_engaged = NO;
    7120             :         afterburner_engaged = NO;
    7121             :         autopilot_engaged = NO;
    7122             :         [self resetAutopilotAI];
    7123             :         
    7124             :         cloaking_device_active = NO;
    7125             :         hyperspeed_engaged = NO;
    7126             :         hyperspeed_locked = NO;
    7127             :         [self safeAllMissiles];
    7128             :         DESTROY(_primaryTarget); // must happen before showing break_pattern to suppress active reticule.
    7129             :         [self clearTargetMemory];
    7130             :         
    7131             :         scanner_zoom_rate = 0.0f;
    7132             :         [UNIVERSE setDisplayText:NO];
    7133             :         [[UNIVERSE gameController] setMouseInteractionModeForFlight];
    7134             :         if ([self status] == STATUS_LAUNCHING)  return; // a JS script has aborted the docking.
    7135             :         
    7136             :         [self setOrientation: kIdentityQuaternion];     // reset orientation to dock
    7137             :         [UNIVERSE setUpBreakPattern:[self breakPatternPosition] orientation:orientation forDocking:YES];
    7138             :         [self playDockWithStation];
    7139             :         [station noteDockedShip:self];
    7140             :         
    7141             :         [[UNIVERSE gameView] clearKeys];        // try to stop key bounces
    7142             : }
    7143             : 
    7144             : 
    7145             : - (void) docked
    7146             : {
    7147             :         StationEntity *dockedStation = [self dockedStation];
    7148             :         if (dockedStation == nil)
    7149             :         {
    7150             :                 [self setStatus:STATUS_IN_FLIGHT];
    7151             :                 return;
    7152             :         }
    7153             :         
    7154             :         [self setStatus:STATUS_DOCKED];
    7155             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];
    7156             :         
    7157             :         [self loseTargetStatus];
    7158             :         
    7159             :         [self setPosition:[dockedStation position]];
    7160             :         [self setOrientation:kIdentityQuaternion];      // reset orientation to dock
    7161             :         
    7162             :         flightRoll = 0.0f;
    7163             :         flightPitch = 0.0f;
    7164             :         flightYaw = 0.0f;
    7165             :         flightSpeed = 0.0f;
    7166             :         
    7167             :         hyperspeed_engaged = NO;
    7168             :         hyperspeed_locked = NO;
    7169             :         
    7170             :         forward_shield =        [self maxForwardShieldLevel];
    7171             :         aft_shield =            [self maxAftShieldLevel];
    7172             :         energy =                        maxEnergy;
    7173             :         weapon_temp =           0.0f;
    7174             :         ship_temperature =      60.0f;
    7175             : 
    7176             :         [self setAlertFlag:ALERT_FLAG_DOCKED to:YES];
    7177             : 
    7178             :         if ([dockedStation localMarket] == nil)
    7179             :         {
    7180             :                 [dockedStation initialiseLocalMarket];
    7181             :         }
    7182             : 
    7183             :         NSString *escapepodReport = [self processEscapePods];
    7184             :         [self addMessageToReport:escapepodReport];
    7185             :         
    7186             :         [self unloadCargoPods]; // fill up the on-ship commodities before...
    7187             : 
    7188             :         // check import status of station
    7189             :         // escape pods must be cleared before this happens
    7190             :         if ([dockedStation marketMonitored])
    7191             :         {
    7192             :                 OOCreditsQuantity oldbounty = [self bounty];
    7193             :                 [self markAsOffender:[dockedStation legalStatusOfManifest:shipCommodityData export:NO] withReason:kOOLegalStatusReasonIllegalImports];
    7194             :                 if ([self bounty] > oldbounty)
    7195             :                 {
    7196             :                         [self addRoleToPlayer:@"trader-smuggler"];
    7197             :                 }
    7198             :         }
    7199             : 
    7200             :         // check contracts
    7201             :         NSString *passengerAndCargoReport = [self checkPassengerContracts]; // Is also processing cargo and parcel contracts.
    7202             :         [self addMessageToReport:passengerAndCargoReport];
    7203             :                 
    7204             :         [UNIVERSE setDisplayText:YES];
    7205             :         
    7206             :         [[OOMusicController sharedController] stopDockingMusic];
    7207             :         [[OOMusicController sharedController] playDockedMusic];
    7208             :         
    7209             :         // Did we fail to observe traffic control regulations? However, due to the state of emergency,
    7210             :         // apply no unauthorized docking penalties if a nova is ongoing.
    7211             :         if ([dockedStation requiresDockingClearance] &&
    7212             :                         ![self clearedToDock] && ![[UNIVERSE sun] willGoNova])
    7213             :         {
    7214             :                 [self penaltyForUnauthorizedDocking];
    7215             :         }
    7216             :                 
    7217             :         // apply any pending fines. (No need to check gui_screen as fines is no longer an on-screen message).
    7218             :         if (dockedStation == [UNIVERSE station])
    7219             :         {
    7220             :                 // TODO: A proper system to allow some OXP stations to have a
    7221             :                 // galcop presence for fines. - CIM 18/11/2012
    7222             :                 if (being_fined && ![[UNIVERSE sun] willGoNova] && ![dockedStation suppressArrivalReports]) [self getFined];
    7223             :         }
    7224             : 
    7225             :         // it's time to check the script - can trigger legacy missions
    7226             :         if (gui_screen != GUI_SCREEN_MISSION)  [self checkScript]; // a scripted pilot could have created a mission screen.
    7227             :         
    7228             :         OOJSStartTimeLimiterWithTimeLimit(kOOJSLongTimeLimit);
    7229             :         [self doScriptEvent:OOJSID("shipDockedWithStation") withArgument:dockedStation];
    7230             :         OOJSStopTimeLimiter();
    7231             :         if ([self status] == STATUS_LAUNCHING) return;
    7232             : 
    7233             :         // if we've not switched to the mission screen yet then proceed normally..
    7234             :         if (gui_screen != GUI_SCREEN_MISSION)
    7235             :         {
    7236             :                 [self setGuiToStatusScreen];
    7237             :         }
    7238             :         [[OOCacheManager sharedCache] flush];
    7239             :         [[OOJavaScriptEngine sharedEngine] garbageCollectionOpportunity:YES];
    7240             :         
    7241             :         // When a mission screen is started, any on-screen message is removed immediately.
    7242             :         [self doWorldEventUntilMissionScreen:OOJSID("missionScreenOpportunity")];     // also displays docking reports first.
    7243             : }
    7244             : 
    7245             : 
    7246           0 : - (void) leaveDock:(StationEntity *)station
    7247             : {
    7248             :         if (station == nil)  return;
    7249             :         NSParameterAssert(station == [self dockedStation]);
    7250             :         
    7251             :         // ensure we've not left keyboard entry on
    7252             :         [[UNIVERSE gameView] allowStringInput: NO];
    7253             :         
    7254             :         if (gui_screen == GUI_SCREEN_MISSION)
    7255             :         {
    7256             :                 [[UNIVERSE gui] clearBackground];
    7257             :                 if (_missionWithCallback)
    7258             :                 {
    7259             :                         [self doMissionCallback];
    7260             :                 }
    7261             :                 // notify older scripts, but do not trigger missionScreenOpportunity.
    7262             :                 [self doWorldEventUntilMissionScreen:OOJSID("missionScreenEnded")];
    7263             :         }
    7264             :         
    7265             :         if ([station marketMonitored])
    7266             :         {
    7267             :                 // 'leaving with those guns were you sir?'
    7268             :                 OOCreditsQuantity oldbounty = [self bounty];
    7269             :                 [self markAsOffender:[station legalStatusOfManifest:shipCommodityData export:YES] withReason:kOOLegalStatusReasonIllegalExports];
    7270             :                 if ([self bounty] > oldbounty)
    7271             :                 {
    7272             :                         [self addRoleToPlayer:@"trader-smuggler"];
    7273             :                 }
    7274             :         }
    7275             :         OOGUIScreenID   oldScreen = gui_screen;
    7276             :         gui_screen = GUI_SCREEN_MAIN;
    7277             :         [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
    7278             : 
    7279             :         if (![hud nonlinearScanner])
    7280             :         {
    7281             :                 [hud setScannerZoom: 1.0];
    7282             :         }
    7283             :         [self loadCargoPods];
    7284             :         // do not do anything that calls JS handlers between now and calling
    7285             :         // [station launchShip] below, or the cargo returned by JS may be off
    7286             :         // CIM - 3.2.2012
    7287             :         
    7288             :         // clear the way
    7289             :         [station autoDockShipsOnApproach];
    7290             :         [station clearDockingCorridor];
    7291             : 
    7292             : //      [self setAlertFlag:ALERT_FLAG_DOCKED to:NO];
    7293             :         [self clearAlertFlags];
    7294             :         [self setDockingClearanceStatus:DOCKING_CLEARANCE_STATUS_NONE];
    7295             :         
    7296             :         scanner_zoom_rate = 0.0f;
    7297             :         currentWeaponFacing = WEAPON_FACING_FORWARD;
    7298             :         [self currentWeaponStats];
    7299             :         
    7300             :         forward_weapon_temp = 0.0f;
    7301             :         aft_weapon_temp = 0.0f;
    7302             :         port_weapon_temp = 0.0f;
    7303             :         starboard_weapon_temp = 0.0f;
    7304             :         
    7305             :         forward_shield = [self maxForwardShieldLevel];
    7306             :         aft_shield = [self maxAftShieldLevel];
    7307             : 
    7308             :         [self clearTargetMemory];
    7309             :         [self setShowDemoShips:NO];
    7310             :         [UNIVERSE setDisplayText:NO];
    7311             :         [[UNIVERSE gameController] setMouseInteractionModeForFlight];
    7312             : 
    7313             :         [[UNIVERSE gameView] clearKeys];        // try to stop keybounces
    7314             :         
    7315             :         if ([self isMouseControlOn])
    7316             :         {
    7317             :                 [[UNIVERSE gameView] resetMouse];
    7318             :         }
    7319             :         
    7320             :         [[OOMusicController sharedController] stop];
    7321             : 
    7322             :         [UNIVERSE forceWitchspaceEntries];
    7323             :         ship_clock_adjust += 600.0;                     // 10 minutes to leave dock
    7324             :         velocity = kZeroVector; // just in case
    7325             : 
    7326             :         [station launchShip:self];
    7327             : 
    7328             :         launchRoll = -flightRoll; // save the station's spin. (inverted for player)
    7329             :         flightRoll = 0; // don't spin when showing the break pattern.
    7330             :         [UNIVERSE setUpBreakPattern:[self breakPatternPosition] orientation:orientation forDocking:YES];
    7331             : 
    7332             :         [self setDockedStation:nil];
    7333             :         
    7334             :         suppressAegisMessages = YES;
    7335             :         [self checkForAegis];
    7336             :         suppressAegisMessages = NO;
    7337             :         ident_engaged = NO;
    7338             :         
    7339             :         [UNIVERSE removeDemoShips];
    7340             :         // MKW - ensure GUI Screen ship is removed
    7341             :         [demoShip release];
    7342             :         demoShip = nil;
    7343             :         
    7344             :         [self playLaunchFromStation];
    7345             : }
    7346             : 
    7347             : 
    7348           0 : - (void) witchStart
    7349             : {
    7350             :         // chances of entering witchspace with autopilot on are very low, but as Berlios bug #18307 has shown us, entirely possible
    7351             :         // so in such cases we need to ensure that at least the docking music stops playing
    7352             :         if (autopilot_engaged)  [self disengageAutopilot];
    7353             :         
    7354             :         if (![hud nonlinearScanner])
    7355             :         {
    7356             :                 [hud setScannerZoom: 1.0];
    7357             :         }
    7358             :         [self safeAllMissiles];
    7359             :         
    7360             :         OOViewID        previousViewDirection = [UNIVERSE viewDirection];
    7361             :         [UNIVERSE setViewDirection:VIEW_FORWARD];
    7362             :         [self noteSwitchToView:VIEW_FORWARD fromView:previousViewDirection]; // notifies scripts of the switch
    7363             :         
    7364             :         currentWeaponFacing = WEAPON_FACING_FORWARD;
    7365             :         [self currentWeaponStats];
    7366             : 
    7367             :         [self transitionToAegisNone];
    7368             :         suppressAegisMessages=YES;
    7369             :         hyperspeed_engaged = NO;
    7370             :         
    7371             :         if ([self primaryTarget] != nil)
    7372             :         {
    7373             :                 [self noteLostTarget];  // losing target? Fire lost target event!
    7374             :                 DESTROY(_primaryTarget);
    7375             :         }
    7376             :         
    7377             :         scanner_zoom_rate = 0.0f;
    7378             :         [UNIVERSE setDisplayText:NO];
    7379             :         
    7380             :         if ( ![self wormhole] && !galactic_witchjump)   // galactic hyperspace does not generate a wormhole
    7381             :         {
    7382             :                 OOLog(kOOLogInconsistentState, @"%@", @"Internal Error : Player entering witchspace with no wormhole.");
    7383             :         }
    7384             :         [UNIVERSE allShipsDoScriptEvent:OOJSID("playerWillEnterWitchspace") andReactToAIMessage:@"PLAYER WITCHSPACE"];
    7385             :         
    7386             :         // set the new market seed now!
    7387             :         // reseeding the RNG should be completely unnecessary here
    7388             : //      ranrot_srand((uint32_t)[[NSDate date] timeIntervalSince1970]);  // seed randomiser by time
    7389             :         market_rnd = ranrot_rand() & 255;                                           // random factor for market values is reset
    7390             : }
    7391             : 
    7392             : 
    7393           0 : - (void) witchEnd
    7394             : {
    7395             :         [UNIVERSE setSystemTo:system_id];
    7396             :         galaxy_coordinates = [[UNIVERSE systemManager] getCoordinatesForSystem:system_id inGalaxy:galaxy_number];
    7397             : 
    7398             :         [UNIVERSE setUpUniverseFromWitchspace];
    7399             :         [[UNIVERSE planet] update: 2.34375 * market_rnd];       // from 0..10 minutes
    7400             :         [[UNIVERSE station] update: 2.34375 * market_rnd];      // from 0..10 minutes
    7401             :         
    7402             :         chart_centre_coordinates = galaxy_coordinates;
    7403             :         target_chart_centre = chart_centre_coordinates;
    7404             : }
    7405             : 
    7406             : 
    7407             : - (BOOL) witchJumpChecklist:(BOOL)isGalacticJump
    7408             : {
    7409             :         // Perform this check only when doing the actual jump
    7410             :         if ([self status] == STATUS_WITCHSPACE_COUNTDOWN)
    7411             :         {
    7412             :                 // check nearby masses
    7413             :                 //UPDATE_STAGE(@"checking for mass blockage");
    7414             :                 ShipEntity* blocker = [UNIVERSE entityForUniversalID:[self checkShipsInVicinityForWitchJumpExit]];
    7415             :                 if (blocker)
    7416             :                 {
    7417             :                         [UNIVERSE clearPreviousMessage];
    7418             :                         NSString *blockerName = [blocker name];
    7419             :                         [UNIVERSE addMessage:OOExpandKey(@"witch-blocked", blockerName) forCount:4.5];
    7420             :                         [self playWitchjumpBlocked];
    7421             :                         [self setStatus:STATUS_IN_FLIGHT];
    7422             :                         ShipScriptEventNoCx(self, "playerJumpFailed", OOJSSTR("blocked"));
    7423             :                         return NO;
    7424             :                 }
    7425             :         }
    7426             : 
    7427             :         // For galactic hyperspace jumps we skip the remaining checks
    7428             :         if (isGalacticJump)
    7429             :         {
    7430             :                 return YES;
    7431             :         }
    7432             : 
    7433             :         // Check we're not jumping into the current system
    7434             :         if (![UNIVERSE inInterstellarSpace] && system_id == target_system_id)
    7435             :         {
    7436             :                 //dont allow player to hyperspace to current location.
    7437             :                 //Note interstellar space will have a system_seed place we came from
    7438             :                 [UNIVERSE clearPreviousMessage];
    7439             :                 [UNIVERSE addMessage:OOExpandKey(@"witch-no-target") forCount: 4.5];
    7440             :                 if ([self status] == STATUS_WITCHSPACE_COUNTDOWN)
    7441             :                 {
    7442             :                         [self playWitchjumpInsufficientFuel];
    7443             :                         [self setStatus:STATUS_IN_FLIGHT];
    7444             :                         ShipScriptEventNoCx(self, "playerJumpFailed", OOJSSTR("no target"));
    7445             :                 }
    7446             :                 else  [self playHyperspaceNoTarget];
    7447             : 
    7448             :                 return NO;
    7449             :         }
    7450             : 
    7451             :         // check max distance permitted
    7452             :         if ([self hyperspaceJumpDistance] > [self maxHyperspaceDistance])
    7453             :         {
    7454             :                 [UNIVERSE clearPreviousMessage];
    7455             :                 [UNIVERSE addMessage:DESC(@"witch-too-far") forCount: 4.5];
    7456             :                 if ([self status] == STATUS_WITCHSPACE_COUNTDOWN)
    7457             :                 {
    7458             :                         [self playWitchjumpDistanceTooGreat];
    7459             :                         [self setStatus:STATUS_IN_FLIGHT];
    7460             :                         ShipScriptEventNoCx(self, "playerJumpFailed", OOJSSTR("too far"));
    7461             :                 }
    7462             :                 else  [self playHyperspaceDistanceTooGreat];
    7463             :                 
    7464             :                 return NO;
    7465             :         }
    7466             : 
    7467             :         // check fuel level
    7468             :         if (![self hasSufficientFuelForJump])
    7469             :         {
    7470             :                 [UNIVERSE clearPreviousMessage];
    7471             :                 [UNIVERSE addMessage:DESC(@"witch-no-fuel") forCount: 4.5];
    7472             :                 if ([self status] == STATUS_WITCHSPACE_COUNTDOWN)
    7473             :                 {
    7474             :                         [self playWitchjumpInsufficientFuel];
    7475             :                         [self setStatus:STATUS_IN_FLIGHT];
    7476             :                         ShipScriptEventNoCx(self, "playerJumpFailed", OOJSSTR("insufficient fuel"));
    7477             :                 }
    7478             :                 else  [self playHyperspaceNoFuel];
    7479             :                 
    7480             :                 return NO;
    7481             :         }
    7482             : 
    7483             :         // All checks passed
    7484             :         return YES;
    7485             : }
    7486             : 
    7487             : - (void) setJumpType:(BOOL)isGalacticJump
    7488             : {
    7489             :         if (isGalacticJump)
    7490             :         {
    7491             :                 galactic_witchjump = YES;
    7492             :         }
    7493             :         else
    7494             :         {
    7495             :                 galactic_witchjump = NO;
    7496             :         }
    7497             : }
    7498             : 
    7499             : 
    7500             : 
    7501           0 : - (double) hyperspaceJumpDistance
    7502             : {
    7503             :         NSPoint targetCoordinates = PointFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:[self nextHopTargetSystemID] inGalaxy:galaxy_number]);
    7504             :         return distanceBetweenPlanetPositions(targetCoordinates.x,targetCoordinates.y,galaxy_coordinates.x,galaxy_coordinates.y);
    7505             : }
    7506             : 
    7507             : 
    7508           0 : - (OOFuelQuantity) fuelRequiredForJump
    7509             : {
    7510             :         return 10.0 * MAX(0.1, [self hyperspaceJumpDistance]);
    7511             : }
    7512             : 
    7513             : 
    7514             : - (BOOL) hasSufficientFuelForJump
    7515             : {
    7516             :         return fuel >= [self fuelRequiredForJump];
    7517             : }
    7518             : 
    7519             : 
    7520           0 : - (void) noteCompassLostTarget
    7521             : {
    7522             :         if ([[self hud] isCompassActive])
    7523             :         {
    7524             :                 // "the compass, it says we're lost!" :)
    7525             :                 JSContext *context = OOJSAcquireContext();
    7526             :                 jsval jsmode = OOJSValueFromCompassMode(context, [self compassMode]);
    7527             :                 ShipScriptEvent(context, self, "compassTargetChanged", JSVAL_VOID, jsmode);
    7528             :                 OOJSRelinquishContext(context);
    7529             :                 
    7530             :                 [[self hud] setCompassActive:NO];       // ensure a target change when returning to normal space.
    7531             :         }
    7532             : }
    7533             : 
    7534             : 
    7535             : - (void) enterGalacticWitchspace
    7536             : {
    7537             :         if (![self witchJumpChecklist:true])
    7538             :                 return;
    7539             : 
    7540             : 
    7541             :         OOGalaxyID destGalaxy = galaxy_number + 1;
    7542             :         if (EXPECT_NOT(destGalaxy >= OO_GALAXIES_AVAILABLE))
    7543             :         {
    7544             :                 destGalaxy = 0;
    7545             :         }
    7546             : 
    7547             : 
    7548             :         [self setStatus:STATUS_ENTERING_WITCHSPACE];
    7549             :         JSContext *context = OOJSAcquireContext();
    7550             :         [self setJumpCause:@"galactic jump"];
    7551             :         [self setPreviousSystemID:[self currentSystemID]];
    7552             :         ShipScriptEvent(context, self, "shipWillEnterWitchspace", STRING_TO_JSVAL(JS_InternString(context, [[self jumpCause] UTF8String])), INT_TO_JSVAL(destGalaxy));
    7553             :         OOJSRelinquishContext(context);
    7554             : 
    7555             :         [self noteCompassLostTarget];
    7556             : 
    7557             :         [self witchStart];
    7558             :         
    7559             :         [UNIVERSE removeAllEntitiesExceptPlayer];
    7560             :         
    7561             :         // remove any contracts and parcels for the old galaxy
    7562             :         if (contracts)
    7563             :                 [contracts removeAllObjects];
    7564             : 
    7565             :         if (parcels)
    7566             :                 [parcels removeAllObjects];
    7567             :         
    7568             :         // remove any mission destinations for the old galaxy
    7569             :         if (missionDestinations)
    7570             :                 [missionDestinations removeAllObjects];
    7571             :         
    7572             :         // expire passenger contracts for the old galaxy
    7573             :         if (passengers)
    7574             :         {
    7575             :                 unsigned i;
    7576             :                 for (i = 0; i < [passengers count]; i++)
    7577             :                 {
    7578             :                         // set the expected arrival time to now, so they storm off the ship at the first port
    7579             :                         NSMutableDictionary* passenger_info = [NSMutableDictionary dictionaryWithDictionary:[passengers oo_dictionaryAtIndex:i]];
    7580             :                         [passenger_info setObject:[NSNumber numberWithDouble:ship_clock] forKey:CONTRACT_KEY_ARRIVAL_TIME];
    7581             :                         [passengers replaceObjectAtIndex:i withObject:passenger_info];
    7582             :                 }
    7583             :         }
    7584             : 
    7585             :         // clear a lot of memory of player actions
    7586             :         if (ship_kills >= 6400)
    7587             :         {
    7588             :                 [self clearRolesFromPlayer:0.25];
    7589             :         }
    7590             :         else if (ship_kills >= 2560)
    7591             :         {
    7592             :                 [self clearRolesFromPlayer:0.5];
    7593             :         }
    7594             :         else
    7595             :         {
    7596             :                 [self clearRolesFromPlayer:0.9];
    7597             :         }       
    7598             :         [roleWeightFlags removeAllObjects];
    7599             :         [roleSystemList removeAllObjects];
    7600             :         
    7601             :         // may be more than one item providing this
    7602             :         [self removeEquipmentItem:[self equipmentItemProviding:@"EQ_GAL_DRIVE"]];
    7603             :         
    7604             :         galaxy_number = destGalaxy;
    7605             : 
    7606             :         [UNIVERSE setGalaxyTo:galaxy_number];
    7607             : 
    7608             :         // Choose the galactic hyperspace behaviour. Refers to where we may actually end up after an intergalactic jump.
    7609             :         // The default behaviour is that the player cannot arrive on unreachable or isolated systems. The options
    7610             :         // in planetinfo.plist, galactic_hyperspace_behaviour key can be used to allow arrival even at unreachable systems,
    7611             :         // or at fixed coordinates on the galactic chart. The key galactic_hyperspace_fixed_coords in planetinfo.plist is
    7612             :         // used in the fixed coordinates case and specifies the exact coordinates for the intergalactic jump.
    7613             :         switch (galacticHyperspaceBehaviour)
    7614             :         {
    7615             :                 case GALACTIC_HYPERSPACE_BEHAVIOUR_FIXED_COORDINATES:                   
    7616             :                         system_id = [UNIVERSE findSystemNumberAtCoords:galacticHyperspaceFixedCoords withGalaxy:galaxy_number includingHidden:YES];
    7617             :                         break;
    7618             :                 case GALACTIC_HYPERSPACE_BEHAVIOUR_ALL_SYSTEMS_REACHABLE:
    7619             :                         system_id = [UNIVERSE findSystemNumberAtCoords:galaxy_coordinates withGalaxy:galaxy_number includingHidden:YES];
    7620             :                         break;
    7621             :                 case GALACTIC_HYPERSPACE_BEHAVIOUR_STANDARD:
    7622             :                 default:
    7623             :                         // instead find a system connected to system 0 near the current coordinates...
    7624             :                         system_id = [UNIVERSE findConnectedSystemAtCoords:galaxy_coordinates withGalaxy:galaxy_number];
    7625             :                         break;
    7626             :         }
    7627             :         target_system_id = system_id;
    7628             :         info_system_id = system_id;
    7629             :         
    7630             :         [self setBounty:0 withReason:kOOLegalStatusReasonNewGalaxy];    // let's make a fresh start!
    7631             :         cursor_coordinates = PointFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:system_id inGalaxy:galaxy_number]);
    7632             : 
    7633             :         [self witchEnd]; // sets coordinates, calls exiting witchspace JS events
    7634             : }
    7635             : 
    7636             : 
    7637             : // now with added misjump goodness!
    7638             : // If the wormhole generator misjumped, the player's ship misjumps too. Kaks 20110211
    7639           0 : - (void) enterWormhole:(WormholeEntity *) w_hole
    7640             : {
    7641             :         if ([self status] == STATUS_ENTERING_WITCHSPACE
    7642             :                         || [self status] == STATUS_EXITING_WITCHSPACE)
    7643             :         {
    7644             :                 return; // has already entered a different wormhole
    7645             :         }
    7646             :         BOOL misjump = [self scriptedMisjump] || [w_hole withMisjump] || flightPitch == max_flight_pitch || randf() > 0.995;
    7647             :         wormhole = [w_hole retain];
    7648             :         [self addScannedWormhole:wormhole];
    7649             :         [self setStatus:STATUS_ENTERING_WITCHSPACE];
    7650             :         JSContext *context = OOJSAcquireContext();
    7651             :         [self setJumpCause:@"wormhole"];
    7652             :         [self setPreviousSystemID:[self currentSystemID]];
    7653             :         ShipScriptEvent(context, self, "shipWillEnterWitchspace", STRING_TO_JSVAL(JS_InternString(context, [[self jumpCause] UTF8String])), INT_TO_JSVAL([w_hole destination]));
    7654             :         OOJSRelinquishContext(context);
    7655             :         if ([self scriptedMisjump]) 
    7656             :         {
    7657             :                 misjump = YES; // a script could just have changed this to true;
    7658             :         }
    7659             : #ifdef OO_DUMP_PLANETINFO
    7660             :         misjump = NO;
    7661             : #endif
    7662             :         if (misjump && [self scriptedMisjumpRange] != 0.5)
    7663             :         {
    7664             :                 [w_hole setMisjumpWithRange:[self scriptedMisjumpRange]]; // overrides wormholes, if player also had non-default scriptedMisjumpRange
    7665             :         }
    7666             :         [self witchJumpTo:[w_hole destination] misjump:misjump];
    7667             : }
    7668             : 
    7669             : 
    7670           0 : - (void) enterWitchspace
    7671             : {
    7672             :         if (![self witchJumpChecklist:false])  return;
    7673             :         
    7674             :         OOSystemID jumpTarget = [self nextHopTargetSystemID];
    7675             : 
    7676             :         //  perform any check here for forced witchspace encounters
    7677             :         unsigned malfunc_chance = 253;
    7678             :         if (ship_trade_in_factor < 80)
    7679             :         {
    7680             :                 malfunc_chance -= (1 + ranrot_rand() % (81-ship_trade_in_factor)) / 2;  // increase chance of misjump in worn-out craft
    7681             :         }
    7682             :         else if (ship_trade_in_factor >= 100)
    7683             :         {
    7684             :                 malfunc_chance = 256; // force no misjumps on first jump
    7685             :         }
    7686             : 
    7687             : #ifdef OO_DUMP_PLANETINFO
    7688             :         BOOL misjump = NO; // debugging
    7689             : #else
    7690             :         BOOL malfunc = ((ranrot_rand() & 0xff) > malfunc_chance);
    7691             :         // 75% of the time a malfunction means a misjump
    7692             :         BOOL misjump = [self scriptedMisjump] || (flightPitch == max_flight_pitch) || (malfunc && (randf() > 0.75));
    7693             : 
    7694             :         if (malfunc && !misjump)
    7695             :         {
    7696             :                 // some malfunctions will start fuel leaks, some will result in no witchjump at all.
    7697             :                 if ([self takeInternalDamage])  // Depending on ship type and loaded cargo, this will be true for 20 - 50% of the time.
    7698             :                 {
    7699             :                         [self playWitchjumpFailure];
    7700             :                         [self setStatus:STATUS_IN_FLIGHT];
    7701             :                         ShipScriptEventNoCx(self, "playerJumpFailed", OOJSSTR("malfunction"));
    7702             :                         return;
    7703             :                 }
    7704             :                 else
    7705             :                 {
    7706             :                         [self setFuelLeak:[NSString stringWithFormat:@"%f", (randf() + randf()) * 5.0]];
    7707             :                 }
    7708             :         }
    7709             : #endif  
    7710             : 
    7711             :         // From this point forward we are -definitely- witchjumping
    7712             :         
    7713             :         // burn the full fuel amount to create the wormhole
    7714             :         fuel -= [self fuelRequiredForJump];
    7715             :         
    7716             :         // Create the players' wormhole
    7717             :         wormhole = [[WormholeEntity alloc] initWormholeTo:jumpTarget fromShip:self];
    7718             :         [UNIVERSE addEntity:wormhole]; // Add new wormhole to Universe to let other ships target it. Required for ships following the player.
    7719             :         [self addScannedWormhole:wormhole];
    7720             :         
    7721             :         [self setStatus:STATUS_ENTERING_WITCHSPACE];
    7722             :         JSContext *context = OOJSAcquireContext();
    7723             :         [self setJumpCause:@"standard jump"];
    7724             :         [self setPreviousSystemID:[self currentSystemID]];
    7725             :         ShipScriptEvent(context, self, "shipWillEnterWitchspace", STRING_TO_JSVAL(JS_InternString(context, [[self jumpCause] UTF8String])), INT_TO_JSVAL(jumpTarget));
    7726             :         OOJSRelinquishContext(context);
    7727             : 
    7728             :         [self updateSystemMemory];
    7729             :         NSUInteger legality = [self legalStatusOfCargoList];
    7730             :         OOCargoQuantity maxSpace = [self maxAvailableCargoSpace];
    7731             :         OOCargoQuantity availSpace = [self availableCargoSpace];
    7732             :         if ([roleWeightFlags objectForKey:@"bought-legal"])
    7733             :         {
    7734             :                 if (maxSpace != availSpace)
    7735             :                 {
    7736             :                         [self addRoleToPlayer:@"trader"];
    7737             :                         if (maxSpace - availSpace > 20 || availSpace == 0)
    7738             :                         {
    7739             :                                 if (legality == 0)
    7740             :                                 {
    7741             :                                         [self addRoleToPlayer:@"trader"];
    7742             :                                 }
    7743             :                         }
    7744             :                 }
    7745             :         }
    7746             :         if ([roleWeightFlags objectForKey:@"bought-illegal"])
    7747             :         {
    7748             :                 if (maxSpace != availSpace && legality > 0)
    7749             :                 {
    7750             :                         [self addRoleToPlayer:@"trader-smuggler"];
    7751             :                         if (maxSpace - availSpace > 20 || availSpace == 0)
    7752             :                         {
    7753             :                                 if (legality >= 20 || legality >= maxSpace)
    7754             :                                 {
    7755             :                                         [self addRoleToPlayer:@"trader-smuggler"];
    7756             :                                 }
    7757             :                         }
    7758             :                 }
    7759             :         }
    7760             :         [roleWeightFlags removeAllObjects];
    7761             : 
    7762             :         [self noteCompassLostTarget];
    7763             :         if ([self scriptedMisjump]) 
    7764             :         {
    7765             :                 misjump = YES; // a script could just have changed this to true;
    7766             :         }
    7767             :         if (misjump)
    7768             :         {
    7769             :                 [wormhole setMisjumpWithRange:[self scriptedMisjumpRange]];
    7770             :         }
    7771             :         [self witchJumpTo:jumpTarget misjump:misjump];
    7772             : }
    7773             : 
    7774             : 
    7775           0 : - (void) witchJumpTo:(OOSystemID)sTo misjump:(BOOL)misjump
    7776             : {
    7777             :         [self witchStart];
    7778             :         if (info_system_id == system_id)
    7779             :         {
    7780             :                 [self setInfoSystemID: sTo moveChart: YES];
    7781             :         }
    7782             :         //wear and tear on all jumps (inc misjumps, failures, and wormholes)
    7783             :         if (2 * market_rnd < ship_trade_in_factor)
    7784             :         {
    7785             :                 // every eight jumps or so drop the price down towards 75%
    7786             :                 [self adjustTradeInFactorBy:-(1 + (market_rnd & 3))];
    7787             :         }
    7788             :         
    7789             :         // set clock after "playerWillEnterWitchspace" and before  removeAllEntitiesExceptPlayer, to allow escorts time to follow their mother. 
    7790             :         NSPoint destCoords = PointFromString([[UNIVERSE systemManager] getProperty:@"coordinates" forSystem:sTo inGalaxy:galaxy_number]);
    7791             :         double distance = distanceBetweenPlanetPositions(destCoords.x,destCoords.y,galaxy_coordinates.x,galaxy_coordinates.y);
    7792             :         
    7793             :         // if we just escaped a system gone nova, make sure all nova parameters are reset
    7794             :         OOSunEntity *theSun = [UNIVERSE sun];
    7795             :         if (theSun && [theSun goneNova])
    7796             :         {
    7797             :                 [theSun resetNova];
    7798             :         }
    7799             :         
    7800             :         [UNIVERSE removeAllEntitiesExceptPlayer];
    7801             :         if (!misjump)
    7802             :         {
    7803             :                 ship_clock_adjust += distance * distance * 3600.0;
    7804             :                 [self setSystemID:sTo];
    7805             :                 [self setBounty:(legalStatus/2) withReason:kOOLegalStatusReasonNewSystem];      // 'another day, another system'
    7806             :                 [self witchEnd];
    7807             :                 if (market_rnd < 8) [self erodeReputation];          // every 32 systems or so, drop back towards 'unknown'
    7808             :         }
    7809             :         else
    7810             :         {
    7811             :                 // Misjump: move halfway there!
    7812             :                 // misjumps do not change legal status.
    7813             :                 if (randf() < 0.1) [self erodeReputation];           // once every 10 misjumps - should be much rarer than successful jumps!
    7814             : 
    7815             :                 [wormhole setMisjump]; 
    7816             :                 // just in case, but this has usually been set already
    7817             : 
    7818             :                 // and now the wormhole has travel time and coordinates calculated
    7819             :                 // so rather than duplicate the calculation we'll just ask it...
    7820             :                 NSPoint dest = [wormhole destinationCoordinates];
    7821             :                 galaxy_coordinates.x = dest.x;
    7822             :                 galaxy_coordinates.y = dest.y;
    7823             : 
    7824             :                 ship_clock_adjust += [wormhole travelTime];
    7825             : 
    7826             :                 [self playWitchjumpMisjump];
    7827             :                 [UNIVERSE setUpUniverseFromMisjump];
    7828             :         }
    7829             : }
    7830             : 
    7831             : 
    7832           0 : - (void) leaveWitchspace
    7833             : {
    7834             :         double          d1 = SCANNER_MAX_RANGE * ((Ranrot() & 255)/256.0 - 0.5);
    7835             :         HPVector                pos = [UNIVERSE getWitchspaceExitPosition];             // no need to reset the PRNG
    7836             :         Quaternion      q1;
    7837             :         HPVector                whpos, exitpos;
    7838             : 
    7839             :         double min_d1 = [UNIVERSE safeWitchspaceExitDistance];
    7840             :         quaternion_set_random(&q1);
    7841             :         if (abs((int)d1) < min_d1)
    7842             :         {
    7843             :                 d1 += ((d1 > 0.0)? min_d1: -min_d1); // not too close to the buoy.
    7844             :         }
    7845             :         HPVector                v1 = HPvector_forward_from_quaternion(q1);
    7846             :         exitpos = HPvector_add(pos, HPvector_multiply_scalar(v1, d1)); // randomise exit position
    7847             :         position = exitpos;
    7848             :         [self setOrientation:[UNIVERSE getWitchspaceExitRotation]];
    7849             : 
    7850             :         // While setting the wormhole position to the player position looks very nice for ships following the player, 
    7851             :         // the more common case of the player following other ships, the player tends to
    7852             :         // ram the back of the ships, or even jump on top of is when the ship jumped without initial speed, which is messy. 
    7853             :         // To avoid this problem, a small wormhole displacement is added.
    7854             :         if (wormhole)   // will be nil for galactic jump
    7855             :         {
    7856             :                 if ([[wormhole shipsInTransit] count] > 0)
    7857             :                 {
    7858             :                         // player is not allone in his wormhole, synchronise player and wormhole position.
    7859             :                         double  wh_arrival_time = ([PLAYER clockTimeAdjusted] - [wormhole arrivalTime]);
    7860             :                         if (wh_arrival_time > 0)
    7861             :                         {
    7862             :                                 // Player is following other ship 
    7863             :                                 whpos = HPvector_add(exitpos, vectorToHPVector(vector_multiply_scalar([self forwardVector], 1000.0f)));
    7864             :                                 [wormhole setContainsPlayer:YES];
    7865             :                         }
    7866             :                         else
    7867             :                         {
    7868             :                                 // Player is the leadship 
    7869             :                                 whpos = HPvector_add(exitpos, vectorToHPVector(vector_multiply_scalar([self forwardVector], -500.0f)));
    7870             :                                 // so it won't contain the player by the time they exit
    7871             :                                 [wormhole setExitSpeed:maxFlightSpeed*WORMHOLE_LEADER_SPEED_FACTOR];
    7872             :                         } 
    7873             : 
    7874             :                         HPVector distance = HPvector_subtract(whpos, pos);
    7875             :                         if (HPmagnitude2(distance) < min_d1*min_d1 ) // within safety distance from the buoy?
    7876             :                         {
    7877             :                                 // the wormhole is to close to the buoy. Move both player and wormhole away from it in the x-y plane.
    7878             :                                 distance.z = 0;
    7879             :                                 distance = HPvector_multiply_scalar(HPvector_normal(distance), min_d1);
    7880             :                                 whpos = HPvector_add(whpos, distance);
    7881             :                                 position = HPvector_add(position, distance);
    7882             :                         }
    7883             :                         [wormhole setExitPosition: whpos];
    7884             :                 }
    7885             :                 else
    7886             :                 {
    7887             :                         // no-one else in the wormhole
    7888             :                         [wormhole setExitSpeed:maxFlightSpeed*WORMHOLE_LEADER_SPEED_FACTOR];
    7889             :                 }
    7890             :         }
    7891             :         /* there's going to be a slight pause at this stage anyway;
    7892             :          * there's also going to be a lot of stale ship scripts. Force a
    7893             :          * garbage collection while we have chance. - CIM */
    7894             :         [[OOJavaScriptEngine sharedEngine] garbageCollectionOpportunity:YES];
    7895             :         flightSpeed = wormhole ? [wormhole exitSpeed] : fmin(maxFlightSpeed,50.0f);
    7896             :         [wormhole release];     // OK even if nil
    7897             :         wormhole = nil;
    7898             : 
    7899             :         flightRoll = 0.0f;
    7900             :         flightPitch = 0.0f;
    7901             :         flightYaw = 0.0f;
    7902             : 
    7903             :         velocity = kZeroVector;
    7904             :         [self setStatus:STATUS_EXITING_WITCHSPACE];
    7905             :         gui_screen = GUI_SCREEN_MAIN;
    7906             :         being_fined = NO;                               // until you're scanned by a copper!
    7907             :         [self clearTargetMemory];
    7908             :         [self setShowDemoShips:NO];
    7909             :         [[UNIVERSE gameController] setMouseInteractionModeForFlight];
    7910             :         [UNIVERSE setDisplayText:NO];
    7911             :         [UNIVERSE setWitchspaceBreakPattern:YES];
    7912             :         [self playExitWitchspace];
    7913             :         if ([self currentSystemID] >= 0)
    7914             :         {
    7915             :                 if (![roleSystemList containsObject:[NSNumber numberWithInt:[self currentSystemID]]])
    7916             :                 {
    7917             :                         // going somewhere new?
    7918             :                         [self clearRoleFromPlayer:NO];
    7919             :                 }
    7920             :         }
    7921             :         
    7922             :         if (galactic_witchjump)
    7923             :         {
    7924             :                 [self doScriptEvent:OOJSID("playerEnteredNewGalaxy") withArgument:[NSNumber numberWithUnsignedInt:galaxy_number]];
    7925             :         }
    7926             :         
    7927             :         [self doScriptEvent:OOJSID("shipWillExitWitchspace") withArgument:[self jumpCause]];
    7928             :         [UNIVERSE setUpBreakPattern:[self breakPatternPosition] orientation:orientation forDocking:NO];
    7929             : }
    7930             : 
    7931             : 
    7932             : ///////////////////////////////////
    7933             : 
    7934             : - (void) setGuiToStatusScreen
    7935             : {
    7936             :         NSString                *systemName = nil;
    7937             :         NSString                *targetSystemName = nil;
    7938             :         NSString                *text = nil;
    7939             :         
    7940             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    7941             :         OOGUIScreenID   oldScreen = gui_screen;
    7942             :         if (oldScreen != GUI_SCREEN_STATUS)
    7943             :         {
    7944             :                 [self noteGUIWillChangeTo:GUI_SCREEN_STATUS];
    7945             :         }
    7946             : 
    7947             :         gui_screen = GUI_SCREEN_STATUS;
    7948             :         BOOL                    guiChanged = (oldScreen != gui_screen);
    7949             :         
    7950             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:NO];
    7951             :         
    7952             :         // Both system_seed & target_system_seed are != nil at all times when this function is called.
    7953             :         
    7954             :         systemName = [UNIVERSE inInterstellarSpace] ? DESC(@"interstellar-space") : [UNIVERSE getSystemName:system_id];
    7955             :         if ([self isDocked] && [self dockedStation] != [UNIVERSE station])
    7956             :         {
    7957             :                 systemName = [NSString stringWithFormat:@"%@ : %@", systemName, [[self dockedStation] displayName]];
    7958             :         }
    7959             : 
    7960             :         targetSystemName =      [UNIVERSE getSystemName:target_system_id];
    7961             :         NSDictionary *systemInfo = [[UNIVERSE systemManager] getPropertiesForSystem:target_system_id inGalaxy:galaxy_number];
    7962             :         NSInteger concealment = [systemInfo oo_intForKey:@"concealment" defaultValue:OO_SYSTEMCONCEALMENT_NONE];
    7963             :         if (concealment >= OO_SYSTEMCONCEALMENT_NONAME) targetSystemName = DESC(@"status-unknown-system");
    7964             : 
    7965             :         OOSystemID nextHop = [self nextHopTargetSystemID];
    7966             :         if (nextHop != target_system_id) {
    7967             :                 NSString *nextHopSystemName = [UNIVERSE getSystemName:nextHop];
    7968             :                 systemInfo = [[UNIVERSE systemManager] getPropertiesForSystem:nextHop inGalaxy:galaxy_number];
    7969             :                 concealment = [systemInfo oo_intForKey:@"concealment" defaultValue:OO_SYSTEMCONCEALMENT_NONE];
    7970             :                 if (concealment >= OO_SYSTEMCONCEALMENT_NONAME) nextHopSystemName = DESC(@"status-unknown-system");
    7971             :                 targetSystemName = OOExpandKey(@"status-hyperspace-system-multi", targetSystemName, nextHopSystemName);
    7972             :         }
    7973             : 
    7974             :         // GUI stuff
    7975             :         {
    7976             :                 NSString                        *shipName = [self displayName];
    7977             :                 NSString                        *legal_desc = nil, *rating_desc = nil,
    7978             :                                                         *alert_desc = nil, *fuel_desc = nil,
    7979             :                                                         *credits_desc = nil;
    7980             :                 
    7981             :                 OOGUIRow                        i;
    7982             :                 OOGUITabSettings        tab_stops;
    7983             :                 tab_stops[0] = 20;
    7984             :                 tab_stops[1] = 160;
    7985             :                 tab_stops[2] = 290;
    7986             :                 [gui overrideTabs:tab_stops from:kGuiStatusTabs length:3];
    7987             :                 [gui setTabStops:tab_stops];
    7988             :                 
    7989             :                 NSString        *lightYearsDesc = DESC(@"status-light-years-desc");
    7990             :                 
    7991             :                 legal_desc = OODisplayStringFromLegalStatus(legalStatus);
    7992             :                 rating_desc = KillCountToRatingAndKillString(ship_kills);
    7993             :                 alert_desc = OODisplayStringFromAlertCondition([self alertCondition]);
    7994             :                 fuel_desc = [NSString stringWithFormat:@"%.1f %@", fuel/10.0, lightYearsDesc];
    7995             :                 credits_desc = OOCredits(credits);
    7996             :                 
    7997             :                 [gui clearAndKeepBackground:!guiChanged];
    7998             :                 text = DESC(@"status-commander-@");
    7999             :                 [gui setTitle:[NSString stringWithFormat:text, [self commanderName]]];
    8000             :                 
    8001             :                 [gui setText:shipName forRow:0 align:GUI_ALIGN_CENTER];
    8002             :                 
    8003             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"status-present-system"), systemName, nil]      forRow:1];
    8004             :                 if ([self hasHyperspaceMotor]) [gui setArray:[NSArray arrayWithObjects:DESC(@"status-hyperspace-system"), targetSystemName, nil] forRow:2];
    8005             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"status-condition"), alert_desc, nil]                   forRow:3];
    8006             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"status-fuel"), fuel_desc, nil]                         forRow:4];
    8007             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"status-cash"), credits_desc, nil]                      forRow:5];
    8008             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"status-legal-status"), legal_desc, nil]                forRow:6];
    8009             :                 [gui setArray:[NSArray arrayWithObjects:DESC(@"status-rating"), rating_desc, nil]                     forRow:7];
    8010             :                 
    8011             : 
    8012             :                 [gui setColor:[gui colorFromSetting:kGuiStatusShipnameColor defaultValue:nil] forRow:0];
    8013             :                 for (i = 1 ; i <= 7 ; ++i)
    8014             :                 {
    8015             :                         // nil default = fall back to global default colour
    8016             :                         [gui setColor:[gui colorFromSetting:kGuiStatusDataColor defaultValue:nil] forRow:i];
    8017             :                 }
    8018             : 
    8019             :                 [gui setText:DESC(@"status-equipment") forRow:9];
    8020             : 
    8021             :                 [gui setColor:[gui colorFromSetting:kGuiStatusEquipmentHeadingColor defaultValue:nil] forRow:9];
    8022             :                 
    8023             :                 [gui setShowTextCursor:NO];
    8024             :         }
    8025             :         /* ends */
    8026             : 
    8027             :         if (lastTextKey)
    8028             :         {
    8029             :                 [lastTextKey release];
    8030             :                 lastTextKey = nil;
    8031             :         }
    8032             :         
    8033             :         [[UNIVERSE gameView] clearMouse];
    8034             :         
    8035             :         // Contributed by Pleb - show ship model if the appropriate user default key has been set - Nikos 20140127
    8036             :         if (EXPECT_NOT([[NSUserDefaults standardUserDefaults] boolForKey:@"show-ship-model-in-status-screen"]))
    8037             :         {
    8038             :                 [UNIVERSE removeDemoShips];
    8039             :                 [self showShipModelWithKey:[self shipDataKey] shipData:nil personality:[self entityPersonalityInt]
    8040             :                                                                         factorX:2.5 factorY:1.7 factorZ:8.0 inContext:@"GUI_SCREEN_STATUS"];
    8041             :                 [self setShowDemoShips:YES];
    8042             :         }
    8043             :         else
    8044             :         {
    8045             :                 [self setShowDemoShips:NO];
    8046             :         }
    8047             :         
    8048             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];
    8049             :         
    8050             :         if (guiChanged)
    8051             :         {
    8052             :                 NSDictionary *fgDescriptor = nil, *bgDescriptor = nil;
    8053             :                 if ([self status] == STATUS_DOCKED)
    8054             :                 {
    8055             :                         fgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"docked_overlay"];
    8056             :                         bgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"status_docked"];
    8057             :                 }
    8058             :                 else
    8059             :                 {
    8060             :                         fgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"overlay"];
    8061             :                         if (alertCondition == ALERT_CONDITION_RED) bgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"status_red_alert"];
    8062             :                         else bgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"status_in_flight"];
    8063             :                 }
    8064             :                 
    8065             :                 [gui setForegroundTextureDescriptor:fgDescriptor];
    8066             :                 
    8067             :                 if (bgDescriptor == nil)  bgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"status"];
    8068             :                 [gui setBackgroundTextureDescriptor:bgDescriptor];
    8069             :                 
    8070             :                 [gui setStatusPage:0];
    8071             :                 [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
    8072             :         }
    8073             : }
    8074             : 
    8075             : 
    8076             : - (NSArray *) equipmentList
    8077             : {
    8078             :         GuiDisplayGen           *gui = [UNIVERSE gui];
    8079             :         NSMutableArray          *quip1 = [NSMutableArray array]; // damaged
    8080             :         NSMutableArray          *quip2 = [NSMutableArray array]; // working
    8081             :         NSEnumerator            *eqTypeEnum = nil;
    8082             :         OOEquipmentType         *eqType = nil;
    8083             :         NSString                        *desc = nil;
    8084             :         NSString                        *alldesc = nil;
    8085             : 
    8086             :         BOOL prioritiseDamaged = [[gui userSettings] oo_boolForKey:kGuiStatusPrioritiseDamaged defaultValue:YES];
    8087             : 
    8088             :         for (eqTypeEnum = [OOEquipmentType reverseEquipmentEnumerator]; (eqType = [eqTypeEnum nextObject]); )
    8089             :         {
    8090             :                 if ([eqType isVisible])
    8091             :                 {
    8092             :                         if ([eqType canCarryMultiple] && ![eqType isMissileOrMine])
    8093             :                         {
    8094             :                                 NSString *damagedIdentifier = [[eqType identifier] stringByAppendingString:@"_DAMAGED"];
    8095             :                                 NSUInteger count = 0, okcount = 0;
    8096             :                                 okcount = [self countEquipmentItem:[eqType identifier]];
    8097             :                                 count = okcount + [self countEquipmentItem:damagedIdentifier];
    8098             :                                 if (count == 0)
    8099             :                                 {
    8100             :                                         // do nothing
    8101             :                                 }
    8102             :                                 // all items okay
    8103             :                                 else if (count == okcount)
    8104             :                                 {
    8105             :                                         // only one installed display normally
    8106             :                                         if (count == 1)
    8107             :                                         {
    8108             :                                                 [quip2 addObject:[NSArray arrayWithObjects:[eqType name], [NSNumber numberWithBool:YES], [eqType displayColor], nil]];
    8109             :                                         }
    8110             :                                         // display plural form
    8111             :                                         else
    8112             :                                         {
    8113             :                                                 NSString *equipmentName = [eqType name];
    8114             :                                                 alldesc = OOExpandKey(@"equipment-plural", count, equipmentName);
    8115             :                                                 [quip2 addObject:[NSArray arrayWithObjects:alldesc, [NSNumber numberWithBool:YES], [eqType displayColor], nil]];
    8116             :                                         }
    8117             :                                 }
    8118             :                                 // all broken, only one installed
    8119             :                                 else if (count == 1 && okcount == 0)
    8120             :                                 {
    8121             :                                         desc = [NSString stringWithFormat:DESC(@"equipment-@-not-available"), [eqType name]];
    8122             :                                         if (prioritiseDamaged)
    8123             :                                         {
    8124             :                                                 [quip1 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:NO], [eqType displayColor], nil]];
    8125             :                                         }
    8126             :                                         else
    8127             :                                         {
    8128             :                                                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:NO], [eqType displayColor], nil]];
    8129             :                                         }
    8130             :                                 }
    8131             :                                 // some broken, multiple installed
    8132             :                                 else
    8133             :                                 {
    8134             :                                         NSString *equipmentName = [eqType name];
    8135             :                                         alldesc = OOExpandKey(@"equipment-plural-some-na", okcount, count, equipmentName);
    8136             :                                         if (prioritiseDamaged)
    8137             :                                         {
    8138             :                                                 [quip1 addObject:[NSArray arrayWithObjects:alldesc, [NSNumber numberWithBool:NO], [eqType displayColor], nil]];
    8139             :                                         }
    8140             :                                         else
    8141             :                                         {
    8142             :                                                 [quip2 addObject:[NSArray arrayWithObjects:alldesc, [NSNumber numberWithBool:NO], [eqType displayColor], nil]];
    8143             :                                         }
    8144             :                                 }
    8145             :                         }
    8146             :                         else if ([self hasEquipmentItem:[eqType identifier]])
    8147             :                         {
    8148             :                                 [quip2 addObject:[NSArray arrayWithObjects:[eqType name], [NSNumber numberWithBool:YES], [eqType displayColor], nil]];
    8149             :                         }
    8150             :                         else 
    8151             :                         {
    8152             :                                 // Check for damaged version
    8153             :                                 if ([self hasEquipmentItem:[[eqType identifier] stringByAppendingString:@"_DAMAGED"]])
    8154             :                                 {
    8155             :                                         desc = [NSString stringWithFormat:DESC(@"equipment-@-not-available"), [eqType name]];
    8156             :                                         
    8157             :                                         if (prioritiseDamaged) 
    8158             :                                         {
    8159             :                                                 [quip1 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:NO], [eqType displayColor], nil]];
    8160             :                                         } 
    8161             :                                         else
    8162             :                                         {
    8163             :                                                 // just add in to the normal array
    8164             :                                                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:NO], [eqType displayColor], nil]];
    8165             :                                         }
    8166             :                                 }
    8167             :                         }
    8168             :                 }
    8169             :         }
    8170             :         
    8171             :         if (max_passengers > 0)
    8172             :         {
    8173             :                 desc = [NSString stringWithFormat:DESC_PLURAL(@"equipment-pass-berth-@", max_passengers), max_passengers];
    8174             :                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:YES], [[OOEquipmentType equipmentTypeWithIdentifier:@"EQ_PASSENGER_BERTH"] displayColor], nil]];
    8175             :         }
    8176             :         
    8177             :         if (!isWeaponNone(forward_weapon_type))
    8178             :         {
    8179             :                 desc = [NSString stringWithFormat:DESC(@"equipment-fwd-weapon-@"),[forward_weapon_type name]];
    8180             :                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:YES], [forward_weapon_type displayColor], nil]];
    8181             :         }
    8182             :         if (!isWeaponNone(aft_weapon_type))
    8183             :         {
    8184             :                 desc = [NSString stringWithFormat:DESC(@"equipment-aft-weapon-@"),[aft_weapon_type name]];
    8185             :                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:YES], [aft_weapon_type displayColor], nil]];
    8186             :         }
    8187             :         if (!isWeaponNone(port_weapon_type))
    8188             :         {
    8189             :                 desc = [NSString stringWithFormat:DESC(@"equipment-port-weapon-@"),[port_weapon_type name]];
    8190             :                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:YES], [port_weapon_type displayColor], nil]];
    8191             :         }
    8192             :         if (!isWeaponNone(starboard_weapon_type))
    8193             :         {
    8194             :                 desc = [NSString stringWithFormat:DESC(@"equipment-stb-weapon-@"),[starboard_weapon_type name]];
    8195             :                 [quip2 addObject:[NSArray arrayWithObjects:desc, [NSNumber numberWithBool:YES], [starboard_weapon_type displayColor], nil]];
    8196             :         }
    8197             :         
    8198             :         // list damaged first, then working
    8199             :         [quip1 addObjectsFromArray:quip2];
    8200             :         return quip1;
    8201             : }
    8202             : 
    8203             : 
    8204             : - (NSUInteger) primedEquipmentCount
    8205             : {
    8206             :         return [eqScripts count];
    8207             : }
    8208             : 
    8209             : 
    8210             : - (NSString *) primedEquipmentName:(NSInteger)offset
    8211             : {
    8212             :         NSUInteger c = [self primedEquipmentCount];
    8213             :         NSUInteger idx = (primedEquipment+(c+1)+offset)%(c+1);
    8214             :         if (idx == c)
    8215             :         {
    8216             :                 return DESC(@"equipment-primed-none-hud-label");
    8217             :         }
    8218             :         else
    8219             :         {
    8220             :                 return [[OOEquipmentType equipmentTypeWithIdentifier:[[eqScripts oo_arrayAtIndex:idx] oo_stringAtIndex:0]] name];
    8221             :         }
    8222             : }
    8223             : 
    8224             : 
    8225             : - (NSString *) currentPrimedEquipment
    8226             : {
    8227             :         NSString *result = @"";
    8228             :         NSUInteger c = [eqScripts count];
    8229             :         if (primedEquipment != c)
    8230             :         {
    8231             :                 result = [[eqScripts oo_arrayAtIndex:primedEquipment] oo_stringAtIndex:0];
    8232             :         }
    8233             :         return result;
    8234             : }
    8235             : 
    8236             : 
    8237             : - (BOOL) setPrimedEquipment:(NSString *)eqKey showMessage:(BOOL)showMsg
    8238             : {
    8239             :         NSUInteger c = [eqScripts count];
    8240             :         NSUInteger current = primedEquipment;
    8241             :         primedEquipment = [self eqScriptIndexForKey:eqKey];     // if key not found primedEquipment is set to primed-none
    8242             :         BOOL unprimeEq = [eqKey isEqualToString:@""];
    8243             :         BOOL result = YES;
    8244             : 
    8245             :         if (primedEquipment == c && !unprimeEq)
    8246             :         {
    8247             :                 primedEquipment = current;
    8248             :                 result = NO;
    8249             :         }
    8250             :         else 
    8251             :         {
    8252             :                 if (primedEquipment != current && showMsg == YES)
    8253             :                 {
    8254             :                         NSString *equipmentName = [[OOEquipmentType equipmentTypeWithIdentifier:[[eqScripts oo_arrayAtIndex:primedEquipment] oo_stringAtIndex:0]] name];
    8255             :                         [UNIVERSE addMessage:unprimeEq ? OOExpandKey(@"equipment-primed-none") : OOExpandKey(@"equipment-primed", equipmentName) forCount:2.0];
    8256             :                 }
    8257             :         }
    8258             :         return result;
    8259             : }
    8260             : 
    8261             : 
    8262             : - (void) activatePrimableEquipment:(NSUInteger)index withMode:(OOPrimedEquipmentMode)mode
    8263             : {
    8264             :         // index == [eqScripts count] means we don't want to activate any equipment.
    8265             :         if(index < [eqScripts count])
    8266             :         {
    8267             :                 OOJSScript *eqScript = [[eqScripts oo_arrayAtIndex:index] objectAtIndex:1];
    8268             :                 JSContext *context = OOJSAcquireContext();
    8269             :                 NSAssert1(mode <= OOPRIMEDEQUIP_MODE, @"Primable equipment mode %i out of range", (int)mode);
    8270             :                 
    8271             :                 switch (mode)
    8272             :                 {
    8273             :                         case OOPRIMEDEQUIP_MODE:
    8274             :                                 [eqScript callMethod:OOJSID("mode") inContext:context withArguments:NULL count:0 result:NULL];
    8275             :                                 break;
    8276             :                         case OOPRIMEDEQUIP_ACTIVATED:
    8277             :                                 [eqScript callMethod:OOJSID("activated") inContext:context withArguments:NULL count:0 result:NULL];
    8278             :                                 break;
    8279             :                 }
    8280             :                 OOJSRelinquishContext(context);
    8281             :         }
    8282             : 
    8283             : }
    8284             : 
    8285             : 
    8286             : - (NSString *) fastEquipmentA
    8287             : {
    8288             :         return _fastEquipmentA;
    8289             : }
    8290             : 
    8291             : 
    8292             : - (NSString *) fastEquipmentB
    8293             : {
    8294             :         return _fastEquipmentB;
    8295             : }
    8296             : 
    8297             : 
    8298             : - (void) setFastEquipmentA:(NSString *)eqKey
    8299             : {
    8300             :         [_fastEquipmentA release];
    8301             :         _fastEquipmentA = [eqKey copy];
    8302             : }
    8303             : 
    8304             : 
    8305             : - (void) setFastEquipmentB:(NSString *)eqKey
    8306             : {
    8307             :         [_fastEquipmentB release];
    8308             :         _fastEquipmentB = [eqKey copy];
    8309             : }
    8310             : 
    8311             : 
    8312           0 : - (OOEquipmentType *) weaponTypeForFacing:(OOWeaponFacing)facing strict:(BOOL)strict
    8313             : {
    8314             :         OOWeaponType weaponType = nil;
    8315             :         
    8316             :         switch (facing)
    8317             :         {
    8318             :                 case WEAPON_FACING_FORWARD:
    8319             :                         weaponType = forward_weapon_type;
    8320             :                         break;
    8321             :                         
    8322             :                 case WEAPON_FACING_AFT:
    8323             :                         weaponType = aft_weapon_type;
    8324             :                         break;
    8325             :                         
    8326             :                 case WEAPON_FACING_PORT:
    8327             :                         weaponType = port_weapon_type;
    8328             :                         break;
    8329             :                         
    8330             :                 case WEAPON_FACING_STARBOARD:
    8331             :                         weaponType = starboard_weapon_type;
    8332             :                         break;
    8333             :                         
    8334             :                 case WEAPON_FACING_NONE:
    8335             :                         break;
    8336             :         }
    8337             : 
    8338             :         return weaponType;
    8339             : }
    8340             : 
    8341             : 
    8342           0 : - (NSArray *) missilesList
    8343             : {
    8344             :         [self tidyMissilePylons];       // just in case.
    8345             :         return [super missilesList];
    8346             : }
    8347             : 
    8348             : 
    8349             : - (NSArray *) cargoList
    8350             : {
    8351             :         NSMutableArray  *manifest = [NSMutableArray array];
    8352             :         NSArray                 *list = [self cargoListForScripting];
    8353             :         NSEnumerator    *cargoEnum = nil;
    8354             :         NSDictionary    *commodity;
    8355             :         
    8356             :         if (specialCargo) [manifest addObject:specialCargo];
    8357             :         
    8358             :         for (cargoEnum = [list objectEnumerator]; (commodity = [cargoEnum nextObject]); )
    8359             :         {
    8360             :                 NSInteger quantity = [commodity oo_integerForKey:@"quantity"];
    8361             :                 NSString *units = [commodity oo_stringForKey:@"unit"];
    8362             :                 NSString *commodityName = [commodity oo_stringForKey:@"displayName"];
    8363             :                 NSInteger containers = [commodity oo_intForKey:@"containers"];
    8364             :                 BOOL extended = ![units isEqualToString:DESC(@"cargo-tons-symbol")] && containers > 0;
    8365             : 
    8366             :                 if (extended) {
    8367             :                         [manifest addObject:OOExpandKey(@"manifest-cargo-quantity-extended", quantity, units, commodityName, containers)];
    8368             :                 } else {
    8369             :                         [manifest addObject:OOExpandKey(@"manifest-cargo-quantity", quantity, units, commodityName)];
    8370             :                 }
    8371             :         }
    8372             :         
    8373             :         return manifest;
    8374             : }
    8375             : 
    8376             : 
    8377           0 : - (NSArray *) cargoListForScripting
    8378             : {
    8379             :         NSMutableArray          *list = [NSMutableArray array];
    8380             :         
    8381             :         NSUInteger                      i, j, commodityCount = [shipCommodityData count];
    8382             :         OOCargoQuantity         quantityInHold[commodityCount];
    8383             :         OOCargoQuantity         containersInHold[commodityCount];
    8384             :         NSArray                         *goods = [shipCommodityData goods];
    8385             : 
    8386             :         // following changed to work whether docked or not
    8387             :         for (i = 0; i < commodityCount; i++)
    8388             :         {
    8389             :                 quantityInHold[i] = [shipCommodityData quantityForGood:[goods oo_stringAtIndex:i]];
    8390             :                 containersInHold[i] = 0;
    8391             :         }
    8392             :         for (i = 0; i < [cargo count]; i++)
    8393             :         {
    8394             :                 ShipEntity *container = [cargo objectAtIndex:i];
    8395             :                 j = [goods indexOfObject:[container commodityType]];
    8396             :                 quantityInHold[j] += [container commodityAmount];
    8397             :                 ++containersInHold[j];
    8398             :         }
    8399             :         
    8400             :         for (i = 0; i < commodityCount; i++)
    8401             :         {
    8402             :                 if (quantityInHold[i] > 0)
    8403             :                 {
    8404             :                         NSMutableDictionary     *commodity = [NSMutableDictionary dictionaryWithCapacity:4];
    8405             :                         NSString *symName = [goods oo_stringAtIndex:i];
    8406             :                         // commodity, quantity - keep consistency between .manifest and .contracts
    8407             :                         [commodity setObject:symName forKey:@"commodity"];
    8408             :                         [commodity setObject:[NSNumber numberWithUnsignedInt:quantityInHold[i]] forKey:@"quantity"];
    8409             :                         [commodity setObject:[NSNumber numberWithUnsignedInt:containersInHold[i]] forKey:@"containers"];
    8410             :                         [commodity setObject:[shipCommodityData nameForGood:symName] forKey:@"displayName"]; 
    8411             :                         [commodity setObject:DisplayStringForMassUnitForCommodity(symName) forKey:@"unit"]; 
    8412             :                         [list addObject:commodity];
    8413             :                 }
    8414             :         }
    8415             : 
    8416             :         return [[list copy] autorelease];       // return an immutable copy
    8417             : }
    8418             : 
    8419             : 
    8420             : // determines general export legality, not tied to a station
    8421             : - (unsigned) legalStatusOfCargoList
    8422             : {
    8423             :         NSString                *good = nil;
    8424             :         OOCargoQuantity amount;
    8425             :         unsigned                penalty = 0;
    8426             : 
    8427             :         foreach (good, [shipCommodityData goods])
    8428             :         {
    8429             :                 amount = [shipCommodityData quantityForGood:good];
    8430             :                 penalty += [shipCommodityData exportLegalityForGood:good] * amount;
    8431             :         }
    8432             :         return penalty;
    8433             : }
    8434             : 
    8435             : 
    8436           0 : - (NSArray*) contractsListForScriptingFromArray:(NSArray *) contracts_array forCargo:(BOOL)forCargo
    8437             : {
    8438             :         NSMutableArray          *result = [NSMutableArray array];
    8439             :         NSUInteger                      i;
    8440             : 
    8441             :         for (i = 0; i < [contracts_array count]; i++)
    8442             :         {
    8443             :                 NSMutableDictionary     *contract = [NSMutableDictionary dictionaryWithCapacity:10];
    8444             :                 NSDictionary            *dict = [contracts_array oo_dictionaryAtIndex:i];
    8445             :                 if (forCargo)
    8446             :                 {
    8447             :                         // commodity, quantity - keep consistency between .manifest and .contracts
    8448             :                         [contract setObject:[dict oo_stringForKey:CARGO_KEY_TYPE] forKey:@"commodity"];
    8449             :                         [contract setObject:[NSNumber numberWithUnsignedInt:[dict oo_intForKey:CARGO_KEY_AMOUNT]] forKey:@"quantity"];
    8450             :                         [contract setObject:[dict oo_stringForKey:CARGO_KEY_DESCRIPTION] forKey:@"description"];
    8451             :                 }
    8452             :                 else
    8453             :                 {
    8454             :                         [contract setObject:[dict oo_stringForKey:PASSENGER_KEY_NAME] forKey:PASSENGER_KEY_NAME];
    8455             :                         [contract setObject:[NSNumber numberWithUnsignedInt:[dict oo_unsignedIntForKey:CONTRACT_KEY_RISK]] forKey:CONTRACT_KEY_RISK]; 
    8456             :                 }
    8457             :                 
    8458             :                 OOSystemID      planet = [dict oo_intForKey:CONTRACT_KEY_DESTINATION];
    8459             :                 NSString        *planetName = [UNIVERSE getSystemName:planet];
    8460             :                 [contract setObject:[NSNumber numberWithUnsignedInt:planet] forKey:CONTRACT_KEY_DESTINATION];
    8461             :                 [contract setObject:planetName forKey:@"destinationName"];
    8462             :                 planet = [dict oo_intForKey:CONTRACT_KEY_START];
    8463             :                 planetName = [UNIVERSE getSystemName: planet];
    8464             :                 [contract setObject:[NSNumber numberWithUnsignedInt:planet] forKey:CONTRACT_KEY_START];
    8465             :                 [contract setObject:planetName forKey:@"startName"];
    8466             :                 
    8467             :                 int             dest_eta = [dict oo_doubleForKey:CONTRACT_KEY_ARRIVAL_TIME] - ship_clock;
    8468             :                 [contract setObject:[NSNumber numberWithInt:dest_eta] forKey:@"eta"];
    8469             :                 [contract setObject:[UNIVERSE shortTimeDescription:dest_eta] forKey:@"etaDescription"];
    8470             :                 [contract setObject:[NSNumber numberWithInt:[dict oo_intForKey:CONTRACT_KEY_PREMIUM]] forKey:CONTRACT_KEY_PREMIUM]; 
    8471             :                 [contract setObject:[NSNumber numberWithInt:[dict oo_intForKey:CONTRACT_KEY_FEE]] forKey:CONTRACT_KEY_FEE]; 
    8472             :                 [result addObject:contract];
    8473             :         }
    8474             : 
    8475             :         return [[result copy] autorelease];     // return an immutable copy
    8476             : }
    8477             : 
    8478             : 
    8479           0 : - (NSArray *) passengerListForScripting
    8480             : {
    8481             :         return [self contractsListForScriptingFromArray:passengers forCargo:NO];
    8482             : }
    8483             : 
    8484             : 
    8485           0 : - (NSArray *) parcelListForScripting
    8486             : {
    8487             :         return [self contractsListForScriptingFromArray:parcels forCargo:NO];
    8488             : }
    8489             : 
    8490             : 
    8491           0 : - (NSArray *) contractListForScripting
    8492             : {
    8493             :         return [self contractsListForScriptingFromArray:contracts forCargo:YES];
    8494             : }
    8495             : 
    8496             : - (void) setGuiToSystemDataScreen
    8497             : {
    8498             :         [self setGuiToSystemDataScreenRefreshBackground: NO];
    8499             : }
    8500             : 
    8501             : - (void) setGuiToSystemDataScreenRefreshBackground: (BOOL) refreshBackground
    8502             : {
    8503             :         NSDictionary    *infoSystemData;
    8504             :         NSString                *infoSystemName;
    8505             :         
    8506             :         infoSystemData = [[UNIVERSE generateSystemData:info_system_id] retain];  // retained
    8507             :         NSInteger concealment = [infoSystemData oo_intForKey:@"concealment" defaultValue:OO_SYSTEMCONCEALMENT_NONE];
    8508             :         infoSystemName = [infoSystemData oo_stringForKey:KEY_NAME];
    8509             :         
    8510             :         BOOL                    sunGoneNova = ([infoSystemData oo_boolForKey:@"sun_gone_nova"]);
    8511             :         OOGUIScreenID   oldScreen = gui_screen;
    8512             :         
    8513             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    8514             :         gui_screen = GUI_SCREEN_SYSTEM_DATA;
    8515             :         BOOL                    guiChanged = (oldScreen != gui_screen);
    8516             : 
    8517             :         Random_Seed             infoSystemRandomSeed = [[UNIVERSE systemManager] getRandomSeedForSystem:info_system_id
    8518             :                                                                                                                                                                   inGalaxy:[self galaxyNumber]];
    8519             :         
    8520             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:NO];
    8521             :         
    8522             :         // GUI stuff
    8523             :         {
    8524             :                 OOGUITabSettings tab_stops;
    8525             :                 tab_stops[0] = 0;
    8526             :                 tab_stops[1] = 96;
    8527             :                 tab_stops[2] = 144;
    8528             :                 [gui overrideTabs:tab_stops from:kGuiSystemdataTabs length:3];
    8529             :                 [gui setTabStops:tab_stops];
    8530             :                 
    8531             :                 NSUInteger techLevel = [infoSystemData oo_intForKey:KEY_TECHLEVEL] + 1;
    8532             :                 int population = [infoSystemData oo_intForKey:KEY_POPULATION];
    8533             :                 int productivity = [infoSystemData oo_intForKey:KEY_PRODUCTIVITY];
    8534             :                 int radius = [infoSystemData oo_intForKey:KEY_RADIUS];
    8535             :                 
    8536             :                 NSString        *government_desc =      [infoSystemData oo_stringForKey:KEY_GOVERNMENT_DESC 
    8537             :                                                                                                                          defaultValue:OODisplayStringFromGovernmentID([infoSystemData oo_intForKey:KEY_GOVERNMENT])];
    8538             :                 NSString        *economy_desc =         [infoSystemData oo_stringForKey:KEY_ECONOMY_DESC 
    8539             :                                                                                                                          defaultValue:OODisplayStringFromEconomyID([infoSystemData oo_intForKey:KEY_ECONOMY])];
    8540             :                 NSString        *inhabitants =          [infoSystemData oo_stringForKey:KEY_INHABITANTS];
    8541             :                 NSString        *system_desc =          [infoSystemData oo_stringForKey:KEY_DESCRIPTION];
    8542             : 
    8543             :                 NSString    *populationDesc =   [infoSystemData oo_stringForKey:KEY_POPULATION_DESC
    8544             :                                                                                                                          defaultValue:OOExpandKeyWithSeed(kNilRandomSeed, @"sysdata-pop-value", population)];
    8545             : 
    8546             :                 if (sunGoneNova)
    8547             :                 {
    8548             :                         population = 0;
    8549             :                         productivity = 0;
    8550             :                         radius = 0;
    8551             :                         techLevel = 0;
    8552             : 
    8553             :                         government_desc = OOExpandKeyWithSeed(infoSystemRandomSeed, @"nova-system-government");
    8554             :                         economy_desc = OOExpandKeyWithSeed(infoSystemRandomSeed, @"nova-system-economy");
    8555             :                         inhabitants = OOExpandKeyWithSeed(infoSystemRandomSeed, @"nova-system-inhabitants");
    8556             :                         {
    8557             :                                 NSString *system = infoSystemName;
    8558             :                                 system_desc = OOExpandKeyWithSeed(infoSystemRandomSeed, @"nova-system-description", system);
    8559             :                         }
    8560             :                         populationDesc = OOExpandKeyWithSeed(infoSystemRandomSeed, @"sysdata-pop-value", population);
    8561             :                 }
    8562             : 
    8563             :                 
    8564             :                 [gui clearAndKeepBackground:!refreshBackground && !guiChanged];
    8565             :                 [UNIVERSE removeDemoShips];
    8566             : 
    8567             :                 if (concealment < OO_SYSTEMCONCEALMENT_NONAME)
    8568             :                 {
    8569             :                         NSString *system = infoSystemName;
    8570             :                         [gui setTitle:OOExpandKeyWithSeed(infoSystemRandomSeed, @"sysdata-data-on-system", system)];
    8571             :                 }
    8572             :                 else
    8573             :                 {
    8574             :                         [gui setTitle:OOExpandKey(@"sysdata-data-on-system-no-name")];
    8575             :                 }
    8576             : 
    8577             :                 if (concealment >= OO_SYSTEMCONCEALMENT_NODATA)
    8578             :                 {
    8579             :                         OOGUIRow i = [gui addLongText:OOExpandKey(@"sysdata-data-on-system-no-data") startingAtRow:15 align:GUI_ALIGN_LEFT];
    8580             :                         missionTextRow = i;
    8581             :                         for (i-- ; i > 14 ; --i)
    8582             :                         {
    8583             :                                 [gui setColor:[gui colorFromSetting:kGuiSystemdataDescriptionColor defaultValue:[OOColor greenColor]] forRow:i];
    8584             :                         }
    8585             :                 }
    8586             :                 else
    8587             :                 {
    8588             :                         NSPoint infoSystemCoordinates = [[UNIVERSE systemManager] getCoordinatesForSystem: info_system_id inGalaxy: galaxy_number];
    8589             :                         double distance = distanceBetweenPlanetPositions(infoSystemCoordinates.x, infoSystemCoordinates.y, galaxy_coordinates.x, galaxy_coordinates.y);
    8590             :                         if(distance == 0.0 && info_system_id != system_id)
    8591             :                         {
    8592             :                                 distance = 0.1;
    8593             :                         }
    8594             :                         NSString *distanceInfo = [NSString stringWithFormat: @"%.1f ly", distance];
    8595             :                         if (ANA_mode != OPTIMIZED_BY_NONE)
    8596             :                         {
    8597             :                                 NSDictionary *routeInfo = nil;
    8598             :                                 routeInfo = [UNIVERSE routeFromSystem: system_id toSystem: info_system_id optimizedBy: ANA_mode];
    8599             :                                 if (routeInfo != nil)
    8600             :                                 {
    8601             :                                         double routeDistance = [[routeInfo objectForKey: @"distance"] doubleValue];
    8602             :                                         double routeTime = [[routeInfo objectForKey: @"time"] doubleValue];
    8603             :                                         int routeJumps = [[routeInfo objectForKey: @"jumps"] intValue];
    8604             :                                         if(routeDistance == 0.0 && info_system_id != system_id) {
    8605             :                                                 routeDistance = 0.1;
    8606             :                                                 routeTime = 0.01;
    8607             :                                                 routeJumps = 0;
    8608             :                                         }
    8609             :                                         distanceInfo = [NSString stringWithFormat: @"%.1f ly / %.1f %@ / %d %@",
    8610             :                                                         routeDistance,
    8611             :                                                         routeTime,
    8612             :                                                         // don't rely on DESC_PLURAL for routeTime since it is of type double
    8613             :                                                         routeTime > 1.05 || routeTime < 0.95 ? DESC(@"sysdata-route-hours%1") : DESC(@"sysdata-route-hours%0"),
    8614             :                                                         routeJumps,
    8615             :                                                         DESC_PLURAL(@"sysdata-route-jumps", routeJumps)];
    8616             :                                 }
    8617             :                         }
    8618             : 
    8619             :                         OOGUIRow i;
    8620             : 
    8621             :                         for (i = 1; i <= 16; i++) {
    8622             :                                 NSString *ln = [NSString stringWithFormat:@"sysdata-line-%ld", (long)i];
    8623             :                                 NSString *line = OOExpandKeyWithSeed(infoSystemRandomSeed, ln, economy_desc, government_desc, techLevel, populationDesc, inhabitants, productivity, radius, distanceInfo);
    8624             :                                 if (![line isEqualToString:@""])
    8625             :                                 {
    8626             :                                         NSArray *lines = [line componentsSeparatedByString:@"\t"];
    8627             :                                         if ([lines count] == 1) 
    8628             :                                         {
    8629             :                                                 [gui setArray:[NSArray arrayWithObjects:[lines objectAtIndex:0],
    8630             :                                                                         nil]
    8631             :                                                         forRow:i];
    8632             :                                         } 
    8633             :                                         if ([lines count] == 2) 
    8634             :                                         {
    8635             :                                                 [gui setArray:[NSArray arrayWithObjects:[lines objectAtIndex:0],
    8636             :                                                                                 [lines objectAtIndex:1],
    8637             :                                                                         nil]
    8638             :                                                         forRow:i];
    8639             :                                         }
    8640             :                                         if ([lines count] == 3) 
    8641             :                                         {
    8642             :                                                 if ([[lines objectAtIndex:2] isEqualToString:@""]) 
    8643             :                                                 {
    8644             :                                                         [gui setArray:[NSArray arrayWithObjects:[lines objectAtIndex:0],
    8645             :                                                                                         [lines objectAtIndex:1],
    8646             :                                                                                 nil]
    8647             :                                                                 forRow:i];
    8648             :                                                 } 
    8649             :                                                 else
    8650             :                                                 {
    8651             :                                                         [gui setArray:[NSArray arrayWithObjects:[lines objectAtIndex:0],
    8652             :                                                                                         [lines objectAtIndex:1],
    8653             :                                                                                         [lines objectAtIndex:2],
    8654             :                                                                                 nil]
    8655             :                                                                 forRow:i];
    8656             :                                                 }
    8657             :                                         }
    8658             :                                 }
    8659             :                                 else 
    8660             :                                 {
    8661             :                                         [gui setArray:[NSArray arrayWithObjects:@"",
    8662             :                                                                 nil]
    8663             :                                                 forRow:i];
    8664             :                                 }
    8665             :                         }
    8666             : 
    8667             : 
    8668             :                         i = [gui addLongText:system_desc startingAtRow:17 align:GUI_ALIGN_LEFT];
    8669             :                         missionTextRow = i;
    8670             :                         for (i-- ; i > 16 ; --i)
    8671             :                         {
    8672             :                                 [gui setColor:[gui colorFromSetting:kGuiSystemdataDescriptionColor defaultValue:[OOColor greenColor]] forRow:i];
    8673             :                         }
    8674             :                         for (i = 1 ; i <= 14 ; ++i)
    8675             :                         {
    8676             :                                 // nil default = fall back to global default colour
    8677             :                                 [gui setColor:[gui colorFromSetting:kGuiSystemdataFactsColor defaultValue:nil] forRow:i];
    8678             :                         }
    8679             :                 }
    8680             : 
    8681             :                 [gui setShowTextCursor:NO];
    8682             :         }
    8683             :         /* ends */
    8684             :         
    8685             :         [lastTextKey release];
    8686             :         lastTextKey = nil;
    8687             :         
    8688             :         [[UNIVERSE gameView] clearMouse];
    8689             :         
    8690             :         [infoSystemData release];
    8691             :         
    8692             :         [self setShowDemoShips:NO];
    8693             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:NO];
    8694             :         
    8695             :         // if the system has gone nova, there's no planet to display
    8696             :         if (!sunGoneNova && concealment < OO_SYSTEMCONCEALMENT_NODATA)
    8697             :         {
    8698             :                 // The next code is generating the miniature planets.
    8699             :                 // When normal planets are displayed, the PRNG is reset. This happens not with procedural planet display.
    8700             :                 RANROTSeed ranrotSavedSeed = RANROTGetFullSeed();
    8701             :                 RNG_Seed saved_seed = currentRandomSeed();
    8702             :                 
    8703             :                 if (info_system_id == system_id)
    8704             :                 {
    8705             :                         [self setBackgroundFromDescriptionsKey:@"gui-scene-show-local-planet"];
    8706             :                 }
    8707             :                 else
    8708             :                 {
    8709             :                         [self setBackgroundFromDescriptionsKey:@"gui-scene-show-planet"];
    8710             :                 }
    8711             :                 
    8712             :                 setRandomSeed(saved_seed);
    8713             :                 RANROTSetFullSeed(ranrotSavedSeed);
    8714             :         }
    8715             :         
    8716             :         if (refreshBackground || guiChanged)
    8717             :         {
    8718             :                 [gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"overlay"];
    8719             :                 [gui setBackgroundTextureKey:sunGoneNova ? @"system_data_nova" : @"system_data"];
    8720             :                 
    8721             :                 [self noteGUIDidChangeFrom:oldScreen to:gui_screen refresh: refreshBackground];
    8722             :                 [self checkScript];     // Still needed by some OXPs?
    8723             :         }
    8724             : }
    8725             : 
    8726             : 
    8727           0 : - (void) prepareMarkedDestination:(NSMutableDictionary *)markers :(NSDictionary *)marker
    8728             : {
    8729             :         NSNumber *key = [NSNumber numberWithInt:[marker oo_intForKey:@"system"]];
    8730             :         NSMutableArray *list = [markers objectForKey:key];
    8731             :         if (list == nil)
    8732             :         {
    8733             :                 list = [NSMutableArray arrayWithObject:marker];
    8734             :         }
    8735             :         else
    8736             :         {
    8737             :                 [list addObject:marker];
    8738             :         }
    8739             :         [markers setObject:list forKey:key];
    8740             : }
    8741             : 
    8742             : 
    8743             : - (NSDictionary *) markedDestinations
    8744             : {
    8745             :         // get a list of systems marked as contract destinations
    8746             :         NSMutableDictionary     *destinations = [NSMutableDictionary dictionaryWithCapacity:256];
    8747             :         unsigned                i;
    8748             :         OOSystemID sysid;
    8749             :         NSDictionary *marker;
    8750             : 
    8751             :         for (i = 0; i < [passengers count]; i++)
    8752             :         {
    8753             :                 sysid = [[passengers oo_dictionaryAtIndex:i]  oo_unsignedCharForKey:CONTRACT_KEY_DESTINATION];
    8754             :                 marker = [self passengerContractMarker:sysid];
    8755             :                 [self prepareMarkedDestination:destinations:marker];
    8756             :         }
    8757             :         for (i = 0; i < [parcels count]; i++)
    8758             :         {
    8759             :                 sysid = [[parcels oo_dictionaryAtIndex:i]  oo_unsignedCharForKey:CONTRACT_KEY_DESTINATION];
    8760             :                 marker = [self parcelContractMarker:sysid];
    8761             :                 [self prepareMarkedDestination:destinations:marker];
    8762             :         }
    8763             :         for (i = 0; i < [contracts count]; i++)
    8764             :         {
    8765             :                 sysid = [[contracts oo_dictionaryAtIndex:i]  oo_unsignedCharForKey:CONTRACT_KEY_DESTINATION];
    8766             :                 marker = [self cargoContractMarker:sysid];
    8767             :                 [self prepareMarkedDestination:destinations:marker];
    8768             :         }
    8769             : 
    8770             :         NSEnumerator                            *keyEnum = nil;
    8771             :         NSString                                        *key = nil;
    8772             : 
    8773             :         for (keyEnum = [missionDestinations keyEnumerator]; (key = [keyEnum nextObject]); )
    8774             :         {
    8775             :                 marker = [missionDestinations objectForKey:key];
    8776             :                 [self prepareMarkedDestination:destinations:marker];
    8777             :         }
    8778             :         
    8779             :         return destinations;
    8780             : }
    8781             : 
    8782             : - (void) setGuiToLongRangeChartScreen
    8783             : {
    8784             :         OOGUIScreenID   oldScreen = gui_screen;
    8785             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    8786             :         [gui clearAndKeepBackground:NO];
    8787             :         [gui setBackgroundTextureKey:@"short_range_chart"];
    8788             :         [self setMissionBackgroundSpecial: nil];
    8789             :         gui_screen = GUI_SCREEN_LONG_RANGE_CHART;
    8790             :         target_chart_zoom = CHART_MAX_ZOOM;
    8791             :         [self setGuiToChartScreenFrom: oldScreen];
    8792             : }
    8793             :         
    8794             : - (void) setGuiToShortRangeChartScreen
    8795             : {
    8796             :         OOGUIScreenID   oldScreen = gui_screen;
    8797             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    8798             :         [gui clearAndKeepBackground:NO];
    8799             :         [gui setBackgroundTextureKey:@"short_range_chart"];
    8800             :         [self setMissionBackgroundSpecial: nil];
    8801             :         gui_screen = GUI_SCREEN_SHORT_RANGE_CHART;
    8802             :         [self setGuiToChartScreenFrom: oldScreen];
    8803             : }
    8804             : 
    8805             : - (void) setGuiToChartScreenFrom: (OOGUIScreenID) oldScreen
    8806             : {
    8807             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    8808             :         
    8809             :         BOOL                    guiChanged = (oldScreen != gui_screen);
    8810             :         
    8811             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
    8812             :         
    8813             :         target_system_id = [UNIVERSE findSystemNumberAtCoords:cursor_coordinates withGalaxy:galaxy_number includingHidden:NO];
    8814             :         
    8815             :         [UNIVERSE preloadPlanetTexturesForSystem:target_system_id];
    8816             :         
    8817             :         // GUI stuff
    8818             :         {
    8819             :                 //[gui clearAndKeepBackground:!guiChanged];
    8820             :                 [gui setStarChartTitle];
    8821             :                 // refresh the short range chart cache, in case we've just loaded a save game with different local overrides, etc.
    8822             :                 [gui refreshStarChart];
    8823             :                 //[gui setText:targetSystemName forRow:19];
    8824             :                 // distance-f & est-travel-time-f are identical between short & long range charts in standard Oolite, however can be alterered separately via OXPs
    8825             :                 //[gui setText:OOExpandKey(@"short-range-chart-distance", distance) forRow:20];
    8826             :                 //NSString *travelTimeRow = @"";
    8827             :                 //if ([self hasHyperspaceMotor] && distance > 0.0 && distance * 10.0 <= fuel)
    8828             :                 //{
    8829             :                 //      double time = estimatedTravelTime;
    8830             :                 //      travelTimeRow = OOExpandKey(@"short-range-chart-est-travel-time", time);
    8831             :                 //}
    8832             :                 //[gui setText:travelTimeRow forRow:21];
    8833             :                 if (gui_screen == GUI_SCREEN_LONG_RANGE_CHART)
    8834             :                 {
    8835             :                         NSString *displaySearchString = planetSearchString ? [planetSearchString capitalizedString] : (NSString *)@"";
    8836             :                         [gui setText:[NSString stringWithFormat:DESC(@"long-range-chart-find-planet-@"), displaySearchString] forRow:GUI_ROW_PLANET_FINDER];
    8837             :                         [gui setColor:[OOColor cyanColor] forRow:GUI_ROW_PLANET_FINDER];
    8838             :                         [gui setShowTextCursor:YES];
    8839             :                         [gui setCurrentRow:GUI_ROW_PLANET_FINDER];
    8840             :                 }
    8841             :                 else
    8842             :                 {
    8843             :                         [gui setShowTextCursor:NO];
    8844             :                 }
    8845             :         }
    8846             :         /* ends */
    8847             :         
    8848             :         [self setShowDemoShips:NO];
    8849             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
    8850             :         
    8851             :         if (guiChanged)
    8852             :         {
    8853             :                 [gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"overlay"];
    8854             :                 
    8855             :                 [gui setBackgroundTextureKey:@"short_range_chart"];
    8856             :                 if (found_system_id >= 0)
    8857             :                 {               
    8858             :                         [UNIVERSE findSystemCoordinatesWithPrefix:[[UNIVERSE getSystemName:found_system_id] lowercaseString] exactMatch:YES];
    8859             :                 }
    8860             :                 [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
    8861             :         }
    8862             : }
    8863             : 
    8864             : 
    8865           0 : static NSString *SliderString(NSInteger amountIn20ths)
    8866             : {
    8867             :         NSString *filledSlider = [@"|||||||||||||||||||||||||" substringToIndex:amountIn20ths];
    8868             :         NSString *emptySlider =  [@"........................." substringToIndex:20 - amountIn20ths];
    8869             :         return [NSString stringWithFormat:@"%@%@", filledSlider, emptySlider];
    8870             : }
    8871             : 
    8872             : 
    8873             : - (void) setGuiToGameOptionsScreen
    8874             : {
    8875             :         MyOpenGLView *gameView = [UNIVERSE gameView];
    8876             : 
    8877             :         [[UNIVERSE gameView] clearMouse];
    8878             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
    8879             :         
    8880             :         // GUI stuff
    8881             :         {
    8882           0 :                 #define OO_SETACCESSCONDITIONFORROW(condition, row)                             \
    8883             :                 do {                                                                                                    \
    8884             :                         if ((condition))                                                                        \
    8885             :                         {                                                                                               \
    8886             :                                 [gui setKey:GUI_KEY_OK forRow:(row)];                   \
    8887             :                         }                                                                                               \
    8888             :                         else                                                                                            \
    8889             :                         {                                                                                               \
    8890             :                                 [gui setColor:[OOColor grayColor] forRow:(row)];        \
    8891             :                         }                                                                                               \
    8892             :                 } while(0)
    8893             :                 BOOL startingGame = [self status] == STATUS_START_GAME;
    8894             :                 GuiDisplayGen* gui = [UNIVERSE gui];
    8895             :                 GUI_ROW_INIT(gui);
    8896             : 
    8897             :                 int first_sel_row = GUI_FIRST_ROW(GAME)-4; // repositioned menu
    8898             : 
    8899             :                 [gui clear];
    8900             :                 [gui setTitle:[NSString stringWithFormat:DESC(@"status-commander-@"), [self commanderName]]]; // Same title as status screen.
    8901             :                 
    8902             : #if OO_RESOLUTION_OPTION
    8903             :                 GameController  *controller = [UNIVERSE gameController];
    8904             :                 
    8905             :                 NSUInteger              displayModeIndex = [controller indexOfCurrentDisplayMode];
    8906             :                 if (displayModeIndex == NSNotFound)
    8907             :                 {
    8908             :                         OOLogWARN(@"display.currentMode.notFound", @"%@", @"couldn't find current fullscreen setting, switching to default.");
    8909             :                         displayModeIndex = 0;
    8910             :                 }
    8911             :                 
    8912             :                 NSArray                 *modeList = [controller displayModes];
    8913             :                 NSDictionary    *mode = nil;
    8914             :                 if ([modeList count])
    8915             :                 {
    8916             :                         mode = [modeList objectAtIndex:displayModeIndex];
    8917             :                 }
    8918             :                 if (mode == nil)  return;       // Got a better idea?
    8919             :                 
    8920             :                 unsigned modeWidth = [mode oo_unsignedIntForKey:kOODisplayWidth];
    8921             :                 unsigned modeHeight = [mode oo_unsignedIntForKey:kOODisplayHeight];
    8922             :                 float modeRefresh = [mode oo_floatForKey:kOODisplayRefreshRate];
    8923             : 
    8924             :                 BOOL runningOnPrimaryDisplayDevice = [gameView isRunningOnPrimaryDisplayDevice];
    8925             : #if OOLITE_WINDOWS
    8926             :                 if (!runningOnPrimaryDisplayDevice)
    8927             :                 {
    8928             :                         MONITORINFOEX mInfo = [gameView currentMonitorInfo];
    8929             :                         modeWidth = mInfo.rcMonitor.right - mInfo.rcMonitor.left;
    8930             :                         modeHeight = mInfo.rcMonitor.bottom - mInfo.rcMonitor.top;
    8931             :                 }
    8932             : #endif
    8933             :                 
    8934             :                 NSString *displayModeString = [self screenModeStringForWidth:modeWidth height:modeHeight refreshRate:modeRefresh];
    8935             :                 
    8936             :                 [gui setText:displayModeString forRow:GUI_ROW(GAME,DISPLAY) align:GUI_ALIGN_CENTER];
    8937             :                 if (runningOnPrimaryDisplayDevice)
    8938             :                 {
    8939             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,DISPLAY)];
    8940             :                 }
    8941             :                 else
    8942             :                 {
    8943             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(GAME,DISPLAY)];
    8944             :                 }
    8945             : #endif  // OO_RESOLUTIOM_OPTION
    8946             : 
    8947             : 
    8948             : #if OOLITE_WINDOWS
    8949             :                 if ([gameView hdrOutput])
    8950             :                 {
    8951             :                         NSArray         *brightnesses = [[UNIVERSE descriptions] oo_arrayForKey: @"hdr_maxBrightness_array"];
    8952             :                         int                     brightnessIdx = [brightnesses indexOfObject:[NSString stringWithFormat:@"%d", (int)[gameView hdrMaxBrightness]]];
    8953             :                         
    8954             :                         if (brightnessIdx == NSNotFound)
    8955             :                         {
    8956             :                                 OOLogWARN(@"hdr.maxBrightness.notFound", @"%@", @"couldn't find current max brightness setting, switching to 400 nits.");
    8957             :                                 brightnessIdx = 0;
    8958             :                         }
    8959             :                                 
    8960             :                         int brightnessValue = [brightnesses oo_intAtIndex:brightnessIdx];
    8961             :                         NSString *maxBrightnessString = OOExpandKey(@"gameoptions-hdr-maxbrightness", brightnessValue);
    8962             :                                                                                                                                                                 
    8963             :                         [gui setText:maxBrightnessString forRow:GUI_ROW(GAME,HDRMAXBRIGHTNESS)  align:GUI_ALIGN_CENTER];
    8964             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,HDRMAXBRIGHTNESS)];
    8965             :                 }
    8966             : #endif
    8967             : 
    8968             : 
    8969             :                 if ([UNIVERSE autoSave])
    8970             :                         [gui setText:DESC(@"gameoptions-autosave-yes") forRow:GUI_ROW(GAME,AUTOSAVE) align:GUI_ALIGN_CENTER];
    8971             :                 else
    8972             :                         [gui setText:DESC(@"gameoptions-autosave-no") forRow:GUI_ROW(GAME,AUTOSAVE) align:GUI_ALIGN_CENTER];
    8973             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,AUTOSAVE)];
    8974             :         
    8975             :                 // volume control
    8976             :                 if ([OOSound respondsToSelector:@selector(masterVolume)] && [OOSound isSoundOK])
    8977             :                 {
    8978             :                         double volume = 100.0 * [OOSound masterVolume];
    8979             :                         int vol = (volume / 5.0 + 0.5); // avoid rounding errors
    8980             :                         NSString* soundVolumeWordDesc = DESC(@"gameoptions-sound-volume");
    8981             :                         if (vol > 0)
    8982             :                                 [gui setText:[NSString stringWithFormat:@"%@%@ ", soundVolumeWordDesc, SliderString(vol)] forRow:GUI_ROW(GAME,VOLUME) align:GUI_ALIGN_CENTER];
    8983             :                         else
    8984             :                                 [gui setText:DESC(@"gameoptions-sound-volume-mute") forRow:GUI_ROW(GAME,VOLUME) align:GUI_ALIGN_CENTER];
    8985             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,VOLUME)];
    8986             :                 }
    8987             :                 else
    8988             :                 {
    8989             :                         [gui setText:DESC(@"gameoptions-volume-external-only") forRow:GUI_ROW(GAME,VOLUME) align:GUI_ALIGN_CENTER];
    8990             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(GAME,VOLUME)];
    8991             :                 }
    8992             :                 
    8993             : #if OOLITE_SDL          
    8994             :                 // gamma control
    8995             :                 float gamma = [gameView gammaValue];
    8996             :                 int gamma5 = (gamma * 5);
    8997             :                 NSString* gammaWordDesc = DESC(@"gameoptions-gamma-value");
    8998             :                 [gui setText:[NSString stringWithFormat:@"%@%@ (%.1f) ", gammaWordDesc, SliderString(gamma5), gamma] forRow:GUI_ROW(GAME,GAMMA) align:GUI_ALIGN_CENTER];
    8999             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,GAMMA)];
    9000             : #endif
    9001             : 
    9002             :                 // field of view control
    9003             :                 float fov = [gameView fov:NO];
    9004             :                 int fovTicks = (int)((fov - MIN_FOV_DEG) * 20 / (MAX_FOV_DEG - MIN_FOV_DEG));
    9005             :                 NSString* fovWordDesc = DESC(@"gameoptions-fov-value");
    9006             :                 [gui setText:[NSString stringWithFormat:@"%@%@ (%d%c) ", fovWordDesc, SliderString(fovTicks), (int)fov, 176 /*176 is the degrees symbol Unicode code point*/] forRow:GUI_ROW(GAME,FOV) align:GUI_ALIGN_CENTER];
    9007             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,FOV)];
    9008             :                 
    9009             :                 // color blind mode
    9010             :                 int colorblindMode = [UNIVERSE colorblindMode];
    9011             :                 NSString *colorblindModeDesc = [[[UNIVERSE descriptions] oo_arrayForKey: @"colorblind_mode"] oo_stringAtIndex:[UNIVERSE useShaders] ? colorblindMode : 0];
    9012             :                 NSString *colorblindModeMsg = OOExpandKey(@"gameoptions-colorblind-mode", colorblindModeDesc);
    9013             :                 [gui setText:colorblindModeMsg forRow:GUI_ROW(GAME,COLORBLINDMODE) align:GUI_ALIGN_CENTER];
    9014             :                 if ([UNIVERSE useShaders])
    9015             :                 {
    9016             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,COLORBLINDMODE)];
    9017             :                 }
    9018             :                 else
    9019             :                 {
    9020             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(GAME,COLORBLINDMODE)];
    9021             :                 }
    9022             :                 
    9023             : #if OOLITE_SPEECH_SYNTH
    9024             :                 // Speech control
    9025             :                 switch (isSpeechOn)
    9026             :                 {
    9027             :                 case OOSPEECHSETTINGS_OFF:
    9028             :                         [gui setText:DESC(@"gameoptions-spoken-messages-no") forRow:GUI_ROW(GAME,SPEECH) align:GUI_ALIGN_CENTER];
    9029             :                         break;
    9030             :                 case OOSPEECHSETTINGS_COMMS:
    9031             :                         [gui setText:DESC(@"gameoptions-spoken-messages-comms") forRow:GUI_ROW(GAME,SPEECH) align:GUI_ALIGN_CENTER];
    9032             :                         break;
    9033             :                 case OOSPEECHSETTINGS_ALL:
    9034             :                         [gui setText:DESC(@"gameoptions-spoken-messages-yes") forRow:GUI_ROW(GAME,SPEECH) align:GUI_ALIGN_CENTER];
    9035             :                         break;
    9036             :                 }
    9037             :                 OO_SETACCESSCONDITIONFORROW(!startingGame, GUI_ROW(GAME,SPEECH));
    9038             :                 
    9039             : #if OOLITE_ESPEAK
    9040             :                 {
    9041             :                         NSString *voiceName = [UNIVERSE voiceName:voice_no];
    9042             :                         NSString *message = OOExpandKey(@"gameoptions-voice-name", voiceName);
    9043             :                         [gui setText:message forRow:GUI_ROW(GAME,SPEECH_LANGUAGE) align:GUI_ALIGN_CENTER];
    9044             :                         OO_SETACCESSCONDITIONFORROW(!startingGame, GUI_ROW(GAME,SPEECH_LANGUAGE));
    9045             : 
    9046             :                         message = [NSString stringWithFormat:DESC(voice_gender_m ? @"gameoptions-voice-M" : @"gameoptions-voice-F")];
    9047             :                         [gui setText:message forRow:GUI_ROW(GAME,SPEECH_GENDER) align:GUI_ALIGN_CENTER];
    9048             :                         OO_SETACCESSCONDITIONFORROW(!startingGame, GUI_ROW(GAME,SPEECH_GENDER));
    9049             :                 }
    9050             : #endif
    9051             : #endif
    9052             : #if !OOLITE_MAC_OS_X
    9053             :                 // window/fullscreen
    9054             :                 if([gameView inFullScreenMode])
    9055             :                 {
    9056             :                         [gui setText:DESC(@"gameoptions-play-in-window") forRow:GUI_ROW(GAME,DISPLAYSTYLE) align:GUI_ALIGN_CENTER];
    9057             :                 }
    9058             :                 else
    9059             :                 {
    9060             :                         [gui setText:DESC(@"gameoptions-play-in-fullscreen") forRow:GUI_ROW(GAME,DISPLAYSTYLE) align:GUI_ALIGN_CENTER];
    9061             :                 }
    9062             :                 [gui setKey: GUI_KEY_OK forRow: GUI_ROW(GAME,DISPLAYSTYLE)];
    9063             : #endif
    9064             :                 
    9065             :                 [gui setText:DESC(@"gameoptions-joystick-configuration") forRow: GUI_ROW(GAME,STICKMAPPER) align: GUI_ALIGN_CENTER];
    9066             :                 OO_SETACCESSCONDITIONFORROW([[OOJoystickManager sharedStickHandler] joystickCount], GUI_ROW(GAME,STICKMAPPER));
    9067             : 
    9068             :                 [gui setText:DESC(@"gameoptions-keyboard-configuration") forRow: GUI_ROW(GAME,KEYMAPPER) align: GUI_ALIGN_CENTER];
    9069             :                 [gui setKey: GUI_KEY_OK forRow: GUI_ROW(GAME,KEYMAPPER)];
    9070             : 
    9071             :                 
    9072             :                 NSString *musicMode = [UNIVERSE descriptionForArrayKey:@"music-mode" index:[[OOMusicController sharedController] mode]];
    9073             :                 NSString *message = OOExpandKey(@"gameoptions-music-mode", musicMode);
    9074             :                 [gui setText:message forRow:GUI_ROW(GAME,MUSIC) align:GUI_ALIGN_CENTER];
    9075             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,MUSIC)];
    9076             : 
    9077             :                 if (![gameView hdrOutput])
    9078             :                 {
    9079             :                         if ([UNIVERSE wireframeGraphics])
    9080             :                                 [gui setText:DESC(@"gameoptions-wireframe-graphics-yes") forRow:GUI_ROW(GAME,WIREFRAMEGRAPHICS) align:GUI_ALIGN_CENTER];
    9081             :                         else
    9082             :                                 [gui setText:DESC(@"gameoptions-wireframe-graphics-no") forRow:GUI_ROW(GAME,WIREFRAMEGRAPHICS) align:GUI_ALIGN_CENTER];
    9083             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,WIREFRAMEGRAPHICS)];
    9084             :                 }
    9085             : #if OOLITE_WINDOWS
    9086             :                 else
    9087             :                 {
    9088             :                         float paperWhite = [gameView hdrPaperWhiteBrightness];
    9089             :                         int paperWhiteTicks = (int)((paperWhite - MIN_HDR_PAPERWHITE) * 20 / (MAX_HDR_PAPERWHITE - MIN_HDR_PAPERWHITE));
    9090             :                         NSString* paperWhiteWordDesc = DESC(@"gameoptions-hdr-paperwhite");
    9091             :                         [gui setText:[NSString stringWithFormat:@"%@%@ (%d) ", paperWhiteWordDesc, SliderString(paperWhiteTicks), (int)paperWhite] forRow:GUI_ROW(GAME,HDRPAPERWHITE) align:GUI_ALIGN_CENTER];
    9092             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,HDRPAPERWHITE)];
    9093             :                 }
    9094             : #endif
    9095             :                 
    9096             : #if !NEW_PLANETS
    9097             :                 if ([UNIVERSE doProcedurallyTexturedPlanets])
    9098             :                         [gui setText:DESC(@"gameoptions-procedurally-textured-planets-yes") forRow:GUI_ROW(GAME,PROCEDURALLYTEXTUREDPLANETS) align:GUI_ALIGN_CENTER];
    9099             :                 else
    9100             :                         [gui setText:DESC(@"gameoptions-procedurally-textured-planets-no") forRow:GUI_ROW(GAME,PROCEDURALLYTEXTUREDPLANETS) align:GUI_ALIGN_CENTER];
    9101             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,PROCEDURALLYTEXTUREDPLANETS)];
    9102             : #endif
    9103             : 
    9104             :                 OOGraphicsDetail detailLevel = [UNIVERSE detailLevel];
    9105             :                 NSString *shaderEffectsOptionsString = OOExpand(@"gameoptions-detaillevel-[detailLevel]", detailLevel);
    9106             :                 [gui setText:OOExpandKey(shaderEffectsOptionsString) forRow:GUI_ROW(GAME,SHADEREFFECTS) align:GUI_ALIGN_CENTER];
    9107             :                 if (![[OOOpenGLExtensionManager sharedManager] shadersForceDisabled])
    9108             :                 {
    9109             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,SHADEREFFECTS)];
    9110             :                 }
    9111             :                 else
    9112             :                 {
    9113             :                         // deactivate this option if shaders have been disabled from the commend line
    9114             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(GAME,SHADEREFFECTS)];
    9115             :                 }
    9116             :                 
    9117             :                 
    9118             :                 if ([UNIVERSE dockingClearanceProtocolActive])
    9119             :                 {
    9120             :                         [gui setText:DESC(@"gameoptions-docking-clearance-yes") forRow:GUI_ROW(GAME,DOCKINGCLEARANCE) align:GUI_ALIGN_CENTER];
    9121             :                 }
    9122             :                 else
    9123             :                 {
    9124             :                         [gui setText:DESC(@"gameoptions-docking-clearance-no") forRow:GUI_ROW(GAME,DOCKINGCLEARANCE) align:GUI_ALIGN_CENTER];
    9125             :                 }
    9126             :                 OO_SETACCESSCONDITIONFORROW(!startingGame, GUI_ROW(GAME,DOCKINGCLEARANCE));
    9127             :                 
    9128             :                 // Back menu option
    9129             :                 [gui setText:DESC(@"gui-back") forRow:GUI_ROW(GAME,BACK) align:GUI_ALIGN_CENTER];
    9130             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(GAME,BACK)];
    9131             : 
    9132             :                 [gui setSelectableRange:NSMakeRange(first_sel_row, GUI_ROW_GAMEOPTIONS_END_OF_LIST)];
    9133             :                 [gui setSelectedRow: first_sel_row];
    9134             : 
    9135             :                 [gui setShowTextCursor:NO];
    9136             :                 [gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"paused_overlay"];
    9137             :                 [gui setBackgroundTextureKey:@"settings"];
    9138             :         }
    9139             :         /* ends */
    9140             : 
    9141             :         [self setShowDemoShips:NO];
    9142             :         gui_screen = GUI_SCREEN_GAMEOPTIONS;
    9143             : 
    9144             :         [self setShowDemoShips:NO];
    9145             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
    9146             : }
    9147             : 
    9148             : 
    9149             : - (void) setGuiToLoadSaveScreen
    9150             : {
    9151             :         BOOL                    gamePaused = [[UNIVERSE gameController] isGamePaused];
    9152             :         BOOL                    canLoadOrSave = NO;
    9153             :         MyOpenGLView    *gameView = [UNIVERSE gameView];
    9154             :         OOGUIScreenID   oldScreen = gui_screen;
    9155             :         
    9156             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
    9157             : 
    9158             :         if ([self status] == STATUS_DOCKED)
    9159             :         {
    9160             :                 if ([self dockedStation] == nil)  [self setDockedAtMainStation];
    9161             :                 canLoadOrSave = (([self dockedStation] == [UNIVERSE station] || [[self dockedStation] allowsSaving]) && !([[UNIVERSE sun] goneNova] || [[UNIVERSE sun] willGoNova]));
    9162             :         }
    9163             :         
    9164             :         BOOL canQuickSave = (canLoadOrSave && ([[gameView gameController] playerFileToLoad] != nil));
    9165             :         
    9166             :         // GUI stuff
    9167             :         {
    9168             :                 GuiDisplayGen* gui = [UNIVERSE gui];
    9169             :                 GUI_ROW_INIT(gui);
    9170             : 
    9171             :                 int first_sel_row = (canLoadOrSave)? GUI_ROW(,SAVE) : GUI_ROW(,GAMEOPTIONS);
    9172             :                 if (canQuickSave)
    9173             :                         first_sel_row = GUI_ROW(,QUICKSAVE);
    9174             : 
    9175             :                 [gui clear];
    9176             :                 [gui setTitle:[NSString stringWithFormat:DESC(@"status-commander-@"), [self commanderName]]]; //Same title as status screen.
    9177             :                 
    9178             :                 [gui setText:DESC(@"options-quick-save") forRow:GUI_ROW(,QUICKSAVE) align:GUI_ALIGN_CENTER];
    9179             :                 if (canQuickSave)
    9180             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(,QUICKSAVE)];
    9181             :                 else
    9182             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(,QUICKSAVE)];
    9183             : 
    9184             :                 [gui setText:DESC(@"options-save-commander") forRow:GUI_ROW(,SAVE) align:GUI_ALIGN_CENTER];
    9185             :                 [gui setText:DESC(@"options-load-commander") forRow:GUI_ROW(,LOAD) align:GUI_ALIGN_CENTER];
    9186             :                 if (canLoadOrSave)
    9187             :                 {
    9188             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(,SAVE)];
    9189             :                         [gui setKey:GUI_KEY_OK forRow:GUI_ROW(,LOAD)];
    9190             :                 }
    9191             :                 else
    9192             :                 {
    9193             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(,SAVE)];
    9194             :                         [gui setColor:[OOColor grayColor] forRow:GUI_ROW(,LOAD)];
    9195             :                 }
    9196             : 
    9197             :                 [gui setText:DESC(@"options-return-to-menu") forRow:GUI_ROW(,BEGIN_NEW) align:GUI_ALIGN_CENTER];
    9198             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(,BEGIN_NEW)];
    9199             : 
    9200             :                 [gui setText:DESC(@"options-game-options") forRow:GUI_ROW(,GAMEOPTIONS) align:GUI_ALIGN_CENTER];
    9201             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(,GAMEOPTIONS)];
    9202             :                 
    9203             : #if OOLITE_SDL
    9204             :                 // GNUstep needs a quit option at present (no Cmd-Q) but
    9205             :                 // doesn't need speech.
    9206             :                 
    9207             :                 // quit menu option
    9208             :                 [gui setText:DESC(@"options-exit-game") forRow:GUI_ROW(,QUIT) align:GUI_ALIGN_CENTER];
    9209             :                 [gui setKey:GUI_KEY_OK forRow:GUI_ROW(,QUIT)];
    9210             : #endif
    9211             :                 
    9212             :                 [gui setSelectableRange:NSMakeRange(first_sel_row, GUI_ROW_OPTIONS_END_OF_LIST)];
    9213             : 
    9214             :                 if (gamePaused || (!canLoadOrSave && [self status] == STATUS_DOCKED))
    9215             :                 {
    9216             :                         [gui setSelectedRow: GUI_ROW(,GAMEOPTIONS)];
    9217             :                 }
    9218             :                 else
    9219             :                 {
    9220             :                         [gui setSelectedRow: first_sel_row];
    9221             :                 }
    9222             :                 
    9223             :                 [gui setShowTextCursor:NO];
    9224             :                 
    9225             :                 if ([gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"paused_overlay"] && [UNIVERSE pauseMessageVisible])
    9226             :                                         [[UNIVERSE messageGUI] clear];
    9227             :                 // Graphically, this screen is analogous to the various settings screens
    9228             :                 [gui setBackgroundTextureKey:@"settings"];
    9229             :         }
    9230             :         /* ends */
    9231             :         
    9232             :         [[UNIVERSE gameView] clearMouse];
    9233             :         
    9234             :         [self setShowDemoShips:NO];
    9235             :         gui_screen = GUI_SCREEN_OPTIONS;
    9236             :         
    9237             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
    9238             :         
    9239             :         if (gamePaused)
    9240             :         {
    9241             :                 [[UNIVERSE messageGUI] clear]; 
    9242             :                 NSString *pauseKey = [PLAYER keyBindingDescription2:@"key_pausebutton"];
    9243             :                 [UNIVERSE addMessage:OOExpandKey(@"game-paused-docked", pauseKey) forCount:1.0 forceDisplay:YES];
    9244             :         }
    9245             :         
    9246             :         [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
    9247             : }
    9248             : 
    9249             : 
    9250           0 : static NSString *last_outfitting_key=nil;
    9251             : 
    9252             : 
    9253             : - (void) highlightEquipShipScreenKey:(NSString *)key
    9254             : {
    9255             :         int                     i=0;
    9256             :         OOGUIRow                row;
    9257             :         NSString                *otherKey = @"";
    9258             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    9259             :         [last_outfitting_key release];
    9260             :         last_outfitting_key = [key copy];
    9261             :         [self setGuiToEquipShipScreen:-1];
    9262             :         key = last_outfitting_key;
    9263             :         // TODO: redo the equipShipScreen in a way that isn't broken. this whole method 'works'
    9264             :         // based on the way setGuiToEquipShipScreen  'worked' on 20090913 - Kaks 
    9265             :         
    9266             :         // setGuiToEquipShipScreen doesn't take a page number, it takes an offset from the beginning
    9267             :         // of the dictionary, the first line will show the key at that offset...
    9268             :         
    9269             :         // try the last page first - 10 pages max.
    9270             :         while (otherKey)
    9271             :         {
    9272             :                 [self setGuiToEquipShipScreen:i];
    9273             :                 for (row = GUI_ROW_EQUIPMENT_START;row<=GUI_MAX_ROWS_EQUIPMENT+2;row++)
    9274             :                 {
    9275             :                         otherKey = [gui keyForRow:row];
    9276             :                         if (!otherKey)
    9277             :                         {
    9278             :                                 [self setGuiToEquipShipScreen:0];
    9279             :                                 return;
    9280             :                         }
    9281             :                         if ([otherKey isEqualToString:key])
    9282             :                         {
    9283             :                                 [gui setSelectedRow:row];
    9284             :                                 [self showInformationForSelectedUpgrade];
    9285             :                                 return;
    9286             :                         }
    9287             :                 }
    9288             :                 if ([otherKey hasPrefix:@"More:"])
    9289             :                 {
    9290             :                         i = [[otherKey componentsSeparatedByString:@":"] oo_intAtIndex:1];
    9291             :                 }
    9292             :                 else
    9293             :                 {
    9294             :                         [self setGuiToEquipShipScreen:0];
    9295             :                         return;
    9296             :                 }
    9297             :         }
    9298             : }
    9299             : 
    9300             : 
    9301             : - (OOWeaponFacingSet) availableFacings
    9302             : {
    9303             :         OOShipRegistry          *registry = [OOShipRegistry sharedRegistry];
    9304             :         NSDictionary            *shipyardInfo = [registry shipyardInfoForKey:[self shipDataKey]];
    9305             :         unsigned                        available_facings = [shipyardInfo oo_unsignedIntForKey:KEY_WEAPON_FACINGS defaultValue:[self weaponFacings]];   // use defaults  explicitly
    9306             :         
    9307             :         return available_facings & VALID_WEAPON_FACINGS;
    9308             : }
    9309             : 
    9310             : 
    9311             : - (void) setGuiToEquipShipScreen:(int)skipParam selectingFacingFor:(NSString *)eqKeyForSelectFacing
    9312             : {
    9313             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
    9314             :         
    9315             :         missiles = [self countMissiles];
    9316             :         OOEntityStatus searchStatus; // use STATUS_TEST, STATUS_DEAD & STATUS_ACTIVE
    9317             :         NSString *showKey = nil;
    9318             :         unsigned skip;
    9319             : 
    9320             :         if (skipParam < 0)
    9321             :         {
    9322             :                 skip = 0;
    9323             :                 searchStatus = STATUS_TEST;
    9324             :         }
    9325             :         else
    9326             :         {
    9327             :                 skip = skipParam;
    9328             :                 searchStatus = STATUS_ACTIVE;
    9329             :         }
    9330             : 
    9331             :         // don't show a "Back" item if we're only skipping one item - just show the item
    9332             :         if (skip == 1)
    9333             :                 skip = 0;
    9334             : 
    9335             :         double priceFactor = 1.0;
    9336             :         OOTechLevelID techlevel = [[UNIVERSE currentSystemData] oo_intForKey:KEY_TECHLEVEL];
    9337             : 
    9338             :         StationEntity *dockedStation = [self dockedStation];
    9339             :         if (dockedStation)
    9340             :         {
    9341             :                 priceFactor = [dockedStation equipmentPriceFactor];
    9342             :                 if ([dockedStation equivalentTechLevel] != NSNotFound)
    9343             :                         techlevel = [dockedStation equivalentTechLevel];
    9344             :         }
    9345             : 
    9346             :         // build an array of all equipment - and take away that which has been bought (or is not permitted)
    9347             :         NSMutableArray          *equipmentAllowed = [NSMutableArray array];
    9348             :         
    9349             :         // find options that agree with this ship
    9350             :         OOShipRegistry          *registry = [OOShipRegistry sharedRegistry];
    9351             :         NSDictionary            *shipyardInfo = [registry shipyardInfoForKey:[self shipDataKey]];
    9352             :         NSMutableSet            *options = [NSMutableSet setWithArray:[shipyardInfo oo_arrayForKey:KEY_OPTIONAL_EQUIPMENT]];
    9353             :         
    9354             :         // add standard items too!
    9355             :         [options addObjectsFromArray:[[shipyardInfo oo_dictionaryForKey:KEY_STANDARD_EQUIPMENT] oo_arrayForKey:KEY_EQUIPMENT_EXTRAS]];
    9356             :         
    9357             :         unsigned                        i = 0;
    9358             :         NSEnumerator            *eqEnum = nil;
    9359             :         OOEquipmentType         *eqType = nil;
    9360             :         unsigned                        available_facings = [shipyardInfo oo_unsignedIntForKey:KEY_WEAPON_FACINGS defaultValue:[self weaponFacings]];   // use defaults  explicitly
    9361             : 
    9362             :         
    9363             :         if (eqKeyForSelectFacing != nil) // Weapons purchase subscreen.
    9364             :         {
    9365             :                 skip = 1;       // show the back button
    9366             :                 // The 3 lines below are needed by the present GUI. TODO:create a sane GUI. Kaks - 20090915 & 201005
    9367             :                 [equipmentAllowed addObject:eqKeyForSelectFacing];
    9368             :                 [equipmentAllowed addObject:eqKeyForSelectFacing];
    9369             :                 [equipmentAllowed addObject:eqKeyForSelectFacing];
    9370             :         }
    9371             :         else for (eqEnum = [OOEquipmentType equipmentEnumeratorOutfitting]; (eqType = [eqEnum nextObject]); i++)
    9372             :         {
    9373             :                 NSString                        *eqKey = [eqType identifier];
    9374             :                 OOTechLevelID           minTechLevel = [eqType effectiveTechLevel];
    9375             :                 
    9376             :                 // set initial availability to NO
    9377             :                 BOOL isOK = NO;
    9378             :                 
    9379             :                 // check special availability
    9380             :                 if ([eqType isAvailableToAll])  [options addObject:eqKey];
    9381             :                 
    9382             :                 // if you have a damaged system you can get it repaired at a tech level one less than that required to buy it
    9383             :                 if (minTechLevel != 0 && [self hasEquipmentItem:[eqType damagedIdentifier]])  minTechLevel--;
    9384             :                 
    9385             :                 // reduce the minimum techlevel occasionally as a bonus..
    9386             :                 if (techlevel < minTechLevel && techlevel + 3 > minTechLevel)
    9387             :                 {
    9388             :                         unsigned day = i * 13 + (unsigned)floor([UNIVERSE getTime] / 86400.0);
    9389             :                         unsigned char dayRnd = (day & 0xff) ^ (unsigned char)system_id;
    9390             :                         OOTechLevelID originalMinTechLevel = minTechLevel;
    9391             :                         
    9392             :                         while (minTechLevel > 0 && minTechLevel > originalMinTechLevel - 3 && !(dayRnd & 7))  // bargain tech days every 1/8 days
    9393             :                         {
    9394             :                                 dayRnd = dayRnd >> 2;
    9395             :                                 minTechLevel--; // occasional bonus items according to TL
    9396             :                         }
    9397             :                 }
    9398             :                 
    9399             :                 // check initial availability against options AND standard extras
    9400             :                 if ([options containsObject:eqKey])
    9401             :                 {
    9402             :                         isOK = YES;
    9403             :                         [options removeObject:eqKey];
    9404             :                 }
    9405             : 
    9406             :                 if (isOK)
    9407             :                 {
    9408             :                         if (techlevel < minTechLevel) isOK = NO;
    9409             :                         if (![self canAddEquipment:eqKey inContext:@"purchase"]) isOK = NO;
    9410             :                         if (available_facings == 0 && [eqType isPrimaryWeapon]) isOK = NO;
    9411             :                         if (isOK)  [equipmentAllowed addObject:eqKey];
    9412             :                 }
    9413             :                 
    9414             :                 if (searchStatus == STATUS_DEAD && isOK)
    9415             :                 {
    9416             :                         showKey = eqKey;
    9417             :                         searchStatus = STATUS_ACTIVE;
    9418             :                 }
    9419             :                 if (searchStatus == STATUS_TEST)
    9420             :                 {
    9421             :                         if (isOK) showKey = eqKey;
    9422             :                         if ([eqKey isEqualToString:last_outfitting_key]) 
    9423             :                                 searchStatus = isOK ? STATUS_ACTIVE : STATUS_DEAD;
    9424             :                 }
    9425             :         }
    9426             :         if (searchStatus != STATUS_TEST && showKey != nil)
    9427             :         {
    9428             :                 [last_outfitting_key release];
    9429             :                 last_outfitting_key = [showKey copy];
    9430             :         }
    9431             :         
    9432             :         // GUI stuff
    9433             :         {
    9434             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
    9435             :                 OOGUIRow                start_row = GUI_ROW_EQUIPMENT_START;
    9436             :                 OOGUIRow                row = start_row;
    9437             :                 unsigned        facing_count = 0;
    9438             :                 BOOL                    displayRow = YES;
    9439             :                 BOOL                    weaponMounted = NO;
    9440             :                 BOOL                    guiChanged = (gui_screen != GUI_SCREEN_EQUIP_SHIP);
    9441             : 
    9442             :                 [gui clearAndKeepBackground:!guiChanged];
    9443             :                 [gui setTitle:DESC(@"equip-title")];
    9444             :                 
    9445             :                 [gui setColor:[gui colorFromSetting:kGuiEquipmentCashColor defaultValue:nil] forRow: GUI_ROW_EQUIPMENT_CASH];
    9446             :                 [gui setText:OOExpandKey(@"equip-cash-value", credits) forRow:GUI_ROW_EQUIPMENT_CASH];
    9447             :                 
    9448             :                 OOGUITabSettings tab_stops;
    9449             :                 tab_stops[0] = 0;
    9450             :                 tab_stops[1] = -360;
    9451             :                 tab_stops[2] = -480;
    9452             :                 [gui overrideTabs:tab_stops from:kGuiEquipmentTabs length:3];
    9453             :                 [gui setTabStops:tab_stops];
    9454             :                 
    9455             :                 unsigned n_rows = GUI_MAX_ROWS_EQUIPMENT;
    9456             :                 NSUInteger count = [equipmentAllowed count];
    9457             : 
    9458             :                 if (count > 0)
    9459             :                 {
    9460             :                         if (skip > 0)        // lose the first row to Back <--
    9461             :                         {
    9462             :                                 unsigned previous;
    9463             : 
    9464             :                                 if (count <= n_rows || skip < n_rows)
    9465             :                                         previous = 0;                                   // single page
    9466             :                                 else
    9467             :                                 {
    9468             :                                         previous = skip - (n_rows - 2); // multi-page. 
    9469             :                                         if (previous < 2)
    9470             :                                                 previous = 0;                           // if only one previous item, just show it
    9471             :                                 }
    9472             : 
    9473             :                                 if (eqKeyForSelectFacing != nil)
    9474             :                                 {
    9475             :                                         previous = 0;
    9476             :                                         // keep weapon selected if we go back.
    9477             :                                         [gui setKey:[NSString stringWithFormat:@"More:%d:%@", previous, eqKeyForSelectFacing] forRow:row];
    9478             :                                 }
    9479             :                                 else
    9480             :                                 {
    9481             :                                         [gui setKey:[NSString stringWithFormat:@"More:%d", previous] forRow:row];
    9482             :                                 }
    9483             :                                 [gui setColor:[gui colorFromSetting:kGuiEquipmentScrollColor defaultValue:[OOColor greenColor]] forRow:row];
    9484             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"", @" <-- ", nil] forRow:row];
    9485             :                                 row++;
    9486             :                         }
    9487             :                         
    9488             :                         for (i = skip; i < count && (row - start_row < (OOGUIRow)n_rows); i++)
    9489             :                         {
    9490             :                                 NSString                        *eqKey = [equipmentAllowed oo_stringAtIndex:i];
    9491             :                                 OOEquipmentType         *eqInfo = [OOEquipmentType equipmentTypeWithIdentifier:eqKey];
    9492             :                                 OOCreditsQuantity       pricePerUnit = [eqInfo price];
    9493             :                                 NSString                        *desc = [NSString stringWithFormat:@" %@ ", [eqInfo name]];
    9494             :                                 NSString                        *eq_key_damaged = [eqInfo damagedIdentifier];
    9495             :                                 double                          price;
    9496             : 
    9497             :                                 OOColor                         *dispCol = [eqInfo displayColor];
    9498             :                                 if (dispCol == nil) dispCol = [gui colorFromSetting:kGuiEquipmentOptionColor defaultValue:nil];
    9499             :                                 [gui setColor:dispCol forRow:row]; 
    9500             : 
    9501             :                                 if ([eqKey isEqual:@"EQ_FUEL"])
    9502             :                                 {
    9503             :                                         price = (PLAYER_MAX_FUEL - fuel) * pricePerUnit * [self fuelChargeRate];
    9504             :                                 }
    9505             :                                 else if ([eqKey isEqualToString:@"EQ_RENOVATION"])
    9506             :                                 {
    9507             :                                         price = [self renovationCosts];
    9508             :                                         [gui setColor:[gui colorFromSetting:kGuiEquipmentRepairColor defaultValue:[OOColor orangeColor]] forRow:row];
    9509             :                                 }
    9510             :                                 else
    9511             :                                 {
    9512             :                                         price = pricePerUnit;
    9513             :                                 }
    9514             :                                 
    9515             :                                 price = [self adjustPriceByScriptForEqKey:eqKey withCurrent:price];
    9516             : 
    9517             :                                 price *= priceFactor;  // increased prices at some stations
    9518             :                                 
    9519             :                                 NSUInteger installTime = [eqInfo installTime];
    9520             :                                 if (installTime == 0)
    9521             :                                 {
    9522             :                                         installTime = 600 + price;
    9523             :                                 }
    9524             :                                 // is this item damaged?
    9525             :                                 if ([self hasEquipmentItem:eq_key_damaged])
    9526             :                                 {
    9527             :                                         desc = [NSString stringWithFormat:DESC(@"equip-repair-@"), desc];
    9528             :                                         price /= 2.0;
    9529             :                                         installTime = [eqInfo repairTime];
    9530             :                                         if (installTime == 0)
    9531             :                                         {
    9532             :                                                 installTime = 600 + price;
    9533             :                                         }
    9534             :                                         [gui setColor:[gui colorFromSetting:kGuiEquipmentRepairColor defaultValue:[OOColor orangeColor]] forRow:row];
    9535             : 
    9536             :                                 }
    9537             :                                 
    9538             :                                 NSString *timeString = [UNIVERSE shortTimeDescription:installTime];
    9539             :                                 NSString *priceString = [NSString stringWithFormat:@" %@ ", OOCredits(price)];
    9540             : 
    9541             :                                 if ([eqKeyForSelectFacing isEqualToString:eqKey])
    9542             :                                 {
    9543             :                                         // Weapons purchase subscreen.
    9544             :                                         while (facing_count < 5)
    9545             :                                         {
    9546             :                                                 NSUInteger multiplier = 1;
    9547             :                                                 switch (facing_count)
    9548             :                                                 {
    9549             :                                                         case 0:
    9550             :                                                                 break;
    9551             :                                                                 
    9552             :                                                         case 1:
    9553             :                                                                 displayRow = available_facings & WEAPON_FACING_FORWARD;
    9554             :                                                                 desc = FORWARD_FACING_STRING;
    9555             :                                                                 weaponMounted = !isWeaponNone(forward_weapon_type);
    9556             :                                                                 if (_multiplyWeapons)
    9557             :                                                                 {
    9558             :                                                                         multiplier = [forwardWeaponOffset count];
    9559             :                                                                 }
    9560             :                                                                 break;
    9561             :                                                                 
    9562             :                                                         case 2:
    9563             :                                                                 displayRow = available_facings & WEAPON_FACING_AFT;
    9564             :                                                                 desc = AFT_FACING_STRING;
    9565             :                                                                 weaponMounted = !isWeaponNone(aft_weapon_type);
    9566             :                                                                 if (_multiplyWeapons)
    9567             :                                                                 {
    9568             :                                                                         multiplier = [aftWeaponOffset count];
    9569             :                                                                 }
    9570             :                                                                 break;
    9571             :                                                                 
    9572             :                                                         case 3:
    9573             :                                                                 displayRow = available_facings & WEAPON_FACING_PORT;
    9574             :                                                                 desc = PORT_FACING_STRING;
    9575             :                                                                 weaponMounted = !isWeaponNone(port_weapon_type);
    9576             :                                                                 if (_multiplyWeapons)
    9577             :                                                                 {
    9578             :                                                                         multiplier = [portWeaponOffset count];
    9579             :                                                                 }
    9580             :                                                                 break;
    9581             :                                                                 
    9582             :                                                         case 4:
    9583             :                                                                 displayRow = available_facings & WEAPON_FACING_STARBOARD;
    9584             :                                                                 desc = STARBOARD_FACING_STRING;
    9585             :                                                                 weaponMounted = !isWeaponNone(starboard_weapon_type);
    9586             :                                                                 if (_multiplyWeapons)
    9587             :                                                                 {
    9588             :                                                                         multiplier = [starboardWeaponOffset count];
    9589             :                                                                 }
    9590             :                                                                 break;
    9591             :                                                 }
    9592             :                                                 
    9593             :                                                 if(weaponMounted)
    9594             :                                                 {
    9595             :                                                         [gui setColor:[gui colorFromSetting:kGuiEquipmentLaserFittedColor defaultValue:[OOColor colorWithRed:0.0f green:0.6f blue:0.0f alpha:1.0f]] forRow:row];
    9596             :                                                 }
    9597             :                                                 else
    9598             :                                                 {
    9599             :                                                         [gui setColor:[gui colorFromSetting:kGuiEquipmentLaserColor defaultValue:[OOColor greenColor]] forRow:row];
    9600             :                                                 }
    9601             :                                                 if (displayRow) // Always true for the first pass. The first pass is used to display the name of the weapon being purchased.
    9602             :                                                 {
    9603             : 
    9604             :                                                         priceString = [NSString stringWithFormat:@" %@ ", OOCredits(price*multiplier)];
    9605             : 
    9606             :                                                         [gui setKey:eqKey forRow:row];
    9607             :                                                         [gui setArray:[NSArray arrayWithObjects:desc, (facing_count > 0 ? priceString : (NSString *)@""), timeString, nil] forRow:row];
    9608             :                                                         row++;
    9609             :                                                 }
    9610             :                                                 facing_count++;
    9611             :                                         }
    9612             :                                 }
    9613             :                                 else
    9614             :                                 {
    9615             :                                         // Normal equipment list.
    9616             :                                         [gui setKey:eqKey forRow:row];
    9617             :                                         // check if the hidevalues property has been set
    9618             :                                         if (![eqInfo hideValues])
    9619             :                                         {
    9620             :                                                 [gui setArray:[NSArray arrayWithObjects:desc, priceString, timeString, nil] forRow:row];
    9621             :                                         }
    9622             :                                         else
    9623             :                                         {
    9624             :                                                 // if so, only output the description
    9625             :                                                 [gui setArray:[NSArray arrayWithObjects:desc, nil] forRow:row];
    9626             :                                         }
    9627             :                                         row++;
    9628             :                                 }
    9629             :                         }
    9630             : 
    9631             :                         if (i < count)
    9632             :                         {
    9633             :                                 // just overwrite the last item :-)
    9634             :                                 [gui setColor:[gui colorFromSetting:kGuiEquipmentScrollColor defaultValue:[OOColor greenColor]] forRow:row-1];
    9635             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"", @" --> ", nil] forRow:row - 1];
    9636             :                                 [gui setKey:[NSString stringWithFormat:@"More:%d", i - 1] forRow:row - 1];
    9637             :                         }
    9638             :                         
    9639             :                         [gui setSelectableRange:NSMakeRange(start_row,row - start_row)];
    9640             : 
    9641             :                         if ([gui selectedRow] != start_row)
    9642             :                                 [gui setSelectedRow:start_row];
    9643             : 
    9644             :                         if (eqKeyForSelectFacing != nil)
    9645             :                         {
    9646             :                                 [gui setSelectedRow:start_row + 1];
    9647             :                                 [self showInformationForSelectedUpgradeWithFormatString:DESC(@"@-select-where-to-install")];
    9648             :                         }
    9649             :                         else
    9650             :                         {
    9651             :                                 [self showInformationForSelectedUpgrade];
    9652             :                         }
    9653             :                 }
    9654             :                 else
    9655             :                 {
    9656             :                         [gui setText:DESC(@"equip-no-equipment-available-for-purchase") forRow:GUI_ROW_NO_SHIPS align:GUI_ALIGN_CENTER];
    9657             :                         [gui setColor:[gui colorFromSetting:kGuiEquipmentUnavailableColor defaultValue:[OOColor greenColor]] forRow:GUI_ROW_NO_SHIPS];
    9658             :                         
    9659             :                         [gui setSelectableRange:NSMakeRange(0,0)];
    9660             :                         [gui setNoSelectedRow];
    9661             :                         [self showInformationForSelectedUpgrade];
    9662             :                 }
    9663             :                 
    9664             :                 [gui setShowTextCursor:NO];
    9665             :                 
    9666             :                 // TODO: split the mount_weapon sub-screen into a separate screen, and use it for pylon mounted wepons as well?
    9667             :                 if (guiChanged)
    9668             :                 {
    9669             :                         [gui setForegroundTextureKey:@"docked_overlay"];
    9670             :                         NSDictionary *background = [UNIVERSE screenTextureDescriptorForKey:@"equip_ship"];
    9671             :                         [self setEquipScreenBackgroundDescriptor:background];
    9672             :                         [gui setBackgroundTextureDescriptor:background];
    9673             :                 }
    9674             :                 else if (eqKeyForSelectFacing != nil) // weapon purchase
    9675             :                 {
    9676             :                         NSDictionary *bgDescriptor = [UNIVERSE screenTextureDescriptorForKey:@"mount_weapon"];
    9677             :                         if (bgDescriptor != nil)  [gui setBackgroundTextureDescriptor:bgDescriptor];
    9678             :                 }
    9679             :                 else // Returning from a weapon purchase. (Also called, redundantly, when paging)
    9680             :                 {
    9681             :                         [gui setBackgroundTextureDescriptor:[self equipScreenBackgroundDescriptor]];
    9682             :                 }
    9683             :         }
    9684             :         /* ends */
    9685             : 
    9686             :         chosen_weapon_facing = WEAPON_FACING_NONE;
    9687             :         [self setShowDemoShips:NO];
    9688             :         gui_screen = GUI_SCREEN_EQUIP_SHIP;
    9689             : 
    9690             :         [self setShowDemoShips:NO];
    9691             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
    9692             : }
    9693             : 
    9694             : 
    9695             : - (void) setGuiToEquipShipScreen:(int)skip
    9696             : {
    9697             :         [self setGuiToEquipShipScreen:skip selectingFacingFor:nil];
    9698             : }
    9699             : 
    9700             : 
    9701             : - (void) showInformationForSelectedUpgrade
    9702             : {
    9703             :         [self showInformationForSelectedUpgradeWithFormatString:nil];
    9704             : }
    9705             : 
    9706             :         
    9707             : - (void) showInformationForSelectedUpgradeWithFormatString:(NSString *)formatString
    9708             : {
    9709             :         GuiDisplayGen* gui = [UNIVERSE gui];
    9710             :         NSString* eqKey = [gui selectedRowKey];
    9711             :         int i;
    9712             : 
    9713             :         OOColor *descColor = [gui colorFromSetting:kGuiEquipmentDescriptionColor defaultValue:[OOColor greenColor]];
    9714             :         for (i = GUI_ROW_EQUIPMENT_DETAIL; i < GUI_MAX_ROWS; i++)
    9715             :         {
    9716             :                 [gui setText:@"" forRow:i];
    9717             :                 [gui setColor:descColor forRow:i];
    9718             :         }
    9719             :         if (eqKey)
    9720             :         {
    9721             :                 if (![eqKey hasPrefix:@"More:"])
    9722             :                 {
    9723             :                         NSString* desc = [[OOEquipmentType equipmentTypeWithIdentifier:eqKey] descriptiveText];
    9724             :                         NSString* eq_key_damaged = [NSString stringWithFormat:@"%@_DAMAGED", eqKey];
    9725             :                         int weight = [[OOEquipmentType equipmentTypeWithIdentifier:eqKey] requiredCargoSpace];
    9726             :                         if ([self hasEquipmentItem:eq_key_damaged])
    9727             :                         {
    9728             :                                 desc = [NSString stringWithFormat:DESC(@"upgradeinfo-@-price-is-for-repairing"), desc];
    9729             :                         }
    9730             :                         else
    9731             :                         {
    9732             :                                 if([eqKey hasSuffix:@"ENERGY_UNIT"] && ([self hasEquipmentItem:@"EQ_ENERGY_UNIT_DAMAGED"] || [self hasEquipmentItem:@"EQ_ENERGY_UNIT"] || [self hasEquipmentItem:@"EQ_NAVAL_ENERGY_UNIT_DAMAGED"]))
    9733             :                                         desc = [NSString stringWithFormat:DESC(@"@-will-replace-other-energy"), desc];
    9734             :                                 if (weight > 0) desc = [NSString stringWithFormat:DESC(@"upgradeinfo-@-weight-d-of-equipment"), desc, weight];
    9735             :                         }
    9736             :                         if (formatString) desc = [NSString stringWithFormat:formatString, desc];
    9737             :                         [gui addLongText:desc startingAtRow:GUI_ROW_EQUIPMENT_DETAIL align:GUI_ALIGN_LEFT];
    9738             :                 }
    9739             :         }
    9740             : }
    9741             : 
    9742             : 
    9743             : - (void) setGuiToInterfacesScreen:(int)skip
    9744             : {
    9745             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
    9746             :         if (gui_screen != GUI_SCREEN_INTERFACES)
    9747             :         {
    9748             :                 [self noteGUIWillChangeTo:GUI_SCREEN_INTERFACES];
    9749             :         }
    9750             :         
    9751             :         // build an array of available interfaces
    9752             :         NSDictionary *interfaces = [[self dockedStation] localInterfaces];
    9753             :         NSArray         *interfaceKeys = [interfaces keysSortedByValueUsingSelector:@selector(interfaceCompare:)]; // sorts by category, then title
    9754             :         int i;
    9755             :         
    9756             :         // GUI stuff
    9757             :         {
    9758             :                 GuiDisplayGen   *gui = [UNIVERSE gui];
    9759             :                 OOGUIRow                start_row = GUI_ROW_INTERFACES_START;
    9760             :                 OOGUIRow                row = start_row;
    9761             :                 BOOL                    guiChanged = (gui_screen != GUI_SCREEN_INTERFACES);
    9762             : 
    9763             :                 [gui clearAndKeepBackground:!guiChanged];
    9764             :                 [gui setTitle:DESC(@"interfaces-title")];
    9765             :                 
    9766             :                 
    9767             :                 OOGUITabSettings tab_stops;
    9768             :                 tab_stops[0] = 0;
    9769             :                 tab_stops[1] = -480;
    9770             :                 [gui overrideTabs:tab_stops from:kGuiInterfaceTabs length:2];
    9771             :                 [gui setTabStops:tab_stops];
    9772             :                 
    9773             :                 unsigned n_rows = GUI_MAX_ROWS_INTERFACES;
    9774             :                 NSUInteger count = [interfaceKeys count];
    9775             : 
    9776             :                 if (count > 0)
    9777             :                 {
    9778             :                         if (skip > 0)        // lose the first row to Back <--
    9779             :                         {
    9780             :                                 unsigned previous;
    9781             :                                 
    9782             :                                 if (count <= n_rows || skip < (NSInteger)n_rows)
    9783             :                                 {
    9784             :                                         previous = 0;                                   // single page
    9785             :                                 }
    9786             :                                 else
    9787             :                                 {
    9788             :                                         previous = skip - (n_rows - 2); // multi-page. 
    9789             :                                         if (previous < 2)
    9790             :                                         {
    9791             :                                                 previous = 0;                           // if only one previous item, just show it
    9792             :                                         }
    9793             :                                 }
    9794             :                                 
    9795             :                                 [gui setKey:[NSString stringWithFormat:@"More:%d", previous] forRow:row];
    9796             :                                 [gui setColor:[gui colorFromSetting:kGuiInterfaceScrollColor defaultValue:[OOColor greenColor]] forRow:row];
    9797             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @" <-- ", nil] forRow:row];
    9798             :                                 row++;
    9799             :                         }
    9800             :                         
    9801             :                         for (i = skip; i < (NSInteger)count && (row - start_row < (OOGUIRow)n_rows); i++)
    9802             :                         {
    9803             :                                 NSString *interfaceKey = [interfaceKeys objectAtIndex:i];
    9804             :                                 OOJSInterfaceDefinition *definition = [interfaces objectForKey:interfaceKey];
    9805             : 
    9806             :                                 [gui setColor:[gui colorFromSetting:kGuiInterfaceEntryColor defaultValue:nil] forRow:row];
    9807             :                                 [gui setKey:interfaceKey forRow:row];
    9808             :                                 [gui setArray:[NSArray arrayWithObjects:[definition title],[definition category], nil] forRow:row];
    9809             : 
    9810             :                                 row++;
    9811             :                         }
    9812             : 
    9813             :                         if (i < (NSInteger)count)
    9814             :                         {
    9815             :                                 // just overwrite the last item :-)
    9816             :                                 [gui setColor:[gui colorFromSetting:kGuiInterfaceScrollColor defaultValue:[OOColor greenColor]] forRow:row - 1];
    9817             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @" --> ", nil] forRow:row - 1];
    9818             :                                 [gui setKey:[NSString stringWithFormat:@"More:%d", i - 1] forRow:row - 1];
    9819             :                         }
    9820             :                         
    9821             :                         [gui setSelectableRange:NSMakeRange(start_row,row - start_row)];
    9822             : 
    9823             :                         if ([gui selectedRow] != start_row)
    9824             :                         {
    9825             :                                 [gui setSelectedRow:start_row];
    9826             :                         }
    9827             : 
    9828             :                         [self showInformationForSelectedInterface];
    9829             :                 }
    9830             :                 else
    9831             :                 {
    9832             :                         [gui setText:DESC(@"interfaces-no-interfaces-available-for-use") forRow:GUI_ROW_NO_INTERFACES align:GUI_ALIGN_LEFT];
    9833             :                         [gui setColor:[gui colorFromSetting:kGuiInterfaceNoneColor defaultValue:[OOColor greenColor]] forRow:GUI_ROW_NO_INTERFACES];
    9834             :                         
    9835             :                         [gui setSelectableRange:NSMakeRange(0,0)];
    9836             :                         [gui setNoSelectedRow];
    9837             : 
    9838             :                 }
    9839             :                 
    9840             :                 [gui setShowTextCursor:NO];
    9841             : 
    9842             :                 NSString *desc = [NSString stringWithFormat:DESC(@"interfaces-for-ship-@-and-station-@"), [self displayName], [[self dockedStation] displayName]];
    9843             :                 [gui setColor:[gui colorFromSetting:kGuiInterfaceHeadingColor defaultValue:nil] forRow:GUI_ROW_INTERFACES_HEADING];
    9844             :                 [gui setText:desc forRow:GUI_ROW_INTERFACES_HEADING];
    9845             : 
    9846             :                 
    9847             :                 if (guiChanged)
    9848             :                 {
    9849             :                         [gui setForegroundTextureKey:@"docked_overlay"];
    9850             :                         NSDictionary *background = [UNIVERSE screenTextureDescriptorForKey:@"interfaces"];
    9851             :                         [gui setBackgroundTextureDescriptor:background];
    9852             :                 }
    9853             :         }
    9854             :         /* ends */
    9855             : 
    9856             : 
    9857             :         [self setShowDemoShips:NO];
    9858             :         
    9859             :         OOGUIScreenID   oldScreen = gui_screen;
    9860             :         gui_screen = GUI_SCREEN_INTERFACES;
    9861             :         [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
    9862             :         
    9863             :         [self setShowDemoShips:NO];
    9864             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
    9865             : 
    9866             : }
    9867             : 
    9868             : 
    9869             : - (void) showInformationForSelectedInterface
    9870             : {
    9871             :         GuiDisplayGen* gui = [UNIVERSE gui];
    9872             :         NSString* interfaceKey = [gui selectedRowKey];
    9873             :         
    9874             :         int i;
    9875             :         
    9876             :         for (i = GUI_ROW_EQUIPMENT_DETAIL; i < GUI_MAX_ROWS; i++)
    9877             :         {
    9878             :                 [gui setText:@"" forRow:i];
    9879             :                 [gui setColor:[gui colorFromSetting:kGuiInterfaceDescriptionColor defaultValue:[OOColor greenColor]] forRow:i];
    9880             :         }
    9881             :         
    9882             :         if (interfaceKey && ![interfaceKey hasPrefix:@"More:"])
    9883             :         {
    9884             :                 NSDictionary *interfaces = [[self dockedStation] localInterfaces];
    9885             :                 OOJSInterfaceDefinition *definition = [interfaces objectForKey:interfaceKey];
    9886             :                 if (definition)
    9887             :                 {
    9888             :                         [gui addLongText:[definition summary] startingAtRow:GUI_ROW_INTERFACES_DETAIL align:GUI_ALIGN_LEFT];
    9889             :                 }
    9890             :         }
    9891             : 
    9892             : }
    9893             : 
    9894             : 
    9895             : - (void) activateSelectedInterface
    9896             : {
    9897             :         GuiDisplayGen* gui = [UNIVERSE gui];
    9898             :         NSString* key = [gui selectedRowKey];
    9899             : 
    9900             :         if ([key hasPrefix:@"More:"])
    9901             :         {
    9902             :                 int             from_item = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
    9903             :                 [self setGuiToInterfacesScreen:from_item];
    9904             : 
    9905             :                 if ([gui selectedRow] < 0)
    9906             :                         [gui setSelectedRow:GUI_ROW_INTERFACES_START];
    9907             :                 if (from_item == 0)
    9908             :                         [gui setSelectedRow:GUI_ROW_INTERFACES_START + GUI_MAX_ROWS_INTERFACES - 1];
    9909             :                 [self showInformationForSelectedInterface];
    9910             : 
    9911             : 
    9912             :                 return;
    9913             :         }
    9914             : 
    9915             :         NSDictionary *interfaces = [[self dockedStation] localInterfaces];
    9916             :         OOJSInterfaceDefinition *definition = [interfaces objectForKey:key];
    9917             :         if (definition)
    9918             :         {
    9919             :                 [[UNIVERSE gameView] clearKeys];
    9920             :                 [definition runCallback:key];
    9921             :         }
    9922             :         else
    9923             :         {
    9924             :                 OOLog(@"interface.missingCallback", @"Unable to find callback definition for key %@", key);
    9925             :         }
    9926             : }
    9927             : 
    9928             : 
    9929             : - (void) setupStartScreenGui
    9930             : {
    9931             :         GuiDisplayGen   *gui = [UNIVERSE gui];
    9932             :         NSString                *text = nil;
    9933             : 
    9934             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
    9935             : 
    9936             :         [gui clear];
    9937             : 
    9938             :         [gui setTitle:@"Oolite"];
    9939             : 
    9940             :         text = DESC(@"game-copyright");
    9941             :         [gui setText:text forRow:15 align:GUI_ALIGN_CENTER];
    9942             :         [gui setColor:[OOColor whiteColor] forRow:15];
    9943             :                 
    9944             :         text = DESC(@"theme-music-credit");
    9945             :         [gui setText:text forRow:17 align:GUI_ALIGN_CENTER];
    9946             :         [gui setColor:[OOColor grayColor] forRow:17];
    9947             :                 
    9948             :         int initialRow = 22;
    9949             :         int row = initialRow;
    9950             : 
    9951             :         text = DESC(@"oolite-start-option-1");
    9952             :         [gui setText:text forRow:row align:GUI_ALIGN_CENTER];
    9953             :         [gui setColor:[OOColor yellowColor] forRow:row];
    9954             :         [gui setKey:[NSString stringWithFormat:@"Start:%d", row] forRow:row];
    9955             : 
    9956             :         ++row;
    9957             : 
    9958             :         text = DESC(@"oolite-start-option-2");
    9959             :         [gui setText:text forRow:row align:GUI_ALIGN_CENTER];
    9960             :         [gui setColor:[OOColor yellowColor] forRow:row];
    9961             :         [gui setKey:[NSString stringWithFormat:@"Start:%d", row] forRow:row];
    9962             : 
    9963             :         ++row;
    9964             : 
    9965             :         text = DESC(@"oolite-start-option-3");
    9966             :         [gui setText:text forRow:row align:GUI_ALIGN_CENTER];
    9967             :         [gui setColor:[OOColor yellowColor] forRow:row];
    9968             :         [gui setKey:[NSString stringWithFormat:@"Start:%d", row] forRow:row];
    9969             : 
    9970             :         ++row;
    9971             : 
    9972             :         text = DESC(@"oolite-start-option-4");
    9973             :         [gui setText:text forRow:row align:GUI_ALIGN_CENTER];
    9974             :         [gui setColor:[OOColor yellowColor] forRow:row];
    9975             :         [gui setKey:[NSString stringWithFormat:@"Start:%d", row] forRow:row];
    9976             : 
    9977             :         ++row;
    9978             : 
    9979             :         text = DESC(@"oolite-start-option-5");
    9980             :         [gui setText:text forRow:row align:GUI_ALIGN_CENTER];
    9981             :         [gui setColor:[OOColor yellowColor] forRow:row];
    9982             :         [gui setKey:[NSString stringWithFormat:@"Start:%d", row] forRow:row];
    9983             : 
    9984             :         ++row;
    9985             : 
    9986             :         text = DESC(@"oolite-start-option-6");
    9987             :         [gui setText:text forRow:row align:GUI_ALIGN_CENTER];
    9988             :         [gui setColor:[OOColor yellowColor] forRow:row];
    9989             :         [gui setKey:[NSString stringWithFormat:@"Start:%d", row] forRow:row];
    9990             : 
    9991             : 
    9992             :         [gui setSelectableRange:NSMakeRange(initialRow, row - initialRow + 1)];
    9993             :         [gui setSelectedRow:initialRow];
    9994             : 
    9995             :         [gui setBackgroundTextureKey:@"intro"];
    9996             : 
    9997             : }
    9998             : 
    9999             : /**
   10000             :  * \ingroup cli
   10001             :  * Scans the command line for -message or -showversion arguments.
   10002             :  */
   10003             : - (void) setGuiToIntroFirstGo:(BOOL)justCobra
   10004             : {
   10005             :         NSString                *text = nil;
   10006             :         GuiDisplayGen   *gui = [UNIVERSE gui];
   10007             :         OOGUIRow                msgLine = 2;
   10008             :         
   10009             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:NO];
   10010             :         [[UNIVERSE gameView] clearMouse];
   10011             :         [[UNIVERSE gameView] clearKeys];
   10012             : 
   10013             : 
   10014             :         if (justCobra)
   10015             :         {
   10016             :                 [UNIVERSE removeDemoShips];
   10017             :                 [[OOCacheManager sharedCache] flush];   // At first startup, a lot of stuff is cached
   10018             :         }
   10019             :         
   10020             :         if (justCobra)
   10021             :         {
   10022             :                 [self setupStartScreenGui];
   10023             :                 
   10024             :                 // check for error messages from Resource Manager
   10025             :                 //[ResourceManager paths]; done in Universe already
   10026             :                 NSString *errors = [ResourceManager errors];
   10027             :                 if (errors != nil)
   10028             :                 {
   10029             :                         OOGUIRow ms_start = msgLine;
   10030             :                         OOGUIRow i = msgLine = [gui addLongText:errors startingAtRow:ms_start align:GUI_ALIGN_LEFT];
   10031             :                         for (i-- ; i >= ms_start ; i--) [gui setColor:[OOColor redColor] forRow:i];
   10032             :                         msgLine++;
   10033             :                 }
   10034             :                 
   10035             :                 // check for messages from OXPs
   10036             :                 NSArray *OXPsWithMessages = [ResourceManager OXPsWithMessagesFound];
   10037             :                 if ([OXPsWithMessages count] > 0)
   10038             :                 {
   10039             :                         NSString *messageToDisplay = @"";
   10040             :                         
   10041             :                         // Show which OXPs were found with messages, but don't spam the screen if more than
   10042             :                         // a certain number of them exist
   10043             :                         if ([OXPsWithMessages count] < 5)
   10044             :                         {
   10045             :                                 NSString *messageSourceList = [OXPsWithMessages componentsJoinedByString:@", "];
   10046             :                                 messageToDisplay = OOExpandKey(@"oxp-containing-messages-list", messageSourceList);
   10047             :                         } else {
   10048             :                                 messageToDisplay = OOExpandKey(@"oxp-containing-messages-found");
   10049             :                         }
   10050             :                         
   10051             :                         OOGUIRow ms_start = msgLine;
   10052             :                         OOGUIRow i = msgLine = [gui addLongText:messageToDisplay startingAtRow:ms_start align:GUI_ALIGN_LEFT];
   10053             :                         for (i--; i >= ms_start; i--)
   10054             :                         {
   10055             :                                 [gui setColor:[OOColor orangeColor] forRow:i];
   10056             :                         }
   10057             :                         msgLine++;
   10058             :                 }
   10059             :                 
   10060             :                 // check for messages from the command line
   10061             :                 NSArray* arguments = [[NSProcessInfo processInfo] arguments];
   10062             :                 unsigned i;
   10063             :                 for (i = 0; i < [arguments count]; i++)
   10064             :                 {
   10065             :                         if (([[arguments objectAtIndex:i] isEqual:@"-message"])&&(i < [arguments count] - 1))
   10066             :                         {
   10067             :                                 OOGUIRow ms_start = msgLine;
   10068             :                                 NSString* message = [arguments oo_stringAtIndex:i + 1];
   10069             :                                 OOGUIRow i = msgLine = [gui addLongText:message startingAtRow:ms_start align:GUI_ALIGN_CENTER];
   10070             :                                 for (i-- ; i >= ms_start; i--)
   10071             :                                 {
   10072             :                                         [gui setColor:[OOColor magentaColor] forRow:i];
   10073             :                                 }
   10074             :                         }
   10075             :                         if ([[arguments objectAtIndex:i] isEqual:@"-showversion"])
   10076             :                         {
   10077             :                                 OOGUIRow ms_start = msgLine;
   10078             :                                 NSString *version = [NSString stringWithFormat:@"Version %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];
   10079             :                                 OOGUIRow i = msgLine = [gui addLongText:version startingAtRow:ms_start align:GUI_ALIGN_CENTER];
   10080             :                                 for (i-- ; i >= ms_start; i--)
   10081             :                                 {
   10082             :                                         [gui setColor:[OOColor magentaColor] forRow:i];
   10083             :                                 }
   10084             :                         }
   10085             :                 }
   10086             :         }
   10087             :         else
   10088             :         {
   10089             :                 [gui clear];
   10090             : 
   10091             :         text = DESC(@"oolite-ship-library-title");
   10092             :                 [gui setTitle:text];
   10093             : 
   10094             :         text = DESC(@"oolite-ship-library-exit");
   10095             :         [gui setText:text forRow:27 align:GUI_ALIGN_CENTER];
   10096             :         [gui setColor:[OOColor yellowColor] forRow:27];
   10097             :         }
   10098             :         
   10099             :         [gui setShowTextCursor:NO];
   10100             :         
   10101             :         [UNIVERSE setupIntroFirstGo: justCobra];
   10102             :         
   10103             :         if (gui != nil)  
   10104             :         {
   10105             :                 gui_screen = justCobra ? GUI_SCREEN_INTRO1 : GUI_SCREEN_SHIPLIBRARY;
   10106             :         }
   10107             :         if ([self status] == STATUS_START_GAME)
   10108             :         {
   10109             :                 [[OOMusicController sharedController] playThemeMusic];
   10110             :         }
   10111             :         
   10112             :         [self setShowDemoShips:YES];
   10113             :         if (justCobra)
   10114             :         {
   10115             :                 [gui setBackgroundTextureKey:@"intro"];
   10116             :         }
   10117             :         else
   10118             :         {
   10119             :                 [gui setBackgroundTextureKey:@"shiplibrary"];
   10120             :         }
   10121             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
   10122             : }
   10123             : 
   10124             : 
   10125             : 
   10126             : - (void) setGuiToOXZManager
   10127             : {
   10128             : 
   10129             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:NO];
   10130             :         [[UNIVERSE gameView] clearMouse];
   10131             :         [UNIVERSE removeDemoShips];
   10132             : 
   10133             :         gui_screen = GUI_SCREEN_OXZMANAGER;
   10134             : 
   10135             :         [[UNIVERSE gui] clearAndKeepBackground:NO];
   10136             : 
   10137             :         [[OOOXZManager sharedManager] gui];
   10138             :         
   10139             :         [[OOMusicController sharedController] playThemeMusic];
   10140             :         [[UNIVERSE gui] setBackgroundTextureKey:@"oxz-manager"];
   10141             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
   10142             : }
   10143             : 
   10144             : 
   10145             : 
   10146             : 
   10147             : 
   10148             : - (void) noteGUIWillChangeTo:(OOGUIScreenID)toScreen
   10149             : {
   10150             :         JSContext *context = OOJSAcquireContext();
   10151             :         ShipScriptEvent(context, self, "guiScreenWillChange", OOJSValueFromGUIScreenID(context, toScreen), OOJSValueFromGUIScreenID(context, gui_screen));
   10152             :         OOJSRelinquishContext(context);
   10153             : }
   10154             : 
   10155             : 
   10156             : - (void) noteGUIDidChangeFrom:(OOGUIScreenID)fromScreen to:(OOGUIScreenID)toScreen
   10157             : {
   10158             :         [self noteGUIDidChangeFrom: fromScreen to: toScreen refresh: NO];
   10159             : }
   10160             : 
   10161             : 
   10162             : - (void) noteGUIDidChangeFrom:(OOGUIScreenID)fromScreen to:(OOGUIScreenID)toScreen refresh: (BOOL) refresh
   10163             : {
   10164             :         // No events triggered if we're changing screens while paused, or if screen never actually changed.
   10165             :         if (fromScreen != toScreen || refresh)
   10166             :         {
   10167             :                 // MKW - release GUI Screen ship, if we have one
   10168             :                 switch (fromScreen)
   10169             :                 {
   10170             :                         case GUI_SCREEN_SHIPYARD:
   10171             :                         case GUI_SCREEN_LOAD:
   10172             :                         case GUI_SCREEN_SAVE:
   10173             :                                 [demoShip release];
   10174             :                                 demoShip = nil;
   10175             :                                 break;
   10176             :                         default:
   10177             :                                 // Nothing
   10178             :                                 break;
   10179             : 
   10180             :                 }
   10181             :                 
   10182             :                 if (toScreen == GUI_SCREEN_SYSTEM_DATA)
   10183             :                 {
   10184             :                         // system data screen: ensure correct sun light color is used on miniature planet
   10185             :                         [[UNIVERSE sun] setSunColor:[OOColor colorWithDescription:[[UNIVERSE systemManager] getProperty:@"sun_color" forSystem:info_system_id inGalaxy:[self galaxyNumber]]]];
   10186             :                 }
   10187             :                 else
   10188             :                 {
   10189             :                         // any other screen: reset local sun light color
   10190             :                         [[UNIVERSE sun] setSunColor:[OOColor colorWithDescription:[[UNIVERSE systemManager] getProperty:@"sun_color" forSystem:system_id inGalaxy:[self galaxyNumber]]]];
   10191             :                 }
   10192             :                 
   10193             :                 if (![[UNIVERSE gameController] isGamePaused])
   10194             :                 {
   10195             :                         JSContext *context = OOJSAcquireContext();
   10196             :                         ShipScriptEvent(context, self, "guiScreenChanged", OOJSValueFromGUIScreenID(context, toScreen), OOJSValueFromGUIScreenID(context, fromScreen));
   10197             :                         OOJSRelinquishContext(context);
   10198             :                 }
   10199             :         }
   10200             : }
   10201             : 
   10202             : 
   10203             : - (void) noteViewDidChangeFrom:(OOViewID)fromView toView:(OOViewID)toView
   10204             : {
   10205             :         [self noteSwitchToView:toView fromView:fromView];
   10206             : }
   10207             : 
   10208             : 
   10209             : - (void) buySelectedItem
   10210             : {
   10211             :         GuiDisplayGen* gui = [UNIVERSE gui];
   10212             :         NSString* key = [gui selectedRowKey];
   10213             : 
   10214             :         if ([key hasPrefix:@"More:"])
   10215             :         {
   10216             :                 int             from_item = [[key componentsSeparatedByString:@":"] oo_intAtIndex:1];
   10217             :                 NSString        *weaponKey = [[key componentsSeparatedByString:@":"] oo_stringAtIndex:2];
   10218             : 
   10219             :                 [self setGuiToEquipShipScreen:from_item];
   10220             :                 if (weaponKey != nil)
   10221             :                 {
   10222             :                         [self highlightEquipShipScreenKey:weaponKey];
   10223             :                 }
   10224             :                 else
   10225             :                 {
   10226             :                         if ([gui selectedRow] < 0)
   10227             :                                 [gui setSelectedRow:GUI_ROW_EQUIPMENT_START];
   10228             :                         if (from_item == 0)
   10229             :                                 [gui setSelectedRow:GUI_ROW_EQUIPMENT_START + GUI_MAX_ROWS_EQUIPMENT - 1];
   10230             :                         [self showInformationForSelectedUpgrade];
   10231             :                 }
   10232             : 
   10233             :                 return;
   10234             :         }
   10235             :         
   10236             :         NSString                *itemText = [gui selectedRowText];
   10237             :         
   10238             :         // FIXME: this is nuts, should be associating lines with keys in some sensible way. --Ahruman 20080311
   10239             :         if ([itemText isEqual:FORWARD_FACING_STRING])
   10240             :                 chosen_weapon_facing = WEAPON_FACING_FORWARD;
   10241             :         if ([itemText isEqual:AFT_FACING_STRING])
   10242             :                 chosen_weapon_facing = WEAPON_FACING_AFT;
   10243             :         if ([itemText isEqual:PORT_FACING_STRING])
   10244             :                 chosen_weapon_facing = WEAPON_FACING_PORT;
   10245             :         if ([itemText isEqual:STARBOARD_FACING_STRING])
   10246             :                 chosen_weapon_facing = WEAPON_FACING_STARBOARD;
   10247             :         
   10248             :         OOCreditsQuantity old_credits = credits;
   10249             :         OOEquipmentType *eqInfo = [OOEquipmentType equipmentTypeWithIdentifier:key];
   10250             :         BOOL isRepair = [self hasEquipmentItem:[eqInfo damagedIdentifier]];
   10251             :         if ([self tryBuyingItem:key])
   10252             :         {
   10253             :                 if (credits == old_credits)
   10254             :                 {
   10255             :                         // laser pre-purchase, or free equipment
   10256             :                         [self playMenuNavigationDown];
   10257             :                 }
   10258             :                 else
   10259             :                 {
   10260             :                         [self playBuyCommodity];
   10261             :                 }                       
   10262             :                         
   10263             :                 if(credits != old_credits || ![key hasPrefix:@"EQ_WEAPON_"])
   10264             :                 {
   10265             :                         // adjust time before playerBoughtEquipment gets to change credits dynamically
   10266             :                         // wind the clock forward by 10 minutes plus 10 minutes for every 60 credits spent
   10267             :                         NSUInteger adjust = 0;
   10268             :                         if (isRepair)
   10269             :                         {
   10270             :                                 adjust = [eqInfo repairTime];
   10271             :                         }
   10272             :                         else
   10273             :                         {
   10274             :                                 adjust = [eqInfo installTime];
   10275             :                         }
   10276             :                         double time_adjust = (old_credits > credits) ? (old_credits - credits) : 0.0;
   10277             :                         [UNIVERSE forceWitchspaceEntries];
   10278             :                         if (adjust == 0)
   10279             :                         {
   10280             :                                 ship_clock_adjust += time_adjust + 600.0;
   10281             :                         }
   10282             :                         else
   10283             :                         {
   10284             :                                 ship_clock_adjust += (double)adjust;
   10285             :                         }
   10286             :                         
   10287             :                         [self doScriptEvent:OOJSID("playerBoughtEquipment") withArguments:[NSArray arrayWithObjects:key, [NSNumber numberWithLongLong:(old_credits - credits)], nil]];
   10288             :                         if (gui_screen == GUI_SCREEN_EQUIP_SHIP) //if we haven't changed gui screen inside playerBoughtEquipment
   10289             :                         { 
   10290             :                                 // show any change due to playerBoughtEquipment
   10291             :                                 [self setGuiToEquipShipScreen:0];
   10292             :                                 // then try to go back where we were
   10293             :                                 [self highlightEquipShipScreenKey:key];
   10294             :                         }
   10295             : 
   10296             :                         if ([UNIVERSE autoSave]) [UNIVERSE setAutoSaveNow:YES];
   10297             :                 }
   10298             :         }
   10299             :         else
   10300             :         {
   10301             :                 [self playCantBuyCommodity];
   10302             :         }
   10303             : }
   10304             : 
   10305             : 
   10306             : - (OOCreditsQuantity) adjustPriceByScriptForEqKey:(NSString *)eqKey withCurrent:(OOCreditsQuantity)price
   10307             : {
   10308             :         NSString *condition_script = [[OOEquipmentType equipmentTypeWithIdentifier:eqKey] conditionScript];
   10309             :         if (condition_script != nil)
   10310             :         {
   10311             :                 OOJSScript *condScript = [UNIVERSE getConditionScript:condition_script];
   10312             :                 if (condScript != nil) // should always be non-nil, but just in case
   10313             :                 {
   10314             :                         JSContext                       *JScontext = OOJSAcquireContext();
   10315             :                         BOOL OK;
   10316             :                         jsval result;
   10317             :                         int32 newPrice;
   10318             :                         jsval args[] = { OOJSValueFromNativeObject(JScontext, eqKey) , JSVAL_NULL };
   10319             :                         OK = JS_NewNumberValue(JScontext, price, &args[1]);
   10320             :                                 
   10321             :                         if (OK)
   10322             :                         {
   10323             :                                 OK = [condScript callMethod:OOJSID("updateEquipmentPrice")
   10324             :                                                                   inContext:JScontext
   10325             :                                                           withArguments:args count:sizeof args / sizeof *args
   10326             :                                                                          result:&result];
   10327             :                         }
   10328             : 
   10329             :                         if (OK)
   10330             :                         {
   10331             :                                 OK = JS_ValueToInt32(JScontext, result, &newPrice);
   10332             :                                 if (OK && newPrice >= 0)
   10333             :                                 {
   10334             :                                         price = (OOCreditsQuantity)newPrice;
   10335             :                                 }
   10336             :                         }
   10337             :                         OOJSRelinquishContext(JScontext);
   10338             :                 }
   10339             :         }
   10340             :         return price;
   10341             : }
   10342             : 
   10343             : 
   10344           0 : - (BOOL) tryBuyingItem:(NSString *)eqKey
   10345             : {
   10346             :         // note this doesn't check the availability by tech-level
   10347             :         OOEquipmentType                 *eqType                 = [OOEquipmentType equipmentTypeWithIdentifier:eqKey];
   10348             :         OOCreditsQuantity               pricePerUnit    = [eqType price];
   10349             :         NSString                                *eqKeyDamaged   = [eqType damagedIdentifier];
   10350             :         double                                  price                   = pricePerUnit;
   10351             :         double                                  priceFactor             = 1.0;
   10352             :         OOCreditsQuantity               tradeIn                 = 0;
   10353             :         BOOL    isRepair = NO;
   10354             :         
   10355             :         // repairs cost 50%
   10356             :         if ([self hasEquipmentItem:eqKeyDamaged])
   10357             :         {
   10358             :                 price /= 2.0;
   10359             :                 isRepair = YES;
   10360             :         }
   10361             :         
   10362             :         if ([eqKey isEqualToString:@"EQ_RENOVATION"])
   10363             :         {
   10364             :                 price = [self renovationCosts];
   10365             :         }
   10366             :         
   10367             :         price = [self adjustPriceByScriptForEqKey:eqKey withCurrent:price];
   10368             : 
   10369             :         StationEntity *dockedStation = [self dockedStation];
   10370             :         if (dockedStation)
   10371             :         {
   10372             :                 priceFactor = [dockedStation equipmentPriceFactor];
   10373             :         }
   10374             :         
   10375             :         price *= priceFactor;  // increased prices at some stations
   10376             :         
   10377             :         if (price > credits)
   10378             :         {
   10379             :                 return NO;
   10380             :         }
   10381             :         
   10382             :         if ([eqType isPrimaryWeapon])
   10383             :         {
   10384             :                 if (chosen_weapon_facing == WEAPON_FACING_NONE)
   10385             :                 {
   10386             :                         [self setGuiToEquipShipScreen:0 selectingFacingFor:eqKey];      // reset
   10387             :                         return YES;
   10388             :                 }
   10389             :                 
   10390             :                 OOWeaponType chosen_weapon = OOWeaponTypeFromEquipmentIdentifierStrict(eqKey);
   10391             :                 OOWeaponType current_weapon = nil;
   10392             : 
   10393             :                 NSUInteger multiplier = 1;
   10394             :                 
   10395             :                 switch (chosen_weapon_facing)
   10396             :                 {
   10397             :                         case WEAPON_FACING_FORWARD:
   10398             :                                 current_weapon = forward_weapon_type;
   10399             :                                 forward_weapon_type = chosen_weapon;
   10400             :                                 if (_multiplyWeapons)
   10401             :                                 {
   10402             :                                         multiplier = [forwardWeaponOffset count];
   10403             :                                 }
   10404             :                                 break;
   10405             :                                 
   10406             :                         case WEAPON_FACING_AFT:
   10407             :                                 current_weapon = aft_weapon_type;
   10408             :                                 aft_weapon_type = chosen_weapon;
   10409             :                                 if (_multiplyWeapons)
   10410             :                                 {
   10411             :                                         multiplier = [aftWeaponOffset count];
   10412             :                                 }
   10413             :                                 break;
   10414             :                                 
   10415             :                         case WEAPON_FACING_PORT:
   10416             :                                 current_weapon = port_weapon_type;
   10417             :                                 port_weapon_type = chosen_weapon;
   10418             :                                 if (_multiplyWeapons)
   10419             :                                 {
   10420             :                                         multiplier = [portWeaponOffset count];
   10421             :                                 }
   10422             :                                 break;
   10423             :                                 
   10424             :                         case WEAPON_FACING_STARBOARD:
   10425             :                                 current_weapon = starboard_weapon_type;
   10426             :                                 starboard_weapon_type = chosen_weapon;
   10427             :                                 if (_multiplyWeapons)
   10428             :                                 {
   10429             :                                         multiplier = [starboardWeaponOffset count];
   10430             :                                 }
   10431             :                                 break;
   10432             :                                 
   10433             :                         case WEAPON_FACING_NONE:
   10434             :                                 break;
   10435             :                 }
   10436             : 
   10437             :                 price *= multiplier;
   10438             :                 
   10439             :                 if (price > credits)
   10440             :                 {
   10441             :                         // not enough money - ensure that weapon
   10442             :                         // type is reset to what it was before
   10443             :                         // the attempt to buy took place
   10444             :                         switch (chosen_weapon_facing)
   10445             :                         {
   10446             :                                 case WEAPON_FACING_FORWARD:
   10447             :                                         forward_weapon_type = current_weapon;
   10448             :                                         break;
   10449             :                                 case WEAPON_FACING_AFT:
   10450             :                                         aft_weapon_type = current_weapon;
   10451             :                                         break;
   10452             :                                 case WEAPON_FACING_PORT:
   10453             :                                         port_weapon_type = current_weapon;
   10454             :                                         break;
   10455             :                                 case WEAPON_FACING_STARBOARD:
   10456             :                                         starboard_weapon_type = current_weapon;
   10457             :                                         break;
   10458             :                                 case WEAPON_FACING_NONE:
   10459             :                                         break;
   10460             :                         }
   10461             :                         return NO;
   10462             :                 }
   10463             :                 credits -= price;
   10464             :                 
   10465             :                 // Refund current_weapon
   10466             :                 if (current_weapon != nil)
   10467             :                 {
   10468             :                         tradeIn = [UNIVERSE getEquipmentPriceForKey:OOEquipmentIdentifierFromWeaponType(current_weapon)] * multiplier;
   10469             :                 }
   10470             :                 
   10471             :                 [self doTradeIn:tradeIn forPriceFactor:priceFactor];
   10472             :                 // If equipped, remove damaged weapon after repairs. -- But there's no way we should get a damaged weapon. Ever.
   10473             :                 [self removeEquipmentItem:eqKeyDamaged];
   10474             :                 return YES;
   10475             :         }
   10476             :         
   10477             :         if ([eqType isMissileOrMine] && missiles >= max_missiles)
   10478             :         {
   10479             :                 OOLog(@"equip.buy.mounted.failed.full", @"%@", @"rejecting missile because already full");
   10480             :                 return NO;
   10481             :         }
   10482             :         
   10483             :         // NSFO!
   10484             :         //unsigned      passenger_space = [[OOEquipmentType equipmentTypeWithIdentifier:@"EQ_PASSENGER_BERTH"] requiredCargoSpace];
   10485             :         //if (passenger_space == 0) passenger_space = PASSENGER_BERTH_SPACE;
   10486             :         
   10487             :         if ([eqKey isEqualToString:@"EQ_PASSENGER_BERTH"] && [self availableCargoSpace] < PASSENGER_BERTH_SPACE)
   10488             :         {
   10489             :                 return NO;
   10490             :         }
   10491             :         
   10492             :         if ([eqKey isEqualToString:@"EQ_FUEL"])
   10493             :         {
   10494             : #if MASS_DEPENDENT_FUEL_PRICES
   10495             :                 OOCreditsQuantity creditsForRefuel = ([self fuelCapacity] - [self fuel]) * pricePerUnit * [self fuelChargeRate];
   10496             : #else
   10497             :                 OOCreditsQuantity creditsForRefuel = ([self fuelCapacity] - [self fuel]) * pricePerUnit;
   10498             : #endif
   10499             :                 if (credits >= creditsForRefuel)     // Ensure we don't overflow
   10500             :                 {
   10501             :                         credits -= creditsForRefuel;
   10502             :                         fuel = [self fuelCapacity];
   10503             :                         return YES;
   10504             :                 }
   10505             :                 else
   10506             :                 {
   10507             :                         return NO;
   10508             :                 }
   10509             :         }
   10510             :         
   10511             :         // check energy unit replacement
   10512             :         if ([eqKey hasSuffix:@"ENERGY_UNIT"] && [self energyUnitType] != ENERGY_UNIT_NONE)
   10513             :         {
   10514             :                 switch ([self energyUnitType])
   10515             :                 {
   10516             :                         case ENERGY_UNIT_NAVAL :
   10517             :                                 [self removeEquipmentItem:@"EQ_NAVAL_ENERGY_UNIT"];
   10518             :                                 tradeIn = [UNIVERSE getEquipmentPriceForKey:@"EQ_NAVAL_ENERGY_UNIT"] / 2;     // 50 % refund
   10519             :                                 break;
   10520             :                         case ENERGY_UNIT_NAVAL_DAMAGED :
   10521             :                                 [self removeEquipmentItem:@"EQ_NAVAL_ENERGY_UNIT_DAMAGED"];
   10522             :                                 tradeIn = [UNIVERSE getEquipmentPriceForKey:@"EQ_NAVAL_ENERGY_UNIT"] / 4;     // half of the working one
   10523             :                                 break;
   10524             :                         case ENERGY_UNIT_NORMAL :
   10525             :                                 [self removeEquipmentItem:@"EQ_ENERGY_UNIT"];
   10526             :                                 tradeIn = [UNIVERSE getEquipmentPriceForKey:@"EQ_ENERGY_UNIT"] * 3 / 4;               // 75 % refund
   10527             :                                 break;
   10528             :                         case ENERGY_UNIT_NORMAL_DAMAGED :
   10529             :                                 [self removeEquipmentItem:@"EQ_ENERGY_UNIT_DAMAGED"];
   10530             :                                 tradeIn = [UNIVERSE getEquipmentPriceForKey:@"EQ_ENERGY_UNIT"] * 3 / 8;               // half of the working one
   10531             :                                 break;
   10532             : 
   10533             :                         default:
   10534             :                                 break;
   10535             :                 }
   10536             :                 [self doTradeIn:tradeIn forPriceFactor:priceFactor];
   10537             :         }
   10538             :         
   10539             :         // maintain ship
   10540             :         if ([eqKey isEqualToString:@"EQ_RENOVATION"])
   10541             :         {
   10542             :                 OOTechLevelID techLevel = NSNotFound;
   10543             :                 if (dockedStation != nil)  techLevel = [dockedStation equivalentTechLevel];
   10544             :                 if (techLevel == NSNotFound)  techLevel = [[UNIVERSE currentSystemData] oo_unsignedIntForKey:KEY_TECHLEVEL];
   10545             :                 
   10546             :                 credits -= price;
   10547             :                 ship_trade_in_factor += 5 + techLevel;  // you get better value at high-tech repair bases
   10548             :                 if (ship_trade_in_factor > 100) ship_trade_in_factor = 100;
   10549             :                 
   10550             :                 [self clearSubEntities];
   10551             :                 [self setUpSubEntities];
   10552             :                 
   10553             :                 return YES;
   10554             :         }
   10555             :         
   10556             :         if ([eqKey hasSuffix:@"MISSILE"] || [eqKey hasSuffix:@"MINE"])
   10557             :         {
   10558             :                 ShipEntity* weapon = [[UNIVERSE newShipWithRole:eqKey] autorelease];
   10559             :                 if (weapon)  OOLog(kOOLogBuyMountedOK, @"Got ship for mounted weapon role %@", eqKey);
   10560             :                 else  OOLog(kOOLogBuyMountedFailed, @"Could not find ship for mounted weapon role %@", eqKey);
   10561             :                 
   10562             :                 BOOL mounted_okay = [self mountMissile:weapon];
   10563             :                 if (mounted_okay)
   10564             :                 {
   10565             :                         credits -= price;
   10566             :                         [self safeAllMissiles];
   10567             :                         [self tidyMissilePylons];
   10568             :                         [self setActiveMissile:0];
   10569             :                 }
   10570             :                 return mounted_okay;
   10571             :         }
   10572             :         
   10573             :         if ([eqKey isEqualToString:@"EQ_PASSENGER_BERTH"])
   10574             :         {
   10575             :                 [self changePassengerBerths:+1];
   10576             :                 credits -= price;
   10577             :                 return YES;
   10578             :         }
   10579             :         
   10580             :         if ([eqKey isEqualToString:@"EQ_PASSENGER_BERTH_REMOVAL"])
   10581             :         {
   10582             :                 [self changePassengerBerths:-1];
   10583             :                 credits -= price;
   10584             :                 return YES;
   10585             :         }
   10586             :         
   10587             :         if ([eqKey isEqualToString:@"EQ_MISSILE_REMOVAL"])
   10588             :         {
   10589             :                 credits -= price;
   10590             :                 tradeIn += [self removeMissiles];
   10591             :                 [self doTradeIn:tradeIn forPriceFactor:priceFactor];
   10592             :                 return YES;
   10593             :         }
   10594             :         
   10595             :         if ([self canAddEquipment:eqKey inContext:@"purchase"])
   10596             :         {
   10597             :                 credits -= price;
   10598             :                 [self addEquipmentItem:eqKey withValidation:NO inContext:@"purchase"]; // no need to validate twice.
   10599             :                 if (isRepair)
   10600             :                 {
   10601             :                         [self doScriptEvent:OOJSID("equipmentRepaired") withArgument:eqKey];
   10602             :                 }
   10603             :                 return YES;
   10604             :         }
   10605             :         
   10606             :         return NO;
   10607             : }
   10608             : 
   10609             : 
   10610             : - (BOOL) setWeaponMount:(OOWeaponFacing)facing toWeapon:(NSString *)eqKey
   10611             : {
   10612             :         return [self setWeaponMount:facing toWeapon:eqKey inContext:@"purchase"];
   10613             : }
   10614             : 
   10615             : 
   10616             : - (BOOL) setWeaponMount:(OOWeaponFacing)facing toWeapon:(NSString *)eqKey inContext:(NSString *) context
   10617             : {
   10618             : 
   10619             :         NSDictionary            *shipyardInfo = [[OOShipRegistry sharedRegistry] shipyardInfoForKey:[self shipDataKey]];
   10620             :         unsigned                        available_facings = [shipyardInfo oo_unsignedIntForKey:KEY_WEAPON_FACINGS defaultValue:[self weaponFacings]];   // use defaults  explicitly
   10621             :         
   10622             :         // facing exists?
   10623             :         if (!(available_facings & facing)) 
   10624             :         {
   10625             :                 return NO;
   10626             :         }
   10627             :         
   10628             :         // weapon allowed (or NONE)?
   10629             :         if (![eqKey isEqualToString:@"EQ_WEAPON_NONE"]) 
   10630             :         {
   10631             :                 if (![self canAddEquipment:eqKey inContext:context]) 
   10632             :                 {
   10633             :                         return NO;
   10634             :                 }
   10635             :         }
   10636             :         
   10637             :         // sets WEAPON_NONE if not recognised
   10638             :         OOWeaponType chosen_weapon = OOWeaponTypeFromEquipmentIdentifierStrict(eqKey);
   10639             :         
   10640             :         switch (facing)
   10641             :         {
   10642             :                 case WEAPON_FACING_FORWARD:
   10643             :                         forward_weapon_type = chosen_weapon;
   10644             :                         break;
   10645             :                         
   10646             :                 case WEAPON_FACING_AFT:
   10647             :                         aft_weapon_type = chosen_weapon;
   10648             :                         break;
   10649             :                         
   10650             :                 case WEAPON_FACING_PORT:
   10651             :                         port_weapon_type = chosen_weapon;
   10652             :                         break;
   10653             :                         
   10654             :                 case WEAPON_FACING_STARBOARD:
   10655             :                         starboard_weapon_type = chosen_weapon;
   10656             :                         break;
   10657             :                         
   10658             :                 case WEAPON_FACING_NONE:
   10659             :                         break;
   10660             :         }
   10661             :         
   10662             :         return YES;
   10663             : }
   10664             : 
   10665             : 
   10666             : - (BOOL) changePassengerBerths:(int) addRemove
   10667             : {
   10668             :         if (addRemove == 0) return NO;
   10669             :         addRemove = (addRemove > 0) ? 1 : -1;        // change only by one berth at a time!
   10670             :         // NSFO!
   10671             :         //unsigned      passenger_space = [[OOEquipmentType equipmentTypeWithIdentifier:@"EQ_PASSENGER_BERTH"] requiredCargoSpace];
   10672             :         //if (passenger_space == 0) passenger_space = PASSENGER_BERTH_SPACE;
   10673             :         if ((max_passengers < 1 && addRemove == -1) || ([self maxAvailableCargoSpace] - current_cargo < PASSENGER_BERTH_SPACE && addRemove == 1)) return NO;
   10674             :         max_passengers += addRemove;
   10675             :         max_cargo -= PASSENGER_BERTH_SPACE * addRemove;
   10676             :         return YES;
   10677             : }
   10678             : 
   10679             : 
   10680           0 : - (OOCreditsQuantity) removeMissiles
   10681             : {
   10682             :         [self safeAllMissiles];
   10683             :         OOCreditsQuantity tradeIn = 0;
   10684             :         unsigned i;
   10685             :         for (i = 0; i < missiles; i++)
   10686             :         {
   10687             :                 NSString *weapon_key = [missile_list[i] identifier];
   10688             :                 
   10689             :                 if (weapon_key != nil)
   10690             :                         tradeIn += (int)[UNIVERSE getEquipmentPriceForKey:weapon_key];
   10691             :         }
   10692             :         
   10693             :         for (i = 0; i < max_missiles; i++)
   10694             :         {
   10695             :                 [missile_entity[i] release];
   10696             :                 missile_entity[i] = nil;
   10697             :         }
   10698             :         
   10699             :         missiles = 0;
   10700             :         return tradeIn;
   10701             : }
   10702             : 
   10703             : 
   10704           0 : - (void) doTradeIn:(OOCreditsQuantity)tradeInValue forPriceFactor:(double)priceFactor
   10705             : {
   10706             :         if (tradeInValue != 0)
   10707             :         {
   10708             :                 if (priceFactor < 1.0)  tradeInValue *= priceFactor;
   10709             :                 credits += tradeInValue;
   10710             :         }
   10711             : }
   10712             : 
   10713             : 
   10714             : - (OOCargoQuantity) cargoQuantityForType:(OOCommodityType)type
   10715             : {
   10716             :         OOCargoQuantity         amount = [shipCommodityData quantityForGood:type];
   10717             :         
   10718             :         if  ([self status] != STATUS_DOCKED)
   10719             :         {
   10720             :                 NSInteger               i;
   10721             :                 OOCommodityType co_type;
   10722             :                 ShipEntity              *cargoItem = nil;
   10723             :                 
   10724             :                 for (i = [cargo count] - 1; i >= 0 ; i--)
   10725             :                 {
   10726             :                         cargoItem = [cargo objectAtIndex:i];
   10727             :                         co_type = [cargoItem commodityType];
   10728             :                         if ([co_type isEqualToString:type])
   10729             :                         {
   10730             :                                 amount += [cargoItem commodityAmount];
   10731             :                         }
   10732             :                 }
   10733             :         }
   10734             :         
   10735             :         return amount;
   10736             : }
   10737             : 
   10738             : 
   10739             : - (OOCargoQuantity) setCargoQuantityForType:(OOCommodityType)type amount:(OOCargoQuantity)amount
   10740             : {
   10741             :         OOMassUnit                      unit = [shipCommodityData massUnitForGood:type];
   10742             :         if([self specialCargo] && unit == UNITS_TONS) return 0; // don't do anything if we've got a special cargo...
   10743             :         
   10744             :         OOCargoQuantity         oldAmount = [self cargoQuantityForType:type];
   10745             :         OOCargoQuantity         available = [self availableCargoSpace];
   10746             :         BOOL                            inPods = ([self status] != STATUS_DOCKED);
   10747             :         
   10748             :         // check it against the max amount.
   10749             :         if (unit == UNITS_TONS && (available + oldAmount) < amount)
   10750             :         {
   10751             :                 amount =  available + oldAmount;
   10752             :         }
   10753             :         // if we have 1499 kg the ship registers only 1 ton, so it's possible to exceed the max cargo:
   10754             :         // eg: with maxAvailableCargoSpace 2 & gold 1499kg, you can still add 1 ton alloy. 
   10755             :         else if (unit == UNITS_KILOGRAMS && amount > oldAmount)
   10756             :         {
   10757             :                 // Allow up to 0.5 ton of kg (& g) goods above the cargo capacity but respect existing quantities.
   10758             :                 OOCargoQuantity         safeAmount = available * KILOGRAMS_PER_POD + MAX_KILOGRAMS_IN_SAFE;
   10759             :                 if (safeAmount < amount) amount = (safeAmount < oldAmount) ? oldAmount : safeAmount;
   10760             :         }
   10761             :         else if (unit == UNITS_GRAMS && amount > oldAmount)
   10762             :         {
   10763             :                 OOCargoQuantity         safeAmount = available * GRAMS_PER_POD + MAX_GRAMS_IN_SAFE;
   10764             :                 if (safeAmount < amount) amount = (safeAmount < oldAmount) ? oldAmount : safeAmount;
   10765             :         }
   10766             :         
   10767             :         if (inPods)
   10768             :         {
   10769             :                 if (amount > oldAmount) // increase
   10770             :                 {
   10771             :                         [self loadCargoPodsForType:type amount:(amount - oldAmount)];
   10772             :                 }
   10773             :                 else
   10774             :                 {
   10775             :                         [self unloadCargoPodsForType:type amount:(oldAmount - amount)];
   10776             :                 }
   10777             :         }
   10778             :         else
   10779             :         {
   10780             :                 [shipCommodityData setQuantity:amount forGood:type];
   10781             :         }
   10782             : 
   10783             :         [self calculateCurrentCargo];
   10784             :         return [shipCommodityData quantityForGood:type];
   10785             : }
   10786             : 
   10787             : 
   10788             : - (void) calculateCurrentCargo
   10789             : {
   10790             :         current_cargo = [self cargoQuantityOnBoard];
   10791             : }
   10792             : 
   10793             : 
   10794           0 : - (OOCargoQuantity) cargoQuantityOnBoard
   10795             : {
   10796             :         if ([self specialCargo] != nil)
   10797             :         {
   10798             :                 return [self maxAvailableCargoSpace];
   10799             :         }       
   10800             :         
   10801             :         /*
   10802             :                 The cargo array is nil when the player ship is docked, due to action in unloadCargopods. For
   10803             :                 this reason, we must use a slightly more complex method to determine the quantity of cargo
   10804             :                 carried in this case - Nikos 20090830
   10805             :                 
   10806             :                 Optimised this method, to compensate for increased usage - Kaks 20091002
   10807             :         */
   10808             :         OOCargoQuantity         cargoQtyOnBoard = 0;
   10809             :         NSString                        *good = nil;
   10810             : 
   10811             :         foreach (good, [shipCommodityData goods])
   10812             :         {
   10813             :                 OOCargoQuantity quantity = [shipCommodityData quantityForGood:good];
   10814             :                 
   10815             :                 OOMassUnit commodityUnits = [shipCommodityData massUnitForGood:good];
   10816             :                 
   10817             :                 if (commodityUnits != UNITS_TONS)
   10818             :                 {
   10819             :                         // calculate the number of pods that would be used
   10820             :                         // we're using integer math, so 99/100 = 0 ,  100/100 = 1, etc...
   10821             :                         
   10822             :                         assert(KILOGRAMS_PER_POD > MAX_KILOGRAMS_IN_SAFE && GRAMS_PER_POD > MAX_GRAMS_IN_SAFE); // otherwise we're in trouble!
   10823             :                         
   10824             :                         if (commodityUnits == UNITS_KILOGRAMS)  quantity = ((KILOGRAMS_PER_POD - MAX_KILOGRAMS_IN_SAFE - 1) + quantity) / KILOGRAMS_PER_POD;
   10825             :                         else  quantity = ((GRAMS_PER_POD - MAX_GRAMS_IN_SAFE - 1) + quantity) / GRAMS_PER_POD;
   10826             :                 }
   10827             :                 cargoQtyOnBoard += quantity;
   10828             :         }
   10829             :         cargoQtyOnBoard += [[self cargo] count];
   10830             :         
   10831             :         return cargoQtyOnBoard;
   10832             : }
   10833             : 
   10834             : 
   10835             : - (OOCommodityMarket *) localMarket
   10836             : {
   10837             :         StationEntity *station = [self dockedStation];
   10838             :         if (station == nil)  
   10839             :         {
   10840             :                 if ([[self primaryTarget] isStation] && [(StationEntity *)[self primaryTarget] marketBroadcast])
   10841             :                 {
   10842             :                         station = [self primaryTarget];
   10843             :                 }
   10844             :                 else
   10845             :                 {
   10846             :                         station = [UNIVERSE station];
   10847             :                 }
   10848             :                 if (station == nil)
   10849             :                 {
   10850             :                         // interstellar space or similar
   10851             :                         return nil;
   10852             :                 }
   10853             :         }
   10854             :         OOCommodityMarket *localMarket = [station localMarket];
   10855             :         if (localMarket == nil)
   10856             :         {
   10857             :                 localMarket = [station initialiseLocalMarket];
   10858             :         }
   10859             :         
   10860             :         return localMarket;
   10861             : }
   10862             : 
   10863             : 
   10864             : - (NSArray *) applyMarketFilter:(NSArray *)goods onMarket:(OOCommodityMarket *)market
   10865             : {
   10866             :         if (marketFilterMode == MARKET_FILTER_MODE_OFF)
   10867             :         {
   10868             :                 return goods;
   10869             :         }
   10870             :         NSMutableArray  *filteredGoods = [NSMutableArray arrayWithCapacity:[goods count]];
   10871             :         OOCommodityType good = nil;
   10872             :         foreach (good, goods)
   10873             :         {
   10874             :                 switch (marketFilterMode)
   10875             :                 {
   10876             :                 case MARKET_FILTER_MODE_OFF:
   10877             :                         // never reached, but keeps compiler happy
   10878             :                         [filteredGoods addObject:good];
   10879             :                         break;
   10880             :                 case MARKET_FILTER_MODE_TRADE:
   10881             :                         if ([market quantityForGood:good] > 0 || [self cargoQuantityForType:good] > 0)
   10882             :                         {
   10883             :                                 [filteredGoods addObject:good];
   10884             :                         }
   10885             :                         break;
   10886             :                 case MARKET_FILTER_MODE_HOLD:
   10887             :                         if ([self cargoQuantityForType:good] > 0)
   10888             :                         {
   10889             :                                 [filteredGoods addObject:good];
   10890             :                         }
   10891             :                         break;
   10892             :                 case MARKET_FILTER_MODE_STOCK:
   10893             :                         if ([market quantityForGood:good] > 0)
   10894             :                         {
   10895             :                                 [filteredGoods addObject:good];
   10896             :                         }
   10897             :                         break;
   10898             :                 case MARKET_FILTER_MODE_LEGAL:
   10899             :                         if ([market exportLegalityForGood:good] == 0 && [market importLegalityForGood:good] == 0)
   10900             :                         {
   10901             :                                 [filteredGoods addObject:good];
   10902             :                         }
   10903             :                         break;
   10904             :                 case MARKET_FILTER_MODE_RESTRICTED:
   10905             :                         if ([market exportLegalityForGood:good] > 0 || [market importLegalityForGood:good] > 0)
   10906             :                         {
   10907             :                                 [filteredGoods addObject:good];
   10908             :                         }
   10909             :                         break;
   10910             :                 }
   10911             :         }
   10912             :         return [[filteredGoods copy] autorelease];
   10913             : }
   10914             : 
   10915             : 
   10916             : - (NSArray *) applyMarketSorter:(NSArray *)goods onMarket:(OOCommodityMarket *)market
   10917             : {
   10918             :         switch (marketSorterMode)
   10919             :         {
   10920             :         case MARKET_SORTER_MODE_ALPHA:
   10921             :                 return [goods sortedArrayUsingFunction:marketSorterByName context:market];
   10922             :         case MARKET_SORTER_MODE_PRICE:
   10923             :                 return [goods sortedArrayUsingFunction:marketSorterByPrice context:market];
   10924             :         case MARKET_SORTER_MODE_STOCK:
   10925             :                 return [goods sortedArrayUsingFunction:marketSorterByQuantity context:market];
   10926             :         case MARKET_SORTER_MODE_HOLD:
   10927             :                 return [goods sortedArrayUsingFunction:marketSorterByQuantity context:shipCommodityData];
   10928             :         case MARKET_SORTER_MODE_UNIT:
   10929             :                 return [goods sortedArrayUsingFunction:marketSorterByMassUnit context:market];  
   10930             :         case MARKET_SORTER_MODE_OFF:
   10931             :                 // keep default sort order
   10932             :                 break;
   10933             :         }
   10934             :         return goods;
   10935             : }
   10936             : 
   10937             : 
   10938           0 : - (void) showMarketScreenHeaders
   10939             : {
   10940             :         GuiDisplayGen           *gui = [UNIVERSE gui];
   10941             :         OOGUITabSettings tab_stops;
   10942             :         tab_stops[0] = 0;
   10943             :         tab_stops[1] = 137; 
   10944             :         tab_stops[2] = 187;
   10945             :         tab_stops[3] = 267;
   10946             :         tab_stops[4] = 321;
   10947             :         tab_stops[5] = 431;
   10948             :         [gui overrideTabs:tab_stops from:kGuiMarketTabs length:6];
   10949             :         [gui setTabStops:tab_stops];
   10950             :         
   10951             :         [gui setColor:[gui colorFromSetting:kGuiMarketHeadingColor defaultValue:[OOColor greenColor]] forRow:GUI_ROW_MARKET_KEY];
   10952             :         [gui setArray:[NSArray arrayWithObjects: DESC(@"commodity-column-title"), OOPadStringToEms(DESC(@"price-column-title"),3.5),
   10953             :                                                    OOPadStringToEms(DESC(@"for-sale-column-title"),3.75), OOPadStringToEms(DESC(@"in-hold-column-title"),5.75), DESC(@"oolite-legality-column-title"), DESC(@"oolite-extras-column-title"), nil] forRow:GUI_ROW_MARKET_KEY];
   10954             :         [gui setArray:[NSArray arrayWithObjects: DESC(@"commodity-column-title"), DESC(@"oolite-extras-column-title"), OOPadStringToEms(DESC(@"price-column-title"),3.5),
   10955             :                                                    OOPadStringToEms(DESC(@"for-sale-column-title"),3.75), OOPadStringToEms(DESC(@"in-hold-column-title"),5.75), DESC(@"oolite-legality-column-title"), nil] forRow:GUI_ROW_MARKET_KEY];
   10956             : 
   10957             : }
   10958             : 
   10959             : 
   10960           0 : - (void) showMarketScreenDataLine:(OOGUIRow)row forGood:(OOCommodityType)good inMarket:(OOCommodityMarket *)localMarket holdQuantity:(OOCargoQuantity)quantity
   10961             : {
   10962             :         GuiDisplayGen           *gui = [UNIVERSE gui];
   10963             :         NSString* desc = [NSString stringWithFormat:@" %@ ", [shipCommodityData nameForGood:good]];
   10964             :         OOCargoQuantity available_units = [localMarket quantityForGood:good];
   10965             :         OOCargoQuantity units_in_hold = quantity;
   10966             :         OOCreditsQuantity pricePerUnit = [localMarket priceForGood:good];
   10967             :         OOMassUnit unit = [shipCommodityData massUnitForGood:good];
   10968             :                         
   10969             :         NSString *available = OOPadStringToEms(((available_units > 0) ? (NSString *)[NSString stringWithFormat:@"%d",available_units] : DESC(@"commodity-quantity-none")), 2.5);
   10970             : 
   10971             :         NSUInteger priceDecimal = pricePerUnit % 10;
   10972             :         NSString *price = [NSString stringWithFormat:@" %@.%lu ",OOPadStringToEms([NSString stringWithFormat:@"%lu",(unsigned long)(pricePerUnit/10)],2.5),priceDecimal];
   10973             :                         
   10974             :         // this works with up to 9999 tons of gemstones. Any more than that, they deserve the formatting they get! :)
   10975             :                         
   10976             :         NSString *owned = OOPadStringToEms((units_in_hold > 0) ? (NSString *)[NSString stringWithFormat:@"%d",units_in_hold] : DESC(@"commodity-quantity-none"), 4.5);
   10977             :         NSString *units = DisplayStringForMassUnit(unit);
   10978             :         NSString *units_available = [NSString stringWithFormat:@" %@ %@ ",available, units];
   10979             :         NSString *units_owned = [NSString stringWithFormat:@" %@ %@ ",owned, units];
   10980             : 
   10981             :         NSUInteger import_legality = [localMarket importLegalityForGood:good];
   10982             :         NSUInteger export_legality = [localMarket exportLegalityForGood:good];
   10983             :         NSString *legaldesc = nil;
   10984             :         if (import_legality == 0)
   10985             :         {
   10986             :                 if (export_legality == 0)
   10987             :                 {
   10988             :                         legaldesc = DESC(@"oolite-legality-clear");
   10989             :                 }
   10990             :                 else
   10991             :                 {
   10992             :                         legaldesc = DESC(@"oolite-legality-import");
   10993             :                 }
   10994             :         } 
   10995             :         else
   10996             :         {
   10997             :                 if (export_legality == 0)
   10998             :                 {
   10999             :                         legaldesc = DESC(@"oolite-legality-export");
   11000             :                 }
   11001             :                 else
   11002             :                 {
   11003             :                         legaldesc = DESC(@"oolite-legality-neither");
   11004             :                 }
   11005             :         }
   11006             :         legaldesc = [NSString stringWithFormat:@" %@ ",legaldesc];
   11007             :                         
   11008             :         NSString *extradesc = [shipCommodityData shortCommentForGood:good];
   11009             : 
   11010             :         [gui setKey:good forRow:row];
   11011             :         [gui setColor:[gui colorFromSetting:kGuiMarketCommodityColor defaultValue:nil] forRow:row];
   11012             :         [gui setArray:[NSArray arrayWithObjects: desc, extradesc, price, units_available, units_owned, legaldesc,  nil] forRow:row++];
   11013             : 
   11014             : }
   11015             : 
   11016             : 
   11017           0 : - (NSString *)marketScreenTitle
   11018             : {
   11019             :         StationEntity *dockedStation = [self dockedStation];
   11020             : 
   11021             :         /* Override normal behaviour if station broadcasts market */
   11022             :         if (dockedStation == nil)  
   11023             :         {
   11024             :                 if ([[self primaryTarget] isStation] && [(StationEntity *)[self primaryTarget] marketBroadcast])
   11025             :                 {
   11026             :                         dockedStation = [self primaryTarget];
   11027             :                 }
   11028             :         }
   11029             : 
   11030             :         NSString *system = nil;
   11031             :         if ([UNIVERSE sun] != nil)  system = [UNIVERSE getSystemName:system_id];
   11032             :         
   11033             :         if (dockedStation == nil || dockedStation == [UNIVERSE station])
   11034             :         {
   11035             :                 if ([UNIVERSE sun] != nil)
   11036             :                 {
   11037             :                         return OOExpandKey(@"system-commodity-market", system);
   11038             :                 }
   11039             :                 else
   11040             :                 {
   11041             :                         // Witchspace
   11042             :                         return OOExpandKey(@"commodity-market");
   11043             :                 }
   11044             :         }
   11045             :         else
   11046             :         {
   11047             :                 NSString *station = [dockedStation displayName];
   11048             :                 return OOExpandKey(@"station-commodity-market", station);
   11049             :         }
   11050             : }
   11051             : 
   11052             : 
   11053             : - (void) setGuiToMarketScreen
   11054             : {
   11055             :         OOCommodityMarket       *localMarket = [self localMarket];
   11056             :         GuiDisplayGen           *gui = [UNIVERSE gui];
   11057             :         OOGUIScreenID           oldScreen = gui_screen;
   11058             :         
   11059             :         gui_screen = GUI_SCREEN_MARKET;
   11060             :         BOOL                    guiChanged = (oldScreen != gui_screen);
   11061             : 
   11062             :         
   11063             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
   11064             :         
   11065             :         // fix problems with economies in witchspace
   11066             :         if (localMarket == nil)
   11067             :         {
   11068             :                 localMarket = [[UNIVERSE commodities] generateBlankMarket];
   11069             :         }
   11070             : 
   11071             :         // following changed to work whether docked or not
   11072             :         NSArray *goods = [self applyMarketSorter:[self applyMarketFilter:[localMarket goods] onMarket:localMarket] onMarket:localMarket];
   11073             :         NSInteger maxOffset = 0;
   11074             :         if ([goods count] > (GUI_ROW_MARKET_END-GUI_ROW_MARKET_START))
   11075             :         {
   11076             :                 maxOffset = [goods count]-(GUI_ROW_MARKET_END-GUI_ROW_MARKET_START);
   11077             :         }
   11078             : 
   11079             :         NSUInteger                      commodityCount = [shipCommodityData count];
   11080             :         OOCargoQuantity         quantityInHold[commodityCount];
   11081             :                 
   11082             :         for (NSUInteger i = 0; i < commodityCount; i++)
   11083             :         {
   11084             :                 quantityInHold[i] = [shipCommodityData quantityForGood:[goods oo_stringAtIndex:i]];
   11085             :         }
   11086             :         for (NSUInteger i = 0; i < [cargo count]; i++)
   11087             :         {
   11088             :                 ShipEntity *container = [cargo objectAtIndex:i];
   11089             :                 NSUInteger goodsIndex = [goods indexOfObject:[container commodityType]];
   11090             :                 // can happen with filters
   11091             :                 if (goodsIndex != NSNotFound)
   11092             :                 {
   11093             :                         quantityInHold[goodsIndex] += [container commodityAmount];
   11094             :                 }
   11095             :         }
   11096             : 
   11097             :         if (marketSelectedCommodity != nil && ([marketSelectedCommodity isEqualToString:@"<<<"] || [marketSelectedCommodity isEqualToString:@">>>"]))
   11098             :         {
   11099             :                 // nothing?
   11100             :         }
   11101             :         else
   11102             :         {
   11103             :                 if (marketSelectedCommodity == nil || [goods indexOfObject:marketSelectedCommodity] == NSNotFound)
   11104             :                 {
   11105             :                         DESTROY(marketSelectedCommodity);
   11106             :                         if ([goods count] > 0)
   11107             :                         {
   11108             :                                 marketSelectedCommodity = [[goods oo_stringAtIndex:0] retain];
   11109             :                         }
   11110             :                 }
   11111             :                 if (maxOffset > 0)
   11112             :                 {
   11113             :                         NSInteger goodsIndex = [goods indexOfObject:marketSelectedCommodity];
   11114             :                         // validate marketOffset when returning from infoscreen
   11115             :                         if (goodsIndex <= marketOffset)
   11116             :                         {
   11117             :                                 // is off top of list, move list upwards
   11118             :                                 if (goodsIndex == 0) {
   11119             :                                         marketOffset = 0;
   11120             :                                 } else {
   11121             :                                         marketOffset = goodsIndex-1;
   11122             :                                 }
   11123             :                         }
   11124             :                         else if (goodsIndex > marketOffset+(GUI_ROW_MARKET_END-GUI_ROW_MARKET_START)-2)
   11125             :                         {
   11126             :                                 // is off bottom of list, move list downwards
   11127             :                                 marketOffset = 2+goodsIndex-(GUI_ROW_MARKET_END-GUI_ROW_MARKET_START);
   11128             :                                 if (marketOffset > maxOffset)
   11129             :                                 {
   11130             :                                         marketOffset = maxOffset;
   11131             :                                 }
   11132             :                         }
   11133             :                 }
   11134             :         }
   11135             : 
   11136             :         // GUI stuff
   11137             :         {
   11138             :                 OOGUIRow                        start_row = GUI_ROW_MARKET_START;
   11139             :                 OOGUIRow                        row = start_row;
   11140             :                 OOGUIRow                        active_row = [gui selectedRow];
   11141             : 
   11142             :                 [gui clearAndKeepBackground:!guiChanged];
   11143             :                 
   11144             :                 
   11145             :                 StationEntity *dockedStation = [self dockedStation];
   11146             :                 if (dockedStation == nil && [[self primaryTarget] isStation] && [(StationEntity *)[self primaryTarget] marketBroadcast])
   11147             :                 {
   11148             :                         dockedStation = [self primaryTarget];
   11149             :                 }
   11150             : 
   11151             :                 [gui setTitle:[self marketScreenTitle]];
   11152             :                 
   11153             :                 [self showMarketScreenHeaders];
   11154             : 
   11155             :                 if (marketOffset > maxOffset)
   11156             :                 {
   11157             :                         marketOffset = 0;
   11158             :                 }
   11159             :                 else if (marketOffset < 0)
   11160             :                 {
   11161             :                         marketOffset = maxOffset;
   11162             :                 }
   11163             : 
   11164             :                 if ([goods count] > 0)
   11165             :                 {
   11166             :                         OOCommodityType good = nil;
   11167             :                         NSInteger i = 0;
   11168             :                         foreach (good, goods)
   11169             :                         {
   11170             :                                 if (i < marketOffset)
   11171             :                                 {
   11172             :                                         ++i;
   11173             :                                         continue;
   11174             :                                 }
   11175             :                                 [self showMarketScreenDataLine:row forGood:good inMarket:localMarket holdQuantity:quantityInHold[i++]];
   11176             :                                 if ([good isEqualToString:marketSelectedCommodity])
   11177             :                                 {
   11178             :                                         active_row = row;
   11179             :                                 }
   11180             : 
   11181             :                                 ++row;
   11182             :                                 if (row >= GUI_ROW_MARKET_END)
   11183             :                                 {
   11184             :                                         break;
   11185             :                                 }
   11186             :                         }
   11187             : 
   11188             :                         if (marketOffset < maxOffset)
   11189             :                         {
   11190             :                                 if ([marketSelectedCommodity isEqualToString:@">>>"])
   11191             :                                 {
   11192             :                                         active_row = GUI_ROW_MARKET_LAST;
   11193             :                                 }
   11194             :                                 [gui setKey:@">>>" forRow:GUI_ROW_MARKET_LAST];
   11195             :                                 [gui setColor:[gui colorFromSetting:kGuiMarketScrollColor defaultValue:[OOColor greenColor]] forRow:GUI_ROW_MARKET_LAST];
   11196             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-more"), @"", @"", @"", @" --> ", nil] forRow:GUI_ROW_MARKET_LAST];
   11197             :                         }
   11198             :                         if (marketOffset > 0)
   11199             :                         {
   11200             :                                 if ([marketSelectedCommodity isEqualToString:@"<<<"])
   11201             :                                 {
   11202             :                                         active_row = GUI_ROW_MARKET_START;
   11203             :                                 }
   11204             :                                 [gui setKey:@"<<<" forRow:GUI_ROW_MARKET_START];
   11205             :                                 [gui setColor:[gui colorFromSetting:kGuiMarketScrollColor defaultValue:[OOColor greenColor]] forRow:GUI_ROW_MARKET_START];
   11206             :                                 [gui setArray:[NSArray arrayWithObjects:DESC(@"gui-back"), @"", @"", @"", @" <-- ", nil] forRow:GUI_ROW_MARKET_START];
   11207             :                         }
   11208             :                 }
   11209             :                 else
   11210             :                 {
   11211             :                         // filter is excluding everything
   11212             :                         [gui setColor:[gui colorFromSetting:kGuiMarketFilteredAllColor defaultValue:[OOColor yellowColor]] forRow:GUI_ROW_MARKET_START];
   11213             :                         [gui setText:DESC(@"oolite-market-filtered-all") forRow:GUI_ROW_MARKET_START];
   11214             :                         active_row = -1;
   11215             :                 }
   11216             : 
   11217             :                  // actually count the containers and  valuables (may be > max_cargo)
   11218             :                 current_cargo = [self cargoQuantityOnBoard];
   11219             :                 if (current_cargo > [self maxAvailableCargoSpace]) current_cargo = [self maxAvailableCargoSpace]; 
   11220             : 
   11221             :                 // filter sort info
   11222             :                 {
   11223             :                         NSString *filterMode = OOExpandKey(OOExpand(@"oolite-market-filter-[marketFilterMode]", marketFilterMode));
   11224             :                         NSString *filterText = OOExpandKey(@"oolite-market-filter-line", filterMode);
   11225             :                         NSString *sortMode = OOExpandKey(OOExpand(@"oolite-market-sorter-[marketSorterMode]", marketSorterMode));
   11226             :                         NSString *sorterText = OOExpandKey(@"oolite-market-sorter-line", sortMode);
   11227             :                         [gui setArray:[NSArray arrayWithObjects:filterText, @"", sorterText, nil] forRow:GUI_ROW_MARKET_END];
   11228             :                 }
   11229             :                 [gui setColor:[gui colorFromSetting:kGuiMarketFilterInfoColor defaultValue:[OOColor greenColor]] forRow:GUI_ROW_MARKET_END];
   11230             : 
   11231             :                 [self showMarketCashAndLoadLine];
   11232             :                 
   11233             :                 [gui setSelectableRange:NSMakeRange(start_row,row - start_row)];
   11234             :                 [gui setSelectedRow:active_row];
   11235             :                 
   11236             :                 [gui setShowTextCursor:NO];
   11237             :         }
   11238             : 
   11239             :         
   11240             :         [[UNIVERSE gameView] clearMouse];
   11241             :         
   11242             :         [self setShowDemoShips:NO];
   11243             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
   11244             :         
   11245             :         if (guiChanged)
   11246             :         {
   11247             :                 [gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"overlay"];
   11248             :                 [gui setBackgroundTextureKey:@"market"];
   11249             :                 [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
   11250             :         }
   11251             : }
   11252             : 
   11253             : 
   11254             : - (void) setGuiToMarketInfoScreen
   11255             : {
   11256             :         OOCommodityMarket       *localMarket = [self localMarket];
   11257             :         GuiDisplayGen           *gui = [UNIVERSE gui];
   11258             :         OOGUIScreenID           oldScreen = gui_screen;
   11259             :         
   11260             :         gui_screen = GUI_SCREEN_MARKETINFO;
   11261             :         BOOL                    guiChanged = (oldScreen != gui_screen);
   11262             : 
   11263             :         
   11264             :         [[UNIVERSE gameController] setMouseInteractionModeForUIWithMouseInteraction:YES];
   11265             :         
   11266             :         // fix problems with economies in witchspace
   11267             :         if (localMarket == nil)
   11268             :         {
   11269             :                 localMarket = [[UNIVERSE commodities] generateBlankMarket];
   11270             :         }
   11271             : 
   11272             :         // following changed to work whether docked or not
   11273             :         NSArray                         *goods = [self applyMarketSorter:[self applyMarketFilter:[localMarket goods] onMarket:localMarket] onMarket:localMarket];
   11274             : 
   11275             :         NSUInteger                      i, j, commodityCount = [shipCommodityData count];
   11276             :         OOCargoQuantity         quantityInHold[commodityCount];
   11277             :                 
   11278             :         for (i = 0; i < commodityCount; i++)
   11279             :         {
   11280             :                 quantityInHold[i] = [shipCommodityData quantityForGood:[goods oo_stringAtIndex:i]];
   11281             :         }
   11282             :         for (i = 0; i < [cargo count]; i++)
   11283             :         {
   11284             :                 ShipEntity *container = [cargo objectAtIndex:i];
   11285             :                 j = [goods indexOfObject:[container commodityType]];
   11286             :                 quantityInHold[j] += [container commodityAmount];
   11287             :         }
   11288             : 
   11289             : 
   11290             :         // GUI stuff
   11291             :         {
   11292             :                 if (EXPECT_NOT(marketSelectedCommodity == nil))
   11293             :                 {
   11294             :                         j = NSNotFound;
   11295             :                 }
   11296             :                 else
   11297             :                 {
   11298             :                         j = [goods indexOfObject:marketSelectedCommodity];
   11299             :                 }
   11300             :                 if (j == NSNotFound)
   11301             :                 {
   11302             :                         DESTROY(marketSelectedCommodity);
   11303             :                         [self setGuiToMarketScreen];
   11304             :                         return;
   11305             :                 }
   11306             : 
   11307             :                 [gui clearAndKeepBackground:!guiChanged];
   11308             : 
   11309             :                 [gui setTitle:[NSString stringWithFormat:DESC(@"oolite-commodity-information-@"), [shipCommodityData nameForGood:marketSelectedCommodity]]];
   11310             : 
   11311             :                 [self showMarketScreenHeaders];
   11312             :                 [self showMarketScreenDataLine:GUI_ROW_MARKET_START forGood:marketSelectedCommodity inMarket:localMarket holdQuantity:quantityInHold[j]];
   11313             : 
   11314             :                 OOCargoQuantity contracted = [self contractedVolumeForGood:marketSelectedCommodity];
   11315             :                 if (contracted > 0)
   11316             :                 {
   11317             :                         OOMassUnit unit = [shipCommodityData massUnitForGood:marketSelectedCommodity];
   11318             :                         [gui setColor:[gui colorFromSetting:kGuiMarketContractedColor defaultValue:nil] forRow:GUI_ROW_MARKET_START+1];
   11319             :                         [gui setText:[NSString stringWithFormat:DESC(@"oolite-commodity-contracted-d-@"), contracted, DisplayStringForMassUnit(unit)] forRow:GUI_ROW_MARKET_START+1];
   11320             :                 }
   11321             : 
   11322             :                 NSString *info = [shipCommodityData commentForGood:marketSelectedCommodity];
   11323             :                 OOGUIRow i = 0;
   11324             :                 if (info == nil || [info length] == 0)
   11325             :                 {
   11326             :                         i = [gui addLongText:DESC(@"oolite-commodity-no-comment") startingAtRow:GUI_ROW_MARKET_START+2 align:GUI_ALIGN_LEFT];
   11327             :                 }
   11328             :                 else
   11329             :                 {
   11330             :                         i = [gui addLongText:info startingAtRow:GUI_ROW_MARKET_START+2 align:GUI_ALIGN_LEFT];
   11331             :                 }
   11332             :                 for (i-- ; i > GUI_ROW_MARKET_START+2 ; --i)
   11333             :                 {
   11334             :                         [gui setColor:[gui colorFromSetting:kGuiMarketDescriptionColor defaultValue:nil] forRow:i];
   11335             :                 }
   11336             : 
   11337             :                 [self showMarketCashAndLoadLine];
   11338             : 
   11339             :         }
   11340             : 
   11341             :         [[UNIVERSE gameView] clearMouse];
   11342             :         
   11343             :         [self setShowDemoShips:NO];
   11344             :         [UNIVERSE enterGUIViewModeWithMouseInteraction:YES];
   11345             :         
   11346             :         if (guiChanged)
   11347             :         {
   11348             :                 [gui setForegroundTextureKey:[self status] == STATUS_DOCKED ? @"docked_overlay" : @"overlay"];
   11349             :                 [gui setBackgroundTextureKey:@"marketinfo"];
   11350             :                 [self noteGUIDidChangeFrom:oldScreen to:gui_screen];
   11351             :         }
   11352             : }
   11353             : 
   11354           0 : - (void) showMarketCashAndLoadLine
   11355             : {
   11356             :         GuiDisplayGen *gui = [UNIVERSE gui];
   11357             :         OOCargoQuantity currentCargo = current_cargo;
   11358             :         OOCargoQuantity cargoCapacity = [self maxAvailableCargoSpace];
   11359             :         [gui setText:OOExpandKey(@"market-cash-and-load", credits, currentCargo, cargoCapacity) forRow:GUI_ROW_MARKET_CASH];
   11360             :         [gui setColor:[gui colorFromSetting:kGuiMarketCashColor defaultValue:[OOColor yellowColor]] forRow:GUI_ROW_MARKET_CASH];
   11361             : }
   11362             : 
   11363             : - (OOGUIScreenID) guiScreen
   11364             : {
   11365             :         return gui_screen;
   11366             : }
   11367             : 
   11368             : 
   11369             : - (BOOL) tryBuyingCommodity:(OOCommodityType)index all:(BOOL)all
   11370             : {
   11371             :         if ([index isEqualToString:@"<<<"] || [index isEqualToString:@">>>"])
   11372             :         {
   11373             :                 ++marketOffset;
   11374             :                 return NO;
   11375             :         }
   11376             : 
   11377             :         if (![self isDocked])  return NO; // can't buy if not docked.
   11378             :         
   11379             :         OOCommodityMarket       *localMarket = [self localMarket];
   11380             :         OOCreditsQuantity       pricePerUnit    = [localMarket priceForGood:index];
   11381             :         OOMassUnit                      unit                    = [localMarket massUnitForGood:index];
   11382             : 
   11383             :         if (specialCargo != nil && unit == UNITS_TONS)
   11384             :         {
   11385             :                 return NO;                                                                      // can't buy tons of stuff when carrying a specialCargo
   11386             :         }
   11387             :         int manifest_quantity = [shipCommodityData quantityForGood:index];
   11388             :         int market_quantity = [localMarket quantityForGood:index];
   11389             :         
   11390             :         int purchase = 1;
   11391             :         if (all)
   11392             :         {
   11393             :                 // if cargo contracts, put a break point on the contract volume
   11394             :                 int contracted = [self contractedVolumeForGood:index];
   11395             :                 if (manifest_quantity >= contracted)
   11396             :                 {
   11397             :                         purchase = [localMarket capacityForGood:index];
   11398             :                 }
   11399             :                 else
   11400             :                 {
   11401             :                         purchase = contracted-manifest_quantity;
   11402             :                 }
   11403             :         }
   11404             :         if (purchase > market_quantity)
   11405             :         {
   11406             :                 purchase = market_quantity;                                     // limit to what's available
   11407             :         }
   11408             :         if (purchase * pricePerUnit > credits)
   11409             :         {
   11410             :                 purchase = floor (credits / pricePerUnit);      // limit to what's affordable
   11411             :         }
   11412             :         // TODO - fix brokenness here...
   11413             :         if (unit == UNITS_TONS && purchase + current_cargo > [self maxAvailableCargoSpace])
   11414             :         {
   11415             :                 purchase = [self availableCargoSpace];          // limit to available cargo space
   11416             :         }
   11417             :         else
   11418             :         {
   11419             :                 if (current_cargo == [self maxAvailableCargoSpace])
   11420             :                 {
   11421             :                         // other cases are fine so long as buying is limited to <1000kg / <1000000g
   11422             :                         // but if this case is true, we need to see if there is more space in
   11423             :                         // the manifest (safe) or an already-accounted-for pod
   11424             :                         if (unit == UNITS_KILOGRAMS)
   11425             :                         {
   11426             :                                 if (manifest_quantity % KILOGRAMS_PER_POD <= MAX_KILOGRAMS_IN_SAFE && (manifest_quantity + purchase) % KILOGRAMS_PER_POD > MAX_KILOGRAMS_IN_SAFE)
   11427             :                                 {
   11428             :                                         // going from < n500 to >= n500 would increase pods needed by 1
   11429             :                                         purchase = MAX_KILOGRAMS_IN_SAFE - manifest_quantity; // max possible
   11430             :                                 }
   11431             :                         }
   11432             :                         else // UNITS_GRAMS
   11433             :                         {
   11434             :                                 if (manifest_quantity % GRAMS_PER_POD <= MAX_GRAMS_IN_SAFE && (manifest_quantity + purchase) % GRAMS_PER_POD > MAX_GRAMS_IN_SAFE)
   11435             :                                 {
   11436             :                                         // going from < n500000 to >= n500000 would increase pods needed by 1
   11437             :                                         purchase = MAX_GRAMS_IN_SAFE - manifest_quantity; // max possible
   11438             :                                 }
   11439             :                         }
   11440             :                 }
   11441             :         }
   11442             :         if (purchase <= 0)
   11443             :         {
   11444             :                 return NO;                                                                      // stop if that results in nothing to be bought
   11445             :         }
   11446             :         
   11447             :         [localMarket removeQuantity:purchase forGood:index];
   11448             :         [shipCommodityData addQuantity:purchase forGood:index];
   11449             :         credits -= pricePerUnit * purchase;
   11450             : 
   11451             :         [self calculateCurrentCargo];
   11452             :         
   11453             :         if ([UNIVERSE autoSave])  [UNIVERSE setAutoSaveNow:YES];
   11454             :         
   11455             :         [self doScriptEvent:OOJSID("playerBoughtCargo") withArguments:[NSArray arrayWithObjects:index, [NSNumber numberWithInt:purchase], [NSNumber numberWithUnsignedLongLong:pricePerUnit], nil]];
   11456             :         if ([localMarket exportLegalityForGood:index] > 0)
   11457             :         {
   11458             :                 [roleWeightFlags setObject:[NSNumber numberWithInt:1] forKey:@"bought-illegal"];
   11459             :         }
   11460             :         else
   11461             :         {
   11462             :                 [roleWeightFlags setObject:[NSNumber numberWithInt:1] forKey:@"bought-legal"];
   11463             :         }
   11464             :         
   11465             :         return YES;
   11466             : }
   11467             : 
   11468             : 
   11469             : - (BOOL) trySellingCommodity:(OOCommodityType)index all:(BOOL)all
   11470             : {
   11471             :         if ([index isEqualToString:@"<<<"] || [index isEqualToString:@">>>"])
   11472             :         {
   11473             :                 --marketOffset;
   11474             :                 return NO;
   11475             :         }
   11476             : 
   11477             :         if (![self isDocked])  return NO; // can't sell if not docked.
   11478             :         
   11479             :         OOCommodityMarket *localMarket = [self localMarket];
   11480             :         int available_units = [shipCommodityData quantityForGood:index];
   11481             :         OOCreditsQuantity pricePerUnit = [localMarket priceForGood:index];
   11482             :         
   11483             :         if (available_units == 0)  return NO;
   11484             : 
   11485             :         int market_quantity = [localMarket quantityForGood:index];
   11486             : 
   11487             :         int capacity = [localMarket capacityForGood:index];
   11488             :         int sell = 1;
   11489             :         if (all)
   11490             :         {
   11491             :                 // if cargo contracts, put a break point on the contract volume
   11492             :                 int contracted = [self contractedVolumeForGood:index];
   11493             :                 if (available_units <= contracted)
   11494             :                 {
   11495             :                         sell = capacity;
   11496             :                 }
   11497             :                 else
   11498             :                 {
   11499             :                         sell = available_units-contracted;
   11500             :                 }
   11501             :         }
   11502             : 
   11503             :         if (sell > available_units)
   11504             :                 sell = available_units;                                 // limit to what's in the hold
   11505             :         if (sell + market_quantity > capacity)
   11506             :                 sell = capacity - market_quantity;                      // avoid flooding the market
   11507             :         if (sell <= 0)
   11508             :                 return NO;                                                              // stop if that results in nothing to be sold
   11509             :         
   11510             :         [localMarket addQuantity:sell forGood:index];
   11511             :         [shipCommodityData removeQuantity:sell forGood:index];
   11512             :         credits += pricePerUnit * sell;
   11513             : 
   11514             :         [self calculateCurrentCargo];
   11515             :         
   11516             :         if ([UNIVERSE autoSave]) [UNIVERSE setAutoSaveNow:YES];
   11517             :         
   11518             :         [self doScriptEvent:OOJSID("playerSoldCargo") withArguments:[NSArray arrayWithObjects:index, [NSNumber numberWithInt:sell], [NSNumber numberWithUnsignedLongLong: pricePerUnit], nil]];
   11519             :         
   11520             :         return YES;
   11521             : }
   11522             : 
   11523             : 
   11524           0 : - (BOOL) isMining
   11525             : {
   11526             :         return using_mining_laser;
   11527             : }
   11528             : 
   11529             : 
   11530             : - (OOSpeechSettings) isSpeechOn
   11531             : {
   11532             :         return isSpeechOn;
   11533             : }
   11534             : 
   11535             : 
   11536           0 : - (BOOL) canAddEquipment:(NSString *)equipmentKey inContext:(NSString *)context
   11537             : {
   11538             :         if ([equipmentKey isEqualToString:@"EQ_RENOVATION"] && !(ship_trade_in_factor < 85 || [[[self shipSubEntityEnumerator] allObjects] count] < [self maxShipSubEntities]))  return NO;
   11539             :         if (![super canAddEquipment:equipmentKey inContext:context])  return NO;
   11540             :         
   11541             :         NSArray *conditions = [[OOEquipmentType equipmentTypeWithIdentifier:equipmentKey] conditions];
   11542             :         if (conditions != nil && ![self scriptTestConditions:conditions])  return NO;
   11543             :         
   11544             :         return YES;
   11545             : }
   11546             : 
   11547             : 
   11548           0 : - (BOOL) addEquipmentItem:(NSString *)equipmentKey inContext:(NSString *)context
   11549             : {
   11550             :         return [self addEquipmentItem:equipmentKey withValidation:YES inContext:context];
   11551             : }
   11552             : 
   11553             : 
   11554           0 : - (BOOL) addEquipmentItem:(NSString *)equipmentKey withValidation:(BOOL)validateAddition inContext:(NSString *)context
   11555             : {
   11556             :         // deal with trumbles..
   11557             :         if ([equipmentKey isEqualToString:@"EQ_TRUMBLE"])
   11558             :         {
   11559             :                 /*      Bug fix: must return here if eqKey == @"EQ_TRUMBLE", even if
   11560             :                         trumbleCount >= 1. Otherwise, the player becomes immune to
   11561             :                         trumbles. See comment in -setCommanderDataFromDictionary: for more
   11562             :                         details.
   11563             :                  -- Ahruman 2008-12-04
   11564             :                  */
   11565             :                 // the old trumbles will kill the new one if there are enough of them.
   11566             :                 if ((trumbleCount < PLAYER_MAX_TRUMBLES / 6) || (trumbleCount < PLAYER_MAX_TRUMBLES / 3 && ranrot_rand() % 2 > 0))
   11567             :                 {
   11568             :                         [self addTrumble:trumble[ranrot_rand() % PLAYER_MAX_TRUMBLES]]; // randomise its looks.
   11569             :                         return YES;
   11570             :                 }
   11571             :                 return NO;
   11572             :         }
   11573             :         
   11574             :         BOOL OK = [super addEquipmentItem:equipmentKey withValidation:validateAddition inContext:context];
   11575             :         
   11576             :         if (OK)
   11577             :         {
   11578             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"] && [self compassMode] == COMPASS_MODE_BASIC)
   11579             :                 {
   11580             :                         [self setCompassMode:COMPASS_MODE_PLANET];
   11581             :                 }
   11582             :                 
   11583             :                 [self addEqScriptForKey:equipmentKey];
   11584             :                 [self addEquipmentWithScriptToCustomKeyArray:equipmentKey];
   11585             :         }
   11586             :         return OK;
   11587             : }
   11588             : 
   11589             : 
   11590             : - (NSMutableArray *) customEquipmentActivation
   11591             : {
   11592             :         return customEquipActivation;
   11593             : }
   11594             : 
   11595             : 
   11596             : - (void) addEquipmentWithScriptToCustomKeyArray:(NSString *)equipmentKey
   11597             : {
   11598             :         NSDictionary *item;
   11599             :         NSUInteger i, j;
   11600             :         NSArray *object;
   11601             : 
   11602             :         for (i = 0; i < [eqScripts count]; i++) 
   11603             :         {
   11604             :                 if ([[[eqScripts oo_arrayAtIndex:i] oo_stringAtIndex:0] isEqualToString:equipmentKey]) 
   11605             :                 {
   11606             :                         //check if this equipment item is already in the array
   11607             :                         for (j = 0; j < [customEquipActivation count]; j++) {
   11608             :                                 item = [customEquipActivation objectAtIndex:j];
   11609             :                                 if ([[item oo_stringForKey:CUSTOMEQUIP_EQUIPKEY] isEqualToString:equipmentKey]) return;
   11610             :                         }
   11611             :                         // if we get here, this item is new
   11612             :                         // add the basic info at this point (equipkey and name only)
   11613             :                         OOEquipmentType *eq = [OOEquipmentType equipmentTypeWithIdentifier:equipmentKey];
   11614             :                         NSMutableDictionary *customKey = [[NSMutableDictionary alloc] initWithObjectsAndKeys:equipmentKey, CUSTOMEQUIP_EQUIPKEY, [eq name], CUSTOMEQUIP_EQUIPNAME, nil];
   11615             :                         
   11616             :                         // grab any default keys from the equipment item
   11617             :                         // default activate
   11618             :                         object = [eq defaultActivateKey];
   11619             :                         if ((object != nil && [object count] > 0))
   11620             :                                 [customKey setObject:object forKey:CUSTOMEQUIP_KEYACTIVATE];
   11621             :                         // default mode
   11622             :                         object = [eq defaultModeKey];
   11623             :                         if ((object != nil && [object count] > 0))
   11624             :                                 [customKey setObject:object forKey:CUSTOMEQUIP_KEYMODE];
   11625             : 
   11626             :                         [customEquipActivation addObject:customKey];
   11627             :                         [customKey release];
   11628             :                         // keep the keypress arrays in sync
   11629             :                         [customActivatePressed addObject:[NSNumber numberWithBool:NO]];
   11630             :                         [customModePressed addObject:[NSNumber numberWithBool:NO]];                     
   11631             : 
   11632             :                         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   11633             :                         [defaults setObject:customEquipActivation forKey:KEYCONFIG_CUSTOMEQUIP];
   11634             :                         return;
   11635             :                 }
   11636             :         }
   11637             : }
   11638             : 
   11639             : 
   11640             : - (void) validateCustomEquipActivationArray
   11641             : {
   11642             :         int i;
   11643             :         bool update = NO;
   11644             :         NSString *equipmentKey;
   11645             :         if ([customEquipActivation count] == 0) return;
   11646             :         for (i = [customEquipActivation count] - 1; i >= 0; i--) {
   11647             :                 equipmentKey = [[customEquipActivation objectAtIndex:i] oo_stringForKey:CUSTOMEQUIP_EQUIPKEY];
   11648             :                 OOEquipmentType *eq = [OOEquipmentType equipmentTypeWithIdentifier:equipmentKey];
   11649             :                 if (!eq) {
   11650             :                         [customEquipActivation removeObjectAtIndex:i];
   11651             :                         [customActivatePressed removeObjectAtIndex:i];
   11652             :                         [customModePressed removeObjectAtIndex:i];
   11653             :                         update = YES;
   11654             :                 }
   11655             :         }
   11656             :         if (update) {
   11657             :                 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   11658             :                 [defaults setObject:customEquipActivation forKey:KEYCONFIG_CUSTOMEQUIP];
   11659             :         }
   11660             : }
   11661             : 
   11662             : 
   11663           0 : - (void) removeEquipmentItem:(NSString *)equipmentKey
   11664             : {
   11665             :         if(![self hasEquipmentItemProviding:@"EQ_ADVANCED_COMPASS"] && [self compassMode] != COMPASS_MODE_BASIC)
   11666             :         {
   11667             :                 [self setCompassMode:COMPASS_MODE_BASIC];
   11668             :         }
   11669             :         [super removeEquipmentItem:equipmentKey];
   11670             :         if(![self hasEquipmentItem:equipmentKey]) {
   11671             :                 // removed the last one
   11672             :                 [self removeEqScriptForKey:equipmentKey];
   11673             :         }
   11674             : }
   11675             : 
   11676             : 
   11677             : - (void) addEquipmentFromCollection:(id)equipment
   11678             : {
   11679             :         NSDictionary    *dict = nil;
   11680             :         NSEnumerator    *eqEnum = nil;
   11681             :         NSString        *eqDesc = nil;
   11682             :         NSUInteger      i, count;
   11683             : 
   11684             :         // Pass 1: Load the entire collection.
   11685             :         if ([equipment isKindOfClass:[NSDictionary class]])
   11686             :         {
   11687             :                 dict = equipment;
   11688             :                 eqEnum = [equipment keyEnumerator];
   11689             :         }
   11690             :         else if ([equipment isKindOfClass:[NSArray class]] || [equipment isKindOfClass:[NSSet class]])
   11691             :         {
   11692             :                 eqEnum = [equipment objectEnumerator];
   11693             :         }
   11694             :         else if ([equipment isKindOfClass:[NSString class]])
   11695             :         {
   11696             :                 eqEnum = [[NSArray arrayWithObject:equipment] objectEnumerator];
   11697             :         }
   11698             :         else
   11699             :         {
   11700             :                 return;
   11701             :         }
   11702             :         
   11703             :         while ((eqDesc = [eqEnum nextObject]))
   11704             :         {
   11705             :                 /*      Bug workaround: extra_equipment should never contain EQ_TRUMBLE,
   11706             :                         which is basically a magic flag passed to awardEquipment: to infect
   11707             :                         the player. However, prior to Oolite 1.70.1, if the player had a
   11708             :                         trumble infection and awardEquipment:EQ_TRUMBLE was called, an
   11709             :                         EQ_TRUMBLE would be added to the equipment list. Subsequent calls
   11710             :                         to awardEquipment:EQ_TRUMBLE would exit early because there was an
   11711             :                         EQ_TRUMBLE in the equipment list. as a result, it would no longer
   11712             :                         be possible to infect the player after the current infection ended.
   11713             :                         
   11714             :                         The bug is fixed in 1.70.1. The following line is to fix old saved
   11715             :                         games which had been "corrupted" by the bug.
   11716             :                         -- Ahruman 2007-12-04
   11717             :                  */
   11718             :                 if ([eqDesc isEqualToString:@"EQ_TRUMBLE"])  continue;
   11719             :                 
   11720             :                 // Traditional form is a dictionary of booleans; we only accept those where the value is true.
   11721             :                 if (dict != nil && ![dict oo_boolForKey:eqDesc])  continue;
   11722             :                 
   11723             :                 // We need to add the entire collection without validation first and then remove the items that are
   11724             :                 // not compliant (like items that do not satisfy the requiresEquipment criterion). This is to avoid
   11725             :                 // unintentionally excluding valid equipment, just because the required equipment existed but had
   11726             :                 // not been yet added to the equipment list at the time of the canAddEquipment validation check.
   11727             :                 // Nikos, 20080817.
   11728             :                 count = [dict oo_unsignedIntegerForKey:eqDesc];
   11729             :                 for (i=0;i<count;i++)
   11730             :                 {
   11731             :                         [self addEquipmentItem:eqDesc withValidation:NO inContext:@"loading"];
   11732             :                 }
   11733             :         }
   11734             :         
   11735             :         // Pass 2: Remove items that do not satisfy validation criteria (like requires_equipment etc.).
   11736             :         if ([equipment isKindOfClass:[NSDictionary class]])
   11737             :         {
   11738             :                 eqEnum = [equipment keyEnumerator];
   11739             :         }
   11740             :         else if ([equipment isKindOfClass:[NSArray class]] || [equipment isKindOfClass:[NSSet class]])
   11741             :         {
   11742             :                 eqEnum = [equipment objectEnumerator];
   11743             :         }
   11744             :         else if ([equipment isKindOfClass:[NSString class]])
   11745             :         {
   11746             :                 eqEnum = [[NSArray arrayWithObject:equipment] objectEnumerator];
   11747             :         }
   11748             :         // Now remove items that should not be in the equipment list.
   11749             :         while ((eqDesc = [eqEnum nextObject]))
   11750             :         {
   11751             :                 if (![self equipmentValidToAdd:eqDesc whileLoading:YES inContext:@"loading"])
   11752             :                 {
   11753             :                         [self removeEquipmentItem:eqDesc];
   11754             :                 }
   11755             :         }
   11756             : }
   11757             : 
   11758             : 
   11759           0 : - (BOOL) hasOneEquipmentItem:(NSString *)itemKey includeMissiles:(BOOL)includeMissiles
   11760             : {
   11761             :         // Check basic equipment the normal way.
   11762             :         if ([super hasOneEquipmentItem:itemKey includeMissiles:NO whileLoading:NO])  return YES;
   11763             :         
   11764             :         // Custom handling for player missiles.
   11765             :         if (includeMissiles)
   11766             :         {
   11767             :                 unsigned i;
   11768             :                 for (i = 0; i < max_missiles; i++)
   11769             :                 {
   11770             :                         if ([[self missileForPylon:i] hasPrimaryRole:itemKey])  return YES;
   11771             :                 }
   11772             :         }
   11773             :         
   11774             :         if ([itemKey isEqualToString:@"EQ_TRUMBLE"])
   11775             :         {
   11776             :                 return [self trumbleCount] > 0;
   11777             :         }
   11778             :         
   11779             :         return NO;
   11780             : }
   11781             : 
   11782             : 
   11783           0 : - (BOOL) hasPrimaryWeapon:(OOWeaponType)weaponType
   11784             : {
   11785             :         if ([[forward_weapon_type identifier] isEqualToString:[weaponType identifier]] ||
   11786             :                 [[aft_weapon_type identifier] isEqualToString:[weaponType identifier]] ||
   11787             :                 [[port_weapon_type identifier] isEqualToString:[weaponType identifier]] ||
   11788             :                 [[starboard_weapon_type identifier] isEqualToString:[weaponType identifier]])
   11789             :         {
   11790             :                 return YES;
   11791             :         }
   11792             :         
   11793             :         return [super hasPrimaryWeapon:weaponType];
   11794             : }
   11795             : 
   11796             : 
   11797           0 : - (BOOL) removeExternalStore:(OOEquipmentType *)eqType
   11798             : {
   11799             :         NSString        *identifier = [eqType identifier];
   11800             :         
   11801             :         // Look for matching missile.
   11802             :         unsigned i;
   11803             :         for (i = 0; i < max_missiles; i++)
   11804             :         {
   11805             :                 if ([[self missileForPylon:i] hasPrimaryRole:identifier])
   11806             :                 {
   11807             :                         [self removeFromPylon:i];
   11808             :                         
   11809             :                         // Just remove one at a time.
   11810             :                         return YES;
   11811             :                 }
   11812             :         }
   11813             :         return NO;
   11814             : }
   11815             : 
   11816             : 
   11817             : - (BOOL) removeFromPylon:(NSUInteger)pylon
   11818             : {
   11819             :         if (pylon >= max_missiles) return NO;
   11820             :         
   11821             :         if (missile_entity[pylon] != nil)
   11822             :         {
   11823             :                 NSString        *identifier = [missile_entity[pylon] primaryRole];
   11824             :                 [super removeExternalStore:[OOEquipmentType equipmentTypeWithIdentifier:identifier]];
   11825             : 
   11826             :                 // Remove the missile (must wait until we've finished with its identifier string!)
   11827             :                 [missile_entity[pylon] release];
   11828             :                 missile_entity[pylon] = nil;
   11829             :                 
   11830             :                 [self tidyMissilePylons];
   11831             :                 
   11832             :                 // This should be the currently selected missile, deselect it.
   11833             :                 if (pylon <= activeMissile)
   11834             :                 {
   11835             :                         if (activeMissile == missiles && missiles > 0) activeMissile--;
   11836             :                         if (activeMissile > 0) activeMissile--;
   11837             :                         else activeMissile = max_missiles - 1;
   11838             :                         
   11839             :                         [self selectNextMissile];
   11840             :                 }
   11841             :                 
   11842             :                 return YES;
   11843             :         }
   11844             : 
   11845             :         return NO;
   11846             : }
   11847             : 
   11848             : 
   11849           0 : - (NSUInteger) parcelCount
   11850             : {
   11851             :         return [parcels count];
   11852             : }
   11853             : 
   11854             : 
   11855           0 : - (NSUInteger) passengerCount
   11856             : {
   11857             :         return [passengers count];
   11858             : }
   11859             : 
   11860             : 
   11861           0 : - (NSUInteger) passengerCapacity
   11862             : {
   11863             :         return max_passengers;
   11864             : }
   11865             : 
   11866             : 
   11867           0 : - (BOOL) hasHostileTarget
   11868             : {
   11869             :         ShipEntity *playersTarget = [self primaryTarget];
   11870             :         return ([playersTarget isShip] && [playersTarget hasHostileTarget] && [playersTarget primaryTarget] == self);
   11871             : }
   11872             : 
   11873             : 
   11874           0 : - (void) receiveCommsMessage:(NSString *) message_text from:(ShipEntity *) other
   11875             : {
   11876             :         if ([self status] == STATUS_DEAD || [self status] == STATUS_DOCKED)
   11877             :         {
   11878             :                 // only when in flight
   11879             :                 return;
   11880             :         }
   11881             :         [UNIVERSE addCommsMessage:[NSString stringWithFormat:@"%@:\n %@", [other displayName], message_text] forCount:4.5];
   11882             :         [super receiveCommsMessage:message_text from:other];
   11883             : }
   11884             : 
   11885             : 
   11886             : - (void) getFined
   11887             : {
   11888             :         if (legalStatus == 0)  return;                          // nothing to pay for
   11889             :         
   11890             :         OOGovernmentID local_gov = [[UNIVERSE currentSystemData] oo_intForKey:KEY_GOVERNMENT];
   11891             :         if ([UNIVERSE inInterstellarSpace])  local_gov = 1;     // equivalent to Feudal. I'm assuming any station in interstellar space is military. -- Ahruman 2008-05-29
   11892             :         OOCreditsQuantity fine = 500 + ((local_gov < 2 || local_gov > 5) ? 500 : 0);
   11893             :         fine *= legalStatus;
   11894             :         if (fine > credits)
   11895             :         {
   11896             :                 int payback = (int)(legalStatus * credits / fine);
   11897             :                 [self setBounty:(legalStatus-payback) withReason:kOOLegalStatusReasonPaidFine];
   11898             :                 credits = 0;
   11899             :         }
   11900             :         else
   11901             :         {
   11902             :                 [self setBounty:0 withReason:kOOLegalStatusReasonPaidFine];
   11903             :                 credits -= fine;
   11904             :         }
   11905             :         
   11906             :         // one of the fined-@-credits strings includes expansion tokens
   11907             :         NSString *fined_message = [NSString stringWithFormat:OOExpandKey(@"fined-@-credits"), OOCredits(fine)];
   11908             :         [self addMessageToReport:fined_message];
   11909             :         [UNIVERSE forceWitchspaceEntries];
   11910             :         ship_clock_adjust += 24 * 3600; // take up a day
   11911             : }
   11912             : 
   11913             : 
   11914             : - (void) adjustTradeInFactorBy:(int)value
   11915             : {
   11916             :         ship_trade_in_factor += value;
   11917             :         if (ship_trade_in_factor < 75)  ship_trade_in_factor = 75;
   11918             :         if (ship_trade_in_factor > 100)  ship_trade_in_factor = 100;
   11919             : }
   11920             : 
   11921             : 
   11922             : - (int) tradeInFactor
   11923             : {
   11924             :         return ship_trade_in_factor;
   11925             : }
   11926             : 
   11927             : 
   11928             : - (double) renovationCosts
   11929             : {
   11930             :         // 5% of value of ships wear + correction for missing subentities.
   11931             :         OOCreditsQuantity shipValue = [UNIVERSE tradeInValueForCommanderDictionary:[self commanderDataDictionary]];
   11932             : 
   11933             :         double costs = 0.005 * (100 - ship_trade_in_factor) * shipValue;
   11934             :         costs += 0.01 * shipValue * [self missingSubEntitiesAdjustment];
   11935             :         costs *= [self renovationFactor];
   11936             :         return cunningFee(costs, 0.05);
   11937             : }
   11938             : 
   11939             : 
   11940             : - (double) renovationFactor
   11941             : {
   11942             :         OOShipRegistry          *registry = [OOShipRegistry sharedRegistry];
   11943             :         NSDictionary            *shipyardInfo = [registry shipyardInfoForKey:[self shipDataKey]];
   11944             :         return [shipyardInfo oo_doubleForKey:KEY_RENOVATION_MULTIPLIER defaultValue:1.0];
   11945             : }
   11946             : 
   11947             : 
   11948             : - (void) setDefaultViewOffsets
   11949             : {
   11950             :         float halfLength = 0.5f * (boundingBox.max.z - boundingBox.min.z);
   11951             :         float halfWidth = 0.5f * (boundingBox.max.x - boundingBox.min.x);
   11952             : 
   11953             :         forwardViewOffset = make_vector(0.0f, 0.0f, boundingBox.max.z - halfLength);
   11954             :         aftViewOffset = make_vector(0.0f, 0.0f, boundingBox.min.z + halfLength);
   11955             :         portViewOffset = make_vector(boundingBox.min.x + halfWidth, 0.0f, 0.0f);
   11956             :         starboardViewOffset = make_vector(boundingBox.max.x - halfWidth, 0.0f, 0.0f);
   11957             :         customViewOffset = kZeroVector;
   11958             : }
   11959             : 
   11960             : 
   11961             : - (void) setDefaultCustomViews
   11962             : {
   11963             :         NSArray *customViews = [[[OOShipRegistry sharedRegistry] shipInfoForKey:PLAYER_SHIP_DESC] oo_arrayForKey:@"custom_views"];
   11964             :         
   11965             :         [_customViews release];
   11966             :         _customViews = nil;
   11967             :         _customViewIndex = 0;
   11968             :         if (customViews != nil)
   11969             :         {
   11970             :                 _customViews = [customViews retain];
   11971             :         }
   11972             : }
   11973             : 
   11974             : 
   11975             : - (Vector) weaponViewOffset
   11976             : {
   11977             :         switch (currentWeaponFacing)
   11978             :         {
   11979             :                 case WEAPON_FACING_FORWARD:
   11980             :                         return forwardViewOffset;
   11981             :                 case WEAPON_FACING_AFT:
   11982             :                         return aftViewOffset;
   11983             :                 case WEAPON_FACING_PORT:
   11984             :                         return portViewOffset;
   11985             :                 case WEAPON_FACING_STARBOARD:
   11986             :                         return starboardViewOffset;
   11987             :                         
   11988             :                 case WEAPON_FACING_NONE:
   11989             :                         // N.b.: this case should never happen.
   11990             :                         return customViewOffset;
   11991             :         }
   11992             :         return kZeroVector;
   11993             : }
   11994             : 
   11995             : 
   11996             : - (void) setUpTrumbles
   11997             : {
   11998             :         NSMutableString *trumbleDigrams = [NSMutableString stringWithCapacity:256];
   11999             :         unichar xchar = (unichar)0;
   12000             :         unichar digramchars[2];
   12001             : 
   12002             :         while ([trumbleDigrams length] < PLAYER_MAX_TRUMBLES + 2)
   12003             :         {
   12004             :                 NSString *commanderName = [self commanderName];
   12005             :                 if ([commanderName length] > 0)
   12006             :                 {
   12007             :                         [trumbleDigrams appendFormat:@"%@%@", commanderName, [[self mesh] modelName]];
   12008             :                 }
   12009             :                 else
   12010             :                 {
   12011             :                         [trumbleDigrams appendString:@"Some Random Text!"];
   12012             :                 }
   12013             :         }
   12014             :         int i;
   12015             :         for (i = 0; i < PLAYER_MAX_TRUMBLES; i++)
   12016             :         {
   12017             :                 digramchars[0] = ([trumbleDigrams characterAtIndex:i] & 0x007f) | 0x0020;
   12018             :                 digramchars[1] = (([trumbleDigrams characterAtIndex:i + 1] ^ xchar) & 0x007f) | 0x0020;
   12019             :                 xchar = digramchars[0];
   12020             :                 NSString *digramstring = [NSString stringWithCharacters:digramchars length:2];
   12021             :                 [trumble[i] release];
   12022             :                 trumble[i] = [[OOTrumble alloc] initForPlayer:self digram:digramstring];
   12023             :         }
   12024             :         
   12025             :         trumbleCount = 0;
   12026             :         
   12027             :         [self setTrumbleAppetiteAccumulator:0.0f];
   12028             : }
   12029             : 
   12030             : 
   12031             : - (void) addTrumble:(OOTrumble *)papaTrumble
   12032             : {
   12033             :         if (trumbleCount >= PLAYER_MAX_TRUMBLES)
   12034             :         {
   12035             :                 return;
   12036             :         }
   12037             :         OOTrumble *trumblePup = trumble[trumbleCount];
   12038             :         [trumblePup spawnFrom:papaTrumble];
   12039             :         trumbleCount++;
   12040             : }
   12041             : 
   12042             : 
   12043             : - (void) removeTrumble:(OOTrumble *)deadTrumble
   12044             : {
   12045             :         if (trumbleCount <= 0)
   12046             :         {
   12047             :                 return;
   12048             :         }
   12049             :         NSUInteger      trumble_index = NSNotFound;
   12050             :         NSUInteger      i;
   12051             :         
   12052             :         for (i = 0; (trumble_index == NSNotFound)&&(i < trumbleCount); i++)
   12053             :         {
   12054             :                 if (trumble[i] == deadTrumble)
   12055             :                         trumble_index = i;
   12056             :         }
   12057             :         if (trumble_index == NSNotFound)
   12058             :         {
   12059             :                 OOLog(@"trumble.zombie", @"DEBUG can't get rid of inactive trumble %@", deadTrumble);
   12060             :                 return;
   12061             :         }
   12062             :         trumbleCount--; // reduce number of trumbles
   12063             :         trumble[trumble_index] = trumble[trumbleCount]; // swap with the current last trumble
   12064             :         trumble[trumbleCount] = deadTrumble;                            // swap with the current last trumble
   12065             : }
   12066             : 
   12067             : 
   12068             : - (OOTrumble**) trumbleArray
   12069             : {
   12070             :         return trumble;
   12071             : }
   12072             : 
   12073             : 
   12074             : - (NSUInteger) trumbleCount
   12075             : {
   12076             :         return trumbleCount;
   12077             : }
   12078             : 
   12079             : 
   12080             : - (id)trumbleValue
   12081             : {
   12082             :         NSString        *namekey = [NSString stringWithFormat:@"%@-humbletrash", [self commanderName]];
   12083             :         int                     trumbleHash;
   12084             :         
   12085             :         clear_checksum();
   12086             :         [self mungChecksumWithNSString:[self commanderName]];
   12087             :         munge_checksum(credits);
   12088             :         munge_checksum(ship_kills);
   12089             :         trumbleHash = munge_checksum(trumbleCount);
   12090             :         
   12091             :         [[NSUserDefaults standardUserDefaults] setInteger:trumbleHash forKey:namekey];
   12092             :         
   12093             :         int i;
   12094             :         NSMutableArray *trumbleArray = [NSMutableArray arrayWithCapacity:PLAYER_MAX_TRUMBLES];
   12095             :         for (i = 0; i < PLAYER_MAX_TRUMBLES; i++)
   12096             :         {
   12097             :                 [trumbleArray addObject:[trumble[i] dictionary]];
   12098             :         }
   12099             :         
   12100             :         return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInteger:trumbleCount], [NSNumber numberWithInt:trumbleHash], trumbleArray, nil];
   12101             : }
   12102             : 
   12103             : 
   12104             : - (void) setTrumbleValueFrom:(NSObject*) trumbleValue
   12105             : {
   12106             :         BOOL info_failed = NO;
   12107             :         int trumbleHash;
   12108             :         int putativeHash = 0;
   12109             :         int putativeNTrumbles = 0;
   12110             :         NSArray *putativeTrumbleArray = nil;
   12111             :         int i;
   12112             :         NSString *namekey = [NSString stringWithFormat:@"%@-humbletrash", [self commanderName]];
   12113             :         
   12114             :         [self setUpTrumbles];
   12115             :         
   12116             :         if (trumbleValue)
   12117             :         {
   12118             :                 BOOL possible_cheat = NO;
   12119             :                 if (![trumbleValue isKindOfClass:[NSArray class]])
   12120             :                         info_failed = YES;
   12121             :                 else
   12122             :                 {
   12123             :                         NSArray* values = (NSArray*) trumbleValue;
   12124             :                         if ([values count] >= 1)
   12125             :                                 putativeNTrumbles = [values oo_intAtIndex:0];
   12126             :                         if ([values count] >= 2)
   12127             :                                 putativeHash = [values oo_intAtIndex:1];
   12128             :                         if ([values count] >= 3)
   12129             :                                 putativeTrumbleArray = [values oo_arrayAtIndex:2];
   12130             :                 }
   12131             :                 // calculate a hash for the putative values
   12132             :                 clear_checksum();
   12133             :                 [self mungChecksumWithNSString:[self commanderName]];
   12134             :                 munge_checksum(credits);
   12135             :                 munge_checksum(ship_kills);
   12136             :                 trumbleHash = munge_checksum(putativeNTrumbles);
   12137             :                 
   12138             :                 if (putativeHash != trumbleHash)
   12139             :                         info_failed = YES;
   12140             :                 
   12141             :                 if (info_failed)
   12142             :                 {
   12143             :                         OOLog(@"cheat.tentative", @"%@", @"POSSIBLE CHEAT DETECTED");
   12144             :                         possible_cheat = YES;
   12145             :                 }
   12146             :                 
   12147             :                 for (i = 1; (info_failed)&&(i < PLAYER_MAX_TRUMBLES); i++)
   12148             :                 {
   12149             :                         // try to determine trumbleCount from the key in the saved game
   12150             :                         clear_checksum();
   12151             :                         [self mungChecksumWithNSString:[self commanderName]];
   12152             :                         munge_checksum(credits);
   12153             :                         munge_checksum(ship_kills);
   12154             :                         trumbleHash = munge_checksum(i);
   12155             :                         if (putativeHash == trumbleHash)
   12156             :                         {
   12157             :                                 info_failed = NO;
   12158             :                                 putativeNTrumbles = i;
   12159             :                         }
   12160             :                 }
   12161             :                 
   12162             :                 if (possible_cheat && !info_failed)
   12163             :                         OOLog(@"cheat.verified", @"%@", @"CHEAT DEFEATED - that's not the way to get rid of trumbles!");
   12164             :         }
   12165             :         else
   12166             :         // if trumbleValue comes in as nil, then probably someone has toyed with the save file
   12167             :         // by removing the entire trumbles array
   12168             :         {
   12169             :                 OOLog(@"cheat.tentative", @"%@", @"POSSIBLE CHEAT DETECTED");
   12170             :                 info_failed = YES;
   12171             :         }
   12172             :         
   12173             :         if (info_failed && [[NSUserDefaults standardUserDefaults] objectForKey:namekey])
   12174             :         {
   12175             :                 // try to determine trumbleCount from the key in user defaults
   12176             :                 putativeHash = (int)[[NSUserDefaults standardUserDefaults] integerForKey:namekey];
   12177             :                 for (i = 1; (info_failed)&&(i < PLAYER_MAX_TRUMBLES); i++)
   12178             :                 {
   12179             :                         clear_checksum();
   12180             :                         [self mungChecksumWithNSString:[self commanderName]];
   12181             :                         munge_checksum(credits);
   12182             :                         munge_checksum(ship_kills);
   12183             :                         trumbleHash = munge_checksum(i);
   12184             :                         if (putativeHash == trumbleHash)
   12185             :                         {
   12186             :                                 info_failed = NO;
   12187             :                                 putativeNTrumbles = i;
   12188             :                         }
   12189             :                 }
   12190             :                 
   12191             :                 if (!info_failed)
   12192             :                         OOLog(@"cheat.verified", @"%@", @"CHEAT DEFEATED - that's not the way to get rid of trumbles!");
   12193             :         }
   12194             :         // at this stage we've done the best we can to stop cheaters
   12195             :         trumbleCount = putativeNTrumbles;
   12196             : 
   12197             :         if ((putativeTrumbleArray != nil) && ([putativeTrumbleArray count] == PLAYER_MAX_TRUMBLES))
   12198             :         {
   12199             :                 for (i = 0; i < PLAYER_MAX_TRUMBLES; i++)
   12200             :                         [trumble[i] setFromDictionary:[putativeTrumbleArray oo_dictionaryAtIndex:i]];
   12201             :         }
   12202             :         
   12203             :         clear_checksum();
   12204             :         [self mungChecksumWithNSString:[self commanderName]];
   12205             :         munge_checksum(credits);
   12206             :         munge_checksum(ship_kills);
   12207             :         trumbleHash = munge_checksum(trumbleCount);
   12208             :         
   12209             :         [[NSUserDefaults standardUserDefaults]  setInteger:trumbleHash forKey:namekey];
   12210             : }
   12211             : 
   12212             : 
   12213             : - (float) trumbleAppetiteAccumulator
   12214             : {
   12215             :         return _trumbleAppetiteAccumulator;
   12216             : }
   12217             : 
   12218             : 
   12219             : - (void) setTrumbleAppetiteAccumulator:(float)value
   12220             : {
   12221             :         _trumbleAppetiteAccumulator = value;
   12222             : }
   12223             : 
   12224             : 
   12225             : - (void) mungChecksumWithNSString:(NSString *)str
   12226             : {
   12227             :         if (str == nil)  return;
   12228             :         
   12229             :         NSUInteger i, length = [str length];
   12230             :         for (i = 0; i < length; i++)
   12231             :         {
   12232             :                 munge_checksum([str characterAtIndex:i]);
   12233             :         }
   12234             : }
   12235             : 
   12236             : 
   12237             : - (NSString *) screenModeStringForWidth:(unsigned)width height:(unsigned)height refreshRate:(float)refreshRate
   12238             : {
   12239             :         if (0.0f != refreshRate)
   12240             :         {
   12241             :                 return OOExpandKey(@"gameoptions-fullscreen-with-refresh-rate", width, height, refreshRate);
   12242             :         }
   12243             :         else
   12244             :         {
   12245             :                 return OOExpandKey(@"gameoptions-fullscreen", width, height);
   12246             :         }
   12247             : }
   12248             : 
   12249             : 
   12250             : - (void) suppressTargetLost
   12251             : {
   12252             :         suppressTargetLost = YES;
   12253             : }
   12254             : 
   12255             : 
   12256             : - (void) setScoopsActive
   12257             : {
   12258             :         scoopsActive = YES;
   12259             : }
   12260             : 
   12261             : 
   12262             : // override shipentity to stop foundTarget being changed during escape sequence
   12263           0 : - (void) setFoundTarget:(Entity *) targetEntity
   12264             : {
   12265             :         /* Rare, but can happen, e.g. if a Q-mine goes off nearby during
   12266             :          * the sequence */
   12267             :         if ([self status] == STATUS_ESCAPE_SEQUENCE)
   12268             :         {
   12269             :                 return;
   12270             :         }
   12271             :         [_foundTarget release];
   12272             :         _foundTarget = [targetEntity weakRetain];
   12273             : }
   12274             : 
   12275             : 
   12276             : // override shipentity addTarget to implement target_memory
   12277           0 : - (void) addTarget:(Entity *) targetEntity
   12278             : {
   12279             :         if ([self status] != STATUS_IN_FLIGHT && [self status] != STATUS_WITCHSPACE_COUNTDOWN)  return;
   12280             :         if (targetEntity == self)  return;
   12281             :         
   12282             :         [super addTarget:targetEntity];
   12283             :         
   12284             :         if ([targetEntity isWormhole])
   12285             :         {
   12286             :                 assert ([self hasEquipmentItemProviding:@"EQ_WORMHOLE_SCANNER"]);
   12287             :                 [self addScannedWormhole:(WormholeEntity*)targetEntity];
   12288             :         }
   12289             :         // wormholes don't go in target memory
   12290             :         else if ([self hasEquipmentItemProviding:@"EQ_TARGET_MEMORY"] && targetEntity != nil)
   12291             :         {
   12292             :                 OOWeakReference *targetRef = [targetEntity weakSelf];
   12293             :                 NSUInteger i = [target_memory indexOfObject:targetRef];
   12294             :                 // if already in target memory, preserve that and just change the index
   12295             :                 if (i != NSNotFound)
   12296             :                 {
   12297             :                         target_memory_index = i;
   12298             :                 }               
   12299             :                 else
   12300             :                 {
   12301             :                         i = [target_memory indexOfObject:[NSNull null]];
   12302             :                         // find and use a blank space in memory
   12303             :                         if (i != NSNotFound)
   12304             :                         {
   12305             :                                 [target_memory replaceObjectAtIndex:i withObject:targetRef];
   12306             :                                 target_memory_index = i;
   12307             :                         }
   12308             :                         else
   12309             :                         {
   12310             :                                 // use the next memory space
   12311             :                                 target_memory_index = (target_memory_index + 1) % PLAYER_TARGET_MEMORY_SIZE;
   12312             :                                 [target_memory replaceObjectAtIndex:target_memory_index withObject:targetRef];
   12313             :                         }
   12314             :                 }
   12315             :         }
   12316             :         
   12317             :         if (ident_engaged)
   12318             :         {
   12319             :                 [self playIdentLockedOn];
   12320             :                 [self printIdentLockedOnForMissile:NO];
   12321             :         }
   12322             :         else if ([targetEntity isShip] && [self weaponsOnline]) // Only let missiles target-lock onto ships
   12323             :         {
   12324             :                 if ([missile_entity[activeMissile] isMissile])
   12325             :                 {
   12326             :                         missile_status = MISSILE_STATUS_TARGET_LOCKED;
   12327             :                         [missile_entity[activeMissile] addTarget:targetEntity];
   12328             :                         [self playMissileLockedOn];
   12329             :                         [self printIdentLockedOnForMissile:YES];
   12330             :                 }
   12331             :                 else // It's a mine or something
   12332             :                 {
   12333             :                         missile_status = MISSILE_STATUS_ARMED;
   12334             :                         [self playIdentLockedOn];
   12335             :                         [self printIdentLockedOnForMissile:NO];
   12336             :                 }
   12337             :         }
   12338             : }
   12339             : 
   12340             : 
   12341             : - (void) clearTargetMemory
   12342             : {
   12343             :         NSUInteger memoryCount = [target_memory count];
   12344             :         for (NSUInteger i = 0; i < PLAYER_TARGET_MEMORY_SIZE; i++)
   12345             :         {
   12346             :                 if (i < memoryCount)
   12347             :                 {
   12348             :                         [target_memory replaceObjectAtIndex:i withObject:[NSNull null]];
   12349             :                 }
   12350             :                 else
   12351             :                 {
   12352             :                         [target_memory addObject:[NSNull null]];
   12353             :                 }
   12354             :         }
   12355             :         target_memory_index = 0;
   12356             : }
   12357             : 
   12358             : 
   12359             : - (NSMutableArray *) targetMemory
   12360             : {
   12361             :         return target_memory;
   12362             : }
   12363             : 
   12364             : - (BOOL) moveTargetMemoryBy:(NSInteger)delta
   12365             : {
   12366             :         unsigned i = 0;
   12367             :         while (i++ < PLAYER_TARGET_MEMORY_SIZE)      // limit loops
   12368             :         {
   12369             :                 NSInteger idx = (NSInteger)target_memory_index + delta;
   12370             :                 while (idx < 0)  idx += PLAYER_TARGET_MEMORY_SIZE;
   12371             :                 while (idx >= PLAYER_TARGET_MEMORY_SIZE) idx -= PLAYER_TARGET_MEMORY_SIZE;
   12372             :                 target_memory_index = idx;
   12373             : 
   12374             :                 id targ_id = [target_memory objectAtIndex:target_memory_index];
   12375             :                 if ([targ_id isProxy])
   12376             :                 {
   12377             :                         ShipEntity *potential_target = [(OOWeakReference *)targ_id weakRefUnderlyingObject];
   12378             :                 
   12379             :                         if ((potential_target)&&(potential_target->isShip)&&([potential_target isInSpace]))
   12380             :                         {
   12381             :                                 if (potential_target->zero_distance < SCANNER_MAX_RANGE2 && (![potential_target isCloaked]))
   12382             :                                 {
   12383             :                                         [super addTarget:potential_target];
   12384             :                                         if (missile_status != MISSILE_STATUS_SAFE)
   12385             :                                         {
   12386             :                                                 if( [missile_entity[activeMissile] isMissile])
   12387             :                                                 {
   12388             :                                                         [missile_entity[activeMissile] addTarget:potential_target];
   12389             :                                                         missile_status = MISSILE_STATUS_TARGET_LOCKED;
   12390             :                                                         [self printIdentLockedOnForMissile:YES];
   12391             :                                                 }
   12392             :                                                 else
   12393             :                                                 {
   12394             :                                                         missile_status = MISSILE_STATUS_ARMED;
   12395             :                                                         [self playIdentLockedOn];
   12396             :                                                         [self printIdentLockedOnForMissile:NO];
   12397             :                                                 }
   12398             :                                         }
   12399             :                                         else
   12400             :                                         {
   12401             :                                                 ident_engaged = YES;
   12402             :                                                 [self printIdentLockedOnForMissile:NO];
   12403             :                                         }
   12404             :                                         [self playTargetSwitched];
   12405             :                                         return YES;
   12406             :                                 }
   12407             :                         }
   12408             :                         else
   12409             :                         {
   12410             :                                 [target_memory replaceObjectAtIndex:target_memory_index withObject:[NSNull null]];
   12411             :                         }
   12412             :                 }
   12413             :         }
   12414             :         
   12415             :         [self playNoTargetInMemory];
   12416             :         return NO;
   12417             : }
   12418             : 
   12419             : 
   12420             : - (void) printIdentLockedOnForMissile:(BOOL)missile
   12421             : {
   12422             :         if ([self primaryTarget] == nil) return;
   12423             :         
   12424             :         NSString *fmt = missile ? @"missile-locked-onto-target" : @"ident-locked-onto-target";
   12425             :         NSString *target = [[self primaryTarget] identFromShip:self];
   12426             :         [UNIVERSE addMessage:OOExpandKey(fmt, target) forCount:4.5];
   12427             : }
   12428             : 
   12429             : 
   12430             : - (Quaternion) customViewQuaternion
   12431             : {
   12432             :         return customViewQuaternion;
   12433             : }
   12434             : 
   12435             : 
   12436             : - (void) setCustomViewQuaternion:(Quaternion)q
   12437             : {
   12438             :         customViewQuaternion = q;
   12439             :         [self setCustomViewData];
   12440             : }
   12441             : 
   12442             : 
   12443             : - (OOMatrix) customViewMatrix
   12444             : {
   12445             :         return customViewMatrix;
   12446             : }
   12447             : 
   12448             : 
   12449             : - (Vector) customViewOffset
   12450             : {
   12451             :         return customViewOffset;
   12452             : }
   12453             : 
   12454             : 
   12455             : - (void) setCustomViewOffset:(Vector) offset
   12456             : {
   12457             :         customViewOffset = offset;
   12458             : }
   12459             : 
   12460             : 
   12461             : - (Vector) customViewRotationCenter
   12462             : {
   12463             :         return customViewRotationCenter;
   12464             : }
   12465             : 
   12466             : 
   12467             : - (void) setCustomViewRotationCenter:(Vector) center
   12468             : {
   12469             :         customViewRotationCenter = center;
   12470             : }
   12471             : 
   12472             : 
   12473             : - (void) customViewZoomIn:(OOScalar) rate
   12474             : {
   12475             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12476             :         customViewOffset = vector_multiply_scalar(customViewOffset, 1.0/rate);
   12477             :         OOScalar m = magnitude(customViewOffset);
   12478             :         if (m < CUSTOM_VIEW_MAX_ZOOM_IN * collision_radius)
   12479             :         {
   12480             :                 scale_vector(&customViewOffset, CUSTOM_VIEW_MAX_ZOOM_IN * collision_radius / m);
   12481             :         }
   12482             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12483             : }
   12484             : 
   12485             : 
   12486             : - (void) customViewZoomOut:(OOScalar) rate
   12487             : {
   12488             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12489             :         customViewOffset = vector_multiply_scalar(customViewOffset, rate);
   12490             :         OOScalar m = magnitude(customViewOffset);
   12491             :         if (m > CUSTOM_VIEW_MAX_ZOOM_OUT * collision_radius)
   12492             :         {
   12493             :                 scale_vector(&customViewOffset, CUSTOM_VIEW_MAX_ZOOM_OUT * collision_radius / m);
   12494             :         }
   12495             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12496             : }
   12497             : 
   12498             : 
   12499             : - (void) customViewRotateLeft:(OOScalar) angle
   12500             : {
   12501             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12502             :         OOScalar m = magnitude(customViewOffset);
   12503             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewUpVector, -angle);
   12504             :         [self setCustomViewData];
   12505             :         customViewOffset = vector_flip(customViewForwardVector);
   12506             :         scale_vector(&customViewOffset, m / magnitude(customViewOffset));
   12507             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12508             : }
   12509             : 
   12510             : 
   12511             : - (void) customViewRotateRight:(OOScalar) angle
   12512             : {
   12513             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12514             :         OOScalar m = magnitude(customViewOffset);
   12515             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewUpVector, angle);
   12516             :         [self setCustomViewData];
   12517             :         customViewOffset = vector_flip(customViewForwardVector);
   12518             :         scale_vector(&customViewOffset, m / magnitude(customViewOffset));
   12519             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12520             : }
   12521             : 
   12522             : 
   12523             : - (void) customViewRotateUp:(OOScalar) angle
   12524             : {
   12525             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12526             :         OOScalar m = magnitude(customViewOffset);
   12527             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewRightVector, -angle);
   12528             :         [self setCustomViewData];
   12529             :         customViewOffset = vector_flip(customViewForwardVector);
   12530             :         scale_vector(&customViewOffset, m / magnitude(customViewOffset));
   12531             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12532             : }
   12533             : 
   12534             : 
   12535             : - (void) customViewRotateDown:(OOScalar) angle
   12536             : {
   12537             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12538             :         OOScalar m = magnitude(customViewOffset);
   12539             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewRightVector, angle);
   12540             :         [self setCustomViewData];
   12541             :         customViewOffset = vector_flip(customViewForwardVector);
   12542             :         scale_vector(&customViewOffset, m / magnitude(customViewOffset));
   12543             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12544             : }
   12545             : 
   12546             : 
   12547             : - (void) customViewRollRight:(OOScalar) angle
   12548             : {
   12549             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12550             :         OOScalar m = magnitude(customViewOffset);
   12551             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewForwardVector, -angle);
   12552             :         [self setCustomViewData];
   12553             :         customViewOffset = vector_flip(customViewForwardVector);
   12554             :         scale_vector(&customViewOffset, m / magnitude(customViewOffset));
   12555             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12556             : }
   12557             : 
   12558             : 
   12559             : - (void) customViewRollLeft:(OOScalar) angle
   12560             : {
   12561             :         customViewOffset = vector_subtract(customViewOffset, customViewRotationCenter);
   12562             :         OOScalar m = magnitude(customViewOffset);
   12563             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewForwardVector, angle);
   12564             :         [self setCustomViewData];
   12565             :         customViewOffset = vector_flip(customViewForwardVector);
   12566             :         scale_vector(&customViewOffset, m / magnitude(customViewOffset));
   12567             :         customViewOffset = vector_add(customViewOffset, customViewRotationCenter);
   12568             : }
   12569             : 
   12570             : 
   12571             : - (void) customViewPanUp:(OOScalar) angle
   12572             : {
   12573             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewRightVector, angle);
   12574             :         [self setCustomViewData];
   12575             :         customViewRotationCenter = vector_subtract(customViewOffset, vector_multiply_scalar(customViewForwardVector, dot_product(customViewOffset, customViewForwardVector)));
   12576             : }
   12577             : 
   12578             : 
   12579             : - (void) customViewPanDown:(OOScalar) angle
   12580             : {
   12581             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewRightVector, -angle);
   12582             :         [self setCustomViewData];
   12583             :         customViewRotationCenter = vector_subtract(customViewOffset, vector_multiply_scalar(customViewForwardVector, dot_product(customViewOffset, customViewForwardVector)));
   12584             : }
   12585             : 
   12586             : 
   12587             : - (void) customViewPanLeft:(OOScalar) angle
   12588             : {
   12589             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewUpVector, angle);
   12590             :         [self setCustomViewData];
   12591             :         customViewRotationCenter = vector_subtract(customViewOffset, vector_multiply_scalar(customViewForwardVector, dot_product(customViewOffset, customViewForwardVector)));
   12592             : }
   12593             : 
   12594             : 
   12595             : - (void) customViewPanRight:(OOScalar) angle
   12596             : {
   12597             :         quaternion_rotate_about_axis(&customViewQuaternion, customViewUpVector, -angle);
   12598             :         [self setCustomViewData];
   12599             :         customViewRotationCenter = vector_subtract(customViewOffset, vector_multiply_scalar(customViewForwardVector, dot_product(customViewOffset, customViewForwardVector)));
   12600             : }
   12601             : 
   12602             : 
   12603             : - (Vector) customViewForwardVector
   12604             : {
   12605             :         return customViewForwardVector;
   12606             : }
   12607             : 
   12608             : 
   12609             : - (Vector) customViewUpVector
   12610             : {
   12611             :         return customViewUpVector;
   12612             : }
   12613             : 
   12614             : 
   12615             : - (Vector) customViewRightVector
   12616             : {
   12617             :         return customViewRightVector;
   12618             : }
   12619             : 
   12620             : 
   12621             : - (NSString *) customViewDescription
   12622             : {
   12623             :         return customViewDescription;
   12624             : }
   12625             : 
   12626             : 
   12627             : - (void) resetCustomView
   12628             : {
   12629             :         [self setCustomViewDataFromDictionary:[_customViews oo_dictionaryAtIndex:_customViewIndex] withScaling:NO];
   12630             : }
   12631             : 
   12632             : 
   12633             : - (void) setCustomViewData
   12634             : {
   12635             :         customViewRightVector = vector_right_from_quaternion(customViewQuaternion);
   12636             :         customViewUpVector = vector_up_from_quaternion(customViewQuaternion);
   12637             :         customViewForwardVector = vector_forward_from_quaternion(customViewQuaternion);
   12638             :         
   12639             :         Quaternion q1 = customViewQuaternion;
   12640             :         q1.w = -q1.w;
   12641             :         customViewMatrix = OOMatrixForQuaternionRotation(q1);
   12642             : }
   12643             : 
   12644             : - (void) setCustomViewDataFromDictionary:(NSDictionary *)viewDict withScaling:(BOOL)withScaling
   12645             : {
   12646             :         customViewMatrix = kIdentityMatrix;
   12647             :         customViewOffset = kZeroVector;
   12648             :         if (viewDict == nil)  return;
   12649             :         
   12650             :         customViewQuaternion = [viewDict oo_quaternionForKey:@"view_orientation"];
   12651             :         [self setCustomViewData];
   12652             :         
   12653             :         // easier to do the multiplication at this point than at load time
   12654             :         if (withScaling)
   12655             :         {
   12656             :                 customViewOffset = vector_multiply_scalar([viewDict oo_vectorForKey:@"view_position"],_scaleFactor);
   12657             :         }
   12658             :         else
   12659             :         {
   12660             :                 // but don't do this when the custom view is set through JS
   12661             :                 customViewOffset = [viewDict oo_vectorForKey:@"view_position"];
   12662             :         }
   12663             :         customViewRotationCenter = vector_subtract(customViewOffset, vector_multiply_scalar(customViewForwardVector, dot_product(customViewOffset, customViewForwardVector)));
   12664             :         customViewDescription = [viewDict oo_stringForKey:@"view_description"];
   12665             :         
   12666             :         NSString *facing = [[viewDict oo_stringForKey:@"weapon_facing"] lowercaseString];
   12667             :         if ([facing isEqual:@"aft"])
   12668             :         {
   12669             :                 currentWeaponFacing = WEAPON_FACING_AFT;
   12670             :         }
   12671             :         else if ([facing isEqual:@"port"])
   12672             :         {
   12673             :                 currentWeaponFacing = WEAPON_FACING_PORT;
   12674             :         }
   12675             :         else if ([facing isEqual:@"starboard"])
   12676             :         {
   12677             :                 currentWeaponFacing = WEAPON_FACING_STARBOARD;
   12678             :         }
   12679             :         else if ([facing isEqual:@"forward"])
   12680             :         {
   12681             :                 currentWeaponFacing = WEAPON_FACING_FORWARD;
   12682             :         }
   12683             :         // if the weapon facing is unset / unknown, 
   12684             :         // don't change current weapon facing!
   12685             : }
   12686             : 
   12687             : 
   12688             : - (BOOL) showInfoFlag
   12689             : {
   12690             :         return show_info_flag;
   12691             : }
   12692             : 
   12693             : 
   12694             : - (NSDictionary *) missionOverlayDescriptor
   12695             : {
   12696             :         return _missionOverlayDescriptor;
   12697             : }
   12698             : 
   12699             : 
   12700             : - (NSDictionary *) missionOverlayDescriptorOrDefault
   12701             : {
   12702             :         NSDictionary *result = [self missionOverlayDescriptor];
   12703             :         if (result == nil)
   12704             :         {
   12705             :                 if ([[self missionTitle] length] == 0)
   12706             :                 {
   12707             :                         result = [UNIVERSE screenTextureDescriptorForKey:@"mission_overlay_no_title"];
   12708             :                 }
   12709             :                 else
   12710             :                 {
   12711             :                         result = [UNIVERSE screenTextureDescriptorForKey:@"mission_overlay_with_title"];
   12712             :                 }
   12713             :         }
   12714             :         
   12715             :         return result;
   12716             : }
   12717             : 
   12718             : 
   12719             : - (void) setMissionOverlayDescriptor:(NSDictionary *)descriptor
   12720             : {
   12721             :         if (descriptor != _missionOverlayDescriptor)
   12722             :         {
   12723             :                 [_missionOverlayDescriptor autorelease];
   12724             :                 _missionOverlayDescriptor = [descriptor copy];
   12725             :         }
   12726             : }
   12727             : 
   12728             : 
   12729             : - (NSDictionary *) missionBackgroundDescriptor
   12730             : {
   12731             :         return _missionBackgroundDescriptor;
   12732             : }
   12733             : 
   12734             : 
   12735             : - (NSDictionary *) missionBackgroundDescriptorOrDefault
   12736             : {
   12737             :         NSDictionary *result = [self missionBackgroundDescriptor];
   12738             :         if (result == nil)
   12739             :         {
   12740             :                 result = [UNIVERSE screenTextureDescriptorForKey:@"mission"];
   12741             :         }
   12742             :         
   12743             :         return result;
   12744             : }
   12745             : 
   12746             : 
   12747             : - (void) setMissionBackgroundDescriptor:(NSDictionary *)descriptor
   12748             : {
   12749             :         if (descriptor != _missionBackgroundDescriptor)
   12750             :         {
   12751             :                 [_missionBackgroundDescriptor autorelease];
   12752             :                 _missionBackgroundDescriptor = [descriptor copy];
   12753             :         }
   12754             : }
   12755             : 
   12756             : 
   12757             : - (OOGUIBackgroundSpecial) missionBackgroundSpecial
   12758             : {
   12759             :         return _missionBackgroundSpecial;
   12760             : }
   12761             : 
   12762             : 
   12763             : - (void) setMissionBackgroundSpecial:(NSString *)special
   12764             : {
   12765             :         if (special == nil) {
   12766             :                 _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_NONE;
   12767             :         }
   12768             :         else if ([special isEqualToString:@"SHORT_RANGE_CHART"])
   12769             :         {
   12770             :                 _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_SHORT;
   12771             :         }
   12772             :         else if ([special isEqualToString:@"SHORT_RANGE_CHART_SHORTEST"])
   12773             :         {
   12774             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
   12775             :                 {
   12776             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_SHORT_ANA_SHORTEST;
   12777             :                 }
   12778             :                 else
   12779             :                 {
   12780             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_SHORT;
   12781             :                 }
   12782             :         }
   12783             :         else if ([special isEqualToString:@"SHORT_RANGE_CHART_QUICKEST"])
   12784             :         {
   12785             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
   12786             :                 {
   12787             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_SHORT_ANA_QUICKEST;
   12788             :                 }
   12789             :                 else
   12790             :                 {
   12791             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_SHORT;
   12792             :                 }
   12793             :         } 
   12794             :         else if ([special isEqualToString:@"CUSTOM_CHART"])
   12795             :         {
   12796             :                 _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_CUSTOM;
   12797             :         }
   12798             :         else if ([special isEqualToString:@"CUSTOM_CHART_SHORTEST"])
   12799             :         {
   12800             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
   12801             :                 {
   12802             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_CUSTOM_ANA_SHORTEST;
   12803             :                 }
   12804             :                 else
   12805             :                 {
   12806             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_CUSTOM;
   12807             :                 }
   12808             :         }
   12809             :         else if ([special isEqualToString:@"CUSTOM_CHART_QUICKEST"])
   12810             :         {
   12811             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
   12812             :                 {
   12813             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_CUSTOM_ANA_QUICKEST;
   12814             :                 }
   12815             :                 else
   12816             :                 {
   12817             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_CUSTOM;
   12818             :                 }
   12819             :         } 
   12820             :         else if ([special isEqualToString:@"LONG_RANGE_CHART"])
   12821             :         {
   12822             :                 _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG;
   12823             :         }
   12824             :         else if ([special isEqualToString:@"LONG_RANGE_CHART_SHORTEST"])
   12825             :         {
   12826             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
   12827             :                 {
   12828             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG_ANA_SHORTEST;
   12829             :                 }
   12830             :                 else
   12831             :                 {
   12832             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG;
   12833             :                 }
   12834             :         }
   12835             :         else if ([special isEqualToString:@"LONG_RANGE_CHART_QUICKEST"])
   12836             :         {
   12837             :                 if ([self hasEquipmentItemProviding:@"EQ_ADVANCED_NAVIGATIONAL_ARRAY"])
   12838             :                 {
   12839             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG_ANA_QUICKEST;
   12840             :                 }
   12841             :                 else
   12842             :                 {
   12843             :                         _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_LONG;
   12844             :                 }
   12845             :         } 
   12846             :         else 
   12847             :         {
   12848             :                 _missionBackgroundSpecial = GUI_BACKGROUND_SPECIAL_NONE;
   12849             :         }
   12850             : }
   12851             : 
   12852             : 
   12853             : - (void) setMissionExitScreen:(OOGUIScreenID)screen
   12854             : {
   12855             :         _missionExitScreen = screen;
   12856             : }
   12857             : 
   12858             : 
   12859             : - (OOGUIScreenID) missionExitScreen
   12860             : {
   12861             :         return _missionExitScreen;
   12862             : }
   12863             : 
   12864             : 
   12865             : - (NSDictionary *) equipScreenBackgroundDescriptor
   12866             : {
   12867             :         return _equipScreenBackgroundDescriptor;
   12868             : }
   12869             : 
   12870             : 
   12871             : - (void) setEquipScreenBackgroundDescriptor:(NSDictionary *)descriptor
   12872             : {
   12873             :         if (descriptor != _equipScreenBackgroundDescriptor)
   12874             :         {
   12875             :                 [_equipScreenBackgroundDescriptor autorelease];
   12876             :                 _equipScreenBackgroundDescriptor = [descriptor copy];
   12877             :         }
   12878             : }
   12879             : 
   12880             : 
   12881             : - (BOOL) scriptsLoaded
   12882             : {
   12883             :         return worldScripts != nil && [worldScripts count] > 0;
   12884             : }
   12885             : 
   12886             : 
   12887             : - (NSArray *) worldScriptNames
   12888             : {
   12889             :         return [worldScripts allKeys];
   12890             : }
   12891             : 
   12892             : 
   12893             : - (NSDictionary *) worldScriptsByName
   12894             : {
   12895             :         return [[worldScripts copy] autorelease];
   12896             : }
   12897             : 
   12898             : 
   12899             : - (OOScript *) commodityScriptNamed:(NSString *)scriptName
   12900             : {
   12901             :         if (scriptName == nil)
   12902             :         {
   12903             :                 return nil;
   12904             :         }
   12905             :         OOScript *cscript = nil;
   12906             :         if ((cscript = [commodityScripts objectForKey:scriptName]))
   12907             :         {
   12908             :                 return cscript;
   12909             :         }
   12910             :         cscript = [OOScript jsScriptFromFileNamed:scriptName properties:nil];
   12911             :         if (cscript != nil)
   12912             :         {
   12913             :                 // storing it in here retains it
   12914             :                 [commodityScripts setObject:cscript forKey:scriptName];
   12915             :         }
   12916             :         else
   12917             :         {
   12918             :                 OOLog(@"script.commodityScript.load",@"Could not load script %@",scriptName);
   12919             :         }
   12920             :         return cscript;
   12921             : }
   12922             : 
   12923             : 
   12924           0 : - (void) doScriptEvent:(jsid)message inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc
   12925             : {
   12926             :         [super doScriptEvent:message inContext:context withArguments:argv count:argc];
   12927             :         [self doWorldScriptEvent:message inContext:context withArguments:argv count:argc timeLimit:0.0];
   12928             : }
   12929             : 
   12930             : 
   12931             : - (BOOL) doWorldEventUntilMissionScreen:(jsid)message
   12932             : {
   12933             :         NSEnumerator    *scriptEnum = [worldScripts objectEnumerator];
   12934             :         OOScript                *theScript;
   12935             : 
   12936             :         // Check for the presence of report messages first.
   12937             :         if (gui_screen != GUI_SCREEN_MISSION && [dockingReport length] > 0 && [self isDocked] && ![[self dockedStation] suppressArrivalReports])
   12938             :         {
   12939             :                 [self setGuiToDockingReportScreen];     // go here instead!
   12940             :                 [[UNIVERSE messageGUI] clear];
   12941             :                 return YES;
   12942             :         }
   12943             :         
   12944             :         JSContext *context = OOJSAcquireContext();
   12945             :         while ((theScript = [scriptEnum nextObject]) && gui_screen != GUI_SCREEN_MISSION && [self isDocked])
   12946             :         {
   12947             :                 [theScript callMethod:message inContext:context withArguments:NULL count:0 result:NULL];
   12948             :         }
   12949             :         OOJSRelinquishContext(context);
   12950             :         
   12951             :         if (gui_screen == GUI_SCREEN_MISSION)
   12952             :         {
   12953             :                 // remove any comms/console messages from the screen!
   12954             :                 [[UNIVERSE messageGUI] clear];
   12955             :                 return YES;
   12956             :         }
   12957             :         
   12958             :         return NO;
   12959             : }
   12960             : 
   12961             : 
   12962             : - (void) doWorldScriptEvent:(jsid)message inContext:(JSContext *)context withArguments:(jsval *)argv count:(uintN)argc timeLimit:(OOTimeDelta)limit
   12963             : {
   12964             :         NSParameterAssert(context != NULL && JS_IsInRequest(context));
   12965             :         
   12966             :         NSEnumerator                    *scriptEnum = nil;
   12967             :         OOScript                                *theScript = nil;
   12968             :         
   12969             :         for (scriptEnum = [worldScripts objectEnumerator]; (theScript = [scriptEnum nextObject]); )
   12970             :         {
   12971             :                 OOJSStartTimeLimiterWithTimeLimit(limit);
   12972             :                 [theScript callMethod:message inContext:context withArguments:argv count:argc result:NULL];
   12973             :                 OOJSStopTimeLimiter();
   12974             :         }
   12975             : }
   12976             : 
   12977             : 
   12978             : - (void) setGalacticHyperspaceBehaviour:(OOGalacticHyperspaceBehaviour)inBehaviour
   12979             : {
   12980             :         if (GALACTIC_HYPERSPACE_BEHAVIOUR_UNKNOWN < inBehaviour && inBehaviour <= GALACTIC_HYPERSPACE_MAX)
   12981             :         {
   12982             :                 galacticHyperspaceBehaviour = inBehaviour;
   12983             :         }
   12984             : }
   12985             : 
   12986             : 
   12987             : - (OOGalacticHyperspaceBehaviour) galacticHyperspaceBehaviour
   12988             : {
   12989             :         return galacticHyperspaceBehaviour;
   12990             : }
   12991             : 
   12992             : 
   12993             : - (void) setGalacticHyperspaceFixedCoords:(NSPoint)point
   12994             : {
   12995             :         return [self setGalacticHyperspaceFixedCoordsX:OOClamp_0_max_f(round(point.x), 255.0f) y:OOClamp_0_max_f(round(point.y), 255.0f)];
   12996             : }
   12997             : 
   12998             : 
   12999             : - (void) setGalacticHyperspaceFixedCoordsX:(unsigned char)x y:(unsigned char)y
   13000             : {
   13001             :         galacticHyperspaceFixedCoords.x = x;
   13002             :         galacticHyperspaceFixedCoords.y = y;
   13003             : }
   13004             : 
   13005             : 
   13006             : - (NSPoint) galacticHyperspaceFixedCoords
   13007             : {
   13008             :         return galacticHyperspaceFixedCoords;
   13009             : }
   13010             : 
   13011             : 
   13012             : - (void) setWitchspaceCountdown:(int)spin_time
   13013             : {
   13014             :         witchspaceCountdown = spin_time;
   13015             : }
   13016             : 
   13017             : - (OOLongRangeChartMode) longRangeChartMode
   13018             : {
   13019             :         return longRangeChartMode;
   13020             : }
   13021             : 
   13022             : 
   13023             : - (void) setLongRangeChartMode:(OOLongRangeChartMode) mode
   13024             : {
   13025             :         longRangeChartMode = mode;
   13026             : }
   13027             : 
   13028             : 
   13029             : - (BOOL) scoopOverride
   13030             : {
   13031             :         return scoopOverride;
   13032             : }
   13033             : 
   13034             : 
   13035             : - (void) setScoopOverride:(BOOL)newValue
   13036             : {
   13037             :         scoopOverride = !!newValue;
   13038             :         if (scoopOverride)  [self setScoopsActive];
   13039             : }
   13040             : 
   13041             : 
   13042             : #if MASS_DEPENDENT_FUEL_PRICES
   13043           0 : - (GLfloat) fuelChargeRate
   13044             : {
   13045             :         GLfloat         rate = 1.0; // Standard charge rate.
   13046             :         
   13047             :         rate = [super fuelChargeRate];
   13048             :         
   13049             :         // Experimental: the state of repair affects the fuel charge rate - more fuel needed for jumps, etc... 
   13050             :         if (EXPECT(ship_trade_in_factor <= 90 && ship_trade_in_factor >= 75))
   13051             :         {
   13052             :                 rate *= 2.0 - (ship_trade_in_factor / 100); // between 1.1x and 1.25x
   13053             :                 //OOLog(@"fuelPrices", @"\"%@\" - repair status: %d%%, adjusted rate to:%.2f)", [self shipDataKey], ship_trade_in_factor, rate);
   13054             :         }
   13055             : 
   13056             :         return rate;
   13057             : }
   13058             : #endif
   13059             : 
   13060             : 
   13061             : - (void) setDockTarget:(ShipEntity *)entity
   13062             : {
   13063             : if ([entity isStation]) _dockTarget = [entity universalID];
   13064             : else _dockTarget = NO_TARGET;
   13065             :         //_dockTarget = [entity isStation] ? [entity universalID]: NO_TARGET;
   13066             : }
   13067             : 
   13068             : 
   13069             : - (NSString *) jumpCause
   13070             : { 
   13071             :         return _jumpCause;
   13072             : }
   13073             : 
   13074             : 
   13075             : - (void) setJumpCause:(NSString *)value
   13076             : {
   13077             :         NSParameterAssert(value != nil);
   13078             :         [_jumpCause autorelease];
   13079             :         _jumpCause = [value copy];
   13080             : }
   13081             : 
   13082             : 
   13083             : - (NSString *) commanderName
   13084             : {
   13085             :         return _commanderName;
   13086             : }
   13087             : 
   13088             : 
   13089             : - (NSString *) lastsaveName
   13090             : {
   13091             :         return _lastsaveName;
   13092             : }
   13093             : 
   13094             : 
   13095             : - (void) setCommanderName:(NSString *)value
   13096             : {
   13097             :         NSParameterAssert(value != nil);
   13098             :         [_commanderName autorelease];
   13099             :         _commanderName = [value copy];
   13100             : }
   13101             : 
   13102             : 
   13103             : - (void) setLastsaveName:(NSString *)value
   13104             : {
   13105             :         NSParameterAssert(value != nil);
   13106             :         [_lastsaveName autorelease];
   13107             :         _lastsaveName = [value copy];
   13108             : }
   13109             : 
   13110             : 
   13111             : - (BOOL) isDocked
   13112             : {
   13113             :         BOOL isDockedStatus = NO;
   13114             :         
   13115             :         switch ([self status])
   13116             :         {
   13117             :                 case STATUS_DOCKED:
   13118             :                 case STATUS_DOCKING:
   13119             :         case STATUS_START_GAME:
   13120             :             isDockedStatus = YES;
   13121             :             break;   
   13122             :                         // special case - can be either docked or not, so avoid safety check below
   13123             :                 case STATUS_RESTART_GAME:
   13124             :                         return NO;
   13125             :                 case STATUS_EFFECT:
   13126             :                 case STATUS_ACTIVE:
   13127             :                 case STATUS_COCKPIT_DISPLAY:
   13128             :                 case STATUS_TEST:
   13129             :                 case STATUS_INACTIVE:
   13130             :                 case STATUS_DEAD:
   13131             :                 case STATUS_IN_FLIGHT:
   13132             :                 case STATUS_AUTOPILOT_ENGAGED:
   13133             :                 case STATUS_LAUNCHING:
   13134             :                 case STATUS_WITCHSPACE_COUNTDOWN:
   13135             :                 case STATUS_ENTERING_WITCHSPACE:
   13136             :                 case STATUS_EXITING_WITCHSPACE:
   13137             :                 case STATUS_ESCAPE_SEQUENCE:
   13138             :                 case STATUS_IN_HOLD:
   13139             :                 case STATUS_BEING_SCOOPED:
   13140             :                 case STATUS_HANDLING_ERROR:
   13141             :             break;
   13142             :                 //no default, so that we get notified by the compiler if something is missing
   13143             :         }
   13144             :         
   13145             : #ifndef NDEBUG
   13146             :         // Sanity check
   13147             :         if (isDockedStatus)
   13148             :         {
   13149             :                 if ([self dockedStation] == nil)
   13150             :                 {
   13151             :                         //there are a number of possible current statuses, not just STATUS_DOCKED
   13152             :                         OOLogERR(kOOLogInconsistentState, @"status is %@, but dockedStation is nil; treating as not docked. %@", OOStringFromEntityStatus([self status]), @"This is an internal error, please report it.");
   13153             :                         [self setStatus:STATUS_IN_FLIGHT];
   13154             :                         isDockedStatus = NO;
   13155             :                 }
   13156             :         }
   13157             :         else
   13158             :         {
   13159             :                 if ([self dockedStation] != nil && [self status] != STATUS_LAUNCHING)
   13160             :                 {
   13161             :                         OOLogERR(kOOLogInconsistentState, @"status is %@, but dockedStation is not nil; treating as docked. %@", OOStringFromEntityStatus([self status]), @"This is an internal error, please report it.");
   13162             :                         [self setStatus:STATUS_DOCKED];
   13163             :                         isDockedStatus = YES;
   13164             :                 }
   13165             :         }
   13166             : #endif
   13167             :         
   13168             :         return isDockedStatus;
   13169             : }
   13170             : 
   13171             : 
   13172             : - (BOOL)clearedToDock
   13173             : {
   13174             :         return dockingClearanceStatus > DOCKING_CLEARANCE_STATUS_REQUESTED || dockingClearanceStatus == DOCKING_CLEARANCE_STATUS_NOT_REQUIRED;
   13175             : }
   13176             : 
   13177             : 
   13178             : - (void)setDockingClearanceStatus:(OODockingClearanceStatus)newValue
   13179             : {
   13180             :         dockingClearanceStatus = newValue;
   13181             :         if (dockingClearanceStatus == DOCKING_CLEARANCE_STATUS_NONE)
   13182             :         {
   13183             :                 targetDockStation = nil;
   13184             :         }
   13185             :         else if (dockingClearanceStatus == DOCKING_CLEARANCE_STATUS_REQUESTED || dockingClearanceStatus == DOCKING_CLEARANCE_STATUS_NOT_REQUIRED)
   13186             :         {
   13187             :                 if ([[self primaryTarget] isStation])
   13188             :                 {
   13189             :                         targetDockStation = [self primaryTarget];
   13190             :                 }
   13191             :                 else
   13192             :                 {
   13193             :                         OOLog(@"player.badDockingTarget", @"Attempt to dock at %@.", [self primaryTarget]);
   13194             :                         targetDockStation = nil;
   13195             :                         dockingClearanceStatus = DOCKING_CLEARANCE_STATUS_NONE;
   13196             :                 }
   13197             :         }
   13198             : }
   13199             : 
   13200             : - (OODockingClearanceStatus)getDockingClearanceStatus
   13201             : {
   13202             :         return dockingClearanceStatus;
   13203             : }
   13204             : 
   13205             : 
   13206             : - (void)penaltyForUnauthorizedDocking
   13207             : {
   13208             :         OOCreditsQuantity       amountToPay = 0;
   13209             :         OOCreditsQuantity       calculatedFine = credits * 0.05;
   13210             :         OOCreditsQuantity       maximumFine = 50000ULL;
   13211             :         
   13212             :         if ([self clearedToDock])
   13213             :                 return;
   13214             :                 
   13215             :         amountToPay = MIN(maximumFine, calculatedFine);
   13216             :         credits -= amountToPay;
   13217             :         [self addMessageToReport:[NSString stringWithFormat:DESC(@"station-docking-clearance-fined-@-cr"), OOCredits(amountToPay)]];
   13218             : }
   13219             : 
   13220             : 
   13221             : //
   13222             : // Wormhole Scanner support functions
   13223             : //
   13224             : - (void)addScannedWormhole:(WormholeEntity*)whole
   13225             : {
   13226             :         assert(scannedWormholes != nil);
   13227             :         assert(whole != nil);
   13228             :         
   13229             :         // Only add if we don't have it already!
   13230             :         NSEnumerator *wormholes = [scannedWormholes objectEnumerator];
   13231             :         WormholeEntity *wh = nil;
   13232             :         while ((wh = [wormholes nextObject]))
   13233             :         {
   13234             :                 if (wh == whole)  return;
   13235             :         }
   13236             :         [whole setScannedAt:[self clockTimeAdjusted]];
   13237             :         [scannedWormholes addObject:whole];
   13238             : }
   13239             : 
   13240             : // Checks through our array of wormholes for any which have expired
   13241             : // If it is in the current system, spawn ships
   13242             : // Else remove it
   13243           0 : - (void)updateWormholes
   13244             : {
   13245             :         assert(scannedWormholes != nil);
   13246             :         
   13247             :         if ([scannedWormholes count] == 0)
   13248             :                 return;
   13249             : 
   13250             :         double now = [self clockTimeAdjusted];
   13251             : 
   13252             :         NSMutableArray * savedWormholes = [[NSMutableArray alloc] initWithCapacity:[scannedWormholes count]];
   13253             :         NSEnumerator * wormholes = [scannedWormholes objectEnumerator];
   13254             :         WormholeEntity *wh;
   13255             : 
   13256             :         while ((wh = (WormholeEntity*)[wormholes nextObject]))
   13257             :         {
   13258             :                 // TODO: Start drawing wormhole exit a few seconds before the first
   13259             :                 //       ship is disgorged.
   13260             :                 if ([wh arrivalTime] > now)
   13261             :                 {
   13262             :                         [savedWormholes addObject:wh];
   13263             :                 }
   13264             :                 else if (NSEqualPoints(galaxy_coordinates, [wh destinationCoordinates]))
   13265             :                 {
   13266             :                         [wh disgorgeShips];
   13267             :                         if ([[wh shipsInTransit] count] > 0)
   13268             :                         {
   13269             :                                 [savedWormholes addObject:wh];
   13270             :                         }
   13271             :                 }
   13272             :                 // Else wormhole has expired in another system, let it expire
   13273             :         }
   13274             : 
   13275             :         [scannedWormholes release];
   13276             :         scannedWormholes = savedWormholes;
   13277             : }
   13278             : 
   13279             : 
   13280             : - (NSArray *) scannedWormholes
   13281             : {
   13282             :         return [NSArray arrayWithArray:scannedWormholes];
   13283             : }
   13284             : 
   13285             : 
   13286             : - (void) initialiseMissionDestinations:(NSDictionary *)destinations andLegacy:(NSArray *)legacy
   13287             : {
   13288             :         NSEnumerator                            *keyEnum = nil;
   13289             :         NSString                                        *key = nil;
   13290             :         id                                                      value = nil;
   13291             : 
   13292             :         /* same need to make inner objects mutable as in localPlanetInfoOverrides */
   13293             : 
   13294             :         [missionDestinations release];
   13295             :         missionDestinations = [[NSMutableDictionary alloc] init];
   13296             : 
   13297             :         for (keyEnum = [destinations keyEnumerator]; (key = [keyEnum nextObject]); )
   13298             :         {
   13299             :                 value = [destinations objectForKey:key];
   13300             :                 if (value != nil)
   13301             :                 {
   13302             :                         if ([value isKindOfClass:[NSDictionary class]])
   13303             :                         {
   13304             :                                 value = [value mutableCopy];
   13305             :                                 [missionDestinations setObject:value forKey:key];
   13306             :                                 [value release];
   13307             :                         }
   13308             :                 }
   13309             :         }
   13310             :         
   13311             :         if (legacy != nil)
   13312             :         {
   13313             :                 OOSystemID dest;
   13314             :                 NSNumber *legacyMarker;
   13315             :                 for (keyEnum = [legacy objectEnumerator]; (legacyMarker = [keyEnum nextObject]); )
   13316             :                 {
   13317             :                         dest = [legacyMarker intValue];
   13318             :                         [self addMissionDestinationMarker:[self defaultMarker:dest]];
   13319             :                 }
   13320             :         }
   13321             : 
   13322             : }
   13323             : 
   13324             : 
   13325             : - (NSString *)markerKey:(NSDictionary *)marker
   13326             : {
   13327             :         return [NSString stringWithFormat:@"%d-%@",[marker oo_intForKey:@"system"], [marker oo_stringForKey:@"name"]];
   13328             : }
   13329             : 
   13330             : 
   13331             : - (void) addMissionDestinationMarker:(NSDictionary *)marker
   13332             : {
   13333             :         NSDictionary *validated = [self validatedMarker:marker];
   13334             :         if (validated == nil) 
   13335             :         {
   13336             :                 return;
   13337             :         }
   13338             :         
   13339             :         [missionDestinations setObject:validated forKey:[self markerKey:validated]];
   13340             : }
   13341             : 
   13342             : 
   13343             : - (BOOL) removeMissionDestinationMarker:(NSDictionary *)marker
   13344             : {
   13345             :         NSDictionary *validated = [self validatedMarker:marker];
   13346             :         if (validated == nil) 
   13347             :         {
   13348             :                 return NO;
   13349             :         }
   13350             :         BOOL result = NO;
   13351             :         if ([missionDestinations objectForKey:[self markerKey:validated]] != nil) {
   13352             :                 result = YES;
   13353             :         }
   13354             :         [missionDestinations removeObjectForKey:[self markerKey:validated]];
   13355             :         return result;
   13356             : }
   13357             : 
   13358             : 
   13359             : - (NSMutableDictionary*) getMissionDestinations
   13360             : {
   13361             :         return missionDestinations;
   13362             : }
   13363             : 
   13364             : 
   13365             : - (NSMutableDictionary*) shipyardRecord
   13366             : {
   13367             :         return shipyard_record;
   13368             : }
   13369             : 
   13370             : 
   13371             : - (void) setLastShot:(NSArray *)shot
   13372             : {
   13373             :         lastShot = [shot retain]; 
   13374             : }
   13375             : 
   13376             : 
   13377             : - (void) clearExtraMissionKeys
   13378             : {
   13379             :         [extraMissionKeys release];
   13380             :         extraMissionKeys = nil;
   13381             : }
   13382             : 
   13383             : 
   13384             : - (void) setExtraMissionKeys:(NSDictionary *)keys
   13385             : {
   13386             :         NSString *key = nil;
   13387             :         NSMutableDictionary *final = [[NSMutableDictionary alloc] init];
   13388             :         foreach (key, [keys allKeys])
   13389             :         {
   13390             :                 [final setObject:[self processKeyCode:[keys oo_arrayForKey:key]] forKey:key];
   13391             :         }
   13392             :         extraMissionKeys = [final copy];
   13393             :         [final release];
   13394             : }
   13395             : 
   13396             : 
   13397             : - (void) clearExtraGuiScreenKeys:(OOGUIScreenID)gui key:(NSString *)key
   13398             : {
   13399             :         NSMutableArray *keydefs = [extraGuiScreenKeys objectForKey:[NSString stringWithFormat:@"%d",gui]];
   13400             :         NSInteger i = [keydefs count];
   13401             :         NSDictionary *def = nil;
   13402             :         while (i--) 
   13403             :         {
   13404             :                 def = [keydefs objectAtIndex:i];
   13405             :                 if (def && [[def oo_stringForKey:@"name"] isEqualToString:key]) 
   13406             :                 {
   13407             :                         [keydefs removeObjectAtIndex:i];
   13408             :                         break;
   13409             :                 }
   13410             :         }
   13411             :         // do we have to put the array back, or does the reference update the source?
   13412             : }
   13413             : 
   13414             : 
   13415             : - (BOOL) setExtraGuiScreenKeys:(OOGUIScreenID)gui definition:(OOJSGuiScreenKeyDefinition *)definition
   13416             : {
   13417             :         // process all the keys in the definition
   13418             :         BOOL result = YES;
   13419             :         NSMutableArray *newarray = nil;
   13420             :         NSString *key = nil;    
   13421             :         NSMutableDictionary *final = [[NSMutableDictionary alloc] init];
   13422             :         NSDictionary *keys = [definition registerKeys];
   13423             :         NSMutableArray *checklist = [[NSMutableArray alloc] init];
   13424             : 
   13425             :         foreach (key, [keys allKeys])
   13426             :         {
   13427             :                 NSArray *item = [self processKeyCode:[keys oo_arrayForKey:key]];
   13428             :                 [checklist addObject:item];
   13429             :                 [final setObject:item forKey:key];
   13430             :         }
   13431             :         [definition setRegisterKeys:[final copy]];
   13432             :         [final release];
   13433             : 
   13434             :         /// create the dictionary, if it doesn't already exist
   13435             :         if (!extraGuiScreenKeys) 
   13436             :         {
   13437             :                 extraGuiScreenKeys = [[NSMutableDictionary alloc] init];
   13438             :         }
   13439             : 
   13440             :         if (![extraGuiScreenKeys objectForKey:[NSString stringWithFormat:@"%d",gui]]) 
   13441             :         {
   13442             :                 // brand new - just add
   13443             :                 newarray = [[NSMutableArray alloc] init];
   13444             :         }
   13445             :         else 
   13446             :         {
   13447             :                 newarray = [[extraGuiScreenKeys objectForKey:[NSString stringWithFormat:@"%d",gui]] mutableCopy];
   13448             :                 NSInteger i = [newarray count];
   13449             :                 NSInteger j = 0;
   13450             :                 OOJSGuiScreenKeyDefinition *def_existing = nil;
   13451             :                 while (i--) 
   13452             :                 {
   13453             :                         def_existing = [newarray objectAtIndex:i]; 
   13454             :                         // if we find this name already in the array, remove it
   13455             :                         if (def_existing && [[def_existing name] isEqualToString:[definition name]])
   13456             :                         {
   13457             :                                 [newarray removeObjectAtIndex:i];
   13458             :                         }
   13459             :                         else 
   13460             :                         {
   13461             :                                 // check whether any of those keycodes is already in use on this screen
   13462             :                                 NSDictionary *keydefs = [def_existing registerKeys];
   13463             : 
   13464             :                                 foreach (key, [keydefs allKeys])
   13465             :                                 {
   13466             :                                         j = [checklist count];
   13467             :                                         while (j--) 
   13468             :                                         {
   13469             :                                                 if ([[NSString stringWithFormat:@"%@",[keydefs objectForKey:key]] isEqualToString:[NSString stringWithFormat:@"%@",[checklist objectAtIndex:j]]]) 
   13470             :                                                 {
   13471             :                                                         result = NO;
   13472             :                                                         OOLog(kOOLogException, @"***** Exception in setExtraGuiScreenKeys: %@ : %@ (%@)", @"invalid key settings", @"key already in use", key);
   13473             :                                                 }
   13474             :                                         }
   13475             :                                 }
   13476             :                         }
   13477             :                 }
   13478             :         }
   13479             :         [newarray addObject:definition];
   13480             :         // only add the item if there were no errors
   13481             :         if (result) [extraGuiScreenKeys setObject:[newarray mutableCopy] forKey:[NSString stringWithFormat:@"%d",gui]];
   13482             :         [newarray release];
   13483             :         return result;
   13484             : }
   13485             : 
   13486             : 
   13487             : #ifndef NDEBUG
   13488           0 : - (void)dumpSelfState
   13489             : {
   13490             :         NSMutableArray          *flags = nil;
   13491             :         NSString                        *flagsString = nil;
   13492             :         
   13493             :         [super dumpSelfState];
   13494             :         
   13495             :         OOLog(@"dumpState.playerEntity", @"Script time: %g", script_time);
   13496             :         OOLog(@"dumpState.playerEntity", @"Script time check: %g", script_time_check);
   13497             :         OOLog(@"dumpState.playerEntity", @"Script time interval: %g", script_time_interval);
   13498             :         OOLog(@"dumpState.playerEntity", @"Roll/pitch/yaw delta: %g, %g, %g", roll_delta, pitch_delta, yaw_delta);
   13499             :         OOLog(@"dumpState.playerEntity", @"Shield: %g fore, %g aft", forward_shield, aft_shield);
   13500             :         OOLog(@"dumpState.playerEntity", @"Alert level: %u, flags: %#x", alertFlags, alertCondition);
   13501             :         OOLog(@"dumpState.playerEntity", @"Missile status: %i", missile_status);
   13502             :         OOLog(@"dumpState.playerEntity", @"Energy unit: %@", EnergyUnitTypeToString([self installedEnergyUnitType]));
   13503             :         OOLog(@"dumpState.playerEntity", @"Fuel leak rate: %g", fuel_leak_rate);
   13504             :         OOLog(@"dumpState.playerEntity", @"Trumble count: %lu", trumbleCount);
   13505             :         
   13506             :         flags = [NSMutableArray array];
   13507           0 :         #define ADD_FLAG_IF_SET(x)              if (x) { [flags addObject:@#x]; }
   13508             :         ADD_FLAG_IF_SET(found_equipment);
   13509             :         ADD_FLAG_IF_SET(pollControls);
   13510             :         ADD_FLAG_IF_SET(suppressTargetLost);
   13511             :         ADD_FLAG_IF_SET(scoopsActive);
   13512             :         ADD_FLAG_IF_SET(game_over);
   13513             :         ADD_FLAG_IF_SET(finished);
   13514             :         ADD_FLAG_IF_SET(bomb_detonated);
   13515             :         ADD_FLAG_IF_SET(autopilot_engaged);
   13516             :         ADD_FLAG_IF_SET(afterburner_engaged);
   13517             :         ADD_FLAG_IF_SET(afterburnerSoundLooping);
   13518             :         ADD_FLAG_IF_SET(hyperspeed_engaged);
   13519             :         ADD_FLAG_IF_SET(travelling_at_hyperspeed);
   13520             :         ADD_FLAG_IF_SET(hyperspeed_locked);
   13521             :         ADD_FLAG_IF_SET(ident_engaged);
   13522             :         ADD_FLAG_IF_SET(galactic_witchjump);
   13523             :         ADD_FLAG_IF_SET(ecm_in_operation);
   13524             :         ADD_FLAG_IF_SET(show_info_flag);
   13525             :         ADD_FLAG_IF_SET(showDemoShips);
   13526             :         ADD_FLAG_IF_SET(rolling);
   13527             :         ADD_FLAG_IF_SET(pitching);
   13528             :         ADD_FLAG_IF_SET(yawing);
   13529             :         ADD_FLAG_IF_SET(using_mining_laser);
   13530             :         ADD_FLAG_IF_SET(mouse_control_on);
   13531             : //      ADD_FLAG_IF_SET(isSpeechOn);
   13532             :         ADD_FLAG_IF_SET(keyboardRollOverride);   // Handle keyboard roll...
   13533             :         ADD_FLAG_IF_SET(keyboardPitchOverride);  // ...and pitch override separately - (fix for BUG #17490)
   13534             :         ADD_FLAG_IF_SET(keyboardYawOverride);
   13535             :         ADD_FLAG_IF_SET(waitingForStickCallback);
   13536             :         flagsString = [flags count] ? [flags componentsJoinedByString:@", "] : (NSString *)@"none";
   13537             :         OOLog(@"dumpState.playerEntity", @"Flags: %@", flagsString);
   13538             : }
   13539             : 
   13540             : 
   13541             : /*      This method exists purely to suppress Clang static analyzer warnings that
   13542             :         these ivars are unused (but may be used by categories, which they are).
   13543             :         FIXME: there must be a feature macro we can use to avoid actually building
   13544             :         this into the app, but I can't find it in docs.
   13545             :         
   13546             :         Mind you, we could suppress some of this by using civilized accessors.
   13547             : */
   13548           0 : - (BOOL) suppressClangStuff
   13549             : {
   13550             :         return missionChoice &&
   13551             :         commanderNameString &&
   13552             :         cdrDetailArray &&
   13553             :         currentPage &&
   13554             :         n_key_roll_left &&
   13555             :         n_key_roll_right &&
   13556             :         n_key_pitch_forward &&
   13557             :         n_key_pitch_back &&
   13558             :         n_key_yaw_left &&
   13559             :         n_key_yaw_right &&
   13560             :         n_key_view_forward &&
   13561             :         n_key_view_aft &&
   13562             :         n_key_view_port &&
   13563             :         n_key_view_starboard &&
   13564             :         n_key_launch_ship &&
   13565             :         n_key_gui_screen_options &&
   13566             :         n_key_gui_screen_equipship &&
   13567             :         n_key_gui_screen_interfaces &&
   13568             :         n_key_gui_screen_status &&
   13569             :         n_key_gui_chart_screens &&
   13570             :         n_key_gui_system_data &&
   13571             :         n_key_gui_market &&
   13572             :         n_key_gui_arrow_left &&
   13573             :         n_key_gui_arrow_right &&
   13574             :         n_key_gui_arrow_up &&
   13575             :         n_key_gui_arrow_down &&
   13576             :         n_key_gui_page_up &&
   13577             :         n_key_gui_page_down &&
   13578             :         n_key_gui_select &&
   13579             :         n_key_increase_speed &&
   13580             :         n_key_decrease_speed &&
   13581             :         n_key_inject_fuel &&
   13582             :         n_key_fire_lasers &&
   13583             :         n_key_launch_missile &&
   13584             :         n_key_next_missile &&
   13585             :         n_key_ecm &&
   13586             :         n_key_prime_next_equipment &&
   13587             :         n_key_prime_previous_equipment &&
   13588             :         n_key_activate_equipment &&
   13589             :         n_key_mode_equipment &&
   13590             :         n_key_fastactivate_equipment_a &&
   13591             :         n_key_fastactivate_equipment_b &&
   13592             :         n_key_target_missile &&
   13593             :         n_key_untarget_missile &&
   13594             :         n_key_target_incoming_missile &&
   13595             :         n_key_ident_system &&
   13596             :         n_key_scanner_zoom &&
   13597             :         n_key_scanner_unzoom &&
   13598             :         n_key_launch_escapepod &&
   13599             :         n_key_galactic_hyperspace &&
   13600             :         n_key_hyperspace &&
   13601             :         n_key_jumpdrive &&
   13602             :         n_key_dump_cargo &&
   13603             :         n_key_rotate_cargo &&
   13604             :         n_key_autopilot &&
   13605             :         n_key_autodock &&
   13606             :         n_key_snapshot &&
   13607             :         n_key_docking_music &&
   13608             :         n_key_advanced_nav_array_next &&
   13609             :         n_key_advanced_nav_array_previous &&
   13610             :         n_key_info_next_system &&
   13611             :         n_key_info_previous_system &&
   13612             :         n_key_map_home &&
   13613             :         n_key_map_end &&
   13614             :         n_key_map_next_system &&
   13615             :         n_key_map_previous_system &&
   13616             :         n_key_map_info &&
   13617             :         n_key_map_zoom_in &&
   13618             :         n_key_map_zoom_out &&
   13619             :         n_key_system_home &&
   13620             :         n_key_system_end &&
   13621             :         n_key_system_next_system &&
   13622             :         n_key_system_previous_system &&
   13623             :         n_key_pausebutton &&
   13624             :         n_key_show_fps &&
   13625             :         n_key_bloom_toggle &&
   13626             :         n_key_mouse_control_roll &&
   13627             :         n_key_mouse_control_yaw &&
   13628             :         n_key_hud_toggle &&
   13629             :         n_key_comms_log &&
   13630             :         n_key_prev_compass_mode &&
   13631             :         n_key_next_compass_mode &&
   13632             :         n_key_chart_highlight &&
   13633             :         n_key_market_filter_cycle &&
   13634             :         n_key_market_sorter_cycle &&
   13635             :         n_key_market_buy_one &&
   13636             :         n_key_market_sell_one &&
   13637             :         n_key_market_buy_max &&
   13638             :         n_key_market_sell_max &&
   13639             :         n_key_next_target &&
   13640             :         n_key_previous_target &&
   13641             :         n_key_custom_view &&
   13642             :         n_key_custom_view_zoom_out &&
   13643             :         n_key_custom_view_zoom_in &&
   13644             :         n_key_custom_view_roll_left &&
   13645             :         n_key_custom_view_pan_left &&
   13646             :         n_key_custom_view_roll_right &&
   13647             :         n_key_custom_view_pan_right &&
   13648             :         n_key_custom_view_rotate_up &&
   13649             :         n_key_custom_view_pan_up &&
   13650             :         n_key_custom_view_rotate_down &&
   13651             :         n_key_custom_view_pan_down &&
   13652             :         n_key_custom_view_rotate_left &&
   13653             :         n_key_custom_view_rotate_right &&
   13654             :         n_key_docking_clearance_request &&
   13655             :         n_key_weapons_online_toggle &&
   13656             :         n_key_cycle_next_mfd &&
   13657             :         n_key_cycle_previous_mfd &&
   13658             :         n_key_switch_next_mfd &&
   13659             :         n_key_switch_previous_mfd &&
   13660             :         n_key_oxzmanager_setfilter &&
   13661             :         n_key_oxzmanager_showinfo &&
   13662             :         n_key_oxzmanager_extract &&
   13663             : #if OO_FOV_INFLIGHT_CONTROL_ENABLED
   13664             :         n_key_inc_field_of_view &&
   13665             :         n_key_dec_field_of_view &&
   13666             : #endif
   13667             :         n_key_dump_target_state &&
   13668             :         n_key_dump_entity_list &&
   13669             :         n_key_debug_full &&
   13670             :         n_key_debug_collision &&
   13671             :         n_key_debug_console_connect &&
   13672             :         n_key_debug_bounding_boxes &&
   13673             :         n_key_debug_shaders &&
   13674             :         n_key_debug_off &&
   13675             :         _sysInfoLight.x &&
   13676             :         selFunctionIdx &&
   13677             :         stickFunctions &&
   13678             :         keyFunctions &&
   13679             :         customEquipActivation &&
   13680             :         customActivatePressed &&
   13681             :         customModePressed &&
   13682             :         kbdLayouts &&
   13683             :         showingLongRangeChart &&
   13684             :         _missionAllowInterrupt &&
   13685             :         _missionScreenID &&
   13686             :         _missionTitle &&
   13687             :         _missionTextEntry;
   13688             : }
   13689             : #endif
   13690             : 
   13691             : @end
   13692             : 
   13693             : 
   13694           0 : NSComparisonResult marketSorterByName(id a, id b, void *context)
   13695             : {
   13696             :         OOCommodityMarket *market = (OOCommodityMarket *)context;
   13697             :         return [[market nameForGood:(OOCommodityType)a] compare:[market nameForGood:(OOCommodityType)b]];
   13698             : }
   13699             : 
   13700             : 
   13701           0 : NSComparisonResult marketSorterByPrice(id a, id b, void *context)
   13702             : {
   13703             :         OOCommodityMarket *market = (OOCommodityMarket *)context;
   13704             :         int result = (int)[market priceForGood:(OOCommodityType)a] - (int)[market priceForGood:(OOCommodityType)b];
   13705             :         if (result < 0)
   13706             :         {
   13707             :                 return NSOrderedAscending;
   13708             :         }
   13709             :         else if (result > 0)
   13710             :         {
   13711             :                 return NSOrderedDescending;
   13712             :         }
   13713             :         else
   13714             :         {
   13715             :                 return NSOrderedSame;
   13716             :         }
   13717             : }
   13718             : 
   13719             : 
   13720           0 : NSComparisonResult marketSorterByQuantity(id a, id b, void *context)
   13721             : {
   13722             :         OOCommodityMarket *market = (OOCommodityMarket *)context;
   13723             :         int result = (int)[market quantityForGood:(OOCommodityType)a] - (int)[market quantityForGood:(OOCommodityType)b];
   13724             :         if (result < 0)
   13725             :         {
   13726             :                 return NSOrderedAscending;
   13727             :         }
   13728             :         else if (result > 0)
   13729             :         {
   13730             :                 return NSOrderedDescending;
   13731             :         }
   13732             :         else
   13733             :         {
   13734             :                 return NSOrderedSame;
   13735             :         }
   13736             : }
   13737             : 
   13738             : 
   13739           0 : NSComparisonResult marketSorterByMassUnit(id a, id b, void *context)
   13740             : {
   13741             :         OOCommodityMarket *market = (OOCommodityMarket *)context;
   13742             :         int result = (int)[market massUnitForGood:(OOCommodityType)a] - (int)[market massUnitForGood:(OOCommodityType)b];
   13743             :         if (result < 0)
   13744             :         {
   13745             :                 return NSOrderedAscending;
   13746             :         }
   13747             :         else if (result > 0)
   13748             :         {
   13749             :                 return NSOrderedDescending;
   13750             :         }
   13751             :         else
   13752             :         {
   13753             :                 return NSOrderedSame;
   13754             :         }
   13755             : }

Generated by: LCOV version 1.14