naev 0.12.6
ai.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
61#include <lauxlib.h>
62#include <lualib.h>
63#include <math.h>
64#include <stdio.h>
65#include <stdlib.h>
66
67#include "SDL_timer.h"
68#include "physfs.h"
69
70#include "naev.h"
72
73#include "ai.h"
74
75#include "array.h"
76#include "board.h"
77#include "conf.h"
78#include "faction.h"
79#include "gatherable.h"
80#include "hook.h"
81#include "log.h"
82#include "ndata.h"
83#include "nlua.h"
84#include "nlua_asteroid.h"
85#include "nlua_pilot.h"
86#include "nlua_spob.h"
87#include "nlua_vec2.h"
88#include "nluadef.h"
89#include "ntracing.h"
90#include "physics.h"
91#include "pilot.h"
92#include "rng.h"
93#include "space.h"
94
95/*
96 * ai flags
97 *
98 * They can be used for stuff like movement or for pieces of code which might
99 * run AI stuff when the AI module is not reentrant.
100 */
101#define ai_setFlag( f ) ( pilot_flags |= f )
102#define ai_isFlag( f ) ( pilot_flags & f )
103/* flags */
104#define AI_DISTRESS ( 1 << 0 )
105
106/*
107 * all the AI profiles
108 */
109static AI_Profile *profiles = NULL;
110static nlua_env equip_env = LUA_NOREF;
112static double ai_dt = 0.;
113
114/*
115 * prototypes
116 */
117/* Internal C routines */
118static void ai_run( nlua_env env, int nargs );
119static int ai_loadProfile( AI_Profile *prof, const char *filename );
120static int ai_setMemory( void );
121static void ai_create( Pilot *pilot );
122static int ai_loadEquip( void );
123static int ai_sort( const void *p1, const void *p2 );
124/* Task management. */
125static void ai_taskGC( Pilot *pilot );
126static Task *ai_createTask( lua_State *L, int subtask );
127static int ai_tasktarget( lua_State *L, const Task *t );
128
129/*
130 * AI routines for Lua
131 */
132/* tasks */
133static int
134aiL_pushtask( lua_State *L ); /* pushtask( string, number/pointer ) */
135static int aiL_poptask( lua_State *L ); /* poptask() */
136static int aiL_taskname( lua_State *L ); /* string taskname() */
137static int aiL_taskdata( lua_State *L ); /* pointer subtaskdata() */
138static int aiL_pushsubtask(
139 lua_State *L ); /* pushsubtask( string, number/pointer, number ) */
140static int aiL_popsubtask( lua_State *L ); /* popsubtask() */
141static int aiL_subtaskname( lua_State *L ); /* string subtaskname() */
142static int aiL_subtaskdata( lua_State *L ); /* pointer subtaskdata() */
143
144/* consult values */
145static int aiL_pilot( lua_State *L ); /* number pilot() */
146static int aiL_getrndpilot( lua_State *L ); /* number getrndpilot() */
147static int aiL_getnearestpilot( lua_State *L ); /* number getnearestpilot() */
148static int aiL_getdistance( lua_State *L ); /* number getdist(vec2) */
149static int aiL_getdistance2( lua_State *L ); /* number getdist(vec2) */
150static int aiL_getflybydistance( lua_State *L ); /* number getflybydist(vec2) */
151static int
152aiL_minbrakedist( lua_State *L ); /* number minbrakedist( [number] ) */
153static int aiL_isbribed( lua_State *L ); /* bool isbribed( number ) */
154static int
155aiL_getGatherable( lua_State *L ); /* integer getgatherable( radius ) */
156static int aiL_instantJump( lua_State *L ); /* bool instantJump() */
157
158/* boolean expressions */
159static int aiL_ismaxvel( lua_State *L ); /* boolean ismaxvel() */
160static int aiL_isstopped( lua_State *L ); /* boolean isstopped() */
161static int aiL_isenemy( lua_State *L ); /* boolean isenemy( number ) */
162static int aiL_isally( lua_State *L ); /* boolean isally( number ) */
163static int aiL_haslockon( lua_State *L ); /* boolean haslockon() */
164static int aiL_hasprojectile( lua_State *L ); /* boolean hasprojectile() */
165static int aiL_scandone( lua_State *L );
166
167/* movement */
168static int aiL_accel( lua_State *L ); /* accel(number); number <= 1. */
169static int aiL_turn( lua_State *L ); /* turn(number); abs(number) <= 1. */
170static int aiL_careful_face( lua_State *L ); /* face( number/pointer, bool) */
171static int aiL_aim( lua_State *L ); /* aim(number) */
172static int aiL_dir( lua_State *L ); /* dir(number/pointer) */
173static int aiL_face( lua_State *L ); /* face( number/pointer, bool) */
174static int aiL_iface( lua_State *L ); /* iface(number/pointer) */
175static int aiL_idir( lua_State *L ); /* idir(number/pointer) */
176static int aiL_follow_accurate( lua_State *L ); /* follow_accurate() */
177static int aiL_face_accurate( lua_State *L ); /* face_accurate() */
178static int aiL_drift_facing( lua_State *L ); /* drift_facing(number/pointer) */
179static int aiL_brake( lua_State *L ); /* brake() */
180static int aiL_getnearestspob( lua_State *L ); /* Vec2 getnearestspob() */
181static int aiL_getspobfrompos( lua_State *L ); /* Vec2 getspobfrompos() */
182static int aiL_getrndspob( lua_State *L ); /* Vec2 getrndspob() */
183static int aiL_getlandspob( lua_State *L ); /* Vec2 getlandspob() */
184static int aiL_land( lua_State *L ); /* bool land() */
185static int aiL_stop( lua_State *L ); /* stop() */
186static int aiL_relvel( lua_State *L ); /* relvel( number ) */
187
188/* Hyperspace. */
189static int aiL_sethyptarget( lua_State *L );
190static int aiL_nearhyptarget( lua_State *L ); /* pointer rndhyptarget() */
191static int aiL_rndhyptarget( lua_State *L ); /* pointer rndhyptarget() */
192static int aiL_hyperspace( lua_State *L ); /* [number] hyperspace() */
193static int aiL_canHyperspace( lua_State *L );
194static int aiL_hyperspaceAbort( lua_State *L );
195
196/* escorts */
197static int aiL_dock( lua_State *L ); /* dock( number ) */
198
199/* combat */
200static int aiL_combat( lua_State *L ); /* combat( number ) */
201static int aiL_settarget( lua_State *L ); /* settarget( number ) */
202static int aiL_weapSet( lua_State *L ); /* weapset( number ) */
203static int aiL_hascannons( lua_State *L ); /* bool hascannons() */
204static int aiL_hasturrets( lua_State *L ); /* bool hasturrets() */
205static int aiL_hasfighterbays( lua_State *L ); /* bool hasfighterbays() */
206static int aiL_hasafterburner( lua_State *L ); /* bool hasafterburner() */
207static int aiL_getenemy( lua_State *L ); /* number getenemy() */
208static int aiL_hostile( lua_State *L ); /* hostile( number ) */
209static int aiL_getweaprangemin( lua_State *L ); /* number getweaprangemin() */
210static int aiL_getweaprange( lua_State *L ); /* number getweaprange() */
211static int aiL_getweapspeed( lua_State *L ); /* number getweapspeed() */
212static int aiL_getweapammo( lua_State *L );
213static int aiL_canboard( lua_State *L ); /* boolean canboard( number ) */
214static int aiL_relsize( lua_State *L ); /* boolean relsize( number ) */
215static int aiL_reldps( lua_State *L ); /* boolean reldps( number ) */
216static int aiL_relhp( lua_State *L ); /* boolean relhp( number ) */
217
218/* timers */
219static int aiL_settimer( lua_State *L ); /* settimer( number, number ) */
220static int aiL_timeup( lua_State *L ); /* boolean timeup( number ) */
221
222/* messages */
223static int aiL_distress( lua_State *L ); /* distress( string [, bool] ) */
224static int aiL_getBoss( lua_State *L ); /* number getBoss() */
225
226/* loot */
227static int aiL_credits( lua_State *L ); /* credits( number ) */
228
229/* misc */
230static int aiL_board( lua_State *L ); /* boolean board() */
231static int aiL_refuel( lua_State *L ); /* boolean, boolean refuel() */
232static int aiL_messages( lua_State *L );
233static int
234aiL_setasterotarget( lua_State *L ); /* setasterotarget( Asteroid ) */
235static int aiL_gatherablePos( lua_State *L ); /* gatherablepos( number ) */
236static int aiL_shoot_indicator( lua_State *L ); /* get shoot indicator */
237static int aiL_set_shoot_indicator( lua_State *L ); /* set shoot indicator */
238static int aiL_stealth( lua_State *L );
239static int aiL_outfitOffAll( lua_State *L );
240
241static const luaL_Reg aiL_methods[] = {
242 /* tasks */
243 { "pushtask", aiL_pushtask },
244 { "poptask", aiL_poptask },
245 { "taskname", aiL_taskname },
246 { "taskdata", aiL_taskdata },
247 { "pushsubtask", aiL_pushsubtask },
248 { "popsubtask", aiL_popsubtask },
249 { "subtaskname", aiL_subtaskname },
250 { "subtaskdata", aiL_subtaskdata },
251 /* is */
252 { "ismaxvel", aiL_ismaxvel },
253 { "isstopped", aiL_isstopped },
254 { "isenemy", aiL_isenemy },
255 { "isally", aiL_isally },
256 { "haslockon", aiL_haslockon },
257 { "hasprojectile", aiL_hasprojectile },
258 { "scandone", aiL_scandone },
259 /* get */
260 { "pilot", aiL_pilot },
261 { "rndpilot", aiL_getrndpilot },
262 { "nearestpilot", aiL_getnearestpilot },
263 { "dist", aiL_getdistance },
264 { "dist2", aiL_getdistance2 },
265 { "flyby_dist", aiL_getflybydistance },
266 { "minbrakedist", aiL_minbrakedist },
267 { "isbribed", aiL_isbribed },
268 { "getgatherable", aiL_getGatherable },
269 { "instantJump", aiL_instantJump },
270 /* movement */
271 { "nearestspob", aiL_getnearestspob },
272 { "spobfrompos", aiL_getspobfrompos },
273 { "rndspob", aiL_getrndspob },
274 { "landspob", aiL_getlandspob },
275 { "land", aiL_land },
276 { "accel", aiL_accel },
277 { "turn", aiL_turn },
278 { "face", aiL_face },
279 { "careful_face", aiL_careful_face },
280 { "iface", aiL_iface },
281 { "dir", aiL_dir },
282 { "idir", aiL_idir },
283 { "drift_facing", aiL_drift_facing },
284 { "brake", aiL_brake },
285 { "stop", aiL_stop },
286 { "relvel", aiL_relvel },
287 { "follow_accurate", aiL_follow_accurate },
288 { "face_accurate", aiL_face_accurate },
289 /* Hyperspace. */
290 { "sethyptarget", aiL_sethyptarget },
291 { "nearhyptarget", aiL_nearhyptarget },
292 { "rndhyptarget", aiL_rndhyptarget },
293 { "hyperspace", aiL_hyperspace },
294 { "canHyperspace", aiL_canHyperspace },
295 { "hyperspaceAbort", aiL_hyperspaceAbort },
296 { "dock", aiL_dock },
297 /* combat */
298 { "aim", aiL_aim },
299 { "combat", aiL_combat },
300 { "settarget", aiL_settarget },
301 { "weapset", aiL_weapSet },
302 { "hascannons", aiL_hascannons },
303 { "hasturrets", aiL_hasturrets },
304 { "hasfighterbays", aiL_hasfighterbays },
305 { "hasafterburner", aiL_hasafterburner },
306 { "getenemy", aiL_getenemy },
307 { "hostile", aiL_hostile },
308 { "getweaprangemin", aiL_getweaprangemin },
309 { "getweaprange", aiL_getweaprange },
310 { "getweapspeed", aiL_getweapspeed },
311 { "getweapammo", aiL_getweapammo },
312 { "canboard", aiL_canboard },
313 { "relsize", aiL_relsize },
314 { "reldps", aiL_reldps },
315 { "relhp", aiL_relhp },
316 /* timers */
317 { "settimer", aiL_settimer },
318 { "timeup", aiL_timeup },
319 /* messages */
320 { "distress", aiL_distress },
321 { "getBoss", aiL_getBoss },
322 /* loot */
323 { "setcredits", aiL_credits },
324 /* misc */
325 { "board", aiL_board },
326 { "refuel", aiL_refuel },
327 { "messages", aiL_messages },
328 { "setasterotarget", aiL_setasterotarget },
329 { "gatherablepos", aiL_gatherablePos },
330 { "shoot_indicator", aiL_shoot_indicator },
331 { "set_shoot_indicator", aiL_set_shoot_indicator },
332 { "stealth", aiL_stealth },
333 { "outfitOffAll", aiL_outfitOffAll },
334 { 0, 0 } /* end */
335};
336
337/*
338 * current pilot "thinking" and assorted variables
339 */
341static double pilot_acc = 0.;
342static double pilot_turn = 0.;
343static int pilot_flags = 0;
344static char
345 aiL_distressmsg[STRMAX_SHORT];
346
347/*
348 * ai status, used so that create functions can't be used elsewhere
349 */
350#define AI_STATUS_NORMAL 1
351#define AI_STATUS_CREATE 2
353
359static void ai_taskGC( Pilot *pilot )
360{
361 Task *prev = NULL;
362 Task *t = pilot->task;
363 while (t != NULL) {
364 if (t->done) {
365 Task *pointer = t;
366 /* Unattach pointer. */
367 t = t->next;
368 if (prev == NULL)
369 pilot->task = t;
370 else
371 prev->next = t;
372 /* Free pointer. */
373 pointer->next = NULL;
374 ai_freetask( pointer );
375 } else {
376 prev = t;
377 t = t->next;
378 }
379 }
380}
381
386{
387 /* Get last task. */
388 for (Task *t = pilot->task; t != NULL; t = t->next)
389 if (!t->done)
390 return t;
391 return NULL;
392}
393
397static int ai_setMemory( void )
398{
399 int oldmem;
400 nlua_env env = cur_pilot->ai->env;
401
402 nlua_getenv( naevL, env, "mem" ); /* oldmem */
403 oldmem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
404
405 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->lua_mem );
406 nlua_setenv( naevL, env, "mem" ); /* pm */
407
408 return oldmem;
409}
410
417{
418 AIMemory oldmem;
419 cur_pilot = p;
420 oldmem.p = cur_pilot;
421 oldmem.mem = ai_setMemory();
422 return oldmem;
423}
424
429{
430 nlua_env env = cur_pilot->ai->env;
431 lua_rawgeti( naevL, LUA_REGISTRYINDEX, oldmem.mem );
432 nlua_setenv( naevL, env, "mem" ); /* pm */
433 luaL_unref( naevL, LUA_REGISTRYINDEX, oldmem.mem );
434 cur_pilot = oldmem.p;
435}
436
440void ai_thinkSetup( double dt )
441{
442 /* Clean up some variables */
443 pilot_acc = 0.;
444 pilot_turn = 0.;
445 pilot_flags = 0;
446 ai_dt = MAX( dt, DOUBLE_TOL );
447}
448
455{
456 /* Make sure pilot_acc and pilot_turn are legal */
457 pilot_acc = CLAMP( -PILOT_REVERSE_THRUST * p->stats.misc_reverse_thrust, 1.,
458 pilot_acc );
459 pilot_turn = CLAMP( -1., 1., pilot_turn );
460
461 /* Set turn and accel. */
464
465 /* other behaviours. */
466 if (ai_isFlag( AI_DISTRESS ))
468}
469
476static void ai_run( nlua_env env, int nargs )
477{
478 if (nlua_pcall( env, nargs, 0 )) { /* error has occurred */
479 WARN( _( "Pilot '%s' ai '%s' error: %s" ), cur_pilot->name,
480 cur_pilot->ai->name, lua_tostring( naevL, -1 ) );
481 lua_pop( naevL, 1 );
482 }
483}
484
494int ai_pinit( Pilot *p, const char *ai )
495{
496 AI_Profile *prof;
497 char buf[PATH_MAX];
498
499 strncpy( buf, ai, sizeof( buf ) - 1 );
500 buf[sizeof( buf ) - 1] = '\0';
501
502 /* Set up the profile. */
503 prof = ai_getProfile( buf );
504 if (prof == NULL) {
505 WARN( _( "AI Profile '%s' not found, using dummy fallback." ), buf );
506 prof = ai_getProfile( "dummy" );
507 }
508 if (prof == NULL) {
509 WARN( _( "Dummy AI Profile not valid! Things are going to break." ) );
510 return -1;
511 }
512 p->ai = prof;
513
514 /* Adds a new pilot memory in the memory table. */
515 lua_newtable( naevL ); /* m */
516
517 /* Copy defaults over from the global memory table. */
518 lua_rawgeti( naevL, LUA_REGISTRYINDEX, prof->lua_mem ); /* m, d */
519 lua_pushnil( naevL ); /* m, d, nil */
520 while (lua_next( naevL, -2 ) != 0) { /* m, d, k, v */
521 lua_pushvalue( naevL, -2 ); /* m, d, k, v, k */
522 lua_pushvalue( naevL, -2 ); /* m, d, k, v, k, v */
523 lua_remove( naevL, -3 ); /* m, d, k, k, v */
524 lua_settable( naevL, -5 ); /* m, d, k */
525 } /* m, d */
526 lua_pop( naevL, 1 ); /* m */
527 p->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
528
529 /* Create the pilot. */
530 ai_create( p );
531 pilot_setFlag( p, PILOT_CREATED_AI );
532
533 /* Initialize randomly within a control tick. */
534 /* This doesn't work as nicely as one would expect because the pilot
535 * has no initial task and control ticks get synchronized if you
536 * spawn a bunch at the same time, which is why we add randomness
537 * elsewhere. */
538 p->tcontrol = RNGF() * p->ai->control_rate;
539
540 return 0;
541}
542
549{
550 /* Clean up tasks. */
551 if (p->task)
552 ai_freetask( p->task );
553 p->task = NULL;
554}
555
562{
563 if (p->ai == NULL)
564 return;
565
566 /* Get rid of pilot's memory. */
567 if (!pilot_isPlayer( p )) { /* Player is an exception as more than one ship
568 shares pilot id. */
569 luaL_unref( naevL, LUA_REGISTRYINDEX, p->lua_mem );
570 p->lua_mem = LUA_NOREF;
571 }
572
573 /* Clear the tasks. */
574 ai_cleartasks( p );
575}
576
577static int ai_sort( const void *p1, const void *p2 )
578{
579 const AI_Profile *ai1 = p1;
580 const AI_Profile *ai2 = p2;
581 return strcmp( ai1->name, ai2->name );
582}
583
589int ai_load( void )
590{
591 char **files;
592#if DEBUGGING
593 Uint32 time = SDL_GetTicks();
594#endif /* DEBUGGING */
595
596 NTracingZone( _ctx, 1 );
597
598 /* get the file list */
599 files = PHYSFS_enumerateFiles( AI_PATH );
600
601 /* Create array. */
603
604 /* load the profiles */
605 for (size_t i = 0; files[i] != NULL; i++) {
606 AI_Profile prof;
607 char path[PATH_MAX];
608 int ret;
609
610 if (!ndata_matchExt( files[i], "lua" ))
611 continue;
612
613 snprintf( path, sizeof( path ), AI_PATH "%s", files[i] );
614 ret = ai_loadProfile( &prof, path ); /* Load the profile */
615 if (ret == 0)
616 array_push_back( &profiles, prof );
617 else
618 WARN( _( "Error loading AI profile '%s'" ), path );
619
620 /* Render if necessary. */
622 }
623 qsort( profiles, array_size( profiles ), sizeof( AI_Profile ), ai_sort );
624
625 /* More clean up. */
626 PHYSFS_freeList( files );
627
628#if DEBUGGING
629 if (conf.devmode) {
630 time = SDL_GetTicks() - time;
631 DEBUG( n_( "Loaded %d AI Profile in %.3f s",
632 "Loaded %d AI Profiles in %.3f s", array_size( profiles ) ),
633 array_size( profiles ), time / 1000. );
634 } else
635 DEBUG( n_( "Loaded %d AI Profile", "Loaded %d AI Profiles",
636 array_size( profiles ) ),
637 array_size( profiles ) );
638#endif /* DEBUGGING */
639
640 /* Create collision stuff. */
641 il_create( &ai_qtquery, 1 );
642
643 /* Load equipment thingy. */
644 ai_loadEquip();
645
646 NTracingZoneEnd( _ctx );
647
648 return 0;
649}
650
654static int ai_loadEquip( void )
655{
656 char *buf;
657 size_t bufsize;
658 const char *filename = AI_EQUIP_PATH;
659
660 NTracingZone( _ctx, 1 );
661
662 /* Make sure doesn't already exist. */
663 nlua_freeEnv( equip_env );
664
665 /* Create new state. */
666 equip_env = nlua_newEnv( "ai_equip" );
668
669 /* Load the file. */
670 buf = ndata_read( filename, &bufsize );
671 if (nlua_dobufenv( equip_env, buf, bufsize, filename ) != 0) {
672 WARN( _( "Error loading file: %s\n"
673 "%s\n"
674 "Most likely Lua file has improper syntax, please check" ),
675 filename, lua_tostring( naevL, -1 ) );
676 NTracingZoneEnd( _ctx );
677 return -1;
678 }
679 free( buf );
680
681 NTracingZoneEnd( _ctx );
682
683 return 0;
684}
685
686int nlua_loadAI( nlua_env env )
687{
688 nlua_register( env, "ai", aiL_methods, 0 );
689 return 0;
690}
691
699static int ai_loadProfile( AI_Profile *prof, const char *filename )
700{
701 char *buf = NULL;
702 size_t bufsize = 0;
703 nlua_env env;
704 size_t len;
705 const char *str;
706
707 /* Set name. */
708 len = strlen( filename ) - strlen( AI_PATH ) - strlen( ".lua" );
709 prof->name = malloc( len + 1 );
710 strncpy( prof->name, &filename[strlen( AI_PATH )], len );
711 prof->name[len] = '\0';
712
713 /* Create Lua. */
714 env = nlua_newEnv( filename );
715 nlua_loadStandard( env );
716 prof->env = env;
717
718 /* Register C functions in Lua */
719 nlua_register( env, "ai", aiL_methods, 0 );
720
721 /* Mark as an ai. */
722 lua_pushboolean( naevL, 1 );
723 nlua_setenv( naevL, env, "__ai" );
724
725 /* Set "mem" to be default template. */
726 lua_newtable( naevL ); /* m */
727 lua_pushvalue( naevL, -1 ); /* m, m */
728 prof->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* m */
729 nlua_setenv( naevL, env, "mem" ); /* */
730
731 /* Now load the file since all the functions have been previously loaded */
732 buf = ndata_read( filename, &bufsize );
733 if (nlua_dobufenv( env, buf, bufsize, filename ) != 0) {
734 WARN( _( "Error loading AI file: %s\n"
735 "%s\n"
736 "Most likely Lua file has improper syntax, please check" ),
737 filename, lua_tostring( naevL, -1 ) );
738 free( prof->name );
739 nlua_freeEnv( env );
740 free( buf );
741 return -1;
742 }
743 free( buf );
744
745 /* Find and set up the necessary references. */
746 str = _( "AI Profile '%s' is missing '%s' function!" );
747 prof->ref_control = nlua_refenvtype( env, "control", LUA_TFUNCTION );
748 if (prof->ref_control == LUA_NOREF)
749 WARN( str, filename, "control" );
750 prof->ref_control_manual =
751 nlua_refenvtype( env, "control_manual", LUA_TFUNCTION );
752 if (prof->ref_control_manual == LUA_NOREF)
753 WARN( str, filename, "control_manual" );
754 prof->ref_refuel = nlua_refenvtype( env, "refuel", LUA_TFUNCTION );
755 if (prof->ref_refuel == LUA_NOREF)
756 WARN( str, filename, "refuel" );
757 prof->ref_create = nlua_refenvtype( env, "create", LUA_TFUNCTION );
758 if (prof->ref_create == LUA_NOREF)
759 WARN( str, filename, "create" );
760
761 /* Get the control rate. */
762 nlua_getenv( naevL, env, "control_rate" );
763 prof->control_rate = lua_tonumber( naevL, -1 );
764 lua_pop( naevL, 1 );
765
766 return 0;
767}
768
775AI_Profile *ai_getProfile( const char *name )
776{
777 const AI_Profile ai = { .name = (char *)name };
778 AI_Profile *ret = bsearch( &ai, profiles, array_size( profiles ),
779 sizeof( AI_Profile ), ai_sort );
780 if (ret == NULL)
781 WARN( _( "AI Profile '%s' not found in AI stack" ), name );
782 return ret;
783}
784
788void ai_exit( void )
789{
790 /* Free AI profiles. */
791 for (int i = 0; i < array_size( profiles ); i++) {
792 free( profiles[i].name );
793 nlua_freeEnv( profiles[i].env );
794 }
796
797 /* Free equipment Lua. */
798 nlua_freeEnv( equip_env );
799 equip_env = LUA_NOREF;
800
801 /* Clean up query stuff. */
802 il_destroy( &ai_qtquery );
803}
804
812void ai_think( Pilot *pilot, double dt, int dotask )
813{
814 nlua_env env;
815 AIMemory oldmem;
816 Task *t;
817
818 /* Must have AI. */
819 if (pilot->ai == NULL)
820 return;
821
822 NTracingZone( _ctx, 1 );
823
824 oldmem = ai_setPilot( pilot );
825 env = cur_pilot->ai->env; /* set the AI profile to the current pilot's */
826
827 ai_thinkSetup( dt );
828 pilot_rmFlag( pilot, PILOT_SCANNING ); /* Reset each frame, only set if the
829 pilot is checking ai.scandone. */
830 /* So the way this works is that, for other than the player, we reset all
831 * the weapon sets every frame, so that the AI has to redo them over and
832 * over. Now, this is a horrible hack so shit works and needs a proper fix.
833 * TODO fix. */
834
835 /* Mark weapon sets as off, and the AI will activate as necessary. */
836 if (!pilot_isPlayer( cur_pilot ))
837 /* Turn off HOLD groups. */
839
840 /* Get current task. */
841 t = ai_curTask( cur_pilot );
842
843 /* control function if pilot is idle or tick is up */
844 if (( cur_pilot->tcontrol < 0. ) || ( t == NULL )) {
845 NTracingZoneName( _ctx_control, "ai_think[control]", 1 );
846
847 double crate = cur_pilot->ai->control_rate;
848 if (pilot_isFlag( pilot, PILOT_PLAYER ) ||
849 pilot_isFlag( cur_pilot, PILOT_MANUAL_CONTROL )) {
850 lua_rawgeti( naevL, LUA_REGISTRYINDEX,
851 cur_pilot->ai->ref_control_manual );
852 lua_pushnumber( naevL, crate - cur_pilot->tcontrol );
853 ai_run( env, 1 );
854 } else {
855 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_control );
856 lua_pushnumber( naevL, crate - cur_pilot->tcontrol );
857 ai_run( env, 1 ); /* run control */
858 }
859 /* Try to desync control ticks when possible by adding randomness. */
860 cur_pilot->tcontrol = crate * ( 0.9 + 0.2 * RNGF() );
861
862 /* Task may have changed due to control tick. */
863 t = ai_curTask( cur_pilot );
864
865 NTracingZoneEnd( _ctx_control );
866 }
867
868 if (!dotask) {
869 ai_unsetPilot( oldmem );
870 NTracingZoneEnd( _ctx );
871 return;
872 }
873
874 /* pilot has a currently running task */
875 if (t != NULL) {
876 int data;
877 NTracingZoneName( _ctx_task, "ai_think[task]", 1 );
878
879 /* Run subtask if available, otherwise run main task. */
880 if (t->subtask != NULL) {
881 lua_rawgeti( naevL, LUA_REGISTRYINDEX, t->subtask->func );
882 /* Use subtask data or task data if subtask is not set. */
883 data = t->subtask->dat;
884 if (data == LUA_NOREF)
885 data = t->dat;
886 } else {
887 lua_rawgeti( naevL, LUA_REGISTRYINDEX, t->func );
888 data = t->dat;
889 }
890 /* Function should be on the stack. */
891 if (data != LUA_NOREF) {
892 lua_rawgeti( naevL, LUA_REGISTRYINDEX, data );
893 ai_run( env, 1 );
894 } else
895 ai_run( env, 0 );
896
897 /* Manual control must check if IDLE hook has to be run. */
898 if (pilot_isFlag( cur_pilot, PILOT_MANUAL_CONTROL )) {
899 /* We must yet check again to see if there still is a current task
900 * running. */
901 if (ai_curTask( cur_pilot ) == NULL)
902 pilot_runHook( cur_pilot, PILOT_HOOK_IDLE );
903 }
904
905 NTracingZoneEnd( _ctx_task );
906 }
907
908 /* Have to update potential outfit state changes here. */
909 if (!pilot_isPlayer( cur_pilot ))
911
912 /* Applies local variables to the pilot. */
914
915 /* Restore memory. */
916 ai_unsetPilot( oldmem );
917
918 /* Clean up if necessary. */
920
921 NTracingZoneEnd( _ctx );
922}
923
929void ai_init( Pilot *p )
930{
931 NTracingZone( _ctx, 1 );
932
933 AIMemory oldmem;
934 if (( p->ai == NULL ) || ( p->ai->ref_create == LUA_NOREF ))
935 return;
936 oldmem = ai_setPilot( p );
937 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->ai->ref_create );
938 ai_run( p->ai->env, 0 ); /* run control */
939 ai_unsetPilot( oldmem );
940
941 NTracingZoneEnd( _ctx );
942}
943
951void ai_attacked( Pilot *attacked, const unsigned int attacker, double dmg )
952{
953 AIMemory oldmem;
954 HookParam hparam[2];
955
956 /* Custom hook parameters. */
957 hparam[0].type = HOOK_PARAM_PILOT;
958 hparam[0].u.lp = attacker;
959 hparam[1].type = HOOK_PARAM_NUMBER;
960 hparam[1].u.num = dmg;
961
962 /* Behaves differently if manually overridden. */
963 pilot_runHookParam( attacked, PILOT_HOOK_ATTACKED, hparam, 2 );
964
965 /* Must have an AI profile. */
966 if (attacked->ai == NULL)
967 return;
968
969 oldmem = ai_setPilot( attacked ); /* Sets cur_pilot. */
970 if (pilot_isFlag( attacked, PILOT_MANUAL_CONTROL ))
971 nlua_getenv( naevL, cur_pilot->ai->env, "attacked_manual" );
972 else
973 nlua_getenv( naevL, cur_pilot->ai->env, "attacked" );
974
975 lua_pushpilot( naevL, attacker );
976 if (nlua_pcall( cur_pilot->ai->env, 1, 0 )) {
977 WARN( _( "Pilot '%s' ai '%s' -> 'attacked': %s" ), cur_pilot->name,
978 cur_pilot->ai->name, lua_tostring( naevL, -1 ) );
979 lua_pop( naevL, 1 );
980 }
981 ai_unsetPilot( oldmem );
982}
983
989void ai_discovered( Pilot *discovered )
990{
991 AIMemory oldmem;
992
993 /* Behaves differently if manually overridden. */
994 pilot_runHook( discovered, PILOT_HOOK_DISCOVERED );
995 if (pilot_isFlag( discovered, PILOT_MANUAL_CONTROL ))
996 return;
997
998 /* Must have an AI profile and not be player. */
999 if (discovered->ai == NULL)
1000 return;
1001
1002 oldmem = ai_setPilot( discovered ); /* Sets cur_pilot. */
1003
1004 /* Only run if discovered function exists. */
1005 nlua_getenv( naevL, cur_pilot->ai->env, "discovered" );
1006 if (lua_isnil( naevL, -1 )) {
1007 lua_pop( naevL, 1 );
1008 ai_unsetPilot( oldmem );
1009 return;
1010 }
1011
1012 if (nlua_pcall( cur_pilot->ai->env, 0, 0 )) {
1013 WARN( _( "Pilot '%s' ai '%s' -> 'discovered': %s" ), cur_pilot->name,
1014 cur_pilot->ai->name, lua_tostring( naevL, -1 ) );
1015 lua_pop( naevL, 1 );
1016 }
1017 ai_unsetPilot( oldmem );
1018}
1019
1025void ai_hail( Pilot *recipient )
1026{
1027 AIMemory oldmem;
1028
1029 /* Make sure it's getable. */
1030 if (!pilot_canTarget( recipient ))
1031 return;
1032
1033 /* Must have an AI profile and not be player. */
1034 if (recipient->ai == NULL)
1035 return;
1036
1037 oldmem = ai_setPilot( recipient ); /* Sets cur_pilot. */
1038
1039 /* Only run if hail function exists. */
1040 nlua_getenv( naevL, cur_pilot->ai->env, "hail" );
1041 if (lua_isnil( naevL, -1 )) {
1042 lua_pop( naevL, 1 );
1043 ai_unsetPilot( oldmem );
1044 return;
1045 }
1046
1047 if (nlua_pcall( cur_pilot->ai->env, 0, 0 )) {
1048 WARN( _( "Pilot '%s' ai '%s' -> 'hail': %s" ), cur_pilot->name,
1049 cur_pilot->ai->name, lua_tostring( naevL, -1 ) );
1050 lua_pop( naevL, 1 );
1051 }
1052 ai_unsetPilot( oldmem );
1053}
1054
1061void ai_refuel( Pilot *refueler, unsigned int target )
1062{
1063 Task *t;
1064
1065 if (cur_pilot->ai->ref_refuel == LUA_NOREF) {
1066 WARN( _( "Pilot '%s' (ai '%s') is trying to refuel when no 'refuel' "
1067 "function is defined!" ),
1068 cur_pilot->name, cur_pilot->ai->name );
1069 return;
1070 }
1071
1072 /* Create the task. */
1073 t = ncalloc( 1, sizeof( Task ) );
1074 t->name = strdup( "refuel" );
1075 lua_rawgeti( naevL, LUA_REGISTRYINDEX, cur_pilot->ai->ref_refuel );
1076 t->func = luaL_ref( naevL, LUA_REGISTRYINDEX );
1077 lua_pushpilot( naevL, target );
1078 t->dat = luaL_ref( naevL, LUA_REGISTRYINDEX );
1079
1080 /* Prepend the task. */
1081 t->next = refueler->task;
1082 refueler->task = t;
1083
1084 return;
1085}
1086
1094void ai_getDistress( const Pilot *p, const Pilot *distressed,
1095 const Pilot *attacker )
1096{
1097 /* Ignore distress signals when under manual control. */
1098 if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ))
1099 return;
1100
1101 /* Must have AI. */
1102 if (cur_pilot->ai == NULL)
1103 return;
1104
1105 if (attacker != NULL)
1106 lua_pushpilot( naevL, attacker->id );
1107 else
1108 lua_pushnil( naevL );
1109 pilot_msg( distressed, p, "distress", -1 );
1110 lua_pop( naevL, 1 );
1111}
1112
1120static void ai_create( Pilot *pilot )
1121{
1122 NTracingZone( _ctx, 1 );
1123
1124 /* Set creation mode. */
1125 if (!pilot_isFlag( pilot, PILOT_CREATED_AI ))
1127
1128 /* Create equipment first - only if creating for the first time. */
1129 if (!pilot_isFlag( pilot, PILOT_NO_OUTFITS ) &&
1130 !pilot_isFlag( pilot, PILOT_NO_EQUIP ) &&
1132 nlua_env env = equip_env;
1133 char *func = "equip_generic";
1134
1135 if (faction_getEquipper( pilot->faction ) != LUA_NOREF) {
1136 env = faction_getEquipper( pilot->faction );
1137 func = "equip";
1138 }
1139 nlua_getenv( naevL, env, func );
1140 lua_pushpilot( naevL, pilot->id );
1141 if (nlua_pcall( env, 1, 0 )) { /* Error has occurred. */
1142 WARN( _( "Pilot '%s' equip '%s' -> '%s': %s" ), pilot->name,
1143 pilot->ai->name, func, lua_tostring( naevL, -1 ) );
1144 lua_pop( naevL, 1 );
1145 }
1146
1147 /* Since the pilot changes outfits and cores, we must heal him up. */
1148 pilot_healLanded( pilot );
1149 }
1150
1151 /* Must have AI. */
1152 if (pilot->ai == NULL) {
1153 NTracingZoneEnd( _ctx );
1154 return;
1155 }
1156
1157 /* Set up. */
1158 ai_init( pilot );
1159
1160 /* Recover normal mode. */
1161 if (!pilot_isFlag( pilot, PILOT_CREATED_AI ))
1163 NTracingZoneEnd( _ctx );
1164}
1165
1169Task *ai_newtask( lua_State *L, Pilot *p, const char *func, int subtask,
1170 int pos )
1171{
1172 Task *t, *pointer;
1173
1174 if (p->ai == NULL) {
1175 NLUA_ERROR(
1176 L, _( "Trying to create new task for pilot '%s' that has no AI!" ),
1177 p->name );
1178 return NULL;
1179 }
1180
1181 /* Check if the function is good. */
1182 nlua_getenv( L, p->ai->env, func );
1183 luaL_checktype( L, -1, LUA_TFUNCTION );
1184
1185 /* Create the new task. */
1186 t = ncalloc( 1, sizeof( Task ) );
1187 t->name = strdup( func );
1188 t->func = luaL_ref( L, LUA_REGISTRYINDEX );
1189 t->dat = LUA_NOREF;
1190
1191 /* Handle subtask and general task. */
1192 if (!subtask) {
1193 if (( pos == 1 ) && ( p->task != NULL )) { /* put at the end */
1194 for (pointer = p->task; pointer->next != NULL; pointer = pointer->next)
1195 ;
1196 pointer->next = t;
1197 } else {
1198 t->next = p->task;
1199 p->task = t;
1200 }
1201 } else {
1202 /* Must have valid task. */
1203 Task *curtask = ai_curTask( p );
1204 if (curtask == NULL) {
1205 ai_freetask( t );
1206 NLUA_ERROR( L, _( "Trying to add subtask '%s' to non-existent task." ),
1207 func );
1208 return NULL;
1209 }
1210
1211 /* Add the subtask. */
1212 if (( pos == 1 ) && ( curtask->subtask != NULL )) { /* put at the end */
1213 for (pointer = curtask->subtask; pointer->next != NULL;
1214 pointer = pointer->next)
1215 ;
1216 pointer->next = t;
1217 } else {
1218 t->next = curtask->subtask;
1219 curtask->subtask = t;
1220 }
1221 }
1222
1223 return t;
1224}
1225
1232{
1233 if (t->func != LUA_NOREF)
1234 luaL_unref( naevL, LUA_REGISTRYINDEX, t->func );
1235
1236 if (t->dat != LUA_NOREF)
1237 luaL_unref( naevL, LUA_REGISTRYINDEX, t->dat );
1238
1239 /* Recursive subtask freeing. */
1240 if (t->subtask != NULL) {
1241 ai_freetask( t->subtask );
1242 t->subtask = NULL;
1243 }
1244
1245 /* Free next task in the chain. */
1246 if (t->next != NULL) {
1247 ai_freetask( t->next ); /* yay recursive freeing */
1248 t->next = NULL;
1249 }
1250
1251 free( t->name );
1252 nfree( t );
1253}
1254
1258static Task *ai_createTask( lua_State *L, int subtask )
1259{
1260 /* Parse basic parameters. */
1261 const char *func = luaL_checkstring( L, 1 );
1262
1263 if (pilot_isPlayer( cur_pilot ) &&
1264 !pilot_isFlag( cur_pilot, PILOT_MANUAL_CONTROL ))
1265 return NULL;
1266
1267 /* Creates a new AI task. */
1268 Task *t = ai_newtask( L, cur_pilot, func, subtask, 0 );
1269 if (t == NULL) {
1270 NLUA_ERROR( L, _( "Failed to create new task for pilot '%s'." ),
1271 cur_pilot->name );
1272 return NULL;
1273 }
1274
1275 /* Set the data. */
1276 if (lua_gettop( L ) > 1) {
1277 lua_pushvalue( L, 2 );
1278 t->dat = luaL_ref( L, LUA_REGISTRYINDEX );
1279 }
1280
1281 return t;
1282}
1283
1287static int ai_tasktarget( lua_State *L, const Task *t )
1288{
1289 if (t->dat == LUA_NOREF)
1290 return 0;
1291 lua_rawgeti( L, LUA_REGISTRYINDEX, t->dat );
1292 return 1;
1293}
1294
1314static int aiL_pushtask( lua_State *L )
1315{
1316 ai_createTask( L, 0 );
1317 aiL_outfitOffAll( L );
1318 return 0;
1319}
1320
1325static int aiL_poptask( lua_State *L )
1326{
1327 (void)L;
1328 Task *t = ai_curTask( cur_pilot );
1329 /* Tasks must exist. */
1330 if (t == NULL) {
1331 NLUA_WARN(
1332 L, _( "Trying to pop task when there are no tasks on the stack." ) );
1333 return 0;
1334 }
1335 t->done = 1;
1336 aiL_outfitOffAll( L );
1337 return 0;
1338}
1339
1346static int aiL_taskname( lua_State *L )
1347{
1348 const Task *t = ai_curTask( cur_pilot );
1349 if (t == NULL)
1350 return 0;
1351 lua_pushstring( L, t->name );
1352 return 1;
1353}
1354
1362static int aiL_taskdata( lua_State *L )
1363{
1364 const Task *t = ai_curTask( cur_pilot );
1365 /* Must have a task. */
1366 if (t == NULL)
1367 return 0;
1368 return ai_tasktarget( L, t );
1369}
1370
1378static int aiL_pushsubtask( lua_State *L )
1379{
1380 ai_createTask( L, 1 );
1381 return 0;
1382}
1383
1389static int aiL_popsubtask( lua_State *L )
1390{
1391 Task *t, *st;
1392 t = ai_curTask( cur_pilot );
1393
1394 /* Tasks must exist. */
1395 if (t == NULL)
1396 return NLUA_ERROR(
1397 L, _( "Trying to pop task when there are no tasks on the stack." ) );
1398 if (t->subtask == NULL)
1399 return NLUA_ERROR( L,
1400 _( "Trying to pop subtask when there are no subtasks "
1401 "for the task '%s'." ),
1402 t->name );
1403
1404 /* Exterminate, annihilate destroy. */
1405 st = t->subtask;
1406 t->subtask = st->next;
1407 st->next = NULL;
1408 ai_freetask( st );
1409 return 0;
1410}
1411
1419static int aiL_subtaskname( lua_State *L )
1420{
1421 const Task *t = ai_curTask( cur_pilot );
1422 if (( t != NULL ) && ( t->subtask != NULL ))
1423 lua_pushstring( L, t->subtask->name );
1424 else
1425 lua_pushnil( L );
1426 return 1;
1427}
1428
1436static int aiL_subtaskdata( lua_State *L )
1437{
1438 const Task *t = ai_curTask( cur_pilot );
1439 /* Must have a subtask. */
1440 if (( t == NULL ) || ( t->subtask == NULL ))
1441 return 0;
1442
1443 return ai_tasktarget( L, t->subtask );
1444}
1445
1452static int aiL_pilot( lua_State *L )
1453{
1454 lua_pushpilot( L, cur_pilot->id );
1455 return 1;
1456}
1457
1464static int aiL_getrndpilot( lua_State *L )
1465{
1466 Pilot *const *pilot_stack = pilot_getAll();
1467 int p = RNG( 0, array_size( pilot_stack ) - 1 );
1468 /* Make sure it can't be the same pilot. */
1469 if (pilot_stack[p]->id == cur_pilot->id) {
1470 p++;
1471 if (p >= array_size( pilot_stack ))
1472 p = 0;
1473 }
1474 /* Last check. */
1475 if (pilot_stack[p]->id == cur_pilot->id)
1476 return 0;
1477 /* Actually found a pilot. */
1478 lua_pushpilot( L, pilot_stack[p]->id );
1479 return 1;
1480}
1481
1488static int aiL_getnearestpilot( lua_State *L )
1489{
1490 /* dist will be initialized to a number */
1491 /* this will only seek out pilots closer than dist */
1492 Pilot *const *pilot_stack = pilot_getAll();
1493 int dist = 1e6;
1494 int candidate_id = -1;
1495
1496 /*cycle through all the pilots and find the closest one that is not the pilot
1497 */
1498 for (int i = 0; i < array_size( pilot_stack ); i++) {
1499 if (pilot_stack[i]->id == cur_pilot->id)
1500 continue;
1501 if (vec2_dist( &pilot_stack[i]->solid.pos, &cur_pilot->solid.pos ) > dist)
1502 continue;
1503 dist = vec2_dist( &pilot_stack[i]->solid.pos, &cur_pilot->solid.pos );
1504 candidate_id = i;
1505 }
1506
1507 /* Last check. */
1508 if (candidate_id == -1)
1509 return 0;
1510
1511 /* Actually found a pilot. */
1512 lua_pushpilot( L, pilot_stack[candidate_id]->id );
1513 return 1;
1514}
1515
1523static int aiL_getdistance( lua_State *L )
1524{
1525 const vec2 *v;
1526
1527 /* vector as a parameter */
1528 if (lua_isvector( L, 1 ))
1529 v = lua_tovector( L, 1 );
1530 /* pilot as parameter */
1531 else if (lua_ispilot( L, 1 )) {
1532 const Pilot *p = luaL_validpilot( L, 1 );
1533 v = &p->solid.pos;
1534 }
1535 /* wrong parameter */
1536 else
1537 NLUA_INVALID_PARAMETER( L, 1 );
1538
1539 lua_pushnumber( L, vec2_dist( v, &cur_pilot->solid.pos ) );
1540 return 1;
1541}
1542
1550static int aiL_getdistance2( lua_State *L )
1551{
1552 const vec2 *v;
1553
1554 /* vector as a parameter */
1555 if (lua_isvector( L, 1 ))
1556 v = lua_tovector( L, 1 );
1557 /* pilot as parameter */
1558 else if (lua_ispilot( L, 1 )) {
1559 const Pilot *p = luaL_validpilot( L, 1 );
1560 v = &p->solid.pos;
1561 }
1562 /* wrong parameter */
1563 else
1564 NLUA_INVALID_PARAMETER( L, 1 );
1565
1566 lua_pushnumber( L, vec2_dist2( v, &cur_pilot->solid.pos ) );
1567 return 1;
1568}
1569
1578static int aiL_getflybydistance( lua_State *L )
1579{
1580 const vec2 *v;
1581 vec2 perp_motion_unit, offset_vect;
1582 int offset_distance;
1583
1584 /* vector as a parameter */
1585 if (lua_isvector( L, 1 ))
1586 v = lua_tovector( L, 1 );
1587 /* pilot id as parameter */
1588 else if (lua_ispilot( L, 1 )) {
1589 const Pilot *p = luaL_validpilot( L, 1 );
1590 v = &p->solid.pos;
1591
1592 /*vec2_cset(&v, VX(pilot->solid.pos) - VX(cur_pilot->solid.pos),
1593 * VY(pilot->solid.pos) - VY(cur_pilot->solid.pos) );*/
1594 } else
1595 NLUA_INVALID_PARAMETER( L, 1 );
1596
1597 vec2_cset( &offset_vect, VX( *v ) - VX( cur_pilot->solid.pos ),
1598 VY( *v ) - VY( cur_pilot->solid.pos ) );
1599 vec2_pset( &perp_motion_unit, 1, VANGLE( cur_pilot->solid.vel ) + M_PI_2 );
1600 offset_distance = vec2_dot( &perp_motion_unit, &offset_vect );
1601
1602 lua_pushnumber( L, offset_distance );
1603 return 1;
1604}
1605
1621static int aiL_minbrakedist( lua_State *L )
1622{
1623 /* More complicated calculation based on relative velocity. */
1624 if (lua_gettop( L ) > 0) {
1625 double time, dist, vel;
1626 vec2 vv;
1627 const Pilot *p = luaL_validpilot( L, 1 );
1628
1629 /* Set up the vectors. */
1630 vec2_cset( &vv, p->solid.vel.x - cur_pilot->solid.vel.x,
1631 p->solid.vel.y - cur_pilot->solid.vel.y );
1632
1633 /* Run the same calculations. */
1634 time = VMOD( vv ) / cur_pilot->accel + ai_dt;
1635
1636 /* Get relative velocity. */
1637 vel = MIN( cur_pilot->speed - VMOD( p->solid.vel ), VMOD( vv ) );
1638 if (vel < 0.)
1639 vel = 0.;
1640 /* Get distance to brake. */
1641 double flytime = time + M_PI / cur_pilot->turn + ai_dt;
1642 dist = vel * (flytime)-0.5 * ( cur_pilot->accel ) * time * time;
1643 lua_pushnumber( L, dist );
1644 lua_pushnumber( L, flytime );
1645 } else {
1646 double flytime;
1647 double dist = pilot_minbrakedist( cur_pilot, ai_dt, &flytime );
1648 lua_pushnumber( L, dist );
1649 lua_pushnumber( L, flytime );
1650 }
1651 return 2;
1652}
1653
1661static int aiL_isbribed( lua_State *L )
1662{
1663 const Pilot *p = luaL_validpilot( L, 1 );
1664 lua_pushboolean( L, pilot_isWithPlayer( p ) &&
1665 pilot_isFlag( cur_pilot, PILOT_BRIBED ) );
1666 return 1;
1667}
1668
1675static int aiL_instantJump( lua_State *L )
1676{
1677 lua_pushboolean( L, cur_pilot->stats.misc_instant_jump );
1678 return 1;
1679}
1680
1687static int aiL_ismaxvel( lua_State *L )
1688{
1689 // lua_pushboolean(L,(VMOD(cur_pilot->solid.vel) >
1690 // (cur_pilot->speed-MIN_VEL_ERR)));
1691 lua_pushboolean( L, ( VMOD( cur_pilot->solid.vel ) >
1692 ( solid_maxspeed( &cur_pilot->solid, cur_pilot->speed,
1693 cur_pilot->accel ) -
1694 MIN_VEL_ERR ) ) );
1695 return 1;
1696}
1697
1704static int aiL_isstopped( lua_State *L )
1705{
1706 lua_pushboolean( L, ( VMOD( cur_pilot->solid.vel ) < MIN_VEL_ERR ) );
1707 return 1;
1708}
1709
1717static int aiL_isenemy( lua_State *L )
1718{
1719 const Pilot *p = luaL_validpilot( L, 1 );
1720
1721 /* Player needs special handling in case of hostility. */
1722 if (pilot_isWithPlayer( p )) {
1723 lua_pushboolean( L, pilot_isHostile( cur_pilot ) );
1724 return 1;
1725 }
1726
1727 /* Check if is ally. */
1728 lua_pushboolean( L, areEnemies( cur_pilot->faction, p->faction ) );
1729 return 1;
1730}
1731
1739static int aiL_isally( lua_State *L )
1740{
1741 const Pilot *p = luaL_validpilot( L, 1 );
1742
1743 /* Player needs special handling in case of friendliness. */
1744 if (pilot_isWithPlayer( p )) {
1745 lua_pushboolean( L, pilot_isFriendly( cur_pilot ) );
1746 return 1;
1747 }
1748
1749 /* Check if is ally. */
1750 lua_pushboolean( L, areAllies( cur_pilot->faction, p->faction ) );
1751 return 1;
1752}
1753
1760
1761static int aiL_haslockon( lua_State *L )
1762{
1763 lua_pushboolean( L, cur_pilot->lockons > 0 );
1764 return 1;
1765}
1766
1773
1774static int aiL_hasprojectile( lua_State *L )
1775{
1776 lua_pushboolean( L, cur_pilot->projectiles > 0 );
1777 return 1;
1778}
1779
1786
1787static int aiL_scandone( lua_State *L )
1788{
1789 pilot_setFlag(
1790 cur_pilot, PILOT_SCANNING ); /*< Indicate pilot is scanning this frame. */
1791 lua_pushboolean( L, pilot_ewScanCheck( cur_pilot ) );
1792 return 1;
1793}
1794
1802static int aiL_accel( lua_State *L )
1803{
1804 double n = luaL_optnumber( L, 1, 1. );
1805 pilot_acc = CLAMP( 0., 1., n );
1806 return 0;
1807}
1808
1815static int aiL_turn( lua_State *L )
1816{
1817 pilot_turn = luaL_checknumber( L, 1 );
1818 return 0;
1819}
1820
1835static int aiL_face( lua_State *L )
1836{
1837 const vec2 *tv; /* get the position to face */
1838 double k_diff, k_vel, diff, vx, vy, dx, dy;
1839 int vel;
1840
1841 /* Default gain. */
1842 k_diff = 1. / ( cur_pilot->turn * ai_dt );
1843 k_vel = 100.; /* overkill gain! */
1844
1845 /* Check if must invert. */
1846 if (lua_toboolean( L, 2 ))
1847 k_diff *= -1;
1848
1849 /* Get first parameter, aka what to face. */
1850 if (lua_ispilot( L, 1 )) {
1851 Pilot *p = luaL_validpilot( L, 1 );
1852 /* Target vector. */
1853 tv = &p->solid.pos;
1854 } else if (lua_isnumber( L, 1 )) {
1855 double d = lua_tonumber( L, 1 );
1856 diff = angle_diff( cur_pilot->solid.dir, d );
1857 /* Make pilot turn. */
1858 pilot_turn = k_diff * diff;
1859 /* Return angle away from target. */
1860 lua_pushnumber( L, ABS( diff ) );
1861 return 1;
1862 } else if (lua_isvector( L, 1 ))
1863 tv = lua_tovector( L, 1 );
1864 else
1865 NLUA_INVALID_PARAMETER( L, 1 );
1866
1867 /* Third parameter. */
1868 vel = lua_toboolean( L, 3 );
1869
1870 /* Tangential component of velocity vector
1871 *
1872 * v: velocity vector
1873 * d: direction vector
1874 *
1875 * d d d
1876 * v_t = v - ( v . --- ) * --- = v - ( v . ----- ) * d
1877 * |d| |d| |d|^2
1878 */
1879 /* Velocity vector. */
1880 vx = cur_pilot->solid.vel.x;
1881 vy = cur_pilot->solid.vel.y;
1882 /* Direction vector. */
1883 dx = tv->x - cur_pilot->solid.pos.x;
1884 dy = tv->y - cur_pilot->solid.pos.y;
1885 if (vel && ( dx || dy )) {
1886 /* Calculate dot product. */
1887 double d = ( vx * dx + vy * dy ) / ( dx * dx + dy * dy );
1888 /* Calculate tangential velocity. */
1889 vx = vx - d * dx;
1890 vy = vy - d * dy;
1891
1892 /* Add velocity compensation. */
1893 dx += -k_vel * vx;
1894 dy += -k_vel * vy;
1895 }
1896
1897 /* Compensate error and rotate. */
1898 diff = angle_diff( cur_pilot->solid.dir, atan2( dy, dx ) );
1899
1900 /* Make pilot turn. */
1901 pilot_turn = k_diff * diff;
1902
1903 /* Return angle away from target. */
1904 lua_pushnumber( L, ABS( diff ) );
1905 return 1;
1906}
1907
1925static int aiL_careful_face( lua_State *L )
1926{
1927 vec2 *tv, F, F1;
1928 Pilot *p;
1929 double d, diff, dist;
1930 Pilot *const *pilot_stack;
1931 int x, y, r;
1932
1933 /* Default gains. */
1934 const double k_diff = 1. / ( cur_pilot->turn * ai_dt );
1935 const double k_goal = 1.;
1936 const double k_enemy = 6e6;
1937
1938 /* Init some variables */
1940 p = cur_pilot;
1941
1942 /* Get first parameter, aka what to face. */
1943 if (lua_ispilot( L, 1 )) {
1944 p = luaL_validpilot( L, 1 );
1945 /* Target vector. */
1946 tv = &p->solid.pos;
1947 } else if (lua_isnumber( L, 1 )) {
1948 d = (double)lua_tonumber( L, 1 );
1949 if (d < 0.)
1950 tv = &cur_pilot->solid.pos;
1951 else
1952 NLUA_INVALID_PARAMETER( L, 1 );
1953 } else if (lua_isvector( L, 1 ))
1954 tv = lua_tovector( L, 1 );
1955 else
1956 NLUA_INVALID_PARAMETER( L, 1 );
1957
1958 /* Init the force, where F1 is roughly normalized to norm 1. */
1959 vec2_csetmin( &F, 0., 0. );
1960 vec2_cset( &F1, tv->x - cur_pilot->solid.pos.x,
1961 tv->y - cur_pilot->solid.pos.y );
1962 dist = VMOD( F1 ) + 0.1; /* Avoid / 0 */
1963 vec2_cset( &F1, F1.x * k_goal / dist, F1.y * k_goal / dist );
1964
1965 /* Cycle through all the pilots in order to compute the force */
1966 x = round( cur_pilot->solid.pos.x );
1967 y = round( cur_pilot->solid.pos.y );
1968 /* It's modulated by k_enemy * k_mult / dist^2, where k_mult<1 and
1969 * k_enemy=6e6 A distance of 5000 should give a maximum factor of 0.24, but
1970 * it should be far away enough to not matter (hopefully).. */
1971 r = 5000;
1972 pilot_collideQueryIL( &ai_qtquery, x - r, y - r, x + r, y + r );
1973 for (int i = 0; i < il_size( &ai_qtquery ); i++) {
1974 const Pilot *p_i = pilot_stack[il_get( &ai_qtquery, i, 0 )];
1975
1976 /* Valid pilot isn't self, is in range, isn't the target and isn't
1977 * disabled */
1978 if (p_i->id == cur_pilot->id)
1979 continue;
1980 if (p_i->id == p->id)
1981 continue;
1982 if (pilot_isDisabled( p_i ))
1983 continue;
1984 if (pilot_inRangePilot( cur_pilot, p_i, &dist ) != 1)
1985 continue;
1986
1987 /* If the enemy is too close, ignore it*/
1988 if (dist < pow2( 750. ))
1989 continue;
1990 dist = sqrt( dist ); /* Have to undo the square. */
1991
1992 /* Check if friendly or not */
1993 if (areEnemies( cur_pilot->faction, p_i->faction )) {
1994 double k_mult =
1995 pilot_relhp( p_i, cur_pilot ) * pilot_reldps( p_i, cur_pilot );
1996 double factor = k_enemy * k_mult / ( dist * dist * dist );
1997 vec2_csetmin(
1998 &F, F.x + factor * ( cur_pilot->solid.pos.x - p_i->solid.pos.x ),
1999 F.y + factor * ( cur_pilot->solid.pos.y - p_i->solid.pos.y ) );
2000 }
2001 }
2002
2003 vec2_cset( &F, F.x + F1.x, F.y + F1.y );
2004
2005 /* Rotate. */
2006 diff = angle_diff( cur_pilot->solid.dir, VANGLE( F ) );
2007
2008 /* Make pilot turn. */
2009 pilot_turn = k_diff * diff;
2010
2011 /* Return angle away from target. */
2012 lua_pushnumber( L, ABS( diff ) );
2013 return 1;
2014}
2015
2027static int aiL_aim( lua_State *L )
2028{
2029 double diff, mod, angle;
2030
2031 if (lua_isasteroid( L, 1 )) {
2032 const Asteroid *a = luaL_validasteroid( L, 1 );
2033 angle = pilot_aimAngle( cur_pilot, &a->sol.pos, &a->sol.vel );
2034 } else {
2035 const Pilot *p = luaL_validpilot( L, 1 );
2036 angle = pilot_aimAngle( cur_pilot, &p->solid.pos, &p->solid.vel );
2037 }
2038
2039 /* Calculate what we need to turn */
2040 mod = 1. / ( cur_pilot->turn * ai_dt );
2041 diff = angle_diff( cur_pilot->solid.dir, angle );
2042 pilot_turn = mod * diff;
2043
2044 lua_pushnumber( L, ABS( diff ) );
2045 return 1;
2046}
2047
2056static int aiL_iface( lua_State *L )
2057{
2058 NLUA_MIN_ARGS( 1 );
2059 vec2 *vec, drift, reference_vector; /* get the position to face */
2060 Pilot *p;
2061 double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
2062
2063 /* Get first parameter, aka what to face. */
2064 p = NULL;
2065 vec = NULL;
2066 if (lua_ispilot( L, 1 ))
2067 p = luaL_validpilot( L, 1 );
2068 else if (lua_isvector( L, 1 ))
2069 vec = lua_tovector( L, 1 );
2070 else
2071 NLUA_INVALID_PARAMETER( L, 1 );
2072
2073 if (vec == NULL) {
2074 if (p == NULL)
2075 return 0; /* Return silently when attempting to face an invalid pilot.
2076 */
2077 /* Establish the current pilot velocity and position vectors */
2078 vec2_cset( &drift, VX( p->solid.vel ) - VX( cur_pilot->solid.vel ),
2079 VY( p->solid.vel ) - VY( cur_pilot->solid.vel ) );
2080 /* Establish the in-line coordinate reference */
2081 vec2_cset( &reference_vector,
2082 VX( p->solid.pos ) - VX( cur_pilot->solid.pos ),
2083 VY( p->solid.pos ) - VY( cur_pilot->solid.pos ) );
2084 } else {
2085 /* Establish the current pilot velocity and position vectors */
2086 vec2_cset( &drift, -VX( cur_pilot->solid.vel ),
2087 -VY( cur_pilot->solid.vel ) );
2088 /* Establish the in-line coordinate reference */
2089 vec2_cset( &reference_vector, VX( *vec ) - VX( cur_pilot->solid.pos ),
2090 VY( *vec ) - VY( cur_pilot->solid.pos ) );
2091 }
2092
2093 /* Break down the the velocity vectors of both craft into UV coordinates */
2094 vec2_uv( &drift_radial, &drift_azimuthal, &drift, &reference_vector );
2095 heading_offset_azimuth =
2096 angle_diff( cur_pilot->solid.dir, VANGLE( reference_vector ) );
2097
2098 /* Now figure out what to do...
2099 * Are we pointing anywhere inside the correct UV quadrant?
2100 * if we're outside the correct UV quadrant, we need to get into it ASAP
2101 * Otherwise match velocities and approach */
2102 if (FABS( heading_offset_azimuth ) < M_PI_2) {
2103 /* This indicates we're in the correct plane*/
2104 /* 1 - 1/(|x|+1) does a pretty nice job of mapping the reals to the
2105 * interval (0...1). That forms the core of this angle calculation */
2106 /* There is nothing special about the scaling parameter of 200; it can be
2107 tuned to get any behavior desired. A lower number will give a more
2108 dramatic 'lead' */
2109 double speedmap =
2110 -copysign( 1. - 1. / ( FABS( drift_azimuthal / 200. ) + 1. ),
2111 drift_azimuthal ) *
2112 M_PI_2;
2113 diff = angle_diff( heading_offset_azimuth, speedmap );
2114 pilot_turn = -diff / ( ai_dt * cur_pilot->turn );
2115 }
2116 /* turn most efficiently to face the target. If we intercept the correct
2117 quadrant in the UV plane first, then the code above will kick in */
2118 /* some special case logic is added to optimize turn time. Reducing this to
2119 only the else cases would speed up the operation but cause the pilot to
2120 turn in the less-than-optimal direction sometimes when between 135 and 225
2121 degrees off from the target */
2122 else {
2123 /* signal that we're not in a productive direction for accelerating */
2124 diff = M_PI;
2125 pilot_turn = heading_offset_azimuth / ( ai_dt * cur_pilot->turn );
2126 }
2127
2128 /* Return angle in degrees away from target. */
2129 lua_pushnumber( L, ABS( diff ) );
2130 return 1;
2131}
2132
2141static int aiL_dir( lua_State *L )
2142{
2143 vec2 sv, tv; /* get the position to face */
2144 double diff;
2145
2146 /* Get first parameter, aka what to face. */
2147 vec2_cset( &sv, VX( cur_pilot->solid.pos ), VY( cur_pilot->solid.pos ) );
2148 if (lua_ispilot( L, 1 )) {
2149 const Pilot *p = luaL_validpilot( L, 1 );
2150 vec2_cset( &tv, VX( p->solid.pos ), VY( p->solid.pos ) );
2151 diff = angle_diff( cur_pilot->solid.dir, vec2_angle( &sv, &tv ) );
2152 } else if (lua_isvector( L, 1 )) {
2153 const vec2 *vec = lua_tovector( L, 1 );
2154 diff = angle_diff( cur_pilot->solid.dir,
2155 vec2_angle( &cur_pilot->solid.pos, vec ) );
2156 } else
2157 NLUA_INVALID_PARAMETER( L, 1 );
2158
2159 /* Return angle in degrees away from target. */
2160 lua_pushnumber( L, diff );
2161 return 1;
2162}
2163
2172static int aiL_idir( lua_State *L )
2173{
2174 NLUA_MIN_ARGS( 1 );
2175 vec2 *vec, drift, reference_vector; /* get the position to face */
2176 Pilot *p;
2177 double diff, heading_offset_azimuth, drift_radial, drift_azimuthal;
2178
2179 /* Get first parameter, aka what to face. */
2180 p = NULL;
2181 vec = NULL;
2182 if (lua_ispilot( L, 1 ))
2183 p = luaL_validpilot( L, 1 );
2184 else if (lua_isvector( L, 1 ))
2185 vec = lua_tovector( L, 1 );
2186 else
2187 NLUA_INVALID_PARAMETER( L, 1 );
2188
2189 if (vec == NULL) {
2190 if (p == NULL)
2191 return 0; /* Return silently when attempting to face an invalid pilot.
2192 */
2193 /* Establish the current pilot velocity and position vectors */
2194 vec2_cset( &drift, VX( p->solid.vel ) - VX( cur_pilot->solid.vel ),
2195 VY( p->solid.vel ) - VY( cur_pilot->solid.vel ) );
2196 /* Establish the in-line coordinate reference */
2197 vec2_cset( &reference_vector,
2198 VX( p->solid.pos ) - VX( cur_pilot->solid.pos ),
2199 VY( p->solid.pos ) - VY( cur_pilot->solid.pos ) );
2200 } else {
2201 /* Establish the current pilot velocity and position vectors */
2202 vec2_cset( &drift, -VX( cur_pilot->solid.vel ),
2203 -VY( cur_pilot->solid.vel ) );
2204 /* Establish the in-line coordinate reference */
2205 vec2_cset( &reference_vector, VX( *vec ) - VX( cur_pilot->solid.pos ),
2206 VY( *vec ) - VY( cur_pilot->solid.pos ) );
2207 }
2208
2209 /* Break down the the velocity vectors of both craft into UV coordinates */
2210 vec2_uv( &drift_radial, &drift_azimuthal, &drift, &reference_vector );
2211 heading_offset_azimuth =
2212 angle_diff( cur_pilot->solid.dir, VANGLE( reference_vector ) );
2213
2214 /* Now figure out what to do...
2215 * Are we pointing anywhere inside the correct UV quadrant?
2216 * if we're outside the correct UV quadrant, we need to get into it ASAP
2217 * Otherwise match velocities and approach */
2218 if (FABS( heading_offset_azimuth ) < M_PI_2) {
2219 /* This indicates we're in the correct plane*/
2220 /* 1 - 1/(|x|+1) does a pretty nice job of mapping the reals to the
2221 * interval (0...1). That forms the core of this angle calculation */
2222 /* There is nothing special about the scaling parameter of 200; it can be
2223 tuned to get any behavior desired. A lower number will give a more
2224 dramatic 'lead' */
2225 double speedmap =
2226 -copysign( 1. - 1. / ( FABS( drift_azimuthal / 200. ) + 1. ),
2227 drift_azimuthal ) *
2228 M_PI_2;
2229 diff = -angle_diff( heading_offset_azimuth, speedmap );
2230 }
2231 /* turn most efficiently to face the target. If we intercept the correct
2232 quadrant in the UV plane first, then the code above will kick in */
2233 /* some special case logic is added to optimize turn time. Reducing this to
2234 only the else cases would speed up the operation but cause the pilot to
2235 turn in the less-than-optimal direction sometimes when between 135 and 225
2236 degrees off from the target */
2237 else {
2238 diff = heading_offset_azimuth;
2239 }
2240
2241 /* Return angle in degrees away from target. */
2242 lua_pushnumber( L, diff );
2243 return 1;
2244}
2245
2253static int aiL_drift_facing( lua_State *L )
2254{
2255 double drift =
2256 angle_diff( VANGLE( cur_pilot->solid.vel ), cur_pilot->solid.dir );
2257 lua_pushnumber( L, drift );
2258 return 1;
2259}
2260
2269static int aiL_brake( lua_State *L )
2270{
2271 double dir, accel, diff;
2272 int isstopped = pilot_isStopped( cur_pilot );
2273 int prefer_rev =
2274 lua_toboolean( L, 1 ) * cur_pilot->stats.misc_reverse_thrust;
2275
2276 if (isstopped) {
2277 lua_pushboolean( L, 1 );
2278 return 1;
2279 }
2280
2281 if (prefer_rev || pilot_brakeCheckReverseThrusters( cur_pilot )) {
2282 dir = VANGLE( cur_pilot->solid.vel );
2283 accel = -PILOT_REVERSE_THRUST;
2284 } else {
2285 dir = VANGLE( cur_pilot->solid.vel ) + M_PI;
2286 accel = 1.;
2287 }
2288
2289 diff = angle_diff( cur_pilot->solid.dir, dir );
2290 pilot_turn = diff / ( cur_pilot->turn * ai_dt );
2291 if (ABS( diff ) < MIN_DIR_ERR)
2292 pilot_acc = accel;
2293 else
2294 pilot_acc = 0.;
2295 lua_pushboolean( L, 0 );
2296 return 1;
2297}
2298
2305static int aiL_getnearestspob( lua_State *L )
2306{
2307 double dist, d;
2308 int j;
2309 LuaSpob spob;
2310
2311 /* cycle through spobs */
2312 dist = HUGE_VAL;
2313 j = -1;
2314 for (int i = 0; i < array_size( cur_system->spobs ); i++) {
2315 if (!spob_hasService( cur_system->spobs[i], SPOB_SERVICE_INHABITED ))
2316 continue;
2317 d = vec2_dist( &cur_system->spobs[i]->pos, &cur_pilot->solid.pos );
2318 if (( !areEnemies( cur_pilot->faction,
2319 cur_system->spobs[i]->presence.faction ) ) &&
2320 ( d < dist )) { /* closer friendly spob */
2321 j = i;
2322 dist = d;
2323 }
2324 }
2325
2326 /* no friendly spob found */
2327 if (j == -1)
2328 return 0;
2329
2330 cur_pilot->nav_spob = j;
2331 spob = cur_system->spobs[j]->id;
2332 lua_pushspob( L, spob );
2333
2334 return 1;
2335}
2336
2344static int aiL_getspobfrompos( lua_State *L )
2345{
2346 int j;
2347 double dist;
2348 LuaSpob spob;
2349 const vec2 *pos = luaL_checkvector( L, 1 );
2350
2351 /* cycle through spobs */
2352 dist = HUGE_VAL;
2353 j = -1;
2354 for (int i = 0; i < array_size( cur_system->spobs ); i++) {
2355 double d;
2356 if (!spob_hasService( cur_system->spobs[i], SPOB_SERVICE_INHABITED ))
2357 continue;
2358 d = vec2_dist( &cur_system->spobs[i]->pos, pos );
2359 if (( !areEnemies( cur_pilot->faction,
2360 cur_system->spobs[i]->presence.faction ) ) &&
2361 ( d < dist )) { /* closer friendly spob */
2362 j = i;
2363 dist = d;
2364 }
2365 }
2366
2367 /* no friendly spob found */
2368 if (j == -1)
2369 return 0;
2370
2371 cur_pilot->nav_spob = j;
2372 spob = cur_system->spobs[j]->id;
2373 lua_pushspob( L, spob );
2374
2375 return 1;
2376}
2377
2384static int aiL_getrndspob( lua_State *L )
2385{
2386 LuaSpob spob;
2387 int p;
2388
2389 /* No spobs. */
2390 if (array_size( cur_system->spobs ) == 0)
2391 return 0;
2392
2393 /* get a random spob */
2394 p = RNG( 0, array_size( cur_system->spobs ) - 1 );
2395
2396 /* Copy the data into a vector */
2397 spob = cur_system->spobs[p]->id;
2398 lua_pushspob( L, spob );
2399
2400 return 1;
2401}
2402
2410static int aiL_getlandspob( lua_State *L )
2411{
2412 int *ind;
2413 int id;
2414 LuaSpob spob;
2415 const Spob *p;
2416 int only_friend;
2417
2418 /* If pilot can't land ignore. */
2419 if (pilot_isFlag( cur_pilot, PILOT_NOLAND ))
2420 return 0;
2421
2422 /* Check if we should get only friendlies. */
2423 only_friend = lua_toboolean( L, 1 );
2424
2425 /* Allocate memory. */
2426 ind = array_create_size( int, array_size( cur_system->spobs ) );
2427
2428 /* Copy friendly spob.s */
2429 for (int i = 0; i < array_size( cur_system->spobs ); i++) {
2430 Spob *pnt = cur_system->spobs[i];
2431
2432 if (!spob_hasService( pnt, SPOB_SERVICE_LAND ))
2433 continue;
2434 if (!spob_hasService( pnt, SPOB_SERVICE_INHABITED ))
2435 continue;
2436
2437 /* Check conditions. */
2438 if (only_friend &&
2439 !areAllies( cur_pilot->faction, pnt->presence.faction ))
2440 continue;
2441 if (areEnemies( cur_pilot->faction, pnt->presence.faction ))
2442 continue;
2443
2444 /* Add it. */
2445 array_push_back( &ind, i );
2446 }
2447
2448 /* no spob to land on found */
2449 if (array_size( ind ) == 0) {
2450 array_free( ind );
2451 return 0;
2452 }
2453
2454 /* we can actually get a random spob now */
2455 id = RNG( 0, array_size( ind ) - 1 );
2456 p = cur_system->spobs[ind[id]];
2457 spob = p->id;
2458 lua_pushspob( L, spob );
2459 cur_pilot->nav_spob = ind[id];
2460 array_free( ind );
2461
2462 return 1;
2463}
2464
2472static int aiL_land( lua_State *L )
2473{
2474 const Spob *spob;
2475 HookParam hparam;
2476
2477 if (!lua_isnoneornil( L, 1 )) {
2478 int i;
2479 const Spob *pnt = luaL_validspob( L, 1 );
2480
2481 /* Find the spob. */
2482 for (i = 0; i < array_size( cur_system->spobs ); i++) {
2483 if (cur_system->spobs[i] == pnt) {
2484 break;
2485 }
2486 }
2487 if (i >= array_size( cur_system->spobs ))
2488 return NLUA_ERROR( L, _( "Spob '%s' not found in system '%s'" ),
2489 pnt->name, cur_system->name );
2490
2491 cur_pilot->nav_spob = i;
2492 }
2493
2494 if (cur_pilot->nav_spob < 0)
2495 return NLUA_ERROR( L, _( "Pilot '%s' (ai '%s') has no land target" ),
2496 cur_pilot->name, cur_pilot->ai->name );
2497
2498 /* Get spob. */
2499 spob = cur_system->spobs[cur_pilot->nav_spob];
2500
2501 /* Check landability. */
2502 if (( spob->lua_can_land == LUA_NOREF ) &&
2503 !spob_hasService( spob, SPOB_SERVICE_LAND )) { /* Basic services */
2504 lua_pushboolean( L, 0 );
2505 return 1;
2506 }
2507 /* TODO can_land is player-specific, we need to implement this on a pilot
2508 level... if ((!pilot_isFlag(cur_pilot, PILOT_MANUAL_CONTROL) &&
2509 !spob->can_land)) { lua_pushboolean(L,0); return 1;
2510 }
2511 */
2512
2513 /* Check landing functionality. */
2514 if (pilot_isFlag( cur_pilot, PILOT_NOLAND )) {
2515 lua_pushboolean( L, 0 );
2516 return 1;
2517 }
2518
2519 /* Check distance. */
2520 if (vec2_dist2( &cur_pilot->solid.pos, &spob->pos ) > pow2( spob->radius )) {
2521 lua_pushboolean( L, 0 );
2522 return 1;
2523 }
2524
2525 /* Check velocity. */
2526 if (vec2_odist2( &cur_pilot->solid.vel ) > pow2( MAX_HYPERSPACE_VEL )) {
2527 lua_pushboolean( L, 0 );
2528 return 1;
2529 }
2530
2531 if (spob->lua_land == LUA_NOREF) {
2532 cur_pilot->landing_delay =
2533 PILOT_LANDING_DELAY * cur_pilot->ship->dt_default;
2534 cur_pilot->ptimer = cur_pilot->landing_delay;
2535 pilot_setFlag( cur_pilot, PILOT_LANDING );
2536 } else {
2537 lua_rawgeti( naevL, LUA_REGISTRYINDEX, spob->lua_land ); /* f */
2538 lua_pushspob( naevL, spob_index( spob ) );
2540 if (nlua_pcall( spob->lua_env, 2, 0 )) {
2541 NLUA_WARN( L, _( "Spob '%s' failed to run '%s':\n%s" ), spob->name,
2542 "land", lua_tostring( naevL, -1 ) );
2543 lua_pop( naevL, 1 );
2544 }
2545 }
2546
2547 hparam.type = HOOK_PARAM_SPOB;
2548 hparam.u.la = spob->id;
2549
2550 pilot_runHookParam( cur_pilot, PILOT_HOOK_LAND, &hparam, 1 );
2551 lua_pushboolean( L, 1 );
2552 return 1;
2553}
2554
2562static int aiL_hyperspace( lua_State *L )
2563{
2564 int canjump;
2565
2566 /* Find the target jump. */
2567 if (!lua_isnoneornil( L, 1 )) {
2568 const JumpPoint *jp = luaL_validjump( L, 1 );
2569 const LuaJump *lj = luaL_checkjump( L, 1 );
2570 if (lj->srcid != cur_system->id)
2571 return NLUA_ERROR( L, _( "Jump point must be in current system." ) );
2572 cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2573 }
2574
2575 canjump = space_hyperspace( cur_pilot );
2576 lua_pushnumber( L, canjump );
2577 return 1;
2578}
2579
2587static int aiL_sethyptarget( lua_State *L )
2588{
2589 const JumpPoint *jp;
2590 const LuaJump *lj;
2591 vec2 vec;
2592 double a, rad;
2593
2594 lj = luaL_checkjump( L, 1 );
2595 jp = luaL_validjump( L, 1 );
2596
2597 if (lj->srcid != cur_system->id)
2598 return NLUA_ERROR( L, _( "Jump point must be in current system." ) );
2599
2600 /* Copy vector. */
2601 vec = jp->pos;
2602
2603 /* Introduce some error. */
2604 a = RNGF() * M_PI * 2.;
2605 rad = RNGF() * 0.5 * jp->radius;
2606 vec2_cadd( &vec, rad * cos( a ), rad * sin( a ) );
2607
2608 /* Set up target. */
2609 cur_pilot->nav_hyperspace = jp - cur_system->jumps;
2610
2611 /* Return vector. */
2612 lua_pushvector( L, vec );
2613
2614 return 1;
2615}
2616
2623static int aiL_nearhyptarget( lua_State *L )
2624{
2625 const JumpPoint *jp;
2626 double mindist, dist;
2627 LuaJump lj;
2628
2629 /* Find nearest jump .*/
2630 mindist = INFINITY;
2631 jp = NULL;
2632 for (int i = 0; i < array_size( cur_system->jumps ); i++) {
2633 const JumpPoint *jiter = &cur_system->jumps[i];
2634 int useshidden = faction_usesHiddenJumps( cur_pilot->faction );
2635
2636 /* Ignore exit only. */
2637 if (jp_isFlag( jiter, JP_EXITONLY ))
2638 continue;
2639
2640 /* We want only standard jump points to be used. */
2641 if (!useshidden && jp_isFlag( jiter, JP_HIDDEN ))
2642 continue;
2643
2644 /* Only jump if there is presence there. */
2645 if (system_getPresence( jiter->target, cur_pilot->faction ) <= 0.)
2646 continue;
2647
2648 /* Get nearest distance. */
2649 dist = vec2_dist2( &cur_pilot->solid.pos, &jiter->pos );
2650 if (dist < mindist) {
2651 jp = jiter;
2652 mindist = dist;
2653 }
2654 }
2655 /* None available. */
2656 if (jp == NULL)
2657 return 0;
2658
2659 lj.destid = jp->targetid;
2660 lj.srcid = cur_system->id;
2661
2662 /* Return Jump. */
2663 lua_pushjump( L, lj );
2664 return 1;
2665}
2666
2673static int aiL_rndhyptarget( lua_State *L )
2674{
2675 JumpPoint **jumps;
2676 int r, useshidden;
2677 int *id;
2678 LuaJump lj;
2679
2680 /* No jumps in the system. */
2681 if (array_size( cur_system->jumps ) == 0)
2682 return 0;
2683
2684 useshidden = faction_usesHiddenJumps( cur_pilot->faction );
2685
2686 /* Find usable jump points. */
2687 jumps = array_create_size( JumpPoint *, array_size( cur_system->jumps ) );
2688 id = array_create_size( int, array_size( cur_system->jumps ) );
2689 for (int i = 0; i < array_size( cur_system->jumps ); i++) {
2690 JumpPoint *jiter = &cur_system->jumps[i];
2691
2692 /* We want only standard jump points to be used. */
2693 if (( !useshidden && jp_isFlag( jiter, JP_HIDDEN ) ) ||
2694 jp_isFlag( jiter, JP_EXITONLY ))
2695 continue;
2696
2697 /* Only jump if there is presence there. */
2698 if (system_getPresence( jiter->target, cur_pilot->faction ) <= 0.)
2699 continue;
2700
2701 array_push_back( &id, i );
2702 array_push_back( &jumps, jiter );
2703 }
2704
2705 /* Try to be more lax. */
2706 if (array_size( jumps ) <= 0) {
2707 for (int i = 0; i < array_size( cur_system->jumps ); i++) {
2708 JumpPoint *jiter = &cur_system->jumps[i];
2709
2710 /* We want only standard jump points to be used. */
2711 if (( !useshidden && jp_isFlag( jiter, JP_HIDDEN ) ) ||
2712 jp_isFlag( jiter, JP_EXITONLY ))
2713 continue;
2714
2715 array_push_back( &id, i );
2716 array_push_back( &jumps, jiter );
2717 }
2718 }
2719
2720 if (array_size( jumps ) <= 0) {
2721 NLUA_WARN( L, _( "Pilot '%s' can't find jump to leave system!" ),
2722 cur_pilot->name );
2723 return 0;
2724 }
2725
2726 /* Choose random jump point. */
2727 r = RNG( 0, MAX( array_size( jumps ) - 1, 0 ) );
2728
2729 lj.destid = jumps[r]->targetid;
2730 lj.srcid = cur_system->id;
2731
2732 /* Clean up. */
2733 array_free( jumps );
2734 array_free( id );
2735
2736 /* Return Jump. */
2737 lua_pushjump( L, lj );
2738 return 1;
2739}
2740
2747static int aiL_canHyperspace( lua_State *L )
2748{
2749 lua_pushboolean( L, space_canHyperspace( cur_pilot ) );
2750 return 1;
2751}
2752
2758static int aiL_hyperspaceAbort( lua_State *L )
2759{
2760 (void)L;
2762 return 0;
2763}
2764
2771static int aiL_relvel( lua_State *L )
2772{
2773 double dot, mod;
2774 vec2 vv, pv;
2775 int absolute;
2776 const Pilot *p = luaL_validpilot( L, 1 );
2777
2778 if (lua_gettop( L ) > 1)
2779 absolute = lua_toboolean( L, 2 );
2780 else
2781 absolute = 0;
2782
2783 /* Get the projection of target on current velocity. */
2784 if (absolute == 0)
2785 vec2_cset( &vv, p->solid.vel.x - cur_pilot->solid.vel.x,
2786 p->solid.vel.y - cur_pilot->solid.vel.y );
2787 else
2788 vec2_cset( &vv, p->solid.vel.x, p->solid.vel.y );
2789
2790 vec2_cset( &pv, p->solid.pos.x - cur_pilot->solid.pos.x,
2791 p->solid.pos.y - cur_pilot->solid.pos.y );
2792 dot = vec2_dot( &pv, &vv );
2793 mod = MAX( VMOD( pv ), 1. ); /* Avoid /0. */
2794
2795 lua_pushnumber( L, dot / mod );
2796 return 1;
2797}
2798
2814static int aiL_follow_accurate( lua_State *L )
2815{
2816 vec2 point, cons, goal, pv;
2817 double radius, angle, Kp, Kd, angle2;
2818 const Pilot *p, *target;
2819 const char *method;
2820
2821 p = cur_pilot;
2822 target = luaL_validpilot( L, 1 );
2823 radius = luaL_optnumber( L, 2, 0. );
2824 angle = luaL_optnumber( L, 3, 0. );
2825 Kp = luaL_optnumber( L, 4, 10. );
2826 Kd = luaL_optnumber( L, 5, 20. );
2827 method = luaL_optstring( L, 6, "velocity" );
2828
2829 if (strcmp( method, "absolute" ) == 0)
2830 angle2 = angle;
2831 else if (strcmp( method, "keepangle" ) == 0) {
2832 vec2_cset( &pv, p->solid.pos.x - target->solid.pos.x,
2833 p->solid.pos.y - target->solid.pos.y );
2834 angle2 = VANGLE( pv );
2835 } else /* method == "velocity" */
2836 angle2 = angle + VANGLE( target->solid.vel );
2837
2838 vec2_cset( &point, VX( target->solid.pos ) + radius * cos( angle2 ),
2839 VY( target->solid.pos ) + radius * sin( angle2 ) );
2840
2841 /* Compute the direction using a pd controller */
2842 vec2_cset( &cons,
2843 ( point.x - p->solid.pos.x ) * Kp +
2844 ( target->solid.vel.x - p->solid.vel.x ) * Kd,
2845 ( point.y - p->solid.pos.y ) * Kp +
2846 ( target->solid.vel.y - p->solid.vel.y ) * Kd );
2847
2848 vec2_cset( &goal, cons.x + p->solid.pos.x, cons.y + p->solid.pos.y );
2849
2850 /* Push info */
2851 lua_pushvector( L, goal );
2852
2853 return 1;
2854}
2855
2870static int aiL_face_accurate( lua_State *L )
2871{
2872 vec2 point, cons, goal, *pos, *vel;
2873 double radius, angle, Kp, Kd;
2874 const Pilot *p = cur_pilot;
2875
2876 pos = lua_tovector( L, 1 );
2877 vel = lua_tovector( L, 2 );
2878 radius = luaL_optnumber( L, 3, 0. );
2879 angle = luaL_optnumber( L, 4, 0. );
2880 Kp = luaL_optnumber( L, 5, 10. );
2881 Kd = luaL_optnumber( L, 6, 20. );
2882
2883 vec2_cset( &point, pos->x + radius * cos( angle ),
2884 pos->y + radius * sin( angle ) );
2885
2886 /* Compute the direction using a pd controller */
2887 vec2_cset(
2888 &cons,
2889 ( point.x - p->solid.pos.x ) * Kp + ( vel->x - p->solid.vel.x ) * Kd,
2890 ( point.y - p->solid.pos.y ) * Kp + ( vel->y - p->solid.vel.y ) * Kd );
2891
2892 vec2_cset( &goal, cons.x + p->solid.pos.x, cons.y + p->solid.pos.y );
2893
2894 /* Push info */
2895 lua_pushvector( L, goal );
2896
2897 return 1;
2898}
2899
2906static int aiL_stop( lua_State *L )
2907{
2908 (void)L; /* avoid gcc warning */
2909
2910 if (VMOD( cur_pilot->solid.vel ) < MIN_VEL_ERR)
2911 vec2_pset( &cur_pilot->solid.vel, 0., 0. );
2912
2913 return 0;
2914}
2915
2922static int aiL_dock( lua_State *L )
2923{
2924 /* Target is another ship. */
2925 Pilot *p = luaL_validpilot( L, 1 );
2926 pilot_dock( cur_pilot, p );
2927 return 0;
2928}
2929
2936static int aiL_combat( lua_State *L )
2937{
2938 if (lua_gettop( L ) > 0) {
2939 int i = lua_toboolean( L, 1 );
2940 if (i == 1)
2941 pilot_setFlag( cur_pilot, PILOT_COMBAT );
2942 else if (i == 0)
2943 pilot_rmFlag( cur_pilot, PILOT_COMBAT );
2944 } else
2945 pilot_setFlag( cur_pilot, PILOT_COMBAT );
2946
2947 return 0;
2948}
2949
2956static int aiL_settarget( lua_State *L )
2957{
2958 const Pilot *p = luaL_validpilot( L, 1 );
2959 pilot_setTarget( cur_pilot, p->id );
2960 return 0;
2961}
2962
2970static int aiL_setasterotarget( lua_State *L )
2971{
2972 const LuaAsteroid_t *la = luaL_checkasteroid( L, 1 );
2973
2974 /* Set the target asteroid. */
2975 cur_pilot->nav_anchor = la->parent;
2976 cur_pilot->nav_asteroid = la->id;
2977
2978 /* Untarget pilot. */
2979 cur_pilot->target = cur_pilot->id;
2980 cur_pilot->ptarget = NULL;
2981
2982 return 0;
2983}
2984
2992static int aiL_getGatherable( lua_State *L )
2993{
2994 int i;
2995 double rad;
2996
2997 if (( lua_gettop( L ) < 1 ) || lua_isnil( L, 1 ))
2998 rad = INFINITY;
2999 else
3000 rad = lua_tonumber( L, 1 );
3001
3002 i = gatherable_getClosest( &cur_pilot->solid.pos, rad );
3003
3004 if (i != -1)
3005 lua_pushnumber( L, i );
3006 else
3007 lua_pushnil( L );
3008
3009 return 1;
3010}
3011
3020static int aiL_gatherablePos( lua_State *L )
3021{
3022 int i, did;
3023 vec2 pos, vel;
3024
3025 i = lua_tointeger( L, 1 );
3026
3027 did = gatherable_getPos( &pos, &vel, i );
3028
3029 if (did == 0) /* No gatherable matching this ID. */
3030 return 0;
3031
3032 lua_pushvector( L, pos );
3033 lua_pushvector( L, vel );
3034
3035 return 2;
3036}
3037
3046static int aiL_weapSet( lua_State *L )
3047{
3048 int id, type;
3049 id = luaL_checkinteger( L, 1 ) - 1;
3050
3051 if (lua_gettop( L ) > 1)
3052 type = lua_toboolean( L, 2 );
3053 else
3054 type = 1;
3055
3056 /* weapset type is weapon or change */
3057 if (type)
3058 pilot_weapSetPress( cur_pilot, id, +1 );
3059 else
3060 pilot_weapSetPress( cur_pilot, id, -1 );
3061 return 0;
3062}
3063
3070static int aiL_hascannons( lua_State *L )
3071{
3072 lua_pushboolean( L, cur_pilot->ncannons > 0 );
3073 return 1;
3074}
3075
3082static int aiL_hasturrets( lua_State *L )
3083{
3084 lua_pushboolean( L, cur_pilot->nturrets > 0 );
3085 return 1;
3086}
3087
3094static int aiL_hasfighterbays( lua_State *L )
3095{
3096 lua_pushboolean( L, cur_pilot->nfighterbays > 0 );
3097 return 1;
3098}
3099
3106static int aiL_hasafterburner( lua_State *L )
3107{
3108 lua_pushboolean( L, cur_pilot->nafterburners > 0 );
3109 return 1;
3110}
3111
3120static int aiL_getenemy( lua_State *L )
3121{
3122 if (lua_isnoneornil( L, 1 )) {
3123 unsigned int id = pilot_getNearestEnemy( cur_pilot );
3124 if (id == 0) /* No enemy found */
3125 return 0;
3126 lua_pushpilot( L, id );
3127 return 1;
3128 } else {
3129 double range = luaL_checknumber( L, 1 );
3130 double r2 = pow2( range );
3131 unsigned int tp = 0;
3132 double d = 0.;
3133 int x, y, r;
3134 Pilot *const *pilot_stack = pilot_getAll();
3135
3136 r = ceil( range );
3137 x = round( cur_pilot->solid.pos.x );
3138 y = round( cur_pilot->solid.pos.y );
3139 pilot_collideQueryIL( &ai_qtquery, x - r, y - r, x + r, y + r );
3140 for (int i = 0; i < il_size( &ai_qtquery ); i++) {
3141 const Pilot *p = pilot_stack[il_get( &ai_qtquery, i, 0 )];
3142 double td;
3143
3144 if (vec2_dist2( &p->solid.pos, &cur_pilot->solid.pos ) > r2)
3145 continue;
3146
3147 if (!pilot_validEnemy( cur_pilot, p ))
3148 continue;
3149
3150 /* Check distance. */
3151 td = vec2_dist2( &p->solid.pos, &cur_pilot->solid.pos );
3152 if (!tp || ( td < d )) {
3153 d = td;
3154 tp = p->id;
3155 }
3156 }
3157 return tp;
3158 }
3159}
3160
3167static int aiL_hostile( lua_State *L )
3168{
3169 const Pilot *p = luaL_validpilot( L, 1 );
3170 if (pilot_isWithPlayer( p ))
3172 return 0;
3173}
3174
3183static int aiL_getweaprangemin( lua_State *L )
3184{
3185 int id = luaL_checkinteger( L, 1 ) - 1;
3186 lua_pushnumber( L, pilot_weapSetRangeMin( cur_pilot, id ) );
3187 return 1;
3188}
3189
3198static int aiL_getweaprange( lua_State *L )
3199{
3200 int id = luaL_checkinteger( L, 1 ) - 1;
3201 lua_pushnumber( L, pilot_weapSetRange( cur_pilot, id ) );
3202 return 1;
3203}
3204
3213static int aiL_getweapspeed( lua_State *L )
3214{
3215 int id = luaL_checkinteger( L, 1 ) - 1;
3216 lua_pushnumber( L, pilot_weapSetSpeed( cur_pilot, id ) );
3217 return 1;
3218}
3219
3228static int aiL_getweapammo( lua_State *L )
3229{
3230 int id = luaL_checkinteger( L, 1 ) - 1;
3231 lua_pushnumber( L, pilot_weapSetAmmo( cur_pilot, id ) );
3232 return 1;
3233}
3234
3242static int aiL_canboard( lua_State *L )
3243{
3244 const Pilot *p = luaL_validpilot( L, 1 );
3245
3246 /* Must be disabled. */
3247 if (!pilot_isDisabled( p )) {
3248 lua_pushboolean( L, 0 );
3249 return 1;
3250 }
3251
3252 /* Check if can be boarded. */
3253 lua_pushboolean( L, !pilot_isFlag( p, PILOT_BOARDED ) );
3254 return 1;
3255}
3256
3265static int aiL_relsize( lua_State *L )
3266{
3267 const Pilot *p = luaL_validpilot( L, 1 );
3268 lua_pushnumber( L, pilot_relsize( cur_pilot, p ) );
3269 return 1;
3270}
3271
3280static int aiL_reldps( lua_State *L )
3281{
3282 const Pilot *p = luaL_validpilot( L, 1 );
3283 lua_pushnumber( L, pilot_reldps( cur_pilot, p ) );
3284 return 1;
3285}
3286
3295static int aiL_relhp( lua_State *L )
3296{
3297 const Pilot *p = luaL_validpilot( L, 1 );
3298 lua_pushnumber( L, pilot_relhp( cur_pilot, p ) );
3299 return 1;
3300}
3301
3308static int aiL_board( lua_State *L )
3309{
3310 lua_pushboolean( L, pilot_board( cur_pilot ) );
3311 return 1;
3312}
3313
3320static int aiL_refuel( lua_State *L )
3321{
3322 lua_pushboolean( L, pilot_refuelStart( cur_pilot ) );
3323 return 1;
3324}
3325
3333static int aiL_settimer( lua_State *L )
3334{
3335 int n = luaL_checkint( L, 1 );
3336 /* Set timer. */
3337 cur_pilot->timer[n] = luaL_optnumber( L, 2, 0. );
3338 return 0;
3339}
3340
3348
3349static int aiL_timeup( lua_State *L )
3350{
3351 int n = luaL_checkint( L, 1 );
3352 lua_pushboolean( L, cur_pilot->timer[n] <= 0. );
3353 return 1;
3354}
3355
3362static int aiL_set_shoot_indicator( lua_State *L )
3363{
3364 cur_pilot->shoot_indicator = lua_toboolean( L, 1 );
3365 return 0;
3366}
3367
3375static int aiL_shoot_indicator( lua_State *L )
3376{
3377 lua_pushboolean( L, cur_pilot->shoot_indicator );
3378 return 1;
3379}
3380
3387static int aiL_distress( lua_State *L )
3388{
3389 if (lua_isstring( L, 1 ))
3390 snprintf( aiL_distressmsg, sizeof( aiL_distressmsg ), "%s",
3391 lua_tostring( L, 1 ) );
3392 else if (lua_isnoneornil( L, 1 ))
3393 aiL_distressmsg[0] = '\0';
3394 else
3395 NLUA_INVALID_PARAMETER( L, 1 );
3396
3397 /* Set flag because code isn't reentrant. */
3399
3400 return 0;
3401}
3402
3409static int aiL_getBoss( lua_State *L )
3410{
3411 unsigned int id = pilot_getBoss( cur_pilot );
3412
3413 if (id == 0) /* No boss found */
3414 return 0;
3415
3416 lua_pushpilot( L, id );
3417
3418 return 1;
3419}
3420
3427static int aiL_credits( lua_State *L )
3428{
3430 /*NLUA_ERROR(L, "This function must be called in \"create\" only.");*/
3431 return 0;
3432 }
3433
3434 cur_pilot->credits = luaL_checklong( L, 1 );
3435
3436 return 0;
3437}
3438
3445static int aiL_messages( lua_State *L )
3446{
3447 lua_rawgeti( L, LUA_REGISTRYINDEX, cur_pilot->messages );
3448 lua_newtable( L );
3449 lua_rawseti( L, LUA_REGISTRYINDEX, cur_pilot->messages );
3450 return 1;
3451}
3452
3462static int aiL_stealth( lua_State *L )
3463{
3464 int b = 1;
3465 if (lua_gettop( L ) > 0)
3466 b = lua_toboolean( L, 1 );
3467
3468 if (!b) {
3470 lua_pushboolean( L, 1 ); /* always succeeds */
3471 return 1;
3472 }
3473
3474 lua_pushboolean( L, pilot_stealth( cur_pilot ) );
3475 return 1;
3476}
3477
3482static int aiL_outfitOffAll( lua_State *L )
3483{
3484 (void)L;
3485 if (pilot_isPlayer( cur_pilot ))
3486 return 0;
3487 for (int i = 0; i < array_size( cur_pilot->outfits ); i++)
3488 cur_pilot->outfits[i]->flags &= ~PILOTOUTFIT_ISON_TOGGLE;
3490 return 0;
3491}
3492
static int ai_setMemory(void)
Sets the cur_pilot's ai.
Definition ai.c:397
Task * ai_newtask(lua_State *L, Pilot *p, const char *func, int subtask, int pos)
Creates a new AI task.
Definition ai.c:1169
void ai_unsetPilot(AIMemory oldmem)
Finishes setting up a pilot.
Definition ai.c:428
static double pilot_turn
Definition ai.c:342
static nlua_env equip_env
Definition ai.c:110
void ai_thinkApply(Pilot *p)
Applies the result of thinking.
Definition ai.c:454
static AI_Profile * profiles
Definition ai.c:109
static int aiL_status
Definition ai.c:352
static IntList ai_qtquery
Definition ai.c:111
static double ai_dt
Definition ai.c:112
Task * ai_curTask(Pilot *pilot)
Gets the current running task.
Definition ai.c:385
void ai_refuel(Pilot *refueler, unsigned int target)
Has a pilot attempt to refuel the other.
Definition ai.c:1061
static const luaL_Reg aiL_methods[]
Definition ai.c:241
static int ai_loadProfile(AI_Profile *prof, const char *filename)
Initializes an AI_Profile and adds it to the stack.
Definition ai.c:699
static void ai_create(Pilot *pilot)
Runs the create() function in the pilot.
Definition ai.c:1120
void ai_cleartasks(Pilot *p)
Clears the pilot's tasks.
Definition ai.c:548
static int ai_tasktarget(lua_State *L, const Task *t)
Pushes a task target.
Definition ai.c:1287
void ai_freetask(Task *t)
Frees an AI task.
Definition ai.c:1231
void ai_think(Pilot *pilot, double dt, int dotask)
Heart of the AI, brains of the pilot.
Definition ai.c:812
void ai_thinkSetup(double dt)
Sets up the pilot for thinking.
Definition ai.c:440
void ai_getDistress(const Pilot *p, const Pilot *distressed, const Pilot *attacker)
Sends a distress signal to a pilot.
Definition ai.c:1094
static double pilot_acc
Definition ai.c:341
AIMemory ai_setPilot(Pilot *p)
Sets the pilot for further AI calls.
Definition ai.c:416
static Task * ai_createTask(lua_State *L, int subtask)
Creates a new task based on stack information.
Definition ai.c:1258
static void ai_run(nlua_env env, int nargs)
Attempts to run a function.
Definition ai.c:476
static char aiL_distressmsg[STRMAX_SHORT]
Definition ai.c:345
void ai_attacked(Pilot *attacked, const unsigned int attacker, double dmg)
Triggers the attacked() function in the pilot's AI.
Definition ai.c:951
#define ai_isFlag(f)
Definition ai.c:102
void ai_destroy(Pilot *p)
Destroys the ai part of the pilot.
Definition ai.c:561
Pilot * cur_pilot
Definition ai.c:340
void ai_hail(Pilot *recipient)
Triggers the hail() function in the pilot's AI.
Definition ai.c:1025
#define ai_setFlag(f)
Definition ai.c:101
void ai_discovered(Pilot *discovered)
Triggers the discovered() function in the pilot's AI.
Definition ai.c:989
void ai_exit(void)
Cleans up global AI.
Definition ai.c:788
static int ai_loadEquip(void)
Loads the equipment selector script.
Definition ai.c:654
void ai_init(Pilot *p)
Initializes the AI.
Definition ai.c:929
#define AI_DISTRESS
Definition ai.c:104
#define AI_STATUS_NORMAL
Definition ai.c:350
static void ai_taskGC(Pilot *pilot)
Runs the garbage collector on the pilot's tasks.
Definition ai.c:359
int ai_pinit(Pilot *p, const char *ai)
Initializes the pilot in the ai.
Definition ai.c:494
#define AI_STATUS_CREATE
Definition ai.c:351
static int pilot_flags
Definition ai.c:343
int ai_load(void)
Initializes the AI stuff which is basically Lua.
Definition ai.c:589
AI_Profile * ai_getProfile(const char *name)
Gets the AI_Profile by name.
Definition ai.c:775
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#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:179
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#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:221
nlua_env faction_getEquipper(int f)
Gets the equipper state associated to the faction scheduler.
Definition faction.c:770
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1450
int faction_usesHiddenJumps(int f)
Checks to see if a faction uses hidden jumps.
Definition faction.c:2356
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition faction.c:1476
int gatherable_getPos(vec2 *pos, vec2 *vel, int id)
Returns the position and velocity of a gatherable.
Definition gatherable.c:197
int gatherable_getClosest(const vec2 *pos, double rad)
Gets the closest gatherable from a given position, within a given radius.
Definition gatherable.c:173
static int aiL_getweaprangemin(lua_State *L)
Gets the minimum range of a weapon set.
Definition ai.c:3183
static int aiL_ismaxvel(lua_State *L)
Checks to see if pilot is at maximum velocity.
Definition ai.c:1687
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:2814
static int aiL_face_accurate(lua_State *L)
Computes the point to face in order to follow a moving object.
Definition ai.c:2870
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:3295
static int aiL_sethyptarget(lua_State *L)
Sets hyperspace target.
Definition ai.c:2587
static int aiL_getspobfrompos(lua_State *L)
Get the nearest friendly spob to a given position.
Definition ai.c:2344
static int aiL_getlandspob(lua_State *L)
Get a random friendly spob.
Definition ai.c:2410
static int aiL_accel(lua_State *L)
Starts accelerating the pilot.
Definition ai.c:1802
static int aiL_weapSet(lua_State *L)
Sets the active weapon set, fires another weapon set or activate an outfit.
Definition ai.c:3046
static int aiL_poptask(lua_State *L)
Pops the current running task.
Definition ai.c:1325
static int aiL_iface(lua_State *L)
Maintains an intercept pursuit course.
Definition ai.c:2056
static int aiL_settarget(lua_State *L)
Sets the pilot's target.
Definition ai.c:2956
static int aiL_nearhyptarget(lua_State *L)
Gets the nearest hyperspace target.
Definition ai.c:2623
static int aiL_getrndspob(lua_State *L)
Get a random spob.
Definition ai.c:2384
static int aiL_pilot(lua_State *L)
Gets the AI's pilot. Lua return parameter: Pilot The AI's pilot.
Definition ai.c:1452
static int aiL_minbrakedist(lua_State *L)
Gets the minimum braking distance.
Definition ai.c:1621
static int aiL_gatherablePos(lua_State *L)
Gets the pos and vel of a given gatherable.
Definition ai.c:3020
static int aiL_relvel(lua_State *L)
Gets the relative velocity of a pilot.
Definition ai.c:2771
static int aiL_haslockon(lua_State *L)
Checks to see if pilot has a missile lockon.
Definition ai.c:1761
static int aiL_refuel(lua_State *L)
Attempts to refuel the pilot's target.
Definition ai.c:3320
static int aiL_getdistance2(lua_State *L)
Gets the squared distance from the pointer.
Definition ai.c:1550
static int aiL_getdistance(lua_State *L)
Gets the distance from the pointer.
Definition ai.c:1523
static int aiL_instantJump(lua_State *L)
Checks to see if pilot can instant jump.
Definition ai.c:1675
static int aiL_land(lua_State *L)
Lands on a spob.
Definition ai.c:2472
static int aiL_isenemy(lua_State *L)
Checks to see if target is an enemy.
Definition ai.c:1717
static int aiL_messages(lua_State *L)
Returns and clears the pilots message queue.
Definition ai.c:3445
static int aiL_turn(lua_State *L)
Starts turning the pilot.
Definition ai.c:1815
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:1419
static int aiL_rndhyptarget(lua_State *L)
Gets a random hyperspace target.
Definition ai.c:2673
static int aiL_dir(lua_State *L)
calculates the direction that the target is relative to the current pilot facing.
Definition ai.c:2141
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:1925
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:1362
static int aiL_hasturrets(lua_State *L)
Does the pilot have turrets?
Definition ai.c:3082
static int aiL_isally(lua_State *L)
Checks to see if target is an ally.
Definition ai.c:1739
static int aiL_hyperspaceAbort(lua_State *L)
Has the AI abandon hyperspace if applicable.
Definition ai.c:2758
static int aiL_relsize(lua_State *L)
Gets the relative size (ship mass) between the current pilot and the specified target.
Definition ai.c:3265
static int aiL_aim(lua_State *L)
Aims at a pilot, trying to hit it rather than move to it.
Definition ai.c:2027
static int aiL_credits(lua_State *L)
Sets the pilots credits. Only call in create().
Definition ai.c:3427
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:3280
static int aiL_canboard(lua_State *L)
Checks to see if pilot can board the target.
Definition ai.c:3242
static int aiL_idir(lua_State *L)
Calculates angle between pilot facing and intercept-course to target.
Definition ai.c:2172
static int aiL_getnearestpilot(lua_State *L)
gets the nearest pilot to the current pilot
Definition ai.c:1488
static int aiL_hostile(lua_State *L)
Sets the enemy hostile (basically notifies of an impending attack).
Definition ai.c:3167
static int aiL_hascannons(lua_State *L)
Does the pilot have cannons?
Definition ai.c:3070
static int aiL_getBoss(lua_State *L)
Picks a pilot that will command the current pilot.
Definition ai.c:3409
static int aiL_isbribed(lua_State *L)
Checks to see if target has bribed pilot.
Definition ai.c:1661
static int aiL_getGatherable(lua_State *L)
Gets the closest gatherable within a radius.
Definition ai.c:2992
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:1346
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:1436
static int aiL_hasprojectile(lua_State *L)
Checks to see if pilot has a projectile after him.
Definition ai.c:1774
static int aiL_getweapammo(lua_State *L)
Gets the ammo of a weapon.
Definition ai.c:3228
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:2253
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:3375
static int aiL_combat(lua_State *L)
Sets the combat flag.
Definition ai.c:2936
static int aiL_distress(lua_State *L)
Sends a distress signal.
Definition ai.c:3387
static int aiL_settimer(lua_State *L)
Sets a timer.
Definition ai.c:3333
static int aiL_setasterotarget(lua_State *L)
Sets the pilot's asteroid target.
Definition ai.c:2970
static int aiL_getweapspeed(lua_State *L)
Gets the speed of a weapon.
Definition ai.c:3213
static int aiL_getrndpilot(lua_State *L)
Gets a random pilot in the system. Lua return parameter: Pilot|nil.
Definition ai.c:1464
static int aiL_set_shoot_indicator(lua_State *L)
Set the seeker shoot indicator.
Definition ai.c:3362
static int aiL_getenemy(lua_State *L)
Gets the nearest enemy.
Definition ai.c:3120
static int aiL_stealth(lua_State *L)
Tries to stealth or destealth the pilot.
Definition ai.c:3462
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:1378
static int aiL_stop(lua_State *L)
Completely stops the pilot if it is below minimum vel error (no insta-stops).
Definition ai.c:2906
static int aiL_getflybydistance(lua_State *L)
Gets the distance from the pointer perpendicular to the current pilot's flight vector.
Definition ai.c:1578
static int aiL_canHyperspace(lua_State *L)
Gets whether or not the pilot can hyperspace.
Definition ai.c:2747
static int aiL_popsubtask(lua_State *L)
Pops the current running task.
Definition ai.c:1389
static int aiL_face(lua_State *L)
Faces the target.
Definition ai.c:1835
static int aiL_timeup(lua_State *L)
Checks a timer.
Definition ai.c:3349
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:1314
static int aiL_scandone(lua_State *L)
Checks to see if pilot has finished scanning their target.
Definition ai.c:1787
static int aiL_getweaprange(lua_State *L)
Gets the range of a weapon set.
Definition ai.c:3198
static int aiL_dock(lua_State *L)
Docks the ship.
Definition ai.c:2922
static int aiL_outfitOffAll(lua_State *L)
Tries to turn off all outfits.
Definition ai.c:3482
static int aiL_hyperspace(lua_State *L)
Tries to enter hyperspace.
Definition ai.c:2562
static int aiL_hasafterburner(lua_State *L)
Does the pilot have afterburners?
Definition ai.c:3106
static int aiL_board(lua_State *L)
Attempts to board the pilot's target.
Definition ai.c:3308
static int aiL_hasfighterbays(lua_State *L)
Does the pilot have fighter bays?
Definition ai.c:3094
static int aiL_brake(lua_State *L)
Brakes the pilot.
Definition ai.c:2269
static int aiL_getnearestspob(lua_State *L)
Get the nearest friendly spob to the pilot.
Definition ai.c:2305
static int aiL_isstopped(lua_State *L)
Checks to see if pilot is stopped.
Definition ai.c:1704
void naev_renderLoadscreen(void)
Renders the loadscreen if necessary.
Definition naev.c:640
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define CLAMP(a, b, x)
Definition naev.h:41
#define ABS(x)
Definition naev.h:32
#define pow2(x)
Definition naev.h:53
#define FABS(x)
Definition naev.h:34
#define MAX(x, y)
Definition naev.h:37
#define PATH_MAX
Definition naev.h:57
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:420
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:914
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:1047
lua_State * naevL
Definition nlua.c:54
int lua_isasteroid(lua_State *L, int ind)
Checks to see if ind is a asteroid.
LuaAsteroid_t * luaL_checkasteroid(lua_State *L, int ind)
Gets asteroid at index or raises error if there is no asteroid at index.
Asteroid * luaL_validasteroid(lua_State *L, int ind)
Gets asteroid at index raising an error if type doesn't match.
LuaJump * luaL_checkjump(lua_State *L, int ind)
Gets jump at index raising an error if isn't a jump.
Definition nlua_jump.c:106
LuaJump * lua_pushjump(lua_State *L, LuaJump jump)
Pushes a jump on the stack.
Definition nlua_jump.c:186
JumpPoint * luaL_validjump(lua_State *L, int ind)
Gets a jump directly.
Definition nlua_jump.c:174
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
Pilot * luaL_validpilot(lua_State *L, int ind)
Makes sure the pilot is valid or raises a Lua error.
Definition nlua_pilot.c:560
int lua_ispilot(lua_State *L, int ind)
Checks to see if ind is a pilot.
Definition nlua_pilot.c:591
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:203
Spob * luaL_validspob(lua_State *L, int ind)
Gets a spob directly.
Definition nlua_spob.c:177
int lua_isvector(lua_State *L, int ind)
Checks to see if ind is a vector.
Definition nlua_vec2.c:161
vec2 * luaL_checkvector(lua_State *L, int ind)
Gets vector at index making sure type is valid.
Definition nlua_vec2.c:130
vec2 * lua_tovector(lua_State *L, int ind)
Represents a 2D vector in Lua.
Definition nlua_vec2.c:119
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition nlua_vec2.c:145
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition pilot.c:699
int pilot_brakeCheckReverseThrusters(const Pilot *p)
See if the pilot wants to use their reverse thrusters to brake.
Definition pilot.c:838
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:4468
int pilot_validEnemy(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid enemy for another pilot.
Definition pilot.c:291
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:4364
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:4445
void pilot_msg(const Pilot *p, const Pilot *receiver, const char *type, unsigned int idx)
Sends a message.
Definition pilot.c:4531
void pilot_setAccel(Pilot *p, double accel)
Sets the pilot's accel.
Definition pilot.c:680
unsigned int pilot_getNearestEnemy(const Pilot *p)
Gets the nearest enemy to the pilot.
Definition pilot.c:331
void pilot_hyperspaceAbort(Pilot *p)
Stops the pilot from hyperspacing.
Definition pilot.c:3140
void pilot_setTurn(Pilot *p, double turn)
Sets the pilot's turn.
Definition pilot.c:688
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition pilot.c:728
int pilot_refuelStart(Pilot *p)
Attempts to start refueling the pilot's target.
Definition pilot.c:3170
double pilot_minbrakedist(const Pilot *p, double dt, double *flytime)
Gets the minimum braking distance for the pilot.
Definition pilot.c:862
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition pilot.c:1105
static Pilot ** pilot_stack
Definition pilot.c:51
void pilot_distress(Pilot *p, Pilot *attacker, const char *msg)
Has the pilot broadcast a distress signal.
Definition pilot.c:1180
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:93
int pilot_canTarget(const Pilot *p)
Same as pilot_validTarget but without the range check.
Definition pilot.c:270
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:1025
unsigned int pilot_getBoss(const Pilot *p)
Get the strongest ally in a given range.
Definition pilot.c:450
void pilot_setTarget(Pilot *p, unsigned int id)
Sets the target of the pilot.
Definition pilot.c:1352
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:256
int pilot_ewScanCheck(const Pilot *p)
Checks to see if a scan is done.
Definition pilot_ew.c:74
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition pilot_ew.c:585
int pilot_stealth(Pilot *p)
Stealths a pilot.
Definition pilot_ew.c:543
int pilot_runHook(Pilot *p, int hook_type)
Tries to run a pilot hook if he has it.
Definition pilot_hook.c:104
void pilot_healLanded(Pilot *pilot)
Cures the pilot as if he was landed.
double pilot_weapSetRangeMin(Pilot *p, int id)
Gets the minimum range of the current pilot weapon set.
void pilot_weapSetAIClear(Pilot *p)
Useful function for AI, clears activeness of all weapon sets.
double pilot_weapSetSpeed(Pilot *p, int id)
Gets the speed of the current pilot weapon set.
double pilot_weapSetAmmo(Pilot *p, int id)
Gets the ammo of the current pilot weapon set.
void pilot_weapSetUpdateOutfitState(Pilot *p)
Updates the local state of all the pilot's outfits based on the weapon sets.
double pilot_weapSetRange(Pilot *p, int id)
Gets the range of the current pilot weapon set.
int pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
static const double d[]
Definition rng.c:263
int space_canHyperspace(const Pilot *p)
Checks to make sure if pilot is far enough away to hyperspace.
Definition space.c:494
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition space.c:1158
StarSystem * cur_system
Definition space.c:110
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition space.c:4537
int space_hyperspace(Pilot *p)
Tries to get the pilot into hyperspace.
Definition space.c:529
Represents a temporary pilot memory. For use with ai_setPilot and ai_unsetPilot.
Definition ai.h:59
Pilot * p
Definition ai.h:61
int mem
Definition ai.h:60
Basic AI profile.
Definition ai.h:39
int lua_mem
Definition ai.h:43
int ref_refuel
Definition ai.h:46
nlua_env env
Definition ai.h:41
int ref_control
Definition ai.h:44
char * name
Definition ai.h:40
double control_rate
Definition ai.h:42
int ref_create
Definition ai.h:47
int ref_control_manual
Definition ai.h:45
Represents a single asteroid.
Definition asteroid.h:88
The actual hook parameter.
Definition hook.h:40
LuaPilot lp
Definition hook.h:46
union HookParam::@114305201244257020071001257133270030317263020235 u
HookParamType type
Definition hook.h:41
LuaSpob la
Definition hook.h:52
double num
Definition hook.h:43
Lua jump Wrapper.
Definition nlua_jump.h:14
int destid
Definition nlua_jump.h:16
int srcid
Definition nlua_jump.h:15
The representation of an in-game pilot.
Definition pilot.h:263
AI_Profile * ai
Definition pilot.h:408
unsigned int id
Definition pilot.h:264
int faction
Definition pilot.h:269
Solid solid
Definition pilot.h:275
char * name
Definition pilot.h:265
Task * task
Definition pilot.h:412
vec2 vel
Definition physics.h:48
vec2 pos
Definition physics.h:49
int faction
Definition space.h:79
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
int lua_land
Definition space.h:170
double radius
Definition space.h:110
nlua_env lua_env
Definition space.h:164
char * name
Definition space.h:105
vec2 pos
Definition space.h:109
int id
Definition space.h:104
int lua_can_land
Definition space.h:169
SpobPresence presence
Definition space.h:121
Basic AI task.
Definition ai.h:23
int func
Definition ai.h:26
char * name
Definition ai.h:25
struct Task_ * subtask
Definition ai.h:29
struct Task_ * next
Definition ai.h:24
int done
Definition ai.h:27
int dat
Definition ai.h:31
Definition msgcat.c:196
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46