#include "lauxlib.h" #include "lualib.h" #include #include "lephisto.h" #include "log.h" #include "pilot.h" #include "player.h" #include "physics.h" #include "pack.h" #include "rng.h" #include "space.h" #include "faction.h" #include "ai.h" // == AI ====================================================== // // -- Goal (Task) based AI with additional optimization. // AI uses the goal (task) based AI approach with tasks scripted // in lua. Additionally there is a task that is hardcoded and // obligatory in any AI script. The 'control' task, whose only // purpose is to assign tasks if there is none, and optimize // or change tasks if there are. // // Eg.. Pilot A is attacking Pilot B. Pilot C then comes along // the same system and is of the same faction as Pilot B. and // therefor attacks Pilot A. Pilot A would keep fighting pilot // B and until the control task comes in. Then the pilot could // run if it deems fit that Pilot C and Pilot B together are // both too strong for A. Or.. Attack C as it is an easy target // to finish. // Basically, there is many possibilities, and it's down to the // Lua fanatics to decide what to do. // // -- AI will follow basic tasks defined from Lua AI scripts. // -- If task is NULL, AI will run "control" task. // -- Task is continued every frame. // -- "control" task is a special task that *must* exist in // any given Pilot AI (missiles, and suck will use "seek". // -- "control" task is not permanent, but transitory. // -- "control" task sets another task. // -- "control" task is also run at a set rate (depending on // Lua global "control_rate") to choose optimal behaviour // (task). // ============================================================ // FUCK LUA!!! #define luaL_register(L,n,l) (luaL_openlib(L, (n),(l), 0)) // Creates a new lua table. #define newtable(L) (lua_newtable(L), lua_gettop(L)) // Register a number constant n to name s (syntax is just like lua_regfunc). #define lua_regnumber(l,s,n) (lua_pushnumber(l,n), lua_setglobal(l,s)) // Registers a C function. #define lua_regfunc(l,s,f) (lua_pushcfunction(l,f), lua_setglobal(L,s)) // L state, void* buf, int n size, char* s identifier. #define luaL_dobuffer(L,b,n,s) \ (luaL_loadbuffer(L,b,n,s) || lua_pcall(L, 0, LUA_MULTRET, 0)) // Don't run the function if (n) params aren't passed. #define MIN_ARGS(n) if(lua_gettop(L) < n) return 0 // Ai flags. #define ai_setFlag(f) (pilot_flags |= f) #define ai_isFlag(f) (pilot_flags & f) // Flags. #define AI_PRIMARY (1<<0) // Firing primary weapon. #define AI_SECONDARY (1<<1) // Firing secondary weapon. // file info. #define AI_PREFIX "../scripts/ai/" #define AI_SUFFIX ".lua" // AI profiles. static AI_Profile* profiles = NULL; static int nprofiles = 0; // Current AI Lua interpreter. static lua_State* L = NULL; // Extern pilot hacks. extern Pilot** pilot_stack; extern int pilots; static int ai_minbrakedist(lua_State* L); // Minimal breaking distance. static int ai_accel(lua_State* L); // Accelerate. // Internal C routines. static void ai_run(const char* funcname); static int ai_loadProfile(char* filename); static void ai_freetask(Task* t); // External C routines. void ai_attacked(Pilot* attacked, const unsigned int attacker); // weapon.c void ai_create(Pilot* pilot); // Ai routines for Lua. // Tasks. static int ai_pushtask(lua_State* L); // pushtask(string, number/pointer, number) static int ai_poptask(lua_State* L); // poptask() static int ai_taskname(lua_State* L); // Number taskname. // Consult values. static int ai_gettarget(lua_State* L); // Pointer gettarget() static int ai_gettargetid(lua_State* L); // Number gettargetis() static int ai_armour(lua_State* L); // armour() static int ai_shield(lua_State* L); // shield() static int ai_parmour(lua_State* L); // parmour() static int ai_pshield(lua_State* L); // pshield() static int ai_getdistance(lua_State* L); // Number getdist(Vec2) static int ai_getpos(lua_State* L); // getpos(number) static int ai_minbrakedist(lua_State* L); // Number minbrakedist() static int ai_cargofree(lua_State* L); // Number cargofree(). // Boolean expressions. static int ai_exists(lua_State* L); // boolean exists static int ai_ismaxvel(lua_State* L); // Boolean ismaxvel() static int ai_isstopped(lua_State* L); // Boolean isstopped() static int ai_isenemy(lua_State* L); // boolean isenemy(number). static int ai_isally(lua_State* L); // boolean isally(number). static int ai_incombat(lua_State* L); // boolean incombat([number]) // Movement. static int ai_accel(lua_State* L); // accel(number); nuimber <= 1. static int ai_turn(lua_State* L); // turn(number); abs(number) <= 1. static int ai_face(lua_State* L); // face(number/pointer) static int ai_brake(lua_State* L); // Brake() static int ai_getnearestplanet(lua_State* L); // pointer getnearestplanet() static int ai_getrndplanet(lua_State* L); // pointer getrndplanet() static int ai_hyperspace(lua_State* L); // [number] hyperspace() static int ai_stop(lua_State* L); // stop() // Combat. static int ai_combat(lua_State* L); // combat(number) static int ai_settarget(lua_State* L); // settarget(number) static int ai_secondary(lua_State* L); // string secondary() static int ai_hasturrets(lua_State* L); // bool hasturrets() static int ai_shoot(lua_State* L); // shoot(number) number = 1,2,3. static int ai_getenemy(lua_State* L); // number getenemy(). static int ai_hostile(lua_State* L); // hostile(number). // Timers. static int ai_settimer(lua_State* L); // settimer(number, number) static int ai_timeup(lua_State* L); // boolean timeup(number) // Messages. static int ai_comm(lua_State* L); // comm(string) static int ai_broadcast(lua_State* L); // broadcast(string) // Loot. static int ai_credits(lua_State* L); // credits(number). static int ai_cargo(lua_State* L); // cargo(name, quantity). static int ai_shipprice(lua_State* L); // shipprice(). // Random. static int ai_rng(lua_State* L); // rng(number, number) static const luaL_Reg ai_methods[] = { // Tasks. { "pushtask", ai_pushtask }, { "poptask", ai_poptask }, { "taskname", ai_taskname }, // Sanity checks. { "exists", ai_exists }, { "ismaxvel", ai_ismaxvel }, { "isstopped", ai_isstopped }, { "isenemy", ai_isenemy }, { "isally", ai_isally }, // Get's. { "incombat", ai_incombat }, { "target", ai_gettarget }, { "targetid", ai_gettargetid }, { "armour", ai_armour }, { "shield", ai_shield }, { "parmour", ai_parmour }, { "pshield", ai_pshield }, { "dist", ai_getdistance }, { "pos", ai_getpos }, { "minbrakedist", ai_minbrakedist }, { "cargofree", ai_cargofree }, { "nearestplanet", ai_getnearestplanet }, { "rndplanet", ai_getrndplanet }, // Movement. { "accel", ai_accel }, { "turn", ai_turn }, { "face", ai_face }, { "brake", ai_brake }, { "stop", ai_stop }, { "hyperspace", ai_hyperspace }, // Combat. { "combat", ai_combat }, { "settarget", ai_settarget }, { "secondary", ai_secondary }, { "hasturrets", ai_hasturrets }, { "shoot", ai_shoot }, { "getenemy", ai_getenemy }, { "hostile", ai_hostile }, // Timers. { "settime", ai_settimer }, { "timeup", ai_timeup }, // Messages. { "comm", ai_comm }, { "broadcast", ai_broadcast }, // Loot. { "setcredits", ai_credits }, { "setcargo", ai_cargo }, { "shipprice", ai_shipprice }, // Random. { "rnd", ai_rng }, { 0, 0 } // End. }; // Current pilot "thinking" and assorted variables. static Pilot* cur_pilot = NULL; static double pilot_acc = 0.; static double pilot_turn = 0.; static int pilot_flags = 0; static int pilot_target = 0; // Ai status: 'Create' functions that can't be used elsewhere. #define AI_STATUS_NORMAL 1 #define AI_STATUS_CREATE 2 static int ai_status = AI_STATUS_NORMAL; // Attempt to run a function. static void ai_run(const char* funcname) { lua_getglobal(L, funcname); if(lua_pcall(L, 0, 0, 0)) // Errors accured. WARN("Pilot '%s' ai -> '%s' : %s", cur_pilot->name, funcname, lua_tostring(L,-1)); } // Destroy the AI part of the pilot. void ai_destroy(Pilot* p) { if(p->task) ai_freetask(p->task); } // Init the AI stuff. Which is basically Lua. int ai_init(void) { char** files; uint32_t nfiles, i; // Get the file list. files = pack_listfiles(data, &nfiles); // Load the profiles. for(i = 0; i < nfiles; i++) if((strncmp(files[i], AI_PREFIX, strlen(AI_PREFIX))==0 && strncmp(files[i] + strlen(files[i]) - strlen(AI_SUFFIX), AI_SUFFIX, strlen(AI_SUFFIX))==0)) if(ai_loadProfile(files[i])) WARN("Error loading AI profile '%s'", files[i]); // Free the char allocated by pack. for(i = 0; i < nfiles; i++) free(files[i]); free(files); DEBUG("Loaded %d AI profile%c", nprofiles, (nprofiles==1)?' ':'s'); return 0; } // Init an IA_Profile and add it to the stack. static int ai_loadProfile(char* filename) { char* buf = NULL; uint32_t bufsize = 0; profiles = realloc(profiles, sizeof(AI_Profile)*(++nprofiles)); profiles[nprofiles-1].name = malloc(sizeof(char)* (strlen(filename)-strlen(AI_PREFIX)-strlen(AI_SUFFIX))+1); snprintf(profiles[nprofiles-1].name, strlen(filename)-strlen(AI_PREFIX)-strlen(AI_SUFFIX)+1, "%s", filename+strlen(AI_PREFIX)); profiles[nprofiles-1].L = luaL_newstate(); if(profiles[nprofiles-1].L == NULL) { ERR("Unable to create a new Lua state"); return -1; } L = profiles[nprofiles-1].L; // Open the standard Lua libraries. //luaL_openlibs(L); // Constants. lua_regnumber(L, "player", PLAYER_ID); // Player id. // Register C funstions in Lua. luaL_register(L, "ai", ai_methods); // Now load the file, since all the functions have been previously loaded. buf = pack_readfile(DATA, filename, &bufsize); if(luaL_dobuffer(L, buf, bufsize, filename) != 0) { ERR("loading AI file: %s", filename); ERR("%s", lua_tostring(L, -1)); WARN("Most likely Lua file has improper syntax, please check it."); return -1; } free(buf); return 0; } // Get the AI_Profile with name. AI_Profile* ai_getProfile(char* name) { if(profiles == NULL) return NULL; int i; for(i = 0; i < nprofiles; i++) if(strcmp(name, profiles[i].name)==0) return &profiles[i]; WARN("AI Profile '%s' not found in AI stack", name); return NULL; } // Clean up global AI void ai_exit(void) { int i; for(i = 0; i < nprofiles; i++) { free(profiles[i].name); lua_close(profiles[i].L); } free(profiles); } // Heart of hearts of the ai!! Brains of the pilot. void ai_think(Pilot* pilot) { cur_pilot = pilot; // Set current pilot being processed. L = cur_pilot->ai->L; // Set the AI profile to the current pilot's. // Clean up some variables. pilot_acc = pilot_turn = 0.; pilot_flags = 0; pilot_target = 0; // Control function if pilot is idle or tick is up. if((cur_pilot->tcontrol < SDL_GetTicks()) || (cur_pilot->task == NULL)) { ai_run("control"); // Run control. lua_getglobal(L, "control_rate"); cur_pilot->tcontrol = SDL_GetTicks() + 1000*(int)lua_tonumber(L, -1); } if(cur_pilot->task != NULL) // Pilot has a currently running task. ai_run(cur_pilot->task->name); // Make sure pilot_acc and pilot_turn are legal moves. if(pilot_acc > 1.) pilot_acc = 1.; // Value must be <= 1. if(pilot_turn > 1.) pilot_turn = 1.; // Value must be between -1 and 1. else if(pilot_turn < -1.) pilot_turn = -1.; cur_pilot->solid->dir_vel = 0.; if(pilot_turn) // Set the turning velocity. cur_pilot->solid->dir_vel -= cur_pilot->turn * pilot_turn; vect_pset(&cur_pilot->solid->force, cur_pilot->thrust * pilot_acc, cur_pilot->solid->dir); // Fire weapons if needs be. if(ai_isFlag(AI_PRIMARY)) pilot_shoot(pilot, pilot_target, 0); // Primary. if(ai_isFlag(AI_SECONDARY)) pilot_shoot(pilot, pilot_target, 1); // Secondary. } // Pilot is attacked. void ai_attacked(Pilot* attacked, const unsigned int attacker) { cur_pilot = attacked; L = cur_pilot->ai->L; lua_getglobal(L, "attacked"); lua_pushnumber(L, attacker); lua_pcall(L, 1, 0, 0); } // Pilot was just created. void ai_create(Pilot* pilot) { cur_pilot = pilot; L = cur_pilot->ai->L; ai_status = AI_STATUS_CREATE; ai_run("create"); ai_status = AI_STATUS_NORMAL; } // ===================== // INTERNAL C FUNCTIONS. // ===================== // Free the task. static void ai_freetask(Task* t) { if(t->next) ai_freetask(t->next); // Woot, recursive freeing! if(t->name) free(t->name); if(t->dtype == TYPE_PTR) free(t->dat.target); free(t); } // ======================================================== // C functions to call from Lua. // ======================================================== // Push the current stack. static int ai_pushtask(lua_State* L) { int pos; if(lua_isnumber(L, 1)) pos = (int) lua_tonumber(L, 1); else return 0; // Invalid param. Task* t = MALLOC_L(Task); t->name = (lua_isstring(L, 2)) ? strdup((char*) lua_tostring(L, 2)) : NULL; t->next = NULL; t->dat.target = NULL; if(lua_gettop(L) > 2) { if(lua_isnumber(L, 3)) { t->dtype = TYPE_INT; t->dat.ID = (unsigned int) lua_tonumber(L, 3); } else if(lua_islightuserdata(L, 3)) { // Only pointer valid is Vec2* in Lua. t->dtype = TYPE_PTR; t->dat.target = MALLOC_L(Vec2); // No idea why vectcpy doesn't work here.. ((Vec2*)t->dat.target)->x = ((Vec2*)lua_topointer(L,3))->x; ((Vec2*)t->dat.target)->y = ((Vec2*)lua_topointer(L,3))->y; ((Vec2*)t->dat.target)->mod = ((Vec2*)lua_topointer(L,3))->mod; ((Vec2*)t->dat.target)->angle = ((Vec2*)lua_topointer(L,3))->angle; } else t->dtype = TYPE_NULL; } if(cur_pilot->task == NULL) // No other tasks. cur_pilot->task = t; else if(pos == 1) { // Put at the end. Task* pointer; for(pointer = cur_pilot->task; pointer->next; pointer = pointer->next); pointer->next = t; } else { // Default put at the beginning. t->next = cur_pilot->task; cur_pilot->task = t; } return 0; } // Pop the current task. static int ai_poptask(lua_State* L) { (void)L; // Just a hack to avoid -W -Wall warnings. Task* t = cur_pilot->task; cur_pilot->task = t->next; t->next = NULL; ai_freetask(t); return 0; } // Grab the current tasks name. static int ai_taskname(lua_State* L) { if(cur_pilot->task) lua_pushstring(L, cur_pilot->task->name); else lua_pushstring(L, "none"); return 1; } // Grab the target pointer. static int ai_gettarget(lua_State* L) { if(cur_pilot->task->dtype == TYPE_PTR) { lua_pushlightuserdata(L, cur_pilot->task->dat.target); return 1; } return 0; } // Get the ID. static int ai_gettargetid(lua_State* L) { if(cur_pilot->task->dtype == TYPE_INT) { lua_pushnumber(L, cur_pilot->task->dat.ID); return 1; } return 0; } // Get the pilots armour. static int ai_armour(lua_State* L) { double d; if(lua_isnumber(L,1)) d = pilot_get((unsigned int)lua_tonumber(L,1))->armour; else d = cur_pilot->armour; lua_pushnumber(L, d); return 1; } // Get pilots shield. static int ai_shield(lua_State* L) { double d; if(lua_isnumber(L,1)) d = pilot_get((unsigned int)lua_tonumber(L,1))->shield; else d = cur_pilot->shield; lua_pushnumber(L, d); return 1; } // Get the pilot's armour in percentage. static int ai_parmour(lua_State* L) { double d; Pilot* p; if(lua_isnumber(L,1)) { p = pilot_get((unsigned int)lua_tonumber(L,1)); d = p->armour / p->armour_max * 100.; } else d = cur_pilot->armour / cur_pilot->armour_max * 100.; lua_pushnumber(L, d); return 1; } // Get the pilot's shield in percentage. static int ai_pshield(lua_State* L) { double d; Pilot* p; if(lua_isnumber(L,1)) { p = pilot_get((unsigned int)lua_tonumber(L,1)); d = p->shield / p->shield_max * 100.; } else d = cur_pilot->shield / cur_pilot->shield_max * 100.; lua_pushnumber(L, d); return 1; } // Get the distance from the pointer. static int ai_getdistance(lua_State* L) { MIN_ARGS(1); Vec2* vect = (Vec2*)lua_topointer(L,1); lua_pushnumber(L, vect_dist(vect, &cur_pilot->solid->pos)); return 1; } // Get the pilots position. static int ai_getpos(lua_State* L) { Pilot* p; if(lua_isnumber(L, 1)) { p = pilot_get((int)lua_tonumber(L,1)); // Pilot ID. if(p == NULL) return 0; } else p = cur_pilot; // Default to ones self. lua_pushlightuserdata(L, &p->solid->pos); return 1; } // ======================================================== // Get the minimum braking distance. // // Braking vel ==> 0 = v - a*dt // Add turn around time (to initial velocity) : // ==> 180.*360./cur_pilot->turn // Add it to general euler equation x = v*t + 0.5 * a * t^2 // Have fun. // // I really hate this function. Why isn't it depricated yet? // ======================================================== static int ai_minbrakedist(lua_State* L) { double time, dist; time = VMOD(cur_pilot->solid->vel) / (cur_pilot->thrust / cur_pilot->solid->mass); dist = VMOD(cur_pilot->solid->vel)*0.9*(time+180./cur_pilot->turn) - 0.5 * (cur_pilot->thrust / cur_pilot->solid->mass)*time*time; lua_pushnumber(L, dist); // return return 1; } static int ai_cargofree(lua_State* L) { lua_pushnumber(L, cur_pilot->cargo_free); return 1; } static int ai_exists(lua_State* L) { MIN_ARGS(1); if(lua_isnumber(L,1)) { lua_pushboolean(L, (pilot_get((unsigned int)lua_tonumber(L,1)) != NULL)?1:0); return 1; } return 0; } // Are we at max velocity? static int ai_ismaxvel(lua_State* L) { lua_pushboolean(L,(VMOD(cur_pilot->solid->vel)>cur_pilot->speed-MIN_VEL_ERR)); return 1; } // Have we stopped? static int ai_isstopped(lua_State* L) { lua_pushboolean(L, VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR); return 1; } // Check if the pilot is an enemy. static int ai_isenemy(lua_State* L) { if(lua_isnumber(L,1)) lua_pushboolean(L, areEnemies(cur_pilot->faction, pilot_get(lua_tonumber(L,1))->faction)); return 1; } // Check if the pilot is an ally. static int ai_isally(lua_State* L) { lua_pushboolean(L, areAllies(cur_pilot->faction, pilot_get(lua_tonumber(L,1))->faction)); return 1; } // Check to see if the pilot is in combat. Defaults to self. static int ai_incombat(lua_State* L) { Pilot* p; if(lua_isnumber(L, 1)) p = pilot_get((unsigned int)lua_tonumber(L,1)); else p = cur_pilot; lua_pushboolean(L,pilot_isFlag(p, PILOT_COMBAT)); return 1; } // Accelerate the pilot based on a param. static int ai_accel(lua_State* L) { pilot_acc = (lua_gettop(L) > 1 && lua_isnumber(L, 1)) ? ABS((double)lua_tonumber(L, 1)) : 1.; return 0; } // Turn the pilot based on a param. static int ai_turn(lua_State* L) { MIN_ARGS(1); pilot_turn = (lua_isnumber(L, 1)) ? (double)lua_tonumber(L, 1) : 0.; return 0; } // Face the target. static int ai_face(lua_State* L) { MIN_ARGS(1); Vec2* v, sv, tv; // Grab the position to face. Pilot* p; double mod, diff; int invert = 0; int n = -2; if(lua_isnumber(L, 1)) n = (int)lua_tonumber(L, 1); if(n >= 0) { p = pilot_get(n); if(p == NULL) return 0; // Make sure pilot is valid. vect_cset(&tv, VX(p->solid->pos) + FACE_WVEL*VX(p->solid->vel), VY(p->solid->pos) + FACE_WVEL*VY(p->solid->vel)); v = NULL; } else if(lua_islightuserdata(L,1)) v = (Vec2*)lua_topointer(L,1); mod = -10; if(lua_gettop(L) > 1 && lua_isnumber(L,2)) invert = (int)lua_tonumber(L,2); if(invert) mod *= -1; vect_cset(&sv, VX(cur_pilot->solid->pos) + FACE_WVEL*VX(cur_pilot->solid->vel), VY(cur_pilot->solid->pos) + FACE_WVEL*VY(cur_pilot->solid->vel)); if(v != NULL) // Target is static. diff = angle_diff(cur_pilot->solid->dir, (n==-1) ? VANGLE(cur_pilot->solid->pos) : vect_angle(&cur_pilot->solid->pos, v)); else // Target is dynamic. diff = angle_diff(cur_pilot->solid->dir, (n==-1) ? VANGLE(sv) : vect_angle(&sv, &tv)); pilot_turn = mod*diff; lua_pushnumber(L, ABS(diff*180./M_PI)); return 1; } // This is generally good for coming to a halt. static int ai_brake(lua_State* L) { (void)L; // Just a hack to avoid -W -Wall warnings. double diff, d; d = cur_pilot->solid->dir+M_PI; if(d >= 2*M_PI) d = fmodf(d, 2*M_PI); diff = angle_diff(d, VANGLE(cur_pilot->solid->vel)); pilot_turn = -10*diff; if(ABS(diff) < MAX_DIR_ERR && VMOD(cur_pilot->solid->vel) > MIN_VEL_ERR) pilot_acc = 1.; return 0; } // Return the nearest friendly planet's position to the pilot. static int ai_getnearestplanet(lua_State* L) { if(cur_system->nplanets == 0) return 0; // No planets. double dist, d; int i, j; // Cycle through planets. for(dist = 0., j = -1, i = 0; i < cur_system->nplanets; i++) { d = vect_dist(&cur_system->planets[i].pos, &cur_pilot->solid->pos); if((!areEnemies(cur_pilot->faction, cur_system->planets[i].faction)) && (d < dist)) { // Closer friendly planet. j = i; dist = d; } } // No friendly planet found. if(j == -1) return 0; lua_pushlightuserdata(L, &cur_system->planets[j].pos); return 1; } // Return a random friendly planet's position to the pilot. static int ai_getrndplanet(lua_State* L) { if(cur_system->nplanets == 0) return 0; // No planets. Planet** planets; int nplanets, i; Vec2 v; planets = malloc(sizeof(Planet*) * cur_system->nplanets); for(nplanets = 0, i = 0; i < cur_system->nplanets; i++) if(planet_hasService(&cur_system->planets[i], PLANET_SERVICE_BASIC) && !areEnemies(cur_pilot->faction, cur_system->planets[i].faction)) planets[nplanets++] = &cur_system->planets[i]; // No planet to land on found. if(nplanets == 0) { free(planets); return 0; } // We can actually get a random planet now. i = RNG(0,nplanets-1); vectcpy(&v, &planets[i]->pos); vect_cadd(&v, RNG(0, planets[i]->gfx_space->sw)-planets[i]->gfx_space->sw/2., RNG(0, planets[i]->gfx_space->sh)-planets[i]->gfx_space->sh/2.); lua_pushlightuserdata(L, &v); free(planets); return 1; } // Attempt to enter the pilot hyperspace. Return the distance if too far away. static int ai_hyperspace(lua_State* L) { int dist; dist = space_hyperspace(cur_pilot); if(dist == 0.) return 0; lua_pushnumber(L,dist); return 1; } // Completely stop the pilot if it is below minimum vel error. (No instant stops.) static int ai_stop(lua_State* L) { (void)L; // Just avoid a gcc warning. if(VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR) vect_pset(&cur_pilot->solid->vel, 0., 0.); return 0; } // Toggle combat flag. Default is on. static int ai_combat(lua_State* L) { int i; if(lua_isnumber(L, 1)) { i = (int)lua_tonumber(L,1); if(i == 1) pilot_setFlag(cur_pilot, PILOT_COMBAT); else if(i == 0) pilot_rmFlag(cur_pilot, PILOT_COMBAT); } else pilot_setFlag(cur_pilot, PILOT_COMBAT); return 0; } // Set the pilots target. static int ai_settarget(lua_State* L) { MIN_ARGS(1); if(lua_isnumber(L,1)) pilot_target = (int)lua_tonumber(L,1); return 0; } // Set the secondary weapon. Biassed towards launchers.. static int ai_secondary(lua_State* L) { if(cur_pilot->secondary) { lua_pushstring(L, outfit_getTypeBroad(cur_pilot->secondary->outfit)); return 1; } PilotOutfit* po = NULL; int i; for(i = 0; i < cur_pilot->noutfits; i++) { if((po == NULL) && (outfit_isWeapon(cur_pilot->outfits[i].outfit) || outfit_isLauncher(cur_pilot->outfits[i].outfit))) po = &cur_pilot->outfits[i]; else if((po != NULL) && outfit_isWeapon(po->outfit) && outfit_isLauncher(cur_pilot->outfits[i].outfit)) po = &cur_pilot->outfits[i]; } if(po) { cur_pilot->secondary = po; pilot_setAmmo(cur_pilot); lua_pushstring(L, outfit_getTypeBroad(po->outfit)); return 1; } lua_pushstring(L, "None"); return 1; } static int ai_hasturrets(lua_State* L) { lua_pushboolean(L, pilot_isFlag(cur_pilot, PILOT_HASTURRET)); return 1; } // Pew pew.. Says the pilot. static int ai_shoot(lua_State* L) { int n = 1; if(lua_isnumber(L, 1)) n = (int)lua_tonumber(L,1); if(n == 1) ai_setFlag(AI_PRIMARY); else if(n == 2) ai_setFlag(AI_SECONDARY); else if(n == 3) ai_setFlag(AI_PRIMARY | AI_SECONDARY); return 0; } // Get the nearest enemy. static int ai_getenemy(lua_State* L) { lua_pushnumber(L,pilot_getNearest(cur_pilot)); return 1; } // Set the enemy hostile. (Simply notifies of an impending attack). static int ai_hostile(lua_State* L) { MIN_ARGS(1); if(lua_isnumber(L,1) && ((unsigned int)lua_tonumber(L,1) == PLAYER_ID)) pilot_setFlag(cur_pilot, PILOT_HOSTILE); return 0; } // Set the timer. static int ai_settimer(lua_State* L) { MIN_ARGS(2); int n; // Get the timer. if(lua_isnumber(L, 1)) n = lua_tonumber(L,1); cur_pilot->timer[n] = (lua_isnumber(L,2)) ? lua_tonumber(L,2) + SDL_GetTicks() : 0; return 0; } // Check the timer. static int ai_timeup(lua_State* L) { MIN_ARGS(1); int n; // Get the timer. if(lua_isnumber(L,1)) n = lua_tonumber(L,1); lua_pushboolean(L, cur_pilot->timer[n] < SDL_GetTicks()); return 1; } // Have the pilot say something to player. static int ai_comm(lua_State* L) { MIN_ARGS(2); if(lua_isnumber(L,1) && (lua_tonumber(L,1)==PLAYER_ID) && lua_isstring(L,2)) player_message("Comm: %s> \"%s\"", cur_pilot->name, lua_tostring(L,2)); return 0; } // Broadcasts to the entire area. static int ai_broadcast(lua_State* L) { MIN_ARGS(1); if(lua_isstring(L, 1)) player_message("Broadcast: %s> \"%s\"", cur_pilot->name, lua_tostring(L,1)); return 0; } // Set the pilots credits. static int ai_credits(lua_State* L) { MIN_ARGS(1); if(ai_status != AI_STATUS_CREATE) return 0; if(lua_isnumber(L,1)) cur_pilot->credits = (int)lua_tonumber(L,1); return 0; } // Set the pilots cargo. static int ai_cargo(lua_State* L) { MIN_ARGS(2); if(ai_status != AI_STATUS_CREATE) return 0; if(lua_isstring(L,1) && lua_isnumber(L,2)) pilot_addCargo(cur_pilot, commodity_get(lua_tostring(L,1)), (int)lua_tonumber(L,2)); return 0; } // Get the pilot's ship value. static int ai_shipprice(lua_State* L) { lua_pushnumber(L, cur_pilot->ship->price); return 1; } // Return a number between low and high. static int ai_rng(lua_State* L) { MIN_ARGS(2); int l, h; if(lua_isnumber(L,1)) l = (int)lua_tonumber(L, 1); if(lua_isnumber(L,1)) h = (int)lua_tonumber(L, 2); lua_pushnumber(L, RNG(l,h)); return 1; }