Line data Source code
1 0 : /*
2 :
3 : OOJSEntity.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 "OOJSEntity.h"
26 : #import "OOJSVector.h"
27 : #import "OOJSQuaternion.h"
28 : #import "OOJavaScriptEngine.h"
29 : #import "OOConstToJSString.h"
30 : #import "EntityOOJavaScriptExtensions.h"
31 : #import "OOJSCall.h"
32 :
33 : #import "OOJSPlayer.h"
34 : #import "PlayerEntity.h"
35 : #import "ShipEntity.h"
36 :
37 :
38 0 : JSObject *gOOEntityJSPrototype;
39 :
40 :
41 : static JSBool EntityGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
42 : static JSBool EntitySetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
43 : #ifndef NDEBUG
44 : static JSBool EntityDumpState(JSContext *context, uintN argc, jsval *vp);
45 : #endif
46 :
47 :
48 0 : JSClass gOOEntityJSClass =
49 : {
50 : "Entity",
51 : JSCLASS_HAS_PRIVATE,
52 :
53 : JS_PropertyStub, // addProperty
54 : JS_PropertyStub, // delProperty
55 : EntityGetProperty, // getProperty
56 : EntitySetProperty, // setProperty
57 : JS_EnumerateStub, // enumerate
58 : JS_ResolveStub, // resolve
59 : JS_ConvertStub, // convert
60 : OOJSObjectWrapperFinalize,// finalize
61 : JSCLASS_NO_OPTIONAL_MEMBERS
62 : };
63 :
64 :
65 0 : enum
66 : {
67 : // Property IDs
68 : kEntity_collisionRadius, // collision radius, double, read-only.
69 : kEntity_distanceTravelled, // distance travelled, double, read-only.
70 : kEntity_energy, // energy, double, read-write.
71 : kEntity_heading, // heading, vector, read-only (like orientation but ignoring twist angle)
72 : kEntity_mass, // mass, double, read-only
73 : kEntity_maxEnergy, // maxEnergy, double, read-only.
74 : kEntity_orientation, // orientation, quaternion, read/write
75 : kEntity_owner, // owner, Entity, read-only. (Parent ship for subentities, station for defense ships, launching ship for missiles etc)
76 : kEntity_position, // position in system space, Vector, read/write
77 : kEntity_scanClass, // scan class, string, read-only
78 : kEntity_spawnTime, // spawn time, double, read-only.
79 : kEntity_status, // entity status, string, read-only
80 : kEntity_isPlanet, // is planet, boolean, read-only.
81 : kEntity_isPlayer, // is player, boolean, read-only.
82 : kEntity_isShip, // is ship, boolean, read-only.
83 : kEntity_isStation, // is station, boolean, read-only.
84 : kEntity_isDock, // is dock, boolean, read-only.
85 : kEntity_isSubEntity, // is subentity, boolean, read-only.
86 : kEntity_isSun, // is sun, boolean, read-only.
87 : kEntity_isSunlit, // is sunlit, boolean, read-only.
88 : kEntity_isValid, // is not stale, boolean, read-only.
89 : kEntity_isInSpace, // is in space, boolean, read-only.
90 : kEntity_isVisible, // is within drawing distance, boolean, read-only.
91 : kEntity_isVisualEffect, // is visual effect, boolean, read-only.
92 : kEntity_isWormhole, // is visual effect, boolean, read-only.
93 : };
94 :
95 :
96 0 : static JSPropertySpec sEntityProperties[] =
97 : {
98 : // JS name ID flags
99 : { "collisionRadius", kEntity_collisionRadius, OOJS_PROP_READONLY_CB },
100 : { "distanceTravelled", kEntity_distanceTravelled, OOJS_PROP_READONLY_CB },
101 : { "energy", kEntity_energy, OOJS_PROP_READWRITE_CB },
102 : { "heading", kEntity_heading, OOJS_PROP_READONLY_CB },
103 : { "mass", kEntity_mass, OOJS_PROP_READONLY_CB },
104 : { "maxEnergy", kEntity_maxEnergy, OOJS_PROP_READWRITE_CB },
105 : { "orientation", kEntity_orientation, OOJS_PROP_READWRITE_CB },
106 : { "owner", kEntity_owner, OOJS_PROP_READONLY_CB },
107 : { "position", kEntity_position, OOJS_PROP_READWRITE_CB },
108 : { "scanClass", kEntity_scanClass, OOJS_PROP_READWRITE_CB },
109 : { "spawnTime", kEntity_spawnTime, OOJS_PROP_READONLY_CB },
110 : { "status", kEntity_status, OOJS_PROP_READONLY_CB },
111 : { "isPlanet", kEntity_isPlanet, OOJS_PROP_READONLY_CB },
112 : { "isPlayer", kEntity_isPlayer, OOJS_PROP_READONLY_CB },
113 : { "isShip", kEntity_isShip, OOJS_PROP_READONLY_CB },
114 : { "isDock", kEntity_isDock, OOJS_PROP_READONLY_CB },
115 : { "isStation", kEntity_isStation, OOJS_PROP_READONLY_CB },
116 : { "isSubEntity", kEntity_isSubEntity, OOJS_PROP_READONLY_CB },
117 : { "isSun", kEntity_isSun, OOJS_PROP_READONLY_CB },
118 : { "isSunlit", kEntity_isSunlit, OOJS_PROP_READONLY_CB },
119 : { "isValid", kEntity_isValid, OOJS_PROP_READONLY_CB },
120 : { "isInSpace", kEntity_isInSpace, OOJS_PROP_READONLY_CB },
121 : { "isVisible", kEntity_isVisible, OOJS_PROP_READONLY_CB },
122 : { "isVisualEffect", kEntity_isVisualEffect, OOJS_PROP_READONLY_CB },
123 : { "isWormhole", kEntity_isWormhole, OOJS_PROP_READONLY_CB },
124 : { 0 }
125 : };
126 :
127 :
128 0 : static JSFunctionSpec sEntityMethods[] =
129 : {
130 : // JS name Function min args
131 : { "toString", OOJSObjectWrapperToString, 0 },
132 : #ifndef NDEBUG
133 : { "dumpState", EntityDumpState, 0 },
134 : #endif
135 : { 0 }
136 : };
137 :
138 :
139 0 : void InitOOJSEntity(JSContext *context, JSObject *global)
140 : {
141 : gOOEntityJSPrototype = JS_InitClass(context, global, NULL, &gOOEntityJSClass, OOJSUnconstructableConstruct, 0, sEntityProperties, sEntityMethods, NULL, NULL);
142 : OOJSRegisterObjectConverter(&gOOEntityJSClass, OOJSBasicPrivateObjectConverter);
143 : }
144 :
145 :
146 0 : BOOL JSValueToEntity(JSContext *context, jsval value, Entity **outEntity)
147 : {
148 : if (JSVAL_IS_OBJECT(value))
149 : {
150 : return OOJSEntityGetEntity(context, JSVAL_TO_OBJECT(value), outEntity);
151 : }
152 :
153 : return NO;
154 : }
155 :
156 :
157 0 : BOOL EntityFromArgumentList(JSContext *context, NSString *scriptClass, NSString *function, uintN argc, jsval *argv, Entity **outEntity, uintN *outConsumed)
158 : {
159 : OOJS_PROFILE_ENTER
160 :
161 : // Sanity checks.
162 : if (outConsumed != NULL) *outConsumed = 0;
163 : if (EXPECT_NOT(argc == 0 || argv == NULL || outEntity == NULL))
164 : {
165 : OOLogGenericParameterError();
166 : return NO;
167 : }
168 :
169 : // Get value, if possible.
170 : if (EXPECT_NOT(!JSValueToEntity(context, argv[0], outEntity)))
171 : {
172 : // Failed; report bad parameters, if given a class and function.
173 : if (scriptClass != nil && function != nil)
174 : {
175 : OOJSReportWarning(context, @"%@.%@(): expected entity, got %@.", scriptClass, function, [NSString stringWithJavaScriptParameters:argv count:1 inContext:context]);
176 : return NO;
177 : }
178 : }
179 :
180 : // Success.
181 : if (outConsumed != NULL) *outConsumed = 1;
182 : return YES;
183 :
184 : OOJS_PROFILE_EXIT
185 : }
186 :
187 :
188 0 : static JSBool EntityGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
189 : {
190 : if (!JSID_IS_INT(propID)) return YES;
191 :
192 : OOJS_NATIVE_ENTER(context)
193 :
194 : Entity *entity = nil;
195 : id result = nil;
196 :
197 : if (EXPECT_NOT(!OOJSEntityGetEntity(context, this, &entity))) return NO;
198 : if (OOIsStaleEntity(entity))
199 : {
200 : if (JSID_TO_INT(propID) == kEntity_isValid) *value = JSVAL_FALSE;
201 : else { *value = JSVAL_VOID; }
202 : return YES;
203 : }
204 :
205 : switch (JSID_TO_INT(propID))
206 : {
207 : case kEntity_collisionRadius:
208 : return JS_NewNumberValue(context, [entity collisionRadius], value);
209 :
210 : case kEntity_position:
211 : return HPVectorToJSValue(context, [entity position], value);
212 :
213 : case kEntity_orientation:
214 : return QuaternionToJSValue(context, [entity normalOrientation], value);
215 :
216 : case kEntity_heading:
217 : return VectorToJSValue(context, vector_forward_from_quaternion([entity normalOrientation]), value);
218 :
219 : case kEntity_status:
220 : *value = OOJSValueFromEntityStatus(context, [entity status]);
221 : return YES;
222 :
223 : case kEntity_scanClass:
224 : *value = OOJSValueFromScanClass(context, [entity scanClass]);
225 : return YES;
226 :
227 : case kEntity_mass:
228 : return JS_NewNumberValue(context, [entity mass], value);
229 :
230 : case kEntity_owner:
231 : result = [entity owner];
232 : if (result == entity) result = nil;
233 : break;
234 :
235 : case kEntity_energy:
236 : return JS_NewNumberValue(context, [entity energy], value);
237 :
238 : case kEntity_maxEnergy:
239 : return JS_NewNumberValue(context, [entity maxEnergy], value);
240 :
241 : case kEntity_isValid:
242 : *value = [entity status] == STATUS_DEAD ? JSVAL_FALSE : JSVAL_TRUE;
243 : return YES;
244 :
245 : case kEntity_isInSpace:
246 : *value = OOJSValueFromBOOL([entity isInSpace]);
247 : return YES;
248 :
249 : case kEntity_isShip:
250 : *value = OOJSValueFromBOOL([entity isShip]);
251 : return YES;
252 :
253 : case kEntity_isStation:
254 : *value = OOJSValueFromBOOL([entity isStation]);
255 : return YES;
256 :
257 : case kEntity_isDock:
258 : *value = OOJSValueFromBOOL([entity isDock]);
259 : return YES;
260 :
261 : case kEntity_isSubEntity:
262 : *value = OOJSValueFromBOOL([entity isSubEntity]);
263 : return YES;
264 :
265 : case kEntity_isPlayer:
266 : *value = OOJSValueFromBOOL([entity isPlayer]);
267 : return YES;
268 :
269 : case kEntity_isPlanet:
270 : *value = OOJSValueFromBOOL([entity isPlanet]);
271 : return YES;
272 :
273 : case kEntity_isSun:
274 : *value = OOJSValueFromBOOL([entity isSun]);
275 : return YES;
276 :
277 : case kEntity_isSunlit:
278 : *value = OOJSValueFromBOOL([entity isSunlit]);
279 : return YES;
280 :
281 : case kEntity_isVisible:
282 : *value = OOJSValueFromBOOL([entity isVisible]);
283 : return YES;
284 :
285 : case kEntity_isVisualEffect:
286 : *value = OOJSValueFromBOOL([entity isVisualEffect]);
287 : return YES;
288 :
289 : case kEntity_isWormhole:
290 : *value = OOJSValueFromBOOL([entity isWormhole]);
291 : return YES;
292 :
293 : case kEntity_distanceTravelled:
294 : return JS_NewNumberValue(context, [entity distanceTravelled], value);
295 :
296 : case kEntity_spawnTime:
297 : return JS_NewNumberValue(context, [entity spawnTime], value);
298 :
299 : default:
300 : OOJSReportBadPropertySelector(context, this, propID, sEntityProperties);
301 : }
302 :
303 : *value = OOJSValueFromNativeObject(context, result);
304 : return YES;
305 :
306 : OOJS_NATIVE_EXIT
307 : }
308 :
309 :
310 0 : static JSBool EntitySetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
311 : {
312 : if (!JSID_IS_INT(propID)) return YES;
313 :
314 : OOJS_NATIVE_ENTER(context)
315 :
316 : Entity *entity = nil;
317 : double fValue;
318 : HPVector hpvValue;
319 : Quaternion qValue;
320 :
321 : if (EXPECT_NOT(!OOJSEntityGetEntity(context, this, &entity))) return NO;
322 : if (OOIsStaleEntity(entity)) return YES;
323 :
324 : switch (JSID_TO_INT(propID))
325 : {
326 : case kEntity_position:
327 : if (JSValueToHPVector(context, *value, &hpvValue))
328 : {
329 : [entity setPosition:hpvValue];
330 : if ([entity isShip])
331 : {
332 : [(ShipEntity *)entity resetExhaustPlumes];
333 : [(ShipEntity *)entity forceAegisCheck];
334 : }
335 : return YES;
336 : }
337 : break;
338 :
339 : case kEntity_orientation:
340 : if (JSValueToQuaternion(context, *value, &qValue))
341 : {
342 : [entity setNormalOrientation:qValue];
343 : return YES;
344 : }
345 : break;
346 :
347 : case kEntity_energy:
348 : if (JS_ValueToNumber(context, *value, &fValue))
349 : {
350 : fValue = OOClamp_0_max_d(fValue, [entity maxEnergy]);
351 : [entity setEnergy:fValue];
352 : return YES;
353 : }
354 : break;
355 :
356 : case kEntity_maxEnergy:
357 : if (JS_ValueToNumber(context, *value, &fValue))
358 : {
359 : if (fValue <= 0.0)
360 : {
361 : OOJSReportError(context, @"entity.maxEnergy must be positive.");
362 : return NO;
363 : }
364 : [entity setMaxEnergy:fValue];
365 : return YES;
366 : }
367 : break;
368 :
369 :
370 : case kEntity_scanClass:
371 : if ([entity isShip] && ![entity isPlayer])
372 : {
373 : OOScanClass newClass = OOScanClassFromJSValue(context, *value);
374 : if (newClass == CLASS_NOT_SET || newClass == CLASS_NO_DRAW || newClass == CLASS_TARGET || newClass == CLASS_WORMHOLE || newClass == CLASS_PLAYER || newClass == CLASS_VISUAL_EFFECT)
375 : {
376 : OOJSReportError(context, @"entity.scanClass cannot be set to that value.");
377 : return NO;
378 : }
379 : [entity setScanClass:newClass];
380 : return YES;
381 : }
382 : else
383 : {
384 : OOJSReportError(context, @"entity.scanClass is read-only except on NPC ships.");
385 : return NO;
386 : }
387 : default:
388 : OOJSReportBadPropertySelector(context, this, propID, sEntityProperties);
389 : return NO;
390 : }
391 :
392 : OOJSReportBadPropertyValue(context, this, propID, sEntityProperties, *value);
393 : return NO;
394 :
395 : OOJS_NATIVE_EXIT
396 : }
397 :
398 :
399 : #ifndef NDEBUG
400 0 : static JSBool EntityDumpState(JSContext *context, uintN argc, jsval *vp)
401 : {
402 : OOJS_PROFILE_ENTER
403 :
404 : Entity *thisEnt = nil;
405 : OOJSEntityGetEntity(context, OOJS_THIS, &thisEnt);
406 : [thisEnt dumpState];
407 :
408 : OOJS_RETURN_VOID;
409 :
410 : OOJS_PROFILE_EXIT
411 : }
412 : #endif
|