LCOV - code coverage report
Current view: top level - Core/Scripting - OOJSShip.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 1 101 1.0 %
Date: 2025-06-23 16:19:27 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOJSShip.m
       4             : 
       5             : Oolite
       6             : Copyright (C) 2004-2013 Giles C Williams and contributors
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 02110-1301, USA.
      22             : 
      23             : */
      24             : 
      25             : #import "OOJSShip.h"
      26             : #import "OOJSEntity.h"
      27             : #import "OOJSWormhole.h"
      28             : #import "OOJSVector.h"
      29             : #import "OOJSQuaternion.h"
      30             : #import "OOJSEquipmentInfo.h"
      31             : #import "OOJavaScriptEngine.h"
      32             : #import "ShipEntity.h"
      33             : #import "ShipEntityAI.h"
      34             : #import "ShipEntityScriptMethods.h"
      35             : #import "StationEntity.h"
      36             : #import "WormholeEntity.h"
      37             : #import "AI.h"
      38             : #import "OOStringParsing.h"
      39             : #import "EntityOOJavaScriptExtensions.h"
      40             : #import "OORoleSet.h"
      41             : #import "OOJSPlayer.h"
      42             : #import "PlayerEntity.h"
      43             : #import "PlayerEntityScriptMethods.h"
      44             : #import "OOShipGroup.h"
      45             : #import "OOShipRegistry.h"
      46             : #import "OOEquipmentType.h"
      47             : #import "ResourceManager.h"
      48             : #import "OOCollectionExtractors.h"
      49             : #import "OOMesh.h"
      50             : #import "OOConstToString.h"
      51             : #import "OOEntityFilterPredicate.h"
      52             : #import "OOCharacter.h"
      53             : 
      54             : 
      55           0 : static JSObject *sShipPrototype;
      56             : 
      57             : 
      58             : static JSBool ShipGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
      59             : static JSBool ShipSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
      60             : 
      61             : static JSBool ShipSetScript(JSContext *context, uintN argc, jsval *vp);
      62             : static JSBool ShipSetAI(JSContext *context, uintN argc, jsval *vp);
      63             : static JSBool ShipSwitchAI(JSContext *context, uintN argc, jsval *vp);
      64             : static JSBool ShipExitAI(JSContext *context, uintN argc, jsval *vp);
      65             : static JSBool ShipReactToAIMessage(JSContext *context, uintN argc, jsval *vp);
      66             : static JSBool ShipSendAIMessage(JSContext *context, uintN argc, jsval *vp);
      67             : static JSBool ShipDeployEscorts(JSContext *context, uintN argc, jsval *vp);
      68             : static JSBool ShipDockEscorts(JSContext *context, uintN argc, jsval *vp);
      69             : static JSBool ShipHasRole(JSContext *context, uintN argc, jsval *vp);
      70             : static JSBool ShipEjectItem(JSContext *context, uintN argc, jsval *vp);
      71             : static JSBool ShipEjectSpecificItem(JSContext *context, uintN argc, jsval *vp);
      72             : static JSBool ShipDumpCargo(JSContext *context, uintN argc, jsval *vp);
      73             : static JSBool ShipAddCargoEntity(JSContext *context, uintN argc, jsval *vp);
      74             : static JSBool ShipSpawn(JSContext *context, uintN argc, jsval *vp);
      75             : static JSBool ShipDealEnergyDamage(JSContext *context, uintN argc, jsval *vp);
      76             : static JSBool ShipExplode(JSContext *context, uintN argc, jsval *vp);
      77             : static JSBool ShipRemove(JSContext *context, uintN argc, jsval *vp);
      78             : static JSBool ShipRunLegacyScriptActions(JSContext *context, uintN argc, jsval *vp);
      79             : static JSBool ShipCommsMessage(JSContext *context, uintN argc, jsval *vp);
      80             : static JSBool ShipFireECM(JSContext *context, uintN argc, jsval *vp);
      81             : static JSBool ShipAbandonShip(JSContext *context, uintN argc, jsval *vp);
      82             : static JSBool ShipCanAwardEquipment(JSContext *context, uintN argc, jsval *vp);
      83             : static JSBool ShipAwardEquipment(JSContext *context, uintN argc, jsval *vp);
      84             : static JSBool ShipAdjustCargo(JSContext *context, uintN argc, jsval *vp);
      85             : static JSBool ShipRequestHelpFromGroup(JSContext *context, uintN argc, jsval *vp);
      86             : static JSBool ShipPatrolReportIn(JSContext *context, uintN argc, jsval *vp);
      87             : 
      88             : static JSBool ShipRemoveEquipment(JSContext *context, uintN argc, jsval *vp);
      89             : static JSBool ShipRestoreSubEntities(JSContext *context, uintN argc, jsval *vp);
      90             : static JSBool ShipHasEquipmentProviding(JSContext *context, uintN argc, jsval *vp);
      91             : static JSBool ShipEquipmentStatus(JSContext *context, uintN argc, jsval *vp);
      92             : static JSBool ShipSetEquipmentStatus(JSContext *context, uintN argc, jsval *vp);
      93             : static JSBool ShipSelectNewMissile(JSContext *context, uintN argc, jsval *vp);
      94             : static JSBool ShipFireMissile(JSContext *context, uintN argc, jsval *vp);
      95             : static JSBool ShipFindNearestStation(JSContext *context, uintN argc, jsval *vp);
      96             : static JSBool ShipSetBounty(JSContext *context, uintN argc, jsval *vp);
      97             : static JSBool ShipSetCargo(JSContext *context, uintN argc, jsval *vp);
      98             : static JSBool ShipSetMaterials(JSContext *context, uintN argc, jsval *vp);
      99             : static JSBool ShipSetShaders(JSContext *context, uintN argc, jsval *vp);
     100             : static JSBool ShipExitSystem(JSContext *context, uintN argc, jsval *vp);
     101             : static JSBool ShipUpdateEscortFormation(JSContext *context, uintN argc, jsval *vp);
     102             : static JSBool ShipClearDefenseTargets(JSContext *context, uintN argc, jsval *vp);
     103             : static JSBool ShipAddDefenseTarget(JSContext *context, uintN argc, jsval *vp);
     104             : static JSBool ShipRemoveDefenseTarget(JSContext *context, uintN argc, jsval *vp);
     105             : static JSBool ShipAddCollisionException(JSContext *context, uintN argc, jsval *vp);
     106             : static JSBool ShipRemoveCollisionException(JSContext *context, uintN argc, jsval *vp);
     107             : static JSBool ShipGetMaterials(JSContext *context, uintN argc, jsval *vp);
     108             : static JSBool ShipGetShaders(JSContext *context, uintN argc, jsval *vp);
     109             : static JSBool ShipBecomeCascadeExplosion(JSContext *context, uintN argc, jsval *vp);
     110             : static JSBool ShipBroadcastCascadeImminent(JSContext *context, uintN argc, jsval *vp);
     111             : static JSBool ShipBroadcastDistressMessage(JSContext *context, uintN argc, jsval *vp);
     112             : static JSBool ShipOfferToEscort(JSContext *context, uintN argc, jsval *vp);
     113             : static JSBool ShipMarkTargetForFines(JSContext *context, uintN argc, jsval *vp);
     114             : static JSBool ShipEnterWormhole(JSContext *context, uintN argc, jsval *vp);
     115             : static JSBool ShipNotifyGroupOfWormhole(JSContext *context, uintN argc, jsval *vp);
     116             : static JSBool ShipThrowSpark(JSContext *context, uintN argc, jsval *vp);
     117             : 
     118             : static JSBool ShipPerformAttack(JSContext *context, uintN argc, jsval *vp);
     119             : static JSBool ShipPerformCollect(JSContext *context, uintN argc, jsval *vp);
     120             : static JSBool ShipPerformEscort(JSContext *context, uintN argc, jsval *vp);
     121             : static JSBool ShipPerformFaceDestination(JSContext *context, uintN argc, jsval *vp);
     122             : static JSBool ShipPerformFlee(JSContext *context, uintN argc, jsval *vp);
     123             : static JSBool ShipPerformFlyToRangeFromDestination(JSContext *context, uintN argc, jsval *vp);
     124             : static JSBool ShipPerformHold(JSContext *context, uintN argc, jsval *vp);
     125             : static JSBool ShipPerformIdle(JSContext *context, uintN argc, jsval *vp);
     126             : static JSBool ShipPerformIntercept(JSContext *context, uintN argc, jsval *vp);
     127             : static JSBool ShipPerformLandOnPlanet(JSContext *context, uintN argc, jsval *vp);
     128             : static JSBool ShipPerformMining(JSContext *context, uintN argc, jsval *vp);
     129             : static JSBool ShipPerformScriptedAI(JSContext *context, uintN argc, jsval *vp);
     130             : static JSBool ShipPerformScriptedAttackAI(JSContext *context, uintN argc, jsval *vp);
     131             : static JSBool ShipPerformStop(JSContext *context, uintN argc, jsval *vp);
     132             : static JSBool ShipPerformTumble(JSContext *context, uintN argc, jsval *vp);
     133             : 
     134             : static JSBool ShipRequestDockingInstructions(JSContext *context, uintN argc, jsval *vp);
     135             : static JSBool ShipRecallDockingInstructions(JSContext *context, uintN argc, jsval *vp);
     136             : static JSBool ShipCheckCourseToDestination(JSContext *context, uintN argc, jsval *vp);
     137             : static JSBool ShipGetSafeCourseToDestination(JSContext *context, uintN argc, jsval *vp);
     138             : static JSBool ShipCheckScanner(JSContext *context, uintN argc, jsval *vp);
     139             : static JSBool ShipThreatAssessment(JSContext *context, uintN argc, jsval *vp);
     140             : static JSBool ShipDamageAssessment(JSContext *context, uintN argc, jsval *vp);
     141             : static double ShipThreatAssessmentWeapon(OOWeaponType wt);
     142             : 
     143             : static JSBool ShipSetCargoType(JSContext *context, uintN argc, jsval *vp);
     144             : static JSBool ShipSetCrew(JSContext *context, uintN argc, jsval *vp);
     145             : 
     146             : static BOOL RemoveOrExplodeShip(JSContext *context, uintN argc, jsval *vp, BOOL explode);
     147             : static JSBool ShipSetMaterialsInternal(JSContext *context, uintN argc, jsval *vp, ShipEntity *thisEnt, BOOL fromShaders);
     148             : 
     149             : static JSBool ShipStaticKeysForRole(JSContext *context, uintN argc, jsval *vp);
     150             : static JSBool ShipStaticKeys(JSContext *context, uintN argc, jsval *vp);
     151             : static JSBool ShipStaticRoles(JSContext *context, uintN argc, jsval *vp);
     152             : static JSBool ShipStaticRoleIsInCategory(JSContext *context, uintN argc, jsval *vp);
     153             : static JSBool ShipStaticShipDataForKey(JSContext *context, uintN argc, jsval *vp);
     154             : static JSBool ShipStaticSetShipDataForKey(JSContext *context, uintN argc, jsval *vp);
     155             : 
     156           0 : static JSClass sShipClass =
     157             : {
     158             :         "Ship",
     159             :         JSCLASS_HAS_PRIVATE,
     160             :         
     161             :         JS_PropertyStub,                // addProperty
     162             :         JS_PropertyStub,                // delProperty
     163             :         ShipGetProperty,                // getProperty
     164             :         ShipSetProperty,                // setProperty
     165             :         JS_EnumerateStub,               // enumerate
     166             :         JS_ResolveStub,                 // resolve
     167             :         JS_ConvertStub,                 // convert
     168             :         OOJSObjectWrapperFinalize,// finalize
     169             :         JSCLASS_NO_OPTIONAL_MEMBERS
     170             : };
     171             : 
     172             : 
     173             : /* It turns out that the value in SpiderMonkey used to identify these
     174             :  * enums is an 8-bit signed int:
     175             :  * developer.mozilla.org/en/docs/SpiderMonkey/JSAPI_Reference/JSPropertySpec
     176             :  * which puts a limit of 256 properties on the ship object.  Moved the
     177             :  * enum to start at -128, so we can use the full 256 rather than just
     178             :  * 128 of them. I don't think any of our other classes are getting
     179             :  * close to the limit yet.
     180             :  * - CIM 29/9/2013
     181             :  */
     182           0 : enum
     183             : {
     184             :         // Property IDs
     185             :         kShip_accuracy = -128,          // the ship's accuracy, float, read/write
     186             :         kShip_aftWeapon,                        // the ship's aft weapon, equipmentType, read/write
     187             :         kShip_AI,                                       // AI state machine name, string, read-only
     188             :         kShip_AIScript,                         // AI script, Script, read-only
     189             :         kShip_AIScriptWakeTime,                         // next wakeup time, integer, read/write
     190             :         kShip_AIState,                          // AI state machine state, string, read/write
     191             :         kShip_AIFoundTarget,            // AI "found target", entity, read/write
     192             :         kShip_AIPrimaryAggressor,       // AI "primary aggressor", entity, read/write
     193             :         kShip_alertCondition,           // number 0-3, read-only, combat alert level
     194             :         kShip_autoAI,                           // bool, read-only, auto_ai from shipdata
     195             :         kShip_autoWeapons,                      // bool, read-only, auto_weapons from shipdata
     196             :         kShip_beaconCode,                       // beacon code, string, read/write
     197             :         kShip_beaconLabel,                      // beacon label, string, read/write
     198             :         kShip_boundingBox,                      // boundingBox, vector, read-only
     199             :         kShip_bounty,                           // bounty, unsigned int, read/write
     200             :         kShip_cargoList,                // cargo on board, array of objects, read-only
     201             :         kShip_cargoSpaceAvailable,      // free cargo space, integer, read-only
     202             :         kShip_cargoSpaceCapacity,       // maximum cargo, integer, read/write
     203             :         kShip_cargoSpaceUsed,           // cargo on board, integer, read-only
     204             :         kShip_collisionExceptions,   // collision exception list, array, read-only
     205             :         kShip_contracts,                        // cargo contracts contracts, array - strings & whatnot, read only
     206             :         kShip_commodity,                        // commodity of a ship, read only
     207             :         kShip_commodityAmount,          // commodityAmount of a ship, read only
     208             :         kShip_cloakAutomatic,           // should cloack start by itself or by script, read/write
     209             :         kShip_crew,                                     // crew, list, read only
     210             :         kShip_cruiseSpeed,                      // desired cruising speed, number, read only
     211             :         kShip_currentWeapon,            // the ship's active weapon, equipmentType, read/write
     212             :         kShip_dataKey,                          // string, read-only, shipdata.plist key
     213             :         kShip_defenseTargets,           // array, read-only, defense targets
     214             :         kShip_desiredRange,                     // desired Range, double, read/write
     215             :         kShip_desiredSpeed,                     // AI desired flight speed, double, read/write
     216             :         kShip_destination,                      // flight destination, Vector, read/write
     217             :         kShip_destinationSystem,        // destination system, number, read/write
     218             :         kShip_displayName,                      // name displayed on screen, string, read/write
     219             :         kShip_dockingInstructions,                      // name displayed on screen, string, read/write
     220             :         kShip_energyRechargeRate,       // energy recharge rate, float, read-only
     221             :         kShip_entityPersonality,        // per-ship random number, int, read-only
     222             :         kShip_equipment,                        // the ship's equipment, array of EquipmentInfo, read only
     223             :         kShip_escortGroup,                      // group, ShipGroup, read-only
     224             :         kShip_escorts,                          // deployed escorts, array of Ship, read-only
     225             :         kShip_exhaustEmissiveColor,     // exhaust emissive color, array, read/write
     226             :         kShip_exhausts,                         // exhausts, array, read-only
     227             :         kShip_extraCargo,                               // cargo space increase granted by large cargo bay, int, read-only
     228             :         kShip_flashers,                         // flashers, array, read-only
     229             :         kShip_forwardWeapon,            // the ship's forward weapon, equipmentType, read/write
     230             :         kShip_fuel,                                     // fuel, float, read/write
     231             :         kShip_fuelChargeRate,           // fuel scoop rate & charge multiplier, float, read-only
     232             :         kShip_group,                            // group, ShipGroup, read/write
     233             :         kShip_hasHostileTarget,         // has hostile target, boolean, read-only
     234             :         kShip_hasHyperspaceMotor,       // has hyperspace motor, boolean, read-only
     235             :         kShip_hasSuspendedAI,           // AI has suspended states, boolean, read-only
     236             :         kShip_heading,                          // forwardVector of a ship, read-only
     237             :         kShip_heatInsulation,           // hull heat insulation, double, read/write
     238             :         kShip_homeSystem,                       // home system, number, read/write
     239             :         kShip_hyperspaceSpinTime,       // hyperspace spin time, float, read/write
     240             :         kShip_injectorBurnRate,         // injector burn rate, number, read/write dLY/s
     241             :         kShip_injectorSpeedFactor,  // injector speed factor, number, read/write
     242             :         kShip_isBeacon,                         // is beacon, boolean, read-only
     243             :         kShip_isBoulder,                        // is a boulder (generates splinters), boolean, read/write
     244             :         kShip_isCargo,                          // contains cargo, boolean, read-only
     245             :         kShip_isCloaked,                        // cloaked, boolean, read/write (if cloaking device installed)
     246             :         kShip_isDerelict,                       // is an abandoned ship, boolean, read-only
     247             :         kShip_isFrangible,                      // frangible, boolean, read-only
     248             :         kShip_isFleeing,                        // is fleeing, boolean, read-only
     249             :         kShip_isJamming,                        // jamming scanners, boolean, read/write (if jammer installed)
     250             :         kShip_isMinable,                        // is a sensible target for mining, boolean, read-only
     251             :         kShip_isMine,                           // is mine, boolean, read-only
     252             :         kShip_isMissile,                        // is missile, boolean, read-only
     253             :         kShip_isPiloted,                        // is piloted, boolean, read-only (includes stations)
     254             :         kShip_isPirate,                         // is pirate, boolean, read-only
     255             :         kShip_isPirateVictim,           // is pirate victim, boolean, read-only
     256             :         kShip_isPolice,                         // is police, boolean, read-only
     257             :         kShip_isRock,                           // is a rock (hermits included), boolean, read-only
     258             :         kShip_isThargoid,                       // is thargoid, boolean, read-only
     259             :         kShip_isTurret,                     // is turret, boolean, read-only
     260             :         kShip_isTrader,                         // is trader, boolean, read-only
     261             :         kShip_isWeapon,                         // is missile or mine, boolean, read-only
     262             :         kShip_laserHeatLevel,                   // active laser temperature, float, read-only
     263             :         kShip_laserHeatLevelAft,                // aft laser temperature, float, read-only
     264             :         kShip_laserHeatLevelForward,    // fore laser temperature, float, read-only
     265             :         kShip_laserHeatLevelPort,               // port laser temperature, float, read-only
     266             :         kShip_laserHeatLevelStarboard,  // starboard laser temperature, float, read-only
     267             :         kShip_lightsActive,                     // flasher/shader light flag, boolean, read/write
     268             :         kShip_markedForFines,   // has been marked for fines
     269             :         kShip_maxEscorts,     // maximum escort count, int, read/write
     270             :         kShip_maxPitch,                         // maximum flight pitch, double, read-only
     271             :         kShip_maxSpeed,                         // maximum flight speed, double, read-only
     272             :         kShip_maxRoll,                          // maximum flight roll, double, read-only
     273             :         kShip_maxYaw,                           // maximum flight yaw, double, read-only
     274             :         kShip_maxThrust,                        // maximum thrust, double, read-only
     275             :         kShip_missileCapacity,          // max missiles capacity, integer, read-only
     276             :         kShip_missileLoadTime,          // missile load time, double, read/write
     277             :         kShip_missiles,                         // the ship's missiles / external storage, array of equipmentTypes, read only
     278             :         kShip_name,                                     // name, string, read-only
     279             :         kShip_parcelCount,              // number of parcels on ship, integer, read-only
     280             :         kShip_parcels,                  // parcel contracts, array - strings & whatnot, read only
     281             :         kShip_passengerCapacity,        // amount of passenger space on ship, integer, read-only
     282             :         kShip_passengerCount,           // number of passengers on ship, integer, read-only
     283             :         kShip_passengers,                       // passengers contracts, array - strings & whatnot, read only
     284             :         kShip_pitch,                            // pitch level, float, read-only
     285             :         kShip_portWeapon,                       // the ship's port weapon, equipmentType, read/write
     286             :         kShip_potentialCollider,        // "proximity alert" ship, Entity, read-only
     287             :         kShip_primaryRole,                      // Primary role, string, read/write
     288             :         kShip_reactionTime,             // AI reaction time, read/write
     289             :         kShip_reportAIMessages,         // report AI messages, boolean, read/write
     290             :         kShip_roleWeights,                      // roles and weights, dictionary, read-only
     291             :         kShip_roles,                            // roles, array, read-only
     292             :         kShip_roll,                                     // roll level, float, read-only
     293             :         kShip_savedCoordinates,         // coordinates in system space for AI use, Vector, read/write
     294             :         kShip_scanDescription,          // STE scan class label, string, read/write
     295             :         kShip_scannerDisplayColor1,     // color of lollipop shown on scanner, array, read/write
     296             :         kShip_scannerDisplayColor2,     // color of lollipop shown on scanner when flashing, array, read/write
     297             :         kShip_scannerHostileDisplayColor1,      // color of lollipop shown on scanner, array, read/write
     298             :         kShip_scannerHostileDisplayColor2,      // color of lollipop shown on scanner when flashing, array, read/write
     299             :         kShip_scannerRange,                     // scanner range, double, read-only
     300             :         kShip_script,                           // script, Script, read-only
     301             :         kShip_scriptedMisjump,          // next jump will miss if set to true, boolean, read/write
     302             :         kShip_scriptedMisjumpRange,  // 0..1 range of next misjump, float, read/write
     303             :         kShip_scriptInfo,                       // arbitrary data for scripts, dictionary, read-only
     304             :         kShip_shipClassName,            // ship type name, string, read/write
     305             :         kShip_shipUniqueName,           // uniqish name, string, read/write
     306             :         kShip_speed,                            // current flight speed, double, read-only
     307             :         kShip_starboardWeapon,          // the ship's starboard weapon, equipmentType, read/write
     308             :         kShip_subEntities,                      // subentities, array of Ship, read-only
     309             :         kShip_subEntityCapacity,        // max subentities for this ship, int, read-only
     310             :         kShip_subEntityRotation,        // subentity rotation velocity, quaternion, read/write
     311             :         kShip_sunGlareFilter,           // sun glare filter multiplier, float, read/write
     312             :         kShip_target,                           // target, Ship, read/write
     313             :         kShip_temperature,                      // hull temperature, double, read/write
     314             :         kShip_thrust,                           // the ship's thrust, double, read/write
     315             :         kShip_thrustVector,                     // thrust-related component of velocity, vector, read-only
     316             :         kShip_trackCloseContacts,       // generate close contact events, boolean, read/write
     317             :         kShip_vectorForward,            // forwardVector of a ship, read-only
     318             :         kShip_vectorRight,                      // rightVector of a ship, read-only
     319             :         kShip_vectorUp,                         // upVector of a ship, read-only
     320             :         kShip_velocity,                         // velocity, vector, read/write
     321             :         kShip_weaponFacings,            // available facings, int, read-only
     322             :         kShip_weaponPositionAft,        // weapon offset, vector, read-only
     323             :         kShip_weaponPositionForward,    // weapon offset, vector, read-only
     324             :         kShip_weaponPositionPort,       // weapon offset, vector, read-only
     325             :         kShip_weaponPositionStarboard,  // weapon offset, vector, read-only
     326             :         kShip_weaponRange,                      // weapon range, double, read-only
     327             :         kShip_withinStationAegis,       // within main station aegis, boolean, read/write
     328             :         kShip_yaw,                                      // yaw level, float, read-only
     329             : };
     330             : 
     331             : 
     332           0 : static JSPropertySpec sShipProperties[] =
     333             : {
     334             :         // JS name                                      ID                                                      flags
     335             :         { "accuracy",                         kShip_accuracy,                         OOJS_PROP_READWRITE_CB },
     336             :         { "aftWeapon",                                kShip_aftWeapon,                        OOJS_PROP_READWRITE_CB },
     337             :         { "AI",                                               kShip_AI,                                       OOJS_PROP_READONLY_CB },
     338             :         { "AIScript",                                 kShip_AIScript,                         OOJS_PROP_READONLY_CB },
     339             :         { "AIScriptWakeTime",                                 kShip_AIScriptWakeTime,                         OOJS_PROP_READWRITE_CB },
     340             :         { "AIState",                          kShip_AIState,                          OOJS_PROP_READWRITE_CB },
     341             :         { "AIFoundTarget",                    kShip_AIFoundTarget,            OOJS_PROP_READWRITE_CB },
     342             :         { "AIPrimaryAggressor",               kShip_AIPrimaryAggressor,       OOJS_PROP_READWRITE_CB },
     343             :         { "alertCondition",                   kShip_alertCondition,           OOJS_PROP_READONLY_CB },
     344             :         { "autoAI",                                   kShip_autoAI,                           OOJS_PROP_READONLY_CB },
     345             :         { "autoWeapons",                      kShip_autoWeapons,                      OOJS_PROP_READONLY_CB },
     346             :         { "beaconCode",                               kShip_beaconCode,                       OOJS_PROP_READWRITE_CB },
     347             :         { "beaconLabel",                      kShip_beaconLabel,                      OOJS_PROP_READWRITE_CB },
     348             :         { "boundingBox",                      kShip_boundingBox,                      OOJS_PROP_READONLY_CB },
     349             :         { "bounty",                                   kShip_bounty,                           OOJS_PROP_READWRITE_CB },
     350             :         { "cargoList",                        kShip_cargoList,                OOJS_PROP_READONLY_CB },        
     351             :         { "cargoSpaceUsed",                   kShip_cargoSpaceUsed,           OOJS_PROP_READONLY_CB },
     352             :         { "cargoSpaceCapacity",               kShip_cargoSpaceCapacity,       OOJS_PROP_READWRITE_CB },
     353             :         { "cargoSpaceAvailable",      kShip_cargoSpaceAvailable,      OOJS_PROP_READONLY_CB },
     354             :         { "collisionExceptions",      kShip_collisionExceptions,      OOJS_PROP_READONLY_CB },
     355             :         { "commodity",                                kShip_commodity,                        OOJS_PROP_READONLY_CB },
     356             :         { "commodityAmount",          kShip_commodityAmount,          OOJS_PROP_READONLY_CB },
     357             :         // contracts instead of cargo to distinguish them from the manifest
     358             :         { "contracts",                                kShip_contracts,                        OOJS_PROP_READONLY_CB },
     359             :         { "cloakAutomatic",                   kShip_cloakAutomatic,           OOJS_PROP_READWRITE_CB},
     360             :         { "crew",                                     kShip_crew,                                     OOJS_PROP_READONLY_CB },
     361             :         { "cruiseSpeed",                      kShip_cruiseSpeed,                      OOJS_PROP_READONLY_CB },
     362             :         { "currentWeapon",                    kShip_currentWeapon,            OOJS_PROP_READWRITE_CB },
     363             :         { "dataKey",                          kShip_dataKey,                          OOJS_PROP_READONLY_CB },
     364             :         { "defenseTargets",                   kShip_defenseTargets,           OOJS_PROP_READONLY_CB },
     365             :         { "desiredRange",                     kShip_desiredRange,                     OOJS_PROP_READWRITE_CB },
     366             :         { "desiredSpeed",                     kShip_desiredSpeed,                     OOJS_PROP_READWRITE_CB },
     367             :         { "destination",                      kShip_destination,                      OOJS_PROP_READWRITE_CB },
     368             :         { "destinationSystem",                kShip_destinationSystem,        OOJS_PROP_READWRITE_CB },
     369             :         { "displayName",                      kShip_displayName,                      OOJS_PROP_READWRITE_CB },
     370             :         { "dockingInstructions",      kShip_dockingInstructions,      OOJS_PROP_READONLY_CB },
     371             :         { "energyRechargeRate",               kShip_energyRechargeRate,       OOJS_PROP_READWRITE_CB },
     372             :         { "entityPersonality",                kShip_entityPersonality,        OOJS_PROP_READWRITE_CB },
     373             :         { "equipment",                                kShip_equipment,                        OOJS_PROP_READONLY_CB },
     374             :         { "escorts",                          kShip_escorts,                          OOJS_PROP_READONLY_CB },
     375             :         { "escortGroup",                      kShip_escortGroup,                      OOJS_PROP_READONLY_CB },
     376             :         { "exhaustEmissiveColor",     kShip_exhaustEmissiveColor,     OOJS_PROP_READWRITE_CB },
     377             :         { "exhausts",                         kShip_exhausts,                         OOJS_PROP_READONLY_CB },
     378             :         { "extraCargo",                               kShip_extraCargo,                       OOJS_PROP_READONLY_CB },
     379             :         { "flashers",                         kShip_flashers,                         OOJS_PROP_READONLY_CB },
     380             :         { "forwardWeapon",                    kShip_forwardWeapon,            OOJS_PROP_READWRITE_CB },
     381             :         { "fuel",                                     kShip_fuel,                                     OOJS_PROP_READWRITE_CB },
     382             :         { "fuelChargeRate",                   kShip_fuelChargeRate,           OOJS_PROP_READONLY_CB },
     383             :         { "group",                                    kShip_group,                            OOJS_PROP_READWRITE_CB },
     384             :         { "hasHostileTarget",         kShip_hasHostileTarget,         OOJS_PROP_READONLY_CB },
     385             :         { "hasHyperspaceMotor",               kShip_hasHyperspaceMotor,       OOJS_PROP_READONLY_CB },
     386             :         { "hasSuspendedAI",                   kShip_hasSuspendedAI,           OOJS_PROP_READONLY_CB },
     387             :         { "heatInsulation",                   kShip_heatInsulation,           OOJS_PROP_READWRITE_CB },
     388             :         { "heading",                          kShip_heading,                          OOJS_PROP_READONLY_CB },
     389             :         { "homeSystem",                               kShip_homeSystem,                       OOJS_PROP_READWRITE_CB },
     390             :         { "hyperspaceSpinTime",               kShip_hyperspaceSpinTime,       OOJS_PROP_READWRITE_CB },
     391             :         { "injectorBurnRate",         kShip_injectorBurnRate,         OOJS_PROP_READWRITE_CB },
     392             :         { "injectorSpeedFactor",      kShip_injectorSpeedFactor,      OOJS_PROP_READWRITE_CB },
     393             :         { "homeSystem",                               kShip_homeSystem,                       OOJS_PROP_READWRITE_CB },
     394             :         { "isBeacon",                         kShip_isBeacon,                         OOJS_PROP_READONLY_CB },
     395             :         { "isCloaked",                                kShip_isCloaked,                        OOJS_PROP_READWRITE_CB },
     396             :         { "isCargo",                          kShip_isCargo,                          OOJS_PROP_READONLY_CB },
     397             :         { "isDerelict",                               kShip_isDerelict,                       OOJS_PROP_READONLY_CB },
     398             :         { "isFrangible",                      kShip_isFrangible,                      OOJS_PROP_READONLY_CB },
     399             :         { "isFleeing",                                kShip_isFleeing,                        OOJS_PROP_READONLY_CB },
     400             :         { "isJamming",                                kShip_isJamming,                        OOJS_PROP_READONLY_CB },
     401             :         { "isMinable",                                kShip_isMinable,                        OOJS_PROP_READONLY_CB },
     402             :         { "isMine",                                   kShip_isMine,                           OOJS_PROP_READONLY_CB },
     403             :         { "isMissile",                                kShip_isMissile,                        OOJS_PROP_READONLY_CB },
     404             :         { "isPiloted",                                kShip_isPiloted,                        OOJS_PROP_READONLY_CB },
     405             :         { "isPirate",                         kShip_isPirate,                         OOJS_PROP_READONLY_CB },
     406             :         { "isPirateVictim",                   kShip_isPirateVictim,           OOJS_PROP_READONLY_CB },
     407             :         { "isPolice",                         kShip_isPolice,                         OOJS_PROP_READONLY_CB },
     408             :         { "isRock",                                   kShip_isRock,                           OOJS_PROP_READONLY_CB },
     409             :         { "isBoulder",                                kShip_isBoulder,                        OOJS_PROP_READWRITE_CB },
     410             :         { "isThargoid",                               kShip_isThargoid,                       OOJS_PROP_READONLY_CB },
     411             :         { "isTurret",                         kShip_isTurret,                         OOJS_PROP_READONLY_CB },
     412             :         { "isTrader",                         kShip_isTrader,                         OOJS_PROP_READONLY_CB },
     413             :         { "isWeapon",                         kShip_isWeapon,                         OOJS_PROP_READONLY_CB },
     414             :         { "laserHeatLevel",                   kShip_laserHeatLevel,           OOJS_PROP_READONLY_CB },
     415             :         { "laserHeatLevelAft",                kShip_laserHeatLevelAft,        OOJS_PROP_READONLY_CB },
     416             :         { "laserHeatLevelForward",    kShip_laserHeatLevelForward,    OOJS_PROP_READONLY_CB },
     417             :         { "laserHeatLevelPort",               kShip_laserHeatLevelPort,       OOJS_PROP_READONLY_CB },
     418             :         { "laserHeatLevelStarboard",  kShip_laserHeatLevelStarboard,  OOJS_PROP_READONLY_CB },
     419             :         { "lightsActive",                     kShip_lightsActive,                     OOJS_PROP_READWRITE_CB },
     420             :         { "markedForFines",                           kShip_markedForFines,                           OOJS_PROP_READONLY_CB },
     421             :         { "maxEscorts",                               kShip_maxEscorts,                               OOJS_PROP_READWRITE_CB },
     422             :         { "maxPitch",                         kShip_maxPitch,                         OOJS_PROP_READWRITE_CB },
     423             :         { "maxSpeed",                         kShip_maxSpeed,                         OOJS_PROP_READWRITE_CB },
     424             :         { "maxRoll",                          kShip_maxRoll,                          OOJS_PROP_READWRITE_CB },
     425             :         { "maxYaw",                                   kShip_maxYaw,                           OOJS_PROP_READWRITE_CB },
     426             :         { "maxThrust",                                kShip_maxThrust,                        OOJS_PROP_READWRITE_CB },
     427             :         { "missileCapacity",          kShip_missileCapacity,          OOJS_PROP_READONLY_CB },
     428             :         { "missileLoadTime",          kShip_missileLoadTime,          OOJS_PROP_READWRITE_CB },
     429             :         { "missiles",                         kShip_missiles,                         OOJS_PROP_READONLY_CB },
     430             :         { "name",                                     kShip_name,                                     OOJS_PROP_READWRITE_CB },
     431             :         { "parcelCount",                      kShip_parcelCount,              OOJS_PROP_READONLY_CB },
     432             :         { "parcels",                          kShip_parcels,                  OOJS_PROP_READONLY_CB },
     433             :         { "passengerCount",                   kShip_passengerCount,           OOJS_PROP_READONLY_CB },
     434             :         { "passengerCapacity",                kShip_passengerCapacity,        OOJS_PROP_READONLY_CB },
     435             :         { "passengers",                               kShip_passengers,                       OOJS_PROP_READONLY_CB },
     436             :         { "pitch",                                    kShip_pitch,                            OOJS_PROP_READONLY_CB },
     437             :         { "portWeapon",                               kShip_portWeapon,                       OOJS_PROP_READWRITE_CB },
     438             :         { "potentialCollider",                kShip_potentialCollider,        OOJS_PROP_READONLY_CB },
     439             :         { "primaryRole",                      kShip_primaryRole,                      OOJS_PROP_READWRITE_CB },
     440             :         { "reactionTime",             kShip_reactionTime,             OOJS_PROP_READWRITE_CB },
     441             :         { "reportAIMessages",         kShip_reportAIMessages,         OOJS_PROP_READWRITE_CB },
     442             :         { "roleWeights",                      kShip_roleWeights,                      OOJS_PROP_READONLY_CB },
     443             :         { "roles",                                    kShip_roles,                            OOJS_PROP_READONLY_CB },
     444             :         { "roll",                                     kShip_roll,                                     OOJS_PROP_READONLY_CB },
     445             :         { "savedCoordinates",         kShip_savedCoordinates,         OOJS_PROP_READWRITE_CB },
     446             :         { "scanDescription",          kShip_scanDescription,          OOJS_PROP_READWRITE_CB },
     447             :         { "scannerDisplayColor1",     kShip_scannerDisplayColor1,     OOJS_PROP_READWRITE_CB },
     448             :         { "scannerDisplayColor2",     kShip_scannerDisplayColor2,     OOJS_PROP_READWRITE_CB },
     449             :         { "scannerHostileDisplayColor1",      kShip_scannerHostileDisplayColor1,      OOJS_PROP_READWRITE_CB },
     450             :         { "scannerHostileDisplayColor2",      kShip_scannerHostileDisplayColor2,      OOJS_PROP_READWRITE_CB },
     451             :         { "scannerRange",                     kShip_scannerRange,                     OOJS_PROP_READONLY_CB },
     452             :         { "script",                                   kShip_script,                           OOJS_PROP_READONLY_CB },
     453             :         { "scriptedMisjump",          kShip_scriptedMisjump,          OOJS_PROP_READWRITE_CB },
     454             :         { "scriptedMisjumpRange",             kShip_scriptedMisjumpRange,             OOJS_PROP_READWRITE_CB },
     455             :         { "scriptInfo",                               kShip_scriptInfo,                       OOJS_PROP_READONLY_CB },
     456             :         { "shipClassName",                    kShip_shipClassName,            OOJS_PROP_READWRITE_CB },
     457             :         { "shipUniqueName",                           kShip_shipUniqueName,                           OOJS_PROP_READWRITE_CB },
     458             :         { "speed",                                    kShip_speed,                            OOJS_PROP_READONLY_CB },
     459             :         { "starboardWeapon",          kShip_starboardWeapon,          OOJS_PROP_READWRITE_CB },
     460             :         { "subEntities",                      kShip_subEntities,                      OOJS_PROP_READONLY_CB },
     461             :         { "subEntityCapacity",                kShip_subEntityCapacity,        OOJS_PROP_READONLY_CB },
     462             :         { "subEntityRotation",                kShip_subEntityRotation,        OOJS_PROP_READWRITE_CB },
     463             :         { "sunGlareFilter",                   kShip_sunGlareFilter,           OOJS_PROP_READWRITE_CB },
     464             :         { "target",                                   kShip_target,                           OOJS_PROP_READWRITE_CB },
     465             :         { "temperature",                      kShip_temperature,                      OOJS_PROP_READWRITE_CB },
     466             :         { "thrust",                                   kShip_thrust,                           OOJS_PROP_READWRITE_CB },
     467             :         { "thrustVector",                     kShip_thrustVector,                     OOJS_PROP_READONLY_CB },
     468             :         { "trackCloseContacts",               kShip_trackCloseContacts,       OOJS_PROP_READWRITE_CB },
     469             :         { "vectorForward",                    kShip_vectorForward,            OOJS_PROP_READONLY_CB },
     470             :         { "vectorRight",                      kShip_vectorRight,                      OOJS_PROP_READONLY_CB },
     471             :         { "vectorUp",                         kShip_vectorUp,                         OOJS_PROP_READONLY_CB },
     472             :         { "velocity",                         kShip_velocity,                         OOJS_PROP_READWRITE_CB },
     473             :         { "weaponFacings",                    kShip_weaponFacings,            OOJS_PROP_READONLY_CB },
     474             :         { "weaponPositionAft",                kShip_weaponPositionAft,        OOJS_PROP_READONLY_CB },
     475             :         { "weaponPositionForward",    kShip_weaponPositionForward,    OOJS_PROP_READONLY_CB },
     476             :         { "weaponPositionPort",               kShip_weaponPositionPort,       OOJS_PROP_READONLY_CB },        
     477             :         { "weaponPositionStarboard",  kShip_weaponPositionStarboard,  OOJS_PROP_READONLY_CB },
     478             :         { "weaponRange",                      kShip_weaponRange,                      OOJS_PROP_READONLY_CB },
     479             :         { "withinStationAegis",               kShip_withinStationAegis,       OOJS_PROP_READONLY_CB },
     480             :         { "yaw",                                      kShip_yaw,                                      OOJS_PROP_READONLY_CB },
     481             :         { 0 }
     482             : };
     483             : 
     484           0 : static JSFunctionSpec sShipMethods[] =
     485             : {
     486             :         // JS name                                      Function                                        min args
     487             :         { "abandonShip",                      ShipAbandonShip,                        0 },
     488             :         { "addCargoEntity",                   ShipAddCargoEntity,                     1 },
     489             :         { "addCollisionException",    ShipAddCollisionException,      1 },
     490             :         { "addDefenseTarget",         ShipAddDefenseTarget,           1 },
     491             :         { "adjustCargo",                      ShipAdjustCargo,                        2 },
     492             :         { "awardEquipment",                   ShipAwardEquipment,                     1 },
     493             :         { "becomeCascadeExplosion",                   ShipBecomeCascadeExplosion,                     0 },
     494             :         { "broadcastCascadeImminent",                 ShipBroadcastCascadeImminent,                   0 },
     495             :         { "broadcastDistressMessage",                 ShipBroadcastDistressMessage,                   0 },
     496             :         { "canAwardEquipment",                ShipCanAwardEquipment,          1 },
     497             :         { "checkCourseToDestination",         ShipCheckCourseToDestination,           0 },
     498             :         { "checkScanner",             ShipCheckScanner,               0 },
     499             :         { "clearDefenseTargets",      ShipClearDefenseTargets,        0 },
     500             :         { "commsMessage",                     ShipCommsMessage,                       1 },
     501             :         { "damageAssessment",         ShipDamageAssessment,           0 },
     502             :         { "dealEnergyDamage",         ShipDealEnergyDamage,           2 },
     503             :         { "deployEscorts",                    ShipDeployEscorts,                      0 },
     504             :         { "dockEscorts",                      ShipDockEscorts,                        0 },
     505             :         { "dumpCargo",                                ShipDumpCargo,                          0 },
     506             :         { "ejectItem",                                ShipEjectItem,                          1 },
     507             :         { "ejectSpecificItem",                ShipEjectSpecificItem,          1 },
     508             :         { "enterWormhole",            ShipEnterWormhole,              0 },
     509             :         { "equipmentStatus",          ShipEquipmentStatus,            1 },
     510             :         { "exitAI",                                   ShipExitAI,                                     0 },
     511             :         { "exitSystem",                               ShipExitSystem,                         0 },
     512             :         { "explode",                          ShipExplode,                            0 },
     513             :         { "fireECM",                          ShipFireECM,                            0 },
     514             :         { "fireMissile",                      ShipFireMissile,                        0 },
     515             :         { "findNearestStation",               ShipFindNearestStation,         0 },
     516             :         { "getMaterials",                     ShipGetMaterials,                       0 },
     517             :         { "getSafeCourseToDestination",               ShipGetSafeCourseToDestination,         0 },
     518             :         { "getShaders",                               ShipGetShaders,                         0 },
     519             :         { "hasEquipmentProviding",    ShipHasEquipmentProviding,              1 },
     520             :         { "hasRole",                          ShipHasRole,                            1 },
     521             :         { "markTargetForFines",                               ShipMarkTargetForFines,                         0 },
     522             :         { "notifyGroupOfWormhole",            ShipNotifyGroupOfWormhole,              0 },
     523             :         { "offerToEscort",                            ShipOfferToEscort,                              1 },
     524             :         { "patrolReportIn", ShipPatrolReportIn, 1},
     525             :   { "performAttack",          ShipPerformAttack,              0 },
     526             :   { "performCollect",         ShipPerformCollect,             0 },
     527             :   { "performEscort",          ShipPerformEscort,              0 },
     528             :   { "performFaceDestination",         ShipPerformFaceDestination,             0 },
     529             :   { "performFlee",            ShipPerformFlee,                0 },
     530             :   { "performFlyToRangeFromDestination",               ShipPerformFlyToRangeFromDestination,           0 },
     531             :   { "performHold",            ShipPerformHold,                0 },
     532             :   { "performIdle",            ShipPerformIdle,                0 },
     533             :   { "performIntercept",               ShipPerformIntercept,           0 },
     534             :   { "performLandOnPlanet",            ShipPerformLandOnPlanet,                0 },
     535             :   { "performMining",          ShipPerformMining,              0 },
     536             :   { "performScriptedAI",              ShipPerformScriptedAI,          0 },
     537             :   { "performScriptedAttackAI",                ShipPerformScriptedAttackAI,            0 },
     538             :   { "performStop",            ShipPerformStop,                0 },
     539             :   { "performTumble",          ShipPerformTumble,              0 },
     540             : 
     541             :         { "reactToAIMessage",         ShipReactToAIMessage,           1 },
     542             :         { "remove",                                   ShipRemove,                                     0 },
     543             :         { "removeCollisionException", ShipRemoveCollisionException,   1 },
     544             :         { "removeDefenseTarget",   ShipRemoveDefenseTarget,   1 },
     545             :         { "removeEquipment",          ShipRemoveEquipment,            1 },
     546             :         { "requestHelpFromGroup", ShipRequestHelpFromGroup, 0},
     547             :         { "requestDockingInstructions", ShipRequestDockingInstructions, 0},
     548             :         { "recallDockingInstructions", ShipRecallDockingInstructions, 0},
     549             :         { "restoreSubEntities",               ShipRestoreSubEntities,         0 },
     550             :         { "__runLegacyScriptActions", ShipRunLegacyScriptActions, 2 },        // Deliberately not documented
     551             :         { "selectNewMissile",         ShipSelectNewMissile,           0 },
     552             :         { "sendAIMessage",                    ShipSendAIMessage,                      1 },
     553             :         { "setAI",                                    ShipSetAI,                                      1 },
     554             :         { "setBounty",                                ShipSetBounty,                          2 },
     555             :         { "setCargo",                         ShipSetCargo,                           1 },
     556             :         { "setCargoType",                             ShipSetCargoType,                               1 },
     557             :         { "setCrew",                          ShipSetCrew,                            1 },
     558             :         { "setEquipmentStatus",               ShipSetEquipmentStatus,         2 },
     559             :         { "setMaterials",                     ShipSetMaterials,                       1 },
     560             :         { "setScript",                                ShipSetScript,                          1 },
     561             :         { "setShaders",                               ShipSetShaders,                         2 },
     562             :         { "spawn",                                    ShipSpawn,                                      1 },
     563             :         // spawnOne() is defined in the prefix script.
     564             :         { "switchAI",                         ShipSwitchAI,                           1 },
     565             :         { "threatAssessment",         ShipThreatAssessment,           1 },
     566             :         { "throwSpark",                               ShipThrowSpark,                         0 },
     567             :         { "updateEscortFormation",    ShipUpdateEscortFormation,      0 },
     568             :         { 0 }
     569             : };
     570             : 
     571           0 : static JSFunctionSpec sShipStaticMethods[] =
     572             : {
     573             :         // JS name                              Function                                                min args
     574             :         { "keys",                             ShipStaticKeys,                                 0 },
     575             :         { "keysForRole",              ShipStaticKeysForRole,                  1 },
     576             :         { "roleIsInCategory", ShipStaticRoleIsInCategory,             2 },
     577             :         { "roles",                            ShipStaticRoles,                                0 },
     578             :         { "shipDataForKey",           ShipStaticShipDataForKey,               1 },
     579             :         { "setShipDataForKey",  ShipStaticSetShipDataForKey,    2 },
     580             :         { 0 }
     581             : };
     582             : 
     583             : 
     584             : DEFINE_JS_OBJECT_GETTER(JSShipGetShipEntity, &sShipClass, sShipPrototype, ShipEntity)
     585             : 
     586             : 
     587           0 : void InitOOJSShip(JSContext *context, JSObject *global)
     588             : {
     589             :         sShipPrototype = JS_InitClass(context, global, JSEntityPrototype(), &sShipClass, OOJSUnconstructableConstruct, 0, sShipProperties, sShipMethods, NULL, sShipStaticMethods);
     590             :         OOJSRegisterObjectConverter(&sShipClass, OOJSBasicPrivateObjectConverter);
     591             :         OOJSRegisterSubclass(&sShipClass, JSEntityClass());
     592             : }
     593             : 
     594             : 
     595           0 : JSClass *JSShipClass(void)
     596             : {
     597             :         return &sShipClass;
     598             : }
     599             : 
     600             : 
     601           0 : JSObject *JSShipPrototype(void)
     602             : {
     603             :         return sShipPrototype;
     604             : }
     605             : 
     606             : 
     607           0 : static JSBool ShipGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
     608             : {
     609             :         if (!JSID_IS_INT(propID))  return YES;
     610             :         
     611             :         OOJS_NATIVE_ENTER(context)
     612             :         
     613             :         ShipEntity                                      *entity = nil;
     614             :         id                                                      result = nil;
     615             :         
     616             :         if (EXPECT_NOT(!JSShipGetShipEntity(context, this, &entity)))  return NO;
     617             :         if (OOIsStaleEntity(entity)) { *value = JSVAL_VOID; return YES; }
     618             :         
     619             :         switch (JSID_TO_INT(propID))
     620             :         {
     621             :                 case kShip_name:
     622             :                         result = [entity name];
     623             :                         break;
     624             :                         
     625             :                 case kShip_displayName:
     626             :                         result = [entity displayName];
     627             :                         break;
     628             : 
     629             :                 case kShip_shipUniqueName:
     630             :                         result = [entity shipUniqueName];
     631             :                         break;
     632             : 
     633             :                 case kShip_shipClassName:
     634             :                         result = [entity shipClassName];
     635             :                         break;
     636             :                 
     637             :                 case kShip_scanDescription:
     638             :                         result = [entity scanDescriptionForScripting];
     639             :                         break;
     640             : 
     641             :                 case kShip_roles:
     642             :                         result = [[entity roleSet] sortedRoles];
     643             :                         break;
     644             :                 
     645             :                 case kShip_roleWeights:
     646             :                         result = [[entity roleSet] rolesAndProbabilities];
     647             :                         break;
     648             :                 
     649             :                 case kShip_primaryRole:
     650             :                         result = [entity primaryRole];
     651             :                         break;
     652             :                 
     653             :                 case kShip_AI:
     654             :                         result = [[entity getAI] name];
     655             :                         break;
     656             :                 
     657             :                 case kShip_AIState:
     658             :                         result = [[entity getAI] state];
     659             :                         break;
     660             :                 
     661             :                 case kShip_AIFoundTarget:
     662             :                         result = [entity foundTarget];
     663             :                         break;
     664             :                 
     665             :                 case kShip_AIPrimaryAggressor:
     666             :                         result = [entity primaryAggressor];
     667             :                         break;
     668             :                 
     669             :                 case kShip_alertCondition:
     670             :                         return JS_NewNumberValue(context, [entity realAlertCondition], value);
     671             : 
     672             :                 case kShip_autoAI:
     673             :                         *value = OOJSValueFromBOOL([entity hasAutoAI]);
     674             :                         return YES;
     675             : 
     676             :                 case kShip_autoWeapons:
     677             :                         *value = OOJSValueFromBOOL([entity hasAutoWeapons]);
     678             :                         return YES;
     679             :                 
     680             :                 case kShip_accuracy:
     681             :                         return JS_NewNumberValue(context, [entity accuracy], value);
     682             :                         
     683             :                 case kShip_fuel:
     684             :                         return JS_NewNumberValue(context, [entity fuel] * 0.1, value);
     685             :                         
     686             :                 case kShip_fuelChargeRate:
     687             :                         return JS_NewNumberValue(context, [entity fuelChargeRate], value);
     688             :                         
     689             :                 case kShip_bounty:
     690             :                         return JS_NewNumberValue(context, [entity bounty], value);
     691             :                         return YES;
     692             :                         
     693             :                 case kShip_subEntities:
     694             :                         result = [entity subEntitiesForScript];
     695             :                         break;
     696             : 
     697             :                 case kShip_exhausts:
     698             :                         result = [[entity exhaustEnumerator] allObjects];
     699             :                         break;
     700             : 
     701             :                 case kShip_flashers:
     702             :                         result = [[entity flasherEnumerator] allObjects];
     703             :                         break;
     704             :                         
     705             :                 case kShip_subEntityCapacity:
     706             :                         return JS_NewNumberValue(context, [entity maxShipSubEntities], value);
     707             :                         return YES;
     708             :                         
     709             :                 case kShip_subEntityRotation:
     710             :                         return QuaternionToJSValue(context, [entity subEntityRotationalVelocity], value);
     711             : 
     712             :                 case kShip_hasSuspendedAI:
     713             :                         *value = OOJSValueFromBOOL([[entity getAI] hasSuspendedStateMachines]);
     714             :                         return YES;
     715             :                         
     716             :                 case kShip_target:
     717             :                         result = [entity primaryTarget];
     718             :                         break;
     719             :                 
     720             :                 case kShip_defenseTargets:
     721             :                 {
     722             :                         [entity validateDefenseTargets];
     723             :                         result = [NSMutableArray arrayWithCapacity:[entity defenseTargetCount]];
     724             :                         NSEnumerator *defTargets = [entity defenseTargetEnumerator];
     725             :                         Entity *target = nil;
     726             :                         while ((target = [[defTargets nextObject] weakRefUnderlyingObject]))
     727             :                         {
     728             :                                 [result addObject:target];
     729             :                         }
     730             :                         break;
     731             :                 }               
     732             : 
     733             :                 case kShip_crew:
     734             :                         result = [entity crewForScripting];
     735             :                         break;
     736             :         
     737             :                 case kShip_escorts:
     738             :                         result = [[entity escortGroup] memberArrayExcludingLeader];
     739             :                         break;
     740             :                         
     741             :                 case kShip_group:
     742             :                         result = [entity group];
     743             :                         break;
     744             :                         
     745             :                 case kShip_escortGroup:
     746             :                         result = [entity escortGroup];
     747             :                         break;
     748             :                         
     749             :                 case kShip_temperature:
     750             :                         return JS_NewNumberValue(context, [entity temperature] / SHIP_MAX_CABIN_TEMP, value);
     751             :                         
     752             :                 case kShip_heatInsulation:
     753             :                         return JS_NewNumberValue(context, [entity heatInsulation], value);
     754             :                         
     755             :                 case kShip_heading:
     756             :                         return VectorToJSValue(context, [entity forwardVector], value);
     757             :                         
     758             :                 case kShip_energyRechargeRate:
     759             :                         return JS_NewNumberValue(context, [entity energyRechargeRate], value);
     760             : 
     761             :                 case kShip_entityPersonality:
     762             :                         *value = INT_TO_JSVAL([entity entityPersonalityInt]);
     763             :                         return YES;
     764             :                         
     765             :                 case kShip_isBeacon:
     766             :                         *value = OOJSValueFromBOOL([entity isBeacon]);
     767             :                         return YES;
     768             :                         
     769             :                 case kShip_beaconCode:
     770             :                         result = [entity beaconCode];
     771             :                         break;
     772             : 
     773             :                 case kShip_beaconLabel:
     774             :                         result = [entity beaconLabel];
     775             :                         break;
     776             :                 
     777             :                 case kShip_isFrangible:
     778             :                         *value = OOJSValueFromBOOL([entity isFrangible]);
     779             :                         return YES;
     780             :                 
     781             :                 case kShip_isCloaked:
     782             :                         *value = OOJSValueFromBOOL([entity isCloaked]);
     783             :                         return YES;
     784             :                         
     785             :                 case kShip_cloakAutomatic:
     786             :                         *value = OOJSValueFromBOOL([entity hasAutoCloak]);
     787             :                         return YES;
     788             :                         
     789             :                 case kShip_isJamming:
     790             :                         *value = OOJSValueFromBOOL([entity isJammingScanning]);
     791             :                         return YES;
     792             :                 
     793             :                 case kShip_potentialCollider:
     794             :                         result = [entity proximityAlert];
     795             :                         break;
     796             :                 
     797             :                 case kShip_hasHostileTarget:
     798             :                         *value = OOJSValueFromBOOL([entity hasHostileTarget]);
     799             :                         return YES;
     800             :                 
     801             :                 case kShip_hasHyperspaceMotor:
     802             :                         *value = OOJSValueFromBOOL([entity hasHyperspaceMotor]);
     803             :                         return YES;
     804             :                 
     805             :                 case kShip_hyperspaceSpinTime:
     806             :                         return JS_NewNumberValue(context, [entity hyperspaceSpinTime], value);
     807             : 
     808             :                 case kShip_weaponRange:
     809             :                         return JS_NewNumberValue(context, [entity weaponRange], value);
     810             : 
     811             :                 case kShip_weaponFacings:
     812             :                         if ([entity isPlayer])
     813             :                         {
     814             :                                 PlayerEntity *pent = (PlayerEntity*)entity;
     815             :                                 return JS_NewNumberValue(context, [pent availableFacings], value);
     816             :                         }
     817             :                         return JS_NewNumberValue(context, [entity weaponFacings], value);
     818             :                 
     819             :                 case kShip_weaponPositionAft:
     820             :                         result = [entity aftWeaponOffset];
     821             :                         break;
     822             :                 
     823             :                 case kShip_weaponPositionForward:
     824             :                         result = [entity forwardWeaponOffset];
     825             :                         break;
     826             : //                      return VectorToJSValue(context, [entity forwardWeaponOffset], value);
     827             :                 
     828             :                 case kShip_weaponPositionPort:
     829             :                         result = [entity portWeaponOffset];
     830             :                         break;
     831             :                 
     832             :                 case kShip_weaponPositionStarboard:
     833             :                         result = [entity starboardWeaponOffset];
     834             :                         break;
     835             :                 
     836             :                 case kShip_scannerRange:
     837             :                         return JS_NewNumberValue(context, [entity scannerRange], value);
     838             :                 
     839             :                 case kShip_reactionTime:
     840             :                         return JS_NewNumberValue(context, [entity reactionTime], value);
     841             :                         
     842             :                 case kShip_reportAIMessages:
     843             :                         *value = OOJSValueFromBOOL([entity reportAIMessages]);
     844             :                         return YES;
     845             :                 
     846             :                 case kShip_withinStationAegis:
     847             :                         *value = OOJSValueFromBOOL([entity withinStationAegis]);
     848             :                         return YES;
     849             :                 
     850             :                 case kShip_cargoSpaceCapacity:
     851             :                         *value = INT_TO_JSVAL([entity maxAvailableCargoSpace]);
     852             :                         return YES;
     853             :                 
     854             :                 case kShip_cargoSpaceUsed:
     855             :                         *value = INT_TO_JSVAL([entity maxAvailableCargoSpace] - [entity availableCargoSpace]);
     856             :                         return YES;
     857             :                 
     858             :                 case kShip_cargoSpaceAvailable:
     859             :                         *value = INT_TO_JSVAL([entity availableCargoSpace]);
     860             :                         return YES;
     861             : 
     862             :           case kShip_cargoList:
     863             :                         result = [entity cargoListForScripting];
     864             :                         break;
     865             : 
     866             :                 case kShip_extraCargo:
     867             :                         return JS_NewNumberValue(context, [entity extraCargo], value);
     868             :                         return YES;
     869             :                 
     870             :                 case kShip_commodity:
     871             :                         if ([entity commodityAmount] > 0)
     872             :                         {
     873             :                                 result = [entity commodityType];
     874             :                         }
     875             :                         break;
     876             :                         
     877             :                 case kShip_commodityAmount:
     878             :                         *value = INT_TO_JSVAL([entity commodityAmount]);
     879             :                         return YES;
     880             : 
     881             :           case kShip_collisionExceptions:
     882             :                         result = [entity collisionExceptions];
     883             :                         break;
     884             : 
     885             :                         
     886             :                 case kShip_speed:
     887             :                         return JS_NewNumberValue(context, [entity flightSpeed], value);
     888             :                         
     889             :                 case kShip_cruiseSpeed:
     890             :                         return JS_NewNumberValue(context, [entity cruiseSpeed], value);
     891             :                 
     892             :                 case kShip_dataKey:
     893             :                         result = [entity shipDataKey];
     894             :                         break;
     895             :                         
     896             :                 case kShip_desiredRange:
     897             :                         return JS_NewNumberValue(context, [entity desiredRange], value);
     898             :                 
     899             :                 case kShip_desiredSpeed:
     900             :                         return JS_NewNumberValue(context, [entity desiredSpeed], value);
     901             :                         
     902             :                 case kShip_destination:
     903             :                         return HPVectorToJSValue(context, [entity destination], value);
     904             :                 
     905             :                 case kShip_markedForFines:
     906             :                         *value = OOJSValueFromBOOL([entity markedForFines]);
     907             :                         return YES;
     908             : 
     909             :                 case kShip_maxEscorts:
     910             :                         return JS_NewNumberValue(context, [entity maxEscortCount], value);
     911             : 
     912             :                 case kShip_maxPitch:
     913             :                         return JS_NewNumberValue(context, [entity maxFlightPitch], value);
     914             :                 
     915             :                 case kShip_maxSpeed:
     916             :                         return JS_NewNumberValue(context, [entity maxFlightSpeed], value);
     917             :                 
     918             :                 case kShip_maxRoll:
     919             :                         return JS_NewNumberValue(context, [entity maxFlightRoll], value);
     920             :                 
     921             :                 case kShip_maxYaw:
     922             :                         return JS_NewNumberValue(context, [entity maxFlightYaw], value);
     923             : 
     924             :                 case kShip_injectorBurnRate:
     925             :                         return JS_NewNumberValue(context, [entity afterburnerRate], value);
     926             : 
     927             :                 case kShip_injectorSpeedFactor:
     928             :                         return JS_NewNumberValue(context, [entity afterburnerFactor], value);
     929             :                         
     930             :                 case kShip_script:
     931             :                         result = [entity shipScript];
     932             :                         break;
     933             : 
     934             :                 case kShip_AIScript:
     935             :                         result = [entity shipAIScript];
     936             :                         break;
     937             : 
     938             :                 case kShip_AIScriptWakeTime:
     939             :                         return JS_NewNumberValue(context, [entity shipAIScriptWakeTime], value);
     940             :                         break;
     941             : 
     942             :                 case kShip_destinationSystem:
     943             :                         return JS_NewNumberValue(context, [entity destinationSystem], value);
     944             :                         break;
     945             : 
     946             :                 case kShip_homeSystem:
     947             :                         return JS_NewNumberValue(context, [entity homeSystem], value);
     948             :                         break;
     949             :                         
     950             :                 case kShip_isPirate:
     951             :                         *value = OOJSValueFromBOOL([entity isPirate]);
     952             :                         return YES;
     953             :                         
     954             :                 case kShip_isPolice:
     955             :                         *value = OOJSValueFromBOOL([entity isPolice]);
     956             :                         return YES;
     957             :                         
     958             :                 case kShip_isThargoid:
     959             :                         *value = OOJSValueFromBOOL([entity isThargoid]);
     960             :                         return YES;
     961             :                         
     962             :                 case kShip_isTurret:
     963             :                         *value = OOJSValueFromBOOL([entity isTurret]);
     964             :                         return YES;
     965             : 
     966             :                 case kShip_isTrader:
     967             :                         *value = OOJSValueFromBOOL([entity isTrader]);
     968             :                         return YES;
     969             :                         
     970             :                 case kShip_isPirateVictim:
     971             :                         *value = OOJSValueFromBOOL([entity isPirateVictim]);
     972             :                         return YES;
     973             :                         
     974             :                 case kShip_isMissile:
     975             :                         *value = OOJSValueFromBOOL([entity isMissile]);
     976             :                         return YES;
     977             :                         
     978             :                 case kShip_isMine:
     979             :                         *value = OOJSValueFromBOOL([entity isMine]);
     980             :                         return YES;
     981             :                         
     982             :                 case kShip_isWeapon:
     983             :                         *value = OOJSValueFromBOOL([entity isWeapon]);
     984             :                         return YES;
     985             :                         
     986             :                 case kShip_isRock:
     987             :                         *value = OOJSValueFromBOOL([entity scanClass] == CLASS_ROCK);   // hermits and asteroids!
     988             :                         return YES;
     989             : 
     990             :                 case kShip_isMinable:
     991             :                         *value = OOJSValueFromBOOL([entity isMinable]);
     992             :                         return YES;
     993             :                         
     994             :                 case kShip_isBoulder:
     995             :                         *value = OOJSValueFromBOOL([entity isBoulder]);
     996             :                         return YES;
     997             : 
     998             :                 case kShip_isFleeing:
     999             :                         if ([entity isPlayer])
    1000             :                         {
    1001             :                                 *value = OOJSValueFromBOOL([(PlayerEntity*)entity fleeingStatus] >= PLAYER_FLEEING_CARGO);
    1002             :                         }
    1003             :                         else
    1004             :                         {
    1005             :                                 *value = OOJSValueFromBOOL([entity behaviour] == BEHAVIOUR_FLEE_TARGET || [entity behaviour] == BEHAVIOUR_FLEE_EVASIVE_ACTION);
    1006             :                         }
    1007             :                         return YES;
    1008             :                         
    1009             :                 case kShip_isCargo:
    1010             :                         *value = OOJSValueFromBOOL([entity scanClass] == CLASS_CARGO && [entity commodityAmount] > 0);
    1011             :                         return YES;
    1012             :                         
    1013             :                 case kShip_isDerelict:
    1014             :                         *value = OOJSValueFromBOOL([entity isHulk]);
    1015             :                         return YES;
    1016             :                         
    1017             :                 case kShip_isPiloted:
    1018             :                         *value = OOJSValueFromBOOL([entity isPlayer] || [[entity crew] count] > 0);
    1019             :                         return YES;
    1020             :                         
    1021             :                 case kShip_scriptedMisjump:
    1022             :                         *value = OOJSValueFromBOOL([entity scriptedMisjump]);
    1023             :                         return YES;
    1024             : 
    1025             :                 case kShip_scriptedMisjumpRange:
    1026             :                         return JS_NewNumberValue(context, [entity scriptedMisjumpRange], value);
    1027             :                         
    1028             :                 case kShip_scriptInfo:
    1029             :                         result = [entity scriptInfo];
    1030             :                         if (result == nil)  result = [NSDictionary dictionary]; // empty rather than null
    1031             :                         break;
    1032             :                         
    1033             :                 case kShip_sunGlareFilter:
    1034             :                         return JS_NewNumberValue(context, [entity sunGlareFilter], value);
    1035             :                         
    1036             :                 case kShip_trackCloseContacts:
    1037             :                         *value = OOJSValueFromBOOL([entity trackCloseContacts]);
    1038             :                         return YES;
    1039             :                         
    1040             :                 case kShip_passengerCount:
    1041             :                         return JS_NewNumberValue(context, [entity passengerCount], value);
    1042             : 
    1043             :                 case kShip_parcelCount:
    1044             :                         return JS_NewNumberValue(context, [entity parcelCount], value);
    1045             :                         
    1046             :                 case kShip_passengerCapacity:
    1047             :                         return JS_NewNumberValue(context, [entity passengerCapacity], value);
    1048             :                 
    1049             :                 case kShip_missileCapacity:
    1050             :                         return JS_NewNumberValue(context, [entity missileCapacity], value);
    1051             :                         
    1052             :                 case kShip_missileLoadTime:
    1053             :                         return JS_NewNumberValue(context, [entity missileLoadTime], value);
    1054             :                 
    1055             :                 case kShip_savedCoordinates:
    1056             :                         return HPVectorToJSValue(context,[entity coordinates], value);
    1057             :                 
    1058             :                 case kShip_equipment:
    1059             :                         result = [entity equipmentListForScripting];
    1060             :                         break;
    1061             :                         
    1062             :                 case kShip_currentWeapon:
    1063             :                         result = [entity weaponTypeForFacing:[entity currentWeaponFacing] strict:YES];
    1064             :                         break;
    1065             :                 
    1066             :                 case kShip_forwardWeapon:
    1067             :                         result = [entity weaponTypeForFacing:WEAPON_FACING_FORWARD strict:YES];
    1068             :                         break;
    1069             :                 
    1070             :                 case kShip_aftWeapon:
    1071             :                         result = [entity weaponTypeForFacing:WEAPON_FACING_AFT strict:YES];
    1072             :                         break;
    1073             :                 
    1074             :                 case kShip_portWeapon:
    1075             :                         result = [entity weaponTypeForFacing:WEAPON_FACING_PORT strict:YES];
    1076             :                         break;
    1077             :                 
    1078             :                 case kShip_starboardWeapon:
    1079             :                         result = [entity weaponTypeForFacing:WEAPON_FACING_STARBOARD strict:YES];
    1080             :                         break;
    1081             :                 
    1082             :                 case kShip_laserHeatLevel:
    1083             :                         return JS_NewNumberValue(context, [entity laserHeatLevel], value);
    1084             :                 
    1085             :                 case kShip_laserHeatLevelAft:
    1086             :                         return JS_NewNumberValue(context, [entity laserHeatLevelAft], value);
    1087             :                 
    1088             :                 case kShip_laserHeatLevelForward:
    1089             :                         return JS_NewNumberValue(context, [entity laserHeatLevelForward], value);
    1090             :                 
    1091             :                 case kShip_laserHeatLevelPort:
    1092             :                         return JS_NewNumberValue(context, [entity laserHeatLevelPort], value);
    1093             :                 
    1094             :                 case kShip_laserHeatLevelStarboard:
    1095             :                         return JS_NewNumberValue(context, [entity laserHeatLevelStarboard], value);
    1096             :                 
    1097             :                 case kShip_missiles:
    1098             :                         result = [entity missilesList];
    1099             :                         break;
    1100             :                 
    1101             :                 case kShip_passengers:
    1102             :                         result = [entity passengerListForScripting];
    1103             :                         break;
    1104             : 
    1105             :                 case kShip_parcels:
    1106             :                         result = [entity parcelListForScripting];
    1107             :                         break;
    1108             :                 
    1109             :                 case kShip_contracts:
    1110             :                         result = [entity contractListForScripting];
    1111             :                         break;
    1112             :                         
    1113             :         case kShip_dockingInstructions:
    1114             :                         result = [entity dockingInstructions];
    1115             :                         break;
    1116             : 
    1117             :                 case kShip_scannerDisplayColor1:
    1118             :                         result = [[entity scannerDisplayColor1] normalizedArray];
    1119             :                         break;
    1120             :                         
    1121             :                 case kShip_scannerDisplayColor2:
    1122             :                         result = [[entity scannerDisplayColor2] normalizedArray];
    1123             :                         break;
    1124             : 
    1125             :                 case kShip_scannerHostileDisplayColor1:
    1126             :                         result = [[entity scannerDisplayColorHostile1] normalizedArray];
    1127             :                         break;
    1128             :                         
    1129             :                 case kShip_scannerHostileDisplayColor2:
    1130             :                         result = [[entity scannerDisplayColorHostile2] normalizedArray];
    1131             :                         break;
    1132             :                         
    1133             :                 case kShip_exhaustEmissiveColor:
    1134             :                         result = [[entity exhaustEmissiveColor] normalizedArray];
    1135             :                         break;
    1136             :                         
    1137             :                 case kShip_maxThrust:
    1138             :                         return JS_NewNumberValue(context, [entity maxThrust], value);
    1139             :                         
    1140             :                 case kShip_thrust:
    1141             :                         return JS_NewNumberValue(context, [entity thrust], value);
    1142             :                         
    1143             :                 case kShip_lightsActive:
    1144             :                         *value = OOJSValueFromBOOL([entity lightsActive]);
    1145             :                         return YES;
    1146             :                         
    1147             :                 case kShip_vectorRight:
    1148             :                         return VectorToJSValue(context, [entity rightVector], value);
    1149             :                         
    1150             :                 case kShip_vectorForward:
    1151             :                         return VectorToJSValue(context, [entity forwardVector], value);
    1152             :                         
    1153             :                 case kShip_vectorUp:
    1154             :                         return VectorToJSValue(context, [entity upVector], value);
    1155             :                         
    1156             :                 case kShip_velocity:
    1157             :                         return VectorToJSValue(context, [entity velocity], value);
    1158             :                         
    1159             :                 case kShip_thrustVector:
    1160             :                         return VectorToJSValue(context, [entity thrustVector], value);
    1161             :                 
    1162             :                 case kShip_pitch:
    1163             :                         return JS_NewNumberValue(context, [entity flightPitch], value);
    1164             :                 
    1165             :                 case kShip_roll:
    1166             :                         return JS_NewNumberValue(context, [entity flightRoll], value);
    1167             :                 
    1168             :                 case kShip_yaw:
    1169             :                         return JS_NewNumberValue(context, [entity flightYaw], value);
    1170             :                 
    1171             :                 case kShip_boundingBox:
    1172             :                         {
    1173             :                                 Vector bbvect;
    1174             :                                 BoundingBox box;
    1175             :                                 
    1176             :                                 if ([entity isSubEntity])
    1177             :                                 {
    1178             :                                         box = [entity boundingBox];
    1179             :                                 }
    1180             :                                 else
    1181             :                                 {
    1182             :                                         box = [entity totalBoundingBox];
    1183             :                                 }
    1184             :                                 bounding_box_get_dimensions(box,&bbvect.x,&bbvect.y,&bbvect.z);
    1185             :                                 return VectorToJSValue(context, bbvect, value);
    1186             :                         }
    1187             :                         
    1188             :                 default:
    1189             :                         OOJSReportBadPropertySelector(context, this, propID, sShipProperties);
    1190             :                         return NO;
    1191             :         }
    1192             :         
    1193             :         *value = OOJSValueFromNativeObject(context, result);
    1194             :         return YES;
    1195             :         
    1196             :         OOJS_NATIVE_EXIT
    1197             : }
    1198             : 
    1199             : 
    1200           0 : static JSBool ShipSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
    1201             : {
    1202             :         if (!JSID_IS_INT(propID))  return YES;
    1203             :         
    1204             :         OOJS_NATIVE_ENTER(context)
    1205             :         
    1206             :         ShipEntity                                      *entity = nil;
    1207             :         ShipEntity                                      *target = nil;
    1208             :         NSString                                        *sValue = nil;
    1209             :         jsdouble                                        fValue;
    1210             :         int32                                           iValue;
    1211             :         JSBool                                          bValue;
    1212             :         Vector                                          vValue;
    1213             :         Quaternion                                      qValue;
    1214             :         HPVector                                                hpvValue;
    1215             :         OOShipGroup                                     *group = nil;
    1216             :         OOColor                                         *colorForScript = nil;
    1217             :         BOOL exists;
    1218             :         
    1219             :         if (EXPECT_NOT(!JSShipGetShipEntity(context, this, &entity)))  return NO;
    1220             :         if (OOIsStaleEntity(entity))  return YES;
    1221             : 
    1222             :         NSCAssert(![entity isTemplateCargoPod], @"-OOJSShip: a template cargo pod has become accessible to Javascript");
    1223             :         
    1224             :         switch (JSID_TO_INT(propID))
    1225             :         {
    1226             :                 case kShip_name:
    1227             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1228             :                         
    1229             :                         sValue = OOStringFromJSValue(context,*value);
    1230             :                         if (sValue != nil)
    1231             :                         {
    1232             :                                 [entity setName:sValue];
    1233             :                                 return YES;
    1234             :                         }
    1235             :                         break;
    1236             :                         
    1237             :                 case kShip_displayName:
    1238             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1239             :                         
    1240             :                         sValue = OOStringFromJSValue(context,*value);
    1241             :                         if (sValue != nil)
    1242             :                         {
    1243             :                                 [entity setDisplayName:sValue];
    1244             :                                 return YES;
    1245             :                         }
    1246             :                         break;
    1247             : 
    1248             :                 case kShip_shipUniqueName:
    1249             :                         sValue = OOStringFromJSValue(context,*value);
    1250             :                         if (sValue != nil)
    1251             :                         {
    1252             :                                 [entity setShipUniqueName:sValue];
    1253             :                                 return YES;
    1254             :                         }
    1255             :                         break;
    1256             : 
    1257             :                 case kShip_shipClassName:
    1258             :                         sValue = OOStringFromJSValue(context,*value);
    1259             :                         if (sValue != nil)
    1260             :                         {
    1261             :                                 [entity setShipClassName:sValue];
    1262             :                                 return YES;
    1263             :                         }
    1264             :                         break;
    1265             : 
    1266             : 
    1267             :                 case kShip_scanDescription:
    1268             :                         sValue = OOStringFromJSValue(context,*value);
    1269             :                         // can set to nil
    1270             :                         [entity setScanDescription:sValue];
    1271             :                         return YES;
    1272             :                 
    1273             :                 case kShip_primaryRole:
    1274             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1275             :                         
    1276             :                         sValue = OOStringFromJSValue(context,*value);
    1277             :                         if (sValue != nil)
    1278             :                         {
    1279             :                                 [entity setPrimaryRole:sValue];
    1280             :                                 return YES;
    1281             :                         }
    1282             :                         break;
    1283             :                 
    1284             :                 case kShip_AIState:
    1285             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1286             :                         
    1287             :                         sValue = OOStringFromJSValue(context,*value);
    1288             :                         if (sValue != nil)
    1289             :                         {
    1290             :                                 [[entity getAI] setState:sValue];
    1291             :                                 return YES;
    1292             :                         }
    1293             :                         break;
    1294             :                 
    1295             :                 case kShip_beaconCode:
    1296             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1297             :                         
    1298             :                         sValue = OOStringFromJSValue(context,*value);
    1299             :                         if ([sValue length] == 0) 
    1300             :                         {
    1301             :                                 if ([entity isBeacon]) 
    1302             :                                 {
    1303             :                                         [UNIVERSE clearBeacon:entity];
    1304             :                                         if ([PLAYER nextBeacon] == entity)
    1305             :                                         {
    1306             :                                                 [PLAYER setCompassMode:COMPASS_MODE_PLANET];
    1307             :                                         }
    1308             :                                 }
    1309             :                         }
    1310             :                         else 
    1311             :                         {
    1312             :                                 if ([entity isBeacon]) 
    1313             :                                 {
    1314             :                                         [entity setBeaconCode:sValue];
    1315             :                                 }
    1316             :                                 else // Universe needs to update beacon lists in this case only
    1317             :                                 {
    1318             :                                         [entity setBeaconCode:sValue];
    1319             :                                         [UNIVERSE setNextBeacon:entity];
    1320             :                                 }
    1321             :                         }
    1322             :                         return YES;
    1323             :                         break;
    1324             : 
    1325             :                 case kShip_beaconLabel:
    1326             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1327             :                         sValue = OOStringFromJSValue(context,*value);
    1328             :                         if (sValue != nil)
    1329             :                         {
    1330             :                                 [entity setBeaconLabel:sValue];
    1331             :                                 return YES;
    1332             :                         }
    1333             :                         break;
    1334             :                         
    1335             :                 case kShip_accuracy:
    1336             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1337             :                         
    1338             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1339             :                         {
    1340             :                                 [entity setAccuracy:fValue];
    1341             :                                 return YES;
    1342             :                         }
    1343             :                         break;
    1344             :                 
    1345             :                 case kShip_fuel:
    1346             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1347             :                         {
    1348             :                                 fValue = OOClamp_0_max_d(fValue, MAX_JUMP_RANGE);
    1349             :                                 [entity setFuel:lround(fValue * 10.0)];
    1350             :                                 return YES;
    1351             :                         }
    1352             :                         break;
    1353             : 
    1354             :                 case kShip_entityPersonality:
    1355             :                         if (JS_ValueToInt32(context, *value, &iValue))
    1356             :                         {
    1357             :                                 if (iValue < 0 || iValue > (int32)ENTITY_PERSONALITY_MAX)
    1358             :                                 {
    1359             :                                         OOJSReportError(context, @"ship.%@ must be >= 0 and <= %u.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties),ENTITY_PERSONALITY_MAX);
    1360             :                                         return NO;
    1361             :                                 }
    1362             :                                 [entity setEntityPersonalityInt: iValue];
    1363             :                                 return YES;
    1364             :                         }
    1365             : 
    1366             :                 case kShip_hyperspaceSpinTime:
    1367             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1368             :                         {
    1369             :                                 [entity setHyperspaceSpinTime:fValue];
    1370             :                                 return YES;
    1371             :                         }
    1372             :                         break;
    1373             :                         
    1374             :                 case kShip_bounty:
    1375             :                         if (JS_ValueToInt32(context, *value, &iValue))
    1376             :                         {
    1377             :                                 if (iValue < 0)  iValue = 0;
    1378             :                                 [entity setBounty:iValue withReason:kOOLegalStatusReasonByScript];
    1379             :                                 return YES;
    1380             :                         }
    1381             :                         break;
    1382             : 
    1383             :                 case kShip_cargoSpaceCapacity:
    1384             :                         if (JS_ValueToInt32(context, *value, &iValue))
    1385             :                         {
    1386             :                                 if (iValue < 0)  iValue = 0;
    1387             :                                 // OXPs using equipment with weight requirements may make us end up with more allocated
    1388             :                                 // cargo space than what we have available. Do not let iValue become negative.
    1389             :                                 if ([entity maxAvailableCargoSpace] < [entity availableCargoSpace])
    1390             :                                 {
    1391             :                                         iValue = 0;
    1392             :                                 }
    1393             :                                 else if ((OOCargoQuantity)iValue < [entity maxAvailableCargoSpace] - [entity availableCargoSpace])
    1394             :                                 {
    1395             :                                         iValue = [entity maxAvailableCargoSpace] - [entity availableCargoSpace];
    1396             :                                 }
    1397             :                                 [entity setMaxAvailableCargoSpace:iValue];
    1398             :                                 return YES;
    1399             :                         }
    1400             :                         break;
    1401             : 
    1402             : 
    1403             :                 case kShip_destinationSystem:
    1404             :                         if (JS_ValueToInt32(context, *value, &iValue))
    1405             :                         {
    1406             :                                 if (iValue < 0)  iValue = 0;
    1407             :                                 [entity setDestinationSystem:iValue];
    1408             :                                 return YES;
    1409             :                         }
    1410             :                         break;
    1411             : 
    1412             :                 case kShip_homeSystem:
    1413             :                         if (JS_ValueToInt32(context, *value, &iValue))
    1414             :                         {
    1415             :                                 if (iValue < 0)  iValue = 0;
    1416             :                                 [entity setHomeSystem:iValue];
    1417             :                                 return YES;
    1418             :                         }
    1419             :                         break;
    1420             :                 
    1421             :                 case kShip_target:
    1422             :                         if (JSVAL_IS_NULL(*value))
    1423             :                         {
    1424             :                                 [entity setTargetForScript:nil];
    1425             :                                 return YES;
    1426             :                         }
    1427             :                         else if (JSValueToEntity(context, *value, &target) && [target isKindOfClass:[ShipEntity class]])
    1428             :                         {
    1429             :                                 [entity setTargetForScript:target];
    1430             :                                 return YES;
    1431             :                         }
    1432             :                         break;
    1433             :                 
    1434             :                 case kShip_AIFoundTarget:
    1435             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1436             :                         
    1437             :                         if (JSVAL_IS_NULL(*value))
    1438             :                         {
    1439             :                                 [entity setFoundTarget:nil];
    1440             :                                 return YES;
    1441             :                         }
    1442             :                         else if (JSValueToEntity(context, *value, &target) && [target isKindOfClass:[ShipEntity class]])
    1443             :                         {
    1444             :                                 [entity setFoundTarget:target];
    1445             :                                 return YES;
    1446             :                         }
    1447             :                         break;
    1448             :                 
    1449             :                 case kShip_AIPrimaryAggressor:
    1450             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1451             :                         
    1452             :                         if (JSVAL_IS_NULL(*value))
    1453             :                         {
    1454             :                                 [entity setPrimaryAggressor:nil];
    1455             :                                 return YES;
    1456             :                         }
    1457             :                         else if (JSValueToEntity(context, *value, &target) && [target isKindOfClass:[ShipEntity class]])
    1458             :                         {
    1459             :                                 [entity setPrimaryAggressor:target];
    1460             :                                 return YES;
    1461             :                         }
    1462             :                         break;
    1463             :                         
    1464             :                 case kShip_group:
    1465             :                         group = OOJSNativeObjectOfClassFromJSValue(context, *value, [OOShipGroup class]);
    1466             :                         if (group != nil || JSVAL_IS_NULL(*value))
    1467             :                         {
    1468             :                                 [entity setGroup:group];
    1469             :                                 return YES;
    1470             :                         }
    1471             :                         break;
    1472             :                 
    1473             :                 case kShip_AIScriptWakeTime:
    1474             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1475             :                         {
    1476             :                                 [entity setAIScriptWakeTime:fValue];
    1477             :                                 return YES;
    1478             :                         }
    1479             :                         break;
    1480             : 
    1481             :                 case kShip_temperature:
    1482             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1483             :                         {
    1484             :                                 fValue = fmax(fValue, 0.0);
    1485             :                                 [entity setTemperature:fValue * SHIP_MAX_CABIN_TEMP];
    1486             :                                 return YES;
    1487             :                         }
    1488             :                         break;
    1489             :                 
    1490             :                 case kShip_heatInsulation:
    1491             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1492             :                         {
    1493             :                                 fValue = fmax(fValue, 0.125);
    1494             :                                 [entity setHeatInsulation:fValue];
    1495             :                                 return YES;
    1496             :                         }
    1497             :                         break;
    1498             :                 
    1499             :                 case kShip_isCloaked:
    1500             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1501             :                         {
    1502             :                                 [entity setCloaked:bValue];
    1503             :                                 return YES;
    1504             :                         }
    1505             :                         break;
    1506             :                         
    1507             :                 case kShip_cloakAutomatic:
    1508             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1509             :                         {
    1510             :                                 [entity setAutoCloak:bValue];
    1511             :                                 return YES;
    1512             :                         }
    1513             :                         break;
    1514             :                         
    1515             :                 case kShip_missileLoadTime:
    1516             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1517             :                         {
    1518             :                                 [entity setMissileLoadTime:fmax(0.0, fValue)];
    1519             :                                 return YES;
    1520             :                         }
    1521             :                         break;
    1522             :                 
    1523             :                 case kShip_reactionTime:
    1524             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1525             :                         
    1526             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1527             :                         {
    1528             :                                 [entity setReactionTime:fValue];
    1529             :                                 return YES;
    1530             :                         }
    1531             :                         break;
    1532             : 
    1533             :                 case kShip_reportAIMessages:
    1534             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1535             :                         {
    1536             :                                 [entity setReportAIMessages:bValue];
    1537             :                                 return YES;
    1538             :                         }
    1539             :                         break;
    1540             :                         
    1541             :                 case kShip_trackCloseContacts:
    1542             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1543             :                         {
    1544             :                                 [entity setTrackCloseContacts:bValue];
    1545             :                                 return YES;
    1546             :                         }
    1547             :                         break;
    1548             :                 
    1549             :                 case kShip_isBoulder:
    1550             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1551             :                         
    1552             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1553             :                         {
    1554             :                                 [entity setIsBoulder:bValue];
    1555             :                                 return YES;
    1556             :                         }
    1557             :                         break;
    1558             :                 
    1559             :                 case kShip_destination:
    1560             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1561             :                         
    1562             :                         if (JSValueToHPVector(context, *value, &hpvValue))
    1563             :                         {
    1564             :                                 // use setEscortDestination rather than setDestination as
    1565             :                                 // scripted amendments shouldn't necessarily reset frustration
    1566             :                                 [entity setEscortDestination:hpvValue];
    1567             :                                 return YES;
    1568             :                         }
    1569             :                         break;
    1570             :                         
    1571             :                 case kShip_desiredSpeed:
    1572             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1573             :                         
    1574             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1575             :                         {
    1576             :                                 [entity setDesiredSpeed:fmax(fValue, 0.0)];
    1577             :                                 return YES;
    1578             :                         }
    1579             :                         break;
    1580             :                 
    1581             :                 case kShip_desiredRange:
    1582             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1583             :                         
    1584             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1585             :                         {
    1586             :                                 [entity setDesiredRange:fmax(fValue, 0.0)];
    1587             :                                 return YES;
    1588             :                         }
    1589             :                         break;
    1590             :                 
    1591             :                 case kShip_savedCoordinates:
    1592             :                         if (JSValueToHPVector(context, *value, &hpvValue))
    1593             :                         {
    1594             :                                 [entity setCoordinate:hpvValue];
    1595             :                                 return YES;
    1596             :                         }
    1597             :                         break;
    1598             :                         
    1599             :                 case kShip_scannerDisplayColor1:
    1600             :                         colorForScript = [OOColor colorWithDescription:OOJSNativeObjectFromJSValue(context, *value)];
    1601             :                         if (colorForScript != nil || JSVAL_IS_NULL(*value))
    1602             :                         {
    1603             :                                 [entity setScannerDisplayColor1:colorForScript];
    1604             :                                 return YES;
    1605             :                         }
    1606             :                         break;
    1607             :                         
    1608             :                 case kShip_scannerDisplayColor2:
    1609             :                         colorForScript = [OOColor colorWithDescription:OOJSNativeObjectFromJSValue(context, *value)];
    1610             :                         if (colorForScript != nil || JSVAL_IS_NULL(*value))
    1611             :                         {
    1612             :                                 [entity setScannerDisplayColor2:colorForScript];
    1613             :                                 return YES;
    1614             :                         }
    1615             :                         break;
    1616             :                         
    1617             :                 case kShip_scannerHostileDisplayColor1:
    1618             :                         colorForScript = [OOColor colorWithDescription:OOJSNativeObjectFromJSValue(context, *value)];
    1619             :                         if (colorForScript != nil || JSVAL_IS_NULL(*value))
    1620             :                         {
    1621             :                                 [entity setScannerDisplayColorHostile1:colorForScript];
    1622             :                                 return YES;
    1623             :                         }
    1624             :                         break;
    1625             :                         
    1626             :                 case kShip_scannerHostileDisplayColor2:
    1627             :                         colorForScript = [OOColor colorWithDescription:OOJSNativeObjectFromJSValue(context, *value)];
    1628             :                         if (colorForScript != nil || JSVAL_IS_NULL(*value))
    1629             :                         {
    1630             :                                 [entity setScannerDisplayColorHostile2:colorForScript];
    1631             :                                 return YES;
    1632             :                         }
    1633             :                         break;
    1634             :                         
    1635             :                 case kShip_exhaustEmissiveColor:
    1636             :                         colorForScript = [OOColor colorWithDescription:OOJSNativeObjectFromJSValue(context, *value)];
    1637             :                         if (colorForScript != nil || JSVAL_IS_NULL(*value))
    1638             :                         {
    1639             :                                 [entity setExhaustEmissiveColor:colorForScript];
    1640             :                                 return YES;
    1641             :                         }
    1642             :                         break;
    1643             :                         
    1644             :                 case kShip_scriptedMisjump:
    1645             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1646             :                         {
    1647             :                                 [entity setScriptedMisjump:bValue];
    1648             :                                 return YES;
    1649             :                         }
    1650             :                         break;
    1651             : 
    1652             :                 case kShip_scriptedMisjumpRange:
    1653             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1654             :                         {
    1655             :                                 if (fValue > 0.0 && fValue < 1.0)
    1656             :                                 {
    1657             :                                         [entity setScriptedMisjumpRange:fValue];
    1658             :                                         return YES;
    1659             :                                 }
    1660             :                                 else
    1661             :                                 {
    1662             :                                         OOJSReportError(context, @"ship.%@ must be > 0.0 and < 1.0.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties));
    1663             :                                         return NO;
    1664             :                                 }
    1665             :                         }
    1666             :                         break;
    1667             : 
    1668             :                 case kShip_subEntityRotation:
    1669             :                         if (JSValueToQuaternion(context, *value, &qValue))
    1670             :                         {
    1671             :                                 [entity setSubEntityRotationalVelocity:qValue];
    1672             :                                 return YES;
    1673             :                         }
    1674             :                         break;
    1675             : 
    1676             :                         
    1677             :                 case kShip_sunGlareFilter:
    1678             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1679             :                         {
    1680             :                                 if (fValue >= 0.0f && fValue <= 1.0f)
    1681             :                                 {
    1682             :                                         [entity setSunGlareFilter:fValue];
    1683             :                                         return YES;
    1684             :                                 }
    1685             :                                 else
    1686             :                                 {
    1687             :                                         OOJSReportError(context, @"ship.%@ must be > 0.0 and < 1.0.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties));
    1688             :                                         return NO;
    1689             :                                 }
    1690             :                         }
    1691             :                         break;
    1692             :                         
    1693             :                 case kShip_thrust:
    1694             : //                      if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1695             :                         
    1696             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1697             :                         {
    1698             :                                 [entity setThrust:OOClamp_0_max_f(fValue, [entity maxThrust])];
    1699             :                                 return YES;
    1700             :                         }
    1701             :                         break;
    1702             : 
    1703             :                 case kShip_maxPitch:
    1704             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1705             :                         {
    1706             :                                 if (fValue < 0)
    1707             :                                 {
    1708             :                                         OOJSReportError(context, @"ship.maxPitch cannot be negative.");
    1709             :                                         return NO;
    1710             :                                 }
    1711             :                                 [entity setMaxFlightPitch:fValue];
    1712             :                                 return YES;
    1713             :                         }
    1714             :                         break;
    1715             : 
    1716             :                 
    1717             :                 case kShip_maxSpeed:
    1718             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1719             :                         {
    1720             :                                 if (fValue < 0)
    1721             :                                 {
    1722             :                                         OOJSReportError(context, @"ship.maxSpeed cannot be negative.");
    1723             :                                         return NO;
    1724             :                                 }
    1725             :                                 [entity setMaxFlightSpeed:fValue];
    1726             :                                 return YES;
    1727             :                         }
    1728             :                         break;
    1729             :                 
    1730             :                 case kShip_maxRoll:
    1731             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1732             :                         {
    1733             :                                 if (fValue < 0)
    1734             :                                 {
    1735             :                                         OOJSReportError(context, @"ship.maxRoll cannot be negative.");
    1736             :                                         return NO;
    1737             :                                 }
    1738             :                                 [entity setMaxFlightRoll:fValue];
    1739             :                                 return YES;
    1740             :                         }
    1741             :                         break;
    1742             :                 
    1743             :                 case kShip_maxYaw:
    1744             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1745             :                         {
    1746             :                                 if (fValue < 0)
    1747             :                                 {
    1748             :                                         OOJSReportError(context, @"ship.maxYaw cannot be negative.");
    1749             :                                         return NO;
    1750             :                                 }
    1751             :                                 [entity setMaxFlightYaw:fValue];
    1752             :                                 return YES;
    1753             :                         }
    1754             :                         break;
    1755             : 
    1756             :                 case kShip_injectorBurnRate:
    1757             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1758             :                         {
    1759             :                                 if (fValue < 0)
    1760             :                                 {
    1761             :                                         OOJSReportError(context, @"ship.injectorBurnRate cannot be negative.");
    1762             :                                         return NO;
    1763             :                                 }
    1764             :                                 [entity setAfterburnerRate:fValue];
    1765             :                                 return YES;
    1766             :                         }
    1767             :                         break;
    1768             : 
    1769             :                 case kShip_injectorSpeedFactor:
    1770             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1771             :                         {
    1772             :                                 if (fValue < 1)
    1773             :                                 {
    1774             :                                         OOJSReportError(context, @"ship.injectorSpeedFactor cannot be less than 1.0.");
    1775             :                                         return NO;
    1776             :                                 }
    1777             : #if OO_VARIABLE_TORUS_SPEED
    1778             :                                 else if (fValue > MIN_HYPERSPEED_FACTOR)
    1779             :                                 {
    1780             :                                         OOJSReportError(context, @"ship.injectorSpeedFactor cannot be higher than minimum torus speed factor (%f).",MIN_HYPERSPEED_FACTOR);
    1781             : #else
    1782             :                                 else if (fValue > HYPERSPEED_FACTOR)
    1783             :                                 {
    1784             :                                         OOJSReportError(context, @"ship.injectorSpeedFactor cannot be higher than torus speed factor (%f).",HYPERSPEED_FACTOR);
    1785             : #endif
    1786             :                                         return NO;
    1787             :                                 }
    1788             :                                 [entity setAfterburnerFactor:fValue];
    1789             :                                 return YES;
    1790             :                         }
    1791             :                         break;
    1792             : 
    1793             : 
    1794             :                 case kShip_maxThrust:
    1795             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1796             :                         {
    1797             :                                 if (fValue < 0)
    1798             :                                 {
    1799             :                                         OOJSReportError(context, @"ship.maxThrust cannot be negative.");
    1800             :                                         return NO;
    1801             :                                 }
    1802             :                                 [entity setMaxThrust:fValue];
    1803             :                                 return YES;
    1804             :                         }
    1805             :                         break;
    1806             : 
    1807             :                 case kShip_energyRechargeRate:
    1808             :                         if (JS_ValueToNumber(context, *value, &fValue))
    1809             :                         {
    1810             :                                 if (fValue < 0)
    1811             :                                 {
    1812             :                                         OOJSReportError(context, @"ship.energyRechargeRate cannot be negative.");
    1813             :                                         return NO;
    1814             :                                 }
    1815             :                                 [entity setEnergyRechargeRate:fValue];
    1816             :                                 return YES;
    1817             :                         }
    1818             :                         break;
    1819             :                         
    1820             : 
    1821             :                 case kShip_lightsActive:
    1822             :                         if (JS_ValueToBoolean(context, *value, &bValue))
    1823             :                         {
    1824             :                                 if (bValue)  [entity switchLightsOn];
    1825             :                                 else  [entity switchLightsOff];
    1826             :                                 return YES;
    1827             :                         }
    1828             :                         break;
    1829             :                         
    1830             :                 case kShip_velocity:
    1831             :                         if (JSValueToVector(context, *value, &vValue))
    1832             :                         {
    1833             :                                 [entity setTotalVelocity:vValue];
    1834             :                                 return YES;
    1835             :                         }
    1836             :                         break;
    1837             :                         
    1838             :                 case kShip_portWeapon:
    1839             :                 case kShip_starboardWeapon:
    1840             :                 case kShip_aftWeapon:
    1841             :                 case kShip_forwardWeapon:
    1842             :                 case kShip_currentWeapon:
    1843             :                         sValue = JSValueToEquipmentKeyRelaxed(context, *value, &exists);
    1844             :                         if (sValue == nil) 
    1845             :                         {
    1846             :                                 sValue = @"EQ_WEAPON_NONE";
    1847             :                         }
    1848             :                         OOWeaponFacing facing = WEAPON_FACING_FORWARD;
    1849             :                         switch (JSID_TO_INT(propID))
    1850             :                         {
    1851             :                                 case kShip_aftWeapon: 
    1852             :                                         facing = WEAPON_FACING_AFT;
    1853             :                                         break;
    1854             :                                 case kShip_forwardWeapon: 
    1855             :                                         facing = WEAPON_FACING_FORWARD;
    1856             :                                         break;
    1857             :                                 case kShip_portWeapon: 
    1858             :                                         facing = WEAPON_FACING_PORT;
    1859             :                                         break;
    1860             :                                 case kShip_starboardWeapon:
    1861             :                                         facing = WEAPON_FACING_STARBOARD;
    1862             :                                         break;
    1863             :                                 case kShip_currentWeapon:
    1864             :                                         facing = [entity currentWeaponFacing];
    1865             :                                         break;
    1866             :                         }
    1867             :                         if ([entity isPlayer])
    1868             :                         {
    1869             :                                 PlayerEntity *pent = (PlayerEntity*)entity;
    1870             :                                 [pent setWeaponMount:facing toWeapon:sValue inContext:@"scripted"];
    1871             :                         }
    1872             :                         else 
    1873             :                         {
    1874             :                                 [entity setWeaponMount:facing toWeapon:sValue];
    1875             :                         }
    1876             :                         return YES;
    1877             : 
    1878             :                 case kShip_maxEscorts:
    1879             :                         if (EXPECT_NOT([entity isPlayer]))  goto playerReadOnly;
    1880             :                         
    1881             :                         if (JS_ValueToInt32(context, *value, &iValue))
    1882             :                         {
    1883             :                                 if ((NSInteger)iValue < (NSInteger)[[entity escortGroup] count] - 1)
    1884             :                                 {
    1885             :                                         OOJSReportError(context, @"ship.%@ must be >= current escort numbers.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties));
    1886             :                                         return NO;
    1887             :                                 }
    1888             :                                 if (iValue > MAX_ESCORTS)
    1889             :                                 {
    1890             :                                         OOJSReportError(context, @"ship.%@ must be <= %d.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties),MAX_ESCORTS);
    1891             :                                         return NO;
    1892             :                                 }
    1893             :                                 [entity setMaxEscortCount:iValue];
    1894             :                                 return YES;
    1895             :                         }
    1896             : 
    1897             :                         break;
    1898             : 
    1899             :                         
    1900             :                 default:
    1901             :                         OOJSReportBadPropertySelector(context, this, propID, sShipProperties);
    1902             :                         return NO;
    1903             :         }
    1904             :         
    1905             :         OOJSReportBadPropertyValue(context, this, propID, sShipProperties, *value);
    1906             :         return NO;
    1907             :         
    1908             : playerReadOnly:
    1909             :         OOJSReportError(context, @"player.ship.%@ is read-only.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties));
    1910             :         return NO;
    1911             : 
    1912             : // Not used (yet)
    1913             : /*
    1914             : npcReadOnly:
    1915             :         OOJSReportError(context, @"npc.ship.%@ is read-only.", OOStringFromJSPropertyIDAndSpec(context, propID, sShipProperties));
    1916             :         return NO;
    1917             : */
    1918             : 
    1919             :         OOJS_NATIVE_EXIT
    1920             : }
    1921             : 
    1922             : 
    1923             : // *** Methods ***
    1924             : 
    1925           0 : #define GET_THIS_SHIP(THISENT) do { \
    1926             :         if (EXPECT_NOT(!JSShipGetShipEntity(context, OOJS_THIS, &THISENT)))  return NO; /* Exception */ \
    1927             :         if (OOIsStaleEntity(THISENT))  OOJS_RETURN_VOID; \
    1928             : } while (0)
    1929             : 
    1930             : 
    1931             : // setScript(scriptName : String)
    1932           0 : static JSBool ShipSetScript(JSContext *context, uintN argc, jsval *vp)
    1933             : {
    1934             :         OOJS_NATIVE_ENTER(context)
    1935             :         
    1936             :         ShipEntity                              *thisEnt = nil;
    1937             :         NSString                                *name = nil;
    1938             :         
    1939             :         GET_THIS_SHIP(thisEnt);
    1940             :         if (argc > 0)  name = OOStringFromJSValue(context, OOJS_ARGV[0]);
    1941             :         if (EXPECT_NOT(name == nil))
    1942             :         {
    1943             :                 OOJSReportBadArguments(context, @"Ship", @"setScript", MIN(argc, 1U), OOJS_ARGV, nil, @"string (script name)");
    1944             :                 return NO;
    1945             :         }
    1946             :         if (EXPECT_NOT([thisEnt isPlayer]))
    1947             :         {
    1948             :                 OOJSReportErrorForCaller(context, @"Ship", @"setScript", @"Not valid for player ship.");
    1949             :                 return NO;
    1950             :         }
    1951             :         
    1952             :         [thisEnt setShipScript:name];
    1953             :         OOJS_RETURN_VOID;
    1954             :         
    1955             :         OOJS_NATIVE_EXIT
    1956             : }
    1957             : 
    1958             : 
    1959             : // setAI(aiName : String)
    1960           0 : static JSBool ShipSetAI(JSContext *context, uintN argc, jsval *vp)
    1961             : {
    1962             :         OOJS_NATIVE_ENTER(context)
    1963             :         
    1964             :         ShipEntity                              *thisEnt = nil;
    1965             :         NSString                                *name = nil;
    1966             :         
    1967             :         GET_THIS_SHIP(thisEnt);
    1968             :         if (argc > 0)  name = OOStringFromJSValue(context, OOJS_ARGV[0]);
    1969             :         if (EXPECT_NOT(name == nil))
    1970             :         {
    1971             :                 OOJSReportBadArguments(context, @"Ship", @"setAI", MIN(argc, 1U), OOJS_ARGV, nil, @"string (AI name)");
    1972             :                 return NO;
    1973             :         }
    1974             :         if (EXPECT_NOT([thisEnt isPlayer]))
    1975             :         {
    1976             :                 OOJSReportErrorForCaller(context, @"Ship", @"setAI", @"Not valid for player ship.");
    1977             :                 return NO;
    1978             :         }
    1979             :         
    1980             :         [thisEnt setAITo:name];
    1981             :         OOJS_RETURN_VOID;
    1982             :         
    1983             :         OOJS_NATIVE_EXIT
    1984             : }
    1985             : 
    1986             : 
    1987             : // switchAI(aiName : String)
    1988           0 : static JSBool ShipSwitchAI(JSContext *context, uintN argc, jsval *vp)
    1989             : {
    1990             :         OOJS_NATIVE_ENTER(context)
    1991             :         
    1992             :         ShipEntity                              *thisEnt = nil;
    1993             :         NSString                                *name = nil;
    1994             :         
    1995             :         GET_THIS_SHIP(thisEnt);
    1996             :         if (argc > 0)  name = OOStringFromJSValue(context, OOJS_ARGV[0]);
    1997             :         if (EXPECT_NOT(name == nil))
    1998             :         {
    1999             :                 OOJSReportBadArguments(context, @"Ship", @"switchAI", MIN(argc, 1U), OOJS_ARGV, nil, @"string (AI name)");
    2000             :                 return NO;
    2001             :         }
    2002             :         if (EXPECT_NOT([thisEnt isPlayer]))
    2003             :         {
    2004             :                 OOJSReportErrorForCaller(context, @"Ship", @"switchAI", @"Not valid for player ship.");
    2005             :                 return NO;
    2006             :         }
    2007             :         
    2008             :         [thisEnt switchAITo:name];
    2009             :         OOJS_RETURN_VOID;
    2010             :         
    2011             :         OOJS_NATIVE_EXIT
    2012             : }
    2013             : 
    2014             : 
    2015             : // exitAI()
    2016           0 : static JSBool ShipExitAI(JSContext *context, uintN argc, jsval *vp)
    2017             : {
    2018             :         OOJS_NATIVE_ENTER(context)
    2019             :         
    2020             :         ShipEntity                              *thisEnt = nil;
    2021             :         AI                                              *thisAI = nil;
    2022             :         NSString                                *message = nil;
    2023             :         
    2024             :         GET_THIS_SHIP(thisEnt);
    2025             :         if (EXPECT_NOT([thisEnt isPlayer]))
    2026             :         {
    2027             :                 OOJSReportErrorForCaller(context, @"Ship", @"exitAI", @"Not valid for player ship.");
    2028             :                 return NO;
    2029             :         }
    2030             :         thisAI = [thisEnt getAI];
    2031             :         
    2032             :         if ([thisAI hasSuspendedStateMachines])
    2033             :         {
    2034             :                 if (argc > 0)
    2035             :                 {
    2036             :                         message = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2037             :                 }
    2038             :                 // Else AI will default to RESTARTED.
    2039             :                 
    2040             :                 [thisAI exitStateMachineWithMessage:message];
    2041             :         }
    2042             :         else
    2043             :         {
    2044             :                 OOJSReportWarningForCaller(context, @"Ship", @"exitAI()", @"Cannot exit current AI state machine because there are no suspended state machines.");
    2045             :         }
    2046             :         OOJS_RETURN_VOID;
    2047             :         
    2048             :         OOJS_NATIVE_EXIT
    2049             : }
    2050             : 
    2051             : 
    2052             : // reactToAIMessage(message : String)
    2053           0 : static JSBool ShipReactToAIMessage(JSContext *context, uintN argc, jsval *vp)
    2054             : {
    2055             :         OOJS_NATIVE_ENTER(context)
    2056             :         
    2057             :         ShipEntity                              *thisEnt = nil;
    2058             :         NSString                                *message = nil;
    2059             :         
    2060             :         GET_THIS_SHIP(thisEnt);
    2061             :         if (argc > 0)  message = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2062             :         if (EXPECT_NOT(message == nil))
    2063             :         {
    2064             :                 OOJSReportBadArguments(context, @"Ship", @"reactToAIMessage", MIN(argc, 1U), OOJS_ARGV, nil, @"string");
    2065             :                 return NO;
    2066             :         }
    2067             :         if (EXPECT_NOT([thisEnt isPlayer]))
    2068             :         {
    2069             :                 OOJSReportErrorForCaller(context, @"Ship", @"reactToAIMessage", @"Not valid for player ship.");
    2070             :                 return NO;
    2071             :         }
    2072             :         
    2073             :         [thisEnt reactToAIMessage:message context:@"JavaScript reactToAIMessage()"];
    2074             :         OOJS_RETURN_VOID;
    2075             :         
    2076             :         OOJS_NATIVE_EXIT
    2077             : }
    2078             : 
    2079             : 
    2080             : // sendAIMessage(message : String)
    2081           0 : static JSBool ShipSendAIMessage(JSContext *context, uintN argc, jsval *vp)
    2082             : {
    2083             :         OOJS_NATIVE_ENTER(context)
    2084             :         
    2085             :         ShipEntity                              *thisEnt = nil;
    2086             :         NSString                                *message = nil;
    2087             :         
    2088             :         GET_THIS_SHIP(thisEnt);
    2089             :         if (argc > 0)  message = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2090             :         if (EXPECT_NOT(message == nil))
    2091             :         {
    2092             :                 OOJSReportBadArguments(context, @"Ship", @"sendAIMessage", MIN(argc, 1U), OOJS_ARGV, nil, @"string");
    2093             :                 return NO;
    2094             :         }
    2095             :         if (EXPECT_NOT([thisEnt isPlayer]))
    2096             :         {
    2097             :                 OOJSReportErrorForCaller(context, @"Ship", @"sendAIMessage", @"Not valid for player ship.");
    2098             :                 return NO;
    2099             :         }
    2100             :         
    2101             :         [thisEnt sendAIMessage:message];
    2102             :         OOJS_RETURN_VOID;
    2103             :         
    2104             :         OOJS_NATIVE_EXIT
    2105             : }
    2106             : 
    2107             : 
    2108             : // deployEscorts()
    2109           0 : static JSBool ShipDeployEscorts(JSContext *context, uintN argc, jsval *vp)
    2110             : {
    2111             :         OOJS_NATIVE_ENTER(context)
    2112             :         
    2113             :         ShipEntity                              *thisEnt = nil;
    2114             :         
    2115             :         GET_THIS_SHIP(thisEnt);
    2116             :         
    2117             :         [thisEnt deployEscorts];
    2118             :         OOJS_RETURN_VOID;
    2119             :         
    2120             :         OOJS_NATIVE_EXIT
    2121             : }
    2122             : 
    2123             : 
    2124             : // dockEscorts()
    2125           0 : static JSBool ShipDockEscorts(JSContext *context, uintN argc, jsval *vp)
    2126             : {
    2127             :         OOJS_NATIVE_ENTER(context)
    2128             :         
    2129             :         ShipEntity                              *thisEnt = nil;
    2130             :         
    2131             :         GET_THIS_SHIP(thisEnt);
    2132             :         
    2133             :         [thisEnt dockEscorts];
    2134             :         OOJS_RETURN_VOID;
    2135             :         
    2136             :         OOJS_NATIVE_EXIT
    2137             : }
    2138             : 
    2139             : 
    2140             : // hasEquipmentProviding(equipment : String) : Boolean
    2141           0 : static JSBool ShipHasEquipmentProviding(JSContext *context, uintN argc, jsval *vp)
    2142             : {
    2143             :         OOJS_NATIVE_ENTER(context)
    2144             :         
    2145             :         ShipEntity                              *thisEnt = nil;
    2146             :         NSString                                *equipment = nil;
    2147             :         
    2148             :         GET_THIS_SHIP(thisEnt);
    2149             :         
    2150             :         if (argc > 0)  equipment = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2151             :         if (EXPECT_NOT(equipment == nil))
    2152             :         {
    2153             :                 OOJSReportBadArguments(context, @"Ship", @"hasEquipmentProviding", MIN(argc, 1U), OOJS_ARGV, nil, @"string (equipment)");
    2154             :                 return NO;
    2155             :         }
    2156             :         
    2157             :         OOJS_RETURN_BOOL([thisEnt hasEquipmentItemProviding:equipment]);
    2158             :         
    2159             :         OOJS_NATIVE_EXIT
    2160             : }
    2161             : 
    2162             : 
    2163             : // hasRole(role : String) : Boolean
    2164           0 : static JSBool ShipHasRole(JSContext *context, uintN argc, jsval *vp)
    2165             : {
    2166             :         OOJS_NATIVE_ENTER(context)
    2167             :         
    2168             :         ShipEntity                              *thisEnt = nil;
    2169             :         NSString                                *role = nil;
    2170             :         
    2171             :         GET_THIS_SHIP(thisEnt);
    2172             :         
    2173             :         if (argc > 0)  role = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2174             :         if (EXPECT_NOT(role == nil))
    2175             :         {
    2176             :                 OOJSReportBadArguments(context, @"Ship", @"hasRole", MIN(argc, 1U), OOJS_ARGV, nil, @"string (role)");
    2177             :                 return NO;
    2178             :         }
    2179             :         
    2180             :         OOJS_RETURN_BOOL([thisEnt hasRole:role]);
    2181             :         
    2182             :         OOJS_NATIVE_EXIT
    2183             : }
    2184             : 
    2185             : 
    2186             : // ejectItem(role : String) : Ship
    2187           0 : static JSBool ShipEjectItem(JSContext *context, uintN argc, jsval *vp)
    2188             : {
    2189             :         OOJS_NATIVE_ENTER(context)
    2190             :         
    2191             :         ShipEntity                              *thisEnt = nil;
    2192             :         NSString                                *role = nil;
    2193             :         
    2194             :         GET_THIS_SHIP(thisEnt);
    2195             :         
    2196             :         if (argc > 0)  role = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2197             :         if (EXPECT_NOT(role == nil))
    2198             :         {
    2199             :                 OOJSReportBadArguments(context, @"Ship", @"ejectItem", MIN(argc, 1U), OOJS_ARGV, nil, @"string (role)");
    2200             :                 return NO;
    2201             :         }
    2202             :         
    2203             :         OOJS_RETURN_OBJECT([thisEnt ejectShipOfRole:role]);
    2204             :         
    2205             :         OOJS_NATIVE_EXIT
    2206             : }
    2207             : 
    2208             : 
    2209             : // addCargoEntity: ship
    2210           0 : static JSBool ShipAddCargoEntity(JSContext *context, uintN argc, jsval *vp)
    2211             : {
    2212             :         OOJS_NATIVE_ENTER(context)
    2213             :         ShipEntity                              *thisEnt = nil;
    2214             :         ShipEntity              *target = nil;
    2215             :         JSBool                                  procEvents = NO;
    2216             :         JSBool                                  procMessages = NO;
    2217             : 
    2218             :         GET_THIS_SHIP(thisEnt);
    2219             : 
    2220             :         if (EXPECT_NOT([thisEnt isPlayer] && [(PlayerEntity *)thisEnt isDocked]))
    2221             :         {
    2222             :                 OOJSReportWarningForCaller(context, @"PlayerShip", @"addCargoEntity", @"Can't add cargo entity while docked, ignoring.");
    2223             :                 return NO;
    2224             :         }
    2225             :         if (EXPECT_NOT(argc == 0 ||
    2226             :                                    JSVAL_IS_NULL(OOJS_ARGV[0]) ||
    2227             :                                    !JSVAL_IS_OBJECT(OOJS_ARGV[0]) ||
    2228             :                                    !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target)))
    2229             :         {
    2230             :                 OOJSReportBadArguments(context, @"PlayerShip", @"addCargoEntity", MIN(argc, 1U), OOJS_ARGV, nil, @"scoopable entity.");
    2231             :                 return NO;
    2232             :         }
    2233             :         if ([target scanClass] != CLASS_CARGO) 
    2234             :         {
    2235             :                 OOJSReportWarningForCaller(context, @"PlayerShip", @"addCargoEntity", @"Scoopable entity not cargo.");
    2236             :                 return NO;
    2237             :         }
    2238             :         if ([target status] != STATUS_IN_FLIGHT) 
    2239             :         {
    2240             :                 OOJSReportWarningForCaller(context, @"PlayerShip", @"addCargoEntity", @"Scoopable entity not in flight.");
    2241             :                 return NO;
    2242             :         }
    2243             :         if (argc >= 2 && EXPECT_NOT(!JS_ValueToBoolean(context, OOJS_ARGV[1], &procEvents)))
    2244             :         {
    2245             :                 OOJSReportBadArguments(context, @"PlayerShip", @"addCargoEntity", MIN(argc, 2U), OOJS_ARGV, nil, @"boolean");
    2246             :                 return NO;
    2247             :         }
    2248             :         if (argc == 3 && EXPECT_NOT(!JS_ValueToBoolean(context, OOJS_ARGV[2], &procMessages)))
    2249             :         {
    2250             :                 OOJSReportBadArguments(context, @"PlayerShip", @"addCargoEntity", MIN(argc, 3U), OOJS_ARGV, nil, @"boolean");
    2251             :                 return NO;
    2252             :         }
    2253             :         
    2254             :         // scoop the object, but don't process any events/messages unless requested
    2255             :         OOJS_BEGIN_FULL_NATIVE(context)
    2256             :         [thisEnt scoopUpProcess:target processEvents:procEvents processMessages:procMessages];
    2257             :         OOJS_END_FULL_NATIVE
    2258             : 
    2259             :         OOJS_RETURN_BOOL([target status] == STATUS_IN_HOLD);
    2260             : 
    2261             :         OOJS_NATIVE_EXIT
    2262             : }
    2263             : 
    2264             : 
    2265             : // ejectSpecificItem(itemKey : String) : Ship
    2266           0 : static JSBool ShipEjectSpecificItem(JSContext *context, uintN argc, jsval *vp)
    2267             : {
    2268             :         OOJS_NATIVE_ENTER(context)
    2269             :         
    2270             :         ShipEntity                              *thisEnt = nil;
    2271             :         NSString                                *itemKey = nil;
    2272             :         
    2273             :         GET_THIS_SHIP(thisEnt);
    2274             :         
    2275             :         if (argc > 0)  itemKey = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2276             :         if (EXPECT_NOT(itemKey == nil))
    2277             :         {
    2278             :                 OOJSReportBadArguments(context, @"Ship", @"ejectSpecificItem", MIN(argc, 1U), OOJS_ARGV, nil, @"string (ship key)");
    2279             :                 return NO;
    2280             :         }
    2281             :         
    2282             :         OOJS_RETURN_OBJECT([thisEnt ejectShipOfType:itemKey]);
    2283             :         
    2284             :         OOJS_NATIVE_EXIT
    2285             : }
    2286             : 
    2287             : 
    2288             : // dumpCargo() : Ship
    2289           0 : static JSBool ShipDumpCargo(JSContext *context, uintN argc, jsval *vp)
    2290             : {
    2291             :         OOJS_NATIVE_ENTER(context)
    2292             :         
    2293             :         ShipEntity                              *thisEnt = nil;
    2294             :         OOCommodityType                 pref = nil;
    2295             : 
    2296             :         GET_THIS_SHIP(thisEnt);
    2297             :         
    2298             :         if (EXPECT_NOT([thisEnt isPlayer] && [(PlayerEntity *)thisEnt isDocked]))
    2299             :         {
    2300             :                 OOJSReportWarningForCaller(context, @"PlayerShip", @"dumpCargo", @"Can't dump cargo while docked, ignoring.");
    2301             :                 OOJS_RETURN_NULL;
    2302             :         }
    2303             :         
    2304             :         if (argc > 1)
    2305             :         {
    2306             :                 pref = OOStringFromJSValue(context, OOJS_ARGV[1]);
    2307             :         }
    2308             : 
    2309             :         // NPCs can queue multiple items to dump
    2310             :         if (!EXPECT_NOT([thisEnt isPlayer]))
    2311             :         {
    2312             :                 int32                                   i, count = 1;
    2313             :                 BOOL                                    gotCount = YES;
    2314             :                 if (argc > 0)  gotCount = JS_ValueToInt32(context, OOJS_ARGV[0], &count);
    2315             :                 if (EXPECT_NOT(!gotCount || count < 1 || count > 64))
    2316             :                 {
    2317             :                         OOJSReportBadArguments(context, @"Ship", @"dumpCargo", MIN(argc, 1U), OOJS_ARGV, nil, @"optional quantity (1 to 64), optional preferred commodity");
    2318             :                         return NO;
    2319             :                 }
    2320             : 
    2321             :                 for (i = 1; i < count; i++)
    2322             :                 {
    2323             :                         [thisEnt performSelector:@selector(dumpCargo) withObject:nil afterDelay:0.75 * i];      // drop 3 canisters per 2 seconds
    2324             :                 }
    2325             :         }
    2326             : 
    2327             :         OOJS_RETURN_OBJECT([thisEnt dumpCargoItem:pref]);
    2328             :         
    2329             :         OOJS_NATIVE_EXIT
    2330             : }
    2331             : 
    2332             : 
    2333             : // spawn(role : String [, number : count]) : Array
    2334           0 : static JSBool ShipSpawn(JSContext *context, uintN argc, jsval *vp)
    2335             : {
    2336             :         OOJS_NATIVE_ENTER(context)
    2337             :         
    2338             :         ShipEntity                              *thisEnt = nil;
    2339             :         NSString                                *role = nil;
    2340             :         int32                                   count = 1;
    2341             :         BOOL                                    gotCount = YES;
    2342             :         NSArray                                 *result = nil;
    2343             :         
    2344             :         GET_THIS_SHIP(thisEnt);
    2345             :         
    2346             :         if (argc > 0)  role = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2347             :         if (argc > 1)  gotCount = JS_ValueToInt32(context, OOJS_ARGV[1], &count);
    2348             :         if (EXPECT_NOT(role == nil || !gotCount || count < 1 || count > 64))
    2349             :         {
    2350             :                 OOJSReportBadArguments(context, @"Ship", @"spawn", MIN(argc, 1U), OOJS_ARGV, nil, @"role and optional quantity (1 to 64)");
    2351             :                 return NO;
    2352             :         }
    2353             :         
    2354             :         OOJS_BEGIN_FULL_NATIVE(context)
    2355             :         result = [thisEnt spawnShipsWithRole:role count:count];
    2356             :         OOJS_END_FULL_NATIVE
    2357             : 
    2358             :         OOJS_RETURN_OBJECT(result);
    2359             :         
    2360             :         OOJS_NATIVE_EXIT
    2361             : }
    2362             : 
    2363             : 
    2364             : // dealEnergyDamage(). Replaces AI's dealEnergyDamageWithinDesiredRange
    2365           0 : static JSBool ShipDealEnergyDamage(JSContext *context, uintN argc, jsval *vp)
    2366             : {
    2367             :         OOJS_NATIVE_ENTER(context)
    2368             :         
    2369             :         ShipEntity                              *thisEnt = nil;
    2370             :         jsdouble baseDamage;
    2371             :         jsdouble range;
    2372             :         jsdouble velocityBias = 0.0;
    2373             :         BOOL gotDamage;
    2374             :         BOOL gotRange;
    2375             :         BOOL gotVBias;
    2376             :         GET_THIS_SHIP(thisEnt);
    2377             : 
    2378             :         if (argc < 2)
    2379             :         {
    2380             :                 OOJSReportBadArguments(context, @"Ship", @"dealEnergyDamage", argc, OOJS_ARGV, nil, @"damage and range needed");
    2381             :                 return NO;
    2382             :         }
    2383             :         
    2384             :         gotDamage = JS_ValueToNumber(context, OOJS_ARGV[0], &baseDamage);
    2385             :         if (EXPECT_NOT(!gotDamage || baseDamage < 0))
    2386             :         {
    2387             :                 OOJSReportBadArguments(context, @"Ship", @"dealEnergyDamage", argc, OOJS_ARGV, nil, @"damage must be positive");
    2388             :                 return NO;
    2389             :         }
    2390             :         gotRange = JS_ValueToNumber(context, OOJS_ARGV[1], &range);
    2391             :         if (EXPECT_NOT(!gotRange || range < 0))
    2392             :         {
    2393             :                 OOJSReportBadArguments(context, @"Ship", @"dealEnergyDamage", argc, OOJS_ARGV, nil, @"range must be positive");
    2394             :                 return NO;
    2395             :         }
    2396             :         if (argc >= 3) 
    2397             :         {
    2398             :                 gotVBias = JS_ValueToNumber(context, OOJS_ARGV[2], &velocityBias);
    2399             :                 if (!gotVBias)
    2400             :                 {
    2401             :                         OOJSReportBadArguments(context, @"Ship", @"dealEnergyDamage", argc, OOJS_ARGV, nil, @"velocity bias must be a number");
    2402             :                         return NO;
    2403             :                 }
    2404             :         }
    2405             : 
    2406             :         [thisEnt dealEnergyDamage:(GLfloat)baseDamage atRange:(GLfloat)range withBias:(GLfloat)velocityBias];
    2407             : 
    2408             :         OOJS_RETURN_VOID;
    2409             : 
    2410             :         OOJS_NATIVE_EXIT
    2411             : }
    2412             : 
    2413             : 
    2414             : // explode()
    2415           0 : static JSBool ShipExplode(JSContext *context, uintN argc, jsval *vp)
    2416             : {
    2417             :         OOJS_NATIVE_ENTER(context)
    2418             :         
    2419             :         return RemoveOrExplodeShip(context, argc, vp, YES);
    2420             :         
    2421             :         OOJS_NATIVE_EXIT
    2422             : }
    2423             : 
    2424             : 
    2425             : // remove([suppressDeathEvent : Boolean = false])
    2426           0 : static JSBool ShipRemove(JSContext *context, uintN argc, jsval *vp)
    2427             : {
    2428             :         OOJS_NATIVE_ENTER(context)
    2429             :         
    2430             :         ShipEntity                              *thisEnt = nil;
    2431             :         JSBool                                  suppressDeathEvent = NO;
    2432             :         
    2433             :         GET_THIS_SHIP(thisEnt);
    2434             :         
    2435             :         if ([thisEnt isPlayer])
    2436             :         {
    2437             :                 OOJSReportError(context, @"Cannot remove() player's ship.");
    2438             :                 return NO;
    2439             :         }
    2440             :         
    2441             :         if (argc > 0 && EXPECT_NOT(!JS_ValueToBoolean(context, OOJS_ARGV[0], &suppressDeathEvent)))
    2442             :         {
    2443             :                 OOJSReportBadArguments(context, @"Ship", @"remove", argc, OOJS_ARGV, nil, @"boolean");
    2444             :                 return NO;
    2445             :         }
    2446             : 
    2447             :         [thisEnt doScriptEvent:OOJSID("shipRemoved") withArgument:[NSNumber numberWithBool:suppressDeathEvent]];
    2448             : 
    2449             :         if (suppressDeathEvent)
    2450             :         {
    2451             :                 [thisEnt removeScript];
    2452             :         }
    2453             :         return RemoveOrExplodeShip(context, argc, vp, NO);
    2454             :         
    2455             :         OOJS_NATIVE_EXIT
    2456             : }
    2457             : 
    2458             : 
    2459             : // __runLegacyScriptActions(target : Ship, actions : Array)
    2460           0 : static JSBool ShipRunLegacyScriptActions(JSContext *context, uintN argc, jsval *vp)
    2461             : {
    2462             :         OOJS_NATIVE_ENTER(context)
    2463             :         
    2464             :         ShipEntity                              *thisEnt = nil;
    2465             :         PlayerEntity                    *player = nil;
    2466             :         ShipEntity                              *target = nil;
    2467             :         NSArray                                 *actions = nil;
    2468             :         
    2469             :         player = OOPlayerForScripting();
    2470             :         GET_THIS_SHIP(thisEnt);
    2471             :         
    2472             :         if (argc > 1)  actions = OOJSNativeObjectFromJSValue(context, OOJS_ARGV[1]);
    2473             :         if (EXPECT_NOT(argc != 2 ||
    2474             :                                    !JSVAL_IS_OBJECT(OOJS_ARGV[0]) ||
    2475             :                                    !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target) ||
    2476             :                                    ![actions isKindOfClass:[NSArray class]]))
    2477             :         {
    2478             :                 OOJSReportBadArguments(context, @"Ship", @"__runLegacyScriptActions", argc, OOJS_ARGV, nil, @"target and array of actions");
    2479             :                 return NO;
    2480             :         }
    2481             :         
    2482             :         if (target != nil)      // Not stale reference
    2483             :         {
    2484             :                 [player setScriptTarget:thisEnt];
    2485             :                 [player runUnsanitizedScriptActions:actions
    2486             :                                                   allowingAIMethods:YES
    2487             :                                                         withContextName:[NSString stringWithFormat:@"<ship \"%@\" legacy actions>", [thisEnt name]]
    2488             :                                                                   forTarget:target];
    2489             :         }
    2490             :         
    2491             :         OOJS_RETURN_VOID;
    2492             :         
    2493             :         OOJS_NATIVE_EXIT
    2494             : }
    2495             : 
    2496             : 
    2497             : // commsMessage(message : String[,target : Ship])
    2498           0 : static JSBool ShipCommsMessage(JSContext *context, uintN argc, jsval *vp)
    2499             : {
    2500             :         OOJS_NATIVE_ENTER(context)
    2501             :         
    2502             :         ShipEntity                              *thisEnt = nil;
    2503             :         NSString                                *message = nil;
    2504             :         ShipEntity                              *target = nil;
    2505             :         
    2506             :         GET_THIS_SHIP(thisEnt);
    2507             :         
    2508             :         if (argc > 0)  message = OOStringFromJSValue(context, OOJS_ARGV[0]);
    2509             :         if (EXPECT_NOT(message == nil || (argc > 1 && (JSVAL_IS_NULL(OOJS_ARGV[1]) || !JSVAL_IS_OBJECT(OOJS_ARGV[1]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[1]), &target)))))
    2510             :         {
    2511             :                 OOJSReportBadArguments(context, @"Ship", @"commsMessage", MIN(argc, 1U), OOJS_ARGV, nil, @"message and optional target");
    2512             :                 return NO;
    2513             :         }
    2514             :         
    2515             :         if (argc < 2)
    2516             :         {
    2517             :                 [thisEnt commsMessage:message withUnpilotedOverride:YES];       // generic broadcast
    2518             :         }
    2519             :         else if (target != nil)  // Not stale reference
    2520             :         {
    2521             :                 [thisEnt sendMessage:message toShip:target withUnpilotedOverride:YES];  // ship-to-ship narrowcast
    2522             :         }
    2523             :         OOJS_RETURN_VOID;
    2524             :         
    2525             :         OOJS_NATIVE_EXIT
    2526             : }
    2527             : 
    2528             : 
    2529             : // fireECM()
    2530           0 : static JSBool ShipFireECM(JSContext *context, uintN argc, jsval *vp)
    2531             : {
    2532             :         OOJS_NATIVE_ENTER(context)
    2533             :         
    2534             :         ShipEntity                              *thisEnt = nil;
    2535             :         BOOL                                    OK;
    2536             :         
    2537             :         GET_THIS_SHIP(thisEnt);
    2538             :         
    2539             :         OK = [thisEnt fireECM];
    2540             :         if (!OK)
    2541             :         {
    2542             :                 OOJSReportWarning(context, @"Ship %@ was requested to fire ECM burst but does not carry ECM equipment.", [thisEnt oo_jsDescription]);
    2543             :         }
    2544             :         
    2545             :         OOJS_RETURN_BOOL(OK);
    2546             :         
    2547             :         OOJS_NATIVE_EXIT
    2548             : }
    2549             : 
    2550             : 
    2551             : // abandonShip()
    2552           0 : static JSBool ShipAbandonShip(JSContext *context, uintN argc, jsval *vp)
    2553             : {
    2554             :         OOJS_NATIVE_ENTER(context)
    2555             :         
    2556             :         ShipEntity                              *thisEnt = nil;
    2557             :         
    2558             :         GET_THIS_SHIP(thisEnt);
    2559             :         OOJS_RETURN_BOOL([thisEnt hasEscapePod] && [thisEnt abandonShip]);
    2560             :         
    2561             :         OOJS_NATIVE_EXIT
    2562             : }
    2563             : 
    2564             : 
    2565             : // canAwardEquipment(type : equipmentInfoExpression)
    2566           0 : static JSBool ShipCanAwardEquipment(JSContext *context, uintN argc, jsval *vp)
    2567             : {
    2568             :         OOJS_NATIVE_ENTER(context)
    2569             :         
    2570             :         ShipEntity                                      *thisEnt = nil;
    2571             :         NSString                                        *key = nil;
    2572             :         NSString                                        *ctx = @"scripted";
    2573             :         BOOL                                            result;
    2574             :         BOOL                                            exists;
    2575             :         
    2576             :         GET_THIS_SHIP(thisEnt);
    2577             :         
    2578             :         if (argc > 0)  key = JSValueToEquipmentKeyRelaxed(context, OOJS_ARGV[0], &exists);
    2579             :         if (EXPECT_NOT(key == nil))
    2580             :         {
    2581             :                 OOJSReportBadArguments(context, @"Ship", @"canAwardEquipment", MIN(argc, 1U), OOJS_ARGV, nil, @"equipment type");
    2582             :                 return NO;
    2583             :         }
    2584             :         
    2585             :         if (exists)
    2586             :         {
    2587             :                 if (argc > 1)
    2588             :                 {
    2589             :                         ctx = OOStringFromJSValue(context, OOJS_ARGV[1]);
    2590             :                         if (![ctx isEqualToString:@"scripted"] && ![ctx isEqualToString:@"purchase"] && ![ctx isEqualToString:@"newShip"] && ![ctx isEqualToString:@"npc"])
    2591             :                         {
    2592             :                                 OOJSReportBadArguments(context, @"Ship", @"canAwardEquipment", MIN(argc, 2U), OOJS_ARGV, nil, @"context");
    2593             :                                 return NO;
    2594             :                         }
    2595             :                 }
    2596             :                 result = YES;
    2597             :                 
    2598             :                 // can't add fuel as equipment.
    2599             :                 if ([key isEqualToString:@"EQ_FUEL"])  result = NO;
    2600             :                 
    2601             :                 if (result)  result = [thisEnt canAddEquipment:key inContext:ctx];
    2602             :         }
    2603             :         else
    2604             :         {
    2605             :                 // Unknown type.
    2606             :                 result = NO;
    2607             :         }
    2608             :         
    2609             :         OOJS_RETURN_BOOL(result);
    2610             :         
    2611             :         OOJS_NATIVE_EXIT
    2612             : }
    2613             : 
    2614             : 
    2615             : // awardEquipment(type : equipmentInfoExpression) : Boolean
    2616           0 : static JSBool ShipAwardEquipment(JSContext *context, uintN argc, jsval *vp)
    2617             : {
    2618             :         OOJS_NATIVE_ENTER(context)
    2619             :         
    2620             :         ShipEntity                                      *thisEnt = nil;
    2621             :         OOEquipmentType                         *eqType = nil;
    2622             :         NSString                                        *identifier = nil;
    2623             :         BOOL                                            OK = YES;
    2624             :         BOOL                                            berth;
    2625             :         BOOL            isRepair = NO;
    2626             :         
    2627             :         GET_THIS_SHIP(thisEnt);
    2628             :         
    2629             :         if (argc > 0)  eqType = JSValueToEquipmentType(context, OOJS_ARGV[0]);
    2630             :         if (EXPECT_NOT(eqType == nil))
    2631             :         {
    2632             :                 OOJSReportBadArguments(context, @"Ship", @"awardEquipment", MIN(argc, 1U), OOJS_ARGV, nil, @"equipment type");
    2633             :                 return NO;
    2634             :         }
    2635             :         
    2636             :         // Check that equipment is permitted.
    2637             :         identifier = [eqType identifier];
    2638             :         berth = [identifier isEqualToString:@"EQ_PASSENGER_BERTH"];
    2639             :         if (berth)
    2640             :         {
    2641             :                 OK = [thisEnt availableCargoSpace] >= [eqType requiredCargoSpace];
    2642             :         }
    2643             :         else if ([identifier isEqualToString:@"EQ_FUEL"])
    2644             :         {
    2645             :                 OK = NO;
    2646             :         }
    2647             :         else
    2648             :         {
    2649             :                 OK = [eqType canCarryMultiple] || ![thisEnt hasEquipmentItem:identifier];
    2650             :         }
    2651             :         
    2652             :         if (OK)
    2653             :         {
    2654             :                 if ([thisEnt isPlayer])
    2655             :                 {
    2656             :                         PlayerEntity *player = (PlayerEntity *)thisEnt;
    2657             :                         
    2658             :                         if ([identifier isEqualToString:@"EQ_MISSILE_REMOVAL"])
    2659             :                         {
    2660             :                                 [player removeMissiles];
    2661             :                         }
    2662             :                         else if ([eqType isMissileOrMine])
    2663             :                         {
    2664             :                                 OK = [player mountMissileWithRole:identifier];
    2665             :                         }
    2666             :                         else if (berth)
    2667             :                         {
    2668             :                                 OK = [player changePassengerBerths: +1];
    2669             :                         }
    2670             :                         else if ([identifier isEqualToString:@"EQ_PASSENGER_BERTH_REMOVAL"])
    2671             :                         {
    2672             :                                 OK = [player changePassengerBerths: -1];
    2673             :                         }
    2674             :                         else
    2675             :                         {
    2676             :                                 isRepair = [thisEnt hasEquipmentItem:[identifier stringByAppendingString:@"_DAMAGED"]];
    2677             :                                 OK = [player addEquipmentItem:identifier withValidation:YES inContext:@"scripted"];
    2678             :                                 if (OK && isRepair) 
    2679             :                                 {
    2680             :                                         [player doScriptEvent:OOJSID("equipmentRepaired") withArgument:identifier];
    2681             :                                 }
    2682             :                         }
    2683             :                 }
    2684             :                 else
    2685             :                 {
    2686             :                         if ([identifier isEqualToString:@"EQ_MISSILE_REMOVAL"])
    2687             :                         {
    2688             :                                 [thisEnt removeMissiles];
    2689             :                         }
    2690             :                         // no passenger handling for NPCs. EQ_CARGO_BAY is dealt with inside addEquipmentItem
    2691             :                         else if (!berth && ![identifier isEqualToString:@"EQ_PASSENGER_BERTH_REMOVAL"])
    2692             :                         {
    2693             :                                 OK = [thisEnt addEquipmentItem:identifier withValidation:YES inContext:@"scripted"];  
    2694             :                         }
    2695             :                         else
    2696             :                         {
    2697             :                                 OK = NO;
    2698             :                         }
    2699             :                 }
    2700             :         }
    2701             :         
    2702             :         OOJS_RETURN_BOOL(OK);
    2703             :         
    2704             :         OOJS_NATIVE_EXIT
    2705             : }
    2706             : 
    2707             : 
    2708             : // removeEquipment(type : equipmentInfoExpression)
    2709           0 : static JSBool ShipRemoveEquipment(JSContext *context, uintN argc, jsval *vp)
    2710             : {
    2711             :         OOJS_NATIVE_ENTER(context)
    2712             :         
    2713             :         ShipEntity                                      *thisEnt = nil;
    2714             :         NSString                                        *key = nil;
    2715             :         BOOL                                            OK = YES;
    2716             :         
    2717             :         GET_THIS_SHIP(thisEnt);
    2718             :         
    2719             :         if (argc > 0)  key = JSValueToEquipmentKey(context, OOJS_ARGV[0]);
    2720             :         if (EXPECT_NOT(key == nil))
    2721             :         {
    2722             :                 OOJSReportBadArguments(context, @"Ship", @"removeEquipment", MIN(argc, 1U), OOJS_ARGV, nil, @"equipment type");
    2723             :                 return NO;
    2724             :         }
    2725             :         // berths are not in hasOneEquipmentItem
    2726             :         OK = [thisEnt hasOneEquipmentItem:key includeMissiles:YES whileLoading:NO] || ([key isEqualToString:@"EQ_PASSENGER_BERTH"] && [thisEnt passengerCapacity] > 0);
    2727             :         if (!OK)
    2728             :         {
    2729             :                 // Allow removal of damaged equipment.
    2730             :                 key = [key stringByAppendingString:@"_DAMAGED"];
    2731             :                 OK = [thisEnt hasOneEquipmentItem:key includeMissiles:NO whileLoading:NO];
    2732             :         }
    2733             :         if (OK)
    2734             :         {
    2735             :                 //exceptions
    2736             :                 if ([key isEqualToString:@"EQ_PASSENGER_BERTH"] || [key isEqualToString:@"EQ_CARGO_BAY"])
    2737             :                 {
    2738             :                         if ([key isEqualToString:@"EQ_PASSENGER_BERTH"])
    2739             :                         {
    2740             :                                 if ([thisEnt passengerCapacity] > [thisEnt passengerCount])
    2741             :                                 {
    2742             :                                         // must be the player's ship!
    2743             :                                         if ([thisEnt isPlayer]) [(PlayerEntity*)thisEnt changePassengerBerths:-1];
    2744             :                                 }
    2745             :                                 else OK = NO;
    2746             :                         }
    2747             :                         else    // EQ_CARGO_BAY
    2748             :                         {
    2749             :                                 // player cargo bay removal handled in script
    2750             :                                 if ([thisEnt isPlayer] || [thisEnt extraCargo] <= [thisEnt availableCargoSpace])
    2751             :                                 {
    2752             :                                         [thisEnt removeEquipmentItem:key];
    2753             :                                 }
    2754             :                                 else OK = NO;
    2755             :                         }
    2756             :                 }
    2757             :                 else
    2758             :                         [thisEnt removeEquipmentItem:key];
    2759             :         }
    2760             :         
    2761             :         OOJS_RETURN_BOOL(OK);
    2762             :         
    2763             :         OOJS_NATIVE_EXIT
    2764             : }
    2765             : 
    2766             : 
    2767             : // restoreSubEntities(): boolean
    2768           0 : static JSBool ShipRestoreSubEntities(JSContext *context, uintN argc, jsval *vp)
    2769             : {
    2770             :         OOJS_NATIVE_ENTER(context)
    2771             :         
    2772             :         ShipEntity                              *thisEnt = nil;
    2773             :         NSUInteger                              numSubEntitiesRestored = 0U;
    2774             :         
    2775             :         GET_THIS_SHIP(thisEnt);
    2776             :         
    2777             :         NSUInteger subCount = [[thisEnt subEntitiesForScript] count];
    2778             :         
    2779             :         [thisEnt clearSubEntities];
    2780             :         [thisEnt setUpSubEntities];
    2781             :         
    2782             :         if ([[thisEnt subEntitiesForScript] count] - subCount > 0)  numSubEntitiesRestored = [[thisEnt subEntitiesForScript] count] - subCount;
    2783             :         
    2784             :         // for each subentity restored, slightly increase the trade-in factor
    2785             :         if ([thisEnt isPlayer])
    2786             :         {
    2787             :                 int tradeInFactorChange = (int)MAX(PLAYER_SHIP_SUBENTITY_TRADE_IN_VALUE * numSubEntitiesRestored, 25U);
    2788             :                 [(PlayerEntity *)thisEnt adjustTradeInFactorBy:tradeInFactorChange];
    2789             :         }
    2790             :         
    2791             :         OOJS_RETURN_BOOL(numSubEntitiesRestored > 0);
    2792             :         
    2793             :         OOJS_NATIVE_EXIT
    2794             : }
    2795             : 
    2796             : 
    2797             : 
    2798             : // setEquipmentStatus(type : equipmentInfoExpression, status : String): boolean
    2799           0 : static JSBool ShipSetEquipmentStatus(JSContext *context, uintN argc, jsval *vp)
    2800             : {
    2801             :         OOJS_NATIVE_ENTER(context)
    2802             :         
    2803             :         // equipment status accepted: @"EQUIPMENT_OK", @"EQUIPMENT_DAMAGED"
    2804             :         
    2805             :         ShipEntity                              *thisEnt = nil;
    2806             :         OOEquipmentType                 *eqType = nil;
    2807             :         NSString                                *key = nil;
    2808             :         NSString                                *damagedKey = nil;
    2809             :         NSString                                *status = nil;
    2810             :         BOOL                                    hasOK = NO, hasDamaged = NO;
    2811             :         
    2812             :         GET_THIS_SHIP(thisEnt);
    2813             :         
    2814             :         if (argc < 2)
    2815             :         {
    2816             :                 OOJSReportBadArguments(context, @"Ship", @"setEquipmentStatus", argc, OOJS_ARGV, nil, @"equipment type and status");
    2817             :                 return NO;
    2818             :         }
    2819             :         
    2820             :         eqType = JSValueToEquipmentType(context, OOJS_ARGV[0]);
    2821             :         if (EXPECT_NOT(eqType == nil))
    2822             :         {
    2823             :                 OOJSReportBadArguments(context, @"Ship", @"setEquipmentStatus", 1, &OOJS_ARGV[0], nil, @"equipment type");
    2824             :                 return NO;
    2825             :         }
    2826             :         
    2827             :         status = OOStringFromJSValue(context, OOJS_ARGV[1]);
    2828             :         if (EXPECT_NOT(status == nil))
    2829             :         {
    2830             :                 OOJSReportBadArguments(context, @"Ship", @"setEquipmentStatus", 1, &OOJS_ARGV[1], nil, @"equipment status");
    2831             :                 return NO;
    2832             :         }
    2833             :         
    2834             :         // EMMSTRAN: use interned strings.
    2835             :         if (![status isEqualToString:@"EQUIPMENT_OK"] && ![status isEqualToString:@"EQUIPMENT_DAMAGED"])
    2836             :         {
    2837             :                 OOJSReportErrorForCaller(context, @"Ship", @"setEquipmentStatus", @"Second parameter for setEquipmentStatus must be either \"EQUIPMENT_OK\" or \"EQUIPMENT_DAMAGED\".");
    2838             :                 return NO;
    2839             :         }
    2840             :         
    2841             :         key = [eqType identifier];
    2842             :         hasOK = [thisEnt hasEquipmentItem:key];
    2843             :         BOOL setOK = [status isEqualToString:@"EQUIPMENT_OK"];
    2844             :         BOOL setDamaged = [status isEqualToString:@"EQUIPMENT_DAMAGED"];
    2845             :         if ([eqType canBeDamaged])
    2846             :         {
    2847             :                 damagedKey = [key stringByAppendingString:@"_DAMAGED"];
    2848             :                 hasDamaged = [thisEnt hasEquipmentItem:damagedKey];
    2849             :                 
    2850             :                 if ((setOK && hasDamaged) || (setDamaged && hasOK))
    2851             :                 {
    2852             :                         // the implementation is identical between player and ship.
    2853             :                         [thisEnt removeEquipmentItem:(setDamaged ? key : damagedKey)];
    2854             :                         if ([thisEnt isPlayer])
    2855             :                         {
    2856             :                                 // these player methods are different to the ship ones.
    2857             :                                 [(PlayerEntity*)thisEnt addEquipmentItem:(setDamaged ? damagedKey : key) withValidation:NO inContext:@"scripted"];
    2858             :                                 if (setDamaged)
    2859             :                                 {
    2860             :                                         [(PlayerEntity*)thisEnt doScriptEvent:OOJSID("equipmentDamaged") withArgument:key];
    2861             :                                 }
    2862             :                                 else if (setOK)
    2863             :                                 {
    2864             :                                         [(PlayerEntity*)thisEnt doScriptEvent:OOJSID("equipmentRepaired") withArgument:key];
    2865             :                                 }
    2866             :                                 
    2867             :                                 // if player's Docking Computers are set to EQUIPMENT_DAMAGED while on, stop them
    2868             :                                 // this is now done in a different method
    2869             :                                 // if (hasOK && [key isEqualToString:@"EQ_DOCK_COMP"])  [(PlayerEntity*)thisEnt disengageAutopilot];
    2870             :                         }
    2871             :                         else
    2872             :                         {
    2873             :                                 [thisEnt addEquipmentItem:(setDamaged ? damagedKey : key) withValidation:NO  inContext:@"scripted"];
    2874             :                                 if (hasOK) [thisEnt doScriptEvent:OOJSID("equipmentDamaged") withArgument:key];
    2875             :                         }
    2876             :                 }
    2877             :         }
    2878             :         else
    2879             :         {
    2880             :                 if (hasOK && ![status isEqualToString:@"EQUIPMENT_OK"])
    2881             :                 {
    2882             :                         OOJSReportWarningForCaller(context, @"Ship", @"setEquipmentStatus", @"Equipment %@ cannot be damaged.", key);
    2883             :                         hasOK = NO;
    2884             :                 }
    2885             :         }
    2886             :         
    2887             :         OOJS_RETURN_BOOL(hasOK || hasDamaged);
    2888             :         
    2889             :         OOJS_NATIVE_EXIT
    2890             : }
    2891             : 
    2892             : 
    2893             : // equipmentStatus(type : equipmentInfoExpression) : String
    2894           0 : static JSBool ShipEquipmentStatus(JSContext *context, uintN argc, jsval *vp)
    2895             : {
    2896             :         OOJS_NATIVE_ENTER(context)
    2897             :         
    2898             :         /*
    2899             :                 Interned string constants.
    2900             :                 Interned strings are guaranteed to survive for the lifetime of the JS
    2901             :                 runtime, which lasts as long as Oolite is running.
    2902             :         */
    2903             :         static jsval strOK, strDamaged, strUnavailable, strUnknown;
    2904             :         static BOOL inited = NO;
    2905             :         if (EXPECT_NOT(!inited))
    2906             :         {
    2907             :                 inited = YES;
    2908             :                 strOK = STRING_TO_JSVAL(JS_InternString(context, "EQUIPMENT_OK"));
    2909             :                 strDamaged = STRING_TO_JSVAL(JS_InternString(context, "EQUIPMENT_DAMAGED"));
    2910             :                 strUnavailable = STRING_TO_JSVAL(JS_InternString(context, "EQUIPMENT_UNAVAILABLE"));
    2911             :                 strUnknown = STRING_TO_JSVAL(JS_InternString(context, "EQUIPMENT_UNKNOWN"));
    2912             :         }
    2913             :         
    2914             :         ShipEntity                              *thisEnt = nil;
    2915             :         NSString                                *key = nil;
    2916             :         JSBool                                  asDict = NO;
    2917             :         NSDictionary                    *dict = nil;
    2918             : 
    2919             :         GET_THIS_SHIP(thisEnt);
    2920             :         
    2921             :         if (argc > 0)  key = JSValueToEquipmentKey(context, OOJS_ARGV[0]);
    2922             :         if (argc > 1)  JS_ValueToBoolean(context, OOJS_ARGV[1], &asDict);
    2923             :         if (EXPECT_NOT(key == nil))
    2924             :         {
    2925             :                 if (argc > 0 && JSVAL_IS_STRING(OOJS_ARGV[0]))
    2926             :                 {
    2927             :                         if (asDict)
    2928             :                         {
    2929             :                                 dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1],@"EQUIPMENT_UNKNOWN",nil];
    2930             :                                 OOJS_RETURN_OBJECT(dict);
    2931             :                         }
    2932             :                         else
    2933             :                         {
    2934             :                                 OOJS_RETURN(strUnknown);
    2935             :                         }
    2936             :                 }
    2937             :                 
    2938             :                 OOJSReportBadArguments(context, @"Ship", @"equipmentStatus", MIN(argc, 1U), &OOJS_ARGV[0], nil, @"equipment type");
    2939             :                 return NO;
    2940             :         }
    2941             :         
    2942             : 
    2943             :         if (asDict)
    2944             :         {
    2945             :                 dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:[thisEnt countEquipmentItem:key]],@"EQUIPMENT_OK",[NSNumber numberWithUnsignedInteger:[thisEnt countEquipmentItem:[key stringByAppendingString:@"_DAMAGED"]]],@"EQUIPMENT_DAMAGED",nil];
    2946             :                 OOJS_RETURN_OBJECT(dict);
    2947             :         }
    2948             :         else
    2949             :         {
    2950             :                 if ([thisEnt hasEquipmentItem:key includeWeapons:YES whileLoading:NO])  OOJS_RETURN(strOK);
    2951             :                 else if ([thisEnt hasEquipmentItem:[key stringByAppendingString:@"_DAMAGED"]])  OOJS_RETURN(strDamaged);
    2952             :         
    2953             :                 OOJS_RETURN(strUnavailable);
    2954             :         }
    2955             :         OOJS_NATIVE_EXIT
    2956             : }
    2957             : 
    2958             : 
    2959             : // selectNewMissile()
    2960           0 : static JSBool ShipSelectNewMissile(JSContext *context, uintN argc, jsval *vp)
    2961             : {
    2962             :         OOJS_NATIVE_ENTER(context)
    2963             :         
    2964             :         ShipEntity                              *thisEnt = nil;
    2965             :         
    2966             :         GET_THIS_SHIP(thisEnt);
    2967             :         
    2968             :         NSString *result = [[thisEnt selectMissile] identifier];
    2969             :         // if there's a badly defined missile, selectMissile may return nil
    2970             :         if (result == nil)  result = @"EQ_MISSILE";
    2971             :         
    2972             :         OOJS_RETURN_OBJECT(result);
    2973             :         
    2974             :         OOJS_NATIVE_EXIT
    2975             : }
    2976             : 
    2977             : 
    2978             : // fireMissile()
    2979           0 : static JSBool ShipFireMissile(JSContext *context, uintN argc, jsval *vp)
    2980             : {
    2981             :         OOJS_NATIVE_ENTER(context)
    2982             :         
    2983             :         ShipEntity                      *thisEnt = nil;
    2984             :         id                                      result = nil;
    2985             :         
    2986             :         GET_THIS_SHIP(thisEnt);
    2987             :         
    2988             :         if (argc > 0)  result = [thisEnt fireMissileWithIdentifier:OOStringFromJSValue(context, OOJS_ARGV[0]) andTarget:[thisEnt primaryTarget]];
    2989             :         else  result = [thisEnt fireMissile];
    2990             :         
    2991             :         OOJS_RETURN_OBJECT(result);
    2992             :         
    2993             :         OOJS_NATIVE_EXIT
    2994             : }
    2995             : 
    2996             : 
    2997             : // findNearestStation
    2998           0 : static JSBool ShipFindNearestStation(JSContext *context, uintN argc, jsval *vp)
    2999             : {
    3000             :         OOJS_NATIVE_ENTER(context)
    3001             :         
    3002             :         ShipEntity                      *thisEnt = nil;
    3003             :         StationEntity           *result = nil;
    3004             : 
    3005             :         GET_THIS_SHIP(thisEnt);
    3006             : 
    3007             :         double                          sdist, distance = 1E32;
    3008             :         
    3009             :         NSEnumerator            *statEnum = [[UNIVERSE stations] objectEnumerator];
    3010             :         StationEntity           *se = nil;
    3011             :         while ((se = [statEnum nextObject]))
    3012             :         {
    3013             :                 sdist = HPdistance2([thisEnt position],[se position]);
    3014             : 
    3015             :                 if (sdist < distance)
    3016             :                 {
    3017             :                         distance = sdist;
    3018             :                         result = se;
    3019             :                 }
    3020             :         }
    3021             :         
    3022             :         OOJS_RETURN_OBJECT(result);
    3023             :         
    3024             :         OOJS_NATIVE_EXIT
    3025             : }
    3026             : 
    3027             : 
    3028             : // setBounty(amount, reason)
    3029           0 : static JSBool ShipSetBounty(JSContext *context, uintN argc, jsval *vp)
    3030             : {
    3031             :         OOJS_NATIVE_ENTER(context)
    3032             :         
    3033             :         ShipEntity                              *thisEnt = nil;
    3034             :         NSString                                *reason = nil;
    3035             :         int32                                   newbounty = 0;
    3036             :         BOOL                                    gotBounty = YES;
    3037             :         
    3038             :         GET_THIS_SHIP(thisEnt);
    3039             :         
    3040             :         if (argc > 0)  gotBounty = JS_ValueToInt32(context, OOJS_ARGV[0], &newbounty);
    3041             :         if (argc > 1)  reason = OOStringFromJSValue(context, OOJS_ARGV[1]);
    3042             :         if (EXPECT_NOT(reason == nil || !gotBounty || newbounty < 0))
    3043             :         {
    3044             :                 OOJSReportBadArguments(context, @"Ship", @"setBounty", argc, OOJS_ARGV, nil, @"new bounty and reason");
    3045             :                 return NO;
    3046             :         }
    3047             :         
    3048             :         [thisEnt setBounty:(OOCreditsQuantity)newbounty withReasonAsString:reason];
    3049             :         
    3050             :         return YES;
    3051             :         
    3052             :         OOJS_NATIVE_EXIT
    3053             : }
    3054             : 
    3055             : 
    3056             : // setCargo(cargoType : String [, number : count])
    3057           0 : static JSBool ShipSetCargo(JSContext *context, uintN argc, jsval *vp)
    3058             : {
    3059             :         OOJS_NATIVE_ENTER(context)
    3060             :         
    3061             :         ShipEntity                              *thisEnt = nil;
    3062             :         OOCommodityType                 commodity = nil;
    3063             :         int32                                   count = 1;
    3064             :         BOOL                                    gotCount = YES;
    3065             :         
    3066             :         GET_THIS_SHIP(thisEnt);
    3067             :         
    3068             :         if (argc > 0)  commodity = OOStringFromJSValue(context, OOJS_ARGV[0]);
    3069             :         if (argc > 1)  gotCount = JS_ValueToInt32(context, OOJS_ARGV[1], &count);
    3070             :         if (EXPECT_NOT(commodity == nil || !gotCount || count < 1))
    3071             :         {
    3072             :                 OOJSReportBadArguments(context, @"Ship", @"setCargo", argc, OOJS_ARGV, nil, @"cargo name and optional positive quantity");
    3073             :                 return NO;
    3074             :         }
    3075             :         
    3076             :         if ([[UNIVERSE commodities] goodDefined:commodity])
    3077             :         {
    3078             :                 [thisEnt setCommodityForPod:commodity andAmount:count];
    3079             :         }
    3080             :         
    3081             :         OOJS_RETURN_BOOL([[UNIVERSE commodities] goodDefined:commodity]);
    3082             :         
    3083             :         OOJS_NATIVE_EXIT
    3084             : }
    3085             : 
    3086             : 
    3087             : // setCrew(crewDefinition : Object)
    3088           0 : static JSBool ShipSetCrew(JSContext *context, uintN argc, jsval *vp)
    3089             : {
    3090             :         /* TODO: ships can in theory have multiple crew, so this could
    3091             :          * allow that to be set. Probably not necessary for now. */
    3092             :         OOJS_NATIVE_ENTER(context)
    3093             :         
    3094             :         ShipEntity                              *thisEnt = nil;
    3095             :         NSDictionary                    *crewDefinition = nil;
    3096             :         JSObject                        *params = NULL;
    3097             : 
    3098             :         GET_THIS_SHIP(thisEnt);
    3099             :         
    3100             :         if (argc < 1 || (!JSVAL_IS_NULL(OOJS_ARGV[0]) && !JS_ValueToObject(context, OOJS_ARGV[0], &params)))
    3101             :         {
    3102             :                 OOJSReportBadArguments(context, @"Ship", @"setCrew", MIN(argc, 1U), OOJS_ARGV, NULL, @"definition");
    3103             :                 return NO;
    3104             :         }
    3105             :         BOOL success = YES;
    3106             : 
    3107             :         if (![thisEnt isExplicitlyUnpiloted])
    3108             :         {
    3109             :                 if (JSVAL_IS_NULL(OOJS_ARGV[0]))
    3110             :                 {
    3111             :                         [thisEnt setCrew:nil];
    3112             :                 }
    3113             :                 else
    3114             :                 {
    3115             :                         crewDefinition = OOJSNativeObjectFromJSObject(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]));
    3116             :                         OOCharacter *crew = [OOCharacter characterWithDictionary:crewDefinition];
    3117             :                         [thisEnt setCrew:[NSArray arrayWithObject:crew]];
    3118             :                 }
    3119             :         }
    3120             :         else
    3121             :         {
    3122             :                 success = NO;
    3123             :         }
    3124             : 
    3125             :         OOJS_RETURN_BOOL(success);
    3126             :         
    3127             :         OOJS_NATIVE_EXIT
    3128             : }
    3129             : 
    3130             : 
    3131             : // setCargoType(cargoType : String)
    3132           0 : static JSBool ShipSetCargoType(JSContext *context, uintN argc, jsval *vp)
    3133             : {
    3134             :         OOJS_NATIVE_ENTER(context)
    3135             :         
    3136             :         ShipEntity                              *thisEnt = nil;
    3137             :         NSString                                *cargoType = nil;
    3138             :         
    3139             :         GET_THIS_SHIP(thisEnt);
    3140             :         
    3141             :         if (argc > 0)  cargoType = OOStringFromJSValue(context, OOJS_ARGV[0]);
    3142             :         if (EXPECT_NOT(cargoType == nil))
    3143             :         {
    3144             :                 OOJSReportBadArguments(context, @"Ship", @"setCargoType", argc, OOJS_ARGV, nil, @"cargo type name");
    3145             :                 return NO;
    3146             :         }
    3147             :         if ([thisEnt cargoType] != CARGO_NOT_CARGO)
    3148             :         {
    3149             :                 OOJSReportBadArguments(context, @"Ship", @"setCargoType", argc, OOJS_ARGV, nil, [NSString stringWithFormat:@"Can only be used on cargo pod carriers, not cargo pods (%@)",[thisEnt shipDataKey]]);
    3150             :                 return NO;
    3151             :         }
    3152             :         BOOL ok = YES;
    3153             :         if ([cargoType isEqualToString:@"SCARCE_GOODS"])
    3154             :         {
    3155             :                 [thisEnt setCargoFlag:CARGO_FLAG_FULL_SCARCE];
    3156             :         }
    3157             :         else if ([cargoType isEqualToString:@"PLENTIFUL_GOODS"])
    3158             :         {
    3159             :                 [thisEnt setCargoFlag:CARGO_FLAG_FULL_PLENTIFUL];
    3160             :         }
    3161             :         else if ([cargoType isEqualToString:@"MEDICAL_GOODS"])
    3162             :         {
    3163             :                 [thisEnt setCargoFlag:CARGO_FLAG_FULL_MEDICAL];
    3164             :         }
    3165             :         else if ([cargoType isEqualToString:@"ILLEGAL_GOODS"])
    3166             :         {
    3167             :                 [thisEnt setCargoFlag:CARGO_FLAG_FULL_CONTRABAND];
    3168             :         }
    3169             :         else if ([cargoType isEqualToString:@"PIRATE_GOODS"])
    3170             :         {
    3171             :                 [thisEnt setCargoFlag:CARGO_FLAG_PIRATE];
    3172             :         }       
    3173             :         else
    3174             :         {
    3175             :                 ok = NO;
    3176             :         }
    3177             :         OOJS_RETURN_BOOL(ok);
    3178             : 
    3179             :         OOJS_NATIVE_EXIT
    3180             : }
    3181             : 
    3182             : // setMaterials(params: dict, [shaders: dict])  // sets materials dictionary. Optional parameter sets the shaders dictionary too.
    3183           0 : static JSBool ShipSetMaterials(JSContext *context, uintN argc, jsval *vp)
    3184             : {
    3185             :         OOJS_NATIVE_ENTER(context)
    3186             :         
    3187             :         ShipEntity                              *thisEnt = nil;
    3188             :         
    3189             :         if (argc < 1)
    3190             :         {
    3191             :                 OOJSReportBadArguments(context, @"Ship", @"setMaterials", 0, OOJS_ARGV, nil, @"parameter object");
    3192             :                 return NO;
    3193             :         }
    3194             :         
    3195             :         GET_THIS_SHIP(thisEnt);
    3196             :         
    3197             :         return ShipSetMaterialsInternal(context, argc, vp, thisEnt, NO);
    3198             :         
    3199             :         OOJS_NATIVE_EXIT
    3200             : }
    3201             : 
    3202             : 
    3203             : // setShaders(params: dict) 
    3204           0 : static JSBool ShipSetShaders(JSContext *context, uintN argc, jsval *vp)
    3205             : {
    3206             :         OOJS_NATIVE_ENTER(context)
    3207             :         
    3208             :         ShipEntity                              *thisEnt = nil;
    3209             :         
    3210             :         GET_THIS_SHIP(thisEnt);
    3211             :         
    3212             :         if (argc < 1)
    3213             :         {
    3214             :                 OOJSReportBadArguments(context, @"Ship", @"setShaders", 0, OOJS_ARGV, nil, @"parameter object");
    3215             :                 return NO;
    3216             :         }
    3217             :         
    3218             :         if (JSVAL_IS_NULL(OOJS_ARGV[0]) || (!JSVAL_IS_NULL(OOJS_ARGV[0]) && !JSVAL_IS_OBJECT(OOJS_ARGV[0])))
    3219             :         {
    3220             :                 // EMMSTRAN: JS_ValueToObject() and normal error handling here.
    3221             :                 OOJSReportWarning(context, @"Ship.%@: expected %@ instead of '%@'.", @"setShaders", @"object", OOStringFromJSValueEvenIfNull(context, OOJS_ARGV[0]));
    3222             :                 OOJS_RETURN_BOOL(NO);
    3223             :         }
    3224             :         
    3225             :         OOJS_ARGV[1] = OOJS_ARGV[0];
    3226             :         return ShipSetMaterialsInternal(context, argc, vp, thisEnt, YES);
    3227             :         
    3228             :         OOJS_NATIVE_EXIT
    3229             : }
    3230             : 
    3231             : 
    3232           0 : static JSBool ShipSetMaterialsInternal(JSContext *context, uintN argc, jsval *vp, ShipEntity *thisEnt, BOOL fromShaders)
    3233             : {
    3234             :         OOJS_PROFILE_ENTER
    3235             :         
    3236             :         JSObject                                *params = NULL;
    3237             :         NSDictionary                    *materials;
    3238             :         NSDictionary                    *shaders;
    3239             :         BOOL                                    withShaders = NO;
    3240             :         BOOL                                    success = NO;
    3241             :         
    3242             :         GET_THIS_SHIP(thisEnt);
    3243             :         
    3244             :         if (JSVAL_IS_NULL(OOJS_ARGV[0]) || (!JSVAL_IS_NULL(OOJS_ARGV[0]) && !JSVAL_IS_OBJECT(OOJS_ARGV[0])))
    3245             :         {
    3246             :                 OOJSReportWarning(context, @"Ship.%@: expected %@ instead of '%@'.", @"setMaterials", @"object", OOStringFromJSValueEvenIfNull(context, OOJS_ARGV[0]));
    3247             :                 OOJS_RETURN_BOOL(NO);
    3248             :         }
    3249             :         
    3250             :         if (argc > 1)
    3251             :         {
    3252             :                 withShaders = YES;
    3253             :                 if (JSVAL_IS_NULL(OOJS_ARGV[1]) || (!JSVAL_IS_NULL(OOJS_ARGV[1]) && !JSVAL_IS_OBJECT(OOJS_ARGV[1])))
    3254             :                 {
    3255             :                         OOJSReportWarning(context, @"Ship.%@: expected %@ instead of '%@'.",  @"setMaterials", @"object as second parameter", OOStringFromJSValueEvenIfNull(context, OOJS_ARGV[1]));
    3256             :                         withShaders = NO;
    3257             :                 }
    3258             :         }
    3259             :         
    3260             :         if (fromShaders)
    3261             :         {
    3262             :                 materials = [[thisEnt mesh] materials];
    3263             :                 params = JSVAL_TO_OBJECT(OOJS_ARGV[0]);
    3264             :                 shaders = OOJSNativeObjectFromJSObject(context, params);
    3265             :         }
    3266             :         else
    3267             :         {
    3268             :                 params = JSVAL_TO_OBJECT(OOJS_ARGV[0]);
    3269             :                 materials = OOJSNativeObjectFromJSObject(context, params);
    3270             :                 if (withShaders)
    3271             :                 {
    3272             :                         params = JSVAL_TO_OBJECT(OOJS_ARGV[1]);
    3273             :                         shaders = OOJSNativeObjectFromJSObject(context, params);
    3274             :                 }
    3275             :                 else
    3276             :                 {
    3277             :                         shaders = [[thisEnt mesh] shaders];
    3278             :                 }
    3279             :         }
    3280             :         
    3281             :         OOJS_BEGIN_FULL_NATIVE(context)
    3282             :         NSDictionary                    *shipDict = [thisEnt shipInfoDictionary];
    3283             :         
    3284             :         // First we test to see if we can create the mesh.
    3285             :         OOMesh *mesh = [OOMesh meshWithName:[shipDict oo_stringForKey:@"model"]
    3286             :                                                            cacheKey:nil
    3287             :                                          materialDictionary:materials
    3288             :                                           shadersDictionary:shaders
    3289             :                                                                  smooth:[shipDict oo_boolForKey:@"smooth" defaultValue:NO]
    3290             :                                                    shaderMacros:[[ResourceManager materialDefaults] oo_dictionaryForKey:@"ship-prefix-macros"]
    3291             :                                         shaderBindingTarget:thisEnt];
    3292             :         
    3293             :         if (mesh != nil)
    3294             :         {
    3295             :                 [thisEnt setMesh:mesh];
    3296             :                 success = YES;
    3297             :         }
    3298             :         OOJS_END_FULL_NATIVE
    3299             :         
    3300             :         OOJS_RETURN_BOOL(success);
    3301             :         
    3302             :         OOJS_PROFILE_EXIT
    3303             : }
    3304             : 
    3305             : 
    3306             : // exitSystem([int systemID])
    3307           0 : static JSBool ShipExitSystem(JSContext *context, uintN argc, jsval *vp)
    3308             : {
    3309             :         OOJS_NATIVE_ENTER(context)
    3310             :         
    3311             :         ShipEntity                      *thisEnt = nil;
    3312             :         int32                           systemID = -1;
    3313             :         BOOL                            OK = NO;
    3314             :         
    3315             :         GET_THIS_SHIP(thisEnt);
    3316             :         if (EXPECT_NOT([thisEnt isPlayer]))
    3317             :         {
    3318             :                 OOJSReportErrorForCaller(context, @"Ship", @"exitSystem", @"Not valid for player ship.");
    3319             :                 return NO;
    3320             :         }
    3321             :         
    3322             :         if (argc > 0)
    3323             :         {
    3324             :                 if (!JS_ValueToInt32(context, OOJS_ARGV[0], &systemID) || systemID < 0 || 255 < systemID)
    3325             :                 {
    3326             :                         OOJSReportBadArguments(context, @"Ship", @"exitSystem", MIN(argc, 1U), OOJS_ARGV, nil, @"system ID");
    3327             :                         return NO;
    3328             :                 }
    3329             :         }
    3330             :         
    3331             :         OK = [thisEnt performHyperSpaceToSpecificSystem:systemID];      // -1 == random destination system
    3332             :         
    3333             :         OOJS_RETURN_BOOL(OK);
    3334             :         
    3335             :         OOJS_NATIVE_EXIT
    3336             : }
    3337             : 
    3338             : 
    3339           0 : static JSBool ShipUpdateEscortFormation(JSContext *context, uintN argc, jsval *vp)
    3340             : {
    3341             :         OOJS_PROFILE_ENTER
    3342             :         
    3343             :         ShipEntity *thisEnt = nil;
    3344             :         GET_THIS_SHIP(thisEnt);
    3345             :         [thisEnt updateEscortFormation];
    3346             :         
    3347             :         OOJS_RETURN_VOID;
    3348             :         
    3349             :         OOJS_PROFILE_EXIT
    3350             : }
    3351             : 
    3352           0 : static BOOL RemoveOrExplodeShip(JSContext *context, uintN argc, jsval *vp, BOOL explode)
    3353             : {
    3354             :         OOJS_PROFILE_ENTER
    3355             :         
    3356             :         ShipEntity                              *thisEnt = nil;
    3357             :         
    3358             :         GET_THIS_SHIP(thisEnt);
    3359             :         
    3360             :         if (EXPECT_NOT([thisEnt isPlayer]))
    3361             :         {
    3362             :                 NSCAssert(explode, @"RemoveOrExplodeShip(): shouldn't be called for player with !explode.");  // player.ship.remove() is blocked by caller.
    3363             :                 PlayerEntity *player = (PlayerEntity *)thisEnt;
    3364             :                 
    3365             :                 if ([player isDocked])
    3366             :                 {
    3367             :                         OOJSReportError(context, @"Cannot explode() player's ship while docked.");
    3368             :                         return NO;
    3369             :                 }
    3370             :         }
    3371             :         
    3372             :         if (thisEnt == (ShipEntity *)[UNIVERSE station])
    3373             :         {
    3374             :                 // Allow exploding of main station (e.g. nova mission)
    3375             :                 [UNIVERSE unMagicMainStation];
    3376             :         }
    3377             :         
    3378             :         if (EXPECT_NOT([thisEnt status] == STATUS_DOCKED))
    3379             :         {
    3380             :                 /* If it's in the launch queue and hasn't yet been added to
    3381             :                  * the universe, set the status to DEAD (so it gets removed
    3382             :                  * from the launch queue) */
    3383             :                 [thisEnt setStatus:STATUS_DEAD];
    3384             :                 /* No shipDied event occurs in this place - it never really
    3385             :                  * existed. This case is just to prevent an error from the
    3386             :                  * usual code branch - removing ships while they're still
    3387             :                  * docked is not recommended. */
    3388             :         }
    3389             :         else
    3390             :         {
    3391             :                 [thisEnt setSuppressExplosion:!explode];
    3392             :                 [thisEnt setEnergy:1];
    3393             :                 [thisEnt takeEnergyDamage:500000000.0 from:nil becauseOf:nil weaponIdentifier:@""];
    3394             :         }
    3395             :         
    3396             :         OOJS_RETURN_VOID;
    3397             :         
    3398             :         OOJS_PROFILE_EXIT
    3399             : }
    3400             : 
    3401           0 : static JSBool ShipClearDefenseTargets(JSContext *context, uintN argc, jsval *vp)
    3402             : {
    3403             :         OOJS_PROFILE_ENTER
    3404             :         
    3405             :         ShipEntity *thisEnt = nil;
    3406             :         GET_THIS_SHIP(thisEnt);
    3407             :         [thisEnt removeAllDefenseTargets];
    3408             :         
    3409             :         OOJS_RETURN_VOID;
    3410             :         
    3411             :         OOJS_PROFILE_EXIT
    3412             : }
    3413             : 
    3414             : 
    3415           0 : static JSBool ShipAddDefenseTarget(JSContext *context, uintN argc, jsval *vp)
    3416             : {
    3417             :         OOJS_PROFILE_ENTER
    3418             :         
    3419             :         ShipEntity *thisEnt = nil;
    3420             :         ShipEntity                              *target = nil;
    3421             : 
    3422             :         GET_THIS_SHIP(thisEnt);
    3423             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && (JSVAL_IS_NULL(OOJS_ARGV[0]) || !JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target)))))
    3424             :         {
    3425             :                 OOJSReportBadArguments(context, @"Ship", @"addDefenseTarget", 1U, OOJS_ARGV, nil, @"target");
    3426             :                 return NO;
    3427             :         }
    3428             :         
    3429             :         [thisEnt addDefenseTarget:target];
    3430             : 
    3431             :         OOJS_RETURN_VOID;
    3432             :         
    3433             :         OOJS_PROFILE_EXIT
    3434             : }
    3435             : 
    3436             : 
    3437           0 : static JSBool ShipRemoveDefenseTarget(JSContext *context, uintN argc, jsval *vp)
    3438             : {
    3439             :         OOJS_PROFILE_ENTER
    3440             :         
    3441             :         ShipEntity *thisEnt = nil;
    3442             :         ShipEntity                              *target = nil;
    3443             : 
    3444             :         GET_THIS_SHIP(thisEnt);
    3445             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && (JSVAL_IS_NULL(OOJS_ARGV[0]) || !JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target)))))
    3446             :         {
    3447             :                 OOJSReportBadArguments(context, @"Ship", @"removeDefenseTarget", 1U, OOJS_ARGV, nil, @"target");
    3448             :                 return NO;
    3449             :         }
    3450             :         
    3451             :         [thisEnt removeDefenseTarget:target];
    3452             : 
    3453             :         OOJS_RETURN_VOID;
    3454             :         
    3455             :         OOJS_PROFILE_EXIT
    3456             : }
    3457             : 
    3458             : 
    3459           0 : static JSBool ShipAddCollisionException(JSContext *context, uintN argc, jsval *vp)
    3460             : {
    3461             :         OOJS_PROFILE_ENTER
    3462             :         
    3463             :         ShipEntity *thisEnt = nil;
    3464             :         ShipEntity                              *target = nil;
    3465             : 
    3466             :         GET_THIS_SHIP(thisEnt);
    3467             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && (JSVAL_IS_NULL(OOJS_ARGV[0]) || !JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target)))))
    3468             :         {
    3469             :                 OOJSReportBadArguments(context, @"Ship", @"addCollisionException", 1U, OOJS_ARGV, nil, @"other ship");
    3470             :                 return NO;
    3471             :         }
    3472             :         
    3473             :         // have to do it both ways because it's not defined which order
    3474             :         // the collisions get tested in. More efficient to add both ways
    3475             :     // than to test both ways
    3476             :         [thisEnt addCollisionException:target];
    3477             :         [target addCollisionException:thisEnt];
    3478             : 
    3479             :         OOJS_RETURN_VOID;
    3480             :         
    3481             :         OOJS_PROFILE_EXIT
    3482             : }
    3483             : 
    3484             : 
    3485           0 : static JSBool ShipRemoveCollisionException(JSContext *context, uintN argc, jsval *vp)
    3486             : {
    3487             :         OOJS_PROFILE_ENTER
    3488             :         
    3489             :         ShipEntity *thisEnt = nil;
    3490             :         ShipEntity                              *target = nil;
    3491             : 
    3492             :         GET_THIS_SHIP(thisEnt);
    3493             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && (JSVAL_IS_NULL(OOJS_ARGV[0]) || !JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target)))))
    3494             :         {
    3495             :                 OOJSReportBadArguments(context, @"Ship", @"removeCollisionException", 1U, OOJS_ARGV, nil, @"other ship");
    3496             :                 return NO;
    3497             :         }
    3498             :         
    3499             :         // doesn't need a check to see if it was already gone
    3500             :         [thisEnt removeCollisionException:target];
    3501             :         [target removeCollisionException:thisEnt];
    3502             : 
    3503             :         OOJS_RETURN_VOID;
    3504             :         
    3505             :         OOJS_PROFILE_EXIT
    3506             : }
    3507             : 
    3508             : 
    3509             : //getMaterials()
    3510           0 : static JSBool ShipGetMaterials(JSContext *context, uintN argc, jsval *vp)
    3511             : {
    3512             :         OOJS_PROFILE_ENTER
    3513             :         
    3514             :         ShipEntity              *thisEnt = nil;
    3515             :         NSObject                        *result = nil;
    3516             :         
    3517             :         GET_THIS_SHIP(thisEnt);
    3518             :         
    3519             :         result = [[thisEnt mesh] materials];
    3520             :         if (result == nil)  result = [NSDictionary dictionary];
    3521             :         OOJS_RETURN_OBJECT(result);
    3522             :         
    3523             :         OOJS_PROFILE_EXIT
    3524             : }
    3525             : 
    3526             : //getShaders()
    3527           0 : static JSBool ShipGetShaders(JSContext *context, uintN argc, jsval *vp)
    3528             : {
    3529             :         OOJS_PROFILE_ENTER
    3530             :         
    3531             :         ShipEntity              *thisEnt = nil;
    3532             :         NSObject                *result = nil;
    3533             :         
    3534             :         GET_THIS_SHIP(thisEnt);
    3535             :         
    3536             :         result = [[thisEnt mesh] shaders];
    3537             :         if (result == nil)  result = [NSDictionary dictionary];
    3538             :         OOJS_RETURN_OBJECT(result);
    3539             :         
    3540             :         OOJS_PROFILE_EXIT
    3541             : }
    3542             : 
    3543           0 : static JSBool ShipBroadcastCascadeImminent(JSContext *context, uintN argc, jsval *vp)
    3544             : {
    3545             :         OOJS_PROFILE_ENTER
    3546             :         
    3547             :         ShipEntity *thisEnt = nil;
    3548             :         GET_THIS_SHIP(thisEnt);
    3549             :         [thisEnt broadcastEnergyBlastImminent];
    3550             :         
    3551             :         OOJS_RETURN_VOID;
    3552             :         
    3553             :         OOJS_PROFILE_EXIT
    3554             : }
    3555             : 
    3556           0 : static JSBool ShipBecomeCascadeExplosion(JSContext *context, uintN argc, jsval *vp)
    3557             : {
    3558             :         OOJS_PROFILE_ENTER
    3559             :         
    3560             :         ShipEntity *thisEnt = nil;
    3561             :         GET_THIS_SHIP(thisEnt);
    3562             :         [thisEnt becomeEnergyBlast];
    3563             :         
    3564             :         OOJS_RETURN_VOID;
    3565             :         
    3566             :         OOJS_PROFILE_EXIT
    3567             : }
    3568             : 
    3569             : 
    3570           0 : static JSBool ShipOfferToEscort(JSContext *context, uintN argc, jsval *vp)
    3571             : {
    3572             :         OOJS_PROFILE_ENTER
    3573             :         
    3574             :         ShipEntity *thisEnt = nil;
    3575             :         ShipEntity                              *mother = nil;
    3576             : 
    3577             :         GET_THIS_SHIP(thisEnt);
    3578             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && (JSVAL_IS_NULL(OOJS_ARGV[0]) || !JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &mother)))))
    3579             :         {
    3580             :                 OOJSReportBadArguments(context, @"Ship", @"offerToEscort", 1U, OOJS_ARGV, nil, @"target");
    3581             :                 return NO;
    3582             :         }
    3583             :         
    3584             :         BOOL result = [thisEnt suggestEscortTo:mother];
    3585             : 
    3586             :         OOJS_RETURN_BOOL(result);
    3587             :         
    3588             :         OOJS_PROFILE_EXIT
    3589             : }
    3590             : 
    3591             : 
    3592           0 : static JSBool ShipRequestHelpFromGroup(JSContext *context, uintN argc, jsval *vp)
    3593             : {
    3594             :         OOJS_PROFILE_ENTER
    3595             :         
    3596             :         ShipEntity *thisEnt = nil;
    3597             : 
    3598             :         GET_THIS_SHIP(thisEnt);
    3599             :         
    3600             :         [thisEnt groupAttackTarget];
    3601             : 
    3602             :         OOJS_RETURN_VOID;
    3603             :         
    3604             :         OOJS_PROFILE_EXIT
    3605             : }
    3606             : 
    3607             : 
    3608           0 : static JSBool ShipPatrolReportIn(JSContext *context, uintN argc, jsval *vp)
    3609             : {
    3610             :         OOJS_PROFILE_ENTER
    3611             :         
    3612             :         ShipEntity *thisEnt = nil;
    3613             :         ShipEntity                              *target = nil;
    3614             : 
    3615             :         GET_THIS_SHIP(thisEnt);
    3616             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && (JSVAL_IS_NULL(OOJS_ARGV[0]) || !JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !JSShipGetShipEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &target)))))
    3617             :         {
    3618             :                 OOJSReportBadArguments(context, @"Ship", @"addDefenseTarget", 1U, OOJS_ARGV, nil, @"target");
    3619             :                 return NO;
    3620             :         }
    3621             :         if ([target isStation])
    3622             :         {
    3623             :                 StationEntity *station = (StationEntity*)target;
    3624             :                 [station acceptPatrolReportFrom:thisEnt];
    3625             :         }
    3626             : 
    3627             :         OOJS_RETURN_VOID;
    3628             :         
    3629             :         OOJS_PROFILE_EXIT
    3630             : }
    3631             : 
    3632             : 
    3633           0 : static JSBool ShipMarkTargetForFines(JSContext *context, uintN argc, jsval *vp)
    3634             : {
    3635             :         OOJS_PROFILE_ENTER
    3636             :         
    3637             :         ShipEntity *thisEnt = nil;
    3638             : 
    3639             :         GET_THIS_SHIP(thisEnt);
    3640             : 
    3641             :         ShipEntity *ship = [thisEnt primaryTarget];
    3642             :         BOOL ok = NO;
    3643             :         if ((ship != nil) && ([ship status] != STATUS_DEAD) && ([ship status] != STATUS_DOCKED))
    3644             :         {
    3645             :                 ok = [ship markForFines];
    3646             :         }
    3647             : 
    3648             :         OOJS_RETURN_BOOL(ok);
    3649             :         
    3650             :         OOJS_PROFILE_EXIT
    3651             : }
    3652             : 
    3653             : 
    3654           0 : static JSBool ShipEnterWormhole(JSContext *context, uintN argc, jsval *vp)
    3655             : {
    3656             :         OOJS_PROFILE_ENTER
    3657             :         
    3658             :         ShipEntity *thisEnt = nil;
    3659             :         Entity  *hole = nil;
    3660             : 
    3661             :         if ([PLAYER status] != STATUS_ENTERING_WITCHSPACE)
    3662             :         {
    3663             :                 OOJSReportError(context, @"Cannot use this function while player's ship not entering witchspace.");
    3664             :                 return NO;
    3665             :         }
    3666             : 
    3667             :         GET_THIS_SHIP(thisEnt);
    3668             :         if (EXPECT_NOT(argc == 0 || (argc > 0 && !JSVAL_IS_NULL(OOJS_ARGV[0]) && (!JSVAL_IS_OBJECT(OOJS_ARGV[0]) || !OOJSEntityGetEntity(context, JSVAL_TO_OBJECT(OOJS_ARGV[0]), &hole)))))
    3669             :         {
    3670             :                 [thisEnt enterPlayerWormhole];
    3671             :         }
    3672             :         else 
    3673             :         {
    3674             :                 if (![hole isWormhole])
    3675             :                 {
    3676             :                         OOJSReportBadArguments(context, @"Ship", @"enterWormhole", 1U, OOJS_ARGV, nil, @"[wormhole]");
    3677             :                         return NO;
    3678             :                 }
    3679             : 
    3680             :                 [thisEnt enterWormhole:(WormholeEntity*)hole];
    3681             :         }
    3682             : 
    3683             :         OOJS_RETURN_VOID;
    3684             :         
    3685             :         OOJS_PROFILE_EXIT
    3686             : }
    3687             : 
    3688             : 
    3689           0 : static JSBool ShipNotifyGroupOfWormhole(JSContext *context, uintN argc, jsval *vp)
    3690             : {
    3691             :         OOJS_PROFILE_ENTER
    3692             :         
    3693             :         ShipEntity *thisEnt = nil;
    3694             :         GET_THIS_SHIP(thisEnt);
    3695             : 
    3696             :         [thisEnt wormholeEntireGroup];
    3697             : 
    3698             :         OOJS_RETURN_VOID;
    3699             :         
    3700             :         OOJS_PROFILE_EXIT
    3701             : }
    3702             : 
    3703             : 
    3704           0 : static JSBool ShipThrowSpark(JSContext *context, uintN argc, jsval *vp)
    3705             : {
    3706             :         OOJS_PROFILE_ENTER
    3707             :         
    3708             :         ShipEntity *thisEnt = nil;
    3709             :         GET_THIS_SHIP(thisEnt);
    3710             :         [thisEnt setThrowSparks:YES];
    3711             :         
    3712             :         OOJS_RETURN_VOID;
    3713             :         
    3714             :         OOJS_PROFILE_EXIT
    3715             : }
    3716             : 
    3717             : 
    3718             : 
    3719           0 : static JSBool ShipPerformAttack(JSContext *context, uintN argc, jsval *vp)
    3720             : {
    3721             :         OOJS_PROFILE_ENTER
    3722             :         
    3723             :         ShipEntity *thisEnt = nil;
    3724             :         GET_THIS_SHIP(thisEnt);
    3725             :         [thisEnt performAttack];
    3726             :         
    3727             :         OOJS_RETURN_VOID;
    3728             :         
    3729             :         OOJS_PROFILE_EXIT
    3730             : }
    3731             : 
    3732             : 
    3733           0 : static JSBool ShipPerformCollect(JSContext *context, uintN argc, jsval *vp)
    3734             : {
    3735             :         OOJS_PROFILE_ENTER
    3736             :         
    3737             :         ShipEntity *thisEnt = nil;
    3738             :         GET_THIS_SHIP(thisEnt);
    3739             :         [thisEnt performCollect];
    3740             :         
    3741             :         OOJS_RETURN_VOID;
    3742             :         
    3743             :         OOJS_PROFILE_EXIT
    3744             : }
    3745             : 
    3746             : 
    3747           0 : static JSBool ShipPerformEscort(JSContext *context, uintN argc, jsval *vp)
    3748             : {
    3749             :         OOJS_PROFILE_ENTER
    3750             :         
    3751             :         ShipEntity *thisEnt = nil;
    3752             :         GET_THIS_SHIP(thisEnt);
    3753             :         [thisEnt performEscort];
    3754             :         
    3755             :         OOJS_RETURN_VOID;
    3756             :         
    3757             :         OOJS_PROFILE_EXIT
    3758             : }
    3759             : 
    3760             : 
    3761           0 : static JSBool ShipPerformFaceDestination(JSContext *context, uintN argc, jsval *vp)
    3762             : {
    3763             :         OOJS_PROFILE_ENTER
    3764             :         
    3765             :         ShipEntity *thisEnt = nil;
    3766             :         GET_THIS_SHIP(thisEnt);
    3767             :         [thisEnt performFaceDestination];
    3768             :         
    3769             :         OOJS_RETURN_VOID;
    3770             :         
    3771             :         OOJS_PROFILE_EXIT
    3772             : }
    3773             : 
    3774             : 
    3775           0 : static JSBool ShipPerformFlee(JSContext *context, uintN argc, jsval *vp)
    3776             : {
    3777             :         OOJS_PROFILE_ENTER
    3778             :         
    3779             :         ShipEntity *thisEnt = nil;
    3780             :         GET_THIS_SHIP(thisEnt);
    3781             :         [thisEnt performFlee];
    3782             :         
    3783             :         OOJS_RETURN_VOID;
    3784             :         
    3785             :         OOJS_PROFILE_EXIT
    3786             : }
    3787             : 
    3788             : 
    3789           0 : static JSBool ShipPerformFlyToRangeFromDestination(JSContext *context, uintN argc, jsval *vp)
    3790             : {
    3791             :         OOJS_PROFILE_ENTER
    3792             :         
    3793             :         ShipEntity *thisEnt = nil;
    3794             :         GET_THIS_SHIP(thisEnt);
    3795             :         [thisEnt performFlyToRangeFromDestination];
    3796             :         
    3797             :         OOJS_RETURN_VOID;
    3798             :         
    3799             :         OOJS_PROFILE_EXIT
    3800             : }
    3801             : 
    3802             : 
    3803           0 : static JSBool ShipPerformHold(JSContext *context, uintN argc, jsval *vp)
    3804             : {
    3805             :         OOJS_PROFILE_ENTER
    3806             :         
    3807             :         ShipEntity *thisEnt = nil;
    3808             :         GET_THIS_SHIP(thisEnt);
    3809             :         [thisEnt performHold];
    3810             :         
    3811             :         OOJS_RETURN_VOID;
    3812             :         
    3813             :         OOJS_PROFILE_EXIT
    3814             : }
    3815             : 
    3816             : 
    3817           0 : static JSBool ShipPerformIdle(JSContext *context, uintN argc, jsval *vp)
    3818             : {
    3819             :         OOJS_PROFILE_ENTER
    3820             :         
    3821             :         ShipEntity *thisEnt = nil;
    3822             :         GET_THIS_SHIP(thisEnt);
    3823             :         [thisEnt performIdle];
    3824             :         
    3825             :         OOJS_RETURN_VOID;
    3826             :         
    3827             :         OOJS_PROFILE_EXIT
    3828             : }
    3829             : 
    3830             : 
    3831           0 : static JSBool ShipPerformIntercept(JSContext *context, uintN argc, jsval *vp)
    3832             : {
    3833             :         OOJS_PROFILE_ENTER
    3834             :         
    3835             :         ShipEntity *thisEnt = nil;
    3836             :         GET_THIS_SHIP(thisEnt);
    3837             :         [thisEnt performIntercept];
    3838             :         
    3839             :         OOJS_RETURN_VOID;
    3840             :         
    3841             :         OOJS_PROFILE_EXIT
    3842             : }
    3843             : 
    3844             : 
    3845           0 : static JSBool ShipPerformLandOnPlanet(JSContext *context, uintN argc, jsval *vp)
    3846             : {
    3847             :         OOJS_PROFILE_ENTER
    3848             :         
    3849             :         ShipEntity *thisEnt = nil;
    3850             :         GET_THIS_SHIP(thisEnt);
    3851             :         [thisEnt performLandOnPlanet];
    3852             :         
    3853             :         OOJS_RETURN_VOID;
    3854             :         
    3855             :         OOJS_PROFILE_EXIT
    3856             : }
    3857             : 
    3858             : 
    3859           0 : static JSBool ShipPerformMining(JSContext *context, uintN argc, jsval *vp)
    3860             : {
    3861             :         OOJS_PROFILE_ENTER
    3862             :         
    3863             :         ShipEntity *thisEnt = nil;
    3864             :         GET_THIS_SHIP(thisEnt);
    3865             :         [thisEnt performMining];
    3866             :         
    3867             :         OOJS_RETURN_VOID;
    3868             :         
    3869             :         OOJS_PROFILE_EXIT
    3870             : }
    3871             : 
    3872             : 
    3873           0 : static JSBool ShipPerformScriptedAI(JSContext *context, uintN argc, jsval *vp)
    3874             : {
    3875             :         OOJS_PROFILE_ENTER
    3876             :         
    3877             :         ShipEntity *thisEnt = nil;
    3878             :         GET_THIS_SHIP(thisEnt);
    3879             :         [thisEnt performScriptedAI];
    3880             :         
    3881             :         OOJS_RETURN_VOID;
    3882             :         
    3883             :         OOJS_PROFILE_EXIT
    3884             : }
    3885             : 
    3886             : 
    3887           0 : static JSBool ShipPerformScriptedAttackAI(JSContext *context, uintN argc, jsval *vp)
    3888             : {
    3889             :         OOJS_PROFILE_ENTER
    3890             :         
    3891             :         ShipEntity *thisEnt = nil;
    3892             :         GET_THIS_SHIP(thisEnt);
    3893             :         [thisEnt performScriptedAttackAI];
    3894             :         
    3895             :         OOJS_RETURN_VOID;
    3896             :         
    3897             :         OOJS_PROFILE_EXIT
    3898             : }
    3899             : 
    3900             : 
    3901           0 : static JSBool ShipPerformStop(JSContext *context, uintN argc, jsval *vp)
    3902             : {
    3903             :         OOJS_PROFILE_ENTER
    3904             :         
    3905             :         ShipEntity *thisEnt = nil;
    3906             :         GET_THIS_SHIP(thisEnt);
    3907             :         [thisEnt performStop];
    3908             :         
    3909             :         OOJS_RETURN_VOID;
    3910             :         
    3911             :         OOJS_PROFILE_EXIT
    3912             : }
    3913             : 
    3914             : 
    3915           0 : static JSBool ShipPerformTumble(JSContext *context, uintN argc, jsval *vp)
    3916             : {
    3917             :         OOJS_PROFILE_ENTER
    3918             :         
    3919             :         ShipEntity *thisEnt = nil;
    3920             :         GET_THIS_SHIP(thisEnt);
    3921             :         [thisEnt performTumble];
    3922             :         
    3923             :         OOJS_RETURN_VOID;
    3924             :         
    3925             :         OOJS_PROFILE_EXIT
    3926             : }
    3927             : 
    3928             : 
    3929           0 : static JSBool ShipRequestDockingInstructions(JSContext *context, uintN argc, jsval *vp)
    3930             : {
    3931             :         OOJS_PROFILE_ENTER
    3932             :         
    3933             :         ShipEntity *thisEnt = nil;
    3934             :         GET_THIS_SHIP(thisEnt);
    3935             :         [thisEnt requestDockingCoordinates];
    3936             :         
    3937             :         NSDictionary *dockingInstructions = [thisEnt dockingInstructions];
    3938             :         if (dockingInstructions != nil)
    3939             :         {
    3940             :                 OOJS_RETURN_OBJECT(dockingInstructions);
    3941             :         }
    3942             :         else
    3943             :         {
    3944             :                 OOJS_RETURN_NULL;
    3945             :         }
    3946             :         
    3947             :         OOJS_PROFILE_EXIT
    3948             : }
    3949             : 
    3950             : 
    3951           0 : static JSBool ShipRecallDockingInstructions(JSContext *context, uintN argc, jsval *vp)
    3952             : {
    3953             :         OOJS_PROFILE_ENTER
    3954             :         
    3955             :         ShipEntity *thisEnt = nil;
    3956             :         GET_THIS_SHIP(thisEnt);
    3957             :         [thisEnt recallDockingInstructions];
    3958             :         
    3959             :         NSDictionary *dockingInstructions = [thisEnt dockingInstructions];
    3960             :         if (dockingInstructions != nil)
    3961             :         {
    3962             :                 OOJS_RETURN_OBJECT(dockingInstructions);
    3963             :         }
    3964             :         else
    3965             :         {
    3966             :                 OOJS_RETURN_NULL;
    3967             :         }
    3968             :         
    3969             :         OOJS_PROFILE_EXIT
    3970             : }
    3971             : 
    3972             : 
    3973           0 : static JSBool ShipBroadcastDistressMessage(JSContext *context, uintN argc, jsval *vp)
    3974             : {
    3975             :         OOJS_PROFILE_ENTER
    3976             :         
    3977             :         ShipEntity *thisEnt = nil;
    3978             :         GET_THIS_SHIP(thisEnt);
    3979             :         [thisEnt broadcastDistressMessageWithDumping:NO];
    3980             :         
    3981             :         OOJS_RETURN_VOID;
    3982             :         
    3983             :         OOJS_PROFILE_EXIT
    3984             : }
    3985             : 
    3986             : 
    3987           0 : static JSBool ShipCheckCourseToDestination(JSContext *context, uintN argc, jsval *vp)
    3988             : {
    3989             :         OOJS_PROFILE_ENTER
    3990             :         
    3991             :         ShipEntity *thisEnt = nil;
    3992             :         GET_THIS_SHIP(thisEnt);
    3993             : 
    3994             :         Entity *hazard = [UNIVERSE hazardOnRouteFromEntity:thisEnt toDistance:[thisEnt desiredRange] fromPoint:[thisEnt destination]];
    3995             : 
    3996             :         OOJS_RETURN_OBJECT(hazard);
    3997             : 
    3998             :         OOJS_PROFILE_EXIT
    3999             : }
    4000             : 
    4001             : 
    4002           0 : static JSBool ShipGetSafeCourseToDestination(JSContext *context, uintN argc, jsval *vp)
    4003             : {
    4004             :         OOJS_PROFILE_ENTER
    4005             :         
    4006             :         ShipEntity *thisEnt = nil;
    4007             :         GET_THIS_SHIP(thisEnt);
    4008             : 
    4009             :         HPVector waypoint = [UNIVERSE getSafeVectorFromEntity:thisEnt toDistance:[thisEnt desiredRange] fromPoint:[thisEnt destination]];
    4010             : 
    4011             :         OOJS_RETURN_HPVECTOR(waypoint);
    4012             : 
    4013             :         OOJS_PROFILE_EXIT
    4014             : 
    4015             : }
    4016             : 
    4017             : 
    4018           0 : static JSBool ShipCheckScanner(JSContext *context, uintN argc, jsval *vp)
    4019             : {
    4020             :         OOJS_PROFILE_ENTER
    4021             :         
    4022             :         ShipEntity *thisEnt = nil;
    4023             :         JSBool  onlyCheckPowered = NO;
    4024             :         
    4025             :         GET_THIS_SHIP(thisEnt);
    4026             :         
    4027             :         if (argc > 0 && EXPECT_NOT(!JS_ValueToBoolean(context, OOJS_ARGV[0], &onlyCheckPowered)))
    4028             :         {
    4029             :                 OOJSReportBadArguments(context, @"Ship", @"checkScanner", argc, OOJS_ARGV, nil, @"boolean");
    4030             :                 return NO;
    4031             :         }
    4032             : 
    4033             :         if (onlyCheckPowered)
    4034             :         {
    4035             :                 [thisEnt checkScannerIgnoringUnpowered];
    4036             :         }
    4037             :         else
    4038             :         {
    4039             :                 [thisEnt checkScanner];
    4040             :         }
    4041             :         ShipEntity **scannedShips = [thisEnt scannedShips];
    4042             :         unsigned num = [thisEnt numberOfScannedShips];
    4043             :         NSMutableArray *scanResult = [NSMutableArray array];
    4044             :         for (unsigned i = 0; i < num ; i++)
    4045             :         {
    4046             :                 [scanResult addObject:scannedShips[i]];
    4047             :         }
    4048             :         OOJS_RETURN_OBJECT(scanResult);
    4049             : 
    4050             :         OOJS_PROFILE_EXIT
    4051             : }
    4052             : 
    4053             : 
    4054           0 : static JSBool ShipAdjustCargo(JSContext *context, uintN argc, jsval *vp)
    4055             : {
    4056             :         OOJS_PROFILE_ENTER
    4057             :         
    4058             :         ShipEntity *thisEnt = nil;
    4059             :         NSString *commodity = @"";
    4060             :         int32 adjustment = 0;
    4061             : 
    4062             :         GET_THIS_SHIP(thisEnt);
    4063             :         
    4064             :         if (argc < 2)
    4065             :         {
    4066             :                 OOJSReportBadArguments(context, @"Ship", @"adjustCargo", argc, OOJS_ARGV, nil, @"commodity, amount");
    4067             :                 return NO;
    4068             :         }
    4069             : 
    4070             :         commodity = OOStringFromJSValue(context, OOJS_ARGV[0]);
    4071             :         if (!JS_ValueToInt32(context, OOJS_ARGV[1], &adjustment))
    4072             :         {
    4073             :                 OOJSReportBadArguments(context, @"Ship", @"adjustCargo", argc, OOJS_ARGV, nil, @"commodity, amount");
    4074             :                 return NO;
    4075             :         }
    4076             : 
    4077             :         if ([thisEnt cargoType] != CARGO_NOT_CARGO || [thisEnt isPlayer])
    4078             :         {
    4079             :                 OOJSReportError(context, @"ship.adjustCargo may only be used on NPC cargo carriers");
    4080             :                 return NO;
    4081             :         }
    4082             : 
    4083             :         BOOL ok = YES;
    4084             : 
    4085             :         if (adjustment > 0)
    4086             :         {
    4087             :                 NSArray *cargo = [UNIVERSE getContainersOfCommodity:commodity :adjustment]; // non-reified templates
    4088             :                 ok = [thisEnt addCargo:cargo];
    4089             :         }
    4090             :         else if (adjustment < 0)
    4091             :         {
    4092             :                 OOCargoQuantity r = (OOCargoQuantity)(-adjustment);
    4093             :                 ok = [thisEnt removeCargo:commodity amount:r];
    4094             :         }
    4095             : 
    4096             : 
    4097             :         OOJS_RETURN_BOOL(ok);
    4098             : 
    4099             :         OOJS_PROFILE_EXIT
    4100             : }
    4101             : 
    4102             : 
    4103             : /* 0 = no significant damage or consumable loss, higher numbers mean some */
    4104           0 : static JSBool ShipDamageAssessment(JSContext *context, uintN argc, jsval *vp)
    4105             : {
    4106             :         OOJS_PROFILE_ENTER
    4107             :         
    4108             :         ShipEntity *thisEnt = nil;
    4109             :         int                     assessment = 0;
    4110             :         
    4111             :         GET_THIS_SHIP(thisEnt);
    4112             :         
    4113             :         // if could have missiles but doesn't, consumables low
    4114             :         if ([thisEnt missileCapacity] > 0 && [[thisEnt missilesList] count] == 0)
    4115             :         {
    4116             :                 assessment++;
    4117             :         }
    4118             :         // if has injectors but fuel is low, consumables low
    4119             :         // if no injectors, not a problem
    4120             :         if ([thisEnt hasFuelInjection] && [thisEnt fuel] < 35)
    4121             :         {
    4122             :                 assessment++;
    4123             :         }
    4124             : 
    4125             :         /* TODO: when NPC equipment can be damaged in combat, assess this
    4126             :          * here */
    4127             : 
    4128             :         OOJS_RETURN_INT(assessment);
    4129             : 
    4130             :         OOJS_PROFILE_EXIT
    4131             : }
    4132             : 
    4133             : 
    4134             : 
    4135           0 : static JSBool ShipThreatAssessment(JSContext *context, uintN argc, jsval *vp)
    4136             : {
    4137             :         OOJS_PROFILE_ENTER
    4138             :         
    4139             :         ShipEntity *thisEnt = nil;
    4140             :         JSBool  fullCheck = NO;
    4141             :         
    4142             :         GET_THIS_SHIP(thisEnt);
    4143             :         
    4144             :         if (argc > 0 && EXPECT_NOT(!JS_ValueToBoolean(context, OOJS_ARGV[0], &fullCheck)))
    4145             :         {
    4146             :                 OOJSReportBadArguments(context, @"Ship", @"threatAssessment", argc, OOJS_ARGV, nil, @"boolean");
    4147             :                 return NO;
    4148             :         }
    4149             :         // start with 2.5 per ship
    4150             :         double assessment = 2.5;
    4151             :         // +/- 0.1 for speed, larger subtraction for very slow ships
    4152             :         GLfloat maxspeed = [thisEnt maxFlightSpeed];
    4153             :         assessment += (maxspeed-300)/1000;
    4154             :         if (maxspeed < 200)
    4155             :         {
    4156             :                 assessment += (maxspeed-200)/500;
    4157             :         }
    4158             :         
    4159             :         /* FIXME: at the moment this means NPCs can detect other NPCs shield
    4160             :          * boosters, since they're implemented as extra energy */
    4161             :         assessment += ([thisEnt maxEnergy]-200)/1000; 
    4162             :         
    4163             :         // add on some for missiles. Mostly ignore 3rd and subsequent
    4164             :         // missiles: either they can be ECMd or the first two are already
    4165             :         // too many.
    4166             :         if ([thisEnt missileCapacity] > 2)
    4167             :         {
    4168             :                 assessment += 0.5;
    4169             :         }
    4170             :         else
    4171             :         {
    4172             :                 assessment += ((double)[thisEnt missileCapacity])/5.0;
    4173             :         }
    4174             : 
    4175             :         /* Turret count is public knowledge */
    4176             :         /* TODO: consider making ship combat behaviour try to
    4177             :          * stay at long range from enemies with turrets. Then
    4178             :          * we could perhaps reduce this bonus a bit. */
    4179             :         assessment += [thisEnt turretCount];
    4180             : 
    4181             :         if (fullCheck)
    4182             :         {
    4183             :                 // consider pilot skill
    4184             :                 if ([thisEnt isPlayer])
    4185             :                 {
    4186             :                         double score = (double)[PLAYER score];
    4187             :                         if (score > 6400) 
    4188             :                         {
    4189             :                                 score = 6400;
    4190             :                         }
    4191             :                         assessment += pow(score,0.33)/10;
    4192             :                         // 0 - 1.8
    4193             :                 }
    4194             :                 else
    4195             :                 {
    4196             :                         assessment += [thisEnt accuracy]/5;
    4197             :                 }
    4198             : 
    4199             :                 // check lasers
    4200             :                 OOWeaponType wt = [thisEnt weaponTypeIDForFacing:WEAPON_FACING_FORWARD strict:NO];
    4201             :                 /* Not affected by multiple mounts here: they're either just a
    4202             :                  * split of the power, or only more dangerous until they
    4203             :                  * overheat */
    4204             :                 assessment += ShipThreatAssessmentWeapon(wt);
    4205             :                 if (isWeaponNone(wt))
    4206             :                 {
    4207             :                         assessment -= 1.5; // further penalty for ships with no forward laser
    4208             :                 }
    4209             : 
    4210             :                 wt = [thisEnt weaponTypeIDForFacing:WEAPON_FACING_AFT strict:NO];
    4211             :                 if (!isWeaponNone(wt))
    4212             :                 {
    4213             :                         assessment += 1 + ShipThreatAssessmentWeapon(wt);
    4214             :                 }
    4215             :                 // port and starboard weapons less important
    4216             :                 wt = [thisEnt weaponTypeIDForFacing:WEAPON_FACING_PORT strict:NO];
    4217             :                 if (!isWeaponNone(wt))
    4218             :                 {
    4219             :                         assessment += 0.2 + ShipThreatAssessmentWeapon(wt)/5.0;
    4220             :                 }
    4221             :                 wt = [thisEnt weaponTypeIDForFacing:WEAPON_FACING_STARBOARD strict:NO];
    4222             :                 if (!isWeaponNone(wt))
    4223             :                 {
    4224             :                         assessment += 0.2 + ShipThreatAssessmentWeapon(wt)/5.0;
    4225             :                 }
    4226             : 
    4227             :                 // combat-related secondary equipment
    4228             :                 if ([thisEnt hasECM])
    4229             :                 {
    4230             :                         assessment += 0.5;
    4231             :                 }
    4232             :                 if ([thisEnt hasFuelInjection])
    4233             :                 {
    4234             :                         assessment += 0.5;
    4235             :                 }
    4236             : 
    4237             :         }
    4238             :         else
    4239             :         {
    4240             :                 // consider thargoids dangerous
    4241             :                 if ([thisEnt isThargoid])
    4242             :                 {
    4243             :                         assessment *= 1.5;
    4244             :                         if ([thisEnt hasRole:@"thargoid-mothership"])
    4245             :                         {
    4246             :                                 assessment += 5;
    4247             :                         }
    4248             :                 }
    4249             :                 else
    4250             :                 {
    4251             :                         // consider that armed ships might have a trick or two
    4252             :                         if ([thisEnt weaponFacings] == 1)
    4253             :                         {
    4254             :                                 assessment += 0.25;
    4255             :                         }
    4256             :                         else
    4257             :                         {
    4258             :                                 // and more than one trick if they can mount multiple lasers
    4259             :                                 assessment += 0.5;
    4260             :                         }
    4261             :                 }
    4262             :         }
    4263             : 
    4264             :         // mostly ignore fleeing ships as threats
    4265             :         if ([thisEnt behaviour] == BEHAVIOUR_FLEE_TARGET || [thisEnt behaviour] == BEHAVIOUR_FLEE_EVASIVE_ACTION)
    4266             :         {
    4267             :                 assessment *= 0.2;
    4268             :         }
    4269             :         else if ([thisEnt isPlayer] && [(PlayerEntity*)thisEnt fleeingStatus] >= PLAYER_FLEEING_CARGO)
    4270             :         {
    4271             :                 assessment *= 0.2;
    4272             :         }
    4273             :         
    4274             :         // don't go too low.
    4275             :         if (assessment < 0.1)
    4276             :         {
    4277             :                 assessment = 0.1;
    4278             :         }
    4279             : 
    4280             :         OOJS_RETURN_DOUBLE(assessment);
    4281             : 
    4282             :         OOJS_PROFILE_EXIT
    4283             : }
    4284             : 
    4285           0 : static double ShipThreatAssessmentWeapon(OOWeaponType wt)
    4286             : {
    4287             :         if (wt == nil)
    4288             :         {
    4289             :                 return -1.0;
    4290             :         }
    4291             :         return [wt weaponThreatAssessment];
    4292             : }
    4293             : 
    4294             : 
    4295             : /** Static methods */
    4296             : 
    4297           1 : static JSBool ShipStaticKeys(JSContext *context, uintN argc, jsval *vp)
    4298             : {
    4299             :         OOJS_NATIVE_ENTER(context);
    4300             :         OOShipRegistry                  *registry = [OOShipRegistry sharedRegistry];
    4301             : 
    4302             :         NSArray *keys = [registry shipKeys];
    4303             :         OOJS_RETURN_OBJECT(keys);               
    4304             : 
    4305             :         OOJS_NATIVE_EXIT
    4306             : }
    4307             : 
    4308           0 : static JSBool ShipStaticKeysForRole(JSContext *context, uintN argc, jsval *vp)
    4309             : {
    4310             :         OOJS_NATIVE_ENTER(context);
    4311             :         OOShipRegistry                  *registry = [OOShipRegistry sharedRegistry];
    4312             : 
    4313             :         if (argc > 0)
    4314             :         {
    4315             :                 NSString *role = OOStringFromJSValue(context, OOJS_ARGV[0]);
    4316             :                 NSArray *keys = [registry shipKeysWithRole:role];
    4317             :                 OOJS_RETURN_OBJECT(keys);               
    4318             :         }
    4319             :         else
    4320             :         {
    4321             :                 OOJSReportBadArguments(context, @"Ship", @"shipKeysForRole", MIN(argc, 1U), OOJS_ARGV, nil, @"ship role");
    4322             :                 return NO;
    4323             :         }
    4324             : 
    4325             :         OOJS_NATIVE_EXIT
    4326             : }
    4327             : 
    4328             : 
    4329           0 : static JSBool ShipStaticRoleIsInCategory(JSContext *context, uintN argc, jsval *vp)
    4330             : {
    4331             :         OOJS_NATIVE_ENTER(context);
    4332             : 
    4333             :         if (argc > 1)
    4334             :         {
    4335             :                 NSString *role = OOStringFromJSValue(context, OOJS_ARGV[0]);
    4336             :                 NSString *category = OOStringFromJSValue(context, OOJS_ARGV[1]);
    4337             : 
    4338             :                 OOJS_RETURN_BOOL([UNIVERSE role:role isInCategory:category]);           
    4339             :         }
    4340             :         else
    4341             :         {
    4342             :                 OOJSReportBadArguments(context, @"Ship", @"roleIsInCategory", MIN(argc, 2U), OOJS_ARGV, nil, @"role, category");
    4343             :                 return NO;
    4344             :         }
    4345             : 
    4346             :         OOJS_NATIVE_EXIT
    4347             : }
    4348             : 
    4349             : 
    4350           0 : static JSBool ShipStaticRoles(JSContext *context, uintN argc, jsval *vp)
    4351             : {
    4352             :         OOJS_NATIVE_ENTER(context);
    4353             :         OOShipRegistry                  *registry = [OOShipRegistry sharedRegistry];
    4354             : 
    4355             :         NSArray *keys = [registry shipRoles];
    4356             :         OOJS_RETURN_OBJECT(keys);               
    4357             : 
    4358             :         OOJS_NATIVE_EXIT
    4359             : }
    4360             : 
    4361             : 
    4362           0 : static JSBool ShipStaticShipDataForKey(JSContext *context, uintN argc, jsval *vp)
    4363             : {
    4364             :         OOJS_NATIVE_ENTER(context);
    4365             :         OOShipRegistry                  *registry = [OOShipRegistry sharedRegistry];
    4366             : 
    4367             :         if (argc > 0)
    4368             :         {
    4369             :                 NSString *key = OOStringFromJSValue(context, OOJS_ARGV[0]);
    4370             :                 NSDictionary *keys = [registry shipInfoForKey:key];
    4371             :                 OOJS_RETURN_OBJECT(keys);               
    4372             :         }
    4373             :         else
    4374             :         {
    4375             :                 OOJSReportBadArguments(context, @"Ship", @"shipDataForKey", MIN(argc, 1U), OOJS_ARGV, nil, @"key");
    4376             :                 return NO;
    4377             :         }
    4378             :         OOJS_NATIVE_EXIT
    4379             : }
    4380             : 
    4381             : 
    4382           0 : static JSBool ShipStaticSetShipDataForKey(JSContext *context, uintN argc, jsval *vp)
    4383             : {
    4384             :         OOJS_NATIVE_ENTER(context);
    4385             :         OOShipRegistry                  *registry = [OOShipRegistry sharedRegistry];
    4386             : 
    4387             :         if (argc >= 2)
    4388             :         {
    4389             :                 NSString *key = OOStringFromJSValue(context, OOJS_ARGV[0]);
    4390             :                 NSDictionary *newShipData = OOJSNativeObjectFromJSObject(context, JSVAL_TO_OBJECT(OOJS_ARGV[1]));
    4391             :                 [registry setShipInfoForKey:key with:newShipData];
    4392             :                 OOJS_RETURN_BOOL(YES);
    4393             :         }
    4394             :         else
    4395             :         {
    4396             :                 OOJSReportBadArguments(context, @"Ship", @"setShipInfoForKey", MIN(argc, 2U), OOJS_ARGV, nil, @"key shipdata");
    4397             :                 return NO;
    4398             :         }
    4399             :         OOJS_NATIVE_EXIT
    4400             : }

Generated by: LCOV version 1.14