naev 0.11.5
ai.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
61#include <ctype.h>
62#include <lauxlib.h>
63#include <lualib.h>
64#include <math.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include "physfs.h"
68
69#include "naev.h"
72#include "ai.h"
73
74#include "conf.h"
75#include "array.h"
76#include "board.h"
77#include "escort.h"
78#include "faction.h"
79#include "hook.h"
80#include "gatherable.h"
81#include "log.h"
82#include "ndata.h"
83#include "nlua.h"
84#include "nlua_asteroid.h"
85#include "nlua_faction.h"
86#include "nlua_pilot.h"
87#include "nlua_spob.h"
88#include "nlua_rnd.h"
89#include "nlua_vec2.h"
90#include "nluadef.h"
91#include "nstring.h"
92#include "physics.h"
93#include "pilot.h"
94#include "player.h"
95#include "rng.h"
96#include "space.h"
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 )
105#define ai_isFlag(f) (pilot_flags & f )
106/* flags */
107#define AI_PRIMARY (1<<0)
108#define AI_SECONDARY (1<<1)
109#define AI_DISTRESS (1<<2)
111/*
112 * all the AI profiles
113 */
114static AI_Profile* profiles = NULL;
115static nlua_env equip_env = LUA_NOREF;
117static double ai_dt = 0.;
119/*
120 * prototypes
121 */
122/* Internal C routines */
123static void ai_run( nlua_env env, int nargs );
124static int ai_loadProfile( AI_Profile *prof, const char* filename );
125static int ai_setMemory (void);
126static void ai_create( Pilot* pilot );
127static int ai_loadEquip (void);
128static int ai_sort( const void *p1, const void *p2 );
129/* Task management. */
130static void ai_taskGC( Pilot* pilot );
131static Task* ai_createTask( lua_State *L, int subtask );
132static int ai_tasktarget( lua_State *L, const Task *t );
133
134/*
135 * AI routines for Lua
136 */
137/* tasks */
138static int aiL_pushtask( lua_State *L ); /* pushtask( string, number/pointer ) */
139static int aiL_poptask( lua_State *L ); /* poptask() */
140static int aiL_taskname( lua_State *L ); /* string taskname() */
141static int aiL_taskdata( lua_State *L ); /* pointer subtaskdata() */
142static int aiL_pushsubtask( lua_State *L ); /* pushsubtask( string, number/pointer, number ) */
143static int aiL_popsubtask( lua_State *L ); /* popsubtask() */
144static int aiL_subtaskname( lua_State *L ); /* string subtaskname() */
145static int aiL_subtaskdata( lua_State *L ); /* pointer subtaskdata() */
146
147/* consult values */
148static int aiL_pilot( lua_State *L ); /* number pilot() */
149static int aiL_getrndpilot( lua_State *L ); /* number getrndpilot() */
150static int aiL_getnearestpilot( lua_State *L ); /* number getnearestpilot() */
151static int aiL_getdistance( lua_State *L ); /* number getdist(vec2) */
152static int aiL_getdistance2( lua_State *L ); /* number getdist(vec2) */
153static int aiL_getflybydistance( lua_State *L ); /* number getflybydist(vec2) */
154static int aiL_minbrakedist( lua_State *L ); /* number minbrakedist( [number] ) */
155static int aiL_isbribed( lua_State *L ); /* bool isbribed( number ) */
156static int aiL_getGatherable( lua_State *L ); /* integer getgatherable( radius ) */
157static int aiL_instantJump( lua_State *L ); /* bool instantJump() */
158
159/* boolean expressions */
160static int aiL_ismaxvel( lua_State *L ); /* boolean ismaxvel() */
161static int aiL_isstopped( lua_State *L ); /* boolean isstopped() */
162static int aiL_isenemy( lua_State *L ); /* boolean isenemy( number ) */
163static int aiL_isally( lua_State *L ); /* boolean isally( number ) */
164static int aiL_haslockon( lua_State *L ); /* boolean haslockon() */
165static int aiL_hasprojectile( lua_State *L ); /* boolean hasprojectile() */
166static int aiL_scandone( lua_State *L );
167
168/* movement */
169static int aiL_accel( lua_State *L ); /* accel(number); number <= 1. */
170static int aiL_turn( lua_State *L ); /* turn(number); abs(number) <= 1. */
171static int aiL_careful_face( lua_State *L ); /* face( number/pointer, bool) */
172static int aiL_aim( lua_State *L ); /* aim(number) */
173static int aiL_dir( lua_State *L ); /* dir(number/pointer) */
174static int aiL_face( lua_State *L ); /* face( number/pointer, bool) */
175static int aiL_iface( lua_State *L ); /* iface(number/pointer) */
176static int aiL_idir( lua_State *L ); /* idir(number/pointer) */
177static int aiL_follow_accurate( lua_State *L ); /* follow_accurate() */
178static int aiL_face_accurate( lua_State *L ); /* face_accurate() */
179static int aiL_drift_facing( lua_State *L ); /* drift_facing(number/pointer) */
180static int aiL_brake( lua_State *L ); /* brake() */
181static int aiL_getnearestspob( lua_State *L ); /* Vec2 getnearestspob() */
182static int aiL_getspobfrompos( lua_State *L ); /* Vec2 getspobfrompos() */
183static int aiL_getrndspob( lua_State *L ); /* Vec2 getrndspob() */
184static int aiL_getlandspob( lua_State *L ); /* Vec2 getlandspob() */
185static int aiL_land( lua_State *L ); /* bool land() */
186static int aiL_stop( lua_State *L ); /* stop() */
187static int aiL_relvel( lua_State *L ); /* relvel( number ) */
188
189/* Hyperspace. */
190static int aiL_sethyptarget( lua_State *L );
191static int aiL_nearhyptarget( lua_State *L ); /* pointer rndhyptarget() */
192static int aiL_rndhyptarget( lua_State *L ); /* pointer rndhyptarget() */
193static int aiL_hyperspace( lua_State *L ); /* [number] hyperspace() */
194static int aiL_canHyperspace( lua_State *L );
195static int aiL_hyperspaceAbort( lua_State *L );
196
197/* escorts */
198static int aiL_dock( lua_State *L ); /* dock( number ) */
199
200/* combat */
201static int aiL_combat( lua_State *L ); /* combat( number ) */
202static int aiL_settarget( lua_State *L ); /* settarget( number ) */
203static int aiL_weapSet( lua_State *L ); /* weapset( number ) */
204static int aiL_shoot( lua_State *L ); /* shoot( number ); number = 1,2,3 */
205static int aiL_hascannons( lua_State *L ); /* bool hascannons() */
206static int aiL_hasturrets( lua_State *L ); /* bool hasturrets() */
207static int aiL_hasfighterbays( lua_State *L ); /* bool hasfighterbays() */
208static int aiL_hasafterburner( lua_State *L ); /* bool hasafterburner() */
209static int aiL_getenemy( lua_State *L ); /* number getenemy() */
210static int aiL_hostile( lua_State *L ); /* hostile( number ) */
211static int aiL_getweaprange( lua_State *L ); /* number getweaprange() */
212static int aiL_getweapspeed( lua_State *L ); /* number getweapspeed() */
213static int aiL_getweapammo( lua_State *L );
214static int aiL_canboard( lua_State *L ); /* boolean canboard( number ) */
215static int aiL_relsize( lua_State *L ); /* boolean relsize( number ) */
216static int aiL_reldps( lua_State *L ); /* boolean reldps( number ) */
217static int aiL_relhp( lua_State *L ); /* boolean relhp( number ) */
218
219/* timers */
220static int aiL_settimer( lua_State *L ); /* settimer( number, number ) */
221static int aiL_timeup( lua_State *L ); /* boolean timeup( number ) */
222
223/* messages */
224static int aiL_distress( lua_State *L ); /* distress( string [, bool] ) */
225static int aiL_getBoss( lua_State *L ); /* number getBoss() */
226
227/* loot */
228static int aiL_credits( lua_State *L ); /* credits( number ) */
229
230/* misc */
231static int aiL_board( lua_State *L ); /* boolean board() */
232static int aiL_refuel( lua_State *L ); /* boolean, boolean refuel() */
233static int aiL_messages( lua_State *L );
234static int aiL_setasterotarget( lua_State *L ); /* setasterotarget( Asteroid ) */
235static int aiL_gatherablePos( lua_State *L ); /* gatherablepos( number ) */
236static int aiL_shoot_indicator( lua_State *L ); /* get shoot indicator */
237static int aiL_set_shoot_indicator( lua_State *L ); /* set shoot indicator */
238static int aiL_stealth( lua_State *L );
239
240static const luaL_Reg aiL_methods[] = {
241 /* tasks */
242 { "pushtask", aiL_pushtask },
243 { "poptask", aiL_poptask },
244 { "taskname", aiL_taskname },
245 { "taskdata", aiL_taskdata },
246 { "pushsubtask", aiL_pushsubtask },
247 { "popsubtask", aiL_popsubtask },
248 { "subtaskname", aiL_subtaskname },
249 { "subtaskdata", aiL_subtaskdata },
250 /* is */
251 { "ismaxvel", aiL_ismaxvel },
252 { "isstopped", aiL_isstopped },
253 { "isenemy", aiL_isenemy },
254 { "isally", aiL_isally },
255 { "haslockon", aiL_haslockon },
256 { "hasprojectile", aiL_hasprojectile },
257 { "scandone", aiL_scandone },
258 /* get */
259 { "pilot", aiL_pilot },
260 { "rndpilot", aiL_getrndpilot },
261 { "nearestpilot", aiL_getnearestpilot },
262 { "dist", aiL_getdistance },
263 { "dist2", aiL_getdistance2 },
264 { "flyby_dist", aiL_getflybydistance },
265 { "minbrakedist", aiL_minbrakedist },
266 { "isbribed", aiL_isbribed },
267 { "getgatherable", aiL_getGatherable },
268 { "instantJump", aiL_instantJump },
269 /* movement */
270 { "nearestspob", aiL_getnearestspob },
271 { "spobfrompos", aiL_getspobfrompos },
272 { "rndspob", aiL_getrndspob },
273 { "landspob", aiL_getlandspob },
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 { "face_accurate", aiL_face_accurate },
288 /* Hyperspace. */
289 { "sethyptarget", aiL_sethyptarget },
290 { "nearhyptarget", aiL_nearhyptarget },
291 { "rndhyptarget", aiL_rndhyptarget },
292 { "hyperspace", aiL_hyperspace },
293 { "canHyperspace", aiL_canHyperspace },
294 { "hyperspaceAbort", aiL_hyperspaceAbort },
295 { "dock", aiL_dock },
296 /* combat */
297 { "aim", aiL_aim },
298 { "combat", aiL_combat },
299 { "settarget", aiL_settarget },
300 { "weapset", aiL_weapSet },
301 { "hascannons", aiL_hascannons },
302 { "hasturrets", aiL_hasturrets },
303 { "hasfighterbays", aiL_hasfighterbays },
304 { "hasafterburner", aiL_hasafterburner },
305 { "shoot", aiL_shoot },
306 { "getenemy", aiL_getenemy },
307 { "hostile", aiL_hostile },
308 { "getweaprange", aiL_getweaprange },
309 { "getweapspeed", aiL_getweapspeed },
310 { "getweapammo", aiL_getweapammo },
311 { "canboard", aiL_canboard },
312 { "relsize", aiL_relsize },
313 { "reldps", aiL_reldps },
314 { "relhp", aiL_relhp },
315 /* timers */
316 { "settimer", aiL_settimer },
317 { "timeup", aiL_timeup },
318 /* messages */
319 { "distress", aiL_distress },
320 { "getBoss", aiL_getBoss },
321 /* loot */
322 { "setcredits", aiL_credits },
323 /* misc */
324 { "board", aiL_board },
325 { "refuel", aiL_refuel },
326 { "messages", aiL_messages },
327 { "setasterotarget", aiL_setasterotarget },
328 { "gatherablepos", aiL_gatherablePos },
329 { "shoot_indicator", aiL_shoot_indicator },
330 { "set_shoot_indicator", aiL_set_shoot_indicator },
331 { "stealth", aiL_stealth },
332 {0,0} /* end */
333};
335/*
336 * current pilot "thinking" and assorted variables
337 */
339static double pilot_acc = 0.;
340static double pilot_turn = 0.;
341static int pilot_flags = 0;
342static char aiL_distressmsg[STRMAX_SHORT];
344/*
345 * ai status, used so that create functions can't be used elsewhere
346 */
347#define AI_STATUS_NORMAL 1
348#define AI_STATUS_CREATE 2
356static void ai_taskGC( Pilot* pilot )
357{
358 Task *prev = NULL;
359 Task *t = pilot->task;
360 while (t != NULL) {
361 if (t->done) {
362 Task * pointer = t;
363 /* Unattach pointer. */
364 t = t->next;
365 if (prev == NULL)
366 pilot->task = t;
367 else
368 prev->next = t;
369 /* Free pointer. */
370 pointer->next = NULL;
371 ai_freetask( pointer );
372 }
373 else {
374 prev = t;
375 t = t->next;
376 }
377 }
378}
379
384{
385 /* Get last task. */
386 for (Task *t=pilot->task; t!=NULL; t=t->next)
387 if (!t->done)
388 return t;
389 return NULL;
390}
391
395static int ai_setMemory (void)
396{
397 int oldmem;
398 nlua_env env = cur_pilot->ai->env;
399
400 nlua_getenv( naevL, env, "mem" ); /* oldmem */
401 oldmem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
402
403 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->lua_mem );
404 nlua_setenv(naevL, env, "mem"); /* pm */
405
406 return oldmem;
407}
408
415{
416 AIMemory oldmem;
417 cur_pilot = p;
418 oldmem.p = cur_pilot;
419 oldmem.mem = ai_setMemory();
420 return oldmem;
421}
422
427{
428 nlua_env env = cur_pilot->ai->env;
429 lua_rawgeti( naevL, LUA_REGISTRYINDEX, oldmem.mem );
430 nlua_setenv( naevL, env, "mem"); /* pm */
431 luaL_unref( naevL, LUA_REGISTRYINDEX, oldmem.mem );
432 cur_pilot = oldmem.p;
433}
434
438void ai_thinkSetup( double dt )
439{
440 /* Clean up some variables */
441 pilot_acc = 0.;
442 pilot_turn = 0.;
443 pilot_flags = 0;
444 ai_dt = MAX( dt, 1e-5 );
445}
446
453{
454 /* Make sure pilot_acc and pilot_turn are legal */
455 pilot_acc = CLAMP( -PILOT_REVERSE_THRUST, 1., pilot_acc );
456 pilot_turn = CLAMP( -1., 1., pilot_turn );
457
458 /* Set turn and accel. */
461
462 /* fire weapons if needed */
464 pilot_shoot(p, 0); /* primary */
466 pilot_shoot(p, 1 ); /* secondary */
467
468 /* other behaviours. */
471}
472
479static void ai_run( nlua_env env, int nargs )
480{
481 if (nlua_pcall(env, nargs, 0)) { /* error has occurred */
482 WARN( _("Pilot '%s' ai '%s' error: %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL,-1));
483 lua_pop(naevL,1);
484 }
485}
486
496int ai_pinit( Pilot *p, const char *ai )
497{
498 AI_Profile *prof;
499 char buf[PATH_MAX];
500
501 strncpy(buf, ai, sizeof(buf)-1);
502 buf[sizeof(buf)-1] = '\0';
503
504 /* Set up the profile. */
505 prof = ai_getProfile(buf);
506 if (prof == NULL) {
507 WARN( _("AI Profile '%s' not found, using dummy fallback."), buf);
508 prof = ai_getProfile("dummy");
509 }
510 if (prof == NULL) {
511 WARN( _("Dummy AI Profile not valid! Things are going to break.") );
512 return -1;
513 }
514 p->ai = prof;
515
516 /* Adds a new pilot memory in the memory table. */
517 lua_newtable(naevL); /* m */
518
519 /* Copy defaults over from the global memory table. */
520 lua_rawgeti( naevL, LUA_REGISTRYINDEX, prof->lua_mem ); /* m, d */
521 lua_pushnil(naevL); /* m, d, nil */
522 while (lua_next(naevL,-2) != 0) { /* m, d, k, v */
523 lua_pushvalue(naevL,-2); /* m, d, k, v, k */
524 lua_pushvalue(naevL,-2); /* m, d, k, v, k, v */
525 lua_remove(naevL, -3); /* m, d, k, k, v */
526 lua_settable(naevL,-5); /* m, d, k */
527 } /* m, d */
528 lua_pop(naevL,1); /* m */
529 p->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
530
531 /* Create the pilot. */
532 ai_create( p );
533 pilot_setFlag(p, PILOT_CREATED_AI);
534
535 /* Initialize randomly within a control tick. */
536 /* This doesn't work as nicely as one would expect because the pilot
537 * has no initial task and control ticks get synchronized if you
538 * spawn a bunch at the same time, which is why we add randomness
539 * elsewhere. */
540 p->tcontrol = RNGF() * p->ai->control_rate;
541
542 return 0;
543}
544
551{
552 /* Clean up tasks. */
553 if (p->task)
554 ai_freetask( p->task );
555 p->task = NULL;
556}
557
564{
565 if (p->ai == NULL)
566 return;
567
568 /* Get rid of pilot's memory. */
569 if (!pilot_isPlayer(p)) { /* Player is an exception as more than one ship shares pilot id. */
570 luaL_unref( naevL, LUA_REGISTRYINDEX, p->lua_mem );
571 p->lua_mem = LUA_NOREF;
572 }
573
574 /* Clear the tasks. */
575 ai_cleartasks( p );
576}
577
578static int ai_sort( const void *p1, const void *p2 )
579{
580 const AI_Profile *ai1 = (const AI_Profile*) p1;
581 const AI_Profile *ai2 = (const AI_Profile*) p2;
582 return strcmp( ai1->name, ai2->name );
583}
584
590int ai_load (void)
591{
592 char** files;
593#if DEBUGGING
594 Uint32 time = SDL_GetTicks();
595#endif /* DEBUGGING */
596
597 /* get the file list */
598 files = PHYSFS_enumerateFiles( AI_PATH );
599
600 /* Create array. */
602
603 /* load the profiles */
604 for (size_t i=0; files[i]!=NULL; i++) {
605 AI_Profile prof;
606 char path[PATH_MAX];
607 int ret;
608
609 if (!ndata_matchExt( files[i], "lua" ))
610 continue;
611
612 snprintf( path, sizeof(path), AI_PATH"%s", files[i] );
613 ret = ai_loadProfile(&prof,path); /* Load the profile */
614 if (ret == 0)
615 array_push_back( &profiles, prof );
616 else
617 WARN( _("Error loading AI profile '%s'"), path);
618
619 /* Render if necessary. */
621 }
622 qsort( profiles, array_size(profiles), sizeof(AI_Profile), ai_sort );
623
624 /* More clean up. */
625 PHYSFS_freeList( files );
626
627#if DEBUGGING
628 if (conf.devmode) {
629 time = SDL_GetTicks() - time;
630 DEBUG( n_("Loaded %d AI Profile in %.3f s", "Loaded %d AI Profiles in %.3f s", array_size(profiles) ), array_size(profiles), time/1000. );
631 }
632 else
633 DEBUG( n_("Loaded %d AI Profile", "Loaded %d AI Profiles", array_size(profiles) ), array_size(profiles) );
634#endif /* DEBUGGING */
635
636 /* Create collision stuff. */
637 il_create( &ai_qtquery, 1 );
638
639 /* Load equipment thingy. */
640 return ai_loadEquip();
641}
642
646static int ai_loadEquip (void)
647{
648 char *buf;
649 size_t bufsize;
650 const char *filename = AI_EQUIP_PATH;
651
652 /* Make sure doesn't already exist. */
653 nlua_freeEnv(equip_env);
654
655 /* Create new state. */
656 equip_env = nlua_newEnv();
658
659 /* Load the file. */
660 buf = ndata_read( filename, &bufsize );
661 if (nlua_dobufenv(equip_env, buf, bufsize, filename) != 0) {
662 WARN( _("Error loading file: %s\n"
663 "%s\n"
664 "Most likely Lua file has improper syntax, please check"),
665 filename, lua_tostring(naevL, -1));
666 return -1;
667 }
668 free(buf);
669
670 return 0;
671}
672
673int nlua_loadAI( nlua_env env )
674{
675 nlua_register(env, "ai", aiL_methods, 0);
676 return 0;
677}
678
686static int ai_loadProfile( AI_Profile *prof, const char* filename )
687{
688 char* buf = NULL;
689 size_t bufsize = 0;
690 nlua_env env;
691 size_t len;
692 const char *str;
693
694 /* Set name. */
695 len = strlen(filename)-strlen(AI_PATH)-strlen(".lua");
696 prof->name = malloc(len+1);
697 strncpy( prof->name, &filename[strlen(AI_PATH)], len );
698 prof->name[len] = '\0';
699
700 /* Create Lua. */
701 env = nlua_newEnv();
703 prof->env = env;
704
705 /* Register C functions in Lua */
706 nlua_register(env, "ai", aiL_methods, 0);
707
708 /* Mark as an ai. */
709 lua_pushboolean( naevL, 1 );
710 nlua_setenv( naevL, env, "__ai" );
711
712 /* Set "mem" to be default template. */
713 lua_newtable(naevL); /* m */
714 lua_pushvalue(naevL,-1); /* m, m */
715 prof->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* m */
716 nlua_setenv(naevL, env, "mem"); /* */
717
718 /* Now load the file since all the functions have been previously loaded */
719 buf = ndata_read( filename, &bufsize );
720 if (nlua_dobufenv(env, buf, bufsize, filename) != 0) {
721 WARN( _("Error loading AI file: %s\n"
722 "%s\n"
723 "Most likely Lua file has improper syntax, please check"),
724 filename, lua_tostring(naevL,-1));
725 free(prof->name);
726 nlua_freeEnv( env );
727 free(buf);
728 return -1;
729 }
730 free(buf);
731
732 /* Find and set up the necessary references. */
733 str = _("AI Profile '%s' is missing '%s' function!");
734 prof->ref_control = nlua_refenvtype( env, "control", LUA_TFUNCTION );
735 if (prof->ref_control == LUA_NOREF)
736 WARN( str, filename, "control" );
737 prof->ref_control_manual = nlua_refenvtype( env, "control_manual", LUA_TFUNCTION );
738 if (prof->ref_control_manual == LUA_NOREF)
739 WARN( str, filename, "control_manual" );
740 prof->ref_refuel = nlua_refenvtype( env, "refuel", LUA_TFUNCTION );
741 if (prof->ref_refuel == LUA_NOREF)
742 WARN( str, filename, "refuel" );
743 prof->ref_create = nlua_refenvtype( env, "create", LUA_TFUNCTION );
744 if (prof->ref_create == LUA_NOREF)
745 WARN( str, filename, "create" );
746
747 /* Get the control rate. */
748 nlua_getenv(naevL, env, "control_rate");
749 prof->control_rate = lua_tonumber(naevL,-1);
750 lua_pop(naevL,1);
751
752 return 0;
753}
754
761AI_Profile* ai_getProfile( const char *name )
762{
763 const AI_Profile ai = { .name = (char*)name };
764 AI_Profile *ret = bsearch( &ai, profiles, array_size(profiles), sizeof(AI_Profile), ai_sort );
765 if (ret==NULL)
766 WARN( _("AI Profile '%s' not found in AI stack"), name);
767 return ret;
768}
769
773void ai_exit (void)
774{
775 /* Free AI profiles. */
776 for (int i=0; i<array_size(profiles); i++) {
777 free(profiles[i].name);
778 nlua_freeEnv(profiles[i].env);
779 }
781
782 /* Free equipment Lua. */
783 nlua_freeEnv(equip_env);
784 equip_env = LUA_NOREF;
785
786 /* Clean up query stuff. */
787 il_destroy( &ai_qtquery );
788}
789
797void ai_think( Pilot* pilot, double dt, int dotask )
798{
799 nlua_env env;
800 AIMemory oldmem;
801 Task *t;
802
803 /* Must have AI. */
804 if (pilot->ai == NULL)
805 return;
806
807 oldmem = ai_setPilot(pilot);
808 env = cur_pilot->ai->env; /* set the AI profile to the current pilot's */
809
810 ai_thinkSetup( dt );
811 pilot_rmFlag( pilot, PILOT_SCANNING ); /* Reset each frame, only set if the pilot is checking ai.scandone. */
812 /* So the way this works is that, for other than the player, we reset all
813 * the weapon sets every frame, so that the AI has to redo them over and
814 * over. Now, this is a horrible hack so shit works and needs a proper fix.
815 * TODO fix. */
816 /* pilot_setTarget( cur_pilot, cur_pilot->id ); */
817 if (cur_pilot->id != PLAYER_ID)
819
820 /* Get current task. */
821 t = ai_curTask( cur_pilot );
822
823 /* control function if pilot is idle or tick is up */
824 if ((cur_pilot->tcontrol < 0.) || (t == NULL)) {
825 double crate = cur_pilot->ai->control_rate;
826 if (pilot_isFlag(pilot,PILOT_PLAYER) ||
827 pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL)) {
828 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_control_manual );
829 lua_pushnumber( naevL, crate-cur_pilot->tcontrol );
830 ai_run(env, 1);
831 } else {
832 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_control );
833 lua_pushnumber( naevL, crate-cur_pilot->tcontrol );
834 ai_run(env, 1); /* run control */
835 }
836 /* Try to desync control ticks when possible by adding randomness. */
837 cur_pilot->tcontrol = crate * (0.9+0.2*RNGF());
838
839 /* Task may have changed due to control tick. */
840 t = ai_curTask( cur_pilot );
841 }
842
843 if (!dotask) {
844 ai_unsetPilot( oldmem );
845 return;
846 }
847
848 /* pilot has a currently running task */
849 if (t != NULL) {
850 int data;
851 /* Run subtask if available, otherwise run main task. */
852 if (t->subtask != NULL) {
853 lua_rawgeti( naevL, LUA_REGISTRYINDEX, t->subtask->func );
854 /* Use subtask data or task data if subtask is not set. */
855 data = t->subtask->dat;
856 if (data == LUA_NOREF)
857 data = t->dat;
858 }
859 else {
860 lua_rawgeti( naevL, LUA_REGISTRYINDEX, t->func );
861 data = t->dat;
862 }
863 /* Function should be on the stack. */
864 if (data != LUA_NOREF) {
865 lua_rawgeti( naevL, LUA_REGISTRYINDEX, data );
866 ai_run(env, 1);
867 } else
868 ai_run(env, 0);
869
870 /* Manual control must check if IDLE hook has to be run. */
871 if (pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL)) {
872 /* We must yet check again to see if there still is a current task running. */
873 if (ai_curTask( cur_pilot ) == NULL)
874 pilot_runHook( cur_pilot, PILOT_HOOK_IDLE );
875 }
876 }
877
878 /* Applies local variables to the pilot. */
880
881 /* Restore memory. */
882 ai_unsetPilot( oldmem );
883
884 /* Clean up if necessary. */
886}
887
893void ai_init( Pilot *p )
894{
895 AIMemory oldmem;
896 if ((p->ai==NULL) || (p->ai->ref_create==LUA_NOREF))
897 return;
898 oldmem = ai_setPilot( p );
899 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->ai->ref_create );
900 ai_run( p->ai->env, 0 ); /* run control */
901 ai_unsetPilot( oldmem );
902}
903
911void ai_attacked( Pilot *attacked, const unsigned int attacker, double dmg )
912{
913 AIMemory oldmem;
914 HookParam hparam[2];
915
916 /* Custom hook parameters. */
917 hparam[0].type = HOOK_PARAM_PILOT;
918 hparam[0].u.lp = attacker;
919 hparam[1].type = HOOK_PARAM_NUMBER;
920 hparam[1].u.num = dmg;
921
922 /* Behaves differently if manually overridden. */
923 pilot_runHookParam( attacked, PILOT_HOOK_ATTACKED, hparam, 2 );
924
925 /* Must have an AI profile. */
926 if (attacked->ai == NULL)
927 return;
928
929 oldmem = ai_setPilot( attacked ); /* Sets cur_pilot. */
930 if (pilot_isFlag( attacked, PILOT_MANUAL_CONTROL ))
931 nlua_getenv(naevL, cur_pilot->ai->env, "attacked_manual");
932 else
933 nlua_getenv(naevL, cur_pilot->ai->env, "attacked");
934
935 lua_pushpilot(naevL, attacker);
936 if (nlua_pcall(cur_pilot->ai->env, 1, 0)) {
937 WARN( _("Pilot '%s' ai '%s' -> 'attacked': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL, -1));
938 lua_pop(naevL, 1);
939 }
940 ai_unsetPilot( oldmem );
941}
942
948void ai_discovered( Pilot* discovered )
949{
950 AIMemory oldmem;
951
952 /* Behaves differently if manually overridden. */
953 pilot_runHook( discovered, PILOT_HOOK_DISCOVERED );
954 if (pilot_isFlag( discovered, PILOT_MANUAL_CONTROL ))
955 return;
956
957 /* Must have an AI profile and not be player. */
958 if (discovered->ai == NULL)
959 return;
960
961 oldmem = ai_setPilot( discovered ); /* Sets cur_pilot. */
962
963 /* Only run if discovered function exists. */
964 nlua_getenv(naevL, cur_pilot->ai->env, "discovered");
965 if (lua_isnil(naevL,-1)) {
966 lua_pop(naevL,1);
967 ai_unsetPilot( oldmem );
968 return;
969 }
970
971 if (nlua_pcall(cur_pilot->ai->env, 0, 0)) {
972 WARN( _("Pilot '%s' ai '%s' -> 'discovered': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL, -1));
973 lua_pop(naevL, 1);
974 }
975 ai_unsetPilot( oldmem );
976}
977
983void ai_hail( Pilot* recipient )
984{
985 AIMemory oldmem;
986
987 /* Make sure it's getable. */
988 if (!pilot_canTarget( recipient ))
989 return;
990
991 /* Must have an AI profile and not be player. */
992 if (recipient->ai == NULL)
993 return;
994
995 oldmem = ai_setPilot( recipient ); /* Sets cur_pilot. */
996
997 /* Only run if hail function exists. */
998 nlua_getenv(naevL, cur_pilot->ai->env, "hail");
999 if (lua_isnil(naevL,-1)) {
1000 lua_pop(naevL,1);
1001 ai_unsetPilot( oldmem );
1002 return;
1003 }
1004
1005 if (nlua_pcall(cur_pilot->ai->env, 0, 0)) {
1006 WARN( _("Pilot '%s' ai '%s' -> 'hail': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL, -1));
1007 lua_pop(naevL, 1);
1008 }
1009 ai_unsetPilot( oldmem );
1010}
1011
1018void ai_refuel( Pilot* refueler, unsigned int target )
1019{
1020 Task *t;
1021
1022 if (cur_pilot->ai->ref_refuel==LUA_NOREF) {
1023 WARN(_("Pilot '%s' (ai '%s') is trying to refuel when no 'refuel' function is defined!"), cur_pilot->name, cur_pilot->ai->name);
1024 return;
1025 }
1026
1027 /* Create the task. */
1028 t = calloc( 1, sizeof(Task) );
1029 t->name = strdup("refuel");
1030 lua_rawgeti(naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_refuel);
1031 t->func = luaL_ref(naevL, LUA_REGISTRYINDEX);
1032 lua_pushpilot(naevL, target);
1033 t->dat = luaL_ref(naevL, LUA_REGISTRYINDEX);
1034
1035 /* Prepend the task. */
1036 t->next = refueler->task;
1037 refueler->task = t;
1038
1039 return;
1040}
1041
1049void ai_getDistress( const Pilot *p, const Pilot *distressed, const Pilot *attacker )
1050{
1051 /* Ignore distress signals when under manual control. */
1052 if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ))
1053 return;
1054
1055 /* Must have AI. */
1056 if (cur_pilot->ai == NULL)
1057 return;
1058
1059 if (attacker != NULL)
1060 lua_pushpilot(naevL, attacker->id);
1061 else
1062 lua_pushnil(naevL);
1063 pilot_msg( distressed, p, "distress", -1 );
1064 lua_pop(naevL,1);
1065}
1066
1074static void ai_create( Pilot* pilot )
1075{
1076 /* Set creation mode. */
1077 if (!pilot_isFlag(pilot, PILOT_CREATED_AI))
1079
1080 /* Create equipment first - only if creating for the first time. */
1081 if (!pilot_isFlag(pilot,PILOT_NO_OUTFITS) && !pilot_isFlag(pilot,PILOT_NO_EQUIP) && (aiL_status==AI_STATUS_CREATE)) {
1082 nlua_env env = equip_env;
1083 char *func = "equip_generic";
1084
1085 if (faction_getEquipper( pilot->faction ) != LUA_NOREF) {
1086 env = faction_getEquipper( pilot->faction );
1087 func = "equip";
1088 }
1089 nlua_getenv(naevL, env, func);
1090 nlua_pushenv(naevL, env);
1091 lua_setfenv(naevL, -2);
1092 lua_pushpilot(naevL, pilot->id);
1093 if (nlua_pcall(env, 1, 0)) { /* Error has occurred. */
1094 WARN( _("Pilot '%s' equip '%s' -> '%s': %s"), pilot->name, pilot->ai->name, func, lua_tostring(naevL, -1));
1095 lua_pop(naevL, 1);
1096 }
1097
1098 /* Since the pilot changes outfits and cores, we must heal him up. */
1099 pilot_healLanded( pilot );
1100 }
1101
1102 /* Must have AI. */
1103 if (pilot->ai == NULL)
1104 return;
1105
1106 /* Set up. */
1107 ai_init( pilot );
1108
1109 /* Recover normal mode. */
1110 if (!pilot_isFlag(pilot, PILOT_CREATED_AI))
1112}
1113
1117Task *ai_newtask( lua_State *L, Pilot *p, const char *func, int subtask, int pos )
1118{
1119 Task *t, *pointer;
1120
1121 if (p->ai==NULL) {
1122 NLUA_ERROR( L, _("Trying to create new task for pilot '%s' that has no AI!"), p->name );
1123 return NULL;
1124 }
1125
1126 /* Check if the function is good. */
1127 nlua_getenv( L, p->ai->env, func );
1128 luaL_checktype( L, -1, LUA_TFUNCTION );
1129
1130 /* Create the new task. */
1131 t = calloc( 1, sizeof(Task) );
1132 t->name = strdup(func);
1133 t->func = luaL_ref( L, LUA_REGISTRYINDEX );
1134 t->dat = LUA_NOREF;
1135
1136 /* Handle subtask and general task. */
1137 if (!subtask) {
1138 if ((pos == 1) && (p->task != NULL)) { /* put at the end */
1139 for (pointer = p->task; pointer->next != NULL; pointer = pointer->next);
1140 pointer->next = t;
1141 }
1142 else {
1143 t->next = p->task;
1144 p->task = t;
1145 }
1146 }
1147 else {
1148 /* Must have valid task. */
1149 Task *curtask = ai_curTask( p );
1150 if (curtask == NULL) {
1151 ai_freetask( t );
1152 NLUA_ERROR( L, _("Trying to add subtask '%s' to non-existent task."), func);
1153 return NULL;
1154 }
1155
1156 /* Add the subtask. */
1157 if ((pos == 1) && (curtask->subtask != NULL)) { /* put at the end */
1158 for (pointer = curtask->subtask; pointer->next != NULL; pointer = pointer->next);
1159 pointer->next = t;
1160 }
1161 else {
1162 t->next = curtask->subtask;
1163 curtask->subtask = t;
1164 }
1165 }
1166
1167 return t;
1168}
1169
1176{
1177 if (t->func != LUA_NOREF)
1178 luaL_unref(naevL, LUA_REGISTRYINDEX, t->func);
1179
1180 if (t->dat != LUA_NOREF)
1181 luaL_unref(naevL, LUA_REGISTRYINDEX, t->dat);
1182
1183 /* Recursive subtask freeing. */
1184 if (t->subtask != NULL) {
1185 ai_freetask(t->subtask);
1186 t->subtask = NULL;
1187 }
1188
1189 /* Free next task in the chain. */
1190 if (t->next != NULL) {
1191 ai_freetask(t->next); /* yay recursive freeing */
1192 t->next = NULL;
1193 }
1194
1195 free(t->name);
1196 free(t);
1197}
1198
1202static Task* ai_createTask( lua_State *L, int subtask )
1203{
1204 /* Parse basic parameters. */
1205 const char *func = luaL_checkstring(L,1);
1206
1207 if (pilot_isPlayer(cur_pilot) && !pilot_isFlag(cur_pilot,PILOT_MANUAL_CONTROL))
1208 return NULL;
1209
1210 /* Creates a new AI task. */
1211 Task *t = ai_newtask( L, cur_pilot, func, subtask, 0 );
1212 if (t==NULL) {
1213 NLUA_ERROR( L, _("Failed to create new task for pilot '%s'."), cur_pilot->name );
1214 return NULL;
1215 }
1216
1217 /* Set the data. */
1218 if (lua_gettop(L) > 1) {
1219 lua_pushvalue(L,2);
1220 t->dat = luaL_ref(L, LUA_REGISTRYINDEX);
1221 }
1222
1223 return t;
1224}
1225
1229static int ai_tasktarget( lua_State *L, const Task *t )
1230{
1231 if (t->dat == LUA_NOREF)
1232 return 0;
1233 lua_rawgeti(L, LUA_REGISTRYINDEX, t->dat);
1234 return 1;
1235}
1236
1256static int aiL_pushtask( lua_State *L )
1257{
1258 ai_createTask( L, 0 );
1259 return 0;
1260}
1266static int aiL_poptask( lua_State *L )
1267{
1268 (void) L;
1269 Task *t = ai_curTask( cur_pilot );
1270 /* Tasks must exist. */
1271 if (t == NULL) {
1272 WARN(_("Trying to pop task when there are no tasks on the stack."));
1273 return 0;
1274 }
1275 /*
1276 if (strcmp(cur_pilot->ai->name,"escort")==0) {
1277 if (cur_pilot->task==t) {
1278 WARN("Popping last task!");
1279 }
1280 }
1281 */
1282 t->done = 1;
1283 return 0;
1284}
1285
1292static int aiL_taskname( lua_State *L )
1293{
1294 const Task *t = ai_curTask( cur_pilot );
1295 if (t)
1296 lua_pushstring(L, t->name);
1297 else
1298 lua_pushnil(L);
1299 return 1;
1300}
1301
1309static int aiL_taskdata( lua_State *L )
1310{
1311 const Task *t = ai_curTask( cur_pilot );
1312
1313 /* Must have a task. */
1314 if (t == NULL)
1315 return 0;
1316
1317 return ai_tasktarget( L, t );
1318}
1319
1327static int aiL_pushsubtask( lua_State *L )
1328{
1329 ai_createTask(L, 1);
1330 return 0;
1331}
1332
1338static int aiL_popsubtask( lua_State *L )
1339{
1340 Task *t, *st;
1341 t = ai_curTask( cur_pilot );
1342
1343 /* Tasks must exist. */
1344 if (t == NULL)
1345 return NLUA_ERROR(L, _("Trying to pop task when there are no tasks on the stack."));
1346 if (t->subtask == NULL)
1347 return NLUA_ERROR(L, _("Trying to pop subtask when there are no subtasks for the task '%s'."), t->name);
1348
1349 /* Exterminate, annihilate destroy. */
1350 st = t->subtask;
1351 t->subtask = st->next;
1352 st->next = NULL;
1353 ai_freetask(st);
1354 return 0;
1355}
1356
1363static int aiL_subtaskname( lua_State *L )
1364{
1365 const Task *t = ai_curTask( cur_pilot );
1366 if ((t != NULL) && (t->subtask != NULL))
1367 lua_pushstring(L, t->subtask->name);
1368 else
1369 lua_pushnil(L);
1370 return 1;
1371}
1372
1380static int aiL_subtaskdata( lua_State *L )
1381{
1382 const Task *t = ai_curTask( cur_pilot );
1383 /* Must have a subtask. */
1384 if ((t == NULL) || (t->subtask == NULL))
1385 return 0;
1386
1387 return ai_tasktarget( L, t->subtask );
1388}
1389
1396static int aiL_pilot( lua_State *L )
1397{
1399 return 1;
1400}
1401
1408static int aiL_getrndpilot( lua_State *L )
1409{
1410 Pilot *const* pilot_stack = pilot_getAll();
1411 int p = RNG(0, array_size(pilot_stack)-1);
1412 /* Make sure it can't be the same pilot. */
1413 if (pilot_stack[p]->id == cur_pilot->id) {
1414 p++;
1415 if (p >= array_size(pilot_stack))
1416 p = 0;
1417 }
1418 /* Last check. */
1419 if (pilot_stack[p]->id == cur_pilot->id)
1420 return 0;
1421 /* Actually found a pilot. */
1422 lua_pushpilot(L, pilot_stack[p]->id);
1423 return 1;
1424}
1425
1432static int aiL_getnearestpilot( lua_State *L )
1433{
1434 /* dist will be initialized to a number */
1435 /* this will only seek out pilots closer than dist */
1436 Pilot *const* pilot_stack = pilot_getAll();
1437 int dist = 1e6;
1438 int candidate_id = -1;
1439
1440 /*cycle through all the pilots and find the closest one that is not the pilot */
1441 for (int i=0; i<array_size(pilot_stack); i++) {
1442 if (pilot_stack[i]->id == cur_pilot->id)
1443 continue;
1444 if (vec2_dist(&pilot_stack[i]->solid.pos, &cur_pilot->solid.pos) > dist)
1445 continue;
1446 dist = vec2_dist(&pilot_stack[i]->solid.pos, &cur_pilot->solid.pos);
1447 candidate_id = i;
1448 }
1449
1450 /* Last check. */
1451 if (candidate_id == -1)
1452 return 0;
1453
1454 /* Actually found a pilot. */
1455 lua_pushpilot(L, pilot_stack[candidate_id]->id);
1456 return 1;
1457}
1458
1466static int aiL_getdistance( lua_State *L )
1467{
1468 const vec2 *v;
1469
1470 /* vector as a parameter */
1471 if (lua_isvector(L,1))
1472 v = lua_tovector(L,1);
1473 /* pilot as parameter */
1474 else if (lua_ispilot(L,1)) {
1475 const Pilot *p = luaL_validpilot(L,1);
1476 v = &p->solid.pos;
1477 }
1478 /* wrong parameter */
1479 else
1480 NLUA_INVALID_PARAMETER(L,1);
1481
1482 lua_pushnumber(L, vec2_dist(v, &cur_pilot->solid.pos));
1483 return 1;
1484}
1485
1493static int aiL_getdistance2( lua_State *L )
1494{
1495 const vec2 *v;
1496
1497 /* vector as a parameter */
1498 if (lua_isvector(L,1))
1499 v = lua_tovector(L,1);
1500 /* pilot as parameter */
1501 else if (lua_ispilot(L,1)) {
1502 const Pilot *p = luaL_validpilot(L,1);
1503 v = &p->solid.pos;
1504 }
1505 /* wrong parameter */
1506 else
1507 NLUA_INVALID_PARAMETER(L,1);
1508
1509 lua_pushnumber(L, vec2_dist2(v, &cur_pilot->solid.pos));
1510 return 1;
1511}
1512
1520static int aiL_getflybydistance( lua_State *L )
1521{
1522 const vec2 *v;
1523 vec2 perp_motion_unit, offset_vect;
1524 int offset_distance;
1525
1526 /* vector as a parameter */
1527 if (lua_isvector(L,1))
1528 v = lua_tovector(L,1);
1529 /* pilot id as parameter */
1530 else if (lua_ispilot(L,1)) {
1531 const Pilot *p = luaL_validpilot(L,1);
1532 v = &p->solid.pos;
1533
1534 /*vec2_cset(&v, VX(pilot->solid.pos) - VX(cur_pilot->solid.pos), VY(pilot->solid.pos) - VY(cur_pilot->solid.pos) );*/
1535 }
1536 else
1537 NLUA_INVALID_PARAMETER(L,1);
1538
1539 vec2_cset(&offset_vect, VX(*v) - VX(cur_pilot->solid.pos), VY(*v) - VY(cur_pilot->solid.pos) );
1540 vec2_pset(&perp_motion_unit, 1, VANGLE(cur_pilot->solid.vel)+M_PI_2);
1541 offset_distance = vec2_dot(&perp_motion_unit, &offset_vect);
1542
1543 lua_pushnumber(L, offset_distance);
1544 return 1;
1545}
1546
1560static int aiL_minbrakedist( lua_State *L )
1561{
1562 /* More complicated calculation based on relative velocity. */
1563 if (lua_gettop(L) > 0) {
1564 double time, dist, vel;
1565 vec2 vv;
1566 const Pilot *p = luaL_validpilot(L,1);
1567
1568 /* Set up the vectors. */
1569 vec2_cset( &vv, p->solid.vel.x - cur_pilot->solid.vel.x,
1570 p->solid.vel.y - cur_pilot->solid.vel.y );
1571
1572 /* Run the same calculations. */
1573 time = VMOD(vv) / cur_pilot->accel + ai_dt;
1574
1575 /* Get relative velocity. */
1576 vel = MIN(cur_pilot->speed - VMOD(p->solid.vel), VMOD(vv));
1577 if (vel < 0.)
1578 vel = 0.;
1579 /* Get distance to brake. */
1580 dist = vel*(time+M_PI/cur_pilot->turn+ai_dt) -
1581 0.5*(cur_pilot->accel)*time*time;
1582 lua_pushnumber(L, dist);
1583 }
1584 else
1585 lua_pushnumber( L, pilot_minbrakedist(cur_pilot, ai_dt) );
1586 return 1;
1587}
1588
1596static int aiL_isbribed( lua_State *L )
1597{
1598 const Pilot *p = luaL_validpilot(L,1);
1599 lua_pushboolean(L, pilot_isWithPlayer(p) && pilot_isFlag(cur_pilot, PILOT_BRIBED));
1600 return 1;
1601}
1602
1609static int aiL_instantJump( lua_State *L )
1610{
1611 lua_pushboolean(L, cur_pilot->stats.misc_instant_jump);
1612 return 1;
1613}
1614
1621static int aiL_ismaxvel( lua_State *L )
1622{
1623 //lua_pushboolean(L,(VMOD(cur_pilot->solid.vel) > (cur_pilot->speed-MIN_VEL_ERR)));
1624 lua_pushboolean(L,(VMOD(cur_pilot->solid.vel) >
1625 (solid_maxspeed(&cur_pilot->solid, cur_pilot->speed, cur_pilot->accel)-MIN_VEL_ERR)));
1626 return 1;
1627}
1628
1635static int aiL_isstopped( lua_State *L )
1636{
1637 lua_pushboolean(L,(VMOD(cur_pilot->solid.vel) < MIN_VEL_ERR));
1638 return 1;
1639}
1640
1648static int aiL_isenemy( lua_State *L )
1649{
1650 const Pilot *p = luaL_validpilot(L,1);
1651
1652 /* Player needs special handling in case of hostility. */
1653 if (pilot_isWithPlayer(p)) {
1654 lua_pushboolean(L, pilot_isHostile(cur_pilot));
1655 return 1;
1656 }
1657
1658 /* Check if is ally. */
1659 lua_pushboolean(L,areEnemies(cur_pilot->faction, p->faction));
1660 return 1;
1661}
1662
1670static int aiL_isally( lua_State *L )
1671{
1672 const Pilot *p = luaL_validpilot(L,1);
1673
1674 /* Player needs special handling in case of friendliness. */
1675 if (pilot_isWithPlayer(p)) {
1676 lua_pushboolean(L, pilot_isFriendly(cur_pilot));
1677 return 1;
1678 }
1679
1680 /* Check if is ally. */
1681 lua_pushboolean(L,areAllies(cur_pilot->faction, p->faction));
1682 return 1;
1683}
1684
1692static int aiL_haslockon( lua_State *L )
1693{
1694 lua_pushboolean(L, cur_pilot->lockons > 0);
1695 return 1;
1696}
1697
1705static int aiL_hasprojectile( lua_State *L )
1706{
1707 lua_pushboolean(L, cur_pilot->projectiles > 0);
1708 return 1;
1709}
1710
1718static int aiL_scandone( lua_State *L )
1719{
1720 pilot_setFlag( cur_pilot, PILOT_SCANNING ); /*< Indicate pilot is scanning this frame. */
1721 lua_pushboolean(L, pilot_ewScanCheck( cur_pilot ) );
1722 return 1;
1723}
1724
1731static int aiL_accel( lua_State *L )
1732{
1733 double n = luaL_optnumber( L, 1, 1. );
1734 pilot_acc = CLAMP( 0., 1., n );
1735 return 0;
1736}
1737
1744static int aiL_turn( lua_State *L )
1745{
1746 pilot_turn = luaL_checknumber(L,1);
1747 return 0;
1748}
1749
1763static int aiL_face( lua_State *L )
1764{
1765 const vec2 *tv; /* get the position to face */
1766 double k_diff, k_vel, diff, vx, vy, dx, dy;
1767 int vel;
1768
1769 /* Default gain. */
1770 k_diff = 1./(cur_pilot->turn*ai_dt);
1771 k_vel = 100.; /* overkill gain! */
1772
1773 /* Check if must invert. */
1774 if (lua_toboolean(L,2))
1775 k_diff *= -1;
1776
1777 /* Get first parameter, aka what to face. */
1778 if (lua_ispilot(L,1)) {
1779 Pilot* p = luaL_validpilot(L,1);
1780 /* Target vector. */
1781 tv = &p->solid.pos;
1782 }
1783 else if (lua_isnumber(L,1)) {
1784 double d = lua_tonumber(L,1);
1785 diff = angle_diff( cur_pilot->solid.dir, d );
1786 /* Make pilot turn. */
1787 pilot_turn = k_diff * diff;
1788 /* Return angle away from target. */
1789 lua_pushnumber(L, ABS(diff));
1790 return 1;
1791 }
1792 else if (lua_isvector(L,1))
1793 tv = lua_tovector(L,1);
1794 else
1795 NLUA_INVALID_PARAMETER(L,1);
1796
1797 /* Third parameter. */
1798 vel = lua_toboolean(L, 3);
1799
1800 /* Tangential component of velocity vector
1801 *
1802 * v: velocity vector
1803 * d: direction vector
1804 *
1805 * d d d
1806 * v_t = v - ( v . --- ) * --- = v - ( v . ----- ) * d
1807 * |d| |d| |d|^2
1808 */
1809 /* Velocity vector. */
1810 vx = cur_pilot->solid.vel.x;
1811 vy = cur_pilot->solid.vel.y;
1812 /* Direction vector. */
1813 dx = tv->x - cur_pilot->solid.pos.x;
1814 dy = tv->y - cur_pilot->solid.pos.y;
1815 if (vel && (dx || dy)) {
1816 /* Calculate dot product. */
1817 double d = (vx * dx + vy * dy) / (dx*dx + dy*dy);
1818 /* Calculate tangential velocity. */
1819 vx = vx - d * dx;
1820 vy = vy - d * dy;
1821
1822 /* Add velocity compensation. */
1823 dx += -k_vel * vx;
1824 dy += -k_vel * vy;
1825 }
1826
1827 /* Compensate error and rotate. */
1828 diff = angle_diff( cur_pilot->solid.dir, atan2( dy, dx ) );
1829
1830 /* Make pilot turn. */
1831 pilot_turn = k_diff * diff;
1832
1833 /* Return angle away from target. */
1834 lua_pushnumber(L, ABS(diff));
1835 return 1;
1836}
1837
1855static int aiL_careful_face( lua_State *L )
1856{
1857 vec2 *tv, F, F1;
1858 Pilot* p;
1859 double d, diff, dist;
1860 Pilot *const* pilot_stack;
1861 int x, y, r;
1862
1863 /* Default gains. */
1864 const double k_diff = 1./(cur_pilot->turn*ai_dt);
1865 const double k_goal = 1.;
1866 const double k_enemy = 6e6;
1867
1868 /* Init some variables */
1870 p = cur_pilot;
1871
1872 /* Get first parameter, aka what to face. */
1873 if (lua_ispilot(L,1)) {
1874 p = luaL_validpilot(L,1);
1875 /* Target vector. */
1876 tv = &p->solid.pos;
1877 }
1878 else if (lua_isnumber(L,1)) {
1879 d = (double)lua_tonumber(L,1);
1880 if (d < 0.)
1881 tv = &cur_pilot->solid.pos;
1882 else
1883 NLUA_INVALID_PARAMETER(L,1);
1884 }
1885 else if (lua_isvector(L,1))
1886 tv = lua_tovector(L,1);
1887 else
1888 NLUA_INVALID_PARAMETER(L,1);
1889
1890 /* Init the force, where F1 is roughly normalized to norm 1. */
1891 vec2_csetmin( &F, 0., 0.) ;
1892 vec2_cset( &F1, tv->x - cur_pilot->solid.pos.x, tv->y - cur_pilot->solid.pos.y) ;
1893 dist = VMOD(F1) + 0.1; /* Avoid / 0 */
1894 vec2_cset( &F1, F1.x * k_goal / dist, F1.y * k_goal / dist) ;
1895
1896 /* Cycle through all the pilots in order to compute the force */
1897 x = round(cur_pilot->solid.pos.x);
1898 y = round(cur_pilot->solid.pos.y);
1899 /* It's modulated by k_enemy * k_mult / dist^2, where k_mult<1 and k_enemy=6e6
1900 * A distance of 5000 should give a maximum factor of 0.24, but it should be
1901 * far away enough to not matter (hopefully).. */
1902 r = 5000;
1903 pilot_collideQueryIL( &ai_qtquery, x-r, y-r, x+r, y+r );
1904 for (int i=0; i<il_size(&ai_qtquery); i++ ) {
1905 const Pilot *p_i = pilot_stack[ il_get( &ai_qtquery, i, 0 ) ];
1906
1907 /* Valid pilot isn't self, is in range, isn't the target and isn't disabled */
1908 if (p_i->id == cur_pilot->id)
1909 continue;
1910 if (p_i->id == p->id)
1911 continue;
1912 if (pilot_isDisabled(p_i) )
1913 continue;
1914 if (pilot_inRangePilot(cur_pilot, p_i, &dist) != 1)
1915 continue;
1916
1917 /* If the enemy is too close, ignore it*/
1918 if (dist < pow2(750.))
1919 continue;
1920 dist = sqrt(dist); /* Have to undo the square. */
1921
1922 /* Check if friendly or not */
1923 if (areEnemies(cur_pilot->faction, p_i->faction)) {
1924 double k_mult = pilot_relhp( p_i, cur_pilot ) * pilot_reldps( p_i, cur_pilot );
1925 double factor = k_enemy * k_mult / (dist*dist*dist);
1926 vec2_csetmin( &F, F.x + factor * (cur_pilot->solid.pos.x - p_i->solid.pos.x),
1927 F.y + factor * (cur_pilot->solid.pos.y - p_i->solid.pos.y) );
1928 }
1929 }
1930
1931 vec2_cset( &F, F.x + F1.x, F.y + F1.y );
1932
1933 /* Rotate. */
1934 diff = angle_diff( cur_pilot->solid.dir, VANGLE(F) );
1935
1936 /* Make pilot turn. */
1937 pilot_turn = k_diff * diff;
1938
1939 /* Return angle away from target. */
1940 lua_pushnumber(L, ABS(diff));
1941 return 1;
1942}
1943
1953static int aiL_aim( lua_State *L )
1954{
1955 double diff, mod, angle;
1956
1957 if (lua_isasteroid(L,1)) {
1958 const Asteroid *a = luaL_validasteroid(L,1);
1959 angle = pilot_aimAngle( cur_pilot, &a->sol.pos, &a->sol.vel );
1960 }
1961 else {
1962 const Pilot *p = luaL_validpilot(L,1);
1963 angle = pilot_aimAngle( cur_pilot, &p->solid.pos, &p->solid.vel );
1964 }
1965
1966 /* Calculate what we need to turn */
1967 mod = 1./(cur_pilot->turn*ai_dt);
1968 diff = angle_diff(cur_pilot->solid.dir, angle);
1969 pilot_turn = mod * diff;
1970
1971 lua_pushnumber(L, ABS(diff));
1972 return 1;
1973}
1974
1982static int aiL_iface( lua_State *L )
1983{
1984 NLUA_MIN_ARGS(1);
1985 vec2 *vec, drift, reference_vector; /* get the position to face */
1986 Pilot* p;
1987 double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
1988
1989 /* Get first parameter, aka what to face. */
1990 p = NULL;
1991 vec = NULL;
1992 if (lua_ispilot(L,1))
1993 p = luaL_validpilot(L,1);
1994 else if (lua_isvector(L,1))
1995 vec = lua_tovector(L,1);
1996 else
1997 NLUA_INVALID_PARAMETER(L,1);
1998
1999 if (vec==NULL) {
2000 if (p == NULL)
2001 return 0; /* Return silently when attempting to face an invalid pilot. */
2002 /* Establish the current pilot velocity and position vectors */
2003 vec2_cset( &drift, VX(p->solid.vel) - VX(cur_pilot->solid.vel), VY(p->solid.vel) - VY(cur_pilot->solid.vel));
2004 /* Establish the in-line coordinate reference */
2005 vec2_cset( &reference_vector, VX(p->solid.pos) - VX(cur_pilot->solid.pos), VY(p->solid.pos) - VY(cur_pilot->solid.pos));
2006 }
2007 else {
2008 /* Establish the current pilot velocity and position vectors */
2009 vec2_cset( &drift, -VX(cur_pilot->solid.vel), -VY(cur_pilot->solid.vel));
2010 /* Establish the in-line coordinate reference */
2011 vec2_cset( &reference_vector, VX(*vec) - VX(cur_pilot->solid.pos), VY(*vec) - VY(cur_pilot->solid.pos));
2012 }
2013
2014 /* Break down the the velocity vectors of both craft into UV coordinates */
2015 vec2_uv(&drift_radial, &drift_azimuthal, &drift, &reference_vector);
2016 heading_offset_azimuth = angle_diff(cur_pilot->solid.dir, VANGLE(reference_vector));
2017
2018 /* Now figure out what to do...
2019 * Are we pointing anywhere inside the correct UV quadrant?
2020 * if we're outside the correct UV quadrant, we need to get into it ASAP
2021 * Otherwise match velocities and approach */
2022 if (FABS(heading_offset_azimuth) < M_PI_2) {
2023 /* This indicates we're in the correct plane*/
2024 /* 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 */
2025 /* There is nothing special about the scaling parameter of 200; it can be tuned to get any behavior desired. A lower
2026 number will give a more dramatic 'lead' */
2027 double speedmap = -1.*copysign(1. - 1. / (FABS(drift_azimuthal/200.) + 1.), drift_azimuthal) * M_PI_2;
2028 diff = angle_diff(heading_offset_azimuth, speedmap);
2029 pilot_turn = -diff / (ai_dt * cur_pilot->turn);
2030 }
2031 /* 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 */
2032 /* some special case logic is added to optimize turn time. Reducing this to only the else cases would speed up the operation
2033 but cause the pilot to turn in the less-than-optimal direction sometimes when between 135 and 225 degrees off from the target */
2034 else {
2035 /* signal that we're not in a productive direction for accelerating */
2036 diff = M_PI;
2037 pilot_turn = heading_offset_azimuth / (ai_dt * cur_pilot->turn);
2038 }
2039
2040 /* Return angle in degrees away from target. */
2041 lua_pushnumber(L, ABS(diff));
2042 return 1;
2043}
2044
2052static int aiL_dir( lua_State *L )
2053{
2054 vec2 sv, tv; /* get the position to face */
2055 double diff;
2056
2057 /* Get first parameter, aka what to face. */
2058 vec2_cset( &sv, VX(cur_pilot->solid.pos), VY(cur_pilot->solid.pos) );
2059 if (lua_ispilot(L,1)) {
2060 const Pilot *p = luaL_validpilot(L,1);
2061 vec2_cset( &tv, VX(p->solid.pos), VY(p->solid.pos) );
2062 diff = angle_diff(cur_pilot->solid.dir,
2063 vec2_angle(&sv, &tv));
2064 }
2065 else if (lua_isvector(L,1)) {
2066 const vec2 *vec = lua_tovector(L,1);
2067 diff = angle_diff( cur_pilot->solid.dir,
2068 vec2_angle(&cur_pilot->solid.pos, vec));
2069 }
2070 else
2071 NLUA_INVALID_PARAMETER(L,1);
2072
2073 /* Return angle in degrees away from target. */
2074 lua_pushnumber(L, diff);
2075 return 1;
2076}
2077
2085static int aiL_idir( lua_State *L )
2086{
2087 NLUA_MIN_ARGS(1);
2088 vec2 *vec, drift, reference_vector; /* get the position to face */
2089 Pilot* p;
2090 double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
2091
2092 /* Get first parameter, aka what to face. */
2093 p = NULL;
2094 vec = NULL;
2095 if (lua_ispilot(L,1))
2096 p = luaL_validpilot(L,1);
2097 else if (lua_isvector(L,1))
2098 vec = lua_tovector(L,1);
2099 else
2100 NLUA_INVALID_PARAMETER(L,1);
2101
2102 if (vec==NULL) {
2103 if (p==NULL)
2104 return 0; /* Return silently when attempting to face an invalid pilot. */
2105 /* Establish the current pilot velocity and position vectors */
2106 vec2_cset( &drift, VX(p->solid.vel) - VX(cur_pilot->solid.vel), VY(p->solid.vel) - VY(cur_pilot->solid.vel));
2107 /* Establish the in-line coordinate reference */
2108 vec2_cset( &reference_vector, VX(p->solid.pos) - VX(cur_pilot->solid.pos), VY(p->solid.pos) - VY(cur_pilot->solid.pos));
2109 }
2110 else {
2111 /* Establish the current pilot velocity and position vectors */
2112 vec2_cset( &drift, -VX(cur_pilot->solid.vel), -VY(cur_pilot->solid.vel));
2113 /* Establish the in-line coordinate reference */
2114 vec2_cset( &reference_vector, VX(*vec) - VX(cur_pilot->solid.pos), VY(*vec) - VY(cur_pilot->solid.pos));
2115 }
2116
2117 /* Break down the the velocity vectors of both craft into UV coordinates */
2118 vec2_uv(&drift_radial, &drift_azimuthal, &drift, &reference_vector);
2119 heading_offset_azimuth = angle_diff(cur_pilot->solid.dir, VANGLE(reference_vector));
2120
2121 /* now figure out what to do*/
2122 /* are we pointing anywhere inside the correct UV quadrant? */
2123 /* if we're outside the correct UV quadrant, we need to get into it ASAP */
2124 /* Otherwise match velocities and approach*/
2125 if (FABS(heading_offset_azimuth) < M_PI_2) {
2126 /* This indicates we're in the correct plane
2127 * 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
2128 * there is nothing special about the scaling parameter of 200; it can be tuned to get any behavior desired. A lower
2129 * number will give a more dramatic 'lead' */
2130 double speedmap = -1.*copysign(1. - 1. / (FABS(drift_azimuthal/200.) + 1.), drift_azimuthal) * M_PI_2;
2131 diff = angle_diff(heading_offset_azimuth, speedmap);
2132
2133 }
2134 /* 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
2135 some special case logic is added to optimize turn time. Reducing this to only the else cases would speed up the operation
2136 but cause the pilot to turn in the less-than-optimal direction sometimes when between 135 and 225 degrees off from the target */
2137 else {
2138 /* signal that we're not in a productive direction for accelerating */
2139 diff = M_PI;
2140 }
2141
2142 /* Return angle in degrees away from target. */
2143 lua_pushnumber(L, diff);
2144 return 1;
2145}
2146
2153static int aiL_drift_facing( lua_State *L )
2154{
2155 double drift = angle_diff(VANGLE(cur_pilot->solid.vel), cur_pilot->solid.dir);
2156 lua_pushnumber(L, drift);
2157 return 1;
2158}
2159
2166static int aiL_brake( lua_State *L )
2167{
2168 double dir, accel, diff;
2169 int isstopped = pilot_isStopped(cur_pilot);
2170
2171 if (isstopped){
2172 lua_pushboolean(L,1);
2173 return 1;
2174 }
2175
2177 dir = VANGLE(cur_pilot->solid.vel);
2178 accel = -PILOT_REVERSE_THRUST;
2179 }
2180 else {
2181 dir = VANGLE(cur_pilot->solid.vel) + M_PI;
2182 accel = 1.;
2183 }
2184
2185 diff = angle_diff( cur_pilot->solid.dir, dir );
2186 pilot_turn = diff / (cur_pilot->turn * ai_dt);
2187 if (ABS(diff) < MIN_DIR_ERR)
2188 pilot_acc = accel;
2189 else
2190 pilot_acc = 0.;
2191 lua_pushboolean(L, 0);
2192 return 1;
2193}
2194
2201static int aiL_getnearestspob( lua_State *L )
2202{
2203 double dist, d;
2204 int j;
2205 LuaSpob spob;
2206
2207 /* cycle through spobs */
2208 dist = HUGE_VAL;
2209 j = -1;
2210 for (int i=0; i<array_size(cur_system->spobs); i++) {
2211 if (!spob_hasService(cur_system->spobs[i],SPOB_SERVICE_INHABITED))
2212 continue;
2213 d = vec2_dist( &cur_system->spobs[i]->pos, &cur_pilot->solid.pos );
2214 if ((!areEnemies(cur_pilot->faction,cur_system->spobs[i]->presence.faction)) &&
2215 (d < dist)) { /* closer friendly spob */
2216 j = i;
2217 dist = d;
2218 }
2219 }
2220
2221 /* no friendly spob found */
2222 if (j == -1) return 0;
2223
2224 cur_pilot->nav_spob = j;
2225 spob = cur_system->spobs[j]->id;
2226 lua_pushspob(L, spob);
2227
2228 return 1;
2229}
2230
2238static int aiL_getspobfrompos( lua_State *L )
2239{
2240 int j;
2241 double dist;
2242 LuaSpob spob;
2243 const vec2 *pos = luaL_checkvector(L,1);
2244
2245 /* cycle through spobs */
2246 dist = HUGE_VAL;
2247 j = -1;
2248 for (int i=0; i<array_size(cur_system->spobs); i++) {
2249 double d;
2250 if (!spob_hasService(cur_system->spobs[i],SPOB_SERVICE_INHABITED))
2251 continue;
2252 d = vec2_dist( &cur_system->spobs[i]->pos, pos );
2253 if ((!areEnemies(cur_pilot->faction,cur_system->spobs[i]->presence.faction)) &&
2254 (d < dist)) { /* closer friendly spob */
2255 j = i;
2256 dist = d;
2257 }
2258 }
2259
2260 /* no friendly spob found */
2261 if (j == -1) return 0;
2262
2263 cur_pilot->nav_spob = j;
2264 spob = cur_system->spobs[j]->id;
2265 lua_pushspob(L, spob);
2266
2267 return 1;
2268}
2269
2276static int aiL_getrndspob( lua_State *L )
2277{
2278 LuaSpob spob;
2279 int p;
2280
2281 /* No spobs. */
2282 if (array_size(cur_system->spobs) == 0)
2283 return 0;
2284
2285 /* get a random spob */
2286 p = RNG(0, array_size(cur_system->spobs)-1);
2287
2288 /* Copy the data into a vector */
2289 spob = cur_system->spobs[p]->id;
2290 lua_pushspob(L, spob);
2291
2292 return 1;
2293}
2294
2302static int aiL_getlandspob( lua_State *L )
2303{
2304 int *ind;
2305 int id;
2306 LuaSpob spob;
2307 const Spob *p;
2308 int only_friend;
2309
2310 /* If pilot can't land ignore. */
2311 if (pilot_isFlag(cur_pilot, PILOT_NOLAND))
2312 return 0;
2313
2314 /* Check if we should get only friendlies. */
2315 only_friend = lua_toboolean(L, 1);
2316
2317 /* Allocate memory. */
2318 ind = array_create_size( int, array_size(cur_system->spobs) );
2319
2320 /* Copy friendly spob.s */
2321 for (int i=0; i<array_size(cur_system->spobs); i++) {
2322 Spob *pnt = cur_system->spobs[i];
2323
2324 if (!spob_hasService(pnt, SPOB_SERVICE_LAND))
2325 continue;
2326 if (!spob_hasService(pnt, SPOB_SERVICE_INHABITED))
2327 continue;
2328
2329 /* Check conditions. */
2330 if (only_friend && !areAllies( cur_pilot->faction, pnt->presence.faction ))
2331 continue;
2333 continue;
2334
2335 /* Add it. */
2336 array_push_back( &ind, i );
2337 }
2338
2339 /* no spob to land on found */
2340 if (array_size(ind)==0) {
2341 array_free(ind);
2342 return 0;
2343 }
2344
2345 /* we can actually get a random spob now */
2346 id = RNG(0,array_size(ind)-1);
2347 p = cur_system->spobs[ ind[ id ] ];
2348 spob = p->id;
2349 lua_pushspob( L, spob );
2350 cur_pilot->nav_spob = ind[ id ];
2351 array_free(ind);
2352
2353 return 1;
2354}
2355
2363static int aiL_land( lua_State *L )
2364{
2365 const Spob *spob;
2366 HookParam hparam;
2367
2368 if (!lua_isnoneornil(L,1)) {
2369 int i;
2370 const Spob *pnt = luaL_validspob( L, 1 );
2371
2372 /* Find the spob. */
2373 for (i=0; i < array_size(cur_system->spobs); i++) {
2374 if (cur_system->spobs[i] == pnt) {
2375 break;
2376 }
2377 }
2378 if (i >= array_size(cur_system->spobs))
2379 return NLUA_ERROR( L, _("Spob '%s' not found in system '%s'"), pnt->name, cur_system->name );
2380
2381 cur_pilot->nav_spob = i;
2382 }
2383
2384 if (cur_pilot->nav_spob < 0)
2385 return NLUA_ERROR( L, _("Pilot '%s' (ai '%s') has no land target"), cur_pilot->name, cur_pilot->ai->name );
2386
2387 /* Get spob. */
2388 spob = cur_system->spobs[ cur_pilot->nav_spob ];
2389
2390 /* Check landability. */
2391 if ((spob->lua_can_land==LUA_NOREF) && !spob_hasService(spob,SPOB_SERVICE_LAND)) { /* Basic services */
2392 lua_pushboolean(L,0);
2393 return 1;
2394 }
2395 /* TODO can_land is player-specific, we need to implement this on a pilot level...
2396 if ((!pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL) && !spob->can_land)) {
2397 lua_pushboolean(L,0);
2398 return 1;
2399 }
2400 */
2401
2402 /* Check landing functionality. */
2403 if (pilot_isFlag(cur_pilot, PILOT_NOLAND)) {
2404 lua_pushboolean(L,0);
2405 return 1;
2406 }
2407
2408 /* Check distance. */
2409 if (vec2_dist2(&cur_pilot->solid.pos,&spob->pos) > pow2(spob->radius)) {
2410 lua_pushboolean(L,0);
2411 return 1;
2412 }
2413
2414 /* Check velocity. */
2415 if (vec2_odist2( &cur_pilot->solid.vel ) > pow2(MAX_HYPERSPACE_VEL)) {
2416 lua_pushboolean(L,0);
2417 return 1;
2418 }
2419
2420 if (spob->lua_land == LUA_NOREF) {
2421 cur_pilot->landing_delay = PILOT_LANDING_DELAY * cur_pilot->ship->dt_default;
2423 pilot_setFlag( cur_pilot, PILOT_LANDING );
2424 }
2425 else {
2426 lua_rawgeti(naevL, LUA_REGISTRYINDEX, spob->lua_land); /* f */
2427 lua_pushspob( naevL, spob_index(spob) );
2428 lua_pushpilot( naevL, cur_pilot->id );
2429 if (nlua_pcall( spob->lua_env, 2, 0 )) {
2430 WARN(_("Spob '%s' failed to run '%s':\n%s"), spob->name, "land", lua_tostring(naevL,-1));
2431 lua_pop(naevL,1);
2432 }
2433 }
2434
2435 hparam.type = HOOK_PARAM_SPOB;
2436 hparam.u.la = spob->id;
2437
2438 pilot_runHookParam( cur_pilot, PILOT_HOOK_LAND, &hparam, 1 );
2439 lua_pushboolean(L,1);
2440 return 1;
2441}
2442
2450static int aiL_hyperspace( lua_State *L )
2451{
2452 int dist;
2453
2454 /* Find the target jump. */
2455 if (!lua_isnoneornil(L,1)) {
2456 const JumpPoint *jp = luaL_validjump( L, 1 );
2457 const LuaJump *lj = luaL_checkjump( L, 1 );
2458 if (lj->srcid != cur_system->id)
2459 return NLUA_ERROR(L, _("Jump point must be in current system."));
2460 cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2461 }
2462
2464 if (dist == 0) {
2467 return 0;
2468 }
2469
2470 lua_pushnumber(L,dist);
2471 return 1;
2472}
2473
2481static int aiL_sethyptarget( lua_State *L )
2482{
2483 const JumpPoint *jp;
2484 const LuaJump *lj;
2485 vec2 vec;
2486 double a, rad;
2487
2488 lj = luaL_checkjump( L, 1 );
2489 jp = luaL_validjump( L, 1 );
2490
2491 if (lj->srcid != cur_system->id)
2492 return NLUA_ERROR(L, _("Jump point must be in current system."));
2493
2494 /* Copy vector. */
2495 vec = jp->pos;
2496
2497 /* Introduce some error. */
2498 a = RNGF() * M_PI * 2.;
2499 rad = RNGF() * 0.5 * jp->radius;
2500 vec2_cadd( &vec, rad*cos(a), rad*sin(a) );
2501
2502 /* Set up target. */
2503 cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2504
2505 /* Return vector. */
2506 lua_pushvector( L, vec );
2507
2508 return 1;
2509}
2510
2517static int aiL_nearhyptarget( lua_State *L )
2518{
2519 const JumpPoint *jp;
2520 double mindist, dist;
2521 LuaJump lj;
2522
2523 /* Find nearest jump .*/
2524 mindist = INFINITY;
2525 jp = NULL;
2526 for (int i=0; i < array_size(cur_system->jumps); i++) {
2527 const JumpPoint *jiter = &cur_system->jumps[i];
2528 int useshidden = faction_usesHiddenJumps( cur_pilot->faction );
2529
2530 /* Ignore exit only. */
2531 if (jp_isFlag( jiter, JP_EXITONLY ))
2532 continue;
2533
2534 /* We want only standard jump points to be used. */
2535 if (!useshidden && jp_isFlag(jiter, JP_HIDDEN))
2536 continue;
2537
2538 /* Only jump if there is presence there. */
2539 if (system_getPresence( jiter->target, cur_pilot->faction ) <= 0.)
2540 continue;
2541
2542 /* Get nearest distance. */
2543 dist = vec2_dist2( &cur_pilot->solid.pos, &jiter->pos );
2544 if (dist < mindist) {
2545 jp = jiter;
2546 mindist = dist;
2547 }
2548 }
2549 /* None available. */
2550 if (jp == NULL)
2551 return 0;
2552
2553 lj.destid = jp->targetid;
2554 lj.srcid = cur_system->id;
2555
2556 /* Return Jump. */
2557 lua_pushjump( L, lj );
2558 return 1;
2559}
2560
2567static int aiL_rndhyptarget( lua_State *L )
2568{
2569 JumpPoint **jumps;
2570 int r, useshidden;
2571 int *id;
2572 LuaJump lj;
2573
2574 /* No jumps in the system. */
2575 if (array_size(cur_system->jumps) == 0)
2576 return 0;
2577
2578 useshidden = faction_usesHiddenJumps( cur_pilot->faction );
2579
2580 /* Find usable jump points. */
2581 jumps = array_create_size( JumpPoint*, array_size(cur_system->jumps) );
2582 id = array_create_size( int, array_size(cur_system->jumps) );
2583 for (int i=0; i < array_size(cur_system->jumps); i++) {
2584 JumpPoint *jiter = &cur_system->jumps[i];
2585
2586 /* We want only standard jump points to be used. */
2587 if ((!useshidden && jp_isFlag(jiter, JP_HIDDEN)) || jp_isFlag(jiter, JP_EXITONLY))
2588 continue;
2589
2590 /* Only jump if there is presence there. */
2591 if (system_getPresence( jiter->target, cur_pilot->faction ) <= 0.)
2592 continue;
2593
2594 array_push_back( &id, i );
2595 array_push_back( &jumps, jiter );
2596 }
2597
2598 /* Try to be more lax. */
2599 if (array_size(jumps) <= 0) {
2600 for (int i=0; i < array_size(cur_system->jumps); i++) {
2601 JumpPoint *jiter = &cur_system->jumps[i];
2602
2603 /* We want only standard jump points to be used. */
2604 if ((!useshidden && jp_isFlag(jiter, JP_HIDDEN)) || jp_isFlag(jiter, JP_EXITONLY))
2605 continue;
2606
2607 array_push_back( &id, i );
2608 array_push_back( &jumps, jiter );
2609 }
2610 }
2611
2612 if (array_size(jumps) <= 0) {
2613 WARN(_("Pilot '%s' can't find jump to leave system!"), cur_pilot->name);
2614 return 0;
2615 }
2616
2617 /* Choose random jump point. */
2618 r = RNG( 0, MAX( array_size(jumps)-1, 0) );
2619
2620 lj.destid = jumps[r]->targetid;
2621 lj.srcid = cur_system->id;
2622
2623 /* Clean up. */
2624 array_free(jumps);
2625 array_free(id);
2626
2627 /* Return Jump. */
2628 lua_pushjump( L, lj );
2629 return 1;
2630}
2631
2638static int aiL_canHyperspace( lua_State *L )
2639{
2640 lua_pushboolean(L, space_canHyperspace(cur_pilot));
2641 return 1;
2642}
2643
2649static int aiL_hyperspaceAbort( lua_State *L )
2650{
2651 (void) L;
2653 return 0;
2654}
2655
2662static int aiL_relvel( lua_State *L )
2663{
2664 double dot, mod;
2665 vec2 vv, pv;
2666 int absolute;
2667 const Pilot *p = luaL_validpilot(L,1);
2668
2669 if (lua_gettop(L) > 1)
2670 absolute = lua_toboolean(L,2);
2671 else
2672 absolute = 0;
2673
2674 /* Get the projection of target on current velocity. */
2675 if (absolute == 0)
2676 vec2_cset( &vv, p->solid.vel.x - cur_pilot->solid.vel.x,
2677 p->solid.vel.y - cur_pilot->solid.vel.y );
2678 else
2679 vec2_cset( &vv, p->solid.vel.x, p->solid.vel.y);
2680
2681 vec2_cset( &pv, p->solid.pos.x - cur_pilot->solid.pos.x,
2682 p->solid.pos.y - cur_pilot->solid.pos.y );
2683 dot = vec2_dot( &pv, &vv );
2684 mod = MAX(VMOD(pv), 1.); /* Avoid /0. */
2685
2686 lua_pushnumber(L, dot / mod );
2687 return 1;
2688}
2689
2703static int aiL_follow_accurate( lua_State *L )
2704{
2705 vec2 point, cons, goal, pv;
2706 double radius, angle, Kp, Kd, angle2;
2707 const Pilot *p, *target;
2708 const char *method;
2709
2710 p = cur_pilot;
2711 target = luaL_validpilot(L,1);
2712 radius = luaL_checknumber(L,2);
2713 angle = luaL_checknumber(L,3);
2714 Kp = luaL_checknumber(L,4);
2715 Kd = luaL_checknumber(L,5);
2716 method = luaL_optstring(L,6,"velocity");
2717
2718 if (strcmp( method, "absolute" ) == 0)
2719 angle2 = angle;
2720 else if (strcmp( method, "keepangle" ) == 0) {
2721 vec2_cset( &pv, p->solid.pos.x - target->solid.pos.x,
2722 p->solid.pos.y - target->solid.pos.y );
2723 angle2 = VANGLE(pv);
2724 }
2725 else /* method == "velocity" */
2726 angle2 = angle + VANGLE( target->solid.vel );
2727
2728 vec2_cset( &point, VX(target->solid.pos) + radius * cos(angle2),
2729 VY(target->solid.pos) + radius * sin(angle2) );
2730
2731 /* Compute the direction using a pd controller */
2732 vec2_cset( &cons, (point.x - p->solid.pos.x) * Kp +
2733 (target->solid.vel.x - p->solid.vel.x) *Kd,
2734 (point.y - p->solid.pos.y) * Kp +
2735 (target->solid.vel.y - p->solid.vel.y) *Kd );
2736
2737 vec2_cset( &goal, cons.x + p->solid.pos.x, cons.y + p->solid.pos.y);
2738
2739 /* Push info */
2740 lua_pushvector( L, goal );
2741
2742 return 1;
2743
2744}
2745
2758static int aiL_face_accurate( lua_State *L )
2759{
2760 vec2 point, cons, goal, *pos, *vel;
2761 double radius, angle, Kp, Kd;
2762 const Pilot *p = cur_pilot;
2763
2764 pos = lua_tovector(L,1);
2765 vel = lua_tovector(L,2);
2766 radius = luaL_checknumber(L,3);
2767 angle = luaL_checknumber(L,4);
2768 Kp = luaL_checknumber(L,5);
2769 Kd = luaL_checknumber(L,6);
2770
2771 vec2_cset( &point, pos->x + radius * cos(angle),
2772 pos->y + radius * sin(angle) );
2773
2774 /* Compute the direction using a pd controller */
2775 vec2_cset( &cons, (point.x - p->solid.pos.x) * Kp +
2776 (vel->x - p->solid.vel.x) *Kd,
2777 (point.y - p->solid.pos.y) * Kp +
2778 (vel->y - p->solid.vel.y) *Kd );
2779
2780 vec2_cset( &goal, cons.x + p->solid.pos.x, cons.y + p->solid.pos.y);
2781
2782 /* Push info */
2783 lua_pushvector( L, goal );
2784
2785 return 1;
2786
2787}
2788
2794static int aiL_stop( lua_State *L )
2795{
2796 (void) L; /* avoid gcc warning */
2797
2798 if (VMOD(cur_pilot->solid.vel) < MIN_VEL_ERR)
2799 vec2_pset( &cur_pilot->solid.vel, 0., 0. );
2800
2801 return 0;
2802}
2803
2810static int aiL_dock( lua_State *L )
2811{
2812 /* Target is another ship. */
2813 Pilot *p = luaL_validpilot(L,1);
2814 pilot_dock(cur_pilot, p);
2815 return 0;
2816}
2817
2824static int aiL_combat( lua_State *L )
2825{
2826 if (lua_gettop(L) > 0) {
2827 int i = lua_toboolean(L,1);
2828 if (i==1)
2829 pilot_setFlag(cur_pilot, PILOT_COMBAT);
2830 else if (i==0)
2831 pilot_rmFlag(cur_pilot, PILOT_COMBAT);
2832 }
2833 else
2834 pilot_setFlag(cur_pilot, PILOT_COMBAT);
2835
2836 return 0;
2837}
2838
2845static int aiL_settarget( lua_State *L )
2846{
2847 const Pilot *p = luaL_validpilot(L,1);
2848 pilot_setTarget( cur_pilot, p->id );
2849 return 0;
2850}
2851
2859static int aiL_setasterotarget( lua_State *L )
2860{
2861 const LuaAsteroid_t *la = luaL_checkasteroid(L,1);
2862
2863 /* Set the target asteroid. */
2864 cur_pilot->nav_anchor = la->parent;
2865 cur_pilot->nav_asteroid = la->id;
2866
2867 /* Untarget pilot. */
2869 cur_pilot->ptarget = NULL;
2870
2871 return 0;
2872}
2873
2881static int aiL_getGatherable( lua_State *L )
2882{
2883 int i;
2884 double rad;
2885
2886 if ((lua_gettop(L) < 1) || lua_isnil(L,1))
2887 rad = INFINITY;
2888 else
2889 rad = lua_tonumber(L,1);
2890
2892
2893 if (i != -1)
2894 lua_pushnumber(L,i);
2895 else
2896 lua_pushnil(L);
2897
2898 return 1;
2899}
2900
2909static int aiL_gatherablePos( lua_State *L )
2910{
2911 int i, did;
2912 vec2 pos, vel;
2913
2914 i = lua_tointeger(L,1);
2915
2916 did = gatherable_getPos( &pos, &vel, i );
2917
2918 if (did == 0) /* No gatherable matching this ID. */
2919 return 0;
2920
2921 lua_pushvector(L, pos);
2922 lua_pushvector(L, vel);
2923
2924 return 2;
2925}
2926
2934static int aiL_weapSet( lua_State *L )
2935{
2936 int id, type;
2937 id = luaL_checkinteger(L,1);
2938
2939 if (lua_gettop(L) > 1)
2940 type = lua_toboolean(L,2);
2941 else
2942 type = 1;
2943
2944 /* weapset type is weapon or change */
2945 if (type)
2946 pilot_weapSetPress( cur_pilot, id, +1 );
2947 else
2948 pilot_weapSetPress( cur_pilot, id, -1 );
2949 return 0;
2950}
2951
2958static int aiL_hascannons( lua_State *L )
2959{
2960 lua_pushboolean( L, cur_pilot->ncannons > 0 );
2961 return 1;
2962}
2963
2970static int aiL_hasturrets( lua_State *L )
2971{
2972 lua_pushboolean( L, cur_pilot->nturrets > 0 );
2973 return 1;
2974}
2975
2982static int aiL_hasfighterbays( lua_State *L )
2983{
2984 lua_pushboolean( L, cur_pilot->nfighterbays > 0 );
2985 return 1;
2986}
2987
2994static int aiL_hasafterburner( lua_State *L )
2995{
2996 lua_pushboolean( L, cur_pilot->nafterburners > 0 );
2997 return 1;
2998}
2999
3006static int aiL_shoot( lua_State *L )
3007{
3008 /* Cooldown is similar to a ship being disabled, but the AI continues to
3009 * think during cooldown, and thus must not be allowed to fire weapons. */
3010 if (pilot_isFlag(cur_pilot, PILOT_COOLDOWN))
3011 return 0;
3012
3013 if (lua_toboolean(L,1))
3015 else
3017 return 0;
3018}
3019
3027static int aiL_getenemy( lua_State *L )
3028{
3029 if (lua_isnoneornil(L,1)) {
3030 unsigned int id = pilot_getNearestEnemy(cur_pilot);
3031 if (id==0) /* No enemy found */
3032 return 0;
3033 lua_pushpilot(L, id);
3034 return 1;
3035 }
3036 else {
3037 double range = luaL_checknumber(L,1);
3038 double r2 = pow2(range);
3039 unsigned int tp = 0;
3040 double d = 0.;
3041 int x, y, r;
3042 Pilot *const* pilot_stack = pilot_getAll();
3043
3044 r = ceil(range);
3045 x = round(cur_pilot->solid.pos.x);
3046 y = round(cur_pilot->solid.pos.y);
3047 pilot_collideQueryIL( &ai_qtquery, x-r, y-r, x+r, y+r );
3048 for (int i=0; i<il_size(&ai_qtquery); i++ ) {
3049 const Pilot *p = pilot_stack[ il_get( &ai_qtquery, i, 0 ) ];
3050 double td;
3051
3052 if (vec2_dist2(&p->solid.pos, &cur_pilot->solid.pos) > r2)
3053 continue;
3054
3055 if (!pilot_validEnemy( cur_pilot, p ))
3056 continue;
3057
3058 /* Check distance. */
3059 td = vec2_dist2(&p->solid.pos, &cur_pilot->solid.pos);
3060 if (!tp || (td < d)) {
3061 d = td;
3062 tp = p->id;
3063 }
3064 }
3065 return tp;
3066 }
3067}
3068
3075static int aiL_hostile( lua_State *L )
3076{
3077 const Pilot *p = luaL_validpilot(L,1);
3078 if (pilot_isWithPlayer(p))
3080 return 0;
3081}
3082
3091static int aiL_getweaprange( lua_State *L )
3092{
3093 int id = luaL_optinteger( L, 1, cur_pilot->active_set );
3094 int level = luaL_optinteger( L, 2, -1 );
3095 lua_pushnumber(L, pilot_weapSetRange( cur_pilot, id, level ) );
3096 return 1;
3097}
3098
3107static int aiL_getweapspeed( lua_State *L )
3108{
3109 int id = luaL_optinteger( L, 1, cur_pilot->active_set );
3110 int level = luaL_optinteger( L, 2, -1 );
3111 lua_pushnumber(L, pilot_weapSetSpeed( cur_pilot, id, level ) );
3112 return 1;
3113}
3114
3123static int aiL_getweapammo( lua_State *L )
3124{
3125 int id = luaL_optinteger( L, 1, cur_pilot->active_set );
3126 int level = luaL_optinteger( L, 2, -1 );
3127 lua_pushnumber(L, pilot_weapSetAmmo( cur_pilot, id, level ) );
3128 return 1;
3129}
3130
3138static int aiL_canboard( lua_State *L )
3139{
3140 const Pilot *p = luaL_validpilot(L,1);
3141
3142 /* Must be disabled. */
3143 if (!pilot_isDisabled(p)) {
3144 lua_pushboolean(L, 0);
3145 return 1;
3146 }
3147
3148 /* Check if can be boarded. */
3149 lua_pushboolean(L, !pilot_isFlag(p, PILOT_BOARDED));
3150 return 1;
3151}
3152
3160static int aiL_relsize( lua_State *L )
3161{
3162 const Pilot *p = luaL_validpilot(L,1);
3163 lua_pushnumber(L, pilot_relsize(cur_pilot, p));
3164 return 1;
3165}
3166
3174static int aiL_reldps( lua_State *L )
3175{
3176 const Pilot *p = luaL_validpilot(L,1);
3177 lua_pushnumber(L, pilot_reldps(cur_pilot, p));
3178 return 1;
3179}
3180
3188static int aiL_relhp( lua_State *L )
3189{
3190 const Pilot *p = luaL_validpilot(L,1);
3191 lua_pushnumber(L, pilot_relhp(cur_pilot, p));
3192 return 1;
3193}
3194
3201static int aiL_board( lua_State *L )
3202{
3203 lua_pushboolean(L, pilot_board( cur_pilot ));
3204 return 1;
3205}
3206
3213static int aiL_refuel( lua_State *L )
3214{
3215 lua_pushboolean(L,pilot_refuelStart(cur_pilot));
3216 return 1;
3217}
3218
3226static int aiL_settimer( lua_State *L )
3227{
3228 int n = luaL_checkint(L,1);
3229 /* Set timer. */
3230 cur_pilot->timer[n] = luaL_optnumber(L,2,0.);
3231 return 0;
3232}
3233
3242static int aiL_timeup( lua_State *L )
3243{
3244 int n = luaL_checkint(L,1);
3245 lua_pushboolean(L, cur_pilot->timer[n] < 0.);
3246 return 1;
3247}
3248
3255static int aiL_set_shoot_indicator( lua_State *L )
3256{
3257 cur_pilot->shoot_indicator = lua_toboolean(L,1);
3258 return 0;
3259}
3260
3267static int aiL_shoot_indicator( lua_State *L )
3268{
3269 lua_pushboolean(L, cur_pilot->shoot_indicator);
3270 return 1;
3271}
3272
3279static int aiL_distress( lua_State *L )
3280{
3281 if (lua_isstring(L,1))
3282 snprintf( aiL_distressmsg, sizeof(aiL_distressmsg), "%s", lua_tostring(L,1) );
3283 else if (lua_isnoneornil(L,1))
3284 aiL_distressmsg[0] = '\0';
3285 else
3286 NLUA_INVALID_PARAMETER(L,1);
3287
3288 /* Set flag because code isn't reentrant. */
3290
3291 return 0;
3292}
3293
3300static int aiL_getBoss( lua_State *L )
3301{
3302 unsigned int id = pilot_getBoss( cur_pilot );
3303
3304 if (id==0) /* No boss found */
3305 return 0;
3306
3307 lua_pushpilot(L, id);
3308
3309 return 1;
3310}
3311
3318static int aiL_credits( lua_State *L )
3319{
3321 /*NLUA_ERROR(L, "This function must be called in \"create\" only.");*/
3322 return 0;
3323 }
3324
3325 cur_pilot->credits = luaL_checklong(L,1);
3326
3327 return 0;
3328}
3329
3336static int aiL_messages( lua_State *L )
3337{
3338 lua_rawgeti(L, LUA_REGISTRYINDEX, cur_pilot->messages);
3339 lua_newtable(L);
3340 lua_rawseti(L, LUA_REGISTRYINDEX, cur_pilot->messages);
3341 return 1;
3342}
3343
3351static int aiL_stealth( lua_State *L )
3352{
3353 int b = 1;
3354 if (lua_gettop(L)>0)
3355 b = lua_toboolean(L,1);
3356
3357 if (!b) {
3359 lua_pushboolean(L,1); /* always succeeds */
3360 return 1;
3361 }
3362
3363 lua_pushboolean(L, pilot_stealth( cur_pilot ));
3364 return 1;
3365}
static int ai_setMemory(void)
Sets the cur_pilot's ai.
Definition ai.c:395
Task * ai_newtask(lua_State *L, Pilot *p, const char *func, int subtask, int pos)
Creates a new AI task.
Definition ai.c:1117
void ai_unsetPilot(AIMemory oldmem)
Finishes setting up a pilot.
Definition ai.c:426
static double pilot_turn
Definition ai.c:340
static nlua_env equip_env
Definition ai.c:115
void ai_thinkApply(Pilot *p)
Applies the result of thinking.
Definition ai.c:452
static AI_Profile * profiles
Definition ai.c:114
static int aiL_status
Definition ai.c:349
static IntList ai_qtquery
Definition ai.c:116
static double ai_dt
Definition ai.c:117
Task * ai_curTask(Pilot *pilot)
Gets the current running task.
Definition ai.c:383
void ai_refuel(Pilot *refueler, unsigned int target)
Has a pilot attempt to refuel the other.
Definition ai.c:1018
static const luaL_Reg aiL_methods[]
Definition ai.c:240
static int ai_loadProfile(AI_Profile *prof, const char *filename)
Initializes an AI_Profile and adds it to the stack.
Definition ai.c:686
static void ai_create(Pilot *pilot)
Runs the create() function in the pilot.
Definition ai.c:1074
void ai_cleartasks(Pilot *p)
Clears the pilot's tasks.
Definition ai.c:550
static int ai_tasktarget(lua_State *L, const Task *t)
Pushes a task target.
Definition ai.c:1229
void ai_freetask(Task *t)
Frees an AI task.
Definition ai.c:1175
void ai_think(Pilot *pilot, double dt, int dotask)
Heart of the AI, brains of the pilot.
Definition ai.c:797
void ai_thinkSetup(double dt)
Sets up the pilot for thinking.
Definition ai.c:438
void ai_getDistress(const Pilot *p, const Pilot *distressed, const Pilot *attacker)
Sends a distress signal to a pilot.
Definition ai.c:1049
static double pilot_acc
Definition ai.c:339
AIMemory ai_setPilot(Pilot *p)
Sets the pilot for further AI calls.
Definition ai.c:414
static Task * ai_createTask(lua_State *L, int subtask)
Creates a new task based on stack information.
Definition ai.c:1202
#define AI_PRIMARY
Definition ai.c:107
#define AI_SECONDARY
Definition ai.c:108
static void ai_run(nlua_env env, int nargs)
Attempts to run a function.
Definition ai.c:479
static char aiL_distressmsg[STRMAX_SHORT]
Definition ai.c:342
void ai_attacked(Pilot *attacked, const unsigned int attacker, double dmg)
Triggers the attacked() function in the pilot's AI.
Definition ai.c:911
#define ai_isFlag(f)
Definition ai.c:105
void ai_destroy(Pilot *p)
Destroys the ai part of the pilot.
Definition ai.c:563
Pilot * cur_pilot
Definition ai.c:338
void ai_hail(Pilot *recipient)
Triggers the hail() function in the pilot's AI.
Definition ai.c:983
#define ai_setFlag(f)
Definition ai.c:104
void ai_discovered(Pilot *discovered)
Triggers the discovered() function in the pilot's AI.
Definition ai.c:948
void ai_exit(void)
Cleans up global AI.
Definition ai.c:773
static int ai_loadEquip(void)
Loads the equipment selector script.
Definition ai.c:646
void ai_init(Pilot *p)
Initializes the AI.
Definition ai.c:893
#define AI_DISTRESS
Definition ai.c:109
#define AI_STATUS_NORMAL
Definition ai.c:347
static void ai_taskGC(Pilot *pilot)
Runs the garbage collector on the pilot's tasks.
Definition ai.c:356
int ai_pinit(Pilot *p, const char *ai)
Initializes the pilot in the ai.
Definition ai.c:496
#define AI_STATUS_CREATE
Definition ai.c:348
static int pilot_flags
Definition ai.c:341
int ai_load(void)
Initializes the AI stuff which is basically Lua.
Definition ai.c:590
AI_Profile * ai_getProfile(const char *name)
Gets the AI_Profile by name.
Definition ai.c:761
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition array.h:102
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
int pilot_board(Pilot *p)
Has a pilot attempt to board another pilot.
Definition board.c:219
nlua_env faction_getEquipper(int f)
Gets the equipper state associated to the faction scheduler.
Definition faction.c:740
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1227
int faction_usesHiddenJumps(int f)
Checks to see if a faction uses hidden jumps.
Definition faction.c:1873
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition faction.c:1253
int gatherable_getPos(vec2 *pos, vec2 *vel, int id)
Returns the position and velocity of a gatherable.
Definition gatherable.c:189
int gatherable_getClosest(const vec2 *pos, double rad)
Gets the closest gatherable from a given position, within a given radius.
Definition gatherable.c:165
static int aiL_ismaxvel(lua_State *L)
Checks to see if pilot is at maximum velocity.
Definition ai.c:1621
static int aiL_follow_accurate(lua_State *L)
Computes the point to face in order to follow another pilot using a PD controller.
Definition ai.c:2703
static int aiL_face_accurate(lua_State *L)
Computes the point to face in order to follow a moving object.
Definition ai.c:2758
static int aiL_relhp(lua_State *L)
Gets the relative health (total shields and armour) between the current pilot and the specified targe...
Definition ai.c:3188
static int aiL_sethyptarget(lua_State *L)
Sets hyperspace target.
Definition ai.c:2481
static int aiL_getspobfrompos(lua_State *L)
Get the nearest friendly spob to a given position.
Definition ai.c:2238
static int aiL_getlandspob(lua_State *L)
Get a random friendly spob.
Definition ai.c:2302
static int aiL_accel(lua_State *L)
Starts accelerating the pilot.
Definition ai.c:1731
static int aiL_weapSet(lua_State *L)
Sets the active weapon set, fires another weapon set or activate an outfit.
Definition ai.c:2934
static int aiL_poptask(lua_State *L)
Pops the current running task.
Definition ai.c:1266
static int aiL_iface(lua_State *L)
Maintains an intercept pursuit course.
Definition ai.c:1982
static int aiL_settarget(lua_State *L)
Sets the pilot's target.
Definition ai.c:2845
static int aiL_nearhyptarget(lua_State *L)
Gets the nearest hyperspace target.
Definition ai.c:2517
static int aiL_getrndspob(lua_State *L)
Get a random spob.
Definition ai.c:2276
static int aiL_pilot(lua_State *L)
Gets the AI's pilot. Lua return parameter: Pilot The AI's pilot.
Definition ai.c:1396
static int aiL_minbrakedist(lua_State *L)
Gets the minimum braking distance.
Definition ai.c:1560
static int aiL_gatherablePos(lua_State *L)
Gets the pos and vel of a given gatherable.
Definition ai.c:2909
static int aiL_relvel(lua_State *L)
Gets the relative velocity of a pilot.
Definition ai.c:2662
static int aiL_haslockon(lua_State *L)
Checks to see if pilot has a missile lockon.
Definition ai.c:1692
static int aiL_refuel(lua_State *L)
Attempts to refuel the pilot's target.
Definition ai.c:3213
static int aiL_getdistance2(lua_State *L)
Gets the squared distance from the pointer.
Definition ai.c:1493
static int aiL_getdistance(lua_State *L)
Gets the distance from the pointer.
Definition ai.c:1466
static int aiL_instantJump(lua_State *L)
Checks to see if pilot can instant jump.
Definition ai.c:1609
static int aiL_land(lua_State *L)
Lands on a spob.
Definition ai.c:2363
static int aiL_isenemy(lua_State *L)
Checks to see if target is an enemy.
Definition ai.c:1648
static int aiL_messages(lua_State *L)
Returns and clears the pilots message queue.
Definition ai.c:3336
static int aiL_turn(lua_State *L)
Starts turning the pilot.
Definition ai.c:1744
static int aiL_subtaskname(lua_State *L)
Gets the current subtask's name. Lua return parameter: string The current subtask name or nil if ther...
Definition ai.c:1363
static int aiL_rndhyptarget(lua_State *L)
Gets a random hyperspace target.
Definition ai.c:2567
static int aiL_dir(lua_State *L)
calculates the direction that the target is relative to the current pilot facing.
Definition ai.c:2052
static int aiL_careful_face(lua_State *L)
Gives the direction to follow in order to reach the target while minimizating risk.
Definition ai.c:1855
static int aiL_taskdata(lua_State *L)
Gets the pilot's task data. Lua return parameter: The pilot's task data or nil if there is no task da...
Definition ai.c:1309
static int aiL_hasturrets(lua_State *L)
Does the pilot have turrets?
Definition ai.c:2970
static int aiL_isally(lua_State *L)
Checks to see if target is an ally.
Definition ai.c:1670
static int aiL_hyperspaceAbort(lua_State *L)
Has the AI abandon hyperspace if applicable.
Definition ai.c:2649
static int aiL_relsize(lua_State *L)
Gets the relative size (ship mass) between the current pilot and the specified target.
Definition ai.c:3160
static int aiL_aim(lua_State *L)
Aims at a pilot, trying to hit it rather than move to it.
Definition ai.c:1953
static int aiL_credits(lua_State *L)
Sets the pilots credits. Only call in create().
Definition ai.c:3318
static int aiL_reldps(lua_State *L)
Gets the relative damage output (total DPS) between the current pilot and the specified target.
Definition ai.c:3174
static int aiL_canboard(lua_State *L)
Checks to see if pilot can board the target.
Definition ai.c:3138
static int aiL_idir(lua_State *L)
Calculates angle between pilot facing and intercept-course to target.
Definition ai.c:2085
static int aiL_getnearestpilot(lua_State *L)
gets the nearest pilot to the current pilot
Definition ai.c:1432
static int aiL_hostile(lua_State *L)
Sets the enemy hostile (basically notifies of an impending attack).
Definition ai.c:3075
static int aiL_hascannons(lua_State *L)
Does the pilot have cannons?
Definition ai.c:2958
static int aiL_getBoss(lua_State *L)
Picks a pilot that will command the current pilot.
Definition ai.c:3300
static int aiL_isbribed(lua_State *L)
Checks to see if target has bribed pilot.
Definition ai.c:1596
static int aiL_getGatherable(lua_State *L)
Gets the closest gatherable within a radius.
Definition ai.c:2881
static int aiL_taskname(lua_State *L)
Gets the current task's name. Lua return parameter: string The current task name or nil if there are ...
Definition ai.c:1292
static int aiL_subtaskdata(lua_State *L)
Gets the pilot's subtask target. Lua return parameter: The pilot's target ship identifier or nil if n...
Definition ai.c:1380
static int aiL_hasprojectile(lua_State *L)
Checks to see if pilot has a projectile after him.
Definition ai.c:1705
static int aiL_getweapammo(lua_State *L)
Gets the ammo of a weapon.
Definition ai.c:3123
static int aiL_drift_facing(lua_State *L)
Calculate the offset between the pilot's current direction of travel and the pilot's current facing.
Definition ai.c:2153
static int aiL_shoot_indicator(lua_State *L)
Access the seeker shoot indicator (that is put to true each time a seeker is shot).
Definition ai.c:3267
static int aiL_combat(lua_State *L)
Sets the combat flag.
Definition ai.c:2824
static int aiL_distress(lua_State *L)
Sends a distress signal.
Definition ai.c:3279
static int aiL_settimer(lua_State *L)
Sets a timer.
Definition ai.c:3226
static int aiL_shoot(lua_State *L)
Makes the pilot shoot.
Definition ai.c:3006
static int aiL_setasterotarget(lua_State *L)
Sets the pilot's asteroid target.
Definition ai.c:2859
static int aiL_getweapspeed(lua_State *L)
Gets the speed of a weapon.
Definition ai.c:3107
static int aiL_getrndpilot(lua_State *L)
Gets a random pilot in the system. Lua return parameter: Pilot|nil.
Definition ai.c:1408
static int aiL_set_shoot_indicator(lua_State *L)
Set the seeker shoot indicator.
Definition ai.c:3255
static int aiL_getenemy(lua_State *L)
Gets the nearest enemy.
Definition ai.c:3027
static int aiL_stealth(lua_State *L)
Tries to stealth or destealth the pilot.
Definition ai.c:3351
static int aiL_pushsubtask(lua_State *L)
Pushes a subtask onto the pilot's task's subtask list. Lua function parameter: string func Name of fu...
Definition ai.c:1327
static int aiL_stop(lua_State *L)
Completely stops the pilot if it is below minimum vel error (no insta-stops).
Definition ai.c:2794
static int aiL_getflybydistance(lua_State *L)
Gets the distance from the pointer perpendicular to the current pilot's flight vector.
Definition ai.c:1520
static int aiL_canHyperspace(lua_State *L)
Gets whether or not the pilot can hyperspace.
Definition ai.c:2638
static int aiL_popsubtask(lua_State *L)
Pops the current running task.
Definition ai.c:1338
static int aiL_face(lua_State *L)
Faces the target.
Definition ai.c:1763
static int aiL_timeup(lua_State *L)
Checks a timer.
Definition ai.c:3242
static int aiL_pushtask(lua_State *L)
Pushes a task onto the pilot's task list. Lua function parameter: string func Name of function to cal...
Definition ai.c:1256
static int aiL_scandone(lua_State *L)
Checks to see if pilot has finished scanning their target.
Definition ai.c:1718
static int aiL_getweaprange(lua_State *L)
Gets the range of a weapon.
Definition ai.c:3091
static int aiL_dock(lua_State *L)
Docks the ship.
Definition ai.c:2810
static int aiL_hyperspace(lua_State *L)
Tries to enter hyperspace.
Definition ai.c:2450
static int aiL_hasafterburner(lua_State *L)
Does the pilot have afterburners?
Definition ai.c:2994
static int aiL_board(lua_State *L)
Attempts to board the pilot's target.
Definition ai.c:3201
static int aiL_hasfighterbays(lua_State *L)
Does the pilot have fighter bays?
Definition ai.c:2982
static int aiL_brake(lua_State *L)
Brakes the pilot.
Definition ai.c:2166
static int aiL_getnearestspob(lua_State *L)
Get the nearest friendly spob to the pilot.
Definition ai.c:2201
static int aiL_isstopped(lua_State *L)
Checks to see if pilot is stopped.
Definition ai.c:1635
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition naev.c:597
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
#define CLAMP(a, b, x)
Definition naev.h:41
#define ABS(x)
Definition naev.h:36
#define pow2(x)
Definition naev.h:46
#define FABS(x)
Definition naev.h:37
#define MAX(x, y)
Definition naev.h:39
#define PATH_MAX
Definition naev.h:50
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:154
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:365
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:798
int nlua_refenvtype(nlua_env env, const char *name, int type)
Gets the reference of a global in a lua environment if it matches a type.
Definition nlua.c:920
int lua_isasteroid(lua_State *L, int ind)
Checks to see if ind is a asteroid.
LuaAsteroid_t * luaL_checkasteroid(lua_State *L, int ind)
Gets asteroid at index or raises error if there is no asteroid at index.
Asteroid * luaL_validasteroid(lua_State *L, int ind)
Gets asteroid at index raising an error if type doesn't match.
LuaJump * luaL_checkjump(lua_State *L, int ind)
Gets jump at index raising an error if isn't a jump.
Definition nlua_jump.c:103
LuaJump * lua_pushjump(lua_State *L, LuaJump jump)
Pushes a jump on the stack.
Definition nlua_jump.c:185
JumpPoint * luaL_validjump(lua_State *L, int ind)
Gets a jump directly.
Definition nlua_jump.c:173
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:563
Pilot * luaL_validpilot(lua_State *L, int ind)
Makes sure the pilot is valid or raises a Lua error.
Definition nlua_pilot.c:547
int lua_ispilot(lua_State *L, int ind)
Checks to see if ind is a pilot.
Definition nlua_pilot.c:578
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:201
Spob * luaL_validspob(lua_State *L, int ind)
Gets a spob directly.
Definition nlua_spob.c:174
int lua_isvector(lua_State *L, int ind)
Checks to see if ind is a vector.
Definition nlua_vec2.c:161
vec2 * luaL_checkvector(lua_State *L, int ind)
Gets vector at index making sure type is valid.
Definition nlua_vec2.c:130
vec2 * lua_tovector(lua_State *L, int ind)
Represents a 2D vector in Lua.
Definition nlua_vec2.c:119
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition nlua_vec2.c:145
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition pilot.c:678
int pilot_brakeCheckReverseThrusters(const Pilot *p)
See if the pilot wants to use their reverse thrusters to brake.
Definition pilot.c:821
double pilot_relhp(const Pilot *cur_pilot, const Pilot *p)
Gets the relative hp(combined shields and armour) between the current pilot and the specified target.
Definition pilot.c:4082
int pilot_validEnemy(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid enemy for another pilot.
Definition pilot.c:277
double pilot_relsize(const Pilot *cur_pilot, const Pilot *p)
Gets the relative size(shipmass) between the current pilot and the specified target.
Definition pilot.c:3982
double pilot_reldps(const Pilot *cur_pilot, const Pilot *p)
Gets the relative damage output(total DPS) between the current pilot and the specified target.
Definition pilot.c:4061
void pilot_msg(const Pilot *p, const Pilot *receiver, const char *type, unsigned int idx)
Sends a message.
Definition pilot.c:4120
void pilot_setAccel(Pilot *p, double accel)
Sets the pilot's accel.
Definition pilot.c:659
unsigned int pilot_getNearestEnemy(const Pilot *p)
Gets the nearest enemy to the pilot.
Definition pilot.c:323
void pilot_hyperspaceAbort(Pilot *p)
Stops the pilot from hyperspacing.
Definition pilot.c:2857
void pilot_setTurn(Pilot *p, double turn)
Sets the pilot's turn.
Definition pilot.c:667
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition pilot.c:708
int pilot_refuelStart(Pilot *p)
Attempts to start refueling the pilot's target.
Definition pilot.c:2887
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition pilot.c:1066
static Pilot ** pilot_stack
Definition pilot.c:61
void pilot_distress(Pilot *p, Pilot *attacker, const char *msg)
Has the pilot broadcast a distress signal.
Definition pilot.c:1139
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:94
int pilot_canTarget(const Pilot *p)
Same as pilot_validTarget but without the range check.
Definition pilot.c:254
double pilot_aimAngle(Pilot *p, const vec2 *pos, const vec2 *vel)
Returns the angle for a pilot to aim at another pilot.
Definition pilot.c:999
double pilot_minbrakedist(const Pilot *p, double dt)
Gets the minimum braking distance for the pilot.
Definition pilot.c:845
unsigned int pilot_getBoss(const Pilot *p)
Get the strongest ally in a given range.
Definition pilot.c:436
void pilot_setTarget(Pilot *p, unsigned int id)
Sets the target of the pilot.
Definition pilot.c:1308
int pilot_inRangePilot(const Pilot *p, const Pilot *target, double *dist2)
Check to see if a pilot is in sensor range of another.
Definition pilot_ew.c:244
int pilot_ewScanCheck(const Pilot *p)
Checks to see if a scan is done.
Definition pilot_ew.c:71
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition pilot_ew.c:552
int pilot_stealth(Pilot *p)
Stealths a pilot.
Definition pilot_ew.c:513
int pilot_runHook(Pilot *p, int hook_type)
Tries to run a pilot hook if he has it.
Definition pilot_hook.c:106
void pilot_healLanded(Pilot *pilot)
Cures the pilot as if he was landed.
int pilot_shoot(Pilot *p, int level)
Makes the pilot shoot.
void pilot_shootStop(Pilot *p, int level)
Have pilot stop shooting their weapon.
void pilot_weapSetAIClear(Pilot *p)
Useful function for AI, clears activeness of all weapon sets.
double pilot_weapSetAmmo(Pilot *p, int id, int level)
Gets the ammo of the current pilot weapon set.
void pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
double pilot_weapSetSpeed(Pilot *p, int id, int level)
Gets the speed of the current pilot weapon set.
double pilot_weapSetRange(Pilot *p, int id, int level)
Gets the range of the current pilot weapon set.
static const double d[]
Definition rng.c:273
int space_canHyperspace(const Pilot *p)
Checks to make sure if pilot is far enough away to hyperspace.
Definition space.c:469
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition space.c:1099
StarSystem * cur_system
Definition space.c:106
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition space.c:4181
int space_hyperspace(Pilot *p)
Tries to get the pilot into hyperspace.
Definition space.c:503
Represents a temporary pilot memory. For use with ai_setPilot and ai_unsetPilot.
Definition ai.h:58
Pilot * p
Definition ai.h:60
int mem
Definition ai.h:59
Basic AI profile.
Definition ai.h:40
int lua_mem
Definition ai.h:44
int ref_refuel
Definition ai.h:47
nlua_env env
Definition ai.h:42
int ref_control
Definition ai.h:45
char * name
Definition ai.h:41
double control_rate
Definition ai.h:43
int ref_create
Definition ai.h:48
int ref_control_manual
Definition ai.h:46
Represents a single asteroid.
Definition asteroid.h:77
The actual hook parameter.
Definition hook.h:38
LuaPilot lp
Definition hook.h:44
HookParamType type
Definition hook.h:39
union HookParam::@25 u
LuaSpob la
Definition hook.h:49
double num
Definition hook.h:41
Lua jump Wrapper.
Definition nlua_jump.h:14
int destid
Definition nlua_jump.h:16
int srcid
Definition nlua_jump.h:15
The representation of an in-game pilot.
Definition pilot.h:217
int nturrets
Definition pilot.h:308
ShipStats stats
Definition pilot.h:294
double accel
Definition pilot.h:242
AI_Profile * ai
Definition pilot.h:350
unsigned int id
Definition pilot.h:218
credits_t credits
Definition pilot.h:325
int nav_hyperspace
Definition pilot.h:345
int projectiles
Definition pilot.h:379
int messages
Definition pilot.h:384
double speed
Definition pilot.h:244
double timer[MAX_AI_TIMERS]
Definition pilot.h:353
const Ship * ship
Definition pilot.h:226
int lockons
Definition pilot.h:378
int ncannons
Definition pilot.h:307
int nav_anchor
Definition pilot.h:346
double ptimer
Definition pilot.h:368
int faction
Definition pilot.h:222
double landing_delay
Definition pilot.h:366
unsigned int shoot_indicator
Definition pilot.h:355
Solid solid
Definition pilot.h:227
char * name
Definition pilot.h:219
void * ptarget
Definition pilot.h:343
double tcontrol
Definition pilot.h:352
Task * task
Definition pilot.h:354
int nfighterbays
Definition pilot.h:310
int active_set
Definition pilot.h:320
unsigned int target
Definition pilot.h:342
int nafterburners
Definition pilot.h:311
int nav_spob
Definition pilot.h:344
int lua_mem
Definition pilot.h:351
double turn
Definition pilot.h:247
int nav_asteroid
Definition pilot.h:347
int devmode
Definition conf.h:157
int misc_instant_jump
Definition shipstats.h:298
double dt_default
Definition ship.h:124
vec2 vel
Definition physics.h:48
double dir
Definition physics.h:46
vec2 pos
Definition physics.h:49
int faction
Definition space.h:67
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:89
int lua_land
Definition space.h:144
double radius
Definition space.h:95
nlua_env lua_env
Definition space.h:138
char * name
Definition space.h:91
vec2 pos
Definition space.h:94
int id
Definition space.h:90
int lua_can_land
Definition space.h:143
SpobPresence presence
Definition space.h:104
Basic AI task.
Definition ai.h:24
int func
Definition ai.h:27
char * name
Definition ai.h:26
struct Task_ * subtask
Definition ai.h:30
struct Task_ * next
Definition ai.h:25
int done
Definition ai.h:28
int dat
Definition ai.h:32
Definition msgcat.c:199
Represents a 2d vector.
Definition vec2.h:32
double y
Definition vec2.h:34
double x
Definition vec2.h:33