naev 0.10.4
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;
117/*
118 * prototypes
119 */
120/* Internal C routines */
121static void ai_run( nlua_env env, int nargs );
122static int ai_loadProfile( AI_Profile *prof, const char* filename );
123static void ai_setMemory (void);
124static void ai_create( Pilot* pilot );
125static int ai_loadEquip (void);
126static int ai_sort( const void *p1, const void *p2 );
127/* Task management. */
128static void ai_taskGC( Pilot* pilot );
129static Task* ai_createTask( lua_State *L, int subtask );
130static int ai_tasktarget( lua_State *L, Task *t );
131
132/*
133 * AI routines for Lua
134 */
135/* tasks */
136static int aiL_pushtask( lua_State *L ); /* pushtask( string, number/pointer ) */
137static int aiL_poptask( lua_State *L ); /* poptask() */
138static int aiL_taskname( lua_State *L ); /* string taskname() */
139static int aiL_taskdata( lua_State *L ); /* pointer subtaskdata() */
140static int aiL_pushsubtask( lua_State *L ); /* pushsubtask( string, number/pointer, number ) */
141static int aiL_popsubtask( lua_State *L ); /* popsubtask() */
142static int aiL_subtaskname( lua_State *L ); /* string subtaskname() */
143static int aiL_subtaskdata( lua_State *L ); /* pointer subtaskdata() */
144
145/* consult values */
146static int aiL_pilot( lua_State *L ); /* number pilot() */
147static int aiL_getrndpilot( lua_State *L ); /* number getrndpilot() */
148static int aiL_getnearestpilot( lua_State *L ); /* number getnearestpilot() */
149static int aiL_getdistance( lua_State *L ); /* number getdist(vec2) */
150static int aiL_getdistance2( lua_State *L ); /* number getdist(vec2) */
151static int aiL_getflybydistance( lua_State *L ); /* number getflybydist(vec2) */
152static int aiL_minbrakedist( lua_State *L ); /* number minbrakedist( [number] ) */
153static int aiL_isbribed( lua_State *L ); /* bool isbribed( number ) */
154static int aiL_getGatherable( lua_State *L ); /* integer getgatherable( radius ) */
155static int aiL_instantJump( lua_State *L ); /* bool instantJump() */
156
157/* boolean expressions */
158static int aiL_ismaxvel( lua_State *L ); /* boolean ismaxvel() */
159static int aiL_isstopped( lua_State *L ); /* boolean isstopped() */
160static int aiL_isenemy( lua_State *L ); /* boolean isenemy( number ) */
161static int aiL_isally( lua_State *L ); /* boolean isally( number ) */
162static int aiL_haslockon( lua_State *L ); /* boolean haslockon() */
163static int aiL_hasprojectile( lua_State *L ); /* boolean hasprojectile() */
164static int aiL_scandone( lua_State *L );
165
166/* movement */
167static int aiL_accel( lua_State *L ); /* accel(number); number <= 1. */
168static int aiL_turn( lua_State *L ); /* turn(number); abs(number) <= 1. */
169static int aiL_face( lua_State *L ); /* face( number/pointer, bool) */
170static int aiL_careful_face( lua_State *L ); /* face( number/pointer, bool) */
171static int aiL_aim( lua_State *L ); /* aim(number) */
172static int aiL_iface( lua_State *L ); /* iface(number/pointer) */
173static int aiL_dir( lua_State *L ); /* dir(number/pointer) */
174static int aiL_idir( lua_State *L ); /* idir(number/pointer) */
175static int aiL_drift_facing( lua_State *L ); /* drift_facing(number/pointer) */
176static int aiL_brake( lua_State *L ); /* brake() */
177static int aiL_getnearestspob( lua_State *L ); /* Vec2 getnearestspob() */
178static int aiL_getspobfrompos( lua_State *L ); /* Vec2 getspobfrompos() */
179static int aiL_getrndspob( lua_State *L ); /* Vec2 getrndspob() */
180static int aiL_getlandspob( lua_State *L ); /* Vec2 getlandspob() */
181static int aiL_land( lua_State *L ); /* bool land() */
182static int aiL_stop( lua_State *L ); /* stop() */
183static int aiL_relvel( lua_State *L ); /* relvel( number ) */
184static int aiL_follow_accurate( lua_State *L ); /* follow_accurate() */
185static int aiL_face_accurate( lua_State *L ); /* face_accurate() */
186
187/* Hyperspace. */
188static int aiL_sethyptarget( lua_State *L );
189static int aiL_nearhyptarget( lua_State *L ); /* pointer rndhyptarget() */
190static int aiL_rndhyptarget( lua_State *L ); /* pointer rndhyptarget() */
191static int aiL_hyperspace( lua_State *L ); /* [number] hyperspace() */
192
193/* escorts */
194static int aiL_dock( lua_State *L ); /* dock( number ) */
195
196/* combat */
197static int aiL_combat( lua_State *L ); /* combat( number ) */
198static int aiL_settarget( lua_State *L ); /* settarget( number ) */
199static int aiL_weapSet( lua_State *L ); /* weapset( number ) */
200static int aiL_shoot( lua_State *L ); /* shoot( number ); number = 1,2,3 */
201static int aiL_hascannons( lua_State *L ); /* bool hascannons() */
202static int aiL_hasturrets( lua_State *L ); /* bool hasturrets() */
203static int aiL_hasfighterbays( lua_State *L ); /* bool hasfighterbays() */
204static int aiL_hasafterburner( lua_State *L ); /* bool hasafterburner() */
205static int aiL_getenemy( lua_State *L ); /* number getenemy() */
206static int aiL_getenemy_size( lua_State *L ); /* number getenemy_size() */
207static int aiL_getenemy_heuristic( lua_State *L ); /* number getenemy_heuristic() */
208static int aiL_hostile( lua_State *L ); /* hostile( number ) */
209static int aiL_getweaprange( lua_State *L ); /* number getweaprange() */
210static int aiL_getweapspeed( lua_State *L ); /* number getweapspeed() */
211static int aiL_getweapammo( lua_State *L );
212static int aiL_canboard( lua_State *L ); /* boolean canboard( number ) */
213static int aiL_relsize( lua_State *L ); /* boolean relsize( number ) */
214static int aiL_reldps( lua_State *L ); /* boolean reldps( number ) */
215static int aiL_relhp( lua_State *L ); /* boolean relhp( number ) */
216
217/* timers */
218static int aiL_settimer( lua_State *L ); /* settimer( number, number ) */
219static int aiL_timeup( lua_State *L ); /* boolean timeup( number ) */
220
221/* messages */
222static int aiL_distress( lua_State *L ); /* distress( string [, bool] ) */
223static int aiL_getBoss( lua_State *L ); /* number getBoss() */
224
225/* loot */
226static int aiL_credits( lua_State *L ); /* credits( number ) */
227
228/* misc */
229static int aiL_board( lua_State *L ); /* boolean board() */
230static int aiL_refuel( lua_State *L ); /* boolean, boolean refuel() */
231static int aiL_messages( lua_State *L );
232static int aiL_setasterotarget( lua_State *L ); /* setasterotarget( Asteroid ) */
233static int aiL_gatherablePos( lua_State *L ); /* gatherablepos( number ) */
234static int aiL_shoot_indicator( lua_State *L ); /* get shoot indicator */
235static int aiL_set_shoot_indicator( lua_State *L ); /* set shoot indicator */
236static int aiL_stealth( lua_State *L );
237
238static const luaL_Reg aiL_methods[] = {
239 /* tasks */
240 { "pushtask", aiL_pushtask },
241 { "poptask", aiL_poptask },
242 { "taskname", aiL_taskname },
243 { "taskdata", aiL_taskdata },
244 { "pushsubtask", aiL_pushsubtask },
245 { "popsubtask", aiL_popsubtask },
246 { "subtaskname", aiL_subtaskname },
247 { "subtaskdata", aiL_subtaskdata },
248 /* is */
249 { "ismaxvel", aiL_ismaxvel },
250 { "isstopped", aiL_isstopped },
251 { "isenemy", aiL_isenemy },
252 { "isally", aiL_isally },
253 { "haslockon", aiL_haslockon },
254 { "hasprojectile", aiL_hasprojectile },
255 { "scandone", aiL_scandone },
256 /* get */
257 { "pilot", aiL_pilot },
258 { "rndpilot", aiL_getrndpilot },
259 { "nearestpilot", aiL_getnearestpilot },
260 { "dist", aiL_getdistance },
261 { "dist2", aiL_getdistance2 },
262 { "flyby_dist", aiL_getflybydistance },
263 { "minbrakedist", aiL_minbrakedist },
264 { "isbribed", aiL_isbribed },
265 { "getgatherable", aiL_getGatherable },
266 { "instantJump", aiL_instantJump },
267 /* movement */
268 { "nearestspob", aiL_getnearestspob },
269 { "spobfrompos", aiL_getspobfrompos },
270 { "rndspob", aiL_getrndspob },
271 { "landspob", aiL_getlandspob },
272 { "land", aiL_land },
273 { "accel", aiL_accel },
274 { "turn", aiL_turn },
275 { "face", aiL_face },
276 { "careful_face", aiL_careful_face },
277 { "iface", aiL_iface },
278 { "dir", aiL_dir },
279 { "idir", aiL_idir },
280 { "drift_facing", aiL_drift_facing },
281 { "brake", aiL_brake },
282 { "stop", aiL_stop },
283 { "relvel", aiL_relvel },
284 { "follow_accurate", aiL_follow_accurate },
285 { "face_accurate", aiL_face_accurate },
286 /* Hyperspace. */
287 { "sethyptarget", aiL_sethyptarget },
288 { "nearhyptarget", aiL_nearhyptarget },
289 { "rndhyptarget", aiL_rndhyptarget },
290 { "hyperspace", aiL_hyperspace },
291 { "dock", aiL_dock },
292 /* combat */
293 { "aim", aiL_aim },
294 { "combat", aiL_combat },
295 { "settarget", aiL_settarget },
296 { "weapset", aiL_weapSet },
297 { "hascannons", aiL_hascannons },
298 { "hasturrets", aiL_hasturrets },
299 { "hasfighterbays", aiL_hasfighterbays },
300 { "hasafterburner", aiL_hasafterburner },
301 { "shoot", aiL_shoot },
302 { "getenemy", aiL_getenemy },
303 { "getenemy_size", aiL_getenemy_size },
304 { "getenemy_heuristic", aiL_getenemy_heuristic },
305 { "hostile", aiL_hostile },
306 { "getweaprange", aiL_getweaprange },
307 { "getweapspeed", aiL_getweapspeed },
308 { "getweapammo", aiL_getweapammo },
309 { "canboard", aiL_canboard },
310 { "relsize", aiL_relsize },
311 { "reldps", aiL_reldps },
312 { "relhp", aiL_relhp },
313 /* timers */
314 { "settimer", aiL_settimer },
315 { "timeup", aiL_timeup },
316 /* messages */
317 { "distress", aiL_distress },
318 { "getBoss", aiL_getBoss },
319 /* loot */
320 { "setcredits", aiL_credits },
321 /* misc */
322 { "board", aiL_board },
323 { "refuel", aiL_refuel },
324 { "messages", aiL_messages },
325 { "setasterotarget", aiL_setasterotarget },
326 { "gatherablepos", aiL_gatherablePos },
327 { "shoot_indicator", aiL_shoot_indicator },
328 { "set_shoot_indicator", aiL_set_shoot_indicator },
329 { "stealth", aiL_stealth },
330 {0,0} /* end */
331};
333/*
334 * current pilot "thinking" and assorted variables
335 */
337static double pilot_acc = 0.;
338static double pilot_turn = 0.;
339static int pilot_flags = 0;
340static char aiL_distressmsg[STRMAX_SHORT];
342/*
343 * ai status, used so that create functions can't be used elsewhere
344 */
345#define AI_STATUS_NORMAL 1
346#define AI_STATUS_CREATE 2
354static void ai_taskGC( Pilot* pilot )
355{
356 Task *prev = NULL;
357 Task *t = pilot->task;
358 while (t != NULL) {
359 if (t->done) {
360 Task * pointer = t;
361 /* Unattach pointer. */
362 t = t->next;
363 if (prev == NULL)
364 pilot->task = t;
365 else
366 prev->next = t;
367 /* Free pointer. */
368 pointer->next = NULL;
369 ai_freetask( pointer );
370 }
371 else {
372 prev = t;
373 t = t->next;
374 }
375 }
376}
377
382{
383 /* Get last task. */
384 for (Task *t=pilot->task; t!=NULL; t=t->next)
385 if (!t->done)
386 return t;
387 return NULL;
388}
389
393static void ai_setMemory (void)
394{
395 nlua_env env = cur_pilot->ai->env;
396 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->lua_mem );
397 nlua_setenv(naevL, env, "mem"); /* pm */
398}
399
406{
407 cur_pilot = p;
408 ai_setMemory();
409}
410
417static void ai_run( nlua_env env, int nargs )
418{
419 if (nlua_pcall(env, nargs, 0)) { /* error has occurred */
420 WARN( _("Pilot '%s' ai '%s' error: %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL,-1));
421 lua_pop(naevL,1);
422 }
423}
424
434int ai_pinit( Pilot *p, const char *ai )
435{
436 AI_Profile *prof;
437 char buf[PATH_MAX];
438
439 strncpy(buf, ai, sizeof(buf)-1);
440 buf[sizeof(buf)-1] = '\0';
441
442 /* Set up the profile. */
443 prof = ai_getProfile(buf);
444 if (prof == NULL) {
445 WARN( _("AI Profile '%s' not found, using dummy fallback."), buf);
446 snprintf(buf, sizeof(buf), "dummy" );
447 prof = ai_getProfile(buf);
448 }
449 if (prof == NULL) {
450 WARN( _("Dummy AI Profile not valid! Things are going to break.") );
451 return -1;
452 }
453 p->ai = prof;
454
455 /* Adds a new pilot memory in the memory table. */
456 lua_newtable(naevL); /* m */
457
458 /* Copy defaults over from the global memory table. */
459 lua_rawgeti( naevL, LUA_REGISTRYINDEX, prof->lua_mem ); /* m, d */
460 lua_pushnil(naevL); /* m, d, nil */
461 while (lua_next(naevL,-2) != 0) { /* m, d, k, v */
462 lua_pushvalue(naevL,-2); /* m, d, k, v, k */
463 lua_pushvalue(naevL,-2); /* m, d, k, v, k, v */
464 lua_remove(naevL, -3); /* m, d, k, k, v */
465 lua_settable(naevL,-5); /* m, d, k */
466 } /* m, d */
467 lua_pop(naevL,1); /* m */
468 p->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
469
470 /* Create the pilot. */
471 ai_create( p );
472 pilot_setFlag(p, PILOT_CREATED_AI);
473
474 /* Initialize randomly within a control tick. */
475 p->tcontrol = RNGF() * p->ai->control_rate;
476
477 return 0;
478}
479
486{
487 /* Clean up tasks. */
488 if (p->task)
489 ai_freetask( p->task );
490 p->task = NULL;
491}
492
499{
500 if (p->ai == NULL)
501 return;
502
503 /* Get rid of pilot's memory. */
504 if (!pilot_isPlayer(p)) { /* Player is an exception as more than one ship shares pilot id. */
505 luaL_unref( naevL, LUA_REGISTRYINDEX, p->lua_mem );
506 p->lua_mem = LUA_NOREF;
507 }
508
509 /* Clear the tasks. */
510 ai_cleartasks( p );
511}
512
513static int ai_sort( const void *p1, const void *p2 )
514{
515 const AI_Profile *ai1 = (const AI_Profile*) p1;
516 const AI_Profile *ai2 = (const AI_Profile*) p2;
517 return strcmp( ai1->name, ai2->name );
518}
519
525int ai_load (void)
526{
527 char** files;
528 Uint32 time = SDL_GetTicks();
529
530 /* get the file list */
531 files = PHYSFS_enumerateFiles( AI_PATH );
532
533 /* Create array. */
535
536 /* load the profiles */
537 for (size_t i=0; files[i]!=NULL; i++) {
538 AI_Profile prof;
539 char path[PATH_MAX];
540 int ret;
541
542 if (!ndata_matchExt( files[i], "lua" ))
543 continue;
544
545 snprintf( path, sizeof(path), AI_PATH"%s", files[i] );
546 ret = ai_loadProfile(&prof,path); /* Load the profile */
547 if (ret == 0)
548 array_push_back( &profiles, prof );
549 else
550 WARN( _("Error loading AI profile '%s'"), path);
551
552 /* Render if necessary. */
554 }
555 qsort( profiles, array_size(profiles), sizeof(AI_Profile), ai_sort );
556
557 /* More clean up. */
558 PHYSFS_freeList( files );
559
560 if (conf.devmode) {
561 time = SDL_GetTicks() - time;
562 DEBUG( n_("Loaded %d AI Profile in %.3f s", "Loaded %d AI Profiles in %.3f s", array_size(profiles) ), array_size(profiles), time/1000. );
563 }
564 else
565 DEBUG( n_("Loaded %d AI Profile", "Loaded %d AI Profiles", array_size(profiles) ), array_size(profiles) );
566
567 /* Load equipment thingy. */
568 return ai_loadEquip();
569}
570
574static int ai_loadEquip (void)
575{
576 char *buf;
577 size_t bufsize;
578 const char *filename = AI_EQUIP_PATH;
579
580 /* Make sure doesn't already exist. */
581 nlua_freeEnv(equip_env);
582
583 /* Create new state. */
584 equip_env = nlua_newEnv();
586
587 /* Load the file. */
588 buf = ndata_read( filename, &bufsize );
589 if (nlua_dobufenv(equip_env, buf, bufsize, filename) != 0) {
590 WARN( _("Error loading file: %s\n"
591 "%s\n"
592 "Most likely Lua file has improper syntax, please check"),
593 filename, lua_tostring(naevL, -1));
594 return -1;
595 }
596 free(buf);
597
598 return 0;
599}
600
608static int ai_loadProfile( AI_Profile *prof, const char* filename )
609{
610 char* buf = NULL;
611 size_t bufsize = 0;
612 nlua_env env;
613 size_t len;
614 const char *str;
615
616 /* Set name. */
617 len = strlen(filename)-strlen(AI_PATH)-strlen(".lua");
618 prof->name = malloc(len+1);
619 strncpy( prof->name, &filename[strlen(AI_PATH)], len );
620 prof->name[len] = '\0';
621
622 /* Create Lua. */
623 env = nlua_newEnv();
625 prof->env = env;
626
627 /* Register C functions in Lua */
628 nlua_register(env, "ai", aiL_methods, 0);
629
630 /* Mark as an ai. */
631 lua_pushboolean( naevL, 1 );
632 nlua_setenv( naevL, env, "__ai" );
633
634 /* Set "mem" to be default template. */
635 lua_newtable(naevL); /* m */
636 lua_pushvalue(naevL,-1); /* m, m */
637 prof->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* m */
638 nlua_setenv(naevL, env, "mem"); /* */
639
640 /* Now load the file since all the functions have been previously loaded */
641 buf = ndata_read( filename, &bufsize );
642 if (nlua_dobufenv(env, buf, bufsize, filename) != 0) {
643 WARN( _("Error loading AI file: %s\n"
644 "%s\n"
645 "Most likely Lua file has improper syntax, please check"),
646 filename, lua_tostring(naevL,-1));
647 free(prof->name);
648 nlua_freeEnv( env );
649 free(buf);
650 return -1;
651 }
652 free(buf);
653
654 /* Find and set up the necessary references. */
655 str = _("AI Profile '%s' is missing '%s' function!");
656 prof->ref_control = nlua_refenvtype( env, "control", LUA_TFUNCTION );
657 if (prof->ref_control == LUA_NOREF)
658 WARN( str, filename, "control" );
659 prof->ref_control_manual = nlua_refenvtype( env, "control_manual", LUA_TFUNCTION );
660 if (prof->ref_control_manual == LUA_NOREF)
661 WARN( str, filename, "control_manual" );
662 prof->ref_refuel = nlua_refenvtype( env, "refuel", LUA_TFUNCTION );
663 if (prof->ref_refuel == LUA_NOREF)
664 WARN( str, filename, "refuel" );
665 prof->ref_create = nlua_refenvtype( env, "create", LUA_TFUNCTION );
666 if (prof->ref_create == LUA_NOREF)
667 WARN( str, filename, "create" );
668
669 /* Get the control rate. */
670 nlua_getenv(naevL, env, "control_rate");
671 prof->control_rate = lua_tonumber(naevL,-1);
672 lua_pop(naevL,1);
673
674 return 0;
675}
676
683AI_Profile* ai_getProfile( const char *name )
684{
685 const AI_Profile ai = { .name = (char*)name };
686 AI_Profile *ret = bsearch( &ai, profiles, array_size(profiles), sizeof(AI_Profile), ai_sort );
687 if (ret==NULL)
688 WARN( _("AI Profile '%s' not found in AI stack"), name);
689 return ret;
690}
691
695void ai_exit (void)
696{
697 /* Free AI profiles. */
698 for (int i=0; i<array_size(profiles); i++) {
699 free(profiles[i].name);
700 nlua_freeEnv(profiles[i].env);
701 }
703
704 /* Free equipment Lua. */
705 nlua_freeEnv(equip_env);
706 equip_env = LUA_NOREF;
707}
708
715void ai_think( Pilot* pilot, const double dt )
716{
717 (void) dt;
718 nlua_env env;
719 int data;
720 Task *t;
721
722 /* Must have AI. */
723 if (pilot->ai == NULL)
724 return;
725
726 ai_setPilot(pilot);
727 env = cur_pilot->ai->env; /* set the AI profile to the current pilot's */
728
729 /* Clean up some variables */
730 pilot_acc = 0;
731 pilot_turn = 0.;
732 pilot_flags = 0;
733 /* So the way this works is that, for other than the player, we reset all
734 * the weapon sets every frame, so that the AI has to redo them over and
735 * over. Now, this is a horrible hack so shit works and needs a proper fix.
736 * TODO fix. */
737 /* pilot_setTarget( cur_pilot, cur_pilot->id ); */
738 if (cur_pilot->id != PLAYER_ID)
740
741 /* Get current task. */
742 t = ai_curTask( cur_pilot );
743
744 /* control function if pilot is idle or tick is up */
745 if ((cur_pilot->tcontrol < 0.) || (t == NULL)) {
746 double crate = cur_pilot->ai->control_rate;
747 if (pilot_isFlag(pilot,PILOT_PLAYER) ||
748 pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL)) {
749 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_control_manual );
750 lua_pushnumber( naevL, crate-cur_pilot->tcontrol );
751 ai_run(env, 1);
752 } else {
753 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_control );
754 lua_pushnumber( naevL, crate-cur_pilot->tcontrol );
755 ai_run(env, 1); /* run control */
756 }
757 cur_pilot->tcontrol = crate;
758
759 /* Task may have changed due to control tick. */
760 t = ai_curTask( cur_pilot );
761 }
762
763 if (pilot_isFlag(pilot,PILOT_PLAYER) &&
764 !pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL))
765 return;
766
767 /* pilot has a currently running task */
768 if (t != NULL) {
769 /* Run subtask if available, otherwise run main task. */
770 if (t->subtask != NULL) {
771 lua_rawgeti( naevL, LUA_REGISTRYINDEX, t->subtask->func );
772 /* Use subtask data or task data if subtask is not set. */
773 data = t->subtask->dat;
774 if (data == LUA_NOREF)
775 data = t->dat;
776 }
777 else {
778 lua_rawgeti( naevL, LUA_REGISTRYINDEX, t->func );
779 data = t->dat;
780 }
781 /* Function should be on the stack. */
782 if (data != LUA_NOREF) {
783 lua_rawgeti( naevL, LUA_REGISTRYINDEX, data );
784 ai_run(env, 1);
785 } else
786 ai_run(env, 0);
787
788 /* Manual control must check if IDLE hook has to be run. */
789 if (pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL)) {
790 /* We must yet check again to see if there still is a current task running. */
791 if (ai_curTask( cur_pilot ) == NULL)
792 pilot_runHook( cur_pilot, PILOT_HOOK_IDLE );
793 }
794 }
795
796 /* make sure pilot_acc and pilot_turn are legal */
797 pilot_acc = CLAMP( -1., 1., pilot_acc );
798 pilot_turn = CLAMP( -1., 1., pilot_turn );
799
800 /* Set turn and thrust. */
803
804 /* fire weapons if needed */
806 pilot_shoot(cur_pilot, 0); /* primary */
808 pilot_shoot(cur_pilot, 1 ); /* secondary */
809
810 /* other behaviours. */
813
814 /* Clean up if necessary. */
816}
817
823void ai_init( Pilot *p )
824{
825 if ((p->ai==NULL) || (p->ai->ref_create==LUA_NOREF))
826 return;
827 ai_setPilot( p );
828 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->ai->ref_create );
829 ai_run( p->ai->env, 0 ); /* run control */
830
831}
832
840void ai_attacked( Pilot *attacked, const unsigned int attacker, double dmg )
841{
842 HookParam hparam[2];
843
844 /* Custom hook parameters. */
845 hparam[0].type = HOOK_PARAM_PILOT;
846 hparam[0].u.lp = attacker;
847 hparam[1].type = HOOK_PARAM_NUMBER;
848 hparam[1].u.num = dmg;
849
850 /* Behaves differently if manually overridden. */
851 pilot_runHookParam( attacked, PILOT_HOOK_ATTACKED, hparam, 2 );
852
853 /* Must have an AI profile. */
854 if (attacked->ai == NULL)
855 return;
856
857 ai_setPilot( attacked ); /* Sets cur_pilot. */
858 if (pilot_isFlag( attacked, PILOT_MANUAL_CONTROL ))
859 nlua_getenv(naevL, cur_pilot->ai->env, "attacked_manual");
860 else
861 nlua_getenv(naevL, cur_pilot->ai->env, "attacked");
862
863 lua_pushpilot(naevL, attacker);
864 if (nlua_pcall(cur_pilot->ai->env, 1, 0)) {
865 WARN( _("Pilot '%s' ai '%s' -> 'attacked': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL, -1));
866 lua_pop(naevL, 1);
867 }
868}
869
875void ai_discovered( Pilot* discovered )
876{
877 /* Behaves differently if manually overridden. */
878 pilot_runHook( discovered, PILOT_HOOK_DISCOVERED );
879 if (pilot_isFlag( discovered, PILOT_MANUAL_CONTROL ))
880 return;
881
882 /* Must have an AI profile and not be player. */
883 if (discovered->ai == NULL)
884 return;
885
886 ai_setPilot( discovered ); /* Sets cur_pilot. */
887
888 /* Only run if discovered function exists. */
889 nlua_getenv(naevL, cur_pilot->ai->env, "discovered");
890 if (lua_isnil(naevL,-1)) {
891 lua_pop(naevL,1);
892 return;
893 }
894
895 if (nlua_pcall(cur_pilot->ai->env, 0, 0)) {
896 WARN( _("Pilot '%s' ai '%s' -> 'discovered': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL, -1));
897 lua_pop(naevL, 1);
898 }
899}
900
906void ai_hail( Pilot* recipient )
907{
908 /* Make sure it's getable. */
909 if (!pilot_canTarget( recipient ))
910 return;
911
912 /* Must have an AI profile and not be player. */
913 if (recipient->ai == NULL)
914 return;
915
916 ai_setPilot( recipient ); /* Sets cur_pilot. */
917
918 /* Only run if hail function exists. */
919 nlua_getenv(naevL, cur_pilot->ai->env, "hail");
920 if (lua_isnil(naevL,-1)) {
921 lua_pop(naevL,1);
922 return;
923 }
924
925 if (nlua_pcall(cur_pilot->ai->env, 0, 0)) {
926 WARN( _("Pilot '%s' ai '%s' -> 'hail': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL, -1));
927 lua_pop(naevL, 1);
928 }
929}
930
937void ai_refuel( Pilot* refueler, unsigned int target )
938{
939 Task *t;
940
941 if (cur_pilot->ai->ref_refuel==LUA_NOREF) {
942 WARN(_("Pilot '%s' (ai '%s') is trying to refuel when no 'refuel' function is defined!"), cur_pilot->name, cur_pilot->ai->name);
943 return;
944 }
945
946 /* Create the task. */
947 t = calloc( 1, sizeof(Task) );
948 t->name = strdup("refuel");
949 lua_rawgeti(naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_refuel);
950 t->func = luaL_ref(naevL, LUA_REGISTRYINDEX);
951 lua_pushpilot(naevL, target);
952 t->dat = luaL_ref(naevL, LUA_REGISTRYINDEX);
953
954 /* Prepend the task. */
955 t->next = refueler->task;
956 refueler->task = t;
957
958 return;
959}
960
968void ai_getDistress( Pilot *p, const Pilot *distressed, const Pilot *attacker )
969{
970 /* Ignore distress signals when under manual control. */
971 if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ))
972 return;
973
974 /* Must have AI. */
975 if (cur_pilot->ai == NULL)
976 return;
977
978 /* Set up the environment. */
979 ai_setPilot(p);
980
981 /* See if function exists. */
982 nlua_getenv(naevL, cur_pilot->ai->env, "distress");
983 if (lua_isnil(naevL,-1)) {
984 lua_pop(naevL,1);
985 return;
986 }
987
988 /* Run the function. */
989 lua_pushpilot(naevL, distressed->id);
990 if (attacker != NULL)
991 lua_pushpilot(naevL, attacker->id);
992 else /* Default to the victim's current target. */
993 lua_pushpilot(naevL, distressed->target);
994
995 if (nlua_pcall(cur_pilot->ai->env, 2, 0)) {
996 WARN( _("Pilot '%s' ai '%s' -> 'distress': %s"), cur_pilot->name, cur_pilot->ai->name, lua_tostring(naevL,-1));
997 lua_pop(naevL,1);
998 }
999}
1000
1008static void ai_create( Pilot* pilot )
1009{
1010 /* Set creation mode. */
1011 if (!pilot_isFlag(pilot, PILOT_CREATED_AI))
1013
1014 /* Create equipment first - only if creating for the first time. */
1015 if (!pilot_isFlag(pilot,PILOT_NO_OUTFITS) && (aiL_status==AI_STATUS_CREATE)) {
1016 nlua_env env = equip_env;
1017 char *func = "equip_generic";
1018
1019 if (faction_getEquipper( pilot->faction ) != LUA_NOREF) {
1020 env = faction_getEquipper( pilot->faction );
1021 func = "equip";
1022 }
1023 nlua_getenv(naevL, env, func);
1024 nlua_pushenv(naevL, env);
1025 lua_setfenv(naevL, -2);
1026 lua_pushpilot(naevL, pilot->id);
1027 if (nlua_pcall(env, 1, 0)) { /* Error has occurred. */
1028 WARN( _("Pilot '%s' equip '%s' -> '%s': %s"), pilot->name, pilot->ai->name, func, lua_tostring(naevL, -1));
1029 lua_pop(naevL, 1);
1030 }
1031
1032 /* Since the pilot changes outfits and cores, we must heal him up. */
1033 pilot_healLanded( pilot );
1034 }
1035
1036 /* Must have AI. */
1037 if (pilot->ai == NULL)
1038 return;
1039
1040 /* Set up. */
1041 ai_init( pilot );
1042
1043 /* Recover normal mode. */
1044 if (!pilot_isFlag(pilot, PILOT_CREATED_AI))
1046}
1047
1051Task *ai_newtask( lua_State *L, Pilot *p, const char *func, int subtask, int pos )
1052{
1053 Task *t, *curtask, *pointer;
1054
1055 if (p->ai==NULL) {
1056 WARN(_("Trying to create new task for pilot '%s' that has no AI!"), p->name);
1057 return NULL;
1058 }
1059
1060 /* Check if the function is good. */
1061 nlua_getenv( L, p->ai->env, func );
1062 luaL_checktype( L, -1, LUA_TFUNCTION );
1063
1064 /* Create the new task. */
1065 t = calloc( 1, sizeof(Task) );
1066 t->name = strdup(func);
1067 t->func = luaL_ref( L, LUA_REGISTRYINDEX );
1068 t->dat = LUA_NOREF;
1069
1070 /* Handle subtask and general task. */
1071 if (!subtask) {
1072 if ((pos == 1) && (p->task != NULL)) { /* put at the end */
1073 for (pointer = p->task; pointer->next != NULL; pointer = pointer->next);
1074 pointer->next = t;
1075 }
1076 else {
1077 t->next = p->task;
1078 p->task = t;
1079 }
1080 }
1081 else {
1082 /* Must have valid task. */
1083 curtask = ai_curTask( p );
1084 if (curtask == NULL) {
1085 ai_freetask( t );
1086 NLUA_ERROR( L, _("Trying to add subtask '%s' to non-existent task."), func);
1087 }
1088
1089 /* Add the subtask. */
1090 if ((pos == 1) && (curtask->subtask != NULL)) { /* put at the end */
1091 for (pointer = curtask->subtask; pointer->next != NULL; pointer = pointer->next);
1092 pointer->next = t;
1093 }
1094 else {
1095 t->next = curtask->subtask;
1096 curtask->subtask = t;
1097 }
1098 }
1099
1100 return t;
1101}
1102
1109{
1110 if (t->func != LUA_NOREF)
1111 luaL_unref(naevL, LUA_REGISTRYINDEX, t->func);
1112
1113 if (t->dat != LUA_NOREF)
1114 luaL_unref(naevL, LUA_REGISTRYINDEX, t->dat);
1115
1116 /* Recursive subtask freeing. */
1117 if (t->subtask != NULL) {
1118 ai_freetask(t->subtask);
1119 t->subtask = NULL;
1120 }
1121
1122 /* Free next task in the chain. */
1123 if (t->next != NULL) {
1124 ai_freetask(t->next); /* yay recursive freeing */
1125 t->next = NULL;
1126 }
1127
1128 free(t->name);
1129 free(t);
1130}
1131
1135static Task* ai_createTask( lua_State *L, int subtask )
1136{
1137 /* Parse basic parameters. */
1138 const char *func = luaL_checkstring(L,1);
1139
1140 if (pilot_isPlayer(cur_pilot) && !pilot_isFlag(cur_pilot,PILOT_MANUAL_CONTROL))
1141 return NULL;
1142
1143 /* Creates a new AI task. */
1144 Task *t = ai_newtask( L, cur_pilot, func, subtask, 0 );
1145
1146 /* Set the data. */
1147 if (lua_gettop(L) > 1) {
1148 lua_pushvalue(L,2);
1149 t->dat = luaL_ref(L, LUA_REGISTRYINDEX);
1150 }
1151
1152 return t;
1153}
1154
1158static int ai_tasktarget( lua_State *L, Task *t )
1159{
1160 if (t->dat == LUA_NOREF)
1161 return 0;
1162 lua_rawgeti(L, LUA_REGISTRYINDEX, t->dat);
1163 return 1;
1164}
1165
1185static int aiL_pushtask( lua_State *L )
1186{
1187 ai_createTask( L, 0 );
1188 return 0;
1189}
1195static int aiL_poptask( lua_State *L )
1196{
1197 Task *t = ai_curTask( cur_pilot );
1198 /* Tasks must exist. */
1199 if (t == NULL) {
1200 NLUA_ERROR(L, _("Trying to pop task when there are no tasks on the stack."));
1201 return 0;
1202 }
1203 t->done = 1;
1204 return 0;
1205}
1206
1213static int aiL_taskname( lua_State *L )
1214{
1215 Task *t = ai_curTask( cur_pilot );
1216 if (t)
1217 lua_pushstring(L, t->name);
1218 else
1219 lua_pushnil(L);
1220 return 1;
1221}
1222
1230static int aiL_taskdata( lua_State *L )
1231{
1232 Task *t = ai_curTask( cur_pilot );
1233
1234 /* Must have a task. */
1235 if (t == NULL)
1236 return 0;
1237
1238 return ai_tasktarget( L, t );
1239}
1240
1248static int aiL_pushsubtask( lua_State *L )
1249{
1250 ai_createTask(L, 1);
1251 return 0;
1252}
1253
1259static int aiL_popsubtask( lua_State *L )
1260{
1261 Task *t, *st;
1262 t = ai_curTask( cur_pilot );
1263
1264 /* Tasks must exist. */
1265 if (t == NULL) {
1266 NLUA_ERROR(L, _("Trying to pop task when there are no tasks on the stack."));
1267 return 0;
1268 }
1269 if (t->subtask == NULL) {
1270 NLUA_ERROR(L, _("Trying to pop subtask when there are no subtasks for the task '%s'."), t->name);
1271 return 0;
1272 }
1273
1274 /* Exterminate, annihilate destroy. */
1275 st = t->subtask;
1276 t->subtask = st->next;
1277 st->next = NULL;
1278 ai_freetask(st);
1279 return 0;
1280}
1281
1288static int aiL_subtaskname( lua_State *L )
1289{
1290 Task *t = ai_curTask( cur_pilot );
1291 if ((t != NULL) && (t->subtask != NULL))
1292 lua_pushstring(L, t->subtask->name);
1293 else
1294 lua_pushnil(L);
1295 return 1;
1296}
1297
1305static int aiL_subtaskdata( lua_State *L )
1306{
1307 Task *t = ai_curTask( cur_pilot );
1308 /* Must have a subtask. */
1309 if ((t == NULL) || (t->subtask == NULL))
1310 return 0;
1311
1312 return ai_tasktarget( L, t->subtask );
1313}
1314
1321static int aiL_pilot( lua_State *L )
1322{
1324 return 1;
1325}
1326
1333static int aiL_getrndpilot( lua_State *L )
1334{
1335 Pilot *const* pilot_stack = pilot_getAll();
1336 int p = RNG(0, array_size(pilot_stack)-1);
1337 /* Make sure it can't be the same pilot. */
1338 if (pilot_stack[p]->id == cur_pilot->id) {
1339 p++;
1340 if (p >= array_size(pilot_stack))
1341 p = 0;
1342 }
1343 /* Last check. */
1344 if (pilot_stack[p]->id == cur_pilot->id)
1345 return 0;
1346 /* Actually found a pilot. */
1347 lua_pushpilot(L, pilot_stack[p]->id);
1348 return 1;
1349}
1350
1357static int aiL_getnearestpilot( lua_State *L )
1358{
1359 /* dist will be initialized to a number */
1360 /* this will only seek out pilots closer than dist */
1361 Pilot *const* pilot_stack = pilot_getAll();
1362 int dist = 1e6;
1363 int candidate_id = -1;
1364
1365 /*cycle through all the pilots and find the closest one that is not the pilot */
1366 for (int i=0; i<array_size(pilot_stack); i++) {
1367 if (pilot_stack[i]->id == cur_pilot->id)
1368 continue;
1369 if (vec2_dist(&pilot_stack[i]->solid->pos, &cur_pilot->solid->pos) > dist)
1370 continue;
1371 dist = vec2_dist(&pilot_stack[i]->solid->pos, &cur_pilot->solid->pos);
1372 candidate_id = i;
1373 }
1374
1375 /* Last check. */
1376 if (candidate_id == -1)
1377 return 0;
1378
1379 /* Actually found a pilot. */
1380 lua_pushpilot(L, pilot_stack[candidate_id]->id);
1381 return 1;
1382}
1383
1391static int aiL_getdistance( lua_State *L )
1392{
1393 vec2 *v;
1394
1395 /* vector as a parameter */
1396 if (lua_isvector(L,1))
1397 v = lua_tovector(L,1);
1398 /* pilot as parameter */
1399 else if (lua_ispilot(L,1)) {
1400 Pilot *p = luaL_validpilot(L,1);
1401 v = &p->solid->pos;
1402 }
1403 /* wrong parameter */
1404 else
1405 NLUA_INVALID_PARAMETER(L);
1406
1407 lua_pushnumber(L, vec2_dist(v, &cur_pilot->solid->pos));
1408 return 1;
1409}
1410
1418static int aiL_getdistance2( lua_State *L )
1419{
1420 vec2 *v;
1421
1422 /* vector as a parameter */
1423 if (lua_isvector(L,1))
1424 v = lua_tovector(L,1);
1425 /* pilot as parameter */
1426 else if (lua_ispilot(L,1)) {
1427 Pilot *p = luaL_validpilot(L,1);
1428 v = &p->solid->pos;
1429 }
1430 /* wrong parameter */
1431 else
1432 NLUA_INVALID_PARAMETER(L);
1433
1434 lua_pushnumber(L, vec2_dist2(v, &cur_pilot->solid->pos));
1435 return 1;
1436}
1437
1445static int aiL_getflybydistance( lua_State *L )
1446{
1447 vec2 *v;
1448 vec2 perp_motion_unit, offset_vect;
1449 int offset_distance;
1450
1451 /* vector as a parameter */
1452 if (lua_isvector(L,1))
1453 v = lua_tovector(L,1);
1454 /* pilot id as parameter */
1455 else if (lua_ispilot(L,1)) {
1456 Pilot *p = luaL_validpilot(L,1);
1457 v = &p->solid->pos;
1458
1459 /*vec2_cset(&v, VX(pilot->solid->pos) - VX(cur_pilot->solid->pos), VY(pilot->solid->pos) - VY(cur_pilot->solid->pos) );*/
1460 }
1461 else
1462 NLUA_INVALID_PARAMETER(L);
1463
1464 vec2_cset(&offset_vect, VX(*v) - VX(cur_pilot->solid->pos), VY(*v) - VY(cur_pilot->solid->pos) );
1465 vec2_pset(&perp_motion_unit, 1, VANGLE(cur_pilot->solid->vel)+M_PI_2);
1466 offset_distance = vec2_dot(&perp_motion_unit, &offset_vect);
1467
1468 lua_pushnumber(L, offset_distance);
1469 return 1;
1470}
1471
1485static int aiL_minbrakedist( lua_State *L )
1486{
1487 double time, dist, vel;
1488 vec2 vv;
1489
1490 /* More complicated calculation based on relative velocity. */
1491 if (lua_gettop(L) > 0) {
1492 Pilot *p = luaL_validpilot(L,1);
1493
1494 /* Set up the vectors. */
1495 vec2_cset( &vv, p->solid->vel.x - cur_pilot->solid->vel.x,
1496 p->solid->vel.y - cur_pilot->solid->vel.y );
1497
1498 /* Run the same calculations. */
1499 time = VMOD(vv) /
1501
1502 /* Get relative velocity. */
1503 vel = MIN(cur_pilot->speed - VMOD(p->solid->vel), VMOD(vv));
1504 if (vel < 0.)
1505 vel = 0.;
1506 }
1507 /* Simple calculation based on distance. */
1508 else {
1509 /* Get current time to reach target. */
1510 time = VMOD(cur_pilot->solid->vel) /
1512
1513 /* Get velocity. */
1514 vel = MIN(cur_pilot->speed,VMOD(cur_pilot->solid->vel));
1515 }
1516 /* Get distance to brake. */
1517 dist = vel*(time+1.1*M_PI/cur_pilot->turn) -
1518 0.5*(cur_pilot->thrust/cur_pilot->solid->mass)*time*time;
1519
1520 lua_pushnumber(L, dist); /* return */
1521 return 1; /* returns one thing */
1522}
1523
1531static int aiL_isbribed( lua_State *L )
1532{
1533 Pilot *p = luaL_validpilot(L,1);
1534 lua_pushboolean(L, pilot_isWithPlayer(p) && pilot_isFlag(cur_pilot, PILOT_BRIBED));
1535 return 1;
1536}
1537
1544static int aiL_instantJump( lua_State *L )
1545{
1546 lua_pushboolean(L, cur_pilot->stats.misc_instant_jump);
1547 return 1;
1548}
1549
1556static int aiL_ismaxvel( lua_State *L )
1557{
1558 //lua_pushboolean(L,(VMOD(cur_pilot->solid->vel) > (cur_pilot->speed-MIN_VEL_ERR)));
1559 lua_pushboolean(L,(VMOD(cur_pilot->solid->vel) >
1560 (solid_maxspeed(cur_pilot->solid, cur_pilot->speed, cur_pilot->thrust)-MIN_VEL_ERR)));
1561 return 1;
1562}
1563
1570static int aiL_isstopped( lua_State *L )
1571{
1572 lua_pushboolean(L,(VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR));
1573 return 1;
1574}
1575
1583static int aiL_isenemy( lua_State *L )
1584{
1585 Pilot *p = luaL_validpilot(L,1);
1586
1587 /* Player needs special handling in case of hostility. */
1588 if (pilot_isWithPlayer(p)) {
1589 lua_pushboolean(L, pilot_isHostile(cur_pilot));
1590 return 1;
1591 }
1592
1593 /* Check if is ally. */
1594 lua_pushboolean(L,areEnemies(cur_pilot->faction, p->faction));
1595 return 1;
1596}
1597
1605static int aiL_isally( lua_State *L )
1606{
1607 Pilot *p = luaL_validpilot(L,1);
1608
1609 /* Player needs special handling in case of friendliness. */
1610 if (pilot_isWithPlayer(p)) {
1611 lua_pushboolean(L, pilot_isFriendly(cur_pilot));
1612 return 1;
1613 }
1614
1615 /* Check if is ally. */
1616 lua_pushboolean(L,areAllies(cur_pilot->faction, p->faction));
1617 return 1;
1618}
1619
1627static int aiL_haslockon( lua_State *L )
1628{
1629 lua_pushboolean(L, cur_pilot->lockons > 0);
1630 return 1;
1631}
1632
1640static int aiL_hasprojectile( lua_State *L )
1641{
1642 lua_pushboolean(L, cur_pilot->projectiles > 0);
1643 return 1;
1644}
1645
1653static int aiL_scandone( lua_State *L )
1654{
1655 lua_pushboolean(L, pilot_ewScanCheck( cur_pilot ) );
1656 return 1;
1657}
1658
1665static int aiL_accel( lua_State *L )
1666{
1667 double n = luaL_optnumber( L, 1, 1. );
1668 pilot_acc = CLAMP( 0., 1., n );
1669 return 0;
1670}
1671
1678static int aiL_turn( lua_State *L )
1679{
1680 pilot_turn = luaL_checknumber(L,1);
1681 return 0;
1682}
1683
1697static int aiL_face( lua_State *L )
1698{
1699 vec2 *tv; /* get the position to face */
1700 double k_diff, k_vel, diff, vx, vy, dx, dy;
1701 int vel;
1702
1703 /* Get first parameter, aka what to face. */
1704 if (lua_ispilot(L,1)) {
1705 Pilot* p = luaL_validpilot(L,1);
1706 /* Target vector. */
1707 tv = &p->solid->pos;
1708 }
1709 else if (lua_isnumber(L,1)) {
1710 double d = lua_tonumber(L,1);
1711 if (d < 0.)
1712 tv = &cur_pilot->solid->pos;
1713 else
1714 NLUA_INVALID_PARAMETER(L);
1715 }
1716 else if (lua_isvector(L,1))
1717 tv = lua_tovector(L,1);
1718 else
1719 NLUA_INVALID_PARAMETER(L);
1720
1721 /* Default gain. */
1722 k_diff = 10.;
1723 k_vel = 100.; /* overkill gain! */
1724
1725 /* Check if must invert. */
1726 if (lua_toboolean(L,2))
1727 k_diff *= -1;
1728
1729 /* Third parameter. */
1730 vel = lua_toboolean(L, 3);
1731
1732 /* Tangential component of velocity vector
1733 *
1734 * v: velocity vector
1735 * d: direction vector
1736 *
1737 * d d d
1738 * v_t = v - ( v . --- ) * --- = v - ( v . ----- ) * d
1739 * |d| |d| |d|^2
1740 */
1741 /* Velocity vector. */
1742 vx = cur_pilot->solid->vel.x;
1743 vy = cur_pilot->solid->vel.y;
1744 /* Direction vector. */
1745 dx = tv->x - cur_pilot->solid->pos.x;
1746 dy = tv->y - cur_pilot->solid->pos.y;
1747 if (vel && (dx || dy)) {
1748 /* Calculate dot product. */
1749 double d = (vx * dx + vy * dy) / (dx*dx + dy*dy);
1750 /* Calculate tangential velocity. */
1751 vx = vx - d * dx;
1752 vy = vy - d * dy;
1753
1754 /* Add velocity compensation. */
1755 dx += -k_vel * vx;
1756 dy += -k_vel * vy;
1757 }
1758
1759 /* Compensate error and rotate. */
1760 diff = angle_diff( cur_pilot->solid->dir, atan2( dy, dx ) );
1761
1762 /* Make pilot turn. */
1763 pilot_turn = k_diff * diff;
1764
1765 /* Return angle away from target. */
1766 lua_pushnumber(L, ABS(diff));
1767 return 1;
1768}
1769
1787static int aiL_careful_face( lua_State *L )
1788{
1789 vec2 *tv, F, F1;
1790 Pilot* p;
1791 Pilot *p_i;
1792 double k_diff, k_goal, k_enemy, k_mult,
1793 d, diff, dist, factor;
1794 int i;
1795 Pilot *const* pilot_stack;
1796
1797 /* Init some variables */
1799 p = cur_pilot;
1800
1801 /* Get first parameter, aka what to face. */
1802 if (lua_ispilot(L,1)) {
1803 p = luaL_validpilot(L,1);
1804 /* Target vector. */
1805 tv = &p->solid->pos;
1806 }
1807 else if (lua_isnumber(L,1)) {
1808 d = (double)lua_tonumber(L,1);
1809 if (d < 0.)
1810 tv = &cur_pilot->solid->pos;
1811 else
1812 NLUA_INVALID_PARAMETER(L);
1813 }
1814 else if (lua_isvector(L,1))
1815 tv = lua_tovector(L,1);
1816 else
1817 NLUA_INVALID_PARAMETER(L);
1818
1819 /* Default gains. */
1820 k_diff = 10.;
1821 k_goal = 1.;
1822 k_enemy = 6000000.;
1823
1824 /* Init the force */
1825 vec2_cset( &F, 0., 0.) ;
1826 vec2_cset( &F1, tv->x - cur_pilot->solid->pos.x, tv->y - cur_pilot->solid->pos.y) ;
1827 dist = VMOD(F1) + 0.1; /* Avoid / 0*/
1828 vec2_cset( &F1, F1.x * k_goal / dist, F1.y * k_goal / dist) ;
1829
1830 /* Cycle through all the pilots in order to compute the force */
1831 for (i=0; i<array_size(pilot_stack); i++) {
1832 p_i = pilot_stack[i];
1833
1834 /* Valid pilot isn't self, is in range, isn't the target and isn't disabled */
1835 if (pilot_isDisabled(p_i) ) continue;
1836 if (p_i->id == cur_pilot->id) continue;
1837 if (p_i->id == p->id) continue;
1838 if (pilot_inRangePilot(cur_pilot, p_i, NULL) != 1) continue;
1839
1840 /* If the enemy is too close, ignore it*/
1841 dist = vec2_dist(&p_i->solid->pos, &cur_pilot->solid->pos);
1842 if (dist < 750) continue;
1843
1844 k_mult = pilot_relhp( p_i, cur_pilot ) * pilot_reldps( p_i, cur_pilot );
1845
1846 /* Check if friendly or not */
1847 if (areEnemies(cur_pilot->faction, p_i->faction)) {
1848 factor = k_enemy * k_mult / (dist*dist*dist);
1849 vec2_cset( &F, F.x + factor * (cur_pilot->solid->pos.x - p_i->solid->pos.x),
1850 F.y + factor * (cur_pilot->solid->pos.y - p_i->solid->pos.y) );
1851 }
1852 }
1853
1854 vec2_cset( &F, F.x + F1.x, F.y + F1.y );
1855
1856 /* Rotate. */
1857 diff = angle_diff( cur_pilot->solid->dir, VANGLE(F) );
1858
1859 /* Make pilot turn. */
1860 pilot_turn = k_diff * diff;
1861
1862 /* Return angle away from target. */
1863 lua_pushnumber(L, ABS(diff));
1864 return 1;
1865}
1866
1876static int aiL_aim( lua_State *L )
1877{
1878 double diff, mod, angle;
1879
1880 if (lua_isasteroid(L,1)) {
1881 Asteroid *a = luaL_validasteroid(L,1);
1882 angle = pilot_aimAngle( cur_pilot, &a->pos, &a->vel );
1883 }
1884 else {
1885 Pilot *p = luaL_validpilot(L,1);
1886 angle = pilot_aimAngle( cur_pilot, &p->solid->pos, &p->solid->vel );
1887 }
1888
1889 /* Calculate what we need to turn */
1890 mod = 10.;
1891 diff = angle_diff(cur_pilot->solid->dir, angle);
1892 pilot_turn = mod * diff;
1893
1894 lua_pushnumber(L, ABS(diff));
1895 return 1;
1896}
1897
1905static int aiL_iface( lua_State *L )
1906{
1907 NLUA_MIN_ARGS(1);
1908 vec2 *vec, drift, reference_vector; /* get the position to face */
1909 Pilot* p;
1910 double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
1911 int azimuthal_sign;
1912 double speedmap;
1913
1914 /* Get first parameter, aka what to face. */
1915 p = NULL;
1916 vec = NULL;
1917 if (lua_ispilot(L,1))
1918 p = luaL_validpilot(L,1);
1919 else if (lua_isvector(L,1))
1920 vec = lua_tovector(L,1);
1921 else NLUA_INVALID_PARAMETER(L);
1922
1923 if (vec==NULL) {
1924 if (p == NULL)
1925 return 0; /* Return silently when attempting to face an invalid pilot. */
1926 /* Establish the current pilot velocity and position vectors */
1927 vec2_cset( &drift, VX(p->solid->vel) - VX(cur_pilot->solid->vel), VY(p->solid->vel) - VY(cur_pilot->solid->vel));
1928 /* Establish the in-line coordinate reference */
1929 vec2_cset( &reference_vector, VX(p->solid->pos) - VX(cur_pilot->solid->pos), VY(p->solid->pos) - VY(cur_pilot->solid->pos));
1930 }
1931 else {
1932 /* Establish the current pilot velocity and position vectors */
1933 vec2_cset( &drift, -VX(cur_pilot->solid->vel), -VY(cur_pilot->solid->vel));
1934 /* Establish the in-line coordinate reference */
1935 vec2_cset( &reference_vector, VX(*vec) - VX(cur_pilot->solid->pos), VY(*vec) - VY(cur_pilot->solid->pos));
1936 }
1937
1938 /* Break down the the velocity vectors of both craft into UV coordinates */
1939 vec2_uv(&drift_radial, &drift_azimuthal, &drift, &reference_vector);
1940 heading_offset_azimuth = angle_diff(cur_pilot->solid->dir, VANGLE(reference_vector));
1941
1942 /* Now figure out what to do...
1943 * Are we pointing anywhere inside the correct UV quadrant?
1944 * if we're outside the correct UV quadrant, we need to get into it ASAP
1945 * Otherwise match velocities and approach */
1946 if (FABS(heading_offset_azimuth) < M_PI_2) {
1947 /* This indicates we're in the correct plane*/
1948 /* 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 */
1949 /* There is nothing special about the scaling parameter of 200; it can be tuned to get any behavior desired. A lower
1950 number will give a more dramatic 'lead' */
1951 speedmap = -1*copysign(1 - 1 / (FABS(drift_azimuthal/200) + 1), drift_azimuthal) * M_PI_2;
1952 diff = angle_diff(heading_offset_azimuth, speedmap);
1953 azimuthal_sign = -1;
1954
1955 /* This indicates we're drifting to the right of the target
1956 * And we need to turn CCW */
1957 if (diff > 0)
1958 pilot_turn = azimuthal_sign;
1959 /* This indicates we're drifting to the left of the target
1960 * And we need to turn CW */
1961 else if (diff < 0)
1962 pilot_turn = -1*azimuthal_sign;
1963 else
1964 pilot_turn = 0;
1965 }
1966 /* 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 */
1967 /* some special case logic is added to optimize turn time. Reducing this to only the else cases would speed up the operation
1968 but cause the pilot to turn in the less-than-optimal direction sometimes when between 135 and 225 degrees off from the target */
1969 else {
1970 /* signal that we're not in a productive direction for thrusting */
1971 diff = M_PI;
1972 azimuthal_sign = 1;
1973
1974 if (heading_offset_azimuth >0)
1975 pilot_turn = azimuthal_sign;
1976 else
1977 pilot_turn = -1*azimuthal_sign;
1978 }
1979
1980 /* Return angle in degrees away from target. */
1981 lua_pushnumber(L, ABS(diff));
1982 return 1;
1983}
1984
1993static int aiL_dir( lua_State *L )
1994{
1995 NLUA_MIN_ARGS(1);
1996 vec2 *vec, sv, tv; /* get the position to face */
1997 Pilot* p;
1998 double diff;
1999 int n;
2000
2001 /* Get first parameter, aka what to face. */
2002 n = -2;
2003 vec = NULL;
2004 if (lua_ispilot(L,1)) {
2005 p = luaL_validpilot(L,1);
2006 vec2_cset( &tv, VX(p->solid->pos), VY(p->solid->pos) );
2007 }
2008 else if (lua_isvector(L,1))
2009 vec = lua_tovector(L,1);
2010 else NLUA_INVALID_PARAMETER(L);
2011
2012 vec2_cset( &sv, VX(cur_pilot->solid->pos), VY(cur_pilot->solid->pos) );
2013
2014 if (vec==NULL) /* target is dynamic */
2015 diff = angle_diff(cur_pilot->solid->dir,
2016 (n==-1) ? VANGLE(sv) :
2017 vec2_angle(&sv, &tv));
2018 else /* target is static */
2019 diff = angle_diff( cur_pilot->solid->dir,
2020 (n==-1) ? VANGLE(cur_pilot->solid->pos) :
2021 vec2_angle(&cur_pilot->solid->pos, vec));
2022
2023 /* Return angle in degrees away from target. */
2024 lua_pushnumber(L, diff);
2025 return 1;
2026}
2027
2035static int aiL_idir( lua_State *L )
2036{
2037 NLUA_MIN_ARGS(1);
2038 vec2 *vec, drift, reference_vector; /* get the position to face */
2039 Pilot* p;
2040 double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
2041 double speedmap;
2042 /*char announcebuffer[255] = " ", announcebuffer2[128];*/
2043
2044 /* Get first parameter, aka what to face. */
2045 p = NULL;
2046 vec = NULL;
2047 if (lua_ispilot(L,1))
2048 p = luaL_validpilot(L,1);
2049 else if (lua_isvector(L,1))
2050 vec = lua_tovector(L,1);
2051 else NLUA_INVALID_PARAMETER(L);
2052
2053 if (vec==NULL) {
2054 if (p == NULL)
2055 return 0; /* Return silently when attempting to face an invalid pilot. */
2056 /* Establish the current pilot velocity and position vectors */
2057 vec2_cset( &drift, VX(p->solid->vel) - VX(cur_pilot->solid->vel), VY(p->solid->vel) - VY(cur_pilot->solid->vel));
2058 /* Establish the in-line coordinate reference */
2059 vec2_cset( &reference_vector, VX(p->solid->pos) - VX(cur_pilot->solid->pos), VY(p->solid->pos) - VY(cur_pilot->solid->pos));
2060 }
2061 else {
2062 /* Establish the current pilot velocity and position vectors */
2063 vec2_cset( &drift, -VX(cur_pilot->solid->vel), -VY(cur_pilot->solid->vel));
2064 /* Establish the in-line coordinate reference */
2065 vec2_cset( &reference_vector, VX(*vec) - VX(cur_pilot->solid->pos), VY(*vec) - VY(cur_pilot->solid->pos));
2066 }
2067
2068 /* Break down the the velocity vectors of both craft into UV coordinates */
2069 vec2_uv(&drift_radial, &drift_azimuthal, &drift, &reference_vector);
2070 heading_offset_azimuth = angle_diff(cur_pilot->solid->dir, VANGLE(reference_vector));
2071
2072 /* now figure out what to do*/
2073 /* are we pointing anywhere inside the correct UV quadrant? */
2074 /* if we're outside the correct UV quadrant, we need to get into it ASAP */
2075 /* Otherwise match velocities and approach*/
2076 if (FABS(heading_offset_azimuth) < M_PI_2) {
2077 /* This indicates we're in the correct plane
2078 * 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
2079 * there is nothing special about the scaling parameter of 200; it can be tuned to get any behavior desired. A lower
2080 * number will give a more dramatic 'lead' */
2081 speedmap = -1*copysign(1 - 1 / (FABS(drift_azimuthal/200) + 1), drift_azimuthal) * M_PI_2;
2082 diff = angle_diff(heading_offset_azimuth, speedmap);
2083
2084 }
2085 /* 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
2086 some special case logic is added to optimize turn time. Reducing this to only the else cases would speed up the operation
2087 but cause the pilot to turn in the less-than-optimal direction sometimes when between 135 and 225 degrees off from the target */
2088 else {
2089 /* signal that we're not in a productive direction for thrusting */
2090 diff = M_PI;
2091 }
2092
2093 /* Return angle in degrees away from target. */
2094 lua_pushnumber(L, diff);
2095 return 1;
2096}
2097
2104static int aiL_drift_facing( lua_State *L )
2105{
2106 double drift = angle_diff(VANGLE(cur_pilot->solid->vel), cur_pilot->solid->dir);
2107 lua_pushnumber(L, drift);
2108 return 1;
2109}
2110
2117static int aiL_brake( lua_State *L )
2118{
2119 int ret = pilot_brake( cur_pilot );
2120
2123
2124 lua_pushboolean(L, ret);
2125 return 1;
2126}
2127
2134static int aiL_getnearestspob( lua_State *L )
2135{
2136 double dist, d;
2137 int i, j;
2138 LuaSpob spob;
2139
2140 /* cycle through spobs */
2141 for (dist=HUGE_VAL, j=-1, i=0; i<array_size(cur_system->spobs); i++) {
2142 if (!spob_hasService(cur_system->spobs[i],SPOB_SERVICE_INHABITED))
2143 continue;
2144 d = vec2_dist( &cur_system->spobs[i]->pos, &cur_pilot->solid->pos );
2145 if ((!areEnemies(cur_pilot->faction,cur_system->spobs[i]->presence.faction)) &&
2146 (d < dist)) { /* closer friendly spob */
2147 j = i;
2148 dist = d;
2149 }
2150 }
2151
2152 /* no friendly spob found */
2153 if (j == -1) return 0;
2154
2155 cur_pilot->nav_spob = j;
2156 spob = cur_system->spobs[j]->id;
2157 lua_pushspob(L, spob);
2158
2159 return 1;
2160}
2161
2169static int aiL_getspobfrompos( lua_State *L )
2170{
2171 vec2 *pos;
2172 double dist, d;
2173 int i, j;
2174 LuaSpob spob;
2175
2176 pos = luaL_checkvector(L,1);
2177
2178 /* cycle through spobs */
2179 for (dist=HUGE_VAL, j=-1, i=0; i<array_size(cur_system->spobs); i++) {
2180 if (!spob_hasService(cur_system->spobs[i],SPOB_SERVICE_INHABITED))
2181 continue;
2182 d = vec2_dist( &cur_system->spobs[i]->pos, pos );
2183 if ((!areEnemies(cur_pilot->faction,cur_system->spobs[i]->presence.faction)) &&
2184 (d < dist)) { /* closer friendly spob */
2185 j = i;
2186 dist = d;
2187 }
2188 }
2189
2190 /* no friendly spob found */
2191 if (j == -1) return 0;
2192
2193 cur_pilot->nav_spob = j;
2194 spob = cur_system->spobs[j]->id;
2195 lua_pushspob(L, spob);
2196
2197 return 1;
2198}
2199
2206static int aiL_getrndspob( lua_State *L )
2207{
2208 LuaSpob spob;
2209 int p;
2210
2211 /* No spobs. */
2212 if (array_size(cur_system->spobs) == 0)
2213 return 0;
2214
2215 /* get a random spob */
2216 p = RNG(0, array_size(cur_system->spobs)-1);
2217
2218 /* Copy the data into a vector */
2219 spob = cur_system->spobs[p]->id;
2220 lua_pushspob(L, spob);
2221
2222 return 1;
2223}
2224
2232static int aiL_getlandspob( lua_State *L )
2233{
2234 int *ind;
2235 int id;
2236 LuaSpob spob;
2237 Spob *p;
2238 int only_friend;
2239
2240 /* If pilot can't land ignore. */
2241 if (pilot_isFlag(cur_pilot, PILOT_NOLAND))
2242 return 0;
2243
2244 /* Check if we should get only friendlies. */
2245 only_friend = lua_toboolean(L, 1);
2246
2247 /* Allocate memory. */
2248 ind = array_create_size( int, array_size(cur_system->spobs) );
2249
2250 /* Copy friendly spob.s */
2251 for (int i=0; i<array_size(cur_system->spobs); i++) {
2252 Spob *pnt = cur_system->spobs[i];
2253
2254 if (!spob_hasService(pnt, SPOB_SERVICE_LAND))
2255 continue;
2256 if (!spob_hasService(pnt, SPOB_SERVICE_INHABITED))
2257 continue;
2258
2259 /* Check conditions. */
2260 if (only_friend && !areAllies( cur_pilot->faction, pnt->presence.faction ))
2261 continue;
2263 continue;
2264
2265 /* Add it. */
2266 array_push_back( &ind, i );
2267 }
2268
2269 /* no spob to land on found */
2270 if (array_size(ind)==0) {
2271 array_free(ind);
2272 return 0;
2273 }
2274
2275 /* we can actually get a random spob now */
2276 id = RNG(0,array_size(ind)-1);
2277 p = cur_system->spobs[ ind[ id ] ];
2278 spob = p->id;
2279 lua_pushspob( L, spob );
2280 cur_pilot->nav_spob = ind[ id ];
2281 array_free(ind);
2282
2283 return 1;
2284}
2285
2293static int aiL_land( lua_State *L )
2294{
2295 Spob *spob;
2296 HookParam hparam;
2297
2298 if (!lua_isnoneornil(L,1)) {
2299 int i;
2300 Spob *pnt = luaL_validspob( L, 1 );
2301
2302 /* Find the spob. */
2303 for (i=0; i < array_size(cur_system->spobs); i++) {
2304 if (cur_system->spobs[i] == pnt) {
2305 break;
2306 }
2307 }
2308 if (i >= array_size(cur_system->spobs)) {
2309 NLUA_ERROR( L, _("Spob '%s' not found in system '%s'"), pnt->name, cur_system->name );
2310 return 0;
2311 }
2312
2313 cur_pilot->nav_spob = i;
2314 }
2315
2316 if (cur_pilot->nav_spob < 0) {
2317 NLUA_ERROR( L, _("Pilot '%s' (ai '%s') has no land target"), cur_pilot->name, cur_pilot->ai->name );
2318 return 0;
2319 }
2320
2321 /* Get spob. */
2322 spob = cur_system->spobs[ cur_pilot->nav_spob ];
2323
2324 /* Check landability. */
2325 if ((spob->lua_can_land==LUA_NOREF) && !spob_hasService(spob,SPOB_SERVICE_LAND)) { /* Basic services */
2326 lua_pushboolean(L,0);
2327 return 1;
2328 }
2329 /* TODO can_land is player-specific, we need to implement this on a pilot level...
2330 if ((!pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL) && !spob->can_land)) {
2331 lua_pushboolean(L,0);
2332 return 1;
2333 }
2334 */
2335
2336 /* Check landing functionality. */
2337 if (pilot_isFlag(cur_pilot, PILOT_NOLAND)) {
2338 lua_pushboolean(L,0);
2339 return 1;
2340 }
2341
2342 /* Check distance. */
2343 if (vec2_dist2(&cur_pilot->solid->pos,&spob->pos) > pow2(spob->radius)) {
2344 lua_pushboolean(L,0);
2345 return 1;
2346 }
2347
2348 /* Check velocity. */
2349 if (vec2_odist2( &cur_pilot->solid->vel ) > pow2(MAX_HYPERSPACE_VEL)) {
2350 lua_pushboolean(L,0);
2351 return 1;
2352 }
2353
2354 if (spob->lua_land == LUA_NOREF) {
2355 cur_pilot->landing_delay = PILOT_LANDING_DELAY * cur_pilot->ship->dt_default;
2357 pilot_setFlag( cur_pilot, PILOT_LANDING );
2358 }
2359 else {
2360 lua_rawgeti(naevL, LUA_REGISTRYINDEX, spob->lua_land); /* f */
2361 lua_pushspob( naevL, spob_index(spob) );
2362 lua_pushpilot( naevL, cur_pilot->id );
2363 if (nlua_pcall( spob->lua_env, 2, 0 )) {
2364 WARN(_("Spob '%s' failed to run '%s':\n%s"), spob->name, "land", lua_tostring(naevL,-1));
2365 lua_pop(naevL,1);
2366 }
2367 }
2368
2369 hparam.type = HOOK_PARAM_SPOB;
2370 hparam.u.la = spob->id;
2371
2372 pilot_runHookParam( cur_pilot, PILOT_HOOK_LAND, &hparam, 1 );
2373 lua_pushboolean(L,1);
2374 return 1;
2375}
2376
2384static int aiL_hyperspace( lua_State *L )
2385{
2386 int dist;
2387
2388 /* Find the target jump. */
2389 if (!lua_isnoneornil(L,1)) {
2390 JumpPoint *jp = luaL_validjump( L, 1 );
2391 LuaJump *lj = luaL_checkjump( L, 1 );
2392 if (lj->srcid != cur_system->id)
2393 NLUA_ERROR(L, _("Jump point must be in current system."));
2394 cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2395 }
2396
2398 if (dist == 0.) {
2401 return 0;
2402 }
2403
2404 lua_pushnumber(L,dist);
2405 return 1;
2406}
2407
2415static int aiL_sethyptarget( lua_State *L )
2416{
2417 JumpPoint *jp;
2418 LuaJump *lj;
2419 vec2 vec;
2420 double a, rad;
2421
2422 lj = luaL_checkjump( L, 1 );
2423 jp = luaL_validjump( L, 1 );
2424
2425 if (lj->srcid != cur_system->id)
2426 NLUA_ERROR(L, _("Jump point must be in current system."));
2427
2428 /* Copy vector. */
2429 vec = jp->pos;
2430
2431 /* Introduce some error. */
2432 a = RNGF() * M_PI * 2.;
2433 rad = RNGF() * 0.5 * jp->radius;
2434 vec2_cadd( &vec, rad*cos(a), rad*sin(a) );
2435
2436 /* Set up target. */
2437 cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2438
2439 /* Return vector. */
2440 lua_pushvector( L, vec );
2441
2442 return 1;
2443}
2444
2451static int aiL_nearhyptarget( lua_State *L )
2452{
2453 JumpPoint *jp;
2454 double mindist, dist;
2455 LuaJump lj;
2456
2457 /* Find nearest jump .*/
2458 mindist = INFINITY;
2459 jp = NULL;
2460 for (int i=0; i < array_size(cur_system->jumps); i++) {
2461 JumpPoint *jiter = &cur_system->jumps[i];
2462 int useshidden = faction_usesHiddenJumps( cur_pilot->faction );
2463
2464 /* We want only standard jump points to be used. */
2465 if ((!useshidden && jp_isFlag(jiter, JP_HIDDEN)) || jp_isFlag(jiter, JP_EXITONLY))
2466 continue;
2467
2468 /* TODO should they try to use systems only with their faction? */
2469
2470 /* Get nearest distance. */
2471 dist = vec2_dist2( &cur_pilot->solid->pos, &jiter->pos );
2472 if (dist < mindist) {
2473 jp = jiter;
2474 mindist = dist;
2475 }
2476 }
2477 /* None available. */
2478 if (jp == NULL)
2479 return 0;
2480
2481 lj.destid = jp->targetid;
2482 lj.srcid = cur_system->id;
2483
2484 /* Return Jump. */
2485 lua_pushjump( L, lj );
2486 return 1;
2487}
2488
2495static int aiL_rndhyptarget( lua_State *L )
2496{
2497 JumpPoint **jumps;
2498 int r, useshidden;
2499 int *id;
2500 LuaJump lj;
2501
2502 /* No jumps in the system. */
2503 if (array_size(cur_system->jumps) == 0)
2504 return 0;
2505
2506 useshidden = faction_usesHiddenJumps( cur_pilot->faction );
2507
2508 /* Find usable jump points. */
2509 jumps = array_create_size( JumpPoint*, array_size(cur_system->jumps) );
2510 id = array_create_size( int, array_size(cur_system->jumps) );
2511 for (int i=0; i < array_size(cur_system->jumps); i++) {
2512 JumpPoint *jiter = &cur_system->jumps[i];
2513
2514 /* We want only standard jump points to be used. */
2515 if ((!useshidden && jp_isFlag(jiter, JP_HIDDEN)) || jp_isFlag(jiter, JP_EXITONLY))
2516 continue;
2517
2518 /* Only jump if there is presence there. */
2519 if (system_getPresence( jiter->target, cur_pilot->faction ) <= 0.)
2520 continue;
2521
2522 array_push_back( &id, i );
2523 array_push_back( &jumps, jiter );
2524 }
2525
2526 /* Try to be more lax. */
2527 if (array_size(jumps) <= 0) {
2528 for (int i=0; i < array_size(cur_system->jumps); i++) {
2529 JumpPoint *jiter = &cur_system->jumps[i];
2530
2531 /* We want only standard jump points to be used. */
2532 if ((!useshidden && jp_isFlag(jiter, JP_HIDDEN)) || jp_isFlag(jiter, JP_EXITONLY))
2533 continue;
2534
2535 array_push_back( &id, i );
2536 array_push_back( &jumps, jiter );
2537 }
2538 }
2539
2540 if (array_size(jumps) <= 0) {
2541 WARN(_("Pilot '%s' can't find jump to leave system!"), cur_pilot->name);
2542 return 0;
2543 }
2544
2545 /* Choose random jump point. */
2546 r = RNG( 0, MAX( array_size(jumps)-1, 0) );
2547
2548 lj.destid = jumps[r]->targetid;
2549 lj.srcid = cur_system->id;
2550
2551 /* Clean up. */
2552 array_free(jumps);
2553 array_free(id);
2554
2555 /* Return Jump. */
2556 lua_pushjump( L, lj );
2557 return 1;
2558}
2559
2566static int aiL_relvel( lua_State *L )
2567{
2568 double dot, mod;
2569 vec2 vv, pv;
2570 int absolute;
2571 Pilot *p = luaL_validpilot(L,1);
2572
2573 if (lua_gettop(L) > 1)
2574 absolute = lua_toboolean(L,2);
2575 else
2576 absolute = 0;
2577
2578 /* Get the projection of target on current velocity. */
2579 if (absolute == 0)
2580 vec2_cset( &vv, p->solid->vel.x - cur_pilot->solid->vel.x,
2581 p->solid->vel.y - cur_pilot->solid->vel.y );
2582 else
2583 vec2_cset( &vv, p->solid->vel.x, p->solid->vel.y);
2584
2585 vec2_cset( &pv, p->solid->pos.x - cur_pilot->solid->pos.x,
2586 p->solid->pos.y - cur_pilot->solid->pos.y );
2587 dot = vec2_dot( &pv, &vv );
2588 mod = MAX(VMOD(pv), 1.); /* Avoid /0. */
2589
2590 lua_pushnumber(L, dot / mod );
2591 return 1;
2592}
2593
2607static int aiL_follow_accurate( lua_State *L )
2608{
2609 vec2 point, cons, goal, pv;
2610 double radius, angle, Kp, Kd, angle2;
2611 Pilot *p, *target;
2612 const char *method;
2613
2614 p = cur_pilot;
2615 target = luaL_validpilot(L,1);
2616 radius = luaL_checknumber(L,2);
2617 angle = luaL_checknumber(L,3);
2618 Kp = luaL_checknumber(L,4);
2619 Kd = luaL_checknumber(L,5);
2620
2621 if (lua_isnoneornil(L, 6))
2622 method = "velocity";
2623 else
2624 method = luaL_checkstring(L,6);
2625
2626 if (strcmp( method, "absolute" ) == 0)
2627 angle2 = angle;
2628 else if (strcmp( method, "keepangle" ) == 0) {
2629 vec2_cset( &pv, p->solid->pos.x - target->solid->pos.x,
2630 p->solid->pos.y - target->solid->pos.y );
2631 angle2 = VANGLE(pv);
2632 }
2633 else /* method == "velocity" */
2634 angle2 = angle + VANGLE( target->solid->vel );
2635
2636 vec2_cset( &point, VX(target->solid->pos) + radius * cos(angle2),
2637 VY(target->solid->pos) + radius * sin(angle2) );
2638
2639 /* Compute the direction using a pd controller */
2640 vec2_cset( &cons, (point.x - p->solid->pos.x) * Kp +
2641 (target->solid->vel.x - p->solid->vel.x) *Kd,
2642 (point.y - p->solid->pos.y) * Kp +
2643 (target->solid->vel.y - p->solid->vel.y) *Kd );
2644
2645 vec2_cset( &goal, cons.x + p->solid->pos.x, cons.y + p->solid->pos.y);
2646
2647 /* Push info */
2648 lua_pushvector( L, goal );
2649
2650 return 1;
2651
2652}
2653
2666static int aiL_face_accurate( lua_State *L )
2667{
2668 vec2 point, cons, goal, *pos, *vel;
2669 double radius, angle, Kp, Kd;
2670 Pilot *p = cur_pilot;
2671 pos = lua_tovector(L,1);
2672 vel = lua_tovector(L,2);
2673 radius = luaL_checknumber(L,3);
2674 angle = luaL_checknumber(L,4);
2675 Kp = luaL_checknumber(L,5);
2676 Kd = luaL_checknumber(L,6);
2677
2678 vec2_cset( &point, pos->x + radius * cos(angle),
2679 pos->y + radius * sin(angle) );
2680
2681 /* Compute the direction using a pd controller */
2682 vec2_cset( &cons, (point.x - p->solid->pos.x) * Kp +
2683 (vel->x - p->solid->vel.x) *Kd,
2684 (point.y - p->solid->pos.y) * Kp +
2685 (vel->y - p->solid->vel.y) *Kd );
2686
2687 vec2_cset( &goal, cons.x + p->solid->pos.x, cons.y + p->solid->pos.y);
2688
2689 /* Push info */
2690 lua_pushvector( L, goal );
2691
2692 return 1;
2693
2694}
2695
2701static int aiL_stop( lua_State *L )
2702{
2703 (void) L; /* avoid gcc warning */
2704
2705 if (VMOD(cur_pilot->solid->vel) < MIN_VEL_ERR)
2706 vec2_pset( &cur_pilot->solid->vel, 0., 0. );
2707
2708 return 0;
2709}
2710
2717static int aiL_dock( lua_State *L )
2718{
2719 /* Target is another ship. */
2720 Pilot *p = luaL_validpilot(L,1);
2722 return 0;
2723}
2724
2731static int aiL_combat( lua_State *L )
2732{
2733 if (lua_gettop(L) > 0) {
2734 int i = lua_toboolean(L,1);
2735 if (i==1)
2736 pilot_setFlag(cur_pilot, PILOT_COMBAT);
2737 else if (i==0)
2738 pilot_rmFlag(cur_pilot, PILOT_COMBAT);
2739 }
2740 else
2741 pilot_setFlag(cur_pilot, PILOT_COMBAT);
2742
2743 return 0;
2744}
2745
2752static int aiL_settarget( lua_State *L )
2753{
2754 Pilot *p = luaL_validpilot(L,1);
2755 pilot_setTarget( cur_pilot, p->id );
2756 return 0;
2757}
2758
2766static int aiL_setasterotarget( lua_State *L )
2767{
2769
2770 /* Set the target asteroid. */
2771 cur_pilot->nav_anchor = la->parent;
2772 cur_pilot->nav_asteroid = la->id;
2773
2774 /* Untarget pilot. */
2776 cur_pilot->ptarget = NULL;
2777
2778 return 0;
2779}
2780
2788static int aiL_getGatherable( lua_State *L )
2789{
2790 int i;
2791 double rad;
2792
2793 if ((lua_gettop(L) < 1) || lua_isnil(L,1))
2794 rad = INFINITY;
2795 else
2796 rad = lua_tonumber(L,1);
2797
2799
2800 if (i != -1)
2801 lua_pushnumber(L,i);
2802 else
2803 lua_pushnil(L);
2804
2805 return 1;
2806}
2807
2816static int aiL_gatherablePos( lua_State *L )
2817{
2818 int i, did;
2819 vec2 pos, vel;
2820
2821 i = lua_tointeger(L,1);
2822
2823 did = gatherable_getPos( &pos, &vel, i );
2824
2825 if (did == 0) /* No gatherable matching this ID. */
2826 return 0;
2827
2828 lua_pushvector(L, pos);
2829 lua_pushvector(L, vel);
2830
2831 return 2;
2832}
2833
2841static int aiL_weapSet( lua_State *L )
2842{
2843 Pilot* p;
2844 int id, type;
2845 PilotWeaponSet *ws;
2846
2847 p = cur_pilot;
2848 id = lua_tonumber(L,1);
2849
2850 if (lua_gettop(L) > 1)
2851 type = lua_toboolean(L,2);
2852 else
2853 type = 1;
2854
2855 ws = &p->weapon_sets[id];
2856
2857 if (ws->type == WEAPSET_TYPE_ACTIVE) {
2858 /* Check if outfit is on */
2859 int on = 1;
2860 int l = array_size(ws->slots);
2861 for (int i=0; i<l; i++) {
2862 if (p->outfits[ ws->slots[i].slotid ]->state == PILOT_OUTFIT_OFF) {
2863 on = 0;
2864 break;
2865 }
2866 }
2867
2868 /* Active weapon sets only care about keypresses. */
2869 /* activate */
2870 if (type && !on)
2871 pilot_weapSetPress(p, id, +1 );
2872 /* deactivate */
2873 if (!type && on)
2874 pilot_weapSetPress(p, id, +1 );
2875 }
2876 else {
2877 /* weapset type is weapon or change */
2878 if (type)
2879 pilot_weapSetPress( cur_pilot, id, +1 );
2880 else
2881 pilot_weapSetPress( cur_pilot, id, -1 );
2882 }
2883 return 0;
2884}
2885
2892static int aiL_hascannons( lua_State *L )
2893{
2894 lua_pushboolean( L, cur_pilot->ncannons > 0 );
2895 return 1;
2896}
2897
2904static int aiL_hasturrets( lua_State *L )
2905{
2906 lua_pushboolean( L, cur_pilot->nturrets > 0 );
2907 return 1;
2908}
2909
2916static int aiL_hasfighterbays( lua_State *L )
2917{
2918 lua_pushboolean( L, cur_pilot->nfighterbays > 0 );
2919 return 1;
2920}
2921
2928static int aiL_hasafterburner( lua_State *L )
2929{
2930 lua_pushboolean( L, cur_pilot->nafterburners > 0 );
2931 return 1;
2932}
2933
2940static int aiL_shoot( lua_State *L )
2941{
2942 /* Cooldown is similar to a ship being disabled, but the AI continues to
2943 * think during cooldown, and thus must not be allowed to fire weapons. */
2944 if (pilot_isFlag(cur_pilot, PILOT_COOLDOWN))
2945 return 0;
2946
2947 if (lua_toboolean(L,1))
2949 else
2951 return 0;
2952}
2953
2960static int aiL_getenemy( lua_State *L )
2961{
2962 unsigned int id;
2963
2965
2966 if (id==0) /* No enemy found */
2967 return 0;
2968
2969 lua_pushpilot(L, id);
2970
2971 return 1;
2972}
2973
2982static int aiL_getenemy_size( lua_State *L )
2983{
2984 unsigned int id;
2985 unsigned int LB, UB;
2986
2987 NLUA_MIN_ARGS(2);
2988
2989 LB = luaL_checklong(L,1);
2990 UB = luaL_checklong(L,2);
2991
2992 if (LB > UB) {
2993 NLUA_ERROR(L, _("Invalid Bounds"));
2994 return 0;
2995 }
2996
2997 id = pilot_getNearestEnemy_size( cur_pilot, LB, UB );
2998
2999 if (id==0) /* No enemy found */
3000 return 0;
3001
3002 lua_pushpilot(L, id);
3003 return 1;
3004}
3005
3016static int aiL_getenemy_heuristic( lua_State *L )
3017{
3018 double mass_factor = luaL_checknumber(L,1);
3019 double health_factor = luaL_checknumber(L,2);
3020 double damage_factor = luaL_checknumber(L,3);
3021 double range_factor = luaL_checknumber(L,4);
3022
3023 unsigned int id = pilot_getNearestEnemy_heuristic( cur_pilot,
3024 mass_factor, health_factor, damage_factor, 1./range_factor );
3025
3026 if (id==0) /* No enemy found */
3027 return 0;
3028
3029 lua_pushpilot(L, id);
3030 return 1;
3031}
3032
3039static int aiL_hostile( lua_State *L )
3040{
3041 Pilot *p = luaL_validpilot(L,1);
3042
3043 if (pilot_isWithPlayer(p))
3045
3046 return 0;
3047}
3048
3057static int aiL_getweaprange( lua_State *L )
3058{
3059 int id = luaL_optinteger( L, 1, cur_pilot->active_set );
3060 int level = luaL_optinteger( L, 2, -1 );
3061 lua_pushnumber(L, pilot_weapSetRange( cur_pilot, id, level ) );
3062 return 1;
3063}
3064
3073static int aiL_getweapspeed( lua_State *L )
3074{
3075 int id = luaL_optinteger( L, 1, cur_pilot->active_set );
3076 int level = luaL_optinteger( L, 2, -1 );
3077 lua_pushnumber(L, pilot_weapSetSpeed( cur_pilot, id, level ) );
3078 return 1;
3079}
3080
3089static int aiL_getweapammo( lua_State *L )
3090{
3091 int id = luaL_optinteger( L, 1, cur_pilot->active_set );
3092 int level = luaL_optinteger( L, 2, -1 );
3093 lua_pushnumber(L, pilot_weapSetAmmo( cur_pilot, id, level ) );
3094 return 1;
3095}
3096
3104static int aiL_canboard( lua_State *L )
3105{
3106 Pilot *p = luaL_validpilot(L,1);
3107
3108 /* Must be disabled. */
3109 if (!pilot_isDisabled(p)) {
3110 lua_pushboolean(L, 0);
3111 return 1;
3112 }
3113
3114 /* Check if can be boarded. */
3115 lua_pushboolean(L, !pilot_isFlag(p, PILOT_BOARDED));
3116 return 1;
3117}
3118
3126static int aiL_relsize( lua_State *L )
3127{
3128 Pilot *p = luaL_validpilot(L,1);
3129 lua_pushnumber(L, pilot_relsize(cur_pilot, p));
3130 return 1;
3131}
3132
3140static int aiL_reldps( lua_State *L )
3141{
3142 Pilot *p = luaL_validpilot(L,1);
3143 lua_pushnumber(L, pilot_reldps(cur_pilot, p));
3144 return 1;
3145}
3146
3154static int aiL_relhp( lua_State *L )
3155{
3156 Pilot *p = luaL_validpilot(L,1);
3157 lua_pushnumber(L, pilot_relhp(cur_pilot, p));
3158 return 1;
3159}
3160
3167static int aiL_board( lua_State *L )
3168{
3169 lua_pushboolean(L, pilot_board( cur_pilot ));
3170 return 1;
3171}
3172
3179static int aiL_refuel( lua_State *L )
3180{
3181 lua_pushboolean(L,pilot_refuelStart(cur_pilot));
3182 return 1;
3183}
3184
3192static int aiL_settimer( lua_State *L )
3193{
3194 int n = luaL_checkint(L,1);
3195 /* Set timer. */
3196 cur_pilot->timer[n] = luaL_optnumber(L,2,0.);
3197 return 0;
3198}
3199
3208static int aiL_timeup( lua_State *L )
3209{
3210 int n = luaL_checkint(L,1);
3211 lua_pushboolean(L, cur_pilot->timer[n] < 0.);
3212 return 1;
3213}
3214
3221static int aiL_set_shoot_indicator( lua_State *L )
3222{
3223 cur_pilot->shoot_indicator = lua_toboolean(L,1);
3224 return 0;
3225}
3226
3233static int aiL_shoot_indicator( lua_State *L )
3234{
3235 lua_pushboolean(L, cur_pilot->shoot_indicator);
3236 return 1;
3237}
3238
3245static int aiL_distress( lua_State *L )
3246{
3247 if (lua_isstring(L,1))
3248 snprintf( aiL_distressmsg, sizeof(aiL_distressmsg), "%s", lua_tostring(L,1) );
3249 else if (lua_isnil(L,1))
3250 aiL_distressmsg[0] = '\0';
3251 else
3252 NLUA_INVALID_PARAMETER(L);
3253
3254 /* Set flag because code isn't reentrant. */
3256
3257 return 0;
3258}
3259
3266static int aiL_getBoss( lua_State *L )
3267{
3268 unsigned int id;
3269
3270 id = pilot_getBoss( cur_pilot );
3271
3272 if (id==0) /* No boss found */
3273 return 0;
3274
3275 lua_pushpilot(L, id);
3276
3277 return 1;
3278}
3279
3286static int aiL_credits( lua_State *L )
3287{
3289 /*NLUA_ERROR(L, "This function must be called in \"create\" only.");*/
3290 return 0;
3291 }
3292
3293 cur_pilot->credits = luaL_checklong(L,1);
3294
3295 return 0;
3296}
3297
3304static int aiL_messages( lua_State *L )
3305{
3306 lua_rawgeti(L, LUA_REGISTRYINDEX, cur_pilot->messages);
3307 lua_newtable(L);
3308 lua_rawseti(L, LUA_REGISTRYINDEX, cur_pilot->messages);
3309 return 1;
3310}
3311
3319static int aiL_stealth( lua_State *L )
3320{
3321 int b = 1;
3322 if (lua_gettop(L)>0)
3323 b = lua_toboolean(L,1);
3324
3325 if (!b) {
3327 lua_pushboolean(L,1); /* always succeeds */
3328 return 1;
3329 }
3330
3331 lua_pushboolean(L, pilot_stealth( cur_pilot ));
3332 return 1;
3333}
Task * ai_newtask(lua_State *L, Pilot *p, const char *func, int subtask, int pos)
Creates a new AI task.
Definition: ai.c:1051
static double pilot_turn
Definition: ai.c:338
static nlua_env equip_env
Definition: ai.c:115
static AI_Profile * profiles
Definition: ai.c:114
static void ai_setMemory(void)
Sets the cur_pilot's ai.
Definition: ai.c:393
static int aiL_status
Definition: ai.c:347
void ai_getDistress(Pilot *p, const Pilot *distressed, const Pilot *attacker)
Sends a distress signal to a pilot.
Definition: ai.c:968
Task * ai_curTask(Pilot *pilot)
Gets the current running task.
Definition: ai.c:381
void ai_refuel(Pilot *refueler, unsigned int target)
Has a pilot attempt to refuel the other.
Definition: ai.c:937
static const luaL_Reg aiL_methods[]
Definition: ai.c:238
static int ai_loadProfile(AI_Profile *prof, const char *filename)
Initializes an AI_Profile and adds it to the stack.
Definition: ai.c:608
static void ai_create(Pilot *pilot)
Runs the create() function in the pilot.
Definition: ai.c:1008
void ai_cleartasks(Pilot *p)
Clears the pilot's tasks.
Definition: ai.c:485
void ai_freetask(Task *t)
Frees an AI task.
Definition: ai.c:1108
static double pilot_acc
Definition: ai.c:337
static Task * ai_createTask(lua_State *L, int subtask)
Creates a new task based on stack information.
Definition: ai.c:1135
#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:417
static char aiL_distressmsg[STRMAX_SHORT]
Definition: ai.c:340
void ai_attacked(Pilot *attacked, const unsigned int attacker, double dmg)
Triggers the attacked() function in the pilot's AI.
Definition: ai.c:840
#define ai_isFlag(f)
Definition: ai.c:105
void ai_destroy(Pilot *p)
Destroys the ai part of the pilot.
Definition: ai.c:498
Pilot * cur_pilot
Definition: ai.c:336
void ai_hail(Pilot *recipient)
Triggers the hail() function in the pilot's AI.
Definition: ai.c:906
#define ai_setFlag(f)
Definition: ai.c:104
static int ai_tasktarget(lua_State *L, Task *t)
Pushes a task target.
Definition: ai.c:1158
void ai_discovered(Pilot *discovered)
Triggers the discovered() function in the pilot's AI.
Definition: ai.c:875
void ai_exit(void)
Cleans up global AI.
Definition: ai.c:695
static int ai_loadEquip(void)
Loads the equipment selector script.
Definition: ai.c:574
void ai_setPilot(Pilot *p)
Sets the pilot for further AI calls.
Definition: ai.c:405
void ai_think(Pilot *pilot, const double dt)
Heart of the AI, brains of the pilot.
Definition: ai.c:715
void ai_init(Pilot *p)
Initializes the AI.
Definition: ai.c:823
#define AI_DISTRESS
Definition: ai.c:109
#define AI_STATUS_NORMAL
Definition: ai.c:345
static void ai_taskGC(Pilot *pilot)
Runs the garbage collector on the pilot's tasks.
Definition: ai.c:354
int ai_pinit(Pilot *p, const char *ai)
Initializes the pilot in the ai.
Definition: ai.c:434
#define AI_STATUS_CREATE
Definition: ai.c:346
static int pilot_flags
Definition: ai.c:339
int ai_load(void)
Initializes the AI stuff which is basically Lua.
Definition: ai.c:525
AI_Profile * ai_getProfile(const char *name)
Gets the AI_Profile by name.
Definition: ai.c:683
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:208
nlua_env faction_getEquipper(int f)
Gets the equipper state associated to the faction scheduler.
Definition: faction.c:738
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition: faction.c:1197
int faction_usesHiddenJumps(int f)
Checks to see if a faction uses hidden jumps.
Definition: faction.c:1838
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition: faction.c:1222
int gatherable_getPos(vec2 *pos, vec2 *vel, int id)
Returns the position and velocity of a gatherable.
Definition: gatherable.c:146
int gatherable_getClosest(vec2 pos, double rad)
Gets the closest gatherable from a given position, within a given radius.
Definition: gatherable.c:122
static int aiL_ismaxvel(lua_State *L)
Checks to see if pilot is at maximum velocity.
Definition: ai.c:1556
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:2607
static int aiL_face_accurate(lua_State *L)
Computes the point to face in order to follow a moving object.
Definition: ai.c:2666
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:3154
static int aiL_sethyptarget(lua_State *L)
Sets hyperspace target.
Definition: ai.c:2415
static int aiL_getspobfrompos(lua_State *L)
Get the nearest friendly spob to a given position.
Definition: ai.c:2169
static int aiL_getlandspob(lua_State *L)
Get a random friendly spob.
Definition: ai.c:2232
static int aiL_accel(lua_State *L)
Starts accelerating the pilot.
Definition: ai.c:1665
static int aiL_weapSet(lua_State *L)
Sets the active weapon set, fires another weapon set or activate an outfit.
Definition: ai.c:2841
static int aiL_poptask(lua_State *L)
Pops the current running task.
Definition: ai.c:1195
static int aiL_iface(lua_State *L)
Maintains an intercept pursuit course.
Definition: ai.c:1905
static int aiL_settarget(lua_State *L)
Sets the pilot's target.
Definition: ai.c:2752
static int aiL_nearhyptarget(lua_State *L)
Gets the nearest hyperspace target.
Definition: ai.c:2451
static int aiL_getrndspob(lua_State *L)
Get a random spob.
Definition: ai.c:2206
static int aiL_pilot(lua_State *L)
Gets the AI's pilot. Lua return parameter: Pilot The AI's pilot.
Definition: ai.c:1321
static int aiL_minbrakedist(lua_State *L)
Gets the minimum braking distance.
Definition: ai.c:1485
static int aiL_gatherablePos(lua_State *L)
Gets the pos and vel of a given gatherable.
Definition: ai.c:2816
static int aiL_getenemy_heuristic(lua_State *L)
Gets the nearest enemy within specified heuristic.
Definition: ai.c:3016
static int aiL_relvel(lua_State *L)
Gets the relative velocity of a pilot.
Definition: ai.c:2566
static int aiL_haslockon(lua_State *L)
Checks to see if pilot has a missile lockon.
Definition: ai.c:1627
static int aiL_refuel(lua_State *L)
Attempts to refuel the pilot's target.
Definition: ai.c:3179
static int aiL_getdistance2(lua_State *L)
Gets the squared distance from the pointer.
Definition: ai.c:1418
static int aiL_getdistance(lua_State *L)
Gets the distance from the pointer.
Definition: ai.c:1391
static int aiL_instantJump(lua_State *L)
Checks to see if pilot can instant jump.
Definition: ai.c:1544
static int aiL_land(lua_State *L)
Lands on a spob.
Definition: ai.c:2293
static int aiL_isenemy(lua_State *L)
Checks to see if target is an enemy.
Definition: ai.c:1583
static int aiL_messages(lua_State *L)
Returns and clears the pilots message queue.
Definition: ai.c:3304
static int aiL_turn(lua_State *L)
Starts turning the pilot.
Definition: ai.c:1678
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:1288
static int aiL_rndhyptarget(lua_State *L)
Gets a random hyperspace target.
Definition: ai.c:2495
static int aiL_dir(lua_State *L)
calculates the direction that the target is relative to the current pilot facing.
Definition: ai.c:1993
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:1787
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:1230
static int aiL_hasturrets(lua_State *L)
Does the pilot have turrets?
Definition: ai.c:2904
static int aiL_isally(lua_State *L)
Checks to see if target is an ally.
Definition: ai.c:1605
static int aiL_relsize(lua_State *L)
Gets the relative size (ship mass) between the current pilot and the specified target.
Definition: ai.c:3126
static int aiL_aim(lua_State *L)
Aims at a pilot, trying to hit it rather than move to it.
Definition: ai.c:1876
static int aiL_credits(lua_State *L)
Sets the pilots credits. Only call in create().
Definition: ai.c:3286
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:3140
static int aiL_canboard(lua_State *L)
Checks to see if pilot can board the target.
Definition: ai.c:3104
static int aiL_idir(lua_State *L)
calculates angle between pilot facing and intercept-course to target.
Definition: ai.c:2035
static int aiL_getnearestpilot(lua_State *L)
gets the nearest pilot to the current pilot
Definition: ai.c:1357
static int aiL_hostile(lua_State *L)
Sets the enemy hostile (basically notifies of an impending attack).
Definition: ai.c:3039
static int aiL_hascannons(lua_State *L)
Does the pilot have cannons?
Definition: ai.c:2892
static int aiL_getBoss(lua_State *L)
Picks a pilot that will command the current pilot.
Definition: ai.c:3266
static int aiL_isbribed(lua_State *L)
Checks to see if target has bribed pilot.
Definition: ai.c:1531
static int aiL_getGatherable(lua_State *L)
Gets the closest gatherable within a radius.
Definition: ai.c:2788
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:1213
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:1305
static int aiL_hasprojectile(lua_State *L)
Checks to see if pilot has a projectile after him.
Definition: ai.c:1640
static int aiL_getweapammo(lua_State *L)
Gets the ammo of a weapon.
Definition: ai.c:3089
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:2104
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:3233
static int aiL_combat(lua_State *L)
Sets the combat flag.
Definition: ai.c:2731
static int aiL_distress(lua_State *L)
Sends a distress signal.
Definition: ai.c:3245
static int aiL_settimer(lua_State *L)
Sets a timer.
Definition: ai.c:3192
static int aiL_shoot(lua_State *L)
Makes the pilot shoot.
Definition: ai.c:2940
static int aiL_setasterotarget(lua_State *L)
Sets the pilot's asteroid target.
Definition: ai.c:2766
static int aiL_getweapspeed(lua_State *L)
Gets the speed of a weapon.
Definition: ai.c:3073
static int aiL_getrndpilot(lua_State *L)
Gets a random pilot in the system. Lua return parameter: Pilot|nil.
Definition: ai.c:1333
static int aiL_set_shoot_indicator(lua_State *L)
Set the seeker shoot indicator.
Definition: ai.c:3221
static int aiL_getenemy(lua_State *L)
Gets the nearest enemy.
Definition: ai.c:2960
static int aiL_stealth(lua_State *L)
Tries to stealth or destealth the pilot.
Definition: ai.c:3319
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:1248
static int aiL_stop(lua_State *L)
Completely stops the pilot if it is below minimum vel error (no insta-stops).
Definition: ai.c:2701
static int aiL_getflybydistance(lua_State *L)
Gets the distance from the pointer perpendicular to the current pilot's flight vector.
Definition: ai.c:1445
static int aiL_popsubtask(lua_State *L)
Pops the current running task.
Definition: ai.c:1259
static int aiL_face(lua_State *L)
Faces the target.
Definition: ai.c:1697
static int aiL_timeup(lua_State *L)
Checks a timer.
Definition: ai.c:3208
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:1185
static int aiL_scandone(lua_State *L)
Checks to see if pilot has finished scanning their target.
Definition: ai.c:1653
static int aiL_getweaprange(lua_State *L)
Gets the range of a weapon.
Definition: ai.c:3057
static int aiL_dock(lua_State *L)
Docks the ship.
Definition: ai.c:2717
static int aiL_getenemy_size(lua_State *L)
Gets the nearest enemy within specified size bounds.
Definition: ai.c:2982
static int aiL_hyperspace(lua_State *L)
Tries to enter hyperspace.
Definition: ai.c:2384
static int aiL_hasafterburner(lua_State *L)
Does the pilot have afterburners?
Definition: ai.c:2928
static int aiL_board(lua_State *L)
Attempts to board the pilot's target.
Definition: ai.c:3167
static int aiL_hasfighterbays(lua_State *L)
Does the pilot have fighter bays?
Definition: ai.c:2916
static int aiL_brake(lua_State *L)
Brakes the pilot.
Definition: ai.c:2117
static int aiL_getnearestspob(lua_State *L)
Get the nearest friendly spob to the pilot.
Definition: ai.c:2134
static int aiL_isstopped(lua_State *L)
Checks to see if pilot is stopped.
Definition: ai.c:1570
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition: naev.c:549
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:366
static char buf[NEWS_MAX_LENGTH]
Definition: news.c:45
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition: nlua.c:760
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:873
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.
Definition: nlua_asteroid.c:95
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:100
LuaJump * lua_pushjump(lua_State *L, LuaJump jump)
Pushes a jump on the stack.
Definition: nlua_jump.c:182
JumpPoint * luaL_validjump(lua_State *L, int ind)
Gets a jump directly.
Definition: nlua_jump.c:170
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition: nlua_pilot.c:495
Pilot * luaL_validpilot(lua_State *L, int ind)
Makes sure the pilot is valid or raises a Lua error.
Definition: nlua_pilot.c:478
int lua_ispilot(lua_State *L, int ind)
Checks to see if ind is a pilot.
Definition: nlua_pilot.c:510
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition: nlua_spob.c:192
Spob * luaL_validspob(lua_State *L, int ind)
Gets a spob directly.
Definition: nlua_spob.c:164
int lua_isvector(lua_State *L, int ind)
Checks to see if ind is a vector.
Definition: nlua_vec2.c:155
vec2 * luaL_checkvector(lua_State *L, int ind)
Gets vector at index making sure type is valid.
Definition: nlua_vec2.c:124
vec2 * lua_tovector(lua_State *L, int ind)
Represents a 2D vector in Lua.
Definition: nlua_vec2.c:113
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition: nlua_vec2.c:139
unsigned int pilot_getNearestEnemy_size(const Pilot *p, double target_mass_LB, double target_mass_UB)
Gets the nearest enemy to the pilot closest to the pilot whose mass is between LB and UB.
Definition: pilot.c:341
void pilot_setThrust(Pilot *p, double thrust)
Sets the pilot's thrust.
Definition: pilot.c:632
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition: pilot.c:651
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:3904
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:3804
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:3883
unsigned int pilot_getNearestEnemy(const Pilot *p)
Gets the nearest enemy to the pilot.
Definition: pilot.c:312
void pilot_setTurn(Pilot *p, double turn)
Sets the pilot's turn.
Definition: pilot.c:640
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition: pilot.c:681
int pilot_refuelStart(Pilot *p)
Attempts to start refueling the pilot's target.
Definition: pilot.c:2794
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition: pilot.c:1111
static Pilot ** pilot_stack
Definition: pilot.c:57
void pilot_distress(Pilot *p, Pilot *attacker, const char *msg)
Has the pilot broadcast a distress signal.
Definition: pilot.c:1182
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition: pilot.c:83
int pilot_canTarget(const Pilot *p)
Same as pilot_validTarget but without the range check.
Definition: pilot.c:243
unsigned int pilot_getNearestEnemy_heuristic(const Pilot *p, double mass_factor, double health_factor, double damage_factor, double range_factor)
Gets the nearest enemy to the pilot closest to the pilot whose mass is between LB and UB.
Definition: pilot.c:376
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:1044
int pilot_brake(Pilot *p)
Causes the pilot to turn around and brake.
Definition: pilot.c:788
unsigned int pilot_getBoss(const Pilot *p)
Get the strongest ally in a given range.
Definition: pilot.c:425
void pilot_setTarget(Pilot *p, unsigned int id)
Sets the target of the pilot.
Definition: pilot.c:1351
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:242
int pilot_ewScanCheck(const Pilot *p)
Checks to see if a scan is done.
Definition: pilot_ew.c:75
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition: pilot_ew.c:549
int pilot_stealth(Pilot *p)
Stealths a pilot.
Definition: pilot_ew.c:513
int pilot_runHookParam(Pilot *p, int hook_type, const HookParam *param, int nparam)
Tries to run a pilot hook if he has it.
Definition: pilot_hook.c:37
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_dock(Pilot *p, Pilot *target)
Docks the pilot on its target pilot.
Definition: pilot_outfit.c:189
int pilot_shoot(Pilot *p, int level)
Makes the pilot shoot.
Definition: pilot_weapon.c:743
void pilot_shootStop(Pilot *p, int level)
Have pilot stop shooting their weapon.
Definition: pilot_weapon.c:769
void pilot_weapSetAIClear(Pilot *p)
Useful function for AI, clears activeness of all weapon sets.
Definition: pilot_weapon.c:150
double pilot_weapSetAmmo(Pilot *p, int id, int level)
Gets the ammo of the current pilot weapon set.
Definition: pilot_weapon.c:676
void pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
Definition: pilot_weapon.c:165
double pilot_weapSetSpeed(Pilot *p, int id, int level)
Gets the speed of the current pilot weapon set.
Definition: pilot_weapon.c:654
double pilot_weapSetRange(Pilot *p, int id, int level)
Gets the range of the current pilot weapon set.
Definition: pilot_weapon.c:632
static const double a[]
Definition: rng.c:247
static const double d[]
Definition: rng.c:273
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition: space.c:1055
StarSystem * cur_system
Definition: space.c:105
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition: space.c:4138
int space_hyperspace(Pilot *p)
Tries to get the pilot into hyperspace.
Definition: space.c:471
Basic AI profile.
Definition: ai.h:41
int lua_mem
Definition: ai.h:45
int ref_refuel
Definition: ai.h:48
nlua_env env
Definition: ai.h:43
int ref_control
Definition: ai.h:46
char * name
Definition: ai.h:42
double control_rate
Definition: ai.h:44
int ref_create
Definition: ai.h:49
int ref_control_manual
Definition: ai.h:47
Represents a single asteroid.
Definition: asteroid.h:76
The actual hook parameter.
Definition: hook.h:35
LuaPilot lp
Definition: hook.h:41
union HookParam::@4 u
HookParamType type
Definition: hook.h:36
LuaSpob la
Definition: hook.h:43
double num
Definition: hook.h:38
Lua jump Wrapper.
Definition: nlua_jump.h:14
int destid
Definition: nlua_jump.h:16
int srcid
Definition: nlua_jump.h:15
A weapon set represents a set of weapons that have an action.
Definition: pilot.h:158
PilotWeaponSetOutfit * slots
Definition: pilot.h:161
The representation of an in-game pilot.
Definition: pilot.h:210
Solid * solid
Definition: pilot.h:220
int nturrets
Definition: pilot.h:300
ShipStats stats
Definition: pilot.h:286
AI_Profile * ai
Definition: pilot.h:342
unsigned int id
Definition: pilot.h:211
double thrust
Definition: pilot.h:235
credits_t credits
Definition: pilot.h:317
int nav_hyperspace
Definition: pilot.h:337
int projectiles
Definition: pilot.h:367
int messages
Definition: pilot.h:372
double speed
Definition: pilot.h:237
double timer[MAX_AI_TIMERS]
Definition: pilot.h:345
const Ship * ship
Definition: pilot.h:219
int lockons
Definition: pilot.h:366
int ncannons
Definition: pilot.h:299
int nav_anchor
Definition: pilot.h:338
double ptimer
Definition: pilot.h:356
int faction
Definition: pilot.h:215
double landing_delay
Definition: pilot.h:354
unsigned int shoot_indicator
Definition: pilot.h:347
char * name
Definition: pilot.h:212
void * ptarget
Definition: pilot.h:335
double tcontrol
Definition: pilot.h:344
Task * task
Definition: pilot.h:346
int nfighterbays
Definition: pilot.h:302
int active_set
Definition: pilot.h:312
unsigned int target
Definition: pilot.h:334
int nafterburners
Definition: pilot.h:303
int nav_spob
Definition: pilot.h:336
int lua_mem
Definition: pilot.h:343
double turn
Definition: pilot.h:240
int nav_asteroid
Definition: pilot.h:339
int devmode
Definition: conf.h:158
int misc_instant_jump
Definition: shipstats.h:298
double dt_default
Definition: ship.h:123
double dir_vel
Definition: physics.h:20
vec2 vel
Definition: physics.h:21
double thrust
Definition: physics.h:23
double dir
Definition: physics.h:19
double mass
Definition: physics.h:18
vec2 pos
Definition: physics.h:22
int faction
Definition: space.h:66
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition: space.h:88
int lua_land
Definition: space.h:140
double radius
Definition: space.h:94
nlua_env lua_env
Definition: space.h:134
char * name
Definition: space.h:90
vec2 pos
Definition: space.h:93
int id
Definition: space.h:89
int lua_can_land
Definition: space.h:139
SpobPresence presence
Definition: space.h:102
Basic AI task.
Definition: ai.h:25
int func
Definition: ai.h:28
char * name
Definition: ai.h:27
struct Task_ * subtask
Definition: ai.h:31
struct Task_ * next
Definition: ai.h:26
int done
Definition: ai.h:29
int dat
Definition: ai.h:33
Definition: msgcat.c:199
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33