// This module defines the table of all functions callable from JS. // it's required by the interpreter; we make use of the opportunity to // document them all in one spot. we thus obviate having to dig through // all the other headers. most of the functions are implemented here; // as for the rest, we only link to their docs (duplication is bad). #include "precompiled.h" #include "ScriptGlue.h" #include "CLogger.h" #include "CConsole.h" #include "CStr.h" #include "EntityHandles.h" #include "Entity.h" #include "EntityManager.h" #include "BaseEntityCollection.h" #include "Scheduler.h" #include "timer.h" #include "LightEnv.h" #include "MapWriter.h" #include "GameEvents.h" #include "Interact.h" #include "Renderer.h" #include "Game.h" #include "Network/Server.h" #include "Network/Client.h" #include "gui/CGUI.h" #include "ps/i18n.h" #include "scripting/JSInterface_Entity.h" #include "scripting/JSCollection.h" #include "scripting/JSInterface_BaseEntity.h" #include "scripting/JSInterface_Vector3D.h" #include "scripting/JSInterface_Selection.h" #include "scripting/JSInterface_Camera.h" #include "scripting/JSInterface_Console.h" #include "scripting/JSInterface_VFS.h" #include "scripting/JSConversions.h" #ifndef NO_GUI # include "gui/scripting/JSInterface_IGUIObject.h" #endif extern CConsole* g_Console; // rationale: the function table is now at the end of the source file to // avoid the need for forward declarations for every function. // all normal function wrappers have the following signature: // JSBool func(JSContext* cx, JSObject* globalObject, uint argc, jsval* argv, jsval* rval); // all property accessors have the following signature: // JSBool accessor(JSContext* cx, JSObject* globalObject, jsval id, jsval* vp); // consistent argc checking for normal function wrappers: reports an // error via JS and returns if number of parameters is incorrect. // .. require exact number (most common case) #define REQUIRE_PARAMS(exact_number, func_name)\ if(argc != exact_number)\ {\ JS_ReportError(cx, #func_name ": number of parameters passed doesn't match expected count");\ return JS_FALSE;\ } // .. require 0 params (avoids L4 warning "unused argv param") #define REQUIRE_NO_PARAMS(func_name)\ UNUSED(argv);\ if(argc != 0)\ {\ JS_ReportError(cx, #func_name ": number of parameters passed doesn't match expected count");\ return JS_FALSE;\ } // .. accept at most N params (e.g. buildTime) #define REQUIRE_MAX_PARAMS(max_number, func_name)\ if(argc > max_number)\ {\ JS_ReportError(cx, #func_name ": too many parameters passed");\ return JS_FALSE;\ } // .. accept at least N params (e.g. issueCommand) #define REQUIRE_MIN_PARAMS(min_number, func_name)\ if(argc < min_number)\ {\ JS_ReportError(cx, #func_name ": too few parameters passed");\ return JS_FALSE;\ } //----------------------------------------------------------------------------- // Output //----------------------------------------------------------------------------- // Write values to the log file. // params: any number of any type. // returns: // notes: // - Each argument is converted to a string and then written to the log. // - Output is in NORMAL style (see LOG). JSBool WriteLog(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval)) { REQUIRE_MIN_PARAMS(1, WriteLog); CStr logMessage; for (int i = 0; i < (int)argc; i++) { try { CStr arg = g_ScriptingHost.ValueToString( argv[i] ); logMessage += arg; } catch( PSERROR_Scripting_ConversionFailed ) { // Do nothing. } } // We should perhaps unicodify (?) the logger at some point. LOG( NORMAL, "script", logMessage ); return JS_TRUE; } //----------------------------------------------------------------------------- // Entity //----------------------------------------------------------------------------- // Retrieve the entity currently occupying the specified handle. // params: handle [int] // returns: entity JSBool getEntityByHandle( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { REQUIRE_PARAMS(1, getEntityByHandle); *rval = JSVAL_NULL; i32 handle; try { handle = g_ScriptingHost.ValueToInt( argv[0] ); } catch( PSERROR_Scripting_ConversionFailed ) { JS_ReportError( cx, "Invalid handle" ); return( JS_TRUE ); } HEntity* v = g_EntityManager.getByHandle( (u16)handle ); if( !v ) { JS_ReportError( cx, "No entity occupying handle: %d", handle ); return( JS_TRUE ); } JSObject* entity = (*v)->GetScript(); *rval = OBJECT_TO_JSVAL( entity ); return( JS_TRUE ); } // Look up an EntityTemplate by name. // params: template name [wstring] // returns: entity template object JSBool getEntityTemplate( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { REQUIRE_PARAMS(1, getEntityTemplate); *rval = JSVAL_NULL; CStrW templateName; try { templateName = g_ScriptingHost.ValueToUCString( argv[0] ); } catch( PSERROR_Scripting_ConversionFailed ) { JS_ReportError( cx, "Invalid template identifier" ); return( JS_TRUE ); } CBaseEntity* v = g_EntityTemplateCollection.getTemplate( templateName ); if( !v ) { JS_ReportError( cx, "No such template: %s", CStr8(templateName).c_str() ); return( JS_TRUE ); } *rval = OBJECT_TO_JSVAL( v->GetScript() ); return( JS_TRUE ); } // Issue a command (network message) to an entity or collection. // params: either an entity- or entity collection object, message ID [int], // any further params needed by CNetMessage::CommandFromJSArgs // returns: command in serialized form [string] JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { REQUIRE_MIN_PARAMS(2, issueCommand); debug_assert(JSVAL_IS_OBJECT(argv[0])); *rval = JSVAL_NULL; CEntityList entities; if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class) entities.push_back( (ToNative(argv[0])) ->me); else entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0])); CNetMessage *msg = CNetMessage::CommandFromJSArgs(entities, cx, argc-1, argv+1); if (msg) { g_Console->InsertMessage(L"issueCommand: %hs", msg->GetString().c_str()); g_Game->GetSimulation()->QueueLocalCommand(msg); *rval = g_ScriptingHost.UCStringToValue(msg->GetString()); } return JS_TRUE; } //----------------------------------------------------------------------------- // Events //----------------------------------------------------------------------------- // Register a global handler for the specified DOM event. // params: event type name [wstring], handler [fragment or function] // returns: whether it was actually newly registered [bool] JSBool AddGlobalHandler( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ) { REQUIRE_PARAMS(2, AddGlobalHandler); *rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.AddHandlerJS( cx, argc, argv ) ); return( JS_TRUE ); } // Remove a previously registered global handler for the specified DOM event. // params: event type name [wstring], handler [fragment or function] // returns: whether it was successfully removed [bool] JSBool RemoveGlobalHandler( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ) { REQUIRE_PARAMS(2, RemoveGlobalHandler); *rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.RemoveHandlerJS( cx, argc, argv ) ); return( JS_TRUE ); } //----------------------------------------------------------------------------- // Timer //----------------------------------------------------------------------------- // Request a callback be executed after the specified delay. // params: callback [fragment or function], delay in milliseconds [int] // returns: // notes: // - Scripts and functions registered this way are called on the first // simulation frame after the specified period has elapsed. If this causes // multiple segments of code to be executed in the same frame, // relative timing is maintained. Delays of 0 milliseconds cause code to be // executed on the following simulation frame. If more than one script or // function is scheduled to execute in the same millisecond, the order of // execution is undefined. Code is scheduled in simulation time, and is // therefore suspended while the game is paused or frozen. Granularity of // timing is also limited to 1/(Simulation frame rate); currently 100ms. // The called function or script executes in the same scope as the // code that called setTimeout (amongst other things, the // 'this' reference is usually maintained) JSBool setTimeout( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_PARAMS(2, setTimeout); size_t delay; try { delay = g_ScriptingHost.ValueToInt( argv[1] ); } catch( PSERROR_Scripting_ConversionFailed ) { JS_ReportError( cx, "Invalid timer parameters" ); return( JS_TRUE ); } switch( JS_TypeOfValue( cx, argv[0] ) ) { case JSTYPE_STRING: { CStrW fragment = g_ScriptingHost.ValueToUCString( argv[0] ); g_Scheduler.pushTime( delay, fragment, JS_GetScopeChain( cx ) ); return( JS_TRUE ); } case JSTYPE_FUNCTION: { JSFunction* fn = JS_ValueToFunction( cx, argv[0] ); g_Scheduler.pushTime( delay, fn, JS_GetScopeChain( cx ) ); return( JS_TRUE ); } default: JS_ReportError( cx, "Invalid timer script" ); return( JS_TRUE ); } } // Request a callback be executed periodically. // params: callback [fragment or function], initial delay in ms [int], period in ms [int] // OR callback [fragment or function], period in ms [int] (initial delay = period) // returns: // notes: // - setTimeout's notes apply here as well. JSBool setInterval( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_MIN_PARAMS(2, setInterval); REQUIRE_MAX_PARAMS(3, setInterval); size_t first, interval; try { first = g_ScriptingHost.ValueToInt( argv[1] ); if( argc == 3 ) { // toDo, first, interval interval = g_ScriptingHost.ValueToInt( argv[2] ); } else { // toDo, interval (first = interval) interval = first; } } catch( PSERROR_Scripting_ConversionFailed ) { JS_ReportError( cx, "Invalid timer parameters" ); return( JS_TRUE ); } switch( JS_TypeOfValue( cx, argv[0] ) ) { case JSTYPE_STRING: { CStrW fragment = g_ScriptingHost.ValueToUCString( argv[0] ); g_Scheduler.pushInterval( first, interval, fragment, JS_GetScopeChain( cx ) ); return( JS_TRUE ); } case JSTYPE_FUNCTION: { JSFunction* fn = JS_ValueToFunction( cx, argv[0] ); g_Scheduler.pushInterval( first, interval, fn, JS_GetScopeChain( cx ) ); return( JS_TRUE ); } default: JS_ReportError( cx, "Invalid timer script" ); return( JS_TRUE ); } } // Cause all periodic functions registered via setInterval to // no longer be called. // params: // returns: // notes: // - Execution continues until the end of the triggered function or // script fragment, but is not triggered again. JSBool cancelInterval( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_NO_PARAMS(cancelInterval); g_Scheduler.m_abortInterval = true; return( JS_TRUE ); } //----------------------------------------------------------------------------- // Game Setup //----------------------------------------------------------------------------- // Create a new network server object. // params: // returns: net server object JSBool createServer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval) { REQUIRE_NO_PARAMS(createServer); if( !g_Game ) g_Game = new CGame(); if( !g_NetServer ) g_NetServer = new CNetServer(g_Game, &g_GameAttributes); *rval = OBJECT_TO_JSVAL(g_NetServer->GetScript()); return( JS_TRUE ); } // Create a new network client object. // params: // returns: net client object JSBool createClient(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval) { REQUIRE_NO_PARAMS(createClient); if( !g_Game ) g_Game = new CGame(); if( !g_NetClient ) g_NetClient = new CNetClient(g_Game, &g_GameAttributes); *rval = OBJECT_TO_JSVAL(g_NetClient->GetScript()); return( JS_TRUE ); } // Begin the process of starting a game. // params: // returns: success [bool] // notes: // - Performs necessary initialization while calling back into the // main loop, so the game remains responsive to display+user input. // - When complete, the engine calls the reallyStartGame JS function. // TODO: Replace startGame with create(Game|Server|Client)/game.start() - // after merging CGame and CGameAttributes JSBool startGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval) { REQUIRE_NO_PARAMS(startGame); *rval = BOOLEAN_TO_JSVAL(JS_TRUE); // Hosted MP Game if (g_NetServer) *rval = BOOLEAN_TO_JSVAL(g_NetServer->StartGame() == 0); // Joined MP Game: startGame is invalid - do nothing else if (g_NetClient) { } // Start an SP Game Session else if (!g_Game) { g_Game = new CGame(); PSRETURN ret = g_Game->StartGame(&g_GameAttributes); if (ret != PSRETURN_OK) { // Failed to start the game - destroy it, and return false delete g_Game; g_Game = NULL; *rval = BOOLEAN_TO_JSVAL(JS_FALSE); return( JS_TRUE ); } } return( JS_TRUE ); } // Immediately ends the current game (if any). // params: // returns: JSBool endGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval)) { REQUIRE_NO_PARAMS(endGame); EndGame(); return JS_TRUE; } //----------------------------------------------------------------------------- // Internationalization //----------------------------------------------------------------------------- // these remain here instead of in the i18n tree because they are // really related to the engine's use of them, as opposed to i18n itself. // contrariwise, translate() cannot be moved here because that would // make i18n dependent on this code and therefore harder to reuse. // Replaces the current language (locale) with a new one. // params: language id [string] as in I18n::LoadLanguage // returns: JSBool loadLanguage(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval)) { REQUIRE_PARAMS(1, loadLanguage); CStr lang = g_ScriptingHost.ValueToString(argv[0]); I18n::LoadLanguage(lang); return JS_TRUE; } // Return identifier of the current language (locale) in use. // params: // returns: language id [string] as in I18n::LoadLanguage JSBool getLanguageID(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval) { REQUIRE_NO_PARAMS(getLanguageID); *rval = JSVAL_NULL; JSString* s = JS_NewStringCopyZ(cx, I18n::CurrentLanguageName()); if (!s) { JS_ReportError(cx, "Error creating string"); return JS_FALSE; } *rval = STRING_TO_JSVAL(s); return JS_TRUE; } //----------------------------------------------------------------------------- // Debug //----------------------------------------------------------------------------- // Deliberately cause the game to crash. // params: // returns: // notes: // - currently implemented via access violation (read of address 0) // - useful for testing the crashlog/stack trace code. JSBool crash(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval)) { REQUIRE_NO_PARAMS(crash); MICROLOG(L"Crashing at user's request."); return *(JSBool*)0; } // Force a JS GC (garbage collection) cycle to take place immediately. // params: // returns: true [bool] // notes: // - writes an indication of how long this took to the console. JSBool forceGC( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ) { REQUIRE_NO_PARAMS(forceGC); double time = get_time(); JS_GC( cx ); time = get_time() - time; g_Console->InsertMessage( L"Garbage collection completed in: %f", time ); *rval = JSVAL_TRUE; return( JS_TRUE ); } //----------------------------------------------------------------------------- // GUI //----------------------------------------------------------------------------- // Returns the sort-of-global object associated with the current GUI. // params: // returns: global object // notes: // - Useful for accessing an object from another scope. JSBool getGUIGlobal( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { REQUIRE_NO_PARAMS(getGUIGlobal); *rval = OBJECT_TO_JSVAL( g_GUI.GetScriptObject() ); return( JS_TRUE ); } //----------------------------------------------------------------------------- // Misc. Engine Interface //----------------------------------------------------------------------------- // Return the global frames-per-second value. // params: // returns: FPS [int] // notes: // - This value is recalculated once a frame. We take special care to // filter it, so it is both accurate and free of jitter. JSBool getFPS( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { REQUIRE_NO_PARAMS(getFPS); *rval = INT_TO_JSVAL(fps); return JS_TRUE; } // Cause the game to exit gracefully. // params: // returns: // notes: // - Exit happens after the current main loop iteration ends // (since this only sets a flag telling it to end) JSBool exitProgram( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_NO_PARAMS(exitProgram); kill_mainloop(); return JS_TRUE; } // Write an indication of total/available video RAM to console. // params: // returns: // notes: // - Not supported on all platforms. // - Only a rough approximation; do not base low-level decisions // ("should I allocate one more texture?") on this. JSBool vmem( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_NO_PARAMS(vmem); #ifdef _WIN32 int left, total; if (GetVRAMInfo(left, total)) g_Console->InsertMessage(L"VRAM: used %d, total %d, free %d", total-left, total, left); else g_Console->InsertMessage(L"VRAM: failed to detect"); #else g_Console->InsertMessage(L"VRAM: [not available on non-Windows]"); #endif return JS_TRUE; } // Change the mouse cursor. // params: cursor name [string] (i.e. basename of definition file and texture) // returns: // notes: // - Cursors are stored in "art\textures\cursors" JSBool setCursor( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_PARAMS(1, setCursor); g_CursorName = g_ScriptingHost.ValueToString(argv[0]); return JS_TRUE; } // Trigger a rewrite of all maps. // params: // returns: // notes: // - Usefulness is unclear. If you need it, consider renaming this and updating the docs. JSBool _rewriteMaps( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_NO_PARAMS(_rewriteMaps); CMapWriter::RewriteAllMaps(g_Game->GetWorld()->GetTerrain(), g_Game->GetWorld()->GetUnitManager(), &g_LightEnv); return JS_TRUE; } // Change the LOD bias. // params: LOD bias [float] // returns: // notes: // - value is as required by GL_TEXTURE_LOD_BIAS. // - useful for adjusting image "sharpness" (since it affects which mipmap level is chosen) JSBool _lodbias( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) ) { REQUIRE_PARAMS(1, _lodbias); g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, (float)g_ScriptingHost.ValueToDouble(argv[0])); return JS_TRUE; } // Focus the game camera on a given position. // params: target position vector [CVector3D] // returns: success [bool] JSBool setCameraTarget( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ) { REQUIRE_PARAMS(1, setCameraTarget); *rval = JSVAL_NULL; CVector3D* target; if( !( target = ToNative( argv[0] ) ) ) { JS_ReportError( cx, "Invalid camera target" ); return( JS_TRUE ); } g_Game->GetView()->SetCameraTarget( *target ); *rval = JSVAL_TRUE; return( JS_TRUE ); } //----------------------------------------------------------------------------- // Miscellany //----------------------------------------------------------------------------- // Return the date/time at which the current executable was compiled. // params: none (-> "date time") OR // what to display [int]: 0 (-> "date"); 1 (-> "time") // returns: date and/or time [string] // notes: // - Displayed on main menu screen; tells non-programmers which auto-build // they are running. Could also be determined via .EXE file properties, // but that's a bit more trouble. // - To be exact, the date/time returned is when scriptglue.cpp was // last compiled; since the auto-build does full rebuilds, that is moot. JSBool buildTime( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { REQUIRE_MAX_PARAMS(1, buildTime); // buildTime( ) = "date time" // buildTime(0) = "date" // buildTime(1) = "time" JSString* s = JS_NewStringCopyZ(cx, argc && argv[0]==JSVAL_ONE ? __TIME__ : argc ? __DATE__ : __DATE__" "__TIME__ ); *rval = STRING_TO_JSVAL(s); return JS_TRUE; } // Return distance between 2 points. // params: 2 position vectors [CVector3D] // returns: Euclidean distance [float] JSBool v3dist( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ) { REQUIRE_PARAMS(2, v3dist); CVector3D* a = ToNative( argv[0] ); CVector3D* b = ToNative( argv[1] ); float dist = ( *a - *b ).GetLength(); *rval = ToJSVal( dist ); return( JS_TRUE ); } // Returns the global object. // params: // returns: global object // notes: // - Useful for accessing an object from another scope. JSBool getGlobal( JSContext* cx, JSObject* globalObject, uint argc, jsval* argv, jsval* rval ) { REQUIRE_NO_PARAMS(getGlobal); *rval = OBJECT_TO_JSVAL( globalObject ); return( JS_TRUE ); } //----------------------------------------------------------------------------- // function table //----------------------------------------------------------------------------- // the JS interpreter expects the table to contain 5-tuples as follows: // - name the function will be called as from script; // - function which will be called; // - number of arguments this function expects // - Flags (deprecated, always zero) // - Extra (reserved for future use, always zero) // // we simplify this a bit with a macro: #define JS_FUNC(script_name, cpp_function, min_params) { #script_name, cpp_function, min_params, 0, 0 }, JSFunctionSpec ScriptFunctionTable[] = { // Console JS_FUNC(writeConsole, JSI_Console::writeConsole, 1) // external // Entity JS_FUNC(getEntityByHandle, getEntityByHandle, 1) JS_FUNC(getEntityTemplate, getEntityTemplate, 1) JS_FUNC(issueCommand, issueCommand, 2) // Camera JS_FUNC(setCameraTarget, setCameraTarget, 1) // GUI #ifndef NO_GUI JS_FUNC(getGUIObjectByName, JSI_IGUIObject::getByName, 1) // external JS_FUNC(getGUIGlobal, getGUIGlobal, 0) #endif // Events JS_FUNC(addGlobalHandler, AddGlobalHandler, 2) JS_FUNC(removeGlobalHandler, RemoveGlobalHandler, 2) // Timer JS_FUNC(setTimeout, setTimeout, 2) JS_FUNC(setInterval, setInterval, 2) JS_FUNC(cancelInterval, cancelInterval, 0) // Game Setup JS_FUNC(startGame, startGame, 0) JS_FUNC(endGame, endGame, 0) JS_FUNC(createClient, createClient, 0) JS_FUNC(createServer, createServer, 0) // VFS (external) JS_FUNC(buildFileList, JSI_VFS::BuildFileList, 1) JS_FUNC(getFileMTime, JSI_VFS::GetFileMTime, 1) JS_FUNC(getFileSize, JSI_VFS::GetFileSize, 1) JS_FUNC(readFile, JSI_VFS::ReadFile, 1) JS_FUNC(readFileLines, JSI_VFS::ReadFileLines, 1) // Internationalization JS_FUNC(loadLanguage, loadLanguage, 1) JS_FUNC(getLanguageID, getLanguageID, 0) // note: i18n/ScriptInterface.cpp registers translate() itself. // rationale: see implementation section above. // Debug JS_FUNC(crash, crash, 0) JS_FUNC(forceGC, forceGC, 0) // Misc. Engine Interface JS_FUNC(writeLog, WriteLog, 1) JS_FUNC(exit, exitProgram, 0) JS_FUNC(vmem, vmem, 0) JS_FUNC(_rewriteMaps, _rewriteMaps, 0) JS_FUNC(_lodbias, _lodbias, 0) JS_FUNC(setCursor, setCursor, 1) JS_FUNC(getFPS, getFPS, 0) // Miscellany JS_FUNC(v3dist, v3dist, 2) JS_FUNC(buildTime, buildTime, 0) JS_FUNC(getGlobal, getGlobal, 0) // end of table marker {0, 0, 0, 0, 0} }; #undef JS_FUNC //----------------------------------------------------------------------------- // property accessors //----------------------------------------------------------------------------- JSBool GetEntitySet( JSContext* cx, JSObject*, jsval argv, jsval* vp ) { std::vector* extant = g_EntityManager.getExtant(); *vp = OBJECT_TO_JSVAL( EntityCollection::Create( *extant ) ); delete( extant ); return( JS_TRUE ); } JSBool GetPlayerSet( JSContext* cx, JSObject*, jsval id, jsval* vp ) { std::vector* players = g_Game->GetPlayers(); *vp = OBJECT_TO_JSVAL( PlayerCollection::Create( *players ) ); return( JS_TRUE ); } JSBool GetLocalPlayer( JSContext* cx, JSObject*, jsval id, jsval* vp ) { *vp = OBJECT_TO_JSVAL( g_Game->GetLocalPlayer()->GetScript() ); return( JS_TRUE ); } JSBool GetGaiaPlayer( JSContext* cx, JSObject*, jsval id, jsval* vp ) { *vp = OBJECT_TO_JSVAL( g_Game->GetPlayer( 0 )->GetScript() ); return( JS_TRUE ); } JSBool SetLocalPlayer( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { CPlayer* newLocalPlayer = ToNative( *vp ); if( !newLocalPlayer ) { JS_ReportError( cx, "Not a valid Player." ); return( JS_TRUE ); } g_Game->SetLocalPlayer( newLocalPlayer ); return( JS_TRUE ); } JSBool GetGameView( JSContext* cx, JSObject*, jsval id, jsval* vp ) { if (g_Game) *vp = OBJECT_TO_JSVAL( g_Game->GetView()->GetScript() ); else *vp = JSVAL_NULL; return( JS_TRUE ); } enum ScriptGlobalTinyIDs { GLOBAL_SELECTION, GLOBAL_GROUPSARRAY, GLOBAL_CAMERA, GLOBAL_CONSOLE, }; // shorthand #define PERM JSPROP_PERMANENT #define CONST JSPROP_READONLY JSPropertySpec ScriptGlobalTable[] = { { "selection" , GLOBAL_SELECTION, PERM, JSI_Selection::getSelection, JSI_Selection::setSelection }, { "groups" , GLOBAL_GROUPSARRAY, PERM, JSI_Selection::getGroups, JSI_Selection::setGroups }, { "camera" , GLOBAL_CAMERA, PERM, JSI_Camera::getCamera, JSI_Camera::setCamera }, { "console" , GLOBAL_CONSOLE, PERM | CONST, JSI_Console::getConsole, 0 }, { "entities" , 0, PERM | CONST, GetEntitySet, 0 }, { "players" , 0, PERM | CONST, GetPlayerSet, 0 }, { "localPlayer", 0, PERM , GetLocalPlayer, SetLocalPlayer }, { "gaiaPlayer" , 0, PERM | CONST, GetGaiaPlayer, 0 }, { "gameView" , 0, PERM | CONST, GetGameView, 0 }, // end of table marker { 0, 0, 0, 0, 0 }, };