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