/** * @file llua_space.c * * @brief Handle the Lua space bindings. * * These bindings control the planets and systems. */ #include "lauxlib.h" #include "lephisto.h" #include "llua.h" #include "lluadef.h" #include "llua_space.h" #include "llua_faction.h" #include "rng.h" #include "pilot.h" #include "player.h" #include "llua_pilot.h" /* From pilot.c */ extern Pilot** pilot_stack; extern int pilot_nstack; static int pilotL_createmetatable(lua_State* L); /* Pilots. */ static int pilot_getPlayer(lua_State* L); static int pilot_addFleet(lua_State* L); static int pilot_clear(lua_State* L); static int pilot_toggleSpawn(lua_State* L); static int pilot_getPilots(lua_State* L); static const luaL_reg pilot_methods[] = { { "player", pilot_getPlayer }, { "add", pilot_addFleet }, { "clear", pilot_clear }, { "toggleSpawn", pilot_toggleSpawn }, { "get", pilot_getPilots }, { 0, 0 } }; /**< Pilot lua methods. */ /* Pilot metatable methods. */ static int pilotL_eq(lua_State* L); static int pilotL_name(lua_State* L); static int pilotL_alive(lua_State* L); static int pilotL_rename(lua_State* L); static int pilotL_position(lua_State* L); static int pilotL_velocity(lua_State* L); static int pilotL_warp(lua_State* L); static int pilotL_broadcast(lua_State* L); static int pilotL_setFaction(lua_State* L); static int pilotL_setHostile(lua_State* L); static const luaL_reg pilotL_methods[] = { { "__eq", pilotL_eq }, { "name", pilotL_name }, { "alive", pilotL_alive }, { "rename", pilotL_rename }, { "pos", pilotL_position }, { "vel", pilotL_velocity }, { "warp", pilotL_warp }, { "broadcast", pilotL_broadcast }, { "setFaction", pilotL_setFaction }, { "setHostile", pilotL_setHostile }, { 0, 0 } }; /**< Pilot metatable methods. */ /** * @fn int lua_loadPilot(lua_State* L, int readonly) * * @brief Load the space library. * @param L State to load space library into. * @return 0 on success. */ int lua_loadPilot(lua_State* L, int readonly) { if(readonly) /* Nothing is read only. */ return 0; /* Register the functions. */ luaL_register(L, "pilot", pilot_methods); /* Register the metatables. */ pilotL_createmetatable(L); return 0; } /** * @fn static int pilotL_createmetatable(lua_State* L) * * @brief Registers the pilot metatable. * @param L Lua state to register metatable in. * @return 0 on success. */ static int pilotL_createmetatable(lua_State* L) { /* Create the metatable. */ luaL_newmetatable(L, PILOT_METATABLE); /* Create the access table. */ lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); /* Register the values. */ luaL_register(L, NULL, pilotL_methods); return 0; } /** * @defgroup PILOT Pilot Lua bindings. * * @brief Lua bindings to interact with pilots. * * @luamod pilot * Functions should be called like: * * @code * pilot.function(parameters) * @endcode */ /** * @defgroup META_PILOT Pilot Metatable * * @brief Represents a pilot in Lua. * * To call members of the metatable always use: * * @code * pilot:function(param) * @endcode */ /** * @fn LuaPilot* lua_topilot(lua_State* L, int ind) * * @brief Get pilot at index. * @param L Lua state to get pilot from. * @param ind Index position to find the pilot. * @return Pilot found at the index in the state. */ LuaPilot* lua_topilot(lua_State* L, int ind) { if(lua_isuserdata(L, ind)) { return (LuaPilot*)lua_touserdata(L, ind); } luaL_typerror(L, ind, PILOT_METATABLE); return NULL; } /** * @fn LuaPilot* lua_pushpilot(lua_State* L, LuaPilot pilot) * * @brief Push a pilot on the stack. * @param L Lua state to push pilot into. * @param pilot Pilot to push. * @return Newly pushed pilot. */ LuaPilot* lua_pushpilot(lua_State* L, LuaPilot pilot) { LuaPilot* p; p = (LuaPilot*)lua_newuserdata(L, sizeof(LuaPilot)); *p = pilot; luaL_getmetatable(L, PILOT_METATABLE); lua_setmetatable(L, -2); return p; } /** * @fn int lua_ispilot(lua_State* L, int ind) * * @brief Check to see if ind is a pilot. * @param L Lua state to check. * @param ind Index position to check. * @return 1 if ind is a pilot. */ int lua_ispilot(lua_State* L, int ind) { int ret; if(lua_getmetatable(L, ind)==0) return 0; lua_getfield(L, LUA_REGISTRYINDEX, PILOT_METATABLE); ret = 0; if(lua_rawequal(L, -1, -2)) /* Does it have the correct mt?*/ ret = 1; lua_pop(L, 2); /* Remove both metatables. */ return ret; } /** * @ingroup PILOT * * @brief Get the players pilot. * @return Pilot pointing to the player. * @luafunc player() */ static int pilot_getPlayer(lua_State* L) { LuaPilot lp; if(player == NULL) { lua_pushnil(L); return 1; } lp.pilot = player->id; lua_pushpilot(L, lp); return 1; } /** * @ingroup PILOT * * @brief Adds a fleet to the system. * @luaparam fleetname Name of the fleet to add. * @luaparam ai If set will override the standard fleet AI. "def" means use default. * @luaparam pos Position to create pilots around instead of choosing randomly. * @return Table populated with all the identifiers of the pilots created. * @luafunc add(fleetname, ai, pos) */ static int pilot_addFleet(lua_State* L) { LLUA_MIN_ARGS(1); Fleet* flt; char* fltname, *fltai; int i, j; unsigned int p; double a; double d; Vec2 vv, vp, vn; FleetPilot* plt; LuaPilot lp; LuaVector* lv; /* Parse first argument - Fleet name */ if(lua_isstring(L, 1)) fltname = (char*) lua_tostring(L, 1); else LLUA_INVALID_PARAMETER(); /* Parse second argument - Fleet IA override. */ if(lua_gettop(L) > 1) { if(lua_isstring(L, 2)) { fltai = (char*)lua_tostring(L, 2); if(strcmp(fltai, "def")==0) /* Check if set to default. */ fltai = NULL; } else LLUA_INVALID_PARAMETER(); } else fltai = NULL; /* Parse third argument - Position. */ if(lua_gettop(L) > 2) { if(lua_isvector(L,3)) lv = lua_tovector(L,3); else LLUA_INVALID_PARAMETER(); } else lv = NULL; /* Needed to determine angle. */ vectnull(&vn); /* Pull the fleet. */ flt = fleet_get(fltname); if(flt == NULL) { LLUA_DEBUG("Fleet not found!"); return 0; } /* Use position passed if possible. */ if(lv != NULL) vectcpy(&vp, &lv->vec); else { d = RNGF()*(HYPERSPACE_ENTER_MAX-HYPERSPACE_ENTER_MIN) + HYPERSPACE_ENTER_MIN; } j = 0; lua_newtable(L); for(i = 0; i < flt->npilots; i++) { plt = &flt->pilots[i]; if(RNG(0, 100) <= plt->chance) { /* Fleet displacement. */ vect_cadd(&vp, RNG(75, 150) * (RNG(0,1) ? 1 : -1), RNG(75, 150) * (RNG(0,1) ? 1 : -1)); /* Set velocity only if no position is set.. */ if(lv != NULL) { if(VMOD(lv->vec) > HYPERSPACE_ENTER_MIN*0.9) { a = vect_angle(&vp, &vn); vect_pset(&vv, HYPERSPACE_VEL, a); } else vectnull(&vv); } else { /* Enterting via hyperspace. */ a = vect_angle(&vp, &vn); vect_pset(&vv, HYPERSPACE_VEL, a); } /* Create the pilot. */ p = pilot_create(plt->ship, plt->name, flt->faction, (fltai != NULL) ? /* Lua AI override. */ fltai : (plt->ai != NULL) ? /* Pilot AI override */ plt->ai : flt->ai, a, &vp, &vv, 0); /* We push each pilot created into a table and return it. */ lua_pushnumber(L, ++j); /* Index, starts with 1. */ lp.pilot = p; lua_pushpilot(L, lp); /* value = LuaPilot */ lua_rawset(L, -3); /* Store the value in the table. */ } } return 1; } /** * @ingroup PILOT * @brief Clears the current system of pilots. Used for epic battles and such. * @luafunc clear() */ static int pilot_clear(lua_State* L) { (void) L; pilots_clean(); return 0; } /** * @ingroup PILOT * * @brief Disable or enable pilot spawning in the current system. If player jumps * the spawn is enabled again automatically. * @luaparam enable true enables spawn, false disables it. * @return The current spawn state. * @luafunc togglespawn(enable) */ static int pilot_toggleSpawn(lua_State* L) { /* Setting it directly. */ if((lua_gettop(L) > 0) && lua_isboolean(L, 1)) space_spawn = lua_toboolean(L, 1); /* Toggling. */ else space_spawn = !space_spawn; lua_pushboolean(L, space_spawn); return 1; } /** * @ingroup PILOT */ static int pilot_getPilots(lua_State* L) { int i, j, k; int* factions; int nfactions; LuaFaction* f; LuaPilot p; /* Check for belonging to faction. */ if(lua_istable(L, 1)) { /* get table length and preallocate. */ nfactions = (int)lua_objlen(L, 1); factions = malloc(sizeof(int) * nfactions); /* Load up the table. */ lua_pushnil(L); i = 0; while(lua_next(L, -2) != 0) { f = lua_tofaction(L, -1); factions[i++] = f->f; lua_pop(L, 1); } /* Now put all the matching pilots in a table. */ lua_newtable(L); k = 1; for(i = 0; i < pilot_nstack; i++) { for(j = 0; j < nfactions; j++) { if((pilot_stack[i]->faction == factions[j]) && !pilot_isDisabled(pilot_stack[i])) { lua_pushnumber(L, k++); /* key. */ p.pilot = pilot_stack[i]->id; lua_pushpilot(L, p); /* Value. */ lua_rawset(L, -3); /* table[key] = value */ break; /* Continue to next pilot. */ } } } /* Clean up. */ free(factions); } else LLUA_INVALID_PARAMETER(); return 1; } /** * @brief Check to see if pilot and p are the same. * @luaparam p Pilot to compare against. * @return true if they are the same. * @luafunc __eq(pilot) */ static int pilotL_eq(lua_State* L) { LLUA_MIN_ARGS(2); LuaPilot* p1, *p2; /* Get parameters. */ p1 = lua_topilot(L, 1); if(lua_ispilot(L, 2)) p2 = lua_topilot(L, 2); else LLUA_INVALID_PARAMETER(); /* Push result. */ lua_pushboolean(L, p1->pilot == p2->pilot); return 1; } /** * @ingroup META_PILOT * * @brief Get the pilots current name. * @return The current name of the pilot. * @luafunc name() */ static int pilotL_name(lua_State* L) { LLUA_MIN_ARGS(1); LuaPilot* p1; Pilot* p; /* Parse parameters. */ p1 = lua_topilot(L, 1); p = pilot_get(p1->pilot); /* Pilot must exist. */ if(p == NULL) return 0; /* Get name. */ lua_pushstring(L, p->name); return 1; } /** * @ingroup META_PILOT * * @brief Checks to see if pilot is still alive. * @return true if pilot is still alive. * @luafunc alive() */ static int pilotL_alive(lua_State* L) { LLUA_MIN_ARGS(1); LuaPilot* lp; Pilot* p; /* Parse parameters. */ lp = lua_topilot(L, 1); p = pilot_get(lp->pilot); /* Check if is alive. */ lua_pushboolean(L, p != NULL); return 1; } /** * @brief Changes the pilots name. * @param name Name to change to. * @luafunc rename(name) */ static int pilotL_rename(lua_State* L) { LLUA_MIN_ARGS(2); LuaPilot* p1; char* name; Pilot* p; /* Parse parameters. */ p1 = lua_topilot(L, 1); p = pilot_get(p1->pilot); if(lua_isstring(L, 2)) name = (char*)lua_tostring(L, 2); else LLUA_INVALID_PARAMETER(); /* Pilot must exist. */ if(p == NULL) return 0; /* Change name. */ if(p->name != NULL) free(p->name); p->name = strdup(name); return 0; } /** * @brief Get the pilots position. * @return The pilots current position. * @luafunc pos() */ static int pilotL_position(lua_State* L) { LLUA_MIN_ARGS(1); LuaPilot* p1; Pilot* p; LuaVector v; /* Parse parameters */ p1 = lua_topilot(L, 1); p = pilot_get(p1->pilot); /* Pilot must exist. */ if(p == NULL) return 0; /* Push position. */ vectcpy(&v.vec, &p->solid->pos); lua_pushvector(L, v); return 1; } /** * @fn static int pilotL_velocity(lua_State* L) * @ingroup META_PILOT * * @brief Gets the pilots velocity. * @return The pilots current velocity. * @luafunc vel() */ static int pilotL_velocity(lua_State* L) { LLUA_MIN_ARGS(1); LuaPilot* p1; Pilot* p; LuaVector v; /* Parse parameters. */ p1 = lua_topilot(L, 1); p = pilot_get(p1->pilot); /* Pilot must exist.. */ if(p == NULL) return 0; /* Push velocity. */ vectcpy(&v.vec, &p->solid->vel); lua_pushvector(L, v); return 1; } /** * @ingroup META_PILOT * * @brief Set the pilots position. * @luaparam pos Position to set. * @luafunc warp( pos ) */ static int pilotL_warp(lua_State* L) { LLUA_MIN_ARGS(2); LuaPilot* p1; Pilot* p; LuaVector* v; /* Parse parameters. */ p1 = lua_topilot(L, 1); p = pilot_get(p1->pilot); if(lua_isvector(L,2)) v = lua_tovector(L, 2); else LLUA_INVALID_PARAMETER(); /* Pilot must exist. */ if(p == NULL) return 0; /* Warp pilot to new position. */ vectcpy(&p->solid->pos, &v->vec); vectnull(&p->solid->vel); /* Clear velocity otherwise it's a bit weird. */ return 0; } /** * @ingroup META_PILOT * * @brief Make the pilot broadcast a message. * @luaparam msg Message to broadcast. * @luafunc broadcast(msg) */ static int pilotL_broadcast(lua_State* L) { LLUA_MIN_ARGS(2); Pilot* p; LuaPilot* lp; char* msg; /* Parse arguments. */ lp = lua_topilot(L, 1); if(lua_isstring(L, 2)) msg = (char*)lua_tostring(L, 2); else LLUA_INVALID_PARAMETER(); /* Check to see if pilot is valid. */ p = pilot_get(lp->pilot); if(p == NULL) return 0; /* Broadcast message. */ player_message("Broadcast %s> \"%s\"", p->name, msg); return 0; } /** * @ingroup META_PILOT * * @brief Set the pilots faction. * @luaparam faction Faction to set by name or faction. * @luafunc setFaction( faction ) */ static int pilotL_setFaction(lua_State* L) { LLUA_MIN_ARGS(2); Pilot* p; LuaPilot* lp; LuaFaction* f; int fid; char* faction; /* Parse parameters. */ lp = lua_topilot(L, 1); if(lua_isstring(L, 2)) { faction = (char*)lua_tostring(L,2); fid = faction_get(faction); } else if(lua_isfaction(L, 2)) { f = lua_tofaction(L, 2); fid = f->f; } else LLUA_INVALID_PARAMETER(); /* Get pilot/faction. */ p = pilot_get(lp->pilot); if(p == NULL) return 0; /* Set the new faction. */ p->faction = fid; return 0; } /** * @ingroup META_PILOT * * @brief Set the pilot as hostile to player. * @luafunc setHostile() */ static int pilotL_setHostile(lua_State* L) { LuaPilot* lp; Pilot* p; /* Get the pilot. */ lp = lua_topilot(L, 1); p = pilot_get(lp->pilot); if(p == NULL) return 0; /* Set as hostile. */ pilot_setHostile(p); return 0; }