#include "ai/ai.h" #include "ai/aigoals.h" #include "asteroid/asteroid.h" #include "camera/camera.h" #include "cfile/cfilesystem.h" #include "cmdline/cmdline.h" #include "cutscene/movie.h" #include "debris/debris.h" #include "freespace.h" #include "gamesequence/gamesequence.h" #include "globalincs/linklist.h" #include "graphics/2d.h" #include "graphics/font.h" #include "graphics/generic.h" #include "graphics/opengl/gropenglpostprocessing.h" #include "headtracking/headtracking.h" #include "hud/hudbrackets.h" #include "hud/hudconfig.h" #include "hud/hudescort.h" #include "hud/hudets.h" #include "hud/hudgauges.h" #include "hud/hudshield.h" #include "iff_defs/iff_defs.h" #include "io/joy.h" #include "io/key.h" #include "io/mouse.h" #include "io/timer.h" #include "jumpnode/jumpnode.h" #include "lighting/lighting.h" #include "menuui/credits.h" #include "mission/missioncampaign.h" #include "mission/missiongoals.h" #include "mission/missionload.h" #include "mission/missionlog.h" #include "mission/missionmessage.h" #include "mission/missiontraining.h" #include "missionui/missionbrief.h" #include "model/model.h" #include "network/multi.h" #include "network/multimsgs.h" #include "object/objectshield.h" #include "object/waypoint.h" #include "parse/parselo.h" #include "scripting/scripting.h" #include "particle/particle.h" #include "playerman/player.h" #include "render/3d.h" #include "render/3dinternal.h" #include "scripting/ade_api.h" #include "ship/ship.h" #include "ship/shipfx.h" #include "ship/shiphit.h" #include "sound/audiostr.h" #include "sound/ds.h" #include "weapon/beam.h" #include "weapon/weapon.h" #define BMPMAN_INTERNAL #include "bmpman/bm_internal.h" #include "scripting/api/bitops.h" #include "scripting/api/enums.h" #include "scripting/api/vecmath.h" #include "scripting/api/event.h" #include "scripting/api/file.h" #include "scripting/api/font.h" #include "scripting/api/gameevent.h" #include "scripting/api/gamestate.h" #include "scripting/api/hudgauge.h" #include "scripting/api/eye.h" #include "scripting/api/model.h" #include "scripting/api/physics_info.h" #include "scripting/api/sexpvar.h" #include "scripting/api/shields.h" #include "scripting/api/shiptype.h" #include "scripting/api/species.h" #include "scripting/api/team.h" #include "scripting/api/streaminganim.h" #include "scripting/api/texture.h" #include "scripting/api/texturemap.h" #include "scripting/api/weaponclass.h" #include "scripting/api/mc_info.h" #include "scripting/api/object.h" #include "scripting/api/asteroid.h" #include "scripting/api/cockpit_display.h" #include "scripting/api/shipclass.h" #include "scripting/api/debris.h" #include "scripting/api/waypoint.h" #include "scripting/api/ship_bank.h" #include "scripting/api/subsystem.h" #include "scripting/api/order.h" #include "scripting/api/ship.h" #include "scripting/api/sound.h" #include "scripting/api/message.h" #include "scripting/api/wing.h" #include "scripting/api/beam.h" #include "scripting/api/player.h" #include "scripting/api/camera.h" #include "scripting/api/control_info.h" #include "scripting/api/particle.h" #include "scripting/api/audio.h" #include "scripting/api/base.h" #include "scripting/api/cfile.h" #include "scripting/api/weapon.h" #include "scripting/api/controls.h" #include "scripting/api/graphics.h" #include "scripting/api/hud.h" using namespace scripting; using namespace scripting::api; //**********Handles /*ade_obj l_Camera("camera", "Camera handle"); ade_obj l_Cmission("cmission", "Campaign mission handle"); //WMC - We can get away with a pointer right now, but if it ever goes dynamic, it'd be a prob ade_obj l_Enum("enumeration", "Enumeration object"); ade_obj l_Event("event", "Mission event handle"); ade_obj l_Font("font", "font handle"); ade_obj l_Matrix("orientation", "Orientation matrix object"); ade_obj l_Model("model", "3D Model (POF) handle"); ade_obj l_Object("object", "Object handle"); ade_obj l_Physics("physics", "Physics handle"); ade_obj l_Player("player", "Player handle"); ade_obj l_Shields("shields", "Shields handle"); ade_obj l_Ship("ship", "Ship handle", &l_Object); ade_obj l_Shipclass("shipclass", "Ship class handle"); ade_obj l_ShipTextures("shiptextures", "Ship textures handle"); ade_obj l_Shiptype("shiptype", "Ship type handle"); ade_obj l_Species("species", "Species handle"); ade_obj l_Subsystem("subsystem", "Ship subsystem handle"); ade_obj l_Team("team", "Team handle"); ade_obj l_Texture("texture", "Texture handle"); ade_obj l_Wing("wing", "Wing handle"); ade_obj l_Vector("vector", "Vector object"); ade_obj l_Weapon("weapon", "Weapon handle", &l_Object); ade_obj l_WeaponBank("weaponbank", "Ship/subystem weapons bank handle"); ade_obj l_WeaponBankType("weaponbanktype", "Ship/subsystem weapons bank type handle"); ade_obj l_Weaponclass("weaponclass", "Weapon class handle"); */ //########################################################### //################################################ //########################################################### //If you are a coder who wants to add libraries, functions, //or objects to Lua, then you want to be below this point. //########################################################### //############################################### //########################################################### //**********LIBRARY: Scripting Variables ade_lib l_HookVar("HookVariables", NULL, "hv", "Hook variables repository"); //WMC: IMPORTANT //Be very careful when modifying this library, as the Globals[] library does depend //on the current number of items in the library. If you add _anything_, modify __len. //Or run changes by me. //*****LIBRARY: Scripting Variables ade_lib l_HookVar_Globals("Globals", &l_HookVar); ADE_INDEXER(l_HookVar_Globals, "number Index", "Array of current HookVariable names", "string", "Hookvariable name, or empty string if invalid index specified") { int idx; if(!ade_get_args(L, "*i", &idx)) return ade_set_error(L, "s", ""); //Get lib lua_getglobal(L, l_HookVar.GetName()); int lib_ldx = lua_gettop(L); if(!lua_isuserdata(L, lib_ldx)) { lua_pop(L, 1); return ade_set_error(L, "s", ""); } //Get metatable lua_getmetatable(L, lib_ldx); int mtb_ldx = lua_gettop(L); if(!lua_istable(L, mtb_ldx)) { lua_pop(L, 2); return ade_set_error(L, "s", ""); } //Get ade members table lua_pushstring(L, "__ademembers"); lua_rawget(L, mtb_ldx); int amt_ldx = lua_gettop(L); if(!lua_istable(L, amt_ldx)) { lua_pop(L, 3); return ade_set_error(L, "s", ""); } //List 'em char *keyname = NULL; int count = 1; lua_pushnil(L); while(lua_next(L, amt_ldx)) { //Now on stack: Key, value lua_pushvalue(L, -2); keyname = (char *)lua_tostring(L, -1); if(strcmp(keyname, "Globals")) { if(count == idx) { //lib, mtb, amt, key, value, string go bye-bye lua_pop(L, 5); return ade_set_args(L, "s", keyname); } count++; } lua_pop(L, 2); //Value, string } lua_pop(L, 3); //lib, mtb, amt return ade_set_error(L, "s", ""); } ADE_FUNC(__len, l_HookVar_Globals, NULL, "Number of HookVariables", "number", "Number of HookVariables") { //Get metatable lua_getglobal(L, l_HookVar.GetName()); int lib_ldx = lua_gettop(L); if(!lua_isuserdata(L, lib_ldx)) { lua_pop(L, 1); return ade_set_error(L, "i", 0); } lua_getmetatable(L, lib_ldx); int mtb_ldx = lua_gettop(L); if(!lua_istable(L, mtb_ldx)) { lua_pop(L, 2); return ade_set_error(L, "i", 0); } //Get ade members table lua_pushstring(L, "__ademembers"); lua_rawget(L, mtb_ldx); int amt_ldx = lua_gettop(L); if(!lua_istable(L, amt_ldx)) { lua_pop(L, 3); return ade_set_error(L, "i", 0); } int total_len = 0; lua_pushnil(L); while(lua_next(L, amt_ldx)) { total_len++; lua_pop(L, 1); //value } size_t num_sub = ade_manager::getInstance()->getEntry(l_HookVar.GetIdx()).Num_subentries; lua_pop(L, 3); //WMC - Return length, minus the 'Globals' library return ade_set_args(L, "i", (int)(total_len - num_sub)); } //**********LIBRARY: Mission ade_lib l_Mission("Mission", NULL, "mn", "Mission library"); // for use in creating faster metadata systems, use in conjunction with getSignature() ADE_FUNC(getObjectFromSignature, l_Mission, "number Signature", "Gets a handle of an object from its signature", "object", "Handle of object with signaure, invalid handle if signature is not in use") { int sig = -1; int objnum; if(!ade_get_args(L, "i", &sig)) return ade_set_error(L, "o", l_Object.Set(object_h())); if (sig == -1) { return ade_set_error(L, "o", l_Object.Set(object_h())); } objnum = obj_get_by_signature(sig); return ade_set_object_with_breed(L, objnum); } ADE_FUNC(evaluateSEXP, l_Mission, "string", "Runs the defined SEXP script", "boolean", "if the operation was successful") { char *s; int r_val; if(!ade_get_args(L, "s", &s)) return ADE_RETURN_FALSE; r_val = run_sexp(s); if (r_val == SEXP_TRUE) return ADE_RETURN_TRUE; else return ADE_RETURN_FALSE; } ADE_FUNC(runSEXP, l_Mission, "string", "Runs the defined SEXP script", "boolean", "if the operation was successful") { char *s; int r_val; char buf[8192]; if(!ade_get_args(L, "s", &s)) return ADE_RETURN_FALSE; while (is_white_space(*s)) s++; if (*s != '(') { static bool Warned_about_runSEXP_parentheses = false; if (!Warned_about_runSEXP_parentheses) { Warned_about_runSEXP_parentheses = true; Warning(LOCATION, "Invalid SEXP syntax: SEXPs must be surrounded by parentheses. For backwards compatibility, the string has been enclosed in parentheses. This may not be correct in all use cases."); } // this is the old sexp handling method, which is incorrect snprintf(buf, 8191, "( when ( true ) ( %s ) )", s); } else { // this is correct usage snprintf(buf, 8191, "( when ( true ) %s )", s); } r_val = run_sexp(buf); if (r_val == SEXP_TRUE) return ADE_RETURN_TRUE; else return ADE_RETURN_FALSE; } //****SUBLIBRARY: Mission/Asteroids ade_lib l_Mission_Asteroids("Asteroids", &l_Mission, NULL, "Asteroids in the mission"); ADE_INDEXER(l_Mission_Asteroids, "number Index", "Gets asteroid", "asteroid", "Asteroid handle, or invalid handle if invalid index specified") { int idx = -1; if( !ade_get_args(L, "*i", &idx) ) { return ade_set_error( L, "o", l_Asteroid.Set( object_h() ) ); } if( idx > -1 && idx < asteroid_count() ) { idx--; //Convert from Lua to C, as lua indices start from 1, not 0 return ade_set_args( L, "o", l_Asteroid.Set( object_h( &Objects[Asteroids[idx].objnum] ), Objects[Asteroids[idx].objnum].signature ) ); } return ade_set_error(L, "o", l_Asteroid.Set( object_h() ) ); } ADE_FUNC(__len, l_Mission_Asteroids, NULL, "Number of asteroids in mission. Note that the value returned is only good until an asteroid is destroyed, and so cannot be relied on for more than one frame.", "number", "Number of asteroids in the mission, or 0 if asteroids are not enabled") { if(Asteroids_enabled) { return ade_set_args(L, "i", asteroid_count()); } return ade_set_args(L, "i", 0); } //****SUBLIBRARY: Mission/Debris ade_lib l_Mission_Debris("Debris", &l_Mission, NULL, "debris in the mission"); ADE_INDEXER(l_Mission_Debris, "number Index", "Array of debris in the current mission", "debris", "Debris handle, or invalid debris handle if index wasn't valid") { int idx = -1; if( !ade_get_args( L, "*i", &idx ) ) { return ade_set_error(L, "o", l_Debris.Set(object_h())); } if( idx > -1 && idx < Num_debris_pieces ) { idx--; // Lua -> C if (Debris[idx].objnum == -1) //Somehow accessed an invalid debris piece return ade_set_error(L, "o", l_Debris.Set(object_h())); return ade_set_args(L, "o", l_Debris.Set(object_h(&Objects[Debris[idx].objnum]), Objects[Debris[idx].objnum].signature)); } return ade_set_error(L, "o", l_Debris.Set(object_h())); } ADE_FUNC(__len, l_Mission_Debris, NULL, "Number of debris pieces in the mission. " "Note that the value returned is only good until a piece of debris is destroyed, and so cannot be relied on for more than one frame.", "number", "Current number of debris particles") { return ade_set_args(L, "i", Num_debris_pieces); } //****SUBLIBRARY: Mission/EscortShips ade_lib l_Mission_EscortShips("EscortShips", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_EscortShips, "number Index", "Gets escort ship at specified index on escort list", "ship", "Specified ship, or invalid ship handle if invalid index") { int idx; if(!ade_get_args(L, "*i", &idx)) return ade_set_error(L, "o", l_Ship.Set(object_h())); if(idx < 1 || idx > hud_escort_num_ships_on_list()) return ade_set_error(L, "o", l_Ship.Set(object_h())); //Lua->FS2 idx--; idx = hud_escort_return_objnum(idx); if(idx < 0) return ade_set_error(L, "o", l_Ship.Set(object_h())); return ade_set_args(L, "o", l_Ship.Set(object_h(&Objects[idx]))); } ADE_FUNC(__len, l_Mission_EscortShips, NULL, "Current number of escort ships", "number", "Current number of escort ships") { return ade_set_args(L, "i", hud_escort_num_ships_on_list()); } //****SUBLIBRARY: Mission/Events ade_lib l_Mission_Events("Events", &l_Mission, NULL, "Events"); ADE_INDEXER(l_Mission_Events, "number Index/string Name", "Indexes events list", "event", "Event handle, or invalid event handle if index was invalid") { char *s; if(!ade_get_args(L, "*s", &s)) return ade_set_error(L, "o", l_Event.Set(-1)); int i; for(i = 0; i < Num_mission_events; i++) { if(!stricmp(Mission_events[i].name, s)) return ade_set_args(L, "o", l_Event.Set(i)); } //Now try as a number i = atoi(s); if(i < 1 || i > Num_mission_events) return ade_set_error(L, "o", l_Event.Set(-1)); //Lua-->FS2 i--; return ade_set_args(L, "o", l_Event.Set(i)); } ADE_FUNC(__len, l_Mission_Events, NULL, "Number of events in mission", "number", "Number of events in mission") { return ade_set_args(L, "i", Num_mission_events); } //****SUBLIBRARY: Mission/SEXPVariables ade_lib l_Mission_SEXPVariables("SEXPVariables", &l_Mission, NULL, "SEXP Variables"); ADE_INDEXER(l_Mission_SEXPVariables, "number Index/string Name", "Array of SEXP variables. Note that you can set a sexp variable using the array, eg \'SEXPVariables[\"newvariable\"] = \"newvalue\"\'", "sexpvariable", "Handle to SEXP variable, or invalid sexpvariable handle if index was invalid") { char *name = NULL; char *newval = NULL; if(!ade_get_args(L, "*s|s", &name, &newval)) return ade_set_error(L, "o", l_SEXPVariable.Set(sexpvar_h())); int idx = get_index_sexp_variable_name(name); if(idx < 0) { idx = atoi(name); //Lua-->FS2 idx--; } if(idx < 0 || idx >= MAX_SEXP_VARIABLES) { if(ADE_SETTING_VAR && newval != NULL) { idx = sexp_add_variable(newval, name, lua_type(L, 2) == LUA_TNUMBER ? SEXP_VARIABLE_NUMBER : SEXP_VARIABLE_STRING); } //We have failed. if(idx < 0) { return ade_set_error(L, "o", l_SEXPVariable.Set(sexpvar_h())); } } else { if(ADE_SETTING_VAR && newval != NULL) { sexp_modify_variable(newval, idx, false); } } return ade_set_args(L, "o", l_SEXPVariable.Set(sexpvar_h(idx))); } ADE_FUNC(__len, l_Mission_SEXPVariables, NULL, "Current number of SEXP variables", "number", "Counts number of loaded SEXP Variables. May be slow.") { return ade_set_args(L, "i", sexp_variable_count()); } //****SUBLIBRARY: Mission/Ships ade_lib l_Mission_Ships("Ships", &l_Mission, NULL, "Ships in the mission"); ADE_INDEXER(l_Mission_Ships, "number Index/string Name", "Gets ship", "ship", "Ship handle, or invalid ship handle if index was invalid") { char *name; if(!ade_get_args(L, "*s", &name)) return ade_set_error(L, "o", l_Ship.Set(object_h())); int idx = ship_name_lookup(name); if(idx > -1) { return ade_set_args(L, "o", l_Ship.Set(object_h(&Objects[Ships[idx].objnum]), Objects[Ships[idx].objnum].signature)); } else { idx = atoi(name); if(idx > 0) { int count=1; for(int i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum < 0 || Objects[Ships[i].objnum].type != OBJ_SHIP) continue; if(count == idx) { return ade_set_args(L, "o", l_Ship.Set(object_h(&Objects[Ships[i].objnum]), Objects[Ships[i].objnum].signature)); } count++; } } } return ade_set_error(L, "o", l_Ship.Set(object_h())); } extern int ships_inited; ADE_FUNC(__len, l_Mission_Ships, NULL, "Number of ships in the mission. " "This function is somewhat slow, and should be set to a variable for use in looping situations. " "Note that the value returned is only good until a ship is destroyed, and so cannot be relied on for more than one frame.", "number", "Number of ships in the mission, or 0 if ships haven't been initialized yet") { if(ships_inited) return ade_set_args(L, "i", ship_get_num_ships()); else return ade_set_args(L, "i", 0); } //****SUBLIBRARY: Mission/Waypoints ade_lib l_Mission_Waypoints("Waypoints", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Waypoints, "number Index", "Array of waypoints in the current mission", "waypoint", "Waypoint handle, or invalid waypoint handle if index was invalid") { int idx; if(!ade_get_args(L, "*i", &idx)) return ade_set_error(L, "o", l_Waypoint.Set(object_h())); //Remember, Lua indices start at 0. int count=0; object *ptr = GET_FIRST(&obj_used_list); while (ptr != END_OF_LIST(&obj_used_list)) { if (ptr->type == OBJ_WAYPOINT) count++; if(count == idx) { return ade_set_args(L, "o", l_Waypoint.Set(object_h(ptr))); } ptr = GET_NEXT(ptr); } return ade_set_error(L, "o", l_Weapon.Set(object_h())); } ADE_FUNC(__len, l_Mission_Waypoints, NULL, "Gets number of waypoints in mission. Note that this is only accurate for one frame.", "number", "Number of waypoints in the mission") { uint count=0; for(uint i = 0; i < MAX_OBJECTS; i++) { if (Objects[i].type == OBJ_WAYPOINT) count++; } return ade_set_args(L, "i", count); } //****SUBLIBRARY: Mission/WaypointLists ade_lib l_Mission_WaypointLists("WaypointLists", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_WaypointLists, "number Index/string WaypointListName", "Array of waypoint lists", "waypointlist", "Gets waypointlist handle") { waypointlist_h wpl; char *name; if(!ade_get_args(L, "*s", &name)) return ade_set_error(L, "o", l_WaypointList.Set(waypointlist_h())); wpl = waypointlist_h(name); if (!wpl.IsValid()) { int idx = atoi(name) - 1; wpl = waypointlist_h(find_waypoint_list_at_index(idx)); } if (wpl.IsValid()) { return ade_set_args(L, "o", l_WaypointList.Set(wpl)); } return ade_set_error(L, "o", l_WaypointList.Set(waypointlist_h())); } ADE_FUNC(__len, l_Mission_WaypointLists, NULL, "Number of waypoint lists in mission. Note that this is only accurate for one frame.", "number", "Number of waypoint lists in the mission") { return ade_set_args(L, "i", Waypoint_lists.size()); } //****SUBLIBRARY: Mission/Weapons ade_lib l_Mission_Weapons("Weapons", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Weapons, "number Index", "Gets handle to a weapon object in the mission.", "weapon", "Weapon handle, or invalid weapon handle if index is invalid") { int idx; if(!ade_get_args(L, "*i", &idx)) return ade_set_error(L, "o", l_Weapon.Set(object_h())); //Remember, Lua indices start at 0. int count=1; for(int i = 0; i < MAX_WEAPONS; i++) { if (Weapons[i].weapon_info_index < 0 || Weapons[i].objnum < 0 || Objects[Weapons[i].objnum].type != OBJ_WEAPON) continue; if(count == idx) { return ade_set_args(L, "o", l_Weapon.Set(object_h(&Objects[Weapons[i].objnum]))); } count++; } return ade_set_error(L, "o", l_Weapon.Set(object_h())); } ADE_FUNC(__len, l_Mission_Weapons, NULL, "Number of weapon objects in mission. Note that this is only accurate for one frame.", "number", "Number of weapon objects in mission") { return ade_set_args(L, "i", Num_weapons); } //****SUBLIBRARY: Mission/Beams ade_lib l_Mission_Beams("Beams", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Beams, "number Index", "Gets handle to a beam object in the mission.", "beam", "Beam handle, or invalid beam handle if index is invalid") { int idx; if(!ade_get_args(L, "*i", &idx)) return ade_set_error(L, "o", l_Beam.Set(object_h())); //Remember, Lua indices start at 0. int count=1; for(int i = 0; i < MAX_BEAMS; i++) { if (Beams[i].weapon_info_index < 0 || Beams[i].objnum < 0 || Objects[Beams[i].objnum].type != OBJ_BEAM) continue; if(count == idx) { return ade_set_args(L, "o", l_Beam.Set(object_h(&Objects[Beams[i].objnum]))); } count++; } return ade_set_error(L, "o", l_Beam.Set(object_h())); } ADE_FUNC(__len, l_Mission_Beams, NULL, "Number of beam objects in mission. Note that this is only accurate for one frame.", "number", "Number of beam objects in mission") { return ade_set_args(L, "i", Beam_count); } //****SUBLIBRARY: Campaign ade_lib l_Campaign("Campaign", NULL, "ca", "Campaign Library"); ADE_FUNC(getNextMissionFilename, l_Campaign, NULL, "Gets next mission filename", "string", "Next mission filename, or nil if the next mission is invalid") { if (Campaign.next_mission < 0 || Campaign.next_mission >= MAX_CAMPAIGN_MISSIONS) { return ADE_RETURN_NIL; } return ade_set_args(L, "s", Campaign.missions[Campaign.next_mission].name); } ADE_FUNC(getPrevMissionFilename, l_Campaign, NULL, "Gets previous mission filename", "string", "Previous mission filename, or nil if the previous mission is invalid") { if (Campaign.prev_mission < 0 || Campaign.prev_mission >= MAX_CAMPAIGN_MISSIONS) { return ADE_RETURN_NIL; } return ade_set_args(L, "s", Campaign.missions[Campaign.prev_mission].name); } // DahBlount - This jumps to a mission, the reason it accepts a boolean value is so that players can return to campaign maps ADE_FUNC(jumpToMission, l_Campaign, "string filename, [boolean hub]", "Jumps to a mission based on the filename. Optionally, the player can be sent to a hub mission without setting missions to skipped.", "boolean", "Jumps to a mission, or returns nil.") { char *filename = NULL;; bool hub = false; if (!ade_get_args(L, "s|b", &filename, &hub)) return ADE_RETURN_NIL; mission_campaign_jump_to_mission(filename, hub); return ADE_RETURN_TRUE; } // TODO: add a proper indexer type that returns a handle // something like ca.Mission[filename/index] //****SUBLIBRARY: Mission/Wings ade_lib l_Mission_Wings("Wings", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Wings, "number Index/string WingName", "Wings in the mission", "wing", "Wing handle, or invalid wing handle if index or name was invalid") { char *name; if(!ade_get_args(L, "*s", &name)) return ade_set_error(L, "o", l_Wing.Set(-1)); //MageKing17 - Make the count-ignoring version of the lookup and leave checking if the wing has any ships to the scripter int idx = wing_lookup(name); if(idx < 0) { idx = atoi(name); if(idx < 1 || idx > Num_wings) return ade_set_error(L, "o", l_Wing.Set(-1)); idx--; //Lua->FS2 } return ade_set_args(L, "o", l_Wing.Set(idx)); } ADE_FUNC(__len, l_Mission_Wings, NULL, "Number of wings in mission", "number", "Number of wings in mission") { return ade_set_args(L, "i", Num_wings); } //****SUBLIBRARY: Mission/Teams ade_lib l_Mission_Teams("Teams", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Teams, "number Index/string TeamName", "Teams in the mission", "team", "Team handle or invalid team handle if the requested team could not be found") { char *name; if(!ade_get_args(L, "*s", &name)) return ade_set_error(L, "o", l_Team.Set(-1)); int idx = iff_lookup(name); if(idx < 0) { idx = atoi(name); idx--; //Lua->FS2 } if(idx < 0 || idx >= Num_iffs) return ade_set_error(L, "o", l_Team.Set(-1)); return ade_set_args(L, "o", l_Team.Set(idx)); } ADE_FUNC(__len, l_Mission_Teams, NULL, "Number of teams in mission", "number", "Number of teams in mission") { return ade_set_args(L, "i", Num_iffs); } //****SUBLIBRARY: Mission/Messages ade_lib l_Mission_Messages("Messages", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Messages, "number Index/string messageName", "Messages of the mission", "message", "Message handle or invalid handle on error") { int idx = -1; if (lua_isnumber(L, 2)) { if (!ade_get_args(L, "*i", &idx)) return ade_set_args(L, "o", l_Message.Set(-1)); idx--; // Lua --> FS2 idx += Num_builtin_messages; } else { char* name = NULL; if (!ade_get_args(L, "*s", &name)) return ade_set_args(L, "o", l_Message.Set(-1)); if (name == NULL) return ade_set_args(L, "o", l_Message.Set(-1)); for (int i = Num_builtin_messages; i < (int) Messages.size(); i++) { if (!stricmp(Messages[i].name, name)) { idx = i; break; } } } if (idx < Num_builtin_messages || idx >= (int) Messages.size()) return ade_set_args(L, "o", l_Message.Set(-1)); else return ade_set_args(L, "o", l_Message.Set(idx)); } ADE_FUNC(__len, l_Mission_Messages, NULL, "Number of messages in the mission", "number", "Number of messages in mission") { return ade_set_args(L, "i", (int) Messages.size() - Num_builtin_messages); } //****SUBLIBRARY: Mission/BuiltinMessages ade_lib l_Mission_BuiltinMessages("BuiltinMessages", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_BuiltinMessages, "number Index/string messageName", "Built-in messages of the mission", "message", "Message handle or invalid handle on error") { int idx = -1; if (lua_isnumber(L, 2)) { if (!ade_get_args(L, "*i", &idx)) return ade_set_args(L, "o", l_Message.Set(-1)); idx--; // Lua --> FS2 } else { char* name = NULL; if (!ade_get_args(L, "*s", &name)) return ade_set_args(L, "o", l_Message.Set(-1)); if (name == NULL) return ade_set_args(L, "o", l_Message.Set(-1)); for (int i = 0; i < Num_builtin_messages; i++) { if (!stricmp(Messages[i].name, name)) { idx = i; break; } } } if (idx < 0 || idx >= Num_builtin_messages) return ade_set_args(L, "o", l_Message.Set(-1)); else return ade_set_args(L, "o", l_Message.Set(idx)); } ADE_FUNC(__len, l_Mission_BuiltinMessages, NULL, "Number of built-in messages in the mission", "number", "Number of messages in mission") { return ade_set_args(L, "i", Num_builtin_messages); } //****SUBLIBRARY: Mission/Personas ade_lib l_Mission_Personas("Personas", &l_Mission, NULL, NULL); ADE_INDEXER(l_Mission_Personas, "number Index/string name", "Personas of the mission", "persona", "Persona handle or invalid handle on error") { int idx = -1; if (lua_isnumber(L, 2)) { if (!ade_get_args(L, "*i", &idx)) return ade_set_args(L, "o", l_Persona.Set(-1)); idx--; // Lua --> FS2 } else { char* name = NULL; if (!ade_get_args(L, "*s", &name)) return ade_set_args(L, "o", l_Persona.Set(-1)); if (name == NULL) return ade_set_args(L, "o", l_Persona.Set(-1)); idx = message_persona_name_lookup(name); } if (idx < 0 || idx >= Num_personas) return ade_set_args(L, "o", l_Persona.Set(-1)); else return ade_set_args(L, "o", l_Persona.Set(idx)); } ADE_FUNC(__len, l_Mission_Personas, NULL, "Number of personas in the mission", "number", "Number of messages in mission") { return ade_set_args(L, "i", Num_personas); } ADE_FUNC(addMessage, l_Mission, "string name, string text[, persona persona]", "Adds a message", "message", "The new message or invalid handle on error") { char* name = NULL; char* text = NULL; int personaIdx = -1; if (!ade_get_args(L, "ss|o", &name, &text, l_Persona.Get(&personaIdx))) return ade_set_error(L, "o", l_Message.Set(-1)); if (name == NULL || text == NULL) return ade_set_error(L, "o", l_Message.Set(-1)); if (personaIdx < 0 || personaIdx >= Num_personas) personaIdx = -1; add_message(name, text, personaIdx, 0); return ade_set_error(L, "o", l_Message.Set((int) Messages.size() - 1)); } ADE_FUNC(sendMessage, l_Mission, "string sender, message message[, number delay=0.0[, enumeration priority = MESSAGE_PRIORITY_NORMAL[, boolean fromCommand = false]]]", "Sends a message from the given source (not from a ship!) with the given priority or optionally sends it from the missions command source.
" "If delay is specified the message will be delayed by the specified time in seconds
" "If you pass nil as the sender then the message will not have a sender.", "boolean", "true if successfull, false otherwise") { char* sender = NULL; int messageIdx = -1; int priority = MESSAGE_PRIORITY_NORMAL; bool fromCommand = false; int messageSource = MESSAGE_SOURCE_SPECIAL; float delay = 0.0f; enum_h* ehp = NULL; // if first is nil then use no source if (lua_isnil(L, 1)) { if (!ade_get_args(L, "*o|fob", l_Message.Get(&messageIdx), &delay, l_Enum.GetPtr(&ehp), &fromCommand)) return ADE_RETURN_FALSE; messageSource = MESSAGE_SOURCE_NONE; } else { if (!ade_get_args(L, "so|fob", &sender, l_Message.Get(&messageIdx), &delay, l_Enum.GetPtr(&ehp), &fromCommand)) return ADE_RETURN_FALSE; if (sender == NULL) return ADE_RETURN_FALSE; } if (fromCommand) messageSource = MESSAGE_SOURCE_COMMAND; if (messageIdx < 0 || messageIdx >= (int) Messages.size()) return ADE_RETURN_FALSE; if (messageIdx < Num_builtin_messages) { LuaError(L, "Cannot send built-in messages!"); return ADE_RETURN_FALSE; } if (delay < 0.0f) { LuaError(L, "Invalid negative delay of %f!", delay); return ADE_RETURN_FALSE; } if (ehp != NULL) { switch(ehp->index) { case LE_MESSAGE_PRIORITY_HIGH: priority = MESSAGE_PRIORITY_HIGH; break; case LE_MESSAGE_PRIORITY_NORMAL: priority = MESSAGE_PRIORITY_NORMAL; break; case LE_MESSAGE_PRIORITY_LOW: priority = MESSAGE_PRIORITY_LOW; break; default: LuaError(L, "Invalid enumeration used! Must be one of MESSAGE_PRIORITY_*."); return ADE_RETURN_FALSE; } } if (messageSource == MESSAGE_SOURCE_NONE) message_send_unique_to_player(Messages[messageIdx].name, NULL, MESSAGE_SOURCE_NONE, priority, 0, fl2i(delay * 1000.0f)); else message_send_unique_to_player(Messages[messageIdx].name, (void*) sender, messageSource, priority, 0, fl2i(delay * 1000.0f)); return ADE_RETURN_TRUE; } ADE_FUNC(sendTrainingMessage, l_Mission, "message message, number time[, number delay=0.0]", "Sends a training message to the player. time is the amount in seconds to display the message, only whole seconds are used!", "boolean", "true if successfull, false otherwise") { int messageIdx = -1; float delay = 0.0f; int time = -1; if (!ade_get_args(L, "oi|f", l_Message.Get(&messageIdx), &time, &delay)) return ADE_RETURN_FALSE; if (messageIdx < 0 || messageIdx >= (int) Messages.size()) return ADE_RETURN_FALSE; if (delay < 0.0f) { LuaError(L, "Got invalid delay of %f seconds!", delay); return ADE_RETURN_FALSE; } if (time < 0) { LuaError(L, "Got invalid time of %d seconds!", time); return ADE_RETURN_FALSE; } message_training_queue(Messages[messageIdx].name, timestamp(fl2i(delay * 1000.0f)), time); return ADE_RETURN_TRUE; } ADE_FUNC(createShip, l_Mission, "[string Name, shipclass Class=Shipclass[1], orientation Orientation=null, vector Position={0,0,0}]", "Creates a ship and returns a handle to it using the specified name, class, world orientation, and world position", "ship", "Ship handle, or invalid ship handle if ship couldn't be created") { char *name = NULL; int sclass = -1; matrix_h *orient = NULL; vec3d pos = vmd_zero_vector; ade_get_args(L, "|sooo", &name, l_Shipclass.Get(&sclass), l_Matrix.GetPtr(&orient), l_Vector.Get(&pos)); matrix *real_orient = &vmd_identity_matrix; if(orient != NULL) { real_orient = orient->GetMatrix(); } int obj_idx = ship_create(real_orient, &pos, sclass, name); if(obj_idx > -1) { model_page_in_textures(Ship_info[sclass].model_num, sclass); return ade_set_args(L, "o", l_Ship.Set(object_h(&Objects[obj_idx]), Objects[obj_idx].signature)); } else return ade_set_error(L, "o", l_Ship.Set(object_h())); } ADE_FUNC(createWaypoint, l_Mission, "[vector Position, waypointlist List]", "Creates a waypoint", "waypoint", "Waypoint handle, or invalid waypoint handle if waypoint couldn't be created") { vec3d *v3 = NULL; waypointlist_h *wlh = NULL; if(!ade_get_args(L, "|oo", l_Vector.GetPtr(&v3), l_WaypointList.GetPtr(&wlh))) return ade_set_error(L, "o", l_Waypoint.Set(object_h())); // determine where we need to create it - it looks like we were given a waypoint list but not a waypoint itself int waypoint_instance = -1; if (wlh && wlh->IsValid()) { int wp_list_index = find_index_of_waypoint_list(wlh->wlp); int wp_index = (int) wlh->wlp->get_waypoints().size() - 1; waypoint_instance = calc_waypoint_instance(wp_list_index, wp_index); } int obj_idx = waypoint_add(v3 != NULL ? v3 : &vmd_zero_vector, waypoint_instance); if(obj_idx >= 0) return ade_set_args(L, "o", l_Waypoint.Set(object_h(&Objects[obj_idx]))); else return ade_set_args(L, "o", l_Waypoint.Set(object_h())); } ADE_FUNC(createWeapon, l_Mission, "[weaponclass Class=WeaponClass[1], orientation Orientation=null, world vector Position={0,0,0}, object Parent = nil, number Group = -1", "Creates a weapon and returns a handle to it. 'Group' is used for lighting grouping purposes;" " for example, quad lasers would only need to act as one light source.", "weapon", "Weapon handle, or invalid weapon handle if weapon couldn't be created.") { int wclass = -1; object_h *parent = NULL; int group = -1; matrix_h *orient = NULL; vec3d pos = vmd_zero_vector; ade_get_args(L, "|ooooi", l_Weaponclass.Get(&wclass), l_Matrix.GetPtr(&orient), l_Vector.Get(&pos), l_Object.GetPtr(&parent), &group); matrix *real_orient = &vmd_identity_matrix; if(orient != NULL) { real_orient = orient->GetMatrix(); } int parent_idx = (parent && parent->IsValid()) ? OBJ_INDEX(parent->objp) : -1; int obj_idx = weapon_create(&pos, real_orient, wclass, parent_idx, group); if(obj_idx > -1) return ade_set_args(L, "o", l_Weapon.Set(object_h(&Objects[obj_idx]), Objects[obj_idx].signature)); else return ade_set_error(L, "o", l_Weapon.Set(object_h())); } ADE_FUNC(getMissionFilename, l_Mission, NULL, "Gets mission filename", "string", "Mission filename, or empty string if game is not in a mission") { return ade_set_args(L, "s", Game_current_mission_filename); } ADE_FUNC(startMission, l_Mission, "[Filename or MISSION_* enumeration, Briefing = true]", "Starts the defined mission", "boolean", "True, or false if the function fails") { bool b = true; char s[MAX_FILENAME_LEN]; char *str = s; if(lua_isstring(L, 1)) { if (!ade_get_args(L, "s|b", &str, &b)) return ade_set_args(L, "b", false); } else { enum_h *e = NULL; if (!ade_get_args(L, "o|b", l_Enum.GetPtr(&e), &b)) return ade_set_args(L, "b", false); if (e->index == LE_MISSION_REPEAT) { if (Num_recent_missions > 0) { strncpy( s, Recent_missions[0], MAX_FILENAME_LEN ); } else { return ade_set_args(L, "b", false); } } else { return ade_set_args(L, "b", false); } } // no filename... bail if (str == NULL) return ade_set_args(L, "b", false); // if mission name has extension... it needs to be removed... char *file_ext; file_ext = strrchr(str, '.'); if (file_ext) *file_ext = 0; // game is in MP mode... or if the file does not exist... bail if ((Game_mode & GM_MULTIPLAYER) || (cf_exists_full(str, CF_TYPE_MISSIONS) != 0)) return ade_set_args(L, "b", false); // mission is already running... if (Game_mode & GM_IN_MISSION) { // TO DO... All the things needed if this function is called in any state of the game while mission is running. // most likely all require 'stricmp(str, Game_current_mission_filename)' to make sure missions arent mixed // but after that it might be possible to imprement method for jumping directly into already running // missions. return ade_set_args(L, "b", false); // if mission is not running } else { // due safety checks of the game_start_mission() function allow only main menu for now. if (gameseq_get_state(gameseq_get_depth()) == GS_STATE_MAIN_MENU) { strcpy_s( Game_current_mission_filename, str ); if (b == true) { // start mission - go via briefing screen gameseq_post_event(GS_EVENT_START_GAME); } else { // start mission - enter the game directly gameseq_post_event(GS_EVENT_START_GAME_QUICK); } return ade_set_args(L, "b", true); } } return ade_set_args(L, "b", false); } ADE_FUNC(getMissionTime, l_Mission, NULL, "Game time in seconds since the mission was started; is affected by time compression", "number", "Mission time (seconds), or 0 if game is not in a mission") { if(!(Game_mode & GM_IN_MISSION)) return ade_set_error(L, "f", 0.0f); return ade_set_args(L, "x", Missiontime); } //WMC - These are in freespace.cpp ADE_FUNC(loadMission, l_Mission, "Mission name", "Loads a mission", "boolean", "True if mission was loaded, otherwise false") { char *s; if(!ade_get_args(L, "s", &s)) return ade_set_error(L, "b", false); // clear post processing settings gr_post_process_set_defaults(); //NOW do the loading stuff game_stop_time(); get_mission_info(s, &The_mission, false); game_level_init(); if(mission_load(s) == -1) return ADE_RETURN_FALSE; game_post_level_init(); Game_mode |= GM_IN_MISSION; return ADE_RETURN_TRUE; } ADE_FUNC(unloadMission, l_Mission, NULL, "Stops the current mission and unloads it", NULL, NULL) { if(Game_mode & GM_IN_MISSION) { game_level_close(); Game_mode &= ~GM_IN_MISSION; strcpy_s(Game_current_mission_filename, ""); } return ADE_RETURN_NIL; } ADE_FUNC(simulateFrame, l_Mission, NULL, "Simulates mission frame", NULL, NULL) { game_update_missiontime(); game_simulation_frame(); return ADE_RETURN_TRUE; } ADE_FUNC(renderFrame, l_Mission, NULL, "Renders mission frame, but does not move anything", NULL, NULL) { camid cid = game_render_frame_setup(); game_render_frame( cid ); game_render_post_frame(); return ADE_RETURN_TRUE; } ADE_FUNC(applyShudder, l_Mission, "number time, number intesity", "Applies a shudder effects to the camera. Time is in seconds. Intensity specifies the shudder effect strength, the Maxim has a value of 1440.", "boolean", "true if successfull, false otherwise") { float time = -1.0f; float intensity = -1.0f; if (!ade_get_args(L, "ff", &time, &intensity)) return ADE_RETURN_FALSE; if (time < 0.0f || intensity < 0.0f) { LuaError(L, "Illegal shudder values given. Must be bigger than zero, got time of %f and intensity of %f.", time, intensity); return ADE_RETURN_FALSE; } int int_time = fl2i(time * 1000.0f); game_shudder_apply(int_time, intensity * 0.01f); return ADE_RETURN_TRUE; } ADE_FUNC(isInCampaign, l_Mission, NULL, "Get whether or not the current mission being played in a campaign (as opposed to the tech room's simulator)", "boolean", "true if in campaign, false if not") { bool b = false; if (Game_mode & GM_CAMPAIGN_MODE) { b = true; } return ade_set_args(L, "b", b); } //**********LIBRARY: Tables ade_lib l_Tables("Tables", NULL, "tb", "Tables library"); //*****SUBLIBRARY: Tables/ShipClasses ade_lib l_Tables_ShipClasses("ShipClasses", &l_Tables, NULL, NULL); ADE_INDEXER(l_Tables_ShipClasses, "number Index/string Name", "Array of ship classes", "shipclass", "Ship handle, or invalid ship handle if index is invalid") { if(!ships_inited) return ade_set_error(L, "o", l_Shipclass.Set(-1)); char *name; if(!ade_get_args(L, "*s", &name)) return ade_set_error(L, "o", l_Shipclass.Set(-1)); int idx = ship_info_lookup(name); if(idx < 0) { idx = atoi(name); if(idx < 1 || idx >= static_cast(Ship_info.size())) return ade_set_error(L, "o", l_Shipclass.Set(-1)); idx--; //Lua->FS2 } return ade_set_args(L, "o", l_Shipclass.Set(idx)); } ADE_FUNC(__len, l_Tables_ShipClasses, NULL, "Number of ship classes", "number", "Number of ship classes, or 0 if ship classes haven't been loaded yet") { if(!ships_inited) return ade_set_args(L, "i", 0); //No ships loaded...should be 0 return ade_set_args(L, "i", Ship_info.size()); } //*****SUBLIBRARY: Tables/WeaponClasses ade_lib l_Tables_WeaponClasses("WeaponClasses", &l_Tables, NULL, NULL); extern int Weapons_inited; ADE_INDEXER(l_Tables_WeaponClasses, "number Index/string WeaponName", "Array of weapon classes", "weapon", "Weapon class handle, or invalid weaponclass handle if index is invalid") { if(!Weapons_inited) return ade_set_error(L, "o", l_Weaponclass.Set(-1)); char *name; if(!ade_get_args(L, "*s", &name)) return 0; int idx = weapon_info_lookup(name); if(idx < 0) { idx = atoi(name); // atoi is good enough here, 0 is invalid anyway if (idx > 0) { idx--; // Lua --> C/C++ } else { return ade_set_args(L, "o", l_Weaponclass.Set(-1)); } } return ade_set_args(L, "o", l_Weaponclass.Set(idx)); } ADE_FUNC(__len, l_Tables_WeaponClasses, NULL, "Number of weapon classes", "number", "Number of weapon classes, or 0 if weapon classes haven't been loaded yet") { if(!Weapons_inited) return ade_set_args(L, "i", 0); return ade_set_args(L, "i", Num_weapon_types); } //*************************Testing stuff************************* //This section is for stuff that's considered experimental. ade_lib l_Testing("Testing", NULL, "ts", "Experimental or testing stuff"); ADE_FUNC(avdTest, l_Testing, NULL, "Test the AVD Physics code", NULL, NULL) { static bool initialized = false; static avd_movement avd; if(!initialized) { avd.setAVD(10.0f, 3.0f, 1.0f, 1.0f, 0.0f); initialized = true; } for(int i = 0; i < 3000; i++) { float Pc, Vc; avd.get((float)i/1000.0f, &Pc, &Vc); gr_set_color(0, 255, 0); gr_pixel(i/10, gr_screen.clip_bottom - (int)(Pc*10.0f), GR_RESIZE_NONE); gr_set_color(255, 0, 0); gr_pixel(i/10, gr_screen.clip_bottom - (int)(Vc*10.0f), GR_RESIZE_NONE); avd.get(&Pc, &Vc); gr_set_color(255, 255, 255); gr_pixel((timestamp()%3000)/10, gr_screen.clip_bottom - (int)(Pc*10.0f), GR_RESIZE_NONE); gr_pixel((timestamp()%3000)/10, gr_screen.clip_bottom - (int)(Vc*10.0f), GR_RESIZE_NONE); } return ADE_RETURN_NIL; } ADE_FUNC(createParticle, l_Testing, "vector Position, vector Velocity, number Lifetime, number Radius, enumeration Type, [number Tracer length=-1, boolean Reverse=false, texture Texture=Nil, object Attached Object=Nil]", "Creates a particle. Use PARTICLE_* enumerations for type." "Reverse reverse animation, if one is specified" "Attached object specifies object that Position will be (and always be) relative to.", "particle", "Handle to the created particle") { particle::particle_info pi; pi.type = particle::PARTICLE_DEBUG; pi.optional_data = -1; pi.attached_objnum = -1; pi.attached_sig = -1; pi.reverse = 0; // Need to consume tracer_length parameter but it isn't used anymore float temp; enum_h *type = NULL; bool rev=false; object_h *objh=NULL; if(!ade_get_args(L, "ooffo|fboo", l_Vector.Get(&pi.pos), l_Vector.Get(&pi.vel), &pi.lifetime, &pi.rad, l_Enum.GetPtr(&type), &temp, &rev, l_Texture.Get((int*)&pi.optional_data), l_Object.GetPtr(&objh))) return ADE_RETURN_NIL; if(type != NULL) { switch(type->index) { case LE_PARTICLE_DEBUG: pi.type = particle::PARTICLE_DEBUG; break; case LE_PARTICLE_FIRE: pi.type = particle::PARTICLE_FIRE; break; case LE_PARTICLE_SMOKE: pi.type = particle::PARTICLE_SMOKE; break; case LE_PARTICLE_SMOKE2: pi.type = particle::PARTICLE_SMOKE2; break; case LE_PARTICLE_BITMAP: if (pi.optional_data < 0) { LuaError(L, "Invalid texture specified for createParticle()!"); } pi.type = particle::PARTICLE_BITMAP; break; } } if(rev) pi.reverse = 0; if(objh != NULL && objh->IsValid()) { pi.attached_objnum = OBJ_INDEX(objh->objp); pi.attached_sig = objh->objp->signature; } particle::WeakParticlePtr p = particle::create(&pi); if (!p.expired()) return ade_set_args(L, "o", l_Particle.Set(particle_h(p))); else return ADE_RETURN_NIL; } ADE_FUNC(getStack, l_Testing, NULL, "Generates an ADE stackdump", "string", "Current Lua stack") { char buf[10240] = {'\0'}; ade_stackdump(L, buf); return ade_set_args(L, "s", buf); } ADE_FUNC(isCurrentPlayerMulti, l_Testing, NULL, "Returns whether current player is a multiplayer pilot or not.", "boolean", "Whether current player is a multiplayer pilot or not") { if(Player == NULL) return ade_set_error(L, "b", false); if(!(Player->flags & PLAYER_FLAGS_IS_MULTI)) return ADE_RETURN_FALSE; return ADE_RETURN_TRUE; } // Om_tracker_flag should already be set in FreeSpace.cpp, needed to determine if PXO is enabled from the registry extern int Om_tracker_flag; // needed for FS2OpenPXO config ADE_FUNC(isPXOEnabled, l_Testing, NULL, "Returns whether PXO is currently enabled in the configuration.", "boolean", "Whether PXO is enabled or not") { if(!(Om_tracker_flag)) return ADE_RETURN_FALSE; return ADE_RETURN_TRUE; } ADE_FUNC(playCutscene, l_Testing, NULL, "Forces a cutscene by the specified filename string to play. Should really only be used in a non-gameplay state (i.e. start of GS_STATE_BRIEFING) otherwise odd side effects may occur. Highly Experimental.", "string", NULL) { //This whole thing is a quick hack and can probably be done way better, but is currently functioning fine for my purposes. char *filename; if (!ade_get_args(L, "s", &filename)) return ADE_RETURN_FALSE; movie::play(filename); return ADE_RETURN_TRUE; } //########################################################### //################################################ //########################################################### //If you are a coder who wants to add libraries, functions, //or objects to Lua, then you want to be above this point. //########################################################### //############################################### //########################################################### // *************************Housekeeping************************* //WMC - The miraculous lines of code that make Lua debugging worth something. lua_Debug Ade_debug_info; char debug_stack[4][32]; void ade_debug_ret(lua_State *L, lua_Debug *ar) { Assert(L != NULL); Assert(ar != NULL); lua_getstack(L, 1, ar); lua_getinfo(L, "nSlu", ar); memcpy(&Ade_debug_info, ar, sizeof(lua_Debug)); int n; for (n = 0; n < 4; n++) { debug_stack[n][0] = '\0'; } for (n = 0; n < 4; n++) { if (lua_getstack(L,n+1, ar) == 0) break; lua_getinfo(L,"n", ar); if (ar->name == NULL) break; strcpy_s(debug_stack[n],ar->name); } } //WMC - because the behavior of the return keyword //was changed, I now have to use this in hooks. static int ade_return_hack(lua_State *L) { int i = 0; int num = lua_gettop(L); for(i = 0; i < num; i++) { lua_pushvalue(L, i+1); } return num; } static void *vm_lua_alloc(void*, void *ptr, size_t, size_t nsize) { if (nsize == 0) { vm_free(ptr); return NULL; } else { return vm_realloc(ptr, nsize); } } //Inits LUA //Note that "libraries" must end with a {NULL, NULL} //element int script_state::CreateLuaState() { mprintf(("LUA: Opening LUA state...\n")); lua_State *L = lua_newstate(vm_lua_alloc, nullptr); if(L == NULL) { Warning(LOCATION, "Could not initialize Lua"); return 0; } //*****INITIALIZE AUXILIARY LIBRARIES mprintf(("LUA: Initializing base Lua libraries...\n")); luaL_openlibs(L); //*****DISABLE DANGEROUS COMMANDS lua_pushstring(L, "os"); lua_rawget(L, LUA_GLOBALSINDEX); int os_ldx = lua_gettop(L); if(lua_istable(L, os_ldx)) { lua_pushstring(L, "execute"); lua_pushnil(L); lua_rawset(L, os_ldx); lua_pushstring(L, "remove"); lua_pushnil(L); lua_rawset(L, os_ldx); lua_pushstring(L, "rename"); lua_pushnil(L); lua_rawset(L, os_ldx); } lua_pop(L, 1); //os table //*****SET DEBUG HOOKS #ifndef NDEBUG lua_sethook(L, ade_debug_ret, LUA_MASKRET, 0); #endif //*****INITIALIZE ADE uint i; mprintf(("LUA: Beginning ADE initialization\n")); for(i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) { //WMC - Do only toplevel table entries, doi if(ade_manager::getInstance()->getEntry(i).ParentIdx == UINT_MAX) //WMC - oh hey, we're done with the meaty point in < 10 lines. ade_manager::getInstance()->getEntry(i).SetTable(L, LUA_GLOBALSINDEX, LUA_GLOBALSINDEX); //Oh the miracles of OOP. } //*****INITIALIZE RETURN HACK FUNCTION lua_pushstring(L, "ade_return_hack"); lua_pushboolean(L, 0); lua_pushcclosure(L, ade_return_hack, 2); lua_setglobal(L, "ade_return_hack"); //*****INITIALIZE ENUMERATION CONSTANTS mprintf(("ADE: Initializing enumeration constants...\n")); enum_h eh; for(i = 0; i < Num_enumerations; i++) { eh.index = Enumerations[i].def; eh.is_constant = true; ade_set_args(L, "o", l_Enum.Set(eh)); lua_setglobal(L, Enumerations[i].name); } //*****ASSIGN LUA SESSION mprintf(("ADE: Assigning Lua session...\n")); SetLuaSession(L); // (void)l_BitOps.GetName(); return 1; } void script_state::EndLuaFrame() { scripting::api::graphics_on_frame(); } void ade_output_toc(FILE *fp, ade_table_entry *ate) { Assert(fp != NULL); Assert(ate != NULL); //WMC - sanity checking if(ate->Name == NULL && ate->ShortName == NULL) { Warning(LOCATION, "Found ade_table_entry with no name or shortname"); return; } fputs("
", fp); if(ate->Name == NULL) { fprintf(fp, "%s", ate->ShortName, ate->ShortName); } else { fprintf(fp, "%s", ate->Name, ate->Name); if(ate->ShortName) fprintf(fp, " (%s)", ate->ShortName); } fputs("", fp); if(ate->Description) fprintf(fp, " - %s\n", ate->Description); fputs("
\n", fp); } static bool sort_table_entries(const ade_table_entry* left, const ade_table_entry* right) { const char* leftCmp = left->Name != nullptr ? left->Name : left->ShortName; const char* rightCmp = right->Name != nullptr ? right->Name : left->ShortName; if (leftCmp == nullptr) { return false; } if (rightCmp == nullptr) { return false; } SCP_string leftStr(leftCmp); std::transform(std::begin(leftStr), std::end(leftStr), std::begin(leftStr), ::tolower); SCP_string rightStr(rightCmp); std::transform(std::begin(rightStr), std::end(rightStr), std::begin(rightStr), ::tolower); return leftStr < rightStr; } static bool sort_doc_entries(const ade_table_entry* left, const ade_table_entry* right) { if (left->Instanced == right->Instanced) { // Same type -> compare as normal return sort_table_entries(left, right); } if (left->Instanced) { return true; } return false; } void script_state::OutputLuaMeta(FILE *fp) { uint i; ade_table_entry *ate; fputs("
\n", fp); SCP_vector table_entries; //***TOC: Libraries fputs("
Libraries
\n", fp); for(i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) { ate = &ade_manager::getInstance()->getEntry(i); if(ate->ParentIdx == UINT_MAX && ate->Type == 'o' && ate->Instanced) { table_entries.push_back(ate); } } std::sort(std::begin(table_entries), std::end(table_entries), sort_table_entries); for (auto entry : table_entries) { ade_output_toc(fp, entry); } table_entries.clear(); //***TOC: Objects fputs("
Types
\n", fp); for(i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) { ate = &ade_manager::getInstance()->getEntry(i); if(ate->ParentIdx == UINT_MAX && ate->Type == 'o' && !ate->Instanced) { table_entries.push_back(ate); } } std::sort(std::begin(table_entries), std::end(table_entries), sort_table_entries); for (auto entry : table_entries) { ade_output_toc(fp, entry); } table_entries.clear(); //***TOC: Enumerations fputs("
Enumerations
", fp); //***End TOC fputs("


", fp); //***Everything fputs("
\n", fp); for(i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) { ate = &ade_manager::getInstance()->getEntry(i); if(ate->ParentIdx == UINT_MAX) table_entries.push_back(ate); } std::sort(std::begin(table_entries), std::end(table_entries), sort_doc_entries); for (auto entry : table_entries) { entry->OutputMeta(fp); } table_entries.clear(); //***Enumerations fprintf(fp, "

Enumerations

"); for(i = 0; i < Num_enumerations; i++) { //WMC - This is in case we ever want to add descriptions to enums. //fprintf(fp, "
%s
%s
", Enumerations[i].name, Enumerations[i].desc); //WMC - Otherwise, just use this. fprintf(fp, "
%s
", Enumerations[i].name); } fputs("
\n", fp); //***End LUA fputs("\n", fp); }