Line data Source code
1 0 : /*
2 :
3 : OOJSEquipmentInfo.m
4 :
5 :
6 : Oolite
7 : Copyright (C) 2004-2013 Giles C Williams and contributors
8 :
9 : This program is free software; you can redistribute it and/or
10 : modify it under the terms of the GNU General Public License
11 : as published by the Free Software Foundation; either version 2
12 : of the License, or (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program; if not, write to the Free Software
21 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 : MA 02110-1301, USA.
23 :
24 : */
25 :
26 : #import "OOJSEquipmentInfo.h"
27 : #import "OOJavaScriptEngine.h"
28 : #import "OOEquipmentType.h"
29 : #import "OOJSPlayer.h"
30 : #import "OODebugStandards.h"
31 :
32 0 : static JSObject *sEquipmentInfoPrototype;
33 :
34 :
35 : static JSBool EquipmentInfoGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
36 : static JSBool EquipmentInfoSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
37 :
38 : static JSBool EquipmentInfoGetAllEqipment(JSContext *context, JSObject *this, jsid propID, jsval *value);
39 :
40 :
41 : // Methods
42 : static JSBool EquipmentInfoStaticInfoForKey(JSContext *context, uintN argc, jsval *vp);
43 :
44 :
45 0 : enum
46 : {
47 : // Property IDs
48 : kEquipmentInfo_calculatedPrice,
49 : kEquipmentInfo_canBeDamaged,
50 : kEquipmentInfo_canCarryMultiple,
51 : kEquipmentInfo_damageProbability,
52 : kEquipmentInfo_description,
53 : kEquipmentInfo_displayColor,
54 : kEquipmentInfo_effectiveTechLevel,
55 : kEquipmentInfo_equipmentKey,
56 : kEquipmentInfo_fastAffinityDefensive,
57 : kEquipmentInfo_fastAffinityOffensive,
58 : kEquipmentInfo_defaultActivateKey,
59 : kEquipmentInfo_defaultModeKey,
60 : kEquipmentInfo_incompatibleEquipment,
61 : kEquipmentInfo_isAvailableToAll,
62 : kEquipmentInfo_isAvailableToNPCs,
63 : kEquipmentInfo_isAvailableToPlayer,
64 : kEquipmentInfo_isExternalStore, // is missile or mine
65 : kEquipmentInfo_isPortableBetweenShips,
66 : kEquipmentInfo_isVisible,
67 : kEquipmentInfo_name,
68 : kEquipmentInfo_price,
69 : kEquipmentInfo_provides,
70 : kEquipmentInfo_requiredCargoSpace,
71 : kEquipmentInfo_requiresAnyEquipment,
72 : kEquipmentInfo_requiresCleanLegalRecord,
73 : kEquipmentInfo_requiresEmptyPylon,
74 : kEquipmentInfo_requiresEquipment,
75 : kEquipmentInfo_requiresFreePassengerBerth,
76 : kEquipmentInfo_requiresFullFuel,
77 : kEquipmentInfo_requiresMountedPylon,
78 : kEquipmentInfo_requiresNonCleanLegalRecord,
79 : kEquipmentInfo_requiresNonFullFuel,
80 : kEquipmentInfo_scriptInfo, // arbitrary data for scripts, dictionary, read-only
81 : kEquipmentInfo_scriptName,
82 : kEquipmentInfo_techLevel,
83 : kEquipmentInfo_weaponInfo
84 : };
85 :
86 :
87 0 : static JSPropertySpec sEquipmentInfoProperties[] =
88 : {
89 : // JS name ID flags
90 : { "calculatedPrice", kEquipmentInfo_calculatedPrice, OOJS_PROP_READONLY_CB },
91 : { "canBeDamaged", kEquipmentInfo_canBeDamaged, OOJS_PROP_READONLY_CB },
92 : { "canCarryMultiple", kEquipmentInfo_canCarryMultiple, OOJS_PROP_READONLY_CB },
93 : { "damageProbability", kEquipmentInfo_damageProbability, OOJS_PROP_READONLY_CB },
94 : { "description", kEquipmentInfo_description, OOJS_PROP_READONLY_CB },
95 : { "displayColor", kEquipmentInfo_displayColor, OOJS_PROP_READWRITE_CB },
96 : { "effectiveTechLevel", kEquipmentInfo_effectiveTechLevel, OOJS_PROP_READWRITE_CB },
97 : { "equipmentKey", kEquipmentInfo_equipmentKey, OOJS_PROP_READONLY_CB },
98 : { "fastAffinityDefensive", kEquipmentInfo_fastAffinityDefensive, OOJS_PROP_READONLY_CB },
99 : { "fastAffinityOffensive", kEquipmentInfo_fastAffinityOffensive, OOJS_PROP_READONLY_CB },
100 : { "defaultActivateKey", kEquipmentInfo_defaultActivateKey, OOJS_PROP_READONLY_CB },
101 : { "defaultModeKey", kEquipmentInfo_defaultModeKey, OOJS_PROP_READONLY_CB },
102 : { "incompatibleEquipment", kEquipmentInfo_incompatibleEquipment, OOJS_PROP_READONLY_CB },
103 : { "isAvailableToAll", kEquipmentInfo_isAvailableToAll, OOJS_PROP_READONLY_CB },
104 : { "isAvailableToNPCs", kEquipmentInfo_isAvailableToNPCs, OOJS_PROP_READONLY_CB },
105 : { "isAvailableToPlayer", kEquipmentInfo_isAvailableToPlayer, OOJS_PROP_READONLY_CB },
106 : { "isExternalStore", kEquipmentInfo_isExternalStore, OOJS_PROP_READONLY_CB },
107 : { "isPortableBetweenShips", kEquipmentInfo_isPortableBetweenShips, OOJS_PROP_READONLY_CB },
108 : { "isVisible", kEquipmentInfo_isVisible, OOJS_PROP_READONLY_CB },
109 : { "name", kEquipmentInfo_name, OOJS_PROP_READONLY_CB },
110 : { "price", kEquipmentInfo_price, OOJS_PROP_READONLY_CB },
111 : { "provides", kEquipmentInfo_provides, OOJS_PROP_READONLY_CB },
112 : { "requiredCargoSpace", kEquipmentInfo_requiredCargoSpace, OOJS_PROP_READONLY_CB },
113 : { "requiresAnyEquipment", kEquipmentInfo_requiresAnyEquipment, OOJS_PROP_READONLY_CB },
114 : { "requiresCleanLegalRecord", kEquipmentInfo_requiresCleanLegalRecord, OOJS_PROP_READONLY_CB },
115 : { "requiresEmptyPylon", kEquipmentInfo_requiresEmptyPylon, OOJS_PROP_READONLY_CB },
116 : { "requiresEquipment", kEquipmentInfo_requiresEquipment, OOJS_PROP_READONLY_CB },
117 : { "requiresFreePassengerBerth", kEquipmentInfo_requiresFreePassengerBerth, OOJS_PROP_READONLY_CB },
118 : { "requiresFullFuel", kEquipmentInfo_requiresFullFuel, OOJS_PROP_READONLY_CB },
119 : { "requiresMountedPylon", kEquipmentInfo_requiresMountedPylon, OOJS_PROP_READONLY_CB },
120 : { "requiresNonCleanLegalRecord", kEquipmentInfo_requiresNonCleanLegalRecord, OOJS_PROP_READONLY_CB },
121 : { "requiresNonFullFuel", kEquipmentInfo_requiresNonFullFuel, OOJS_PROP_READONLY_CB },
122 : { "scriptInfo", kEquipmentInfo_scriptInfo, OOJS_PROP_READONLY_CB },
123 : { "scriptName", kEquipmentInfo_scriptName, OOJS_PROP_READONLY_CB },
124 : { "techLevel", kEquipmentInfo_techLevel, OOJS_PROP_READONLY_CB },
125 : { "weaponInfo", kEquipmentInfo_weaponInfo, OOJS_PROP_READONLY_CB },
126 : { 0 }
127 : };
128 :
129 :
130 0 : static JSPropertySpec sEquipmentInfoStaticProperties[] =
131 : {
132 : { "allEquipment", 0, OOJS_PROP_READONLY_CB, EquipmentInfoGetAllEqipment },
133 : { 0 }
134 : };
135 :
136 :
137 0 : static JSFunctionSpec sEquipmentInfoMethods[] =
138 : {
139 : // JS name Function min args
140 : { "toString", OOJSObjectWrapperToString, 0 },
141 : { 0 }
142 : };
143 :
144 :
145 0 : static JSFunctionSpec sEquipmentInfoStaticMethods[] =
146 : {
147 : // JS name Function min args
148 : { "infoForKey", EquipmentInfoStaticInfoForKey, 0 },
149 : { 0 }
150 : };
151 :
152 :
153 0 : static JSClass sEquipmentInfoClass =
154 : {
155 : "EquipmentInfo",
156 : JSCLASS_HAS_PRIVATE,
157 :
158 : JS_PropertyStub, // addProperty
159 : JS_PropertyStub, // delProperty
160 : EquipmentInfoGetProperty, // getProperty
161 : EquipmentInfoSetProperty, // setProperty
162 : JS_EnumerateStub, // enumerate
163 : JS_ResolveStub, // resolve
164 : JS_ConvertStub, // convert
165 : OOJSObjectWrapperFinalize, // finalize
166 : JSCLASS_NO_OPTIONAL_MEMBERS
167 : };
168 :
169 :
170 0 : DEFINE_JS_OBJECT_GETTER(JSEquipmentInfoGetEquipmentType, &sEquipmentInfoClass, sEquipmentInfoPrototype, OOEquipmentType);
171 :
172 :
173 : // *** Public ***
174 :
175 0 : void InitOOJSEquipmentInfo(JSContext *context, JSObject *global)
176 : {
177 : sEquipmentInfoPrototype = JS_InitClass(context, global, NULL, &sEquipmentInfoClass, OOJSUnconstructableConstruct, 0, sEquipmentInfoProperties, sEquipmentInfoMethods, sEquipmentInfoStaticProperties, sEquipmentInfoStaticMethods);
178 :
179 : OOJSRegisterObjectConverter(&sEquipmentInfoClass, OOJSBasicPrivateObjectConverter);
180 : }
181 :
182 :
183 0 : OOEquipmentType *JSValueToEquipmentType(JSContext *context, jsval value)
184 : {
185 : OOJS_PROFILE_ENTER
186 :
187 : if (JSVAL_IS_OBJECT(value))
188 : {
189 : JSObject *object = JSVAL_TO_OBJECT(value);
190 : if (JS_InstanceOf(context, JSVAL_TO_OBJECT(value), &sEquipmentInfoClass, NULL))
191 : {
192 : return (OOEquipmentType *)JS_GetPrivate(context, object);
193 : }
194 : }
195 :
196 : NSString *string = OOStringFromJSValue(context, value);
197 : if (string != nil) return [OOEquipmentType equipmentTypeWithIdentifier:string];
198 : return nil;
199 :
200 : OOJS_PROFILE_EXIT
201 : }
202 :
203 :
204 0 : NSString *JSValueToEquipmentKey(JSContext *context, jsval value)
205 : {
206 : return [JSValueToEquipmentType(context, value) identifier];
207 : }
208 :
209 :
210 0 : NSString *JSValueToEquipmentKeyRelaxed(JSContext *context, jsval value, BOOL *outExists)
211 : {
212 : OOJS_PROFILE_ENTER
213 :
214 : NSString *result = nil;
215 : BOOL exists = NO;
216 : id objValue = OOJSNativeObjectFromJSValue(context, value);
217 :
218 : if ([objValue isKindOfClass:[OOEquipmentType class]])
219 : {
220 : result = [objValue identifier];
221 : exists = YES;
222 : }
223 : else if ([objValue isKindOfClass:[NSString class]])
224 : {
225 : /* To enforce deliberate backwards incompatibility, reject strings
226 : ending with _DAMAGED unless someone actually named an equip that
227 : way.
228 : */
229 : exists = [OOEquipmentType equipmentTypeWithIdentifier:objValue] != nil;
230 : if (exists || ![objValue hasSuffix:@"_DAMAGED"])
231 : {
232 : result = objValue;
233 : }
234 : }
235 :
236 : if (outExists != NULL) *outExists = exists;
237 : return result;
238 :
239 : OOJS_PROFILE_EXIT
240 : }
241 :
242 :
243 : // *** Implementation stuff ***
244 :
245 0 : static JSBool EquipmentInfoGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
246 : {
247 : if (!JSID_IS_INT(propID)) return YES;
248 :
249 : OOJS_NATIVE_ENTER(context)
250 :
251 : OOEquipmentType *eqType = nil;
252 : id result = nil;
253 :
254 : if (EXPECT_NOT(!JSEquipmentInfoGetEquipmentType(context, this, &eqType))) return NO;
255 :
256 : switch (JSID_TO_INT(propID))
257 : {
258 : case kEquipmentInfo_equipmentKey:
259 : result = [eqType identifier];
260 : break;
261 :
262 : case kEquipmentInfo_name:
263 : result = [eqType name];
264 : break;
265 :
266 : case kEquipmentInfo_calculatedPrice:
267 : if ([[eqType identifier] isEqual:@"EQ_FUEL"])
268 : {
269 : return JS_NewNumberValue(context, (PLAYER_MAX_FUEL - [OOPlayerForScripting() fuel]) * [eqType price] * [OOPlayerForScripting() fuelChargeRate], value);
270 : }
271 : else if ([[eqType identifier] isEqual:@"EQ_RENOVATION"])
272 : {
273 : return JS_NewNumberValue(context, [OOPlayerForScripting() renovationCosts], value);
274 : }
275 : else
276 : {
277 : return JS_NewNumberValue(context, [OOPlayerForScripting() adjustPriceByScriptForEqKey:[eqType identifier] withCurrent:[eqType price]], value);
278 : }
279 : case kEquipmentInfo_canCarryMultiple:
280 : *value = OOJSValueFromBOOL([eqType canCarryMultiple]);
281 : return YES;
282 :
283 : case kEquipmentInfo_canBeDamaged:
284 : *value = OOJSValueFromBOOL([eqType canBeDamaged]);
285 : return YES;
286 :
287 : case kEquipmentInfo_description:
288 : result = [eqType descriptiveText];
289 : break;
290 :
291 : case kEquipmentInfo_damageProbability:
292 : return JS_NewNumberValue(context, [eqType damageProbability], value);
293 :
294 : case kEquipmentInfo_displayColor:
295 : result = [[eqType displayColor] normalizedArray];
296 : break;
297 :
298 : case kEquipmentInfo_fastAffinityDefensive:
299 : *value = OOJSValueFromBOOL([eqType fastAffinityDefensive]);
300 : return YES;
301 :
302 : case kEquipmentInfo_fastAffinityOffensive:
303 : *value = OOJSValueFromBOOL([eqType fastAffinityOffensive]);
304 : return YES;
305 :
306 : case kEquipmentInfo_defaultActivateKey:
307 : result = [eqType defaultActivateKey];
308 : break;
309 :
310 : case kEquipmentInfo_defaultModeKey:
311 : result = [eqType defaultModeKey];
312 : break;
313 :
314 : case kEquipmentInfo_techLevel:
315 : *value = INT_TO_JSVAL((int32_t)[eqType techLevel]);
316 : return YES;
317 :
318 : case kEquipmentInfo_effectiveTechLevel:
319 : *value = INT_TO_JSVAL((int32_t)[eqType effectiveTechLevel]);
320 : return YES;
321 :
322 : case kEquipmentInfo_price:
323 : return JS_NewNumberValue(context, [eqType price], value);
324 :
325 : case kEquipmentInfo_provides:
326 : result = [eqType providesForScripting];
327 : break;
328 :
329 : case kEquipmentInfo_isAvailableToAll:
330 : *value = OOJSValueFromBOOL([eqType isAvailableToAll]);
331 : return YES;
332 :
333 : case kEquipmentInfo_isAvailableToNPCs:
334 : *value = OOJSValueFromBOOL([eqType isAvailableToNPCs]);
335 : return YES;
336 :
337 : case kEquipmentInfo_isAvailableToPlayer:
338 : *value = OOJSValueFromBOOL([eqType isAvailableToPlayer]);
339 : return YES;
340 :
341 : case kEquipmentInfo_requiresEmptyPylon:
342 : *value = OOJSValueFromBOOL([eqType requiresEmptyPylon]);
343 : return YES;
344 :
345 : case kEquipmentInfo_requiresMountedPylon:
346 : *value = OOJSValueFromBOOL([eqType requiresMountedPylon]);
347 : return YES;
348 :
349 : case kEquipmentInfo_requiresCleanLegalRecord:
350 : *value = OOJSValueFromBOOL([eqType requiresCleanLegalRecord]);
351 : return YES;
352 :
353 : case kEquipmentInfo_requiresNonCleanLegalRecord:
354 : *value = OOJSValueFromBOOL([eqType requiresNonCleanLegalRecord]);
355 : return YES;
356 :
357 : case kEquipmentInfo_requiresFreePassengerBerth:
358 : *value = OOJSValueFromBOOL([eqType requiresFreePassengerBerth]);
359 : return YES;
360 :
361 : case kEquipmentInfo_requiresFullFuel:
362 : *value = OOJSValueFromBOOL([eqType requiresFullFuel]);
363 : return YES;
364 :
365 : case kEquipmentInfo_requiresNonFullFuel:
366 : *value = OOJSValueFromBOOL([eqType requiresNonFullFuel]);
367 : return YES;
368 :
369 : case kEquipmentInfo_isExternalStore:
370 : *value = OOJSValueFromBOOL([eqType isMissileOrMine]);
371 : return YES;
372 :
373 : case kEquipmentInfo_isPortableBetweenShips:
374 : *value = OOJSValueFromBOOL([eqType isPortableBetweenShips]);
375 : return YES;
376 :
377 : case kEquipmentInfo_isVisible:
378 : *value = OOJSValueFromBOOL([eqType isVisible]);
379 : return YES;
380 :
381 : case kEquipmentInfo_requiredCargoSpace:
382 : *value = INT_TO_JSVAL((int32_t)[eqType requiredCargoSpace]);
383 : return YES;
384 :
385 : case kEquipmentInfo_requiresEquipment:
386 : result = [[eqType requiresEquipment] allObjects];
387 : break;
388 :
389 : case kEquipmentInfo_requiresAnyEquipment:
390 : result = [[eqType requiresAnyEquipment] allObjects];
391 : break;
392 :
393 : case kEquipmentInfo_incompatibleEquipment:
394 : result = [[eqType incompatibleEquipment] allObjects];
395 : break;
396 :
397 : case kEquipmentInfo_scriptInfo:
398 : result = [eqType scriptInfo];
399 : if (result == nil) result = [NSDictionary dictionary]; // empty rather than null
400 : break;
401 :
402 : case kEquipmentInfo_scriptName:
403 : result = [eqType scriptName];
404 : if (result == nil) result = @"";
405 : break;
406 :
407 : case kEquipmentInfo_weaponInfo:
408 : result = [eqType weaponInfo];
409 : if (result == nil) result = [NSDictionary dictionary]; // empty rather than null
410 : break;
411 :
412 : default:
413 : OOJSReportBadPropertySelector(context, this, propID, sEquipmentInfoProperties);
414 : return NO;
415 : }
416 :
417 : *value = OOJSValueFromNativeObject(context, result);
418 : return YES;
419 :
420 : OOJS_NATIVE_EXIT
421 : }
422 :
423 :
424 0 : static JSBool EquipmentInfoSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
425 : {
426 : if (!JSID_IS_INT(propID)) return YES;
427 :
428 : OOJS_NATIVE_ENTER(context)
429 :
430 : OOEquipmentType *eqType = nil;
431 : int32 iValue;
432 : OOColor *colorForScript = nil;
433 :
434 : if (EXPECT_NOT(!JSEquipmentInfoGetEquipmentType(context, this, &eqType))) return NO;
435 :
436 : switch (JSID_TO_INT(propID))
437 : {
438 : case kEquipmentInfo_displayColor:
439 : colorForScript = [OOColor colorWithDescription:OOJSNativeObjectFromJSValue(context, *value)];
440 : if (colorForScript != nil || JSVAL_IS_NULL(*value))
441 : {
442 : [eqType setDisplayColor:colorForScript];
443 : return YES;
444 : }
445 : break;
446 : case kEquipmentInfo_effectiveTechLevel:
447 : OOStandardsDeprecated([NSString stringWithFormat:@"TL99 for variable tech level is deprecated for %@",[eqType identifier]]);
448 : if (!OOEnforceStandards() && [eqType techLevel] == kOOVariableTechLevel)
449 : {
450 : if (JSVAL_IS_NULL(*value))
451 : {
452 : // reset mission variable
453 : [OOPlayerForScripting() setMissionVariable:nil
454 : forKey:[@"mission_TL_FOR_" stringByAppendingString:[eqType identifier]]];
455 : return YES;
456 : }
457 : if (JS_ValueToInt32(context, *value, &iValue))
458 : {
459 : if (iValue < 0) iValue = 0;
460 : if (15 < iValue && iValue != kOOVariableTechLevel) iValue = 15;
461 : [OOPlayerForScripting() setMissionVariable:[NSString stringWithFormat:@"%u", iValue]
462 : forKey:[@"mission_TL_FOR_" stringByAppendingString:[eqType identifier]]];
463 : return YES;
464 : }
465 : }
466 : else
467 : {
468 : OOJSReportWarning(context, @"Cannot modify effective tech level for %@, because its base tech level is not 99.", [eqType identifier]);
469 : return YES;
470 : }
471 : break;
472 :
473 : default:
474 : OOJSReportBadPropertySelector(context, this, propID, sEquipmentInfoProperties);
475 : return NO;
476 : }
477 :
478 : OOJSReportBadPropertyValue(context, this, propID, sEquipmentInfoProperties, *value);
479 : return NO;
480 :
481 : OOJS_NATIVE_EXIT
482 : }
483 :
484 :
485 0 : static JSBool EquipmentInfoGetAllEqipment(JSContext *context, JSObject *this, jsid propID, jsval *value)
486 : {
487 : OOJS_NATIVE_ENTER(context)
488 :
489 : *value = OOJSValueFromNativeObject(context, [OOEquipmentType allEquipmentTypes]);
490 : return YES;
491 :
492 : OOJS_NATIVE_EXIT
493 : }
494 :
495 :
496 : @implementation OOEquipmentType (OOJavaScriptExtensions)
497 :
498 0 : - (jsval) oo_jsValueInContext:(JSContext *)context
499 : {
500 : if (_jsSelf == NULL)
501 : {
502 : _jsSelf = JS_NewObject(context, &sEquipmentInfoClass, sEquipmentInfoPrototype, NULL);
503 : if (_jsSelf != NULL)
504 : {
505 : if (!JS_SetPrivate(context, _jsSelf, [self retain])) _jsSelf = NULL;
506 : }
507 : }
508 :
509 : return OBJECT_TO_JSVAL(_jsSelf);
510 : }
511 :
512 :
513 0 : - (NSString *) oo_jsClassName
514 : {
515 : return @"EquipmentInfo";
516 : }
517 :
518 :
519 0 : - (void) oo_clearJSSelf:(JSObject *)selfVal
520 : {
521 : if (_jsSelf == selfVal) _jsSelf = NULL;
522 : }
523 :
524 : @end
525 :
526 :
527 : // *** Static methods ***
528 :
529 : // infoForKey(key : String): EquipmentInfo
530 0 : static JSBool EquipmentInfoStaticInfoForKey(JSContext *context, uintN argc, jsval *vp)
531 : {
532 : OOJS_NATIVE_ENTER(context)
533 :
534 : NSString *key = nil;
535 :
536 : if (argc > 0) key = OOStringFromJSValue(context, OOJS_ARGV[0]);
537 : if (key == nil)
538 : {
539 : OOJSReportBadArguments(context, @"EquipmentInfo", @"infoForKey", MIN(argc, 1U), OOJS_ARGV, nil, @"string");
540 : return NO;
541 : }
542 :
543 : OOJS_RETURN_OBJECT([OOEquipmentType equipmentTypeWithIdentifier:key]);
544 :
545 : OOJS_NATIVE_EXIT
546 : }
|