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], ¶ms)))
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 : }
|