1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file ai.c
7  *
8  * @brief Controls the Pilot AI.
9  *
10  * AI Overview
11  *
12  * Concept: Goal (Task) Based AI with additional Optimization
13  *
14  *  AI uses the goal (task) based AI approach with tasks scripted in Lua,
15  * additionally there is a task that is hard-coded and obligatory in any AI
16  * script, the 'control' task, whose sole purpose is to assign tasks if there
17  * is no current tasks and optimizes or changes tasks if there are.
18  *
19  *  For example: Pilot A is attacking Pilot B.  Say that Pilot C then comes in
20  * the same system and is of the same faction as Pilot B, and therefore attacks
21  * Pilot A.  Pilot A would keep on fighting Pilot B until the control task
22  * kicks in.  Then he/she could run if it deems that Pilot C and Pilot B
23  * together are too strong for him/her, or attack Pilot C because it's an
24  * easier target to finish off then Pilot B.  Therefore there are endless
25  * possibilities and it's up to the AI coder to set up.
26  *
27  *
28  * Specification
29  *
30  *   -  AI will follow basic tasks defined from Lua AI script.
31  *     - if Task is NULL, AI will run "control" task
32  *     - Task is continued every frame
33  *     - Tasks can have subtasks which will be closed when parent task is dead.
34  *     -  "control" task is a special task that MUST exist in any given  Pilot AI
35  *        (missiles and such will use "seek")
36  *     - "control" task is not permanent, but transitory
37  *     - "control" task sets another task
38  *   - "control" task is also run at a set rate (depending on Lua global "control_rate")
39  *     to choose optimal behaviour (task)
40  *
41  * Memory
42  *
43  *  The AI currently has per-pilot memory which is accessible as "mem".  This
44  * memory is actually stored in the table pilotmem[cur_pilot->id].  This allows
45  * the pilot to keep some memory always accessible between runs without having
46  * to rely on the storage space a task has.
47  *
48  * Garbage Collector
49  *
50  *  The tasks are not deleted directly but are marked for deletion and are then
51  * cleaned up in a garbage collector. This is to avoid accessing invalid task
52  * memory.
53  *
54  * @note Nothing in this file can be considered reentrant.  Plan accordingly.
55  *
56  * @todo Clean up most of the code, it was written as one of the first
57  *         subsystems and is pretty lacking in quite a few aspects. Notably
58  *         removing the entire lightuserdata thing and actually go with full
59  *         userdata.
60  */
61 
62 
63 #include "ai.h"
64 
65 #include "naev.h"
66 
67 #include <stdlib.h>
68 #include <stdio.h> /* malloc realloc */
69 #include <math.h>
70 #include <ctype.h> /* isdigit */
71 
72 /* yay more Lua */
73 #include <lauxlib.h>
74 #include <lualib.h>
75 
76 #include "nstring.h" /* strncpy strlen strncat strcmp strdup */
77 #include "log.h"
78 #include "pilot.h"
79 #include "player.h"
80 #include "physics.h"
81 #include "ndata.h"
82 #include "rng.h"
83 #include "space.h"
84 #include "faction.h"
85 #include "escort.h"
86 #include "nlua.h"
87 #include "nluadef.h"
88 #include "nlua_vec2.h"
89 #include "nlua_rnd.h"
90 #include "nlua_pilot.h"
91 #include "nlua_planet.h"
92 #include "nlua_faction.h"
93 #include "board.h"
94 #include "hook.h"
95 #include "array.h"
96 
97 
98 /*
99  * ai flags
100  *
101  * They can be used for stuff like movement or for pieces of code which might
102  *  run AI stuff when the AI module is not reentrant.
103  */
104 #define ai_setFlag(f)   (pilot_flags |= f ) /**< Sets pilot flag f */
105 #define ai_isFlag(f)    (pilot_flags & f ) /**< Checks pilot flag f */
106 /* flags */
107 #define AI_PRIMARY      (1<<0)   /**< Firing primary weapon */
108 #define AI_SECONDARY    (1<<1)   /**< Firing secondary weapon */
109 #define AI_DISTRESS     (1<<2)   /**< Sent distress signal. */
110 
111 
112 /*
113  * file info
114  */
115 #define AI_SUFFIX       ".lua" /**< AI file suffix. */
116 #define AI_MEM_DEF      "def" /**< Default pilot memory. */
117 
118 
119 /*
120  * all the AI profiles
121  */
122 static AI_Profile* profiles = NULL; /**< Array of AI_Profiles loaded. */
123 static nlua_env equip_env = LUA_NOREF; /**< Equipment enviornment. */
124 
125 
126 /*
127  * extern pilot hacks
128  */
129 extern Pilot** pilot_stack;
130 extern int pilot_nstack;
131 
132 
133 /*
134  * prototypes
135  */
136 /* Internal C routines */
137 static void ai_run( nlua_env env, const char *funcname );
138 static int ai_loadProfile( const char* filename );
139 static void ai_setMemory (void);
140 static void ai_create( Pilot* pilot );
141 static int ai_loadEquip (void);
142 /* Task management. */
143 static void ai_taskGC( Pilot* pilot );
144 static Task* ai_curTask( Pilot* pilot );
145 static Task* ai_createTask( lua_State *L, int subtask );
146 static int ai_tasktarget( lua_State *L, Task *t );
147 
148 
149 
150 /*
151  * AI routines for Lua
152  */
153 /* tasks */
154 static int aiL_pushtask( lua_State *L ); /* pushtask( string, number/pointer ) */
155 static int aiL_poptask( lua_State *L ); /* poptask() */
156 static int aiL_taskname( lua_State *L ); /* string taskname() */
157 static int aiL_gettarget( lua_State *L ); /* pointer gettarget() */
158 static int aiL_pushsubtask( lua_State *L ); /* pushsubtask( string, number/pointer, number ) */
159 static int aiL_popsubtask( lua_State *L ); /* popsubtask() */
160 static int aiL_subtaskname( lua_State *L ); /* string subtaskname() */
161 static int aiL_getsubtarget( lua_State *L ); /* pointer subtarget() */
162 
163 /* consult values */
164 static int aiL_pilot( lua_State *L ); /* number pilot() */
165 static int aiL_getrndpilot( lua_State *L ); /* number getrndpilot() */
166 static int aiL_getnearestpilot( lua_State *L ); /* number getnearestpilot() */
167 static int aiL_getdistance( lua_State *L ); /* number getdist(Vector2d) */
168 static int aiL_getflybydistance( lua_State *L ); /* number getflybydist(Vector2d) */
169 static int aiL_minbrakedist( lua_State *L ); /* number minbrakedist( [number] ) */
170 static int aiL_isbribed( lua_State *L ); /* bool isbribed( number ) */
171 static int aiL_getstanding( lua_State *L ); /* number getstanding( number ) */
172 
173 /* boolean expressions */
174 static int aiL_ismaxvel( lua_State *L ); /* boolean ismaxvel() */
175 static int aiL_isstopped( lua_State *L ); /* boolean isstopped() */
176 static int aiL_isenemy( lua_State *L ); /* boolean isenemy( number ) */
177 static int aiL_isally( lua_State *L ); /* boolean isally( number ) */
178 static int aiL_haslockon( lua_State *L ); /* boolean haslockon() */
179 
180 /* movement */
181 static int aiL_accel( lua_State *L ); /* accel(number); number <= 1. */
182 static int aiL_turn( lua_State *L ); /* turn(number); abs(number) <= 1. */
183 static int aiL_face( lua_State *L ); /* face( number/pointer, bool) */
184 static int aiL_careful_face( lua_State *L ); /* face( number/pointer, bool) */
185 static int aiL_aim( lua_State *L ); /* aim(number) */
186 static int aiL_iface( lua_State *L ); /* iface(number/pointer) */
187 static int aiL_dir( lua_State *L ); /* dir(number/pointer) */
188 static int aiL_idir( lua_State *L ); /* idir(number/pointer) */
189 static int aiL_drift_facing( lua_State *L ); /* drift_facing(number/pointer) */
190 static int aiL_brake( lua_State *L ); /* brake() */
191 static int aiL_getnearestplanet( lua_State *L ); /* Vec2 getnearestplanet() */
192 static int aiL_getrndplanet( lua_State *L ); /* Vec2 getrndplanet() */
193 static int aiL_getlandplanet( lua_State *L ); /* Vec2 getlandplanet() */
194 static int aiL_land( lua_State *L ); /* bool land() */
195 static int aiL_stop( lua_State *L ); /* stop() */
196 static int aiL_relvel( lua_State *L ); /* relvel( number ) */
197 static int aiL_follow_accurate( lua_State *L ); /* follow_accurate() */
198 
199 /* Hyperspace. */
200 static int aiL_sethyptarget( lua_State *L );
201 static int aiL_nearhyptarget( lua_State *L ); /* pointer rndhyptarget() */
202 static int aiL_rndhyptarget( lua_State *L ); /* pointer rndhyptarget() */
203 static int aiL_hyperspace( lua_State *L ); /* [number] hyperspace() */
204 
205 /* escorts */
206 static int aiL_dock( lua_State *L ); /* dock( number ) */
207 
208 /* combat */
209 static int aiL_combat( lua_State *L ); /* combat( number ) */
210 static int aiL_settarget( lua_State *L ); /* settarget( number ) */
211 static int aiL_weapSet( lua_State *L ); /* weapset( number ) */
212 static int aiL_shoot( lua_State *L ); /* shoot( number ); number = 1,2,3 */
213 static int aiL_hascannons( lua_State *L ); /* bool hascannons() */
214 static int aiL_hasturrets( lua_State *L ); /* bool hasturrets() */
215 static int aiL_hasjammers( lua_State *L ); /* bool hasjammers() */
216 static int aiL_hasafterburner( lua_State *L ); /* bool hasafterburner() */
217 static int aiL_getenemy( lua_State *L ); /* number getenemy() */
218 static int aiL_getenemy_size( lua_State *L ); /* number getenemy_size() */
219 static int aiL_getenemy_heuristic( lua_State *L ); /* number getenemy_heuristic() */
220 static int aiL_hostile( lua_State *L ); /* hostile( number ) */
221 static int aiL_getweaprange( lua_State *L ); /* number getweaprange() */
222 static int aiL_getweapspeed( lua_State *L ); /* number getweapspeed() */
223 static int aiL_canboard( lua_State *L ); /* boolean canboard( number ) */
224 static int aiL_relsize( lua_State *L ); /* boolean relsize( number ) */
225 static int aiL_reldps( lua_State *L ); /* boolean reldps( number ) */
226 static int aiL_relhp( lua_State *L ); /* boolean relhp( number ) */
227 
228 /* timers */
229 static int aiL_settimer( lua_State *L ); /* settimer( number, number ) */
230 static int aiL_timeup( lua_State *L ); /* boolean timeup( number ) */
231 
232 /* messages */
233 static int aiL_distress( lua_State *L ); /* distress( string [, bool] ) */
234 static int aiL_getBoss( lua_State *L ); /* number getBoss() */
235 
236 /* loot */
237 static int aiL_credits( lua_State *L ); /* credits( number ) */
238 
239 /* misc */
240 static int aiL_board( lua_State *L ); /* boolean board() */
241 static int aiL_refuel( lua_State *L ); /* boolean, boolean refuel() */
242 static int aiL_messages( lua_State *L );
243 
244 
245 static const luaL_reg aiL_methods[] = {
246    /* tasks */
247    { "pushtask", aiL_pushtask },
248    { "poptask", aiL_poptask },
249    { "taskname", aiL_taskname },
250    { "target", aiL_gettarget },
251    { "pushsubtask", aiL_pushsubtask },
252    { "popsubtask", aiL_popsubtask },
253    { "subtaskname", aiL_subtaskname },
254    { "subtarget", aiL_getsubtarget },
255    /* is */
256    { "ismaxvel", aiL_ismaxvel },
257    { "isstopped", aiL_isstopped },
258    { "isenemy", aiL_isenemy },
259    { "isally", aiL_isally },
260    { "haslockon", aiL_haslockon },
261    /* get */
262    { "pilot", aiL_pilot },
263    { "rndpilot", aiL_getrndpilot },
264    { "nearestpilot", aiL_getnearestpilot },
265    { "dist", aiL_getdistance },
266    { "flyby_dist", aiL_getflybydistance },
267    { "minbrakedist", aiL_minbrakedist },
268    { "isbribed", aiL_isbribed },
269    { "getstanding", aiL_getstanding },
270    /* movement */
271    { "nearestplanet", aiL_getnearestplanet },
272    { "rndplanet", aiL_getrndplanet },
273    { "landplanet", aiL_getlandplanet },
274    { "land", aiL_land },
275    { "accel", aiL_accel },
276    { "turn", aiL_turn },
277    { "face", aiL_face },
278    { "careful_face", aiL_careful_face },
279    { "iface", aiL_iface },
280    { "dir", aiL_dir },
281    { "idir", aiL_idir },
282    { "drift_facing", aiL_drift_facing },
283    { "brake", aiL_brake },
284    { "stop", aiL_stop },
285    { "relvel", aiL_relvel },
286    { "follow_accurate", aiL_follow_accurate },
287    /* Hyperspace. */
288    { "sethyptarget", aiL_sethyptarget },
289    { "nearhyptarget", aiL_nearhyptarget },
290    { "rndhyptarget", aiL_rndhyptarget },
291    { "hyperspace", aiL_hyperspace },
292    { "dock", aiL_dock },
293    /* combat */
294    { "aim", aiL_aim },
295    { "combat", aiL_combat },
296    { "settarget", aiL_settarget },
297    { "weapset", aiL_weapSet },
298    { "hascannons", aiL_hascannons },
299    { "hasturrets", aiL_hasturrets },
300    { "hasjammers", aiL_hasjammers },
301    { "hasafterburner", aiL_hasafterburner },
302    { "shoot", aiL_shoot },
303    { "getenemy", aiL_getenemy },
304    { "getenemy_size", aiL_getenemy_size },
305    { "getenemy_heuristic", aiL_getenemy_heuristic },
306    { "hostile", aiL_hostile },
307    { "getweaprange", aiL_getweaprange },
308    { "getweapspeed", aiL_getweapspeed },
309    { "canboard", aiL_canboard },
310    { "relsize", aiL_relsize },
311    { "reldps", aiL_reldps },
312    { "relhp", aiL_relhp },
313    /* timers */
314    { "settimer", aiL_settimer },
315    { "timeup", aiL_timeup },
316    /* messages */
317    { "distress", aiL_distress },
318    { "getBoss", aiL_getBoss },
319    /* loot */
320    { "setcredits", aiL_credits },
321    /* misc */
322    { "board", aiL_board },
323    { "refuel", aiL_refuel },
324    { "messages", aiL_messages },
325    {0,0} /* end */
326 }; /**< Lua AI Function table. */
327 
328 
329 
330 /*
331  * current pilot "thinking" and assorted variables
332  */
333 Pilot *cur_pilot           = NULL; /**< Current pilot.  All functions use this. */
334 static double pilot_acc    = 0.; /**< Current pilot's acceleration. */
335 static double pilot_turn   = 0.; /**< Current pilot's turning. */
336 static int pilot_flags     = 0; /**< Handle stuff like weapon firing. */
337 static char aiL_distressmsg[PATH_MAX]; /**< Buffer to store distress message. */
338 
339 /*
340  * ai status, used so that create functions can't be used elsewhere
341  */
342 #define AI_STATUS_NORMAL      1 /**< Normal AI function behaviour. */
343 #define AI_STATUS_CREATE      2 /**< AI is running create function. */
344 static int aiL_status = AI_STATUS_NORMAL; /**< Current AI run status. */
345 
346 
347 /**
348  * @brief Runs the garbage collector on the pilot's tasks.
349  *
350  *    @param pilot Pilot to clean up.
351  */
ai_taskGC(Pilot * pilot)352 static void ai_taskGC( Pilot* pilot )
353 {
354    Task *t, *prev, *pointer;
355 
356    prev  = NULL;
357    t     = pilot->task;
358    while (t != NULL) {
359       if (t->done) {
360          pointer = t;
361          /* Unattach pointer. */
362          t       = t->next;
363          if (prev == NULL)
364             pilot->task = t;
365          else
366             prev->next  = t;
367          /* Free pointer. */
368          pointer->next = NULL;
369          ai_freetask( pointer );
370       }
371       else {
372          prev    = t;
373          t       = t->next;
374       }
375    }
376 }
377 
378 
379 /**
380  * @brief Gets the current running task.
381  */
ai_curTask(Pilot * pilot)382 static Task* ai_curTask( Pilot* pilot )
383 {
384    Task *t;
385    /* Get last task. */
386    for (t=pilot->task; t!=NULL; t=t->next)
387       if (!t->done)
388          return t;
389    return NULL;
390 }
391 
392 
393 /**
394  * @brief Sets the cur_pilot's ai.
395  */
ai_setMemory(void)396 static void ai_setMemory (void)
397 {
398    nlua_env env;
399    env = cur_pilot->ai->env;
400 
401    nlua_getenv(env, AI_MEM); /* pm */
402    lua_rawgeti(naevL, -1, cur_pilot->id); /* pm, t */
403    nlua_setenv(env, "mem"); /* pm */
404    lua_pop(naevL, 1); /* */
405 }
406 
407 
408 /**
409  * @brief Sets the pilot for further AI calls.
410  *
411  *    @param p Pilot to set.
412  */
ai_setPilot(Pilot * p)413 void ai_setPilot( Pilot *p )
414 {
415    cur_pilot = p;
416    ai_setMemory();
417 }
418 
419 
420 /**
421  * @brief Attempts to run a function.
422  *
423  *    @param[in] L Lua state to run function on.
424  *    @param[in] funcname Function to run.
425  */
ai_run(nlua_env env,const char * funcname)426 static void ai_run( nlua_env env, const char *funcname )
427 {
428    nlua_getenv(env, funcname);
429 
430 #ifdef DEBUGGING
431    if (lua_isnil(naevL, -1)) {
432       WARN("Pilot '%s' ai -> '%s': attempting to run non-existant function",
433             cur_pilot->name, funcname );
434       lua_pop(naevL,1);
435       return;
436    }
437 #endif /* DEBUGGING */
438 
439    if (nlua_pcall(env, 0, 0)) { /* error has occurred */
440       WARN("Pilot '%s' ai -> '%s': %s", cur_pilot->name, funcname, lua_tostring(naevL,-1));
441       lua_pop(naevL,1);
442    }
443 }
444 
445 
446 /**
447  * @brief Initializes the pilot in the ai.
448  *
449  * Mainly used to create the pilot's memory table.
450  *
451  *    @param p Pilot to initialize in AI.
452  *    @param ai AI to initialize pilot.
453  *    @return 0 on success.
454  */
ai_pinit(Pilot * p,const char * ai)455 int ai_pinit( Pilot *p, const char *ai )
456 {
457    AI_Profile *prof;
458    char buf[PATH_MAX];
459 
460    strncpy(buf, ai, sizeof(buf));
461 
462    /* Set up the profile. */
463    prof = ai_getProfile(buf);
464    if (prof == NULL) {
465       WARN("AI Profile '%s' not found, using dummy fallback.", buf);
466       nsnprintf(buf, sizeof(buf), "dummy" );
467       prof = ai_getProfile(buf);
468    }
469    p->ai = prof;
470 
471    /* Adds a new pilot memory in the memory table. */
472    nlua_getenv(p->ai->env, AI_MEM);  /* pm */
473    lua_newtable(naevL);              /* pm, nt */
474    lua_pushvalue(naevL, -1);         /* pm, nt, nt */
475    lua_rawseti(naevL, -3, p->id);    /* pm, nt */
476 
477    /* Copy defaults over. */
478    lua_pushstring(naevL, AI_MEM_DEF);/* pm, nt, s */
479    lua_gettable(naevL, -3);          /* pm, nt, dt */
480 #if DEBUGGING
481    if (lua_isnil(naevL,-1))
482       WARN( "AI profile '%s' has no default memory for pilot '%s'.",
483             buf, p->name );
484 #endif
485    lua_pushnil(naevL);               /* pm, nt, dt, nil */
486    while (lua_next(naevL,-2) != 0) { /* pm, nt, dt, k, v */
487       lua_pushvalue(naevL,-2);       /* pm, nt, dt, k, v, k */
488       lua_pushvalue(naevL,-2);       /* pm, nt, dt, k, v, k, v */
489       lua_remove(naevL, -3);         /* pm, nt, dt, k, k, v */
490       lua_settable(naevL,-5);        /* pm, nt, dt, k */
491    }                             /* pm, nt, dt */
492    lua_pop(naevL,3);                 /* */
493 
494    /* Create the pilot. */
495    ai_create( p );
496    pilot_setFlag(p, PILOT_CREATED_AI);
497 
498    /* Set fuel.  Hack until we do it through AI itself. */
499    if (!pilot_isPlayer(p)) {
500       p->fuel  = (RNG_2SIGMA()/4. + 0.5) * (p->fuel_max - p->fuel_consumption);
501       p->fuel += p->fuel_consumption;
502    }
503 
504    return 0;
505 }
506 
507 
508 /**
509  * @brief Clears the pilot's tasks.
510  *
511  *    @param p Pilot to clear tasks of.
512  */
ai_cleartasks(Pilot * p)513 void ai_cleartasks( Pilot* p )
514 {
515    /* Clean up tasks. */
516    if (p->task)
517       ai_freetask( p->task );
518    p->task = NULL;
519 }
520 
521 
522 /**
523  * @brief Destroys the ai part of the pilot
524  *
525  *    @param[in] p Pilot to destroy its AI part.
526  */
ai_destroy(Pilot * p)527 void ai_destroy( Pilot* p )
528 {
529    nlua_env env;
530    env = p->ai->env;
531 
532    /* Get rid of pilot's memory. */
533    if (!pilot_isPlayer(p)) { /* Player is an exception as more than one ship shares pilot id. */
534       nlua_getenv(env, AI_MEM);  /* t */
535       lua_pushnil(naevL);        /* t, nil */
536       lua_rawseti(naevL,-2, p->id);/* t */
537       lua_pop(naevL, 1);         /* */
538    }
539 
540    /* Clear the tasks. */
541    ai_cleartasks( p );
542 }
543 
544 
545 /**
546  * @brief Initializes the AI stuff which is basically Lua.
547  *
548  *    @return 0 on no errors.
549  */
ai_load(void)550 int ai_load (void)
551 {
552    char** files;
553    uint32_t nfiles, i;
554    char path[PATH_MAX];
555    int flen, suflen;
556    int n;
557 
558    /* get the file list */
559    files = ndata_list( AI_PATH, &nfiles );
560 
561    /* load the profiles */
562    suflen = strlen(AI_SUFFIX);
563    for (i=0; i<nfiles; i++) {
564       flen = strlen(files[i]);
565       if ((flen > suflen) &&
566             strncmp(&files[i][flen-suflen], AI_SUFFIX, suflen)==0) {
567 
568          nsnprintf( path, PATH_MAX, AI_PATH"%s", files[i] );
569          if (ai_loadProfile(path)) /* Load the profile */
570             WARN("Error loading AI profile '%s'", path);
571       }
572 
573       /* Clean up. */
574       free(files[i]);
575    }
576 
577    n = array_size(profiles);
578    DEBUG("Loaded %d AI Profile%c", n, (n==1)?' ':'s');
579 
580    /* More clean up. */
581    free(files);
582 
583    /* Load equipment thingy. */
584    return ai_loadEquip();
585 }
586 
587 
588 /**
589  * @brief Loads the equipment selector script.
590  */
ai_loadEquip(void)591 static int ai_loadEquip (void)
592 {
593    char *buf;
594    uint32_t bufsize;
595    const char *filename = "dat/factions/equip/generic.lua";
596 
597    /* Make sure doesn't already exist. */
598    if (equip_env != LUA_NOREF)
599       nlua_freeEnv(equip_env);
600 
601    /* Create new state. */
602    equip_env = nlua_newEnv(1);
603    nlua_loadStandard(equip_env);
604 
605    /* Load the file. */
606    buf = ndata_read( filename, &bufsize );
607    if (nlua_dobufenv(equip_env, buf, bufsize, filename) != 0) {
608       WARN("Error loading file: %s\n"
609           "%s\n"
610           "Most likely Lua file has improper syntax, please check",
611             filename, lua_tostring(naevL, -1));
612       return -1;
613    }
614    free(buf);
615 
616    return 0;
617 }
618 
619 
620 /**
621  * @brief Initializes an AI_Profile and adds it to the stack.
622  *
623  *    @param[in] filename File to create the profile from.
624  *    @return 0 on no error.
625  */
ai_loadProfile(const char * filename)626 static int ai_loadProfile( const char* filename )
627 {
628    char* buf = NULL;
629    uint32_t bufsize = 0;
630    nlua_env env;
631    AI_Profile *prof;
632    size_t len;
633 
634    /* Create array if necessary. */
635    if (profiles == NULL)
636       profiles = array_create( AI_Profile );
637 
638    /* Grow array. */
639    prof = &array_grow(&profiles);
640 
641    /* Set name. */
642    len = strlen(filename)-strlen(AI_PATH)-strlen(AI_SUFFIX);
643    prof->name = malloc(len+1);
644    strncpy( prof->name, &filename[strlen(AI_PATH)], len );
645    prof->name[len] = '\0';
646 
647    /* Create Lua. */
648    env = nlua_newEnv(1);
649    nlua_loadStandard(env);
650    prof->env = env;
651 
652    /* Register C functions in Lua */
653    nlua_register(env, "ai", aiL_methods, 0);
654 
655    /* Add the player memory table. */
656    lua_newtable(naevL);              /* pm */
657    lua_pushvalue(naevL, -1);         /* pm, pm */
658    nlua_setenv(env, AI_MEM);         /* pm */
659 
660    /* Set "mem" to be default template. */
661    lua_newtable(naevL);              /* pm, nt */
662    lua_pushvalue(naevL,-1);          /* pm, nt, nt */
663    lua_setfield(naevL,-3,AI_MEM_DEF); /* pm, nt */
664    nlua_setenv(env, "mem");          /* pm */
665    lua_pop(naevL, 1);                /*  */
666 
667    /* Now load the file since all the functions have been previously loaded */
668    buf = ndata_read( filename, &bufsize );
669    if (nlua_dobufenv(env, buf, bufsize, filename) != 0) {
670       WARN("Error loading AI file: %s\n"
671           "%s\n"
672           "Most likely Lua file has improper syntax, please check",
673             filename, lua_tostring(naevL,-1));
674       array_erase( &profiles, prof, &prof[1] );
675       free(prof->name);
676       nlua_freeEnv( env );
677       free(buf);
678       return -1;
679    }
680    free(buf);
681 
682    return 0;
683 }
684 
685 
686 /**
687  * @brief Gets the AI_Profile by name.
688  *
689  *    @param[in] name Name of the profile to get.
690  *    @return The profile or NULL on error.
691  */
ai_getProfile(char * name)692 AI_Profile* ai_getProfile( char* name )
693 {
694    int i;
695 
696    if (profiles == NULL)
697       return NULL;
698 
699    for (i=0; i<array_size(profiles); i++)
700       if (strcmp(name,profiles[i].name)==0)
701          return &profiles[i];
702 
703    WARN("AI Profile '%s' not found in AI stack", name);
704    return NULL;
705 }
706 
707 
708 /**
709  * @brief Cleans up global AI.
710  */
ai_exit(void)711 void ai_exit (void)
712 {
713    int i;
714 
715    /* Free AI profiles. */
716    for (i=0; i<array_size(profiles); i++) {
717       free(profiles[i].name);
718       nlua_freeEnv(profiles[i].env);
719    }
720    array_free( profiles );
721 
722    /* Free equipment Lua. */
723    if (equip_env != LUA_NOREF)
724       nlua_freeEnv(equip_env);
725    equip_env = LUA_NOREF;
726 }
727 
728 
729 /**
730  * @brief Heart of the AI, brains of the pilot.
731  *
732  *    @param pilot Pilot that needs to think.
733  */
ai_think(Pilot * pilot,const double dt)734 void ai_think( Pilot* pilot, const double dt )
735 {
736    nlua_env env;
737    (void) dt;
738 
739    Task *t;
740 
741    /* Must have AI. */
742    if (cur_pilot->ai == NULL)
743       return;
744 
745    ai_setPilot(pilot);
746    env = cur_pilot->ai->env; /* set the AI profile to the current pilot's */
747 
748    /* Clean up some variables */
749    pilot_acc         = 0;
750    pilot_turn        = 0.;
751    pilot_flags       = 0;
752    /* pilot_setTarget( cur_pilot, cur_pilot->id ); */
753    pilot_weapSetAIClear( cur_pilot ); /* Hack so shit works. TODO fix. */
754 
755    /* Get current task. */
756    t = ai_curTask( cur_pilot );
757 
758    /* control function if pilot is idle or tick is up */
759    if ((cur_pilot->tcontrol < 0.) || (t == NULL)) {
760       if (pilot_isFlag(pilot,PILOT_PLAYER) ||
761           pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL)) {
762          nlua_getenv(env, "control_manual");
763          if (!lua_isnil(naevL, -1))
764             ai_run(env, "control_manual");
765          lua_pop(naevL, 1);
766       } else {
767          ai_run(env, "control"); /* run control */
768       }
769 
770       nlua_getenv(env, "control_rate");
771       cur_pilot->tcontrol = lua_tonumber(naevL,-1);
772       lua_pop(naevL,1);
773 
774       /* Task may have changed due to control tick. */
775       t = ai_curTask( cur_pilot );
776    }
777 
778    if (pilot_isFlag(pilot,PILOT_PLAYER) &&
779        !pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL))
780       return;
781 
782    /* pilot has a currently running task */
783    if (t != NULL) {
784       /* Run subtask if available, otherwise run main task. */
785       if (t->subtask != NULL)
786          ai_run(env, t->subtask->name);
787       else
788          ai_run(env, t->name);
789 
790       /* Manual control must check if IDLE hook has to be run. */
791       if (pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL)) {
792          /* We must yet check again to see if there still is a current task running. */
793          if (ai_curTask( cur_pilot ) == NULL)
794             pilot_runHook( cur_pilot, PILOT_HOOK_IDLE );
795       }
796    }
797 
798    /* make sure pilot_acc and pilot_turn are legal */
799    pilot_acc   = CLAMP( -1., 1., pilot_acc );
800    pilot_turn  = CLAMP( -1., 1., pilot_turn );
801 
802    /* Set turn and thrust. */
803    pilot_setTurn( cur_pilot, pilot_turn );
804    pilot_setThrust( cur_pilot, pilot_acc );
805 
806    /* fire weapons if needed */
807    if (ai_isFlag(AI_PRIMARY))
808       pilot_shoot(cur_pilot, 0); /* primary */
809    if (ai_isFlag(AI_SECONDARY))
810       pilot_shoot(cur_pilot, 1 ); /* secondary */
811 
812    /* other behaviours. */
813    if (ai_isFlag(AI_DISTRESS))
814       pilot_distress(cur_pilot, NULL, aiL_distressmsg, 0);
815 
816    /* Clean up if necessary. */
817    ai_taskGC( cur_pilot );
818 }
819 
820 
821 /**
822  * @brief Triggers the attacked() function in the pilot's AI.
823  *
824  *    @param attacked Pilot that is attacked.
825  *    @param[in] attacker ID of the attacker.
826  *    @param[i] dmg Damage done by the attacker.
827  */
ai_attacked(Pilot * attacked,const unsigned int attacker,double dmg)828 void ai_attacked( Pilot* attacked, const unsigned int attacker, double dmg )
829 {
830    HookParam hparam[2];
831 
832    /* Custom hook parameters. */
833    hparam[0].type       = HOOK_PARAM_PILOT;
834    hparam[0].u.lp       = attacker;
835    hparam[1].type       = HOOK_PARAM_NUMBER;
836    hparam[1].u.num      = dmg;
837 
838    /* Behaves differently if manually overridden. */
839    pilot_runHookParam( attacked, PILOT_HOOK_ATTACKED, hparam, 2 );
840    if (pilot_isFlag( attacked, PILOT_MANUAL_CONTROL ))
841       return;
842 
843    /* Must have an AI profile and not be player. */
844    if (attacked->ai == NULL)
845       return;
846 
847    ai_setPilot( attacked ); /* Sets cur_pilot. */
848 
849    nlua_getenv(cur_pilot->ai->env, "attacked");
850 
851    lua_pushpilot(naevL, attacker);
852    if (nlua_pcall(cur_pilot->ai->env, 1, 0)) {
853       WARN("Pilot '%s' ai -> 'attacked': %s", cur_pilot->name, lua_tostring(naevL, -1));
854       lua_pop(naevL, 1);
855    }
856 }
857 
858 
859 /**
860  * @brief Has a pilot attempt to refuel the other.
861  *
862  *    @param refueler Pilot doing the refueling.
863  *    @param target Pilot to refuel.
864  */
ai_refuel(Pilot * refueler,unsigned int target)865 void ai_refuel( Pilot* refueler, unsigned int target )
866 {
867    Task *t;
868 
869    /* Create the task. */
870    t           = calloc( 1, sizeof(Task) );
871    t->name     = strdup("refuel");
872    lua_pushpilot(naevL, target);
873    t->dat      = luaL_ref(naevL, LUA_REGISTRYINDEX);
874 
875    /* Prepend the task. */
876    t->next     = refueler->task;
877    refueler->task = t;
878 
879    return;
880 }
881 
882 
883 /**
884  * @brief Sends a distress signal to a pilot.
885  *
886  *    @param p Pilot receiving the distress signal.
887  *    @param distressed Pilot sending the distress signal.
888  */
ai_getDistress(Pilot * p,const Pilot * distressed,const Pilot * attacker)889 void ai_getDistress( Pilot *p, const Pilot *distressed, const Pilot *attacker )
890 {
891    /* Ignore distress signals when under manual control. */
892    if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ))
893       return;
894 
895    /* Must have AI. */
896    if (cur_pilot->ai == NULL)
897       return;
898 
899    /* Set up the environment. */
900    ai_setPilot(p);
901 
902    /* See if function exists. */
903    nlua_getenv(cur_pilot->ai->env, "distress");
904    if (lua_isnil(naevL,-1)) {
905       lua_pop(naevL,1);
906       return;
907    }
908 
909    /* Run the function. */
910    lua_pushpilot(naevL, distressed->id);
911    if (attacker != NULL)
912       lua_pushpilot(naevL, attacker->id);
913    else /* Default to the victim's current target. */
914       lua_pushpilot(naevL, distressed->target);
915 
916    if (nlua_pcall(cur_pilot->ai->env, 2, 0)) {
917       WARN("Pilot '%s' ai -> 'distress': %s", cur_pilot->name, lua_tostring(naevL,-1));
918       lua_pop(naevL,1);
919    }
920 }
921 
922 
923 /**
924  * @brief Runs the create() function in the pilot.
925  *
926  * Should create all the gear and such the pilot has.
927  *
928  *    @param pilot Pilot to "create".
929  */
ai_create(Pilot * pilot)930 static void ai_create( Pilot* pilot )
931 {
932    nlua_env env;
933    char *func;
934 
935    env = equip_env;
936    func = "equip_generic";
937 
938    /* Set creation mode. */
939    if (!pilot_isFlag(pilot, PILOT_CREATED_AI))
940       aiL_status = AI_STATUS_CREATE;
941 
942    /* Create equipment first - only if creating for the first time. */
943    if (!pilot_isFlag(pilot,PILOT_PLAYER) && (aiL_status==AI_STATUS_CREATE) &&
944             !pilot_isFlag(pilot, PILOT_EMPTY)) {
945       if  (faction_getEquipper( pilot->faction ) != LUA_NOREF) {
946          env = faction_getEquipper( pilot->faction );
947          func = "equip";
948       }
949       nlua_getenv(env, func);
950       nlua_pushenv(env);
951       lua_setfenv(naevL, -2);
952       lua_pushpilot(naevL, pilot->id);
953       if (nlua_pcall(env, 1, 0)) { /* Error has occurred. */
954          WARN("Pilot '%s' equip -> '%s': %s", pilot->name, func, lua_tostring(naevL, -1));
955          lua_pop(naevL, 1);
956       }
957    }
958 
959    /* Since the pilot changes outfits and cores, we must heal him up. */
960    pilot_healLanded( pilot );
961 
962    /* Must have AI. */
963    if (pilot->ai == NULL)
964       return;
965 
966    /* Prepare AI (this sets cur_pilot among others). */
967    ai_setPilot( pilot );
968 
969    /* Prepare stack. */
970    nlua_getenv(cur_pilot->ai->env, "create");
971 
972    /* Run function. */
973    if (nlua_pcall(cur_pilot->ai->env, 0, 0)) { /* error has occurred */
974       WARN("Pilot '%s' ai -> '%s': %s", cur_pilot->name, "create", lua_tostring(naevL,-1));
975       lua_pop(naevL,1);
976    }
977 
978    /* Recover normal mode. */
979    if (!pilot_isFlag(pilot, PILOT_CREATED_AI))
980       aiL_status = AI_STATUS_NORMAL;
981 }
982 
983 
984 /**
985  * @brief Creates a new AI task.
986  */
ai_newtask(Pilot * p,const char * func,int subtask,int pos)987 Task *ai_newtask( Pilot *p, const char *func, int subtask, int pos )
988 {
989    Task *t, *curtask, *pointer;
990 
991    /* Create the new task. */
992    t           = calloc( 1, sizeof(Task) );
993    t->name     = strdup(func);
994    lua_pushnil(naevL);
995    t->dat      = luaL_ref(naevL, LUA_REGISTRYINDEX);
996 
997    /* Handle subtask and general task. */
998    if (!subtask) {
999       if ((pos == 1) && (p->task != NULL)) { /* put at the end */
1000          for (pointer = p->task; pointer->next != NULL; pointer = pointer->next);
1001          pointer->next = t;
1002       }
1003       else {
1004          t->next = p->task;
1005          p->task = t;
1006       }
1007    }
1008    else {
1009       /* Must have valid task. */
1010       curtask = ai_curTask( p );
1011       if (curtask == NULL) {
1012          WARN("Trying to add subtask '%s' to non-existant task.", func);
1013          ai_freetask( t );
1014          return NULL;
1015       }
1016 
1017       /* Add the subtask. */
1018       if ((pos == 1) && (curtask->subtask != NULL)) { /* put at the end */
1019          for (pointer = curtask->subtask; pointer->next != NULL; pointer = pointer->next);
1020          pointer->next = t;
1021       }
1022       else {
1023          t->next           = curtask->subtask;
1024          curtask->subtask  = t;
1025       }
1026    }
1027 
1028    return t;
1029 }
1030 
1031 
1032 /**
1033  * @brief Frees an AI task.
1034  *
1035  *    @param t Task to free.
1036  */
ai_freetask(Task * t)1037 void ai_freetask( Task* t )
1038 {
1039    luaL_unref(naevL, LUA_REGISTRYINDEX, t->dat);
1040 
1041    /* Recursive subtask freeing. */
1042    if (t->subtask != NULL) {
1043       ai_freetask(t->subtask);
1044       t->subtask = NULL;
1045    }
1046 
1047    /* Free next task in the chain. */
1048    if (t->next != NULL) {
1049       ai_freetask(t->next); /* yay recursive freeing */
1050       t->next = NULL;
1051    }
1052 
1053    if (t->name)
1054       free(t->name);
1055    free(t);
1056 }
1057 
1058 
1059 /**
1060  * @brief Creates a new task based on stack information.
1061  */
ai_createTask(lua_State * L,int subtask)1062 static Task* ai_createTask( lua_State *L, int subtask )
1063 {
1064    const char *func;
1065    Task *t;
1066 
1067    /* Parse basic parameters. */
1068    func  = luaL_checkstring(L,1);
1069 
1070    /* Creates a new AI task. */
1071    t     = ai_newtask( cur_pilot, func, subtask, 0 );
1072 
1073    /* Set the data. */
1074    if (lua_gettop(L) > 1) {
1075       t->dat = luaL_ref(L, LUA_REGISTRYINDEX);
1076    }
1077 
1078    return t;
1079 }
1080 
1081 
1082 /**
1083  * @brief Pushes a task target.
1084  */
ai_tasktarget(lua_State * L,Task * t)1085 static int ai_tasktarget( lua_State *L, Task *t )
1086 {
1087    lua_rawgeti(L, LUA_REGISTRYINDEX, t->dat);
1088    return 1;
1089 }
1090 
1091 
1092 /**
1093  * @defgroup AI Lua AI Bindings
1094  *
1095  * @brief Handles how the AI interacts with the universe.
1096  *
1097  * Usage is:
1098  * @code
1099  * ai.function( params )
1100  * @endcode
1101  *
1102  * @luamod ai
1103  *
1104  * @{
1105  */
1106 /**
1107  * @brief Pushes a task onto the pilot's task list.
1108  *    @luatparam string func Name of function to call for task.
1109  *    @luaparam[opt] data Data to pass to the function.  Supports any lua type.
1110  * @luafunc pushtask( func, data )
1111  *    @param L Lua state.
1112  *    @return Number of Lua parameters.
1113  */
aiL_pushtask(lua_State * L)1114 static int aiL_pushtask( lua_State *L )
1115 {
1116    ai_createTask( L, 0 );
1117 
1118    return 0;
1119 }
1120 /**
1121  * @brief Pops the current running task.
1122  * @luafunc poptask()
1123  *    @param L Lua state.
1124  *    @return Number of Lua parameters.
1125  */
aiL_poptask(lua_State * L)1126 static int aiL_poptask( lua_State *L )
1127 {
1128    Task* t = ai_curTask( cur_pilot );
1129 
1130    /* Tasks must exist. */
1131    if (t == NULL) {
1132       NLUA_ERROR(L, "Trying to pop task when there are no tasks on the stack.");
1133       return 0;
1134    }
1135 
1136    t->done = 1;
1137    return 0;
1138 }
1139 
1140 /**
1141  * @brief Gets the current task's name.
1142  *    @luatreturn string The current task name or nil if there are no tasks.
1143  * @luafunc taskname()
1144  *    @param L Lua state.
1145  *    @return Number of Lua parameters.
1146  */
aiL_taskname(lua_State * L)1147 static int aiL_taskname( lua_State *L )
1148 {
1149    Task *t = ai_curTask( cur_pilot );
1150    if (t)
1151       lua_pushstring(L, t->name);
1152    else
1153       lua_pushnil(L);
1154    return 1;
1155 }
1156 
1157 /**
1158  * @brief Gets the pilot's task target.
1159  *    @luareturn The pilot's target ship identifier or nil if no target.
1160  *    @luasee pushtask
1161  * @luafunc target()
1162  *    @param L Lua state.
1163  *    @return Number of Lua parameters.
1164  */
aiL_gettarget(lua_State * L)1165 static int aiL_gettarget( lua_State *L )
1166 {
1167    Task *t = ai_curTask( cur_pilot );
1168 
1169    /* Must have a task. */
1170    if (t == NULL)
1171       return 0;
1172 
1173    return ai_tasktarget( L, t );
1174 }
1175 
1176 /**
1177  * @brief Pushes a subtask onto the pilot's task's subtask list.
1178  *    @luatparam string func Name of function to call for task.
1179  *    @luaparam[opt] data Data to pass to the function.  Supports any lua type.
1180  * @luafunc pushsubtask( func, data )
1181  *    @param L Lua state.
1182  *    @return Number of Lua parameters.
1183  */
aiL_pushsubtask(lua_State * L)1184 static int aiL_pushsubtask( lua_State *L )
1185 {
1186    ai_createTask(L, 1);
1187    return 0;
1188 }
1189 
1190 /**
1191  * @brief Pops the current running task.
1192  * @luafunc popsubtask()
1193  *    @param L Lua state.
1194  *    @return Number of Lua parameters.
1195  */
aiL_popsubtask(lua_State * L)1196 static int aiL_popsubtask( lua_State *L )
1197 {
1198    Task *t, *st;
1199    t = ai_curTask( cur_pilot );
1200 
1201    /* Tasks must exist. */
1202    if (t == NULL) {
1203       NLUA_ERROR(L, "Trying to pop task when there are no tasks on the stack.");
1204       return 0;
1205    }
1206    if (t->subtask == NULL) {
1207       NLUA_ERROR(L, "Trying to pop subtask when there are no subtasks for the task '%s'.", t->name);
1208       return 0;
1209    }
1210 
1211    /* Exterminate, annihilate destroy. */
1212    st          = t->subtask;
1213    t->subtask  = st->next;
1214    st->next    = NULL;
1215    ai_freetask(st);
1216    return 0;
1217 }
1218 
1219 /**
1220  * @brief Gets the current subtask's name.
1221  *    @luatreturn string The current subtask name or nil if there are no subtasks.
1222  * @luafunc subtaskname()
1223  *    @param L Lua state.
1224  *    @return Number of Lua parameters.
1225  */
aiL_subtaskname(lua_State * L)1226 static int aiL_subtaskname( lua_State *L )
1227 {
1228    Task *t = ai_curTask( cur_pilot );
1229    if ((t != NULL) && (t->subtask != NULL))
1230       lua_pushstring(L, t->subtask->name);
1231    else
1232       lua_pushnil(L);
1233    return 1;
1234 }
1235 
1236 /**
1237  * @brief Gets the pilot's subtask target.
1238  *    @luareturn The pilot's target ship identifier or nil if no target.
1239  *    @luasee pushsubtask
1240  * @luafunc subtarget()
1241  *    @param L Lua state.
1242  *    @return Number of Lua parameters.
1243  */
aiL_getsubtarget(lua_State * L)1244 static int aiL_getsubtarget( lua_State *L )
1245 {
1246    Task *t = ai_curTask( cur_pilot );
1247    /* Must have a subtask. */
1248    if ((t == NULL) || (t->subtask == NULL))
1249       return 0;
1250 
1251    return ai_tasktarget( L, t->subtask );
1252 }
1253 
1254 
1255 /**
1256  * @brief Gets the AI's pilot.
1257  *    @luatreturn Pilot The AI's pilot.
1258  * @luafunc pilot()
1259  *    @param L Lua state.
1260  *    @return Number of Lua parameters.
1261  */
aiL_pilot(lua_State * L)1262 static int aiL_pilot( lua_State *L )
1263 {
1264    lua_pushpilot(L, cur_pilot->id);
1265    return 1;
1266 }
1267 
1268 
1269 /**
1270  * @brief Gets a random pilot in the system.
1271  *    @luatreturn Pilot|nil
1272  * @luafunc rndpilot()
1273  *    @param L Lua state.
1274  *    @return Number of Lua parameters.
1275  */
aiL_getrndpilot(lua_State * L)1276 static int aiL_getrndpilot( lua_State *L )
1277 {
1278    int p;
1279 
1280    p = RNG(0, pilot_nstack-1);
1281    /* Make sure it can't be the same pilot. */
1282    if (pilot_stack[p]->id == cur_pilot->id) {
1283       p++;
1284       if (p >= pilot_nstack)
1285          p = 0;
1286    }
1287    /* Last check. */
1288    if (pilot_stack[p]->id == cur_pilot->id)
1289       return 0;
1290    /* Actually found a pilot. */
1291    lua_pushpilot(L, pilot_stack[p]->id);
1292    return 1;
1293 }
1294 
1295 /**
1296  * @brief gets the nearest pilot to the current pilot
1297  *
1298  *    @luatreturn Pilot|nil
1299  *    @luafunc nearestpilot()
1300  */
aiL_getnearestpilot(lua_State * L)1301 static int aiL_getnearestpilot( lua_State *L )
1302 {
1303 
1304    /*dist will be initialized to a number*/
1305    /*this will only seek out pilots closer than dist*/
1306    int dist=1000;
1307    int i;
1308    int candidate_id = -1;
1309 
1310    /*cycle through all the pilots and find the closest one that is not the pilot */
1311 
1312    for(i = 0; i<pilot_nstack; i++)
1313    {
1314        if(pilot_stack[i]->id != cur_pilot->id && vect_dist(&pilot_stack[i]->solid->pos, &cur_pilot->solid->pos) < dist)
1315        {
1316             dist = vect_dist(&pilot_stack[i]->solid->pos, &cur_pilot->solid->pos);
1317             candidate_id = i;
1318        }
1319    }
1320 
1321    /* Last check. */
1322    if (candidate_id == -1)
1323       return 0;
1324 
1325    /* Actually found a pilot. */
1326    lua_pushpilot(L, pilot_stack[candidate_id]->id);
1327    return 1;
1328 }
1329 
1330 /**
1331  * @brief Gets the distance from the pointer.
1332  *
1333  *    @luatparam Vec2|Pilot pointer
1334  *    @luatreturn number The distance from the pointer.
1335  *    @luafunc dist( pointer )
1336  */
aiL_getdistance(lua_State * L)1337 static int aiL_getdistance( lua_State *L )
1338 {
1339    Vector2d *v;
1340    Pilot *p;
1341 
1342    /* vector as a parameter */
1343    if (lua_isvector(L,1))
1344       v = lua_tovector(L,1);
1345 
1346    /* pilot as parameter */
1347    else if (lua_ispilot(L,1)) {
1348       p = luaL_validpilot(L,1);
1349       v = &p->solid->pos;
1350    }
1351 
1352    /* wrong parameter */
1353    else
1354       NLUA_INVALID_PARAMETER(L);
1355 
1356    lua_pushnumber(L, vect_dist(v, &cur_pilot->solid->pos));
1357    return 1;
1358 }
1359 
1360 /**
1361  * @brief Gets the distance from the pointer perpendicular to the current pilot's flight vector.
1362  *
1363  *    @luatparam Vec2|Pilot pointer
1364  *    @luatreturn number offset_distance
1365  *    @luafunc flyby_dist( pointer )
1366  */
aiL_getflybydistance(lua_State * L)1367 static int aiL_getflybydistance( lua_State *L )
1368 {
1369    Vector2d *v;
1370    Vector2d perp_motion_unit, offset_vect;
1371    Pilot *p;
1372    int offset_distance;
1373 
1374    v = NULL;
1375 
1376    /* vector as a parameter */
1377    if (lua_isvector(L,1))
1378       v = lua_tovector(L,1);
1379    /* pilot id as parameter */
1380    else if (lua_ispilot(L,1)) {
1381       p = luaL_validpilot(L,1);
1382       v = &p->solid->pos;
1383 
1384       /*vect_cset(&v, VX(pilot->solid->pos) - VX(cur_pilot->solid->pos), VY(pilot->solid->pos) - VY(cur_pilot->solid->pos) );*/
1385    }
1386    else {
1387       NLUA_INVALID_PARAMETER(L);
1388       return 0;
1389    }
1390 
1391    vect_cset(&offset_vect, VX(*v) - VX(cur_pilot->solid->pos), VY(*v) - VY(cur_pilot->solid->pos) );
1392    vect_pset(&perp_motion_unit, 1, VANGLE(cur_pilot->solid->vel)+M_PI_2);
1393    offset_distance = vect_dot(&perp_motion_unit, &offset_vect);
1394 
1395    lua_pushnumber(L, offset_distance);
1396    return 1;
1397 }
1398 
1399 /**
1400  * @brief Gets the minimum braking distance.
1401  *
1402  * braking vel ==> 0 = v - a*dt
1403  * add turn around time (to inital vel) ==> 180.*360./cur_pilot->turn
1404  * add it to general euler equation  x = v * t + 0.5 * a * t^2
1405  * and voila!
1406  *
1407  * I hate this function and it'll probably need to get changed in the future
1408  *
1409  *
1410  *    @luatreturn number Minimum braking distance.
1411  *    @luafunc minbrakedist()
1412  */
aiL_minbrakedist(lua_State * L)1413 static int aiL_minbrakedist( lua_State *L )
1414 {
1415    double time, dist, vel;
1416    Vector2d vv;
1417    Pilot *p;
1418 
1419    /* More complicated calculation based on relative velocity. */
1420    if (lua_gettop(L) > 0) {
1421       p = luaL_validpilot(L,1);
1422 
1423       /* Set up the vectors. */
1424       vect_cset( &vv, p->solid->vel.x - cur_pilot->solid->vel.x,
1425             p->solid->vel.y - cur_pilot->solid->vel.y );
1426 
1427       /* Run the same calculations. */
1428       time = VMOD(vv) /
1429             (cur_pilot->thrust / cur_pilot->solid->mass);
1430 
1431       /* Get relative velocity. */
1432       vel = MIN(cur_pilot->speed - VMOD(p->solid->vel), VMOD(vv));
1433       if (vel < 0.)
1434          vel = 0.;
1435    }
1436 
1437    /* Simple calculation based on distance. */
1438    else {
1439       /* Get current time to reach target. */
1440       time = VMOD(cur_pilot->solid->vel) /
1441             (cur_pilot->thrust / cur_pilot->solid->mass);
1442 
1443       /* Get velocity. */
1444       vel = MIN(cur_pilot->speed,VMOD(cur_pilot->solid->vel));
1445    }
1446    /* Get distance to brake. */
1447    dist = vel*(time+1.1*M_PI/cur_pilot->turn) -
1448          0.5*(cur_pilot->thrust/cur_pilot->solid->mass)*time*time;
1449 
1450    lua_pushnumber(L, dist); /* return */
1451    return 1; /* returns one thing */
1452 }
1453 
1454 
1455 /**
1456  * @brief Checks to see if target has bribed pilot.
1457  *
1458  *    @luatparam Pilot target
1459  *    @luatreturn boolean Whether the target has bribed pilot.
1460  *    @luafunc isbribed( target )
1461  */
aiL_isbribed(lua_State * L)1462 static int aiL_isbribed( lua_State *L )
1463 {
1464    Pilot *p;
1465    p = luaL_validpilot(L,1);
1466    lua_pushboolean(L, (p->id == PLAYER_ID) && pilot_isFlag(cur_pilot,PILOT_BRIBED));
1467    return 1;
1468 }
1469 
1470 
1471 /**
1472  * @brief Gets the standing of the target pilot with the current pilot.
1473  *
1474  *    @luatparam Pilot target Pilot to get faction standing of.
1475  *    @luatreturn number|nil The faction standing of the target [-100,100] or nil if invalid.
1476  * @luafunc getstanding( target )
1477  */
aiL_getstanding(lua_State * L)1478 static int aiL_getstanding( lua_State *L )
1479 {
1480    Pilot *p;
1481 
1482    /* Get parameters. */
1483    p = luaL_validpilot(L,1);
1484 
1485    /* Get faction standing. */
1486    if (p->faction == FACTION_PLAYER)
1487       lua_pushnumber(L, faction_getPlayer(cur_pilot->faction));
1488    else {
1489       if (areAllies( cur_pilot->faction, p->faction ))
1490          lua_pushnumber(L, 100);
1491       else if (areEnemies( cur_pilot->faction, p->faction ))
1492          lua_pushnumber(L,-100);
1493       else
1494          lua_pushnumber(L, 0);
1495    }
1496 
1497    return 1;
1498 }
1499 
1500 
1501 /**
1502  * @brief Checks to see if pilot is at maximum velocity.
1503  *
1504  *    @luatreturn boolean Whether the pilot is at maximum velocity.
1505  *    @luafunc ismaxvel()
1506  */
aiL_ismaxvel(lua_State * L)1507 static int aiL_ismaxvel( lua_State *L )
1508 {
1509    lua_pushboolean(L,(VMOD(cur_pilot->solid->vel) > cur_pilot->speed-MIN_VEL_ERR));
1510    return 1;
1511 }
1512 
1513 
1514 /**
1515  * @brief Checks to see if pilot is stopped.
1516  *
1517  *    @luatreturn boolean Whether the pilot is stopped.
1518  *    @luafunc isstopped()
1519  */
aiL_isstopped(lua_State * L)1520 static int aiL_isstopped( lua_State *L )
1521 {
1522    lua_pushboolean(L,(VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR));
1523    return 1;
1524 }
1525 
1526 
1527 /**
1528  * @brief Checks to see if target is an enemy.
1529  *
1530  *    @luatparam Pilot target
1531  *    @luatreturn boolean Whether the target is an enemy.
1532  *    @luafunc isenemy( target )
1533  */
aiL_isenemy(lua_State * L)1534 static int aiL_isenemy( lua_State *L )
1535 {
1536    Pilot *p;
1537 
1538    /* Get the pilot. */
1539    p = luaL_validpilot(L,1);
1540 
1541    /* Player needs special handling in case of hostility. */
1542    if (p->faction == FACTION_PLAYER) {
1543       lua_pushboolean(L, pilot_isHostile(cur_pilot));
1544       lua_pushboolean(L,1);
1545       return 1;
1546    }
1547 
1548    /* Check if is ally. */
1549    lua_pushboolean(L,areEnemies(cur_pilot->faction, p->faction));
1550    return 1;
1551 }
1552 
1553 /**
1554  * @brief Checks to see if target is an ally.
1555  *
1556  *    @luatparam Pilot target
1557  *    @luatreturn boolean Whether the target is an ally.
1558  *    @luafunc isally( target )
1559  */
aiL_isally(lua_State * L)1560 static int aiL_isally( lua_State *L )
1561 {
1562    Pilot *p;
1563 
1564    /* Get the pilot. */
1565    p = luaL_validpilot(L,1);
1566 
1567    /* Player needs special handling in case of friendliness. */
1568    if (p->faction == FACTION_PLAYER) {
1569       lua_pushboolean(L, pilot_isFriendly(cur_pilot));
1570       return 1;
1571    }
1572 
1573    /* Check if is ally. */
1574    lua_pushboolean(L,areAllies(cur_pilot->faction, p->faction));
1575    return 1;
1576 }
1577 
1578 
1579 /**
1580  * @brief Checks to see if pilot has a missile lockon.
1581  *
1582  *    @luatreturn boolean Whether the pilot has a missile lockon.
1583  *    @luafunc haslockon()
1584  */
1585 
aiL_haslockon(lua_State * L)1586 static int aiL_haslockon( lua_State *L )
1587 {
1588    lua_pushboolean(L, cur_pilot->lockons > 0);
1589    return 1;
1590 }
1591 
1592 
1593 /**
1594  * @brief Starts accelerating the pilot.
1595  *
1596  *    @luatparam[opt=1.] number acceleration Fraction of pilot's maximum acceleration from 0 to 1.
1597  *    @luafunc accel( acceleration )
1598  */
aiL_accel(lua_State * L)1599 static int aiL_accel( lua_State *L )
1600 {
1601    double n;
1602 
1603    if (lua_gettop(L) > 1 && lua_isnumber(L,1)) {
1604       n = (double)lua_tonumber(L,1);
1605 
1606       if (n > 1.) n = 1.;
1607       else if (n < 0.) n = 0.;
1608       pilot_acc = n;
1609    }
1610    else
1611       pilot_acc = 1.;
1612 
1613    return 0;
1614 }
1615 
1616 
1617 /**
1618  * @brief Starts turning the pilot.
1619  *
1620  *    @luatparam number vel Directional velocity from -1 to 1.
1621  *    @luafunc turn( vel )
1622  */
aiL_turn(lua_State * L)1623 static int aiL_turn( lua_State *L )
1624 {
1625    pilot_turn = luaL_checknumber(L,1);
1626    return 0;
1627 }
1628 
1629 
1630 /**
1631  * @brief Faces the target.
1632  *
1633  * @usage ai.face( a_pilot ) -- Face a pilot
1634  * @usage ai.face( a_pilot, true ) -- Face away from a pilot
1635  * @usage ai.face( a_pilot, nil, true ) -- Compensate velocity facing a pilot
1636  *
1637  *    @luatparam Pilot|Vec2|number target Target to face.
1638  *    @luatparam boolean invert Invert away from target.
1639  *    @luatparam boolean compensate Compensate for velocity?
1640  *    @luatreturn number Angle offset in degrees.
1641  * @luafunc face( target, invert, compensate )
1642  */
aiL_face(lua_State * L)1643 static int aiL_face( lua_State *L )
1644 {
1645    Vector2d *tv; /* get the position to face */
1646    Pilot* p;
1647    double k_diff, k_vel, d, diff, vx, vy, dx, dy;
1648    int vel;
1649 
1650    /* Get first parameter, aka what to face. */
1651    if (lua_ispilot(L,1)) {
1652       p = luaL_validpilot(L,1);
1653       /* Target vector. */
1654       tv = &p->solid->pos;
1655    }
1656    else if (lua_isnumber(L,1)) {
1657       d = (double)lua_tonumber(L,1);
1658       if (d < 0.)
1659          tv = &cur_pilot->solid->pos;
1660       else
1661          NLUA_INVALID_PARAMETER(L);
1662    }
1663    else if (lua_isvector(L,1))
1664       tv = lua_tovector(L,1);
1665    else
1666       NLUA_INVALID_PARAMETER(L);
1667 
1668    /* Default gain. */
1669    k_diff = 10.;
1670    k_vel  = 100.; /* overkill gain! */
1671 
1672    /* Check if must invert. */
1673    if (lua_toboolean(L,2))
1674       k_diff *= -1;
1675 
1676    /* Third parameter. */
1677    vel = lua_toboolean(L, 3);
1678 
1679    /* Tangential component of velocity vector
1680     *
1681     * v: velocity vector
1682     * d: direction vector
1683     *
1684     *                  d       d                d
1685     * v_t = v - ( v . --- ) * --- = v - ( v . ----- ) * d
1686     *                 |d|     |d|             |d|^2
1687     */
1688    /* Velocity vector. */
1689    vx = cur_pilot->solid->vel.x;
1690    vy = cur_pilot->solid->vel.y;
1691    /* Direction vector. */
1692    dx = tv->x - cur_pilot->solid->pos.x;
1693    dy = tv->y - cur_pilot->solid->pos.y;
1694    if (vel) {
1695       /* Calculate dot product. */
1696       d = (vx * dx + vy * dy) / (dx*dx + dy*dy);
1697       /* Calculate tangential velocity. */
1698       vx = vx - d * dx;
1699       vy = vy - d * dy;
1700 
1701       /* Add velocity compensation. */
1702       dx += -k_vel * vx;
1703       dy += -k_vel * vy;
1704    }
1705 
1706    /* Compensate error and rotate. */
1707    diff = angle_diff( cur_pilot->solid->dir, atan2( dy, dx ) );
1708 
1709    /* Make pilot turn. */
1710    pilot_turn = k_diff * diff;
1711 
1712    /* Return angle in degrees away from target. */
1713    lua_pushnumber(L, ABS(diff*180./M_PI));
1714    return 1;
1715 }
1716 
1717 
1718 /**
1719  * @brief Gives the direction to follow in order to reach the target while
1720  *  minimizating risk.
1721  *
1722  * This method is based on a simplified version of trajectory generation in
1723  * mobile robotics using the potential method.
1724  *
1725  * The principle is to consider the mobile object (ship) as a mechanical object.
1726  * Obstacles (enemies) and the target exert
1727  * attractive or repulsive force on this object.
1728  *
1729  * Only visible ships are taken into account.
1730  *
1731  *    @luatparam Pilot|Vec2|number target Target to go to.
1732  * @luafunc careful_face( target )
1733  */
aiL_careful_face(lua_State * L)1734 static int aiL_careful_face( lua_State *L )
1735 {
1736    Vector2d *tv, F, F1;
1737    Pilot* p;
1738    Pilot *p_i;
1739    double k_diff, k_goal, k_enemy, k_mult,
1740           d, diff, dist, factor;
1741    int i;
1742 
1743    /* Init some variables */
1744    p = cur_pilot;
1745 
1746    /* Get first parameter, aka what to face. */
1747    if (lua_ispilot(L,1)) {
1748       p = luaL_validpilot(L,1);
1749       /* Target vector. */
1750       tv = &p->solid->pos;
1751    }
1752    else if (lua_isnumber(L,1)) {
1753       d = (double)lua_tonumber(L,1);
1754       if (d < 0.)
1755          tv = &cur_pilot->solid->pos;
1756       else
1757          NLUA_INVALID_PARAMETER(L);
1758    }
1759    else if (lua_isvector(L,1))
1760       tv = lua_tovector(L,1);
1761    else
1762       NLUA_INVALID_PARAMETER(L);
1763 
1764    /* Default gains. */
1765    k_diff = 10.;
1766    k_goal = 1.;
1767    k_enemy = 6000000.;
1768 
1769    /* Init the force */
1770    vect_cset( &F, 0., 0.) ;
1771    vect_cset( &F1, tv->x - cur_pilot->solid->pos.x, tv->y - cur_pilot->solid->pos.y) ;
1772    dist = VMOD(F1) + 0.1; /* Avoid / 0*/
1773    vect_cset( &F1, F1.x * k_goal / dist, F1.y * k_goal / dist) ;
1774 
1775    /* Cycle through all the pilots in order to compute the force */
1776    for(i=0; i<pilot_nstack; i++) {
1777       p_i = pilot_stack[i];
1778 
1779       /* Valid pilot isn't self, is in range, isn't the target and isn't disabled */
1780       if (pilot_isDisabled(p_i) ) continue;
1781       if (p_i->id == cur_pilot->id) continue;
1782       if (p_i->id == p->id) continue;
1783       if (pilot_inRangePilot(cur_pilot, p_i) != 1) continue;
1784 
1785       /* If the enemy is too close, ignore it*/
1786       dist = vect_dist(&p_i->solid->pos, &cur_pilot->solid->pos);
1787       if (dist < 750) continue;
1788 
1789       k_mult = pilot_relhp( p_i, cur_pilot ) * pilot_reldps( p_i, cur_pilot );
1790 
1791       /* Check if friendly or not */
1792       if (areEnemies(cur_pilot->faction, p_i->faction)) {
1793          factor = k_enemy * k_mult / (dist*dist*dist);
1794          vect_cset( &F, F.x + factor * (cur_pilot->solid->pos.x - p_i->solid->pos.x),
1795                 F.y + factor * (cur_pilot->solid->pos.y - p_i->solid->pos.y) );
1796       }
1797    }
1798 
1799    vect_cset( &F, F.x + F1.x, F.y + F1.y );
1800 
1801    /* Rotate. */
1802    diff = angle_diff( cur_pilot->solid->dir, VANGLE(F) );
1803 
1804    /* Make pilot turn. */
1805    pilot_turn = k_diff * diff;
1806 
1807    /* Return angle in degrees away from target. */
1808    lua_pushnumber(L, ABS(diff*180./M_PI));
1809    return 1;
1810 }
1811 
1812 
1813 /**
1814  * @brief Aims at a pilot, trying to hit it rather than move to it.
1815  *
1816  * This method uses a polar UV decomposition to get a more accurate time-of-flight
1817  *
1818  *    @luatparam Pilot target The pilot to aim at
1819  *    @luatreturn number The offset from the target aiming position (in degrees).
1820  * @luafunc aim( target )
1821  */
aiL_aim(lua_State * L)1822 static int aiL_aim( lua_State *L )
1823 {
1824    double x,y;
1825    double t;
1826    Pilot *p;
1827    Vector2d tv, approach_vector, relative_location, orthoradial_vector;
1828    double dist, diff;
1829    double mod;
1830    double speed;
1831    double radial_speed;
1832    double orthoradial_speed;
1833 
1834    /* Only acceptable parameter is pilot */
1835    p = luaL_validpilot(L,1);
1836 
1837    /* Get the distance */
1838    dist = vect_dist( &cur_pilot->solid->pos, &p->solid->pos );
1839 
1840    /* Check if should recalculate weapon speed with secondary weapon. */
1841    speed = pilot_weapSetSpeed( cur_pilot, cur_pilot->active_set, -1 );
1842 
1843    /* determine the radial, or approach speed */
1844    /*
1845     *approach_vector (denote Va) is the relative velocites of the pilot and target
1846     *relative_location (denote Vr) is the vector that points from the target to the pilot
1847     *
1848     *Va dot Vr is the rate of approach between the target and the pilot.
1849     *If this is greater than 0, the target is approaching the pilot, if less than 0, the target is fleeing.
1850     *
1851     *Va dot Vr + ShotSpeed is the net closing velocity for the shot, and is used to compute the time of flight for the shot.
1852     *
1853     *Position prediction logic is the same as the previous function
1854     */
1855    vect_cset(&approach_vector, VX(cur_pilot->solid->vel) - VX(p->solid->vel), VY(cur_pilot->solid->vel) - VY(p->solid->vel) );
1856    vect_cset(&relative_location, VX(p->solid->pos) -  VX(cur_pilot->solid->pos),  VY(p->solid->pos) - VY(cur_pilot->solid->pos) );
1857    vect_cset(&orthoradial_vector, VY(cur_pilot->solid->pos) - VY(p->solid->pos), VX(p->solid->pos) -  VX(cur_pilot->solid->pos) );
1858 
1859    radial_speed = vect_dot(&approach_vector, &relative_location);
1860    radial_speed = radial_speed / VMOD(relative_location);
1861 
1862    orthoradial_speed = vect_dot(&approach_vector, &orthoradial_vector);
1863    orthoradial_speed = orthoradial_speed / VMOD(relative_location);
1864 
1865    /* Time for shots to reach that distance */
1866    /* t is the real positive solution of a 2nd order equation*/
1867    /* if the target is not hittable (i.e., fleeing faster than our shots can fly, determinant <= 0), just face the target */
1868    if( ((speed*speed - VMOD(approach_vector)*VMOD(approach_vector)) != 0) && (speed*speed - orthoradial_speed*orthoradial_speed) > 0)
1869       t = dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) - radial_speed) /
1870             (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
1871    else
1872       t = 0;
1873 
1874    /* if t < 0, try the other solution*/
1875    if (t < 0)
1876       t = - dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) + radial_speed) /
1877             (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
1878 
1879    /* if t still < 0, no solution*/
1880    if (t < 0)
1881       t = 0;
1882 
1883    /* Position is calculated on where it should be */
1884    x = p->solid->pos.x + p->solid->vel.x*t
1885       - (cur_pilot->solid->pos.x + cur_pilot->solid->vel.x*t);
1886    y = p->solid->pos.y + p->solid->vel.y*t
1887       - (cur_pilot->solid->pos.y + cur_pilot->solid->vel.y*t);
1888    vect_cset( &tv, x, y );
1889 
1890    /* Calculate what we need to turn */
1891    mod = 10.;
1892    diff = angle_diff(cur_pilot->solid->dir, VANGLE(tv));
1893    pilot_turn = mod * diff;
1894 
1895    /* Return distance to target (in grad) */
1896    lua_pushnumber(L, ABS(diff*180./M_PI));
1897    return 1;
1898 }
1899 
1900 
1901 /**
1902  * @brief Maintains an intercept pursuit course.
1903  *
1904  *    @luatparam Pilot|Vec2 target Position or pilot to intercept.
1905  *    @luatreturn number The offset from the proper intercept course (in degrees).
1906  * @luafunc iface( target )
1907  */
aiL_iface(lua_State * L)1908 static int aiL_iface( lua_State *L )
1909 {
1910    NLUA_MIN_ARGS(1);
1911    Vector2d *vec, drift, reference_vector; /* get the position to face */
1912    Pilot* p;
1913    double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
1914    int azimuthal_sign;
1915    double speedmap;
1916 
1917    /* Get first parameter, aka what to face. */
1918    p  = NULL;
1919    vec = NULL;
1920    if (lua_ispilot(L,1))
1921       p = luaL_validpilot(L,1);
1922    else if (lua_isvector(L,1))
1923       vec = lua_tovector(L,1);
1924    else NLUA_INVALID_PARAMETER(L);
1925 
1926    if (vec==NULL) {
1927       if (p == NULL)
1928          return 0; /* Return silently when attempting to face an invalid pilot. */
1929       /* Establish the current pilot velocity and position vectors */
1930       vect_cset( &drift, VX(p->solid->vel) - VX(cur_pilot->solid->vel), VY(p->solid->vel) - VY(cur_pilot->solid->vel));
1931       /* Establish the in-line coordinate reference */
1932       vect_cset( &reference_vector, VX(p->solid->pos) - VX(cur_pilot->solid->pos), VY(p->solid->pos) - VY(cur_pilot->solid->pos));
1933    }
1934    else {
1935       /* Establish the current pilot velocity and position vectors */
1936       vect_cset( &drift, -VX(cur_pilot->solid->vel), -VY(cur_pilot->solid->vel));
1937       /* Establish the in-line coordinate reference */
1938       vect_cset( &reference_vector, VX(*vec) - VX(cur_pilot->solid->pos), VY(*vec) - VY(cur_pilot->solid->pos));
1939    }
1940 
1941    /* Break down the the velocity vectors of both craft into UV coordinates */
1942    vect_uv(&drift_radial, &drift_azimuthal, &drift, &reference_vector);
1943    heading_offset_azimuth = angle_diff(cur_pilot->solid->dir, VANGLE(reference_vector));
1944 
1945    /* Now figure out what to do...
1946     * Are we pointing anywhere inside the correct UV quadrant?
1947     * if we're outside the correct UV quadrant, we need to get into it ASAP
1948     * Otherwise match velocities and approach */
1949    if (fabs(heading_offset_azimuth) < M_PI_2) {
1950       /* This indicates we're in the correct plane*/
1951       /* 1 - 1/(|x|+1) does a pretty nice job of mapping the reals to the interval (0...1). That forms the core of this angle calculation */
1952       /* There is nothing special about the scaling parameter of 200; it can be tuned to get any behavior desired. A lower
1953          number will give a more dramatic 'lead' */
1954       speedmap = -1*copysign(1 - 1 / (fabs(drift_azimuthal/200) + 1), drift_azimuthal) * M_PI_2;
1955       diff = angle_diff(heading_offset_azimuth, speedmap);
1956       azimuthal_sign = -1;
1957 
1958       /* This indicates we're drifting to the right of the target
1959        * And we need to turn CCW */
1960       if (diff > 0)
1961          pilot_turn = azimuthal_sign;
1962       /* This indicates we're drifting to the left of the target
1963        * And we need to turn CW */
1964       else if (diff < 0)
1965          pilot_turn = -1*azimuthal_sign;
1966       else
1967          pilot_turn = 0;
1968    }
1969    /* turn most efficiently to face the target. If we intercept the correct quadrant in the UV plane first, then the code above will kick in */
1970    /* some special case logic is added to optimize turn time. Reducing this to only the else cases would speed up the operation
1971       but cause the pilot to turn in the less-than-optimal direction sometimes when between 135 and 225 degrees off from the target */
1972    else {
1973       /* signal that we're not in a productive direction for thrusting */
1974       diff = M_PI;
1975       azimuthal_sign = 1;
1976 
1977 
1978       if(heading_offset_azimuth >0)
1979          pilot_turn = azimuthal_sign;
1980       else
1981          pilot_turn = -1*azimuthal_sign;
1982    }
1983 
1984    /* Return angle in degrees away from target. */
1985    lua_pushnumber(L, ABS(diff*180./M_PI));
1986    return 1;
1987 }
1988 
1989 /**
1990  * @brief calculates the direction that the target is relative to the current pilot facing.
1991  *
1992  *    @luatparam Pilot|Vec2 target Position or pilot to compare facing to
1993  *    @luatreturn number The facing offset to the target (in degrees).
1994  * @luafunc dir( target )
1995  *
1996  */
aiL_dir(lua_State * L)1997 static int aiL_dir( lua_State *L )
1998 {
1999    NLUA_MIN_ARGS(1);
2000    Vector2d *vec, sv, tv; /* get the position to face */
2001    Pilot* p;
2002    double diff;
2003    int n;
2004 
2005    /* Get first parameter, aka what to face. */
2006    n  = -2;
2007    vec = NULL;
2008    if (lua_ispilot(L,1)) {
2009       p = luaL_validpilot(L,1);
2010       vect_cset( &tv, VX(p->solid->pos), VY(p->solid->pos) );
2011    }
2012    else if (lua_isvector(L,1))
2013       vec = lua_tovector(L,1);
2014    else NLUA_INVALID_PARAMETER(L);
2015 
2016    vect_cset( &sv, VX(cur_pilot->solid->pos), VY(cur_pilot->solid->pos) );
2017 
2018    if (vec==NULL) /* target is dynamic */
2019       diff = angle_diff(cur_pilot->solid->dir,
2020             (n==-1) ? VANGLE(sv) :
2021             vect_angle(&sv, &tv));
2022    else /* target is static */
2023       diff = angle_diff( cur_pilot->solid->dir,
2024             (n==-1) ? VANGLE(cur_pilot->solid->pos) :
2025             vect_angle(&cur_pilot->solid->pos, vec));
2026 
2027 
2028    /* Return angle in degrees away from target. */
2029    lua_pushnumber(L, diff*180./M_PI);
2030    return 1;
2031 }
2032 
2033 /**
2034  * @brief calculates angle between pilot facing and intercept-course to target.
2035  *
2036  *    @luatparam Pilot|Vec2 target Position or pilot to compare facing to
2037  *    @luatreturn number The facing offset to intercept-course to the target (in degrees).
2038  * @luafunc idir( target )
2039  */
aiL_idir(lua_State * L)2040 static int aiL_idir( lua_State *L )
2041 {
2042    NLUA_MIN_ARGS(1);
2043    Vector2d *vec, drift, reference_vector; /* get the position to face */
2044    Pilot* p;
2045    double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
2046    double speedmap;
2047    /*char announcebuffer[255] = " ", announcebuffer2[128];*/
2048 
2049    /* Get first parameter, aka what to face. */
2050    p  = NULL;
2051    vec = NULL;
2052    if (lua_ispilot(L,1))
2053       p = luaL_validpilot(L,1);
2054    else if (lua_isvector(L,1))
2055       vec = lua_tovector(L,1);
2056    else NLUA_INVALID_PARAMETER(L);
2057 
2058    if (vec==NULL) {
2059       if (p == NULL)
2060          return 0; /* Return silently when attempting to face an invalid pilot. */
2061       /* Establish the current pilot velocity and position vectors */
2062       vect_cset( &drift, VX(p->solid->vel) - VX(cur_pilot->solid->vel), VY(p->solid->vel) - VY(cur_pilot->solid->vel));
2063       /* Establish the in-line coordinate reference */
2064       vect_cset( &reference_vector, VX(p->solid->pos) - VX(cur_pilot->solid->pos), VY(p->solid->pos) - VY(cur_pilot->solid->pos));
2065    }
2066    else {
2067       /* Establish the current pilot velocity and position vectors */
2068       vect_cset( &drift, -VX(cur_pilot->solid->vel), -VY(cur_pilot->solid->vel));
2069       /* Establish the in-line coordinate reference */
2070       vect_cset( &reference_vector, VX(*vec) - VX(cur_pilot->solid->pos), VY(*vec) - VY(cur_pilot->solid->pos));
2071    }
2072 
2073    /* Break down the the velocity vectors of both craft into UV coordinates */
2074    vect_uv(&drift_radial, &drift_azimuthal, &drift, &reference_vector);
2075    heading_offset_azimuth = angle_diff(cur_pilot->solid->dir, VANGLE(reference_vector));
2076 
2077    /* now figure out what to do*/
2078    /* are we pointing anywhere inside the correct UV quadrant? */
2079    /* if we're outside the correct UV quadrant, we need to get into it ASAP */
2080    /* Otherwise match velocities and approach*/
2081    if (fabs(heading_offset_azimuth) < M_PI_2) {
2082       /* This indicates we're in the correct plane
2083        * 1 - 1/(|x|+1) does a pretty nice job of mapping the reals to the interval (0...1). That forms the core of this angle calculation
2084        * there is nothing special about the scaling parameter of 200; it can be tuned to get any behavior desired. A lower
2085        * number will give a more dramatic 'lead' */
2086       speedmap = -1*copysign(1 - 1 / (fabs(drift_azimuthal/200) + 1), drift_azimuthal) * M_PI_2;
2087       diff = angle_diff(heading_offset_azimuth, speedmap);
2088 
2089    }
2090    /* Turn most efficiently to face the target. If we intercept the correct quadrant in the UV plane first, then the code above will kick in
2091       some special case logic is added to optimize turn time. Reducing this to only the else cases would speed up the operation
2092       but cause the pilot to turn in the less-than-optimal direction sometimes when between 135 and 225 degrees off from the target */
2093    else{
2094       /* signal that we're not in a productive direction for thrusting */
2095       diff        = M_PI;
2096    }
2097 
2098    /* Return angle in degrees away from target. */
2099    lua_pushnumber(L, diff*180./M_PI);
2100    return 1;
2101 }
2102 
2103 /**
2104  * @brief Calculate the offset between the pilot's current direction of travel and the pilot's current facing.
2105  *
2106  *    @luatreturn number Offset
2107  *    @luafunc drift_facing()
2108  */
aiL_drift_facing(lua_State * L)2109 static int aiL_drift_facing( lua_State *L )
2110 {
2111     double drift;
2112     drift = angle_diff(VANGLE(cur_pilot->solid->vel), cur_pilot->solid->dir);
2113     lua_pushnumber(L, drift*180./M_PI);
2114     return 1;
2115 }
2116 
2117 /**
2118  * @brief Brakes the pilot.
2119  *
2120  *    @luatreturn boolean Whether braking is finished.
2121  *    @luafunc brake()
2122  */
2123 
aiL_brake(lua_State * L)2124 static int aiL_brake( lua_State *L )
2125 {
2126    int ret;
2127 
2128    ret = pilot_brake( cur_pilot );
2129 
2130    pilot_acc = cur_pilot->solid->thrust / cur_pilot->thrust;
2131    pilot_turn = cur_pilot->solid->dir_vel / cur_pilot->turn;
2132 
2133    lua_pushboolean(L, ret);
2134    return 1;
2135 }
2136 
2137 
2138 /**
2139  * @brief Get the nearest friendly planet to the pilot.
2140  *
2141  *    @luatreturn Planet|nil
2142  *    @luafunc nearestplanet()
2143  */
aiL_getnearestplanet(lua_State * L)2144 static int aiL_getnearestplanet( lua_State *L )
2145 {
2146    double dist, d;
2147    int i, j;
2148    LuaPlanet planet;
2149 
2150    if (cur_system->nplanets == 0) return 0; /* no planets */
2151 
2152    /* cycle through planets */
2153    for (dist=1./0., j=-1, i=0; i<cur_system->nplanets; i++) {
2154       if (!planet_hasService(cur_system->planets[i],PLANET_SERVICE_INHABITED))
2155          continue;
2156       d = vect_dist( &cur_system->planets[i]->pos, &cur_pilot->solid->pos );
2157       if ((!areEnemies(cur_pilot->faction,cur_system->planets[i]->faction)) &&
2158             (d < dist)) { /* closer friendly planet */
2159          j = i;
2160          dist = d;
2161       }
2162    }
2163 
2164    /* no friendly planet found */
2165    if (j == -1) return 0;
2166 
2167    cur_pilot->nav_planet = j;
2168    planet = cur_system->planets[j]->id;
2169    lua_pushplanet(L, planet);
2170 
2171    return 1;
2172 }
2173 
2174 
2175 /**
2176  * @brief Get a random planet.
2177  *
2178  *    @luatreturn Planet|nil
2179  *    @luafunc rndplanet()
2180  */
aiL_getrndplanet(lua_State * L)2181 static int aiL_getrndplanet( lua_State *L )
2182 {
2183    LuaPlanet planet;
2184    int p;
2185 
2186    if (cur_system->nplanets == 0) return 0; /* no planets */
2187 
2188    /* get a random planet */
2189    p = RNG(0, cur_system->nplanets-1);
2190 
2191    /* Copy the data into a vector */
2192    planet = cur_system->planets[p]->id;
2193    lua_pushplanet(L, planet);
2194 
2195    return 1;
2196 }
2197 
2198 /**
2199  * @brief Get a random friendly planet.
2200  *
2201  *    @luatparam boolean only_friend Only check for ally planets.
2202  *    @luatreturn Planet|nil
2203  * @luafunc landplanet( only_friend )
2204  */
aiL_getlandplanet(lua_State * L)2205 static int aiL_getlandplanet( lua_State *L )
2206 {
2207    int *ind;
2208    int nplanets, i;
2209    LuaPlanet planet;
2210    Planet *p;
2211    int only_friend;
2212 
2213    /* Must have planets. */
2214    if (cur_system->nplanets == 0)
2215       return 0; /* no planets */
2216 
2217    /* Check if we should get only friendlies. */
2218    only_friend = lua_toboolean(L, 1);
2219 
2220    /* Allocate memory. */
2221    ind = malloc( sizeof(int) * cur_system->nplanets );
2222 
2223    /* Copy friendly planet.s */
2224    for (nplanets=0, i=0; i<cur_system->nplanets; i++) {
2225       if (!planet_hasService(cur_system->planets[i],PLANET_SERVICE_INHABITED))
2226          continue;
2227 
2228       /* Check conditions. */
2229       if (only_friend && !areAllies( cur_pilot->faction, cur_system->planets[i]->faction ))
2230          continue;
2231       else if (!only_friend && areEnemies(cur_pilot->faction,cur_system->planets[i]->faction))
2232          continue;
2233 
2234       /* Add it. */
2235       ind[ nplanets++ ] = i;
2236    }
2237 
2238    /* no planet to land on found */
2239    if (nplanets==0) {
2240       free(ind);
2241       return 0;
2242    }
2243 
2244    /* we can actually get a random planet now */
2245    i = RNG(0,nplanets-1);
2246    p = cur_system->planets[ ind[i] ];
2247    planet = p->id;
2248    lua_pushplanet( L, planet );
2249    cur_pilot->nav_planet   = ind[ i ];
2250    free(ind);
2251 
2252    return 1;
2253 }
2254 
2255 
2256 /**
2257  * @brief Lands on a planet.
2258  *
2259  *    @luatreturn boolean Whether landing was successful.
2260  *    @luafunc land()
2261  */
aiL_land(lua_State * L)2262 static int aiL_land( lua_State *L )
2263 {
2264    int ret;
2265    Planet *planet;
2266    HookParam hparam;
2267 
2268    ret = 0;
2269 
2270    if (cur_pilot->nav_planet < 0) {
2271       NLUA_ERROR( L, "Pilot '%s' has no land target", cur_pilot->name );
2272       return 0;
2273    }
2274 
2275    /* Get planet. */
2276    planet = cur_system->planets[ cur_pilot->nav_planet ];
2277 
2278    /* Check landability. */
2279    if (!planet_hasService(planet,PLANET_SERVICE_INHABITED))
2280       ret++;
2281 
2282    /* Check distance. */
2283    if (vect_dist2(&cur_pilot->solid->pos,&planet->pos) > pow2(planet->radius))
2284       ret++;
2285 
2286    /* Check velocity. */
2287    if ((pow2(VX(cur_pilot->solid->vel)) + pow2(VY(cur_pilot->solid->vel))) >
2288          (double)pow2(MAX_HYPERSPACE_VEL))
2289       ret++;
2290 
2291    /* Check landing functionality. */
2292    if (pilot_isFlag(cur_pilot, PILOT_NOLAND))
2293       ret++;
2294 
2295    if (!ret) {
2296       cur_pilot->ptimer = PILOT_LANDING_DELAY;
2297       pilot_setFlag( cur_pilot, PILOT_LANDING );
2298 
2299       hparam.type    = HOOK_PARAM_ASSET;
2300       hparam.u.la    = planet->id;
2301 
2302       pilot_runHookParam( cur_pilot, PILOT_HOOK_LAND, &hparam, 1 );
2303    }
2304 
2305    lua_pushboolean(L,!ret);
2306    return 1;
2307 }
2308 
2309 
2310 /**
2311  * @brief Tries to enter hyperspace.
2312  *
2313  *    @luatreturn number|nil Distance if too far away.
2314  *    @luafunc hyperspace()
2315  */
aiL_hyperspace(lua_State * L)2316 static int aiL_hyperspace( lua_State *L )
2317 {
2318    int dist;
2319 
2320    dist = space_hyperspace(cur_pilot);
2321    if (dist == 0.) {
2322       pilot_shootStop( cur_pilot, 0 );
2323       pilot_shootStop( cur_pilot, 1 );
2324       return 0;
2325    }
2326 
2327    lua_pushnumber(L,dist);
2328    return 1;
2329 }
2330 
2331 
2332 /**
2333  * @brief Sets hyperspace target.
2334  *
2335  *    @luatparam Jump target Hyperspace target
2336  *    @luareturn Vec2 Where to go to jump
2337  *    @luafunc sethyptarget(target)
2338  */
aiL_sethyptarget(lua_State * L)2339 static int aiL_sethyptarget( lua_State *L )
2340 {
2341    JumpPoint *jp;
2342    LuaJump *lj;
2343    Vector2d vec;
2344    double a, rad;
2345 
2346    lj = luaL_checkjump( L, 1 );
2347    jp = luaL_validjump( L, 1 );
2348 
2349    if ( lj->srcid != cur_system->id )
2350       NLUA_ERROR(L, "Jump point must be in current system.");
2351 
2352    /* Copy vector. */
2353    vec = jp->pos;
2354 
2355    /* Introduce some error. */
2356    a     = RNGF() * M_PI * 2.;
2357    rad   = RNGF() * 0.5 * jp->radius;
2358    vect_cadd( &vec, rad*cos(a), rad*sin(a) );
2359 
2360    /* Set up target. */
2361    cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2362 
2363    /* Return vector. */
2364    lua_pushvector( L, vec );
2365 
2366    return 1;
2367 }
2368 
2369 
2370 /**
2371  * @brief Gets the nearest hyperspace target.
2372  *
2373  *    @luatreturn JumpPoint|nil
2374  *    @luafunc nearhyptarget()
2375  */
aiL_nearhyptarget(lua_State * L)2376 static int aiL_nearhyptarget( lua_State *L )
2377 {
2378    JumpPoint *jp, *jiter;
2379    double mindist, dist;
2380    int i;
2381    LuaJump lj;
2382 
2383    /* No jumps. */
2384    if (cur_system->njumps == 0)
2385       return 0;
2386 
2387    /* Find nearest jump .*/
2388    mindist = INFINITY;
2389    jp      = NULL;
2390    for (i=0; i <cur_system->njumps; i++) {
2391       jiter = &cur_system->jumps[i];
2392       /* We want only standard jump points to be used. */
2393       if (jp_isFlag(jiter, JP_HIDDEN) || jp_isFlag(jiter, JP_EXITONLY))
2394          continue;
2395       /* Get nearest distance. */
2396       dist  = vect_dist2( &cur_pilot->solid->pos, &jiter->pos );
2397       if (dist < mindist) {
2398          jp       = jiter;
2399          mindist  = dist;
2400       }
2401    }
2402    /* None available. */
2403    if (jp == NULL)
2404       return 0;
2405 
2406    lj.destid = jp->targetid;
2407    lj.srcid = cur_system->id;
2408 
2409    /* Return Jump. */
2410    lua_pushjump( L, lj );
2411    return 1;
2412 }
2413 
2414 
2415 /**
2416  * @brief Gets a random hyperspace target.
2417  *
2418  *    @luatreturn JumpPoint|nil
2419  *    @luafunc rndhyptarget()
2420  */
aiL_rndhyptarget(lua_State * L)2421 static int aiL_rndhyptarget( lua_State *L )
2422 {
2423    JumpPoint **jumps, *jiter;
2424    int i, j, r;
2425    int *id;
2426    LuaJump lj;
2427 
2428    /* No jumps in the system. */
2429    if (cur_system->njumps == 0)
2430       return 0;
2431 
2432    /* Find usable jump points. */
2433    jumps = malloc( sizeof(JumpPoint*) * cur_system->njumps );
2434    id    = malloc( sizeof(int) * cur_system->njumps );
2435    j = 0;
2436    for (i=0; i < cur_system->njumps; i++) {
2437       jiter = &cur_system->jumps[i];
2438       /* We want only standard jump points to be used. */
2439       if (jp_isFlag(jiter, JP_HIDDEN) || jp_isFlag(jiter, JP_EXITONLY))
2440          continue;
2441       id[j]      = i;
2442       jumps[j++] = jiter;
2443    }
2444 
2445    /* Choose random jump point. */
2446    r = RNG(0, j-1);
2447 
2448    lj.destid = jumps[r]->targetid;
2449    lj.srcid = cur_system->id;
2450 
2451    /* Clean up. */
2452    free(jumps);
2453    free(id);
2454 
2455    /* Return Jump. */
2456    lua_pushjump( L, lj );
2457    return 1;
2458 }
2459 
2460 /**
2461  * @brief Gets the relative velocity of a pilot.
2462  *
2463  *    @luatreturn number Relative velocity.
2464  * @luafunc relvel()
2465  */
aiL_relvel(lua_State * L)2466 static int aiL_relvel( lua_State *L )
2467 {
2468    double dot, mod;
2469    Pilot *p;
2470    Vector2d vv, pv;
2471    int absolute;
2472 
2473    p = luaL_validpilot(L,1);
2474 
2475    if (lua_gettop(L) > 1)
2476       absolute = lua_toboolean(L,2);
2477    else
2478       absolute = 0;
2479 
2480    /* Get the projection of target on current velocity. */
2481    if (absolute == 0)
2482       vect_cset( &vv, p->solid->vel.x - cur_pilot->solid->vel.x,
2483             p->solid->vel.y - cur_pilot->solid->vel.y );
2484    else
2485       vect_cset( &vv, p->solid->vel.x, p->solid->vel.y);
2486 
2487    vect_cset( &pv, p->solid->pos.x - cur_pilot->solid->pos.x,
2488          p->solid->pos.y - cur_pilot->solid->pos.y );
2489    dot = vect_dot( &pv, &vv );
2490    mod = MAX(VMOD(pv), 1.); /* Avoid /0. */
2491 
2492    lua_pushnumber(L, dot / mod );
2493    return 1;
2494 }
2495 
2496 /**
2497  * @brief Computes the point to face in order to
2498  *        follow an other pilot using a PD controller.
2499  *
2500  *    @luatparam Pilot target The pilot to follow
2501  *    @luatparam number radius The requested distance between p and target
2502  *    @luatparam number angle The requested angle between p and target
2503  *    @luatparam number Kp The first controller parameter
2504  *    @luatparam number Kd The second controller parameter
2505  *    @luatparam[opt] string method Method to compute goal angle
2506  *    @luareturn The point to go to as a vector2.
2507  * @luafunc follow_accurate( target, radius, angle, Kp, Kd, method )
2508  */
aiL_follow_accurate(lua_State * L)2509 static int aiL_follow_accurate( lua_State *L )
2510 {
2511    Vector2d point, cons, goal, pv;
2512    double radius, angle, Kp, Kd, angle2;
2513    Pilot *p, *target;
2514    const char *method;
2515 
2516    p = cur_pilot;
2517    target = luaL_validpilot(L,1);
2518    radius = luaL_checklong(L,2);
2519    angle = luaL_checklong(L,3);
2520    Kp = luaL_checklong(L,4);
2521    Kd = luaL_checklong(L,5);
2522 
2523    if (lua_isnoneornil(L, 6))
2524       method = "velocity";
2525    else
2526       method = luaL_checkstring(L,6);
2527 
2528    if (strcmp( method, "absolute" ) == 0)
2529       angle2 = angle * M_PI/180;
2530    else if (strcmp( method, "keepangle" ) == 0){
2531       vect_cset( &pv, p->solid->pos.x - target->solid->pos.x,
2532             p->solid->pos.y - target->solid->pos.y );
2533       angle2 = VANGLE(pv);
2534       }
2535    else /* method == "velocity" */
2536       angle2 = angle * M_PI/180 + VANGLE( target->solid->vel );
2537 
2538    vect_cset( &point, VX(target->solid->pos) + radius * cos(angle2),
2539          VY(target->solid->pos) + radius * sin(angle2) );
2540 
2541    /*  Compute the direction using a pd controller */
2542    vect_cset( &cons, (point.x - p->solid->pos.x) * Kp +
2543          (target->solid->vel.x - p->solid->vel.x) *Kd,
2544          (point.y - p->solid->pos.y) * Kp +
2545          (target->solid->vel.y - p->solid->vel.y) *Kd );
2546 
2547    vect_cset( &goal, cons.x + p->solid->pos.x, cons.y + p->solid->pos.y);
2548 
2549    /* Push info */
2550    lua_pushvector( L, goal );
2551 
2552    return 1;
2553 
2554 }
2555 
2556 /**
2557  * @brief Completely stops the pilot if it is below minimum vel error (no insta-stops).
2558  *
2559  *    @luafunc stop()
2560  */
aiL_stop(lua_State * L)2561 static int aiL_stop( lua_State *L )
2562 {
2563    (void) L; /* avoid gcc warning */
2564 
2565    if (VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR)
2566       vect_pset( &cur_pilot->solid->vel, 0., 0. );
2567 
2568    return 0;
2569 }
2570 
2571 /**
2572  * @brief Docks the ship.
2573  *
2574  *    @luatparam Pilot target Pilot to dock with.
2575  *    @luafunc dock( target )
2576  */
aiL_dock(lua_State * L)2577 static int aiL_dock( lua_State *L )
2578 {
2579    Pilot *p;
2580 
2581    /* Target is another ship. */
2582    p = luaL_validpilot(L,1);
2583    pilot_dock(cur_pilot, p, 1);
2584 
2585    return 0;
2586 }
2587 
2588 
2589 /**
2590  * @brief Sets the combat flag.
2591  *
2592  *    @luatparam[opt=true] boolean val Value to set flag to.
2593  *    @luafunc combat( val )
2594  */
aiL_combat(lua_State * L)2595 static int aiL_combat( lua_State *L )
2596 {
2597    int i;
2598 
2599    if (lua_gettop(L) > 0) {
2600       i = lua_toboolean(L,1);
2601       if (i==1) pilot_setFlag(cur_pilot, PILOT_COMBAT);
2602       else if (i==0) pilot_rmFlag(cur_pilot, PILOT_COMBAT);
2603    }
2604    else pilot_setFlag(cur_pilot, PILOT_COMBAT);
2605 
2606    return 0;
2607 }
2608 
2609 
2610 /**
2611  * @brief Sets the pilot's target.
2612  *
2613  *    @luaparam target Pilot to target.
2614  *    @luafunc settarget( target )
2615  */
aiL_settarget(lua_State * L)2616 static int aiL_settarget( lua_State *L )
2617 {
2618    Pilot *p;
2619    p = luaL_validpilot(L,1);
2620    pilot_setTarget( cur_pilot, p->id );
2621    return 0;
2622 }
2623 
2624 
2625 /**
2626  * @brief Sets the active weapon set, fires another weapon set or activate an outfit.
2627  *
2628  *    @luatparam number id ID of the weapon set to switch to or fire.
2629  *    @luatparam[opt=true] boolean type true to activate, false to deactivate.
2630  * @luafunc weapset( id, type )
2631  */
aiL_weapSet(lua_State * L)2632 static int aiL_weapSet( lua_State *L )
2633 {
2634    Pilot* p;
2635    int id, type, on, l, i;
2636    PilotWeaponSet *ws;
2637 
2638    p = cur_pilot;
2639    id = lua_tonumber(L,1);
2640 
2641    if (lua_gettop(L) > 1)
2642       type = lua_toboolean(L,2);
2643    else
2644       type = 1;
2645 
2646    ws = &p->weapon_sets[id];
2647 
2648    if (ws->type == WEAPSET_TYPE_ACTIVE)
2649    {
2650       /* Check if outfit is on */
2651       on = 1;
2652       l  = array_size(ws->slots);
2653       for (i=0; i<l; i++) {
2654          if (ws->slots[i].slot->state == PILOT_OUTFIT_OFF) {
2655             on = 0;
2656             break;
2657          }
2658       }
2659 
2660       /* activate */
2661       if (type && !on)
2662          pilot_weapSetPress(p, id, 1 );
2663       /* deactivate */
2664       if (!type && on)
2665          pilot_weapSetPress(p, id, 1 );
2666    }
2667    else /* weapset type is weapon or change */
2668       pilot_weapSetPress( cur_pilot, id, 1 );
2669    return 0;
2670 }
2671 
2672 
2673 /**
2674  * @brief Does the pilot have cannons?
2675  *
2676  *    @luatreturn boolean True if the pilot has cannons.
2677  * @luafunc hascannons()
2678  */
aiL_hascannons(lua_State * L)2679 static int aiL_hascannons( lua_State *L )
2680 {
2681    lua_pushboolean( L, cur_pilot->ncannons > 0 );
2682    return 1;
2683 }
2684 
2685 
2686 /**
2687  * @brief Does the pilot have turrets?
2688  *
2689  *    @luatreturn boolean True if the pilot has turrets.
2690  * @luafunc hasturrets()
2691  */
aiL_hasturrets(lua_State * L)2692 static int aiL_hasturrets( lua_State *L )
2693 {
2694    lua_pushboolean( L, cur_pilot->nturrets > 0 );
2695    return 1;
2696 }
2697 
2698 
2699 /**
2700  * @brief Does the pilot have jammers?
2701  *
2702  *    @luatreturn boolean True if the pilot has jammers.
2703  * @luafunc hasjammers()
2704  */
aiL_hasjammers(lua_State * L)2705 static int aiL_hasjammers( lua_State *L )
2706 {
2707    lua_pushboolean( L, cur_pilot->njammers > 0 );
2708    return 1;
2709 }
2710 
2711 
2712 /**
2713  * @brief Does the pilot have afterburners?
2714  *
2715  *    @luatreturn boolean True if the pilot has afterburners.
2716  * @luafunc hasafterburners()
2717  */
aiL_hasafterburner(lua_State * L)2718 static int aiL_hasafterburner( lua_State *L )
2719 {
2720    lua_pushboolean( L, cur_pilot->nafterburners > 0 );
2721    return 1;
2722 }
2723 
2724 
2725 /**
2726  * @brief Makes the pilot shoot
2727  *
2728  *    @luatparam[opt=false] boolean secondary Fire secondary weapons instead of primary.
2729  *    @luafunc shoot( secondary )
2730  */
aiL_shoot(lua_State * L)2731 static int aiL_shoot( lua_State *L )
2732 {
2733    /* Cooldown is similar to a ship being disabled, but the AI continues to
2734     * think during cooldown, and thus must not be allowed to fire weapons. */
2735    if (pilot_isFlag(cur_pilot, PILOT_COOLDOWN))
2736       return 0;
2737 
2738    if (lua_toboolean(L,1))
2739       ai_setFlag(AI_SECONDARY);
2740    else
2741       ai_setFlag(AI_PRIMARY);
2742    return 0;
2743 }
2744 
2745 
2746 /**
2747  * @brief Gets the nearest enemy.
2748  *
2749  *    @luatreturn Pilot|nil
2750  *    @luafunc getenemy()
2751  */
aiL_getenemy(lua_State * L)2752 static int aiL_getenemy( lua_State *L )
2753 {
2754    unsigned int id;
2755 
2756    id = pilot_getNearestEnemy(cur_pilot);
2757 
2758    if (id==0) /* No enemy found */
2759       return 0;
2760 
2761    lua_pushpilot(L, id);
2762 
2763    return 1;
2764 }
2765 
2766 /**
2767  * @brief Gets the nearest enemy within specified size bounds.
2768  *
2769  *  @luatparam number lb Lower size bound
2770  *  @luatparam number ub upper size bound
2771  *  @luatreturn Pilot
2772  *  @luafunc getenemy_size( lb, ub )
2773  */
aiL_getenemy_size(lua_State * L)2774 static int aiL_getenemy_size( lua_State *L )
2775 {
2776    unsigned int id;
2777    unsigned int LB, UB;
2778 
2779    NLUA_MIN_ARGS(2);
2780 
2781    LB = luaL_checklong(L,1);
2782    UB = luaL_checklong(L,2);
2783 
2784    if (LB > UB) {
2785       NLUA_ERROR(L, "Invalid Bounds");
2786       return 0;
2787    }
2788 
2789    id = pilot_getNearestEnemy_size( cur_pilot, LB, UB );
2790 
2791    if (id==0) /* No enemy found */
2792       return 0;
2793 
2794    lua_pushpilot(L, id);
2795    return 1;
2796 }
2797 
2798 
2799 /**
2800  * @brief Gets the nearest enemy within specified heuristic.
2801  *
2802  *  @luatparam number mass goal mass map (0-1)
2803  *  @luatparam number dps goal DPS map (0-1)
2804  *  @luatparam number hp goal HP map (0-1)
2805  *  @luatparam number range weighting for range (typically > 1)
2806  *  @luatreturn Pilot the best fitting target
2807  *  @luafunc getenemy_heuristic( mass, dps, hp, range )
2808  */
aiL_getenemy_heuristic(lua_State * L)2809 static int aiL_getenemy_heuristic( lua_State *L )
2810 {
2811 
2812    unsigned int id;
2813    double mass_factor, health_factor, damage_factor, range_factor;
2814 
2815    mass_factor    = luaL_checklong(L,1);
2816    health_factor  = luaL_checklong(L,2);
2817    damage_factor  = luaL_checklong(L,3);
2818    range_factor   = luaL_checklong(L,4);
2819 
2820    id = pilot_getNearestEnemy_heuristic( cur_pilot,
2821          mass_factor, health_factor, damage_factor, 1./range_factor );
2822 
2823    if (id==0) /* No enemy found */
2824       return 0;
2825 
2826    lua_pushpilot(L, id);
2827    return 1;
2828 }
2829 
2830 
2831 /**
2832  * @brief Sets the enemy hostile (basically notifies of an impending attack).
2833  *
2834  *    @luatparam Pilot target Pilot to set hostile.
2835  *    @luafunc hostile( target )
2836  */
aiL_hostile(lua_State * L)2837 static int aiL_hostile( lua_State *L )
2838 {
2839    Pilot *p;
2840 
2841    p = luaL_validpilot(L,1);
2842 
2843    if (p->faction == FACTION_PLAYER)
2844       pilot_setHostile(cur_pilot);
2845 
2846    return 0;
2847 }
2848 
2849 
2850 /**
2851  * @brief Gets the range of a weapon.
2852  *
2853  *    @luatparam[opt] number id Optional parameter indicating id of weapon set to get range of, defaults to selected one.
2854  *    @luatparam[opt=-1] number level Level of weapon set to get range of.
2855  *    @luatreturn number The range of the weapon set.
2856  * @luafunc getweaprange( id, level )
2857  */
aiL_getweaprange(lua_State * L)2858 static int aiL_getweaprange( lua_State *L )
2859 {
2860    int id;
2861    int level;
2862 
2863    id    = cur_pilot->active_set;
2864    level = -1;
2865    if (lua_isnumber(L,1))
2866       id = luaL_checkint(L,1);
2867    if (lua_isnumber(L,2))
2868       level = luaL_checkint(L,2);
2869 
2870    lua_pushnumber(L, pilot_weapSetRange( cur_pilot, id, level ) );
2871    return 1;
2872 }
2873 
2874 
2875 /**
2876  * @brief Gets the speed of a weapon.
2877  *
2878  *    @luatparam[opt] number id Optional parameter indicating id of weapon set to get speed of, defaults to selected one.
2879  *    @luatparam[opt=-1] number level Level of weapon set to get range of.
2880  *    @luatreturn number The range of the weapon set.
2881  * @luafunc getweapspeed( id, level )
2882  */
aiL_getweapspeed(lua_State * L)2883 static int aiL_getweapspeed( lua_State *L )
2884 {
2885    int id;
2886    int level;
2887 
2888    id    = cur_pilot->active_set;
2889    level = -1;
2890    if (lua_isnumber(L,1))
2891       id = luaL_checkint(L,1);
2892    if (lua_isnumber(L,2))
2893       level = luaL_checkint(L,2);
2894 
2895    lua_pushnumber(L, pilot_weapSetSpeed( cur_pilot, id, level ) );
2896    return 1;
2897 }
2898 
2899 
2900 /**
2901  * @brief Checks to see if pilot can board the target.
2902  *
2903  *    @luatparam Pilot target Target to see if pilot can board.
2904  *    @luatreturn boolean true if pilot can board, false if it can't.
2905  * @luafunc canboard( target )
2906  */
aiL_canboard(lua_State * L)2907 static int aiL_canboard( lua_State *L )
2908 {
2909    Pilot *p;
2910 
2911    /* Get parameters. */
2912    p = luaL_validpilot(L,1);
2913 
2914    /* Must be disabled. */
2915    if (!pilot_isDisabled(p)) {
2916       lua_pushboolean(L, 0);
2917       return 1;
2918    }
2919 
2920    /* Check if can be boarded. */
2921    lua_pushboolean(L, !pilot_isFlag(p, PILOT_BOARDED));
2922    return 1;
2923 }
2924 
2925 /**
2926  * @brief Gets the relative size (ship mass) between the current pilot and the specified target.
2927  *
2928  *    @luatparam Pilot target The pilot whose mass we will compare.
2929  *    @luatreturn number A number from 0 to 1 mapping the relative masses.
2930  * @luafunc relsize( target )
2931  */
aiL_relsize(lua_State * L)2932 static int aiL_relsize( lua_State *L )
2933 {
2934    Pilot *p;
2935 
2936    /* Get the pilot. */
2937    p = luaL_validpilot(L,1);
2938 
2939    lua_pushnumber(L, pilot_relsize(cur_pilot, p));
2940 
2941    return 1;
2942 }
2943 
2944 
2945 /**
2946  * @brief Gets the relative damage output (total DPS) between the current pilot and the specified target.
2947  *
2948  *    @luatparam Pilot target The pilot whose DPS we will compare.
2949  *    @luatreturn number A number from 0 to 1 mapping the relative DPSes.
2950  * @luafunc reldps( target )
2951  */
aiL_reldps(lua_State * L)2952 static int aiL_reldps( lua_State *L )
2953 {
2954    Pilot *p;
2955 
2956    /* Get the pilot. */
2957    p = luaL_validpilot(L,1);
2958 
2959    lua_pushnumber(L, pilot_reldps(cur_pilot, p));
2960 
2961    return 1;
2962 }
2963 
2964 
2965 /**
2966  * @brief Gets the relative health (total shields and armour) between the current pilot and the specified target
2967  *
2968  *    @luatparam Pilot target The pilot whose health we will compare.
2969  *    @luatreturn number A number from 0 to 1 mapping the relative healths.
2970  *    @luafunc relhp(target)
2971  */
aiL_relhp(lua_State * L)2972 static int aiL_relhp( lua_State *L )
2973 {
2974    Pilot *p;
2975 
2976    /* Get the pilot. */
2977    p = luaL_validpilot(L,1);
2978 
2979    lua_pushnumber(L, pilot_relhp(cur_pilot, p));
2980 
2981    return 1;
2982 }
2983 
2984 
2985 
2986 /**
2987  * @brief Attempts to board the pilot's target.
2988  *
2989  *    @luatreturn boolean true if was able to board the target.
2990  *    @luafunc board()
2991  */
aiL_board(lua_State * L)2992 static int aiL_board( lua_State *L )
2993 {
2994    lua_pushboolean(L, pilot_board( cur_pilot ));
2995    return 1;
2996 }
2997 
2998 
2999 /**
3000  * @brief Attempts to refuel the pilot's target.
3001  *
3002  *    @luatreturn boolean true if pilot has begun refueling, false if it hasn't.
3003  *    @luafunc refuel()
3004  */
aiL_refuel(lua_State * L)3005 static int aiL_refuel( lua_State *L )
3006 {
3007    lua_pushboolean(L,pilot_refuelStart(cur_pilot));
3008    return 1;
3009 }
3010 
3011 
3012 /**
3013  * @brief Sets a timer.
3014  *
3015  *    @luatparam number timer Timer number.
3016  *    @luatparam[opt=0] number time Number of seconds to set timer to.
3017  *    @luafunc settimer(timer, time)
3018  */
aiL_settimer(lua_State * L)3019 static int aiL_settimer( lua_State *L )
3020 {
3021    int n;
3022 
3023    /* Get parameters. */
3024    n = luaL_checkint(L,1);
3025 
3026    /* Set timer. */
3027    cur_pilot->timer[n] = (lua_isnumber(L,2)) ? lua_tonumber(L,2)/1000. : 0;
3028 
3029    return 0;
3030 }
3031 
3032 
3033 /**
3034  * @brief Checks a timer.
3035  *
3036  *    @luatparam number timer Timer number.
3037  *    @luatreturn boolean Whether time is up.
3038  *    @luafunc timeup(timer)
3039  */
3040 
aiL_timeup(lua_State * L)3041 static int aiL_timeup( lua_State *L )
3042 {
3043    int n;
3044 
3045    /* Get parameters. */
3046    n = luaL_checkint(L,1);
3047 
3048    lua_pushboolean(L, cur_pilot->timer[n] < 0.);
3049    return 1;
3050 }
3051 
3052 
3053 /**
3054  * @brief Sends a distress signal.
3055  *
3056  *    @luatparam string|nil msg Message to send or nil.
3057  *    @luafunc distress( msg )
3058  */
aiL_distress(lua_State * L)3059 static int aiL_distress( lua_State *L )
3060 {
3061    if (lua_isstring(L,1))
3062       nsnprintf( aiL_distressmsg, PATH_MAX, "%s", lua_tostring(L,1) );
3063    else if (lua_isnil(L,1))
3064       aiL_distressmsg[0] = '\0';
3065    else
3066       NLUA_INVALID_PARAMETER(L);
3067 
3068    /* Set flag because code isn't reentrant. */
3069    ai_setFlag(AI_DISTRESS);
3070 
3071    return 0;
3072 }
3073 
3074 
3075 /**
3076  * @brief Picks a pilot that will command the current pilot.
3077  *
3078  *    @luatreturn Pilot|nil
3079  *    @luafunc getBoss()
3080  */
aiL_getBoss(lua_State * L)3081 static int aiL_getBoss( lua_State *L )
3082 {
3083    unsigned int id;
3084 
3085    id = pilot_getBoss( cur_pilot );
3086 
3087    if (id==0) /* No boss found */
3088       return 0;
3089 
3090    lua_pushpilot(L, id);
3091 
3092    return 1;
3093 }
3094 
3095 /**
3096  * @brief Sets the pilot_nstack credits. Only call in create().
3097  *
3098  *    @luatparam number num Number of credits.
3099  *    @luafunc setcredits( num )
3100  */
aiL_credits(lua_State * L)3101 static int aiL_credits( lua_State *L )
3102 {
3103    if (aiL_status != AI_STATUS_CREATE) {
3104       /*NLUA_ERROR(L, "This function must be called in \"create\" only.");*/
3105       return 0;
3106    }
3107 
3108    cur_pilot->credits = luaL_checklong(L,1);
3109 
3110    return 0;
3111 }
3112 
3113 
3114 /**
3115  * @brief Returns and clears the pilots message queue.
3116  *
3117  *    @luafunc messages()
3118  *    @luatreturn {{},...} Messages.
3119  */
aiL_messages(lua_State * L)3120 static int aiL_messages( lua_State *L )
3121 {
3122    lua_rawgeti(L, LUA_REGISTRYINDEX, cur_pilot->messages);
3123    lua_newtable(naevL);
3124    lua_rawseti(L, LUA_REGISTRYINDEX, cur_pilot->messages);
3125    return 1;
3126 }
3127 
3128 /**
3129  * @}
3130  */
3131