#include #include #include #include #include "lua.h" #include "lauxlib.h" #include "llua.h" #include "lluadef.h" #include "hook.h" #include "mission.h" #include "log.h" #include "lephisto.h" #include "rng.h" #include "space.h" #include "toolkit.h" #include "land.h" #include "pilot.h" #include "player.h" #include "ltime.h" #include "xml.h" #include "misn_lua.h" // Similar to lua vars, but with less variety. #define MISN_VAR_NIL 0 #define MISN_VAR_NUM 1 #define MISN_VAR_BOOL 2 #define MISN_VAR_STR 3 typedef struct misn_var_ { char* name; char type; union { double num; char* str; int b; } d; } misn_var; // Variable stack. static misn_var* var_stack = NULL; static int var_nstack = 0; static int var_mstack = 0; // Current mission. static Mission* cur_mission = NULL; static int misn_delete = 0; // If 1 delete current mission. // Static. static int var_add(misn_var* var); static void var_free(misn_var* var); static unsigned int hook_generic(lua_State* L, char* stack); // Extern. int misn_run(Mission* misn, char* func); int var_save(xmlTextWriterPtr writer); int var_load(xmlNodePtr parent); // -- Libraries. -- // Mission. static int misn_setTitle(lua_State* L); static int misn_setDesc(lua_State* L); static int misn_setReward(lua_State* L); static int misn_factions(lua_State* L); static int misn_accept(lua_State* L); static int misn_finish(lua_State* L); static const luaL_reg misn_methods[] = { { "setTitle", misn_setTitle }, { "setDesc", misn_setDesc }, { "setReward", misn_setReward }, { "factions", misn_factions }, { "accept", misn_accept }, { "finish", misn_finish }, { 0, 0 } }; // Var. static int var_peek(lua_State* L); static int var_pop(lua_State* L); static int var_push(lua_State* L); static const luaL_reg var_methods[] = { { "peek", var_peek }, { "pop", var_pop}, { "push", var_push}, {0, 0} }; // Only conditional. static const luaL_reg var_cond_methods[] = { { "peek", var_peek }, {0, 0 } }; // Player. static int player_getname(lua_State* L); static int player_shipname(lua_State* L); static int player_freeSpace(lua_State* L); static int player_addCargo(lua_State* L); static int player_rmCargo(lua_State* L); static int player_pay(lua_State* L); static int player_msg(lua_State* L); static int player_modFaction(lua_State* L); static int player_getFaction(lua_State* L); static const luaL_reg player_methods[] = { { "name", player_getname }, { "ship", player_shipname }, { "freeCargo", player_freeSpace }, { "addCargo", player_addCargo }, { "rmCargo", player_rmCargo }, { "pay", player_pay }, { "msg", player_msg }, { "modFaction", player_modFaction }, { "getFaction", player_getFaction }, { 0, 0 } }; // Hooks. static int hook_land(lua_State* L); static int hook_takeoff(lua_State* L); static int hook_time(lua_State* L); static int hook_enter(lua_State* L); static int hook_pilotDeath(lua_State* L); static const luaL_reg hook_methods[] = { { "land", hook_land }, { "takeoff", hook_takeoff }, { "time", hook_time }, { "enter", hook_enter }, { "pilotDeath", hook_pilotDeath }, { 0, 0 } }; // Pilots. static int pilot_addFleet(lua_State* L); static int pilot_rename(lua_State* L); static const luaL_reg pilot_methods[] = { { "add", pilot_addFleet }, { "rename", pilot_rename }, { 0, 0 } }; // Register all the libaries. int misn_loadLibs(lua_State* L) { lua_loadLephisto(L); lua_loadMisn(L); lua_loadVar(L, 0); lua_loadSpace(L, 0); lua_loadTime(L, 0); lua_loadPlayer(L); lua_loadRnd(L); lua_loadTk(L); lua_loadHook(L); lua_loadPilot(L); return 0; } int misn_loadCondLibs(lua_State* L) { lua_loadTime(L, 1); lua_loadVar(L, 1); return 0; } // Individual libarary loading. int lua_loadMisn(lua_State* L) { luaL_register(L, "misn", misn_methods); return 0; } int lua_loadVar(lua_State* L, int readonly) { if(readonly == 0) luaL_register(L, "var", var_methods); else luaL_register(L, "var", var_cond_methods); return 0; } int lua_loadPlayer(lua_State* L) { luaL_register(L, "player", player_methods); return 0; } int lua_loadHook(lua_State* L) { luaL_register(L, "hook", hook_methods); return 0; } int lua_loadPilot(lua_State* L) { luaL_register(L, "pilot", pilot_methods); return 0; } // Run a mission function. // // -1 on error, 1 on misn.finish() call and 0 normally. int misn_run(Mission* misn, char* func) { int i, ret; char* err; cur_mission = misn; misn_delete = 0; lua_getglobal(misn->L, func); if((ret = lua_pcall(misn->L, 0, 0, 0))) { // Did an oops. err = (lua_isstring(misn->L, -1)) ? (char*) lua_tostring(misn->L, -1) : NULL; if(strcmp(err, "Mission Done")) WARN("Mission '%s' -> '%s' : %s", cur_mission->data->name, func, (err) ? err : "Unknown Error"); else ret = 1; } // Mission is finished. if(misn_delete) { mission_cleanup(cur_mission); for(i = 0; i < MISSION_MAX; i++) if(cur_mission == &player_missions[i]) { memmove(&player_missions[i], &player_missions[i+1], sizeof(Mission) * (MISSION_MAX-i-1)); break; } } cur_mission = NULL; return ret; } // Save the mission variables. int var_save(xmlTextWriterPtr writer) { int i; xmlw_startElem(writer, "vars"); for(i = 0; i < var_nstack; i++) { xmlw_startElem(writer, "vars"); xmlw_attr(writer, "name", var_stack[i].name); switch(var_stack[i].type) { case MISN_VAR_NIL: xmlw_attr(writer, "type", "nil"); break; case MISN_VAR_NUM: xmlw_attr(writer, "type", "num"); xmlw_str(writer, "%d", var_stack[i].d.num); break; case MISN_VAR_BOOL: xmlw_attr(writer, "type", "bool"); xmlw_str(writer, "%d", var_stack[i].d.b); break; case MISN_VAR_STR: xmlw_attr(writer, "type", "str"); xmlw_str(writer, var_stack[i].d.str); break; } xmlw_endElem(writer); // var. } xmlw_endElem(writer); // vars. return 0; } // Load the vars. int var_load(xmlNodePtr parent) { char* str; xmlNodePtr node, cur; misn_var var; var_cleanup(); node = parent->xmlChildrenNode; do { if(xml_isNode(node, "vars")) { cur = node->xmlChildrenNode; do { if(xml_isNode(cur, "var")) { xmlr_attr(cur, "name", var.name); xmlr_attr(cur, "type", str); if(strcmp(str, "nil")==0) var.type = MISN_VAR_NIL; else if(strcmp(str, "num")==0) { var.type = MISN_VAR_NUM; var.d.num = atoi(xml_get(cur)); } else if(strcmp(str, "bool")) { var.type = MISN_VAR_BOOL; var.d.b = atoi(xml_get(cur)); } else if(strcmp(str, "str")) { var.type = MISN_VAR_STR; var.d.str = atoi(xml_get(cur)); } else { // Supeh error checking. WARN("Unknown var type '%s'", str); free(var.name); continue; } free(str); var_add(&var); } } while(xml_nextNode(cur)); } } while(xml_nextNode(node)); return 0; } // Add a var to the stack, strings will be SHARED, don't free!!! static int var_add(misn_var* new_var) { int i; if(var_nstack+1 > var_mstack) { // More memory. var_mstack += 64; // Overkill much?? var_stack = realloc(var_stack, var_mstack * sizeof(misn_var)); } // Check if already exists. for(i = 0; i < var_nstack; i++) if(strcmp(new_var->name, var_stack[i].name)==0) { var_free(&var_stack[i]); memcpy(&var_stack[i], new_var, sizeof(misn_var)); return 0; } memcpy(&var_stack[var_nstack], new_var, sizeof(misn_var)); var_nstack++; return 0; } // -- Mission. -- static int misn_setTitle(lua_State* L) { LLUA_MIN_ARGS(1); if(lua_isstring(L, -1)) { if(cur_mission->title) // Cleanup the old title. free(cur_mission->title); cur_mission->title = strdup((char*)lua_tostring(L, -1)); } return 0; } static int misn_setDesc(lua_State* L) { LLUA_MIN_ARGS(1); if(lua_isstring(L, -1)) { if(cur_mission->desc) // Cleanup the old description. free(cur_mission->desc); cur_mission->desc = strdup((char*)lua_tostring(L, -1)); } return 0; } static int misn_setReward(lua_State* L) { LLUA_MIN_ARGS(1); if(lua_isstring(L, -1)) { if(cur_mission->reward) // Cleanup the old reward. free(cur_mission->reward); cur_mission->reward = strdup((char*)lua_tostring(L, -1)); } return 0; } static int misn_factions(lua_State* L) { int i; MissionData* dat; dat = cur_mission->data; // We'll push all the factions in table form. lua_newtable(L); for(i = 0; i < dat->avail.nfactions; i++) { lua_pushnumber(L, i+1); // Index, starts with 1. lua_pushnumber(L, dat->avail.factions[i]); // Value. lua_rawset(L, -3); // Store the value in the table. } return 1; } static int misn_accept(lua_State* L) { int i, ret; ret = 0; // Find the last mission. for(i = 0; i < MISSION_MAX; i++) if(player_missions[i].data == NULL) break; // No missions left. if(i >= MISSION_MAX) ret = 1; else { memcpy(&player_missions[i], cur_mission, sizeof(Mission)); memset(cur_mission, 0, sizeof(Mission)); cur_mission = &player_missions[i]; } lua_pushboolean(L, !ret); // We'll convert C style return to lua. return 1; } static int misn_finish(lua_State* L) { int b; if(lua_isboolean(L, -1)) b = lua_toboolean(L, -1); else { lua_pushstring(L, "Mission Done"); lua_error(L); // THERE IS NO RETURN! return 0; } misn_delete = 1; if(b && mis_isFlag(cur_mission->data, MISSION_UNIQUE)) player_missionFinished(mission_getID(cur_mission->data->name)); lua_pushstring(L, "Mission Done"); lua_error(L); // Should not return.. return 0; } // -- Var. -- // Check if a variable exists. int var_checkflag(char* str) { int i; for(i = 0; i < var_nstack; i++) if(strcmp(var_stack[i].name, str)==0) return 1; return 0; } static int var_peek(lua_State* L) { LLUA_MIN_ARGS(1); int i; char* str; if(lua_isstring(L, -1)) str = (char*) lua_tostring(L, -1); else { LLUA_DEBUG("Trying to peek a var with non-string name"); return 0; } for(i = 0; i < var_nstack; i++) if(strcmp(str, var_stack[i].name)==0) { switch(var_stack[i].type) { case MISN_VAR_NIL: lua_pushnil(L); break; case MISN_VAR_NUM: lua_pushnumber(L, var_stack[i].d.num); break; case MISN_VAR_BOOL: lua_pushboolean(L, var_stack[i].d.b); break; case MISN_VAR_STR: lua_pushstring(L, var_stack[i].d.str); break; } return 1; } lua_pushnil(L); return 1; } static int var_pop(lua_State* L) { LLUA_MIN_ARGS(1); int i; char* str; if(lua_isstring(L, -1)) str = (char*) lua_tostring(L, -1); else { LLUA_DEBUG("Trying to pop a var with non-string name"); return 0; } for(i = 0; i < var_nstack; i++) if(strcmp(str, var_stack[i].name)==0) { var_free(&var_stack[i]); memmove(&var_stack[i], &var_stack[i+1], sizeof(misn_var)*(var_nstack-i-1)); var_stack--; return 0; } LLUA_DEBUG("Var '%s' not found in stack", str); return 0; } static int var_push(lua_State* L) { LLUA_MIN_ARGS(2); char* str; misn_var var; if(lua_isstring(L, -2)) str = (char*) lua_tostring(L, -2); else { LLUA_DEBUG("Trying to push a var with non-string name"); return 0; } var.name = strdup(str); // Store appropriate data. if(lua_isnil(L, -1)) var.type = MISN_VAR_NIL; else if(lua_isnumber(L, -1)) { var.type = MISN_VAR_NUM; var.d.num = (double)lua_tonumber(L, -1); } else if(lua_isboolean(L, -1)) { var.type = MISN_VAR_BOOL; var.d.b = lua_toboolean(L, -1); } else if(lua_isstring(L, -1)) { var.type = MISN_VAR_STR; var.d.str = strdup((char*)lua_tostring(L, -1)); } else { LLUA_DEBUG("Trying to push a var of invalid data type to stack"); return 0; } var_add(&var); return 0; } static void var_free(misn_var* var) { switch(var->type) { case MISN_VAR_STR: if(var->d.str != NULL) { free(var->d.str); var->d.str = NULL; } break; case MISN_VAR_NIL: case MISN_VAR_NUM: case MISN_VAR_BOOL: break; } if(var->name != NULL) { free(var->name); var->name = NULL; } } void var_cleanup(void) { int i; for(i = 0; i < var_nstack; i++) var_free(&var_stack[i]); if(var_stack != NULL) free(var_stack); var_stack = NULL; var_nstack = 0; var_mstack = 0; } // -- Player. -- static int player_getname(lua_State* L) { lua_pushstring(L, player_name); return 1; } static int player_shipname(lua_State* L) { lua_pushstring(L, player->name); return 1; } static int player_freeSpace(lua_State* L) { lua_pushnumber(L, pilot_cargoFree(player)); return 1; } static int player_addCargo(lua_State* L) { Commodity* cargo; int quantity, ret; LLUA_MIN_ARGS(2); if(lua_isstring(L, -2)) cargo = commodity_get((char*) lua_tostring(L, -2)); else return 0; if(lua_isnumber(L, -1)) quantity = (int)lua_tonumber(L, -1); else return 0; ret = pilot_addMissionCargo(player, cargo, quantity); mission_linkCargo(cur_mission, ret); lua_pushnumber(L, ret); return 1; } static int player_rmCargo(lua_State* L) { int ret; unsigned int id; LLUA_MIN_ARGS(1); if(lua_isnumber(L, -1)) id = (unsigned int) lua_tonumber(L, -1); else return 0; ret = pilot_rmMissionCargo(player, id); mission_unlinkCargo(cur_mission, id); lua_pushboolean(L, !ret); return 1; } static int player_pay(lua_State* L) { int money; LLUA_MIN_ARGS(1); if(lua_isnumber(L, -1)) money = (int) lua_tonumber(L, -1); else return 0; player->credits += money; return 0; } static int player_msg(lua_State* L) { LLUA_MIN_ARGS(1); char* str; if(lua_isstring(L, -1)) str = (char*) lua_tostring(L, -1); else return 0; player_message(str); return 0; } static int player_modFaction(lua_State* L) { LLUA_MIN_ARGS(2); int f, mod; if(lua_isstring(L, -2)) f = faction_get(lua_tostring(L, -2)); else LLUA_INVALID_PARAMETER(); if(lua_isnumber(L, -1)) mod = (int)lua_tonumber(L, -1); else LLUA_INVALID_PARAMETER(); faction_modPlayer(f, mod); return 0; } static int player_getFaction(lua_State* L) { LLUA_MIN_ARGS(1); int f; if(lua_isstring(L, -1)) f = faction_get(lua_tostring(L, -1)); else LLUA_INVALID_PARAMETER(); lua_pushnumber(L, faction_getPlayer(f)); return 1; } // -- HOOK -- static unsigned int hook_generic(lua_State* L, char* stack) { int i; char* func; LLUA_MIN_ARGS(1); // Make sure mission is a player mission. for(i = 0; i < MISSION_MAX; i++) if(player_missions[i].id == cur_mission->id) break; if(i >= MISSION_MAX) { WARN("Mission not in stack trying to hook"); return 0; } if(lua_isstring(L, -1)) func = (char*)lua_tostring(L, -1); else { WARN("Mission '%s': trying to push non-valid function hook", cur_mission->data->name); return 0; } return hook_add(cur_mission->id, func, stack); } static int hook_land(lua_State* L) { hook_generic(L, "land"); return 0; } static int hook_takeoff(lua_State* L) { hook_generic(L, "takeoff"); return 0; } static int hook_time(lua_State* L) { hook_generic(L, "time"); return 0; } static int hook_enter(lua_State* L) { hook_generic(L, "enter"); return 0; } static int hook_pilotDeath(lua_State* L) { LLUA_MIN_ARGS(2); unsigned int h, p; if(lua_isnumber(L, -2)) p = (unsigned int) lua_tonumber(L, -2); else LLUA_INVALID_PARAMETER(); h = hook_generic(L, "death"); // We won't actually call the death stack directly. pilot_addHook(pilot_get(p), PILOT_HOOK_DEATH, h); return 0; } // -- Pilot. -- static int pilot_addFleet(lua_State* L) { LLUA_MIN_ARGS(1); Fleet* flt; char* fltname; int i, j; unsigned int p; double a; Vec2 vv, vp, vn; if(lua_isstring(L, -1)) fltname = (char*) lua_tostring(L, -1); else LLUA_INVALID_PARAMETER(); // Pull the fleet. flt = fleet_get(fltname); if(flt == NULL) { LLUA_DEBUG("Fleet not found!"); return 0; } // This should probably be done better.. vect_pset(&vp, RNG(MIN_HYPERSPACE_DIST, MIN_HYPERSPACE_DIST*1.5), RNG(0, 360)*M_PI/180.); vectnull(&vn); // Now we start adding pilots and toss ids into the table we return. j = 0; for(i = 0; i < flt->npilots; i++) { if(RNG(0, 100) <= flt->pilots[i].chance) { // Fleet displacement. vect_cadd(&vp, RNG(75, 150) & (RNG(0, 1) ? 1: -1), RNG(75, 150) * (RNG(0, 1) ? 1 : -1)); a = vect_angle(&vp, &vn); vectnull(&vv); p = pilot_create(flt->pilots[i].ship, flt->pilots[i].name, flt->faction, flt->ai, a, &vp, &vv, 0); // We push each pilot created into a table and return it. lua_pushnumber(L, ++j); // Index start with 1. lua_pushnumber(L, p); // value = pilot id. lua_rawset(L, -3); // Store the value in the table. } } return 1; } static int pilot_rename(lua_State* L) { LLUA_MIN_ARGS(2); char* name; unsigned int id; Pilot* p; if(lua_isnumber(L, -2)) id = (unsigned int) lua_tonumber(L, -2); else LLUA_INVALID_PARAMETER(); if(lua_isstring(L, -1)) name = (char*) lua_tostring(L, -1); else LLUA_INVALID_PARAMETER(); p = pilot_get(id); free(p->name); p->name = strdup(name); return 0; }