#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); /* External. */ extern void mission_sysMark(void); /* -- 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_setMarker(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 }, { "setMarker", misn_setMarker }, { "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 != NULL) /* Cleanup old reward. */ /* Cleanup the old reward. */ free(cur_mission->reward); cur_mission->reward = strdup((char*)lua_tostring(L, 1)); } else LLUA_INVALID_PARAMETER(); return 0; } static int misn_setMarker(lua_State* L) { if(lua_isstring(L, 1)) { if(cur_mission->sys_marker != NULL) /* Cleanup old markers. */ free(cur_mission->sys_marker); cur_mission->sys_marker = strdup((char*)lua_tostring(L, 1)); #ifdef DEBUG if(system_get(cur_mission->sys_marker)==NULL) LLUA_DEBUG("Marking unexistant system '%s'", cur_mission->sys_marker); #endif mission_sysMark(); } else if(cur_mission->sys_marker != NULL) /* No parameter nullifies. */ free(cur_mission->sys_marker); 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; }