// Woot, LUA!!!!!! #include #include #include #include #include "main.h" #include "log.h" #include "pilot.h" #include "player.h" #include "physics.h" #include "pack.h" #include "rng.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). // ============================================================ // Call the AI function with name f. #define AI_LCALL(f) (lua_getglobal(L, f), lua_pcall(L, 0, 0, 0)) // Don't run the function if (n) params aren't passed. #define MIN_ARGS(n) if(lua_gettop(L) < n) return 0 #define MAX_DIR_ERR 5.0*M_PI/180. #define MIN_VEL_ERR 2.5 static int ai_minbrakedist(lua_State* L); // Minimal breaking distance. static int ai_accel(lua_State* L); // Accelerate. // Internal C routines. static void ai_freetask(Task* t); // 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_getdistance(lua_State* L); // Number getdist(Vec2) static int ai_getpos(lua_State* L); // getpos(number/pilot) static int ai_minbrakedist(lua_State* L); // Number minbrakedist() // Boolean expressions. 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); // bool isenemy(pointer). static int ai_isally(lua_State* L); // bool isally(pointer). // 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() // Combat. static int ai_shoot(lua_State* L); // shoot(number) number = 1,2,3. static int ai_getenemy(lua_State* L); // pointer getenemy(). // Misc. static int ai_createvect(lua_State* L); // createvect(number, number) static int ai_say(lua_State* L); // say(string) static int ai_rng(lua_State* L); // rng(number, number) // Global Lua interpreter. static lua_State* L = NULL; // Current pilot "thinking" and assorted variables. static Pilot* cur_pilot = NULL; static double pilot_acc = 0.; static double pilot_turn = 0.; static int pilot_primary = 0; // 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) { L = luaL_newstate(); if(L == NULL) { ERR("Unable to create a new Lua state"); return -1; } // Open the standard Lua libraries. luaL_openlibs(L); // Register C funstions in Lua. // Tasks. lua_register(L, "pushtask", ai_pushtask); lua_register(L, "poptask", ai_poptask); lua_register(L, "taskname", ai_taskname); // Consult. lua_register(L, "gettarget", ai_gettarget); lua_register(L, "gettargetid", ai_gettargetid); lua_register(L, "getdist", ai_getdistance); lua_register(L, "getpos", ai_getpos); lua_register(L, "minbrakedist", ai_minbrakedist); // Boolean. lua_register(L, "ismaxvel", ai_ismaxvel); lua_register(L, "isstopped", ai_isstopped); lua_register(L, "isenemy", ai_isenemy); lua_register(L, "isally", ai_isally); // Movement. lua_register(L, "accel", ai_accel); lua_register(L, "turn", ai_turn); lua_register(L, "face", ai_face); lua_register(L, "brake", ai_brake); // Combat. lua_register(L, "shoot", ai_shoot); lua_register(L, "getenemy", ai_getenemy); // Misc. lua_register(L, "createvect", ai_createvect); lua_register(L, "say", ai_say); lua_register(L, "rng", ai_rng); char* buf = pack_readfile(DATA, "../scripts/ai/test.lua", NULL); if(luaL_dostring(L, buf) != 0) { ERR("loading AI file: %s", "../scripts/ai/test.lua"); WARN("Most likely Lua file has improper syntax, please check it."); return -1; } free(buf); return 0; } // Clean up global AI void ai_exit(void) { lua_close(L); } // Heart of hearts of the ai!! Brains of the pilot. void ai_think(Pilot* pilot) { cur_pilot = pilot; // Set current pilot being processed. pilot_acc = pilot_turn = 0.; // Clean up some variables. if(cur_pilot->task == NULL) // Idle git! AI_LCALL("control"); else // Pilot has a currently running task. AI_LCALL(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->ship->turn * pilot_turn; vect_pset(&cur_pilot->solid->force, cur_pilot->ship->thrust * pilot_acc, cur_pilot->solid->dir); if(pilot_primary) pilot_shoot(pilot, 0); // AMG, he's gunna shoot! } // ===================== // 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->target) free(t->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->target = NULL; if(lua_gettop(L) > 2) { if(lua_isnumber(L, 3)) { t->dtype = TYPE_INT; t->ID = (unsigned int) lua_tonumber(L, 3); } else if(lua_islightuserdata(L, 3)) { t-dtype = TYPE_PTR; t->target = (void*)lua_topointer(L, 3); } 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_pushnil(L); 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->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->ID); return 1; } return 0; } // 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, 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. else if(lua_islightuserdata(L, 1)) p = (Pilot*)lua_topointer(L, 1); // Pilot pointer. 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->ship->turn // Add it to general euler equation x = v*t + 0.5 * a * t^2 // Have fun. // ======================================================== static int ai_minbrakedist(lua_State* L) { double time = VMOD(cur_pilot->solid->vel) / (cur_pilot->ship->thrust / cur_pilot->solid->mass); double dist = VMOD(cur_pilot->solid->vel) * (time + cur_pilot->ship->turn/360.) - 0.5 * (cur_pilot->ship->thrust / cur_pilot->solid->mass)*time*time; lua_pushnumber(L, dist); // return return 1; } // Are we at max velocity? static int ai_ismaxvel(lua_State* L) { lua_pushboolean(L, VMOD(cur_pilot->solid->vel) == cur_pilot->ship->speed); 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) { return 1; } // Check if the pilot is an ally. static int ai_isally(lua_State* L) { 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; // Grab the position to face. if(lua_isnumber(L,1)) v = &pilot_get((unsigned int)lua_tonumber(L,1))->solid->pos; else if(lua_islightuserdata(L,1)) v = (Vec2*)lua_topointer(L,1); double mod = -10; if(lua_gettop(L) > 1 && lua_isnumber(L,2)) switch((int)lua_tonumber(L,2)) { case 0: break; case 1: mod *= -1; break; case 2: break; } double diff = angle_diff(cur_pilot->solid->dir, vect_angle(&cur_pilot->solid->pos, v)); 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 = angle_diff(cur_pilot->solid->dir, VANGLE(cur_pilot->solid->vel)); pilot_turn = 10*diff; if(diff < MAX_DIR_ERR && VMOD(cur_pilot->solid->vel) > MIN_VEL_ERR) pilot_acc = 1.; return 0; } // 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) pilot_primary = 1; //else if(n == 2) pilot_secondary = 1; //else if(n == 3) pilot_primary = pilot_secondary = 1; return 0; } // Get the nearest enemy. static int ai_getenemy(lua_State* L) { return 0; } // Create a vector. static int ai_createvect(lua_State* L) { MIN_ARGS(2); Vec2* v = MALLOC_L(Vec2); double x = (lua_isnumber(L, 1)) ? (double)lua_tonumber(L,1) : 0.; double y = (lua_isnumber(L, 2)) ? (double)lua_tonumber(L,2) : 0.; vect_cset(v, x, y); lua_pushlightuserdata(L, (void*)v); return 1; } // Have the pilot say something to player. static int ai_say(lua_State* L) { MIN_ARGS(1); if(lua_isstring(L, 1)) player_message("Comm %s> \"%s\"", cur_pilot->name, lua_tostring(L, 1)); return 0; } // 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; }