oolite/src/Core/JavaScript/ScriptEngine.m
Jens Ayton 6345c35592 This week's monster submit:
* Changed required.plist version parsing to treat version strings as series of integers separated by points. This allows:
  - correct parsing of bug-fix versions like 1.67.1
  - correctly sorting 1.100 after 1.99.
* Deleted just about all commented-out NSLog()s (several hundred), and some other commented-out code. (commented-out or #ifdefed-out code with no indication of when you might want to reinstate it is worse than useless.)
* Ensured that most NSLog()s will at least include their class/module in their message class.
* Renamed Universe's -recycledOrNew:, -getShipWithRole: and -getShip: to -allocRecycledOrNewEntity:, -newShipWithRole: and -newShipWithName: to better match Objective-C coding conventions. (Methods whose result needs releasing should always be called alloc*, copy*, retain* or new*.)
* Replaced -[Universe generateSystemDescription] with DescriptionForSystem().
* Replaced -[Universe getRandomDigrams] with RandomDigrams().
* Replaced +[Universe systemSeedString] with StringFromRandomSeed().
* Replaced [Universe entityZero] with [PlayerEntity sharedPlayer].
* Replaced scriptedUniverse in ScriptEngine with [Universe sharedUniverse].
* Removed HAVE_SOUND. If you're porting to a platform without sound, use a no-op implementation of OOSound.
* Removed all (commented-out) NSBeep()s.
* Made settings caching in OOLog actually work. Doing complete message class setting resolution every time OOLog() was called was around 2% of our per-frame cost. (With this fixed, cache hit rate is well over 99% after a few seconds of play.) *headdesk*  (It may also be worth moving the settings-check into the macro, to avoid evaluating parameters for log messages that are never seen, but this might cause obscure bugs due to the parameters having side effects.)

Stuff that should have been in revision 859:
* Replaced all occurrences of -[Universe expandDescription:forSystem:]  with ExpandDescriptionForSeed() and ExpandDescriptionForCurrentSystem().
* Replaced  -[Universe expandDescriptionWithLocals:forSystem:withLocalVariables:] with ExpandDescriptionsWithLocalsForSystemSeed() and ExpandDescriptionsWithLocalsForCurrentSystem().


git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@872 127b21dd-08f5-0310-b4b7-95ae10353056
2007-03-26 01:24:50 +00:00

868 lines
28 KiB
Objective-C

/*
ScriptEngine.h
JavaScript support for Oolite
Copyright (C) 2007 David Taylor
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#import "ScriptEngine.h"
#import "OXPScript.h"
#include <stdio.h>
#include <string.h>
#define kOOLogUnconvertedNSLog @"unclassified.JSScriptEngine"
JSObject *xglob, *universeObj, *systemObj, *playerObj, *missionObj;
extern OXPScript *currentOXPScript;
NSString *JSValToNSString(JSContext *cx, jsval val) {
JSString *str = JS_ValueToString(cx, val);
char *chars = JS_GetStringBytes(str);
return [NSString stringWithCString:chars];
}
void NSStringToJSVal(JSContext *cx, NSString *string, jsval *vp) {
const char *strptr = [string cString];
JSString *js_strptr = JS_NewStringCopyZ(cx, strptr);
*vp = STRING_TO_JSVAL(js_strptr);
}
void BOOLToJSVal(JSContext *cx, BOOL b, jsval *vp) {
if (b == YES)
*vp = BOOLEAN_TO_JSVAL(1);
else
*vp = BOOLEAN_TO_JSVAL(0);
}
//===========================================================================
// MissionVars class
//===========================================================================
JSBool MissionVarsGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool MissionVarsSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSClass MissionVars_class = {
"MissionVars", JSCLASS_HAS_PRIVATE,
JS_PropertyStub,JS_PropertyStub,MissionVarsGetProperty,MissionVarsSetProperty,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
JSBool MissionVarsGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
NSDictionary *mission_variables = [playerEntity mission_variables];
if (JSVAL_IS_STRING(id)) {
NSString *key = JSValToNSString(cx, id);
NSString *value = (NSString *)[mission_variables objectForKey:key];
if (!value)
*vp = JSVAL_VOID;
else
{
int i;
int c = [value length];
BOOL isNumber = YES;
// The point of this code is to try and tell the JS interpreter to treat numeric strings
// as numbers where possible so that standard arithmetic works as you'd expect rather than
// 1+1 == "11". So a JSVAL_DOUBLE is returned if possible, otherwise a JSVAL_STRING is returned.
NSCharacterSet *numberCharSet = [NSCharacterSet characterSetWithCharactersInString:@"1234567890-."];
for (i = 0; i < c; i++) {
if ([numberCharSet characterIsMember:[value characterAtIndex:i]] == NO) {
isNumber = NO;
break;
}
}
if (isNumber) {
jsdouble ds = [value doubleValue];
JSBool ok = JS_NewDoubleValue(cx, ds, vp);
if (ok)
return JS_TRUE;
}
const char *name_str = [value cString];
JSString *js_name = JS_NewStringCopyZ(cx, name_str);
*vp = STRING_TO_JSVAL(js_name);
}
}
return JS_TRUE;
}
JSBool MissionVarsSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
NSDictionary *mission_variables = [playerEntity mission_variables];
if (JSVAL_IS_STRING(id)) {
NSString *key = JSValToNSString(cx, id);
NSString *value = JSValToNSString(cx, *vp);
[mission_variables setValue:value forKey:key];
}
return JS_TRUE;
}
//===========================================================================
// Global object class
//===========================================================================
JSBool GlobalGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSClass global_class = {
"Oolite",0,
JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
enum global_propertyIds {
GLOBAL_GALAXY_NUMBER, GLOBAL_PLANET_NUMBER, GLOBAL_DOCKED_AT_MAIN_STATION, GLOBAL_DOCKED_STATION_NAME, GLOBAL_MISSION_VARS,
GLOBAL_GUI_SCREEN, GLOBAL_STATUS_STRING
};
JSPropertySpec Global_props[] = {
{ "GalaxyNumber", GLOBAL_GALAXY_NUMBER, JSPROP_ENUMERATE, GlobalGetProperty },
{ "PlanetNumber", GLOBAL_PLANET_NUMBER, JSPROP_ENUMERATE, GlobalGetProperty },
{ "DockedAtMainStation", GLOBAL_DOCKED_AT_MAIN_STATION, JSPROP_ENUMERATE, GlobalGetProperty },
{ "StationName", GLOBAL_DOCKED_STATION_NAME, JSPROP_ENUMERATE, GlobalGetProperty },
{ "MissionVars", GLOBAL_MISSION_VARS, JSPROP_ENUMERATE, GlobalGetProperty },
{ "GUIScreen", GLOBAL_GUI_SCREEN, JSPROP_ENUMERATE, GlobalGetProperty },
{ "StatusString", GLOBAL_STATUS_STRING, JSPROP_ENUMERATE, GlobalGetProperty },
{ 0 }
};
//JSBool GlobalEnableLogging(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool GlobalLog(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool GlobalListenForKey(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSFunctionSpec Global_funcs[] = {
// { "EnableLogging", GlobalEnableLogging, 1, 0 },
{ "Log", GlobalLog, 1, 0 },
{ "ListenForKey", GlobalListenForKey, 1, 0 },
{ 0 }
};
JSBool GlobalListenForKey(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
NSString *key = JSValToNSString(cx, argv[0]);
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity mapKey:key toOXP:currentOXPScript];
return JS_TRUE;
}
/*
JSBool GlobalEnableLogging(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
}
*/
JSBool GlobalLog(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
JSString *str;
str = JS_ValueToString(cx, argv[0]);
NSLog(@"%s", JS_GetStringBytes(str));
return JS_TRUE;
}
JSBool GlobalGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
switch (JSVAL_TO_INT(id)) {
case GLOBAL_GALAXY_NUMBER:
*vp = INT_TO_JSVAL([[playerEntity galaxy_number] intValue]);
break;
case GLOBAL_PLANET_NUMBER:
*vp = INT_TO_JSVAL([[playerEntity planet_number] intValue]);
break;
case GLOBAL_DOCKED_AT_MAIN_STATION:
BOOLToJSVal(cx, [[playerEntity dockedAtMainStation_bool] isEqualToString:@"YES"], vp);
break;
case GLOBAL_DOCKED_STATION_NAME:
NSStringToJSVal(cx, [playerEntity dockedStationName_string], vp);
break;
case GLOBAL_GUI_SCREEN:
NSStringToJSVal(cx, [playerEntity gui_screen_string], vp);
break;
case GLOBAL_STATUS_STRING:
NSStringToJSVal(cx, [playerEntity status_string], vp);
break;
case GLOBAL_MISSION_VARS: {
JSObject *mv = JS_DefineObject(cx, xglob, "MissionVars", &MissionVars_class, 0x00, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
*vp = OBJECT_TO_JSVAL(mv);
break;
}
}
return JS_TRUE;
}
#import "JSUniverse.h"
//===========================================================================
// Player proxy
//===========================================================================
JSBool PlayerGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool PlayerSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSClass Player_class = {
"Player", JSCLASS_HAS_PRIVATE,
JS_PropertyStub,JS_PropertyStub,PlayerGetProperty,PlayerSetProperty,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
enum Player_propertyIds {
PE_SHIP_DESCRIPTION, PE_COMMANDER_NAME, PE_SCORE, PE_CREDITS, PE_LEGAL_STATUS,
PE_FUEL_LEVEL, PE_FUEL_LEAK_RATE, PE_ALERT_CONDITION, PE_ALERT_FLAGS
};
JSPropertySpec Player_props[] = {
{ "ShipDescription", PE_SHIP_DESCRIPTION, JSPROP_ENUMERATE },
{ "Name", PE_COMMANDER_NAME, JSPROP_ENUMERATE },
{ "Score", PE_SCORE, JSPROP_ENUMERATE },
{ "Credits", PE_CREDITS, JSPROP_ENUMERATE },
{ "LegalStatus", PE_LEGAL_STATUS, JSPROP_ENUMERATE },
{ "Fuel", PE_FUEL_LEVEL, JSPROP_ENUMERATE },
{ "FuelLeakRate", PE_FUEL_LEAK_RATE, JSPROP_ENUMERATE },
{ "AlertCondition", PE_ALERT_CONDITION, JSPROP_ENUMERATE },
{ "AlertFlags", PE_ALERT_FLAGS, JSPROP_ENUMERATE },
{ 0 }
};
JSBool PlayerAwardEquipment(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerRemoveEquipment(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerHasEquipment(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerLaunch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerCall(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerAwardCargo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerRemoveAllCargo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool PlayerUseSpecialCargo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSFunctionSpec Player_funcs[] = {
{ "AwardEquipment", PlayerAwardEquipment, 1, 0 },
{ "RemoveEquipment", PlayerRemoveEquipment, 1, 0 },
{ "HasEquipment", PlayerHasEquipment, 1, 0 },
{ "Launch", PlayerLaunch, 0, 0 },
{ "Call", PlayerCall, 2, 0 },
{ "AwardCargo", PlayerAwardCargo, 2, 0 },
{ "RemoveAllCargo", PlayerRemoveAllCargo, 0, 0 },
{ "UseSpecialCargo", PlayerUseSpecialCargo, 1, 0 },
{ 0 }
};
JSBool PlayerAwardCargo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
if (argc == 2) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
NSString *amount_type = [NSString stringWithFormat:@"%@ %@", JSValToNSString(cx, argv[0]), JSValToNSString(cx, argv[1])];
[playerEntity awardCargo:amount_type];
}
return JS_TRUE;
}
JSBool PlayerRemoveAllCargo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity removeAllCargo];
return JS_TRUE;
}
JSBool PlayerUseSpecialCargo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
if (argc == 1) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity useSpecialCargo:JSValToNSString(cx, argv[0])];
}
return JS_TRUE;
}
JSBool PlayerAwardEquipment(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
JSString *jskey = JS_ValueToString(cx, argv[0]);
[playerEntity awardEquipment: [NSString stringWithCString:JS_GetStringBytes(jskey)]];
}
return JS_TRUE;
}
JSBool PlayerRemoveEquipment(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
JSString *jskey = JS_ValueToString(cx, argv[0]);
[playerEntity removeEquipment: [NSString stringWithCString:JS_GetStringBytes(jskey)]];
}
return JS_TRUE;
}
JSBool PlayerHasEquipment(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
JSString *jskey = JS_ValueToString(cx, argv[0]);
BOOLToJSVal(cx, [playerEntity has_extra_equipment: [NSString stringWithCString:JS_GetStringBytes(jskey)]], rval);
}
return JS_TRUE;
}
JSBool PlayerLaunch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity launchFromStation];
return JS_TRUE;
}
JSBool PlayerCall(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0) {
NSString *selectorString = JSValToNSString(cx, argv[0]);
// Check if the selector needs a trailing colon to flag an argument will be sent
if (argc > 1)
selectorString = [NSString stringWithFormat:@"%@:", selectorString];
SEL _selector = NSSelectorFromString(selectorString);
if ([playerEntity respondsToSelector:_selector]) {
if (argc == 1)
[playerEntity performSelector:_selector];
else {
NSString *valueString = JSValToNSString(cx, argv[1]);
[playerEntity performSelector:_selector withObject:valueString];
}
}
}
return JS_TRUE;
}
JSBool PlayerGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
switch (JSVAL_TO_INT(id)) {
case PE_SHIP_DESCRIPTION:
NSStringToJSVal(cx, [playerEntity commanderShip_string], vp);
break;
case PE_COMMANDER_NAME:
NSStringToJSVal(cx, [playerEntity commanderName_string], vp);
break;
case PE_SCORE:
JS_NewDoubleValue(cx, [[playerEntity score_number] doubleValue], vp);
break;
case PE_LEGAL_STATUS:
JS_NewDoubleValue(cx, [[playerEntity legalStatus_number] doubleValue], vp);
break;
case PE_CREDITS:
JS_NewDoubleValue(cx, [[playerEntity credits_number] doubleValue], vp);
break;
case PE_FUEL_LEVEL:
JS_NewDoubleValue(cx, [[playerEntity fuel_level_number] doubleValue], vp);
break;
case PE_FUEL_LEAK_RATE:
JS_NewDoubleValue(cx, [[playerEntity fuel_leak_rate_number] doubleValue], vp);
break;
case PE_ALERT_CONDITION:
*vp = INT_TO_JSVAL([playerEntity alert_condition]);
break;
case PE_ALERT_FLAGS:
*vp = INT_TO_JSVAL([playerEntity alert_flags]);
break;
}
return JS_TRUE;
}
JSBool PlayerSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
switch (JSVAL_TO_INT(id)) {
case PE_SCORE:
[playerEntity setKills:[JSValToNSString(cx, *vp) intValue]];
break;
case PE_LEGAL_STATUS:
[playerEntity setLegalStatus:JSValToNSString(cx, *vp)];
break;
case PE_CREDITS:
[playerEntity setCredits:[JSValToNSString(cx, *vp) intValue]];
break;
case PE_FUEL_LEVEL:
[playerEntity setCredits:(int)([JSValToNSString(cx, *vp) doubleValue] * 10)];
break;
case PE_FUEL_LEAK_RATE:
[playerEntity setLegalStatus:JSValToNSString(cx, *vp)];
break;
}
return JS_TRUE;
}
//===========================================================================
// System (solar system) proxy
//===========================================================================
JSBool SystemGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool SystemSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSClass System_class = {
"System", JSCLASS_HAS_PRIVATE,
JS_PropertyStub,JS_PropertyStub,SystemGetProperty,SystemSetProperty,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
enum System_propertyIds {
SYS_ID, SYS_NAME, SYS_DESCRIPTION, SYS_GOING_NOVA, SYS_GONE_NOVA, SYS_GOVT_STR, SYS_GOVT_ID, SYS_ECONOMY_ID,
SYS_TECH_LVL, SYS_POPULATION, SYS_PRODUCTIVITY, SYS_INHABITANTS
};
JSPropertySpec System_props[] = {
{ "Id", SYS_ID, JSPROP_ENUMERATE },
{ "Name", SYS_NAME, JSPROP_ENUMERATE },
{ "Description", SYS_DESCRIPTION, JSPROP_ENUMERATE },
{ "InhabitantsDescription", SYS_INHABITANTS, JSPROP_ENUMERATE },
{ "GoingNova", SYS_GOING_NOVA, JSPROP_ENUMERATE },
{ "GoneNova", SYS_GONE_NOVA, JSPROP_ENUMERATE },
{ "GovernmentDescription", SYS_GOVT_STR, JSPROP_ENUMERATE },
{ "GovernmentId", SYS_GOVT_ID, JSPROP_ENUMERATE },
{ "EconomyId", SYS_ECONOMY_ID, JSPROP_ENUMERATE },
{ "TechLevel", SYS_TECH_LVL, JSPROP_ENUMERATE },
{ "Population", SYS_POPULATION, JSPROP_ENUMERATE },
{ "Productivity", SYS_PRODUCTIVITY, JSPROP_ENUMERATE },
{ 0 }
};
JSBool SystemAddPlanet(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool SystemAddMoon(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool SystemSendAllShipsAway(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool SystemSetSunNova(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSFunctionSpec System_funcs[] = {
{ "AddPlanet", SystemAddPlanet, 1, 0 },
{ "AddMoon", SystemAddMoon, 1, 0 },
{ "SendAllShipsAway", SystemSendAllShipsAway, 1, 0 },
{ "SetSunNova", SystemSetSunNova, 1, 0 },
{ 0 }
};
JSBool SystemAddPlanet(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
NSString *key = JSValToNSString(cx, argv[0]);
[playerEntity addPlanet:key];
}
return JS_TRUE;
}
JSBool SystemAddMoon(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
NSString *key = JSValToNSString(cx, argv[0]);
[playerEntity addMoon:key];
}
return JS_TRUE;
}
JSBool SystemSendAllShipsAway(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity sendAllShipsAway];
return JS_TRUE;
}
JSBool SystemSetSunNova(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0) {
NSString *key = JSValToNSString(cx, argv[0]);
[playerEntity setSunNovaIn:key];
}
return JS_TRUE;
}
static Random_Seed currentSystem;
static NSDictionary *planetinfo = nil;
JSBool SystemGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
NSString *str;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (!equal_seeds(currentSystem, playerEntity->system_seed)) {
//fprintf(stdout, "Current system has changed, regenerating local copy of planetinfo\r\n");
currentSystem = playerEntity->system_seed;
if (planetinfo)
[planetinfo release];
planetinfo = [[[Universe sharedUniverse] generateSystemData:currentSystem] retain];
}
switch (JSVAL_TO_INT(id)) {
case SYS_ID:
*vp = INT_TO_JSVAL([[playerEntity planet_number] intValue]);
break;
case SYS_NAME:
str = (NSString *)[planetinfo objectForKey:KEY_NAME];
if (!str)
str = @"None";
NSStringToJSVal(cx, str, vp);
break;
case SYS_DESCRIPTION:
str = (NSString *)[planetinfo objectForKey:KEY_DESCRIPTION];
if (!str)
str = @"None";
NSStringToJSVal(cx, str, vp);
break;
case SYS_INHABITANTS:
str = (NSString *)[planetinfo objectForKey:KEY_INHABITANTS];
if (!str)
str = @"None";
NSStringToJSVal(cx, str, vp);
break;
case SYS_GOING_NOVA:
BOOLToJSVal(cx, [[playerEntity sunWillGoNova_bool] isEqualToString:@"YES"], vp);
break;
case SYS_GONE_NOVA:
BOOLToJSVal(cx, [[playerEntity sunGoneNova_bool] isEqualToString:@"YES"], vp);
break;
case SYS_GOVT_STR:
NSStringToJSVal(cx, [playerEntity systemGovernment_string], vp);
break;
case SYS_GOVT_ID:
JS_NewDoubleValue(cx, [[playerEntity systemGovernment_number] doubleValue], vp);
break;
case SYS_ECONOMY_ID:
JS_NewDoubleValue(cx, [[playerEntity systemEconomy_number] doubleValue], vp);
break;
case SYS_TECH_LVL:
JS_NewDoubleValue(cx, [[playerEntity systemTechLevel_number] doubleValue], vp);
break;
case SYS_POPULATION:
JS_NewDoubleValue(cx, [[playerEntity systemPopulation_number] doubleValue], vp);
break;
case SYS_PRODUCTIVITY:
JS_NewDoubleValue(cx, [[playerEntity systemProductivity_number] doubleValue], vp);
break;
}
return JS_TRUE;
}
JSBool SystemSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (!equal_seeds(currentSystem, playerEntity->system_seed)) {
//fprintf(stdout, "Current system has changed, regenerating local copy of planetinfo\r\n");
currentSystem = playerEntity->system_seed;
if (planetinfo)
[planetinfo release];
planetinfo = [[[Universe sharedUniverse] generateSystemData:currentSystem] retain];
}
int gn = [[playerEntity galaxy_number] intValue];
int pn = [[playerEntity planet_number] intValue];
switch (JSVAL_TO_INT(id)) {
case SYS_NAME:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_NAME value:JSValToNSString(cx, *vp)];
break;
case SYS_DESCRIPTION:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_DESCRIPTION value:JSValToNSString(cx, *vp)];
break;
case SYS_INHABITANTS:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_INHABITANTS value:JSValToNSString(cx, *vp)];
break;
/*
case SYS_GOING_NOVA:
//BOOLToJSVal(cx, [[playerEntity sunWillGoNova_bool] isEqualToString:@"YES"], vp);
break;
case SYS_GONE_NOVA:
//BOOLToJSVal(cx, [[playerEntity sunGoneNova_bool] isEqualToString:@"YES"], vp);
break;
*/
case SYS_GOVT_ID:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_GOVERNMENT value:[NSNumber numberWithInt:[JSValToNSString(cx, *vp) intValue]]];
break;
case SYS_ECONOMY_ID:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_ECONOMY value:[NSNumber numberWithInt:[JSValToNSString(cx, *vp) intValue]]];
break;
case SYS_TECH_LVL:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_TECHLEVEL value:[NSNumber numberWithInt:[JSValToNSString(cx, *vp) intValue]]];
break;
case SYS_POPULATION:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_POPULATION value:[NSNumber numberWithInt:[JSValToNSString(cx, *vp) intValue]]];
break;
case SYS_PRODUCTIVITY:
[[Universe sharedUniverse] setSystemDataForGalaxy:gn planet:pn key:KEY_PRODUCTIVITY value:[NSNumber numberWithInt:[JSValToNSString(cx, *vp) intValue]]];
break;
}
return JS_TRUE;
}
//===========================================================================
// Mission class
//===========================================================================
JSBool MissionGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSBool MissionSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
JSClass Mission_class = {
"Mission", JSCLASS_HAS_PRIVATE,
JS_PropertyStub,JS_PropertyStub,MissionGetProperty,MissionSetProperty,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
enum Mission_propertyIds {
MISSION_TEXT, MISSION_MUSIC, MISSION_IMAGE, MISSION_CHOICES, MISSION_CHOICE, MISSION_INSTRUCTIONS
};
JSPropertySpec Mission_props[] = {
{ "MissionScreenTextKey", MISSION_TEXT, JSPROP_ENUMERATE },
{ "MusicFilename", MISSION_MUSIC, JSPROP_ENUMERATE },
{ "ImageFilename", MISSION_IMAGE, JSPROP_ENUMERATE },
{ "ChoicesKey", MISSION_CHOICES, JSPROP_ENUMERATE },
{ "Choice", MISSION_CHOICE, JSPROP_ENUMERATE },
{ "InstructionsKey", MISSION_INSTRUCTIONS, JSPROP_ENUMERATE },
{ 0 }
};
JSBool MissionShowMissionScreen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool MissionShowShipModel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool MissionResetMissionChoice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool MissionMarkSystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSBool MissionUnmarkSystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
JSFunctionSpec Mission_funcs[] = {
{ "ShowMissionScreen", MissionShowMissionScreen, 0, 0 },
{ "ShowShipModel", MissionShowShipModel, 1, 0 },
{ "ResetMissionChoice", MissionResetMissionChoice, 0, 0 },
{ "MarkSystem", MissionMarkSystem, 1, 0 },
{ "UnmarkSystem", MissionUnmarkSystem, 1, 0 },
{ 0 }
};
JSBool MissionGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
NSString *str;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
switch (JSVAL_TO_INT(id)) {
case MISSION_CHOICE:
str = (NSString *)[playerEntity missionChoice_string];
if (!str)
str = @"None";
NSStringToJSVal(cx, str, vp);
break;
}
return JS_TRUE;
}
JSBool MissionSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
if (!JSVAL_IS_INT(id)) return JS_TRUE;
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
switch (JSVAL_TO_INT(id)) {
case MISSION_TEXT: {
if (JSVAL_IS_STRING(*vp)) {
JSString *jskey = JS_ValueToString(cx, *vp);
[playerEntity addMissionText: [NSString stringWithCString:JS_GetStringBytes(jskey)]];
}
break;
}
case MISSION_MUSIC: {
if (JSVAL_IS_STRING(*vp)) {
JSString *jskey = JS_ValueToString(cx, *vp);
[playerEntity setMissionMusic: [NSString stringWithCString:JS_GetStringBytes(jskey)]];
}
break;
}
case MISSION_IMAGE: {
if (JSVAL_IS_STRING(*vp)) {
NSString *str = JSValToNSString(cx, *vp);
if ([str length] == 0)
str = @"none";
[playerEntity setMissionImage:str];
}
break;
}
case MISSION_CHOICES: {
if (JSVAL_IS_STRING(*vp)) {
JSString *jskey = JS_ValueToString(cx, *vp);
[playerEntity setMissionChoices: [NSString stringWithCString:JS_GetStringBytes(jskey)]];
}
break;
}
case MISSION_INSTRUCTIONS: {
if (JSVAL_IS_STRING(*vp)) {
JSString *jskey = JS_ValueToString(cx, *vp);
NSString *ins = [NSString stringWithCString:JS_GetStringBytes(jskey)];
if ([ins length])
[playerEntity setMissionDescription:ins forMission:[currentOXPScript name]];
else
[playerEntity clearMissionDescriptionForMission:[currentOXPScript name]];
}
break;
}
}
return JS_TRUE;
}
JSBool MissionShowMissionScreen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity setGuiToMissionScreen];
return JS_TRUE;
}
JSBool MissionShowShipModel(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
JSString *jskey = JS_ValueToString(cx, argv[0]);
[playerEntity showShipModel: [NSString stringWithCString:JS_GetStringBytes(jskey)]];
}
return JS_TRUE;
}
JSBool MissionResetMissionChoice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
PlayerEntity *playerEntity = [PlayerEntity sharedPlayer];
[playerEntity resetMissionChoice];
return JS_TRUE;
}
JSBool MissionMarkSystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
return JS_TRUE;
}
JSBool MissionUnmarkSystem(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
return JS_TRUE;
}
void reportJSError(JSContext *cx, const char *message, JSErrorReport *report) {
NSLog(@"JavaScript error: %s", message);
NSLog(@"%s:%d: %s", report->filename, report->lineno, report->linebuf);
}
//===========================================================================
// JavaScript engine initialisation and shutdown
//===========================================================================
@implementation ScriptEngine
- (id) initWithUniverse: (Universe *)universe
{
self = [super init];
/*set up global JS variables, including global and custom objects */
/* initialize the JS run time, and return result in rt */
rt = JS_NewRuntime(8L * 1024L * 1024L);
/* if rt does not have a value, end the program here */
if (!rt) {
[super dealloc];
exit(1); //return nil;
}
/* create a context and associate it with the JS run time */
cx = JS_NewContext(rt, 8192);
NSLog(@"created context");
/* if cx does not have a value, end the program here */
if (cx == NULL) {
[super dealloc];
exit(1); //return nil;
}
JS_SetErrorReporter(cx, reportJSError);
/* create the global object here */
glob = JS_NewObject(cx, &global_class, NULL, NULL);
xglob = glob;
/* initialize the built-in JS objects and the global object */
builtins = JS_InitStandardClasses(cx, glob);
JS_DefineProperties(cx, glob, Global_props);
JS_DefineFunctions(cx, glob, Global_funcs);
universeObj = JS_DefineObject(cx, glob, "Universe", &Universe_class, NULL, JSPROP_ENUMERATE);
//JS_DefineProperties(cx, universeObj, Universe_props);
JS_DefineFunctions(cx, universeObj, Universe_funcs);
systemObj = JS_DefineObject(cx, glob, "System", &System_class, NULL, JSPROP_ENUMERATE);
JS_DefineProperties(cx, systemObj, System_props);
JS_DefineFunctions(cx, systemObj, System_funcs);
playerObj = JS_DefineObject(cx, glob, "Player", &Player_class, NULL, JSPROP_ENUMERATE);
JS_DefineProperties(cx, playerObj, Player_props);
JS_DefineFunctions(cx, playerObj, Player_funcs);
missionObj = JS_DefineObject(cx, glob, "Mission", &Mission_class, NULL, JSPROP_ENUMERATE);
JS_DefineProperties(cx, missionObj, Mission_props);
JS_DefineFunctions(cx, missionObj, Mission_funcs);
return self;
}
- (void) dealloc
{
// free up the OXPScripts too!
JS_DestroyContext(cx);
/* Before exiting the application, free the JS run time */
JS_DestroyRuntime(rt);
[super dealloc];
}
- (JSContext *) context
{
return cx;
}
@end