naev 0.12.5
hook.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
25#include <stdlib.h>
26
27#include "naev.h"
29
30#include "hook.h"
31
32#include "array.h"
33#include "claim.h"
34#include "event.h"
35#include "log.h"
36#include "menu.h"
37#include "mission.h"
38#include "nlua_commodity.h"
39#include "nlua_evt.h"
40#include "nlua_hook.h"
41#include "nlua_outfit.h"
42#include "nlua_pilot.h"
43#include "nlua_ship.h"
44#include "player.h"
45#include "space.h"
46
50typedef struct HookQueue_s {
51 struct HookQueue_s *next;
52 char *stack;
53 unsigned int id;
54 HookParam hparam[HOOK_MAX_PARAM];
56static HookQueue_t *hook_queue = NULL;
57static int hook_atomic = 0;
58static ntime_t hook_time_accum = 0;
59
69
75typedef struct Hook_ {
76 struct Hook_ *next;
77
78 unsigned int id;
79 char *stack;
80 int created;
81 int delete;
84 int once;
85
86 /* Timer information. */
88 double ms;
89
90 /* Date information. */
91 int is_date;
92 ntime_t res;
93 ntime_t acc;
94
96 union {
97 struct {
98 unsigned int parent;
99 char *func;
100 } misn;
101 struct {
102 unsigned int parent;
103 char *func;
104 } event;
105 struct {
106 int ( *func )( void * );
107 void *data;
108 } func;
109 } u;
110} Hook;
111
112/*
113 * the stack
114 */
115static unsigned int hook_id = 0;
116static Hook *hook_list = NULL;
117static int hook_runningstack = 0;
118static int hook_loadingstack = 0;
119
120/*
121 * prototypes
122 */
123/* Execution. */
124static int hooks_executeParam( const char *stack, const HookParam *param );
125static void hooks_updateDateExecute( ntime_t change );
126/* intern */
127static void hook_rmRaw( Hook *h );
128static void hooks_purgeList( void );
129static Hook *hook_get( unsigned int id );
130static unsigned int hook_genID( void );
131static Hook *hook_new( HookType_t type, const char *stack );
132static int hook_parseParam( const HookParam *param );
133static int hook_runMisn( Hook *hook, const HookParam *param, int claims );
134static int hook_runEvent( Hook *hook, const HookParam *param, int claims );
135static int hook_run( Hook *hook, const HookParam *param, int claims );
136static void hook_free( Hook *h );
137static int hook_needSave( Hook *h );
138static int hook_parse( xmlNodePtr base );
139/* externed */
140int hook_save( xmlTextWriterPtr writer );
141int hook_load( xmlNodePtr parent );
142/* Misc. */
143static Mission *hook_getMission( Hook *hook );
144
148static int hq_add( HookQueue_t *hq )
149{
150 HookQueue_t *c;
151
152 /* Set as head. */
153 if ( hook_queue == NULL ) {
154 hook_queue = hq;
155 return 0;
156 }
157
158 /* Find tail. */
159 for ( c = hook_queue; c->next != NULL; c = c->next )
160 ;
161 c->next = hq;
162 return 0;
163}
164
168static void hq_free( HookQueue_t *hq )
169{
170 free( hq->stack );
171 free( hq );
172}
173
177static void hq_clear( void )
178{
179 HookQueue_t *hq;
180 while ( hook_queue != NULL ) {
181 hq = hook_queue;
182 hook_queue = hq->next;
183 hq_free( hq );
184 }
185}
186
192{
193 hook_atomic = 1;
194}
195
199void hook_exclusionEnd( double dt )
200{
201 HookQueue_t *hq;
202 ntime_t temp;
203 hook_atomic = 0;
204
205 /* Handle hook queue. */
206 while ( hook_queue != NULL ) {
207 /* Move hook down. */
208 hq = hook_queue;
209 hook_queue = hq->next;
210
211 /* Execute. */
212 hooks_executeParam( hq->stack, hq->hparam );
213
214 /* Clean up. */
215 hq_free( hq );
216 }
217
218 /* Update timer hooks. */
219 hooks_update( dt );
220
221 /* Run assorted hooks. */
222 if ( player.p !=
223 NULL ) /* All these hooks rely on player actually existing. */
225
226 /* Time hooks. */
227 temp = hook_time_accum;
228 hook_time_accum = 0;
230
231 /* Purge the dead. */
233}
234
241static int hook_parseParam( const HookParam *param )
242{
243 int n;
244
245 if ( param == NULL )
246 return 0;
247
248 n = 0;
249 while ( param[n].type != HOOK_PARAM_SENTINEL ) {
250 switch ( param[n].type ) {
251 case HOOK_PARAM_NIL:
252 lua_pushnil( naevL );
253 break;
254 case HOOK_PARAM_NUMBER:
255 lua_pushnumber( naevL, param[n].u.num );
256 break;
257 case HOOK_PARAM_STRING:
258 lua_pushstring( naevL, param[n].u.str );
259 break;
260 case HOOK_PARAM_BOOL:
261 lua_pushboolean( naevL, param[n].u.b );
262 break;
263 case HOOK_PARAM_PILOT:
264 lua_pushpilot( naevL, param[n].u.lp );
265 break;
266 case HOOK_PARAM_SHIP:
267 lua_pushship( naevL, param[n].u.ship );
268 break;
269 case HOOK_PARAM_OUTFIT:
270 lua_pushoutfit( naevL, param[n].u.outfit );
271 break;
272 case HOOK_PARAM_COMMODITY:
273 lua_pushcommodity( naevL, param[n].u.commodity );
274 break;
275 case HOOK_PARAM_FACTION:
276 lua_pushfaction( naevL, param[n].u.lf );
277 break;
278 case HOOK_PARAM_SSYS:
279 lua_pushsystem( naevL, param[n].u.ls );
280 break;
281 case HOOK_PARAM_SPOB:
282 lua_pushspob( naevL, param[n].u.la );
283 break;
284 case HOOK_PARAM_JUMP:
285 lua_pushjump( naevL, param[n].u.lj );
286 break;
287 case HOOK_PARAM_REF:
288 lua_rawgeti( naevL, LUA_REGISTRYINDEX, param[n].u.ref );
289 break;
290
291 default:
292 WARN( _( "Unknown Lua parameter type." ) );
293 lua_pushnil( naevL );
294 break;
295 }
296 n++;
297 }
298
299 return n;
300}
301
311static int hook_runMisn( Hook *hook, const HookParam *param, int claims )
312{
313 unsigned int id;
314 Mission *misn;
315 int n;
316
317 /* Simplicity. */
318 id = hook->id;
319
320 /* Make sure it's valid. */
321 if ( hook->u.misn.parent == 0 ) {
322 WARN( _( "Trying to run hook with nonexistent parent: deleting" ) );
323 hook->delete = 1; /* so we delete it */
324 return -1;
325 }
326
327 /* Locate the mission */
328 misn = hook_getMission( hook );
329 if ( misn == NULL ) {
330 WARN( _( "Trying to run hook with parent not in player mission stack: "
331 "deleting" ) );
332 hook->delete = 1; /* so we delete it. */
333 return -1;
334 }
335
336 /* Make sure it's claimed. */
337 if ( ( claims > 0 ) &&
338 ( claim_testSys( misn->claims, cur_system->id ) != claims ) )
339 return 1;
340
341 /* Delete if only supposed to run once. */
342 if ( hook->once )
343 hook_rmRaw( hook );
344
345 /* Set up hook parameters. */
346 misn_runStart( misn, hook->u.misn.func );
347 n = hook_parseParam( param );
348
349 /* Add hook parameters. */
350 n += hookL_getarg( id );
351
352 /* Run mission code. */
353 hook->ran_once = 1;
354 if ( misn_runFunc( misn, hook->u.misn.func, n ) <
355 0 ) { /* error has occurred */
356 WARN( _( "Hook [%s] '%d' -> '%s' failed" ), hook->stack, hook->id,
357 hook->u.misn.func );
358 /* Don't remove hooks on failure, or it can lead to stuck missions (like
359 * rehab). */
360 // hook_rmRaw( hook );
361 return -1;
362 }
363 return 0;
364}
365
375static int hook_runEvent( Hook *hook, const HookParam *param, int claims )
376{
377 int ret, n, id;
378
379 /* Simplicity. */
380 id = hook->id;
381
382 /* Must match claims. */
383 if ( ( claims > 0 ) &&
384 ( event_testClaims( hook->u.event.parent, cur_system->id ) != claims ) )
385 return 1;
386
387 /* Delete if only supposed to run once. */
388 if ( hook->once )
389 hook_rmRaw( hook );
390
391 /* Set up hook parameters. */
392 if ( event_get( hook->u.event.parent ) == NULL ) {
393 WARN( _( "Hook [%s] '%d' -> '%s' failed, event does not exist. Deleting "
394 "hook." ),
395 hook->stack, id, hook->u.event.func );
396 hook->delete = 1; /* Set for deletion. */
397 return -1;
398 }
399
400 event_runStart( hook->u.event.parent, hook->u.event.func );
401 n = hook_parseParam( param );
402
403 /* Add hook parameters. */
404 n += hookL_getarg( id );
405
406 /* Run the hook. */
407 ret = event_runFunc( hook->u.event.parent, hook->u.event.func, n );
408 hook->ran_once = 1;
409 if ( ret < 0 ) {
410 WARN( _( "Hook [%s] '%d' -> '%s' failed" ), hook->stack, hook->id,
411 hook->u.event.func );
412 /* Don't remove hooks on failure, or it can lead to stuck missions (like
413 * rehab). */
414 // hook_rmRaw( hook );
415 return -1;
416 }
417 return 0;
418}
419
429static int hook_run( Hook *hook, const HookParam *param, int claims )
430{
431 int ret;
432
433 /* Do not run if pending deletion. */
434 if ( hook->delete )
435 return 0;
436
437 /* Don't run anything when main menu is open. */
438 if ( menu_isOpen( MENU_MAIN ) )
439 return 0;
440
441 switch ( hook->type ) {
442 case HOOK_TYPE_MISN:
443 ret = hook_runMisn( hook, param, claims );
444 break;
445
446 case HOOK_TYPE_EVENT:
447 ret = hook_runEvent( hook, param, claims );
448 break;
449
450 case HOOK_TYPE_FUNC:
451 /* We have to remove the hook first, so it doesn't get run again.
452 * Note that the function will not do any checks nor has arguments, since
453 * it is C-side. */
454 if ( hook->once )
455 hook->delete = 1;
456 ret = hook->u.func.func( hook->u.func.data );
457 break;
458
459 default:
460 WARN( _( "Invalid hook type '%d', deleting." ), hook->type );
461 hook->delete = 1;
462 return -1;
463 }
464
465 return ret;
466}
467
473static unsigned int hook_genID( void )
474{
475 unsigned int id = ++hook_id; /* default id, not safe if loading */
476
477 /* If not loading we can just return. */
478 if ( !hook_loadingstack )
479 return id;
480
481 /* Must check ids for collisions. */
482 for ( Hook *h = hook_list; h != NULL; h = h->next )
483 if ( id == h->id ) /* Check for collision. */
484 return hook_genID(); /* recursively try again */
485
486 return id;
487}
488
496static Hook *hook_new( HookType_t type, const char *stack )
497{
498 /* Get and create new hook. */
499 Hook *new_hook = calloc( 1, sizeof( Hook ) );
500 if ( hook_list == NULL )
501 hook_list = new_hook;
502 else {
503 /* Put at front, O(1). */
504 new_hook->next = hook_list;
505 hook_list = new_hook;
506 }
507
508 /* Fill out generic details. */
509 new_hook->type = type;
510 new_hook->id = hook_genID();
511 new_hook->stack = strdup( stack );
512 new_hook->created = 1;
513
515 if ( strcmp( stack, "safe" ) == 0 )
516 new_hook->once = 1;
517
518 return new_hook;
519}
520
529unsigned int hook_addMisn( unsigned int parent, const char *func,
530 const char *stack )
531{
532 /* Create the new hook. */
533 Hook *new_hook = hook_new( HOOK_TYPE_MISN, stack );
534
535 /* Put mission specific details. */
536 new_hook->u.misn.parent = parent;
537 new_hook->u.misn.func = strdup( func );
538
539 return new_hook->id;
540}
541
550unsigned int hook_addEvent( unsigned int parent, const char *func,
551 const char *stack )
552{
553 /* Create the new hook. */
554 Hook *new_hook = hook_new( HOOK_TYPE_EVENT, stack );
555
556 /* Put event specific details. */
557 new_hook->u.event.parent = parent;
558 new_hook->u.event.func = strdup( func );
559
560 return new_hook->id;
561}
562
571unsigned int hook_addTimerMisn( unsigned int parent, const char *func,
572 double ms )
573{
574 /* Create the new hook. */
575 Hook *new_hook = hook_new( HOOK_TYPE_MISN, "timer" );
576
577 /* Put mission specific details. */
578 new_hook->u.misn.parent = parent;
579 new_hook->u.misn.func = strdup( func );
580
581 /* Timer information. */
582 new_hook->is_timer = 1;
583 new_hook->ms = ms;
584
585 return new_hook->id;
586}
587
596unsigned int hook_addTimerEvt( unsigned int parent, const char *func,
597 double ms )
598{
599 /* Create the new hook. */
600 Hook *new_hook = hook_new( HOOK_TYPE_EVENT, "timer" );
601
602 /* Put event specific details. */
603 new_hook->u.event.parent = parent;
604 new_hook->u.event.func = strdup( func );
605
606 /* Timer information. */
607 new_hook->is_timer = 1;
608 new_hook->ms = ms;
609
610 return new_hook->id;
611}
612
616unsigned int hook_addTimerFunc( int ( *func )( void * ), void *data, double ms )
617{
618 Hook *new_hook = hook_new( HOOK_TYPE_FUNC, "timer" );
619
620 /* Function special stuff. */
621 new_hook->u.func.func = func;
622 new_hook->u.func.data = data;
623
624 /* Timer information. */
625 new_hook->is_timer = 1;
626 new_hook->ms = ms;
627
628 return new_hook->id;
629}
630
634unsigned int hook_addFunc( int ( *func )( void * ), void *data,
635 const char *stack )
636{
637 Hook *new_hook = hook_new( HOOK_TYPE_FUNC, stack );
638
639 /* Function special stuff. */
640 new_hook->u.func.func = func;
641 new_hook->u.func.data = data;
642
643 return new_hook->id;
644}
645
649static void hooks_purgeList( void )
650{
651 Hook *h, *hl;
652
653 /* Do not run while stack is being run. */
654 if ( hook_runningstack )
655 return;
656
657 /* Second pass to delete. */
658 hl = NULL;
659 h = hook_list;
660 while ( h != NULL ) {
661 /* Find valid timer hooks. */
662 if ( h->delete ) {
663
664 if ( hl == NULL )
665 hook_list = h->next;
666 else
667 hl->next = h->next;
668
669 /* Free. */
670 h->next = NULL;
671 hook_free( h );
672
673 /* Last. */
674 h = hl;
675 }
676
677 hl = h;
678 if ( h == NULL )
679 h = hook_list;
680 else
681 h = h->next;
682 }
683}
684
688void hooks_updateDate( ntime_t change )
689{
690 if ( change > 0 )
691 hook_time_accum += change;
692}
693
697static void hooks_updateDateExecute( ntime_t change )
698{
699 /* Don't update without player. */
700 if ( ( player.p == NULL ) || player_isFlag( PLAYER_CREATING ) )
701 return;
702
703 /* Clear creation flags. */
704 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
705 h->created = 0;
706 if ( h->is_date )
707 h->ran_once = 0;
708 }
709
710 hook_runningstack++; /* running hooks */
711 for ( int j = 1; j >= 0; j-- ) {
712 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
713 /* Not be deleting. */
714 if ( h->delete )
715 continue;
716 /* Find valid date hooks. */
717 if ( h->is_date == 0 )
718 continue;
719 /* Don't update newly created hooks. */
720 if ( h->created != 0 )
721 continue;
722
723 /* Decrement timer and check to see if should run. */
724 if ( j == 1 )
725 h->acc += change;
726 if ( h->acc < h->res )
727 continue;
728
729 /* Time is modified at the end. */
730 if ( j == 0 )
731 h->acc %= h->res; /* We'll skip all buggers. */
732 if ( h->ran_once )
733 continue;
734
735 /* Run the date hook. */
736 hook_run( h, NULL, j );
737 /* Date hooks are not deleted. */
738 }
739 }
740 hook_runningstack--; /* not running hooks anymore */
741
742 /* Second pass to delete. */
744}
745
746unsigned int hook_addDateMisn( unsigned int parent, const char *func,
747 ntime_t resolution )
748{
749 /* Create the new hook. */
750 Hook *new_hook = hook_new( HOOK_TYPE_MISN, "date" );
751
752 /* Put mission specific details. */
753 new_hook->u.misn.parent = parent;
754 new_hook->u.misn.func = strdup( func );
755
756 /* Timer information. */
757 new_hook->is_date = 1;
758 new_hook->res = resolution;
759 new_hook->acc = 0;
760
761 return new_hook->id;
762}
763
764unsigned int hook_addDateEvt( unsigned int parent, const char *func,
765 ntime_t resolution )
766{
767 /* Create the new hook. */
768 Hook *new_hook = hook_new( HOOK_TYPE_EVENT, "date" );
769
770 /* Put event specific details. */
771 new_hook->u.event.parent = parent;
772 new_hook->u.event.func = strdup( func );
773
774 /* Timer information. */
775 new_hook->is_date = 1;
776 new_hook->res = resolution;
777 new_hook->acc = 0;
778
779 return new_hook->id;
780}
781
785void hooks_update( double dt )
786{
787 Hook *h;
788
789 /* Don't update without player. */
790 if ( ( player.p == NULL ) || player_isFlag( PLAYER_CREATING ) ||
791 player_isFlag( PLAYER_DESTROYED ) )
792 return;
793
794 /* Clear creation flags. */
795 for ( h = hook_list; h != NULL; h = h->next )
796 h->created = 0;
797
798 hook_runningstack++; /* running hooks */
799 for ( int j = 1; j >= 0; j-- ) {
800 for ( h = hook_list; h != NULL; h = h->next ) {
801 /* Not be deleting. */
802 if ( h->delete )
803 continue;
804 /* Find valid timer hooks. */
805 if ( h->is_timer == 0 )
806 continue;
807 /* Don't update newly created hooks. */
808 if ( h->created != 0 )
809 continue;
810
811 /* Decrement timer and check to see if should run. */
812 if ( j == 1 )
813 h->ms -= dt;
814 if ( h->ms > 0. )
815 continue;
816
817 /* Run the timer hook. */
818 hook_run( h, NULL, j );
819 if ( h->ran_once ) /* Remove when run. */
820 hook_rmRaw( h );
821 }
822 }
823 hook_runningstack--; /* not running hooks anymore */
824
825 /* Second pass to delete. */
827}
828
833{
834 for ( int i = 0; i < array_size( player_missions ); i++ )
835 if ( player_missions[i]->id == hook->u.misn.parent )
836 return player_missions[i];
837
838 return NULL;
839}
840
846void hook_rm( unsigned int id )
847{
848 Hook *h = hook_get( id );
849 if ( h == NULL )
850 return;
851
852 hook_rmRaw( h );
853}
854
858static void hook_rmRaw( Hook *h )
859{
860 h->delete = 1;
861 hookL_unsetarg( h->id );
862}
863
869void hook_rmMisnParent( unsigned int parent )
870{
871 for ( Hook *h = hook_list; h != NULL; h = h->next )
872 if ( ( h->type == HOOK_TYPE_MISN ) && ( parent == h->u.misn.parent ) )
873 h->delete = 1;
874}
875
881void hook_rmEventParent( unsigned int parent )
882{
883 for ( Hook *h = hook_list; h != NULL; h = h->next )
884 if ( ( h->type == HOOK_TYPE_EVENT ) && ( parent == h->u.event.parent ) )
885 h->delete = 1;
886}
887
894int hook_hasMisnParent( unsigned int parent )
895{
896 int num = 0;
897 for ( Hook *h = hook_list; h != NULL; h = h->next )
898 if ( ( h->type == HOOK_TYPE_MISN ) && ( parent == h->u.misn.parent ) )
899 num++;
900
901 return num;
902}
903
910int hook_hasEventParent( unsigned int parent )
911{
912 int num = 0;
913 for ( Hook *h = hook_list; h != NULL; h = h->next )
914 if ( ( h->type == HOOK_TYPE_EVENT ) && ( parent == h->u.event.parent ) )
915 num++;
916
917 return num;
918}
919
920static int hooks_executeParam( const char *stack, const HookParam *param )
921{
922 int run;
923
924 /* Don't update if player is dead. */
925 if ( ( player.p == NULL ) || player_isFlag( PLAYER_DESTROYED ) )
926 return 0;
927
928 /* Reset the current stack's ran and creation flags. */
929 for ( Hook *h = hook_list; h != NULL; h = h->next )
930 if ( strcmp( stack, h->stack ) == 0 ) {
931 h->ran_once = 0;
932 h->created = 0;
933 }
934
935 run = 0;
936 hook_runningstack++; /* running hooks */
937 for ( int j = 1; j >= 0; j-- ) {
938 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
939 /* Should be deleted. */
940 if ( h->delete )
941 continue;
942 /* Don't run again. */
943 if ( h->ran_once )
944 continue;
945 /* Don't update newly created hooks. */
946 if ( h->created != 0 )
947 continue;
948 /* Doesn't match stack. */
949 if ( strcmp( stack, h->stack ) != 0 )
950 continue;
951
952 /* Run hook. */
953 hook_run( h, param, j );
954 run++;
955
956 /* If hook_cleanup was run, hook_list will be NULL */
957 if ( hook_list == NULL )
958 break;
959 }
960 if ( hook_list == NULL )
961 break;
962 }
963 hook_runningstack--; /* not running hooks anymore */
964
965 /* Free reference parameters. */
966 if ( param != NULL ) {
967 int n = 0;
968 while ( param[n].type != HOOK_PARAM_SENTINEL ) {
969 switch ( param[n].type ) {
970 case HOOK_PARAM_REF:
971 luaL_unref( naevL, LUA_REGISTRYINDEX, param[n].u.ref );
972 break;
973
974 default:
975 break;
976 }
977 n++;
978 }
979 }
980
981 /* Check claims. */
982 if ( run )
984
985 return run;
986}
987
996int hooks_runParamDeferred( const char *stack, const HookParam *param )
997{
998 int i;
999 HookQueue_t *hq;
1000
1001 /* Don't update if player is dead. */
1002 if ( ( player.p == NULL ) || player_isFlag( PLAYER_DESTROYED ) )
1003 return 0;
1004
1005 hq = calloc( 1, sizeof( HookQueue_t ) );
1006 hq->stack = strdup( stack );
1007 i = 0;
1008 if ( param != NULL ) {
1009 for ( ; param[i].type != HOOK_PARAM_SENTINEL; i++ )
1010 hq->hparam[i] = param[i];
1011 }
1012#ifdef DEBUGGING
1013 if ( i >= HOOK_MAX_PARAM )
1014 WARN( _( "HOOK_MAX_PARAM is set too low (%d), need at least %d!" ),
1015 HOOK_MAX_PARAM, i );
1016#endif /* DEBUGGING */
1017 hq->hparam[i].type = HOOK_PARAM_SENTINEL;
1018 hq_add( hq );
1019 return 0;
1020}
1021
1029int hooks_runParam( const char *stack, const HookParam *param )
1030{
1031 /* Don't update if player is dead. */
1032 if ( ( player.p == NULL ) || player_isFlag( PLAYER_DESTROYED ) )
1033 return 0;
1034
1035 /* Not time to run hooks, so queue them. */
1036 if ( hook_atomic )
1037 return hooks_runParamDeferred( stack, param );
1038
1039 /* Execute. */
1040 return hooks_executeParam( stack, param );
1041}
1042
1049int hooks_run( const char *stack )
1050{
1051 return hooks_runParam( stack, NULL );
1052}
1053
1057static Hook *hook_get( unsigned int id )
1058{
1059 for ( Hook *h = hook_list; h != NULL; h = h->next )
1060 if ( h->id == id )
1061 return h;
1062
1063 return NULL;
1064}
1065
1069nlua_env hook_env( unsigned int hook )
1070{
1071 Mission *misn;
1072 Event_t *evt;
1073
1074 Hook *h = hook_get( hook );
1075 if ( h == NULL )
1076 return LUA_NOREF;
1077
1078 switch ( h->type ) {
1079 case HOOK_TYPE_MISN:
1080 misn = hook_getMission( h );
1081 if ( misn != NULL )
1082 return misn->env;
1083 break;
1084 case HOOK_TYPE_EVENT:
1085 evt = event_get( h->u.event.parent );
1086 if ( evt != NULL )
1087 return evt->env;
1088 break;
1089 default:
1090 break;
1091 }
1092
1093 return LUA_NOREF;
1094}
1095
1103int hook_runIDparam( unsigned int id, const HookParam *param )
1104{
1105 Hook *h;
1106
1107 /* Don't update if player is dead. */
1108 if ( ( player.p == NULL ) || player_isFlag( PLAYER_DESTROYED ) )
1109 return 0;
1110
1111 /* Try to find the hook and run it. */
1112 h = hook_get( id );
1113 if ( h == NULL ) {
1114 WARN( _( "Attempting to run hook of id '%d' which is not in the stack" ),
1115 id );
1116 return -1;
1117 }
1118 hook_run( h, param, -1 );
1119
1120 return 0;
1121}
1122
1129int hook_runID( unsigned int id )
1130{
1131 return hook_runIDparam( id, 0 );
1132}
1133
1139static void hook_free( Hook *h )
1140{
1141 /* Remove from all the pilots. */
1142 pilots_rmHook( h->id );
1143
1144 /* Generic freeing. */
1145 free( h->stack );
1146
1147 /* Free type specific. */
1148 switch ( h->type ) {
1149 case HOOK_TYPE_MISN:
1150 free( h->u.misn.func );
1151 break;
1152
1153 case HOOK_TYPE_EVENT:
1154 free( h->u.event.func );
1155 break;
1156
1157 default:
1158 break;
1159 }
1160
1161 free( h );
1162}
1163
1167void hook_cleanup( void )
1168{
1169 Hook *h;
1170
1171 /* Clear queued hooks. */
1172 hq_clear();
1173
1174 h = hook_list;
1175 while ( h != NULL ) {
1176 Hook *hn = h->next;
1177 hook_free( h );
1178 h = hn;
1179 }
1180 /* safe defaults just in case */
1181 hook_list = NULL;
1182}
1183
1187void hook_clear( void )
1188{
1189 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
1190 if ( h->delete )
1191 continue;
1192 hook_rmRaw( h );
1193 }
1194}
1195
1199void hook_clearMissionTimers( unsigned int parent )
1200{
1201 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
1202 if ( !h->is_timer )
1203 continue;
1204 if ( ( h->type == HOOK_TYPE_MISN ) && ( parent == h->u.misn.parent ) )
1205 h->delete = 1;
1206 }
1207}
1208
1212void hook_clearEventTimers( unsigned int parent )
1213{
1214 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
1215 if ( !h->is_timer )
1216 continue;
1217 if ( ( h->type == HOOK_TYPE_EVENT ) && ( parent == h->u.event.parent ) )
1218 h->delete = 1;
1219 }
1220}
1221
1228static int hook_needSave( Hook *h )
1229{
1230 const char *nosave[] = { "p_death", "p_board", "p_disable",
1231 "p_jump", "p_attacked", "p_idle", /* pilot hooks */
1232 "timer", /* timers */
1233 "end" };
1234
1235 /* Impossible to save functions. */
1236 if ( h->type == HOOK_TYPE_FUNC )
1237 return 0;
1238
1239 /* Events must need saving. */
1240 if ( ( h->type == HOOK_TYPE_EVENT ) && !event_save( h->u.event.parent ) )
1241 return 0;
1242
1243 /* Must not be pending deletion. */
1244 if ( h->delete )
1245 return 0;
1246
1247 /* Make sure it's in the proper stack. */
1248 for ( int i = 0; strcmp( nosave[i], "end" ) != 0; i++ )
1249 if ( strcmp( nosave[i], h->stack ) == 0 )
1250 return 0;
1251
1252 return 1;
1253}
1254
1261int hook_save( xmlTextWriterPtr writer )
1262{
1263 xmlw_startElem( writer, "hooks" );
1264 for ( Hook *h = hook_list; h != NULL; h = h->next ) {
1265
1266 if ( !hook_needSave( h ) )
1267 continue; /* no need to save it */
1268
1269 xmlw_startElem( writer, "hook" );
1270
1271 switch ( h->type ) {
1272 case HOOK_TYPE_MISN:
1273 xmlw_attr( writer, "type", "misn" ); /* Save attribute. */
1274 xmlw_elem( writer, "parent", "%u", h->u.misn.parent );
1275 xmlw_elem( writer, "func", "%s", h->u.misn.func );
1276 break;
1277
1278 case HOOK_TYPE_EVENT:
1279 xmlw_attr( writer, "type", "event" ); /* Save attribute. */
1280 xmlw_elem( writer, "parent", "%u", h->u.event.parent );
1281 xmlw_elem( writer, "func", "%s", h->u.event.func );
1282 break;
1283
1284 default:
1285 WARN( _( "Something has gone screwy here..." ) );
1286 break;
1287 }
1288
1289 /* Generic information. */
1290 xmlw_elem( writer, "id", "%u", h->id );
1291 xmlw_elem( writer, "stack", "%s", h->stack );
1292
1293 /* Store additional date information. */
1294 if ( h->is_date )
1295 xmlw_elem( writer, "resolution", "%" PRId64, h->res );
1296
1297 xmlw_endElem( writer ); /* "hook" */
1298 }
1299 xmlw_endElem( writer ); /* "hooks" */
1300
1301 return 0;
1302}
1303
1310int hook_load( xmlNodePtr parent )
1311{
1312 xmlNodePtr node;
1313
1314 /* We're loading. */
1316
1317 node = parent->xmlChildrenNode;
1318 do {
1319 if ( xml_isNode( node, "hooks" ) )
1320 hook_parse( node );
1321 } while ( xml_nextNode( node ) );
1322
1323 /* Done loading. */
1325
1326 /* Set ID gen to highest hook. */
1327 for ( Hook *h = hook_list; h != NULL; h = h->next )
1328 hook_id = MAX( h->id, hook_id );
1329
1330 return 0;
1331}
1332
1339static int hook_parse( xmlNodePtr base )
1340{
1341 xmlNodePtr node, cur;
1342 char *func, *stack, *stype;
1343 unsigned int parent, id, new_id;
1344 HookType_t type;
1345 Hook *h;
1346 int is_date;
1347 ntime_t res = 0;
1348
1349 node = base->xmlChildrenNode;
1350 do {
1351 if ( xml_isNode( node, "hook" ) ) {
1352 id = 0;
1353 parent = 0;
1354 func = NULL;
1355 stack = NULL;
1356 is_date = 0;
1357
1358 /* Handle the type. */
1359 xmlr_attr_strd( node, "type", stype );
1360 /* Default to mission for old saves. */
1361 if ( stype == NULL )
1362 type = HOOK_TYPE_MISN;
1363 /* Mission type. */
1364 else if ( strcmp( stype, "misn" ) == 0 ) {
1365 type = HOOK_TYPE_MISN;
1366 free( stype );
1367 }
1368 /* Event type. */
1369 else if ( strcmp( stype, "event" ) == 0 ) {
1370 type = HOOK_TYPE_EVENT;
1371 free( stype );
1372 }
1373 /* Unknown. */
1374 else {
1375 WARN( _( "Hook of unknown type '%s' found, skipping." ), stype );
1376 free( stype );
1377 continue;
1378 }
1379
1380 /* Handle the data. */
1381 cur = node->xmlChildrenNode;
1382 do {
1383 xml_onlyNodes( cur );
1384
1385 /* ID. */
1386 xmlr_long( cur, "id", id );
1387
1388 /* Generic. */
1389 xmlr_str( cur, "stack", stack );
1390
1391 /* Type specific. */
1392 if ( ( type == HOOK_TYPE_MISN ) || ( type == HOOK_TYPE_EVENT ) ) {
1393 xmlr_uint( cur, "parent", parent );
1394 xmlr_str( cur, "func", func );
1395 }
1396
1397 /* Check for date hook. */
1398 if ( xml_isNode( cur, "resolution" ) ) {
1399 is_date = 1;
1400 res = xml_getLong( cur );
1401 continue;
1402 }
1403
1404 // cppcheck-suppress nullPointerRedundantCheck
1405 WARN( _( "Save has unknown hook node '%s'." ), cur->name );
1406 } while ( xml_nextNode( cur ) );
1407
1408 /* Check for validity. */
1409 if ( ( parent == 0 ) || ( func == NULL ) || ( stack == NULL ) ) {
1410 WARN( _( "Invalid hook." ) );
1411 continue;
1412 }
1413
1414 /* Create the hook. */
1415 switch ( type ) {
1416 case HOOK_TYPE_MISN:
1417 new_id = hook_addMisn( parent, func, stack );
1418 break;
1419 case HOOK_TYPE_EVENT:
1420 new_id = hook_addEvent( parent, func, stack );
1421 break;
1422 default:
1423 WARN( _( "Save has unsupported hook type." ) );
1424 continue;
1425 }
1426
1427 /* Set the id. */
1428 if ( id != 0 ) {
1429 h = hook_get( new_id );
1430 h->id = id;
1431
1432 /* Additional info. */
1433 if ( is_date ) {
1434 h->is_date = 1;
1435 h->res = res;
1436 }
1437 }
1438 }
1439 } while ( xml_nextNode( node ) );
1440
1441 return 0;
1442}
Provides macros to work with dynamic arrays.
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
int claim_testSys(const Claim_t *claim, int sys)
Tests to see if a system is claimed by a system claim.
Definition claim.c:170
void claim_activateAll(void)
Activates all the claims.
Definition claim.c:240
Event_t * event_get(unsigned int eventid)
Gets an event.
Definition event.c:104
int event_save(unsigned int eventid)
Checks to see if an event should be saved.
Definition event.c:289
int event_testClaims(unsigned int eventid, int sys)
Tests to see if an event has claimed a system.
Definition event.c:800
static unsigned int hook_id
Definition hook.c:115
static int hook_runningstack
Definition hook.c:117
static ntime_t hook_time_accum
Definition hook.c:58
unsigned int hook_addTimerEvt(unsigned int parent, const char *func, double ms)
Adds a new event type hook timer.
Definition hook.c:596
int hook_runIDparam(unsigned int id, const HookParam *param)
Runs a single hook by id.
Definition hook.c:1103
static int hook_parseParam(const HookParam *param)
Parses hook parameters.
Definition hook.c:241
static Hook * hook_new(HookType_t type, const char *stack)
Generates and allocates a new hook.
Definition hook.c:496
int hooks_runParam(const char *stack, const HookParam *param)
Runs all the hooks of stack.
Definition hook.c:1029
static int hook_runMisn(Hook *hook, const HookParam *param, int claims)
Runs a mission hook.
Definition hook.c:311
void hook_rmMisnParent(unsigned int parent)
Removes all hooks belonging to parent mission.
Definition hook.c:869
static void hq_free(HookQueue_t *hq)
Frees a queued hook.
Definition hook.c:168
static int hook_loadingstack
Definition hook.c:118
static void hq_clear(void)
Clears the queued hooks.
Definition hook.c:177
int hook_load(xmlNodePtr parent)
Loads hooks for a player.
Definition hook.c:1310
static Hook * hook_get(unsigned int id)
Gets a hook by ID.
Definition hook.c:1057
void hooks_update(double dt)
Updates all the hook timer related stuff.
Definition hook.c:785
int hook_runID(unsigned int id)
Runs a single hook by id.
Definition hook.c:1129
unsigned int hook_addTimerFunc(int(*func)(void *), void *data, double ms)
Adds a function hook to be run.
Definition hook.c:616
HookType_t
Types of hook.
Definition hook.c:63
@ HOOK_TYPE_EVENT
Definition hook.c:66
@ HOOK_TYPE_NULL
Definition hook.c:64
@ HOOK_TYPE_MISN
Definition hook.c:65
@ HOOK_TYPE_FUNC
Definition hook.c:67
int hook_hasEventParent(unsigned int parent)
Checks to see how many hooks there are with the same event parent.
Definition hook.c:910
static void hooks_updateDateExecute(ntime_t change)
Updates date hooks and runs them if necessary.
Definition hook.c:697
static void hooks_purgeList(void)
Purges the list of deletable hooks.
Definition hook.c:649
static int hq_add(HookQueue_t *hq)
Definition hook.c:148
void hook_clear(void)
Clears the hooks.
Definition hook.c:1187
static HookQueue_t * hook_queue
Definition hook.c:56
void hook_cleanup(void)
Gets rid of all current hooks.
Definition hook.c:1167
int hooks_runParamDeferred(const char *stack, const HookParam *param)
Runs all the hooks of stack in the next frame. Does not trigger right away.
Definition hook.c:996
static int hook_runEvent(Hook *hook, const HookParam *param, int claims)
Runs a Event function hook.
Definition hook.c:375
static int hook_atomic
Definition hook.c:57
nlua_env hook_env(unsigned int hook)
Gets the lua env for a hook.
Definition hook.c:1069
void hooks_updateDate(ntime_t change)
Updates the time to see if it should be updated.
Definition hook.c:688
static int hook_run(Hook *hook, const HookParam *param, int claims)
Runs a hook.
Definition hook.c:429
static Mission * hook_getMission(Hook *hook)
Gets the mission of a hook.
Definition hook.c:832
unsigned int hook_addEvent(unsigned int parent, const char *func, const char *stack)
Adds a new event type hook.
Definition hook.c:550
static void hook_free(Hook *h)
Frees a hook.
Definition hook.c:1139
void hook_rm(unsigned int id)
Removes a hook.
Definition hook.c:846
static Hook * hook_list
Definition hook.c:116
void hook_clearEventTimers(unsigned int parent)
Clears the timer hooks for an event.
Definition hook.c:1212
static int hook_needSave(Hook *h)
Checks if a hook needs to be saved.
Definition hook.c:1228
void hook_exclusionEnd(double dt)
Ends exclusion zone and runs all the queued hooks.
Definition hook.c:199
int hooks_run(const char *stack)
Runs all the hooks of stack.
Definition hook.c:1049
void hook_rmEventParent(unsigned int parent)
Removes all hooks belonging to parent event.
Definition hook.c:881
unsigned int hook_addFunc(int(*func)(void *), void *data, const char *stack)
Adds a function hook to be run.
Definition hook.c:634
static unsigned int hook_genID(void)
Generates a new hook id.
Definition hook.c:473
int hook_hasMisnParent(unsigned int parent)
Checks to see how many hooks there are with the same mission parent.
Definition hook.c:894
void hook_exclusionStart(void)
Starts the hook exclusion zone, this makes hooks queue until exclusion is done.
Definition hook.c:191
unsigned int hook_addTimerMisn(unsigned int parent, const char *func, double ms)
Adds a new mission type hook timer hook.
Definition hook.c:571
static int hook_parse(xmlNodePtr base)
Parses an individual hook.
Definition hook.c:1339
unsigned int hook_addMisn(unsigned int parent, const char *func, const char *stack)
Adds a new mission type hook.
Definition hook.c:529
static void hook_rmRaw(Hook *h)
Removes a hook.
Definition hook.c:858
void hook_clearMissionTimers(unsigned int parent)
Clears the timer hooks for a mission.
Definition hook.c:1199
int hook_save(xmlTextWriterPtr writer)
Saves all the hooks.
Definition hook.c:1261
Handles the important game menus.
#define MENU_MAIN
Definition menu.h:9
#define menu_isOpen(f)
Definition menu.h:16
Mission ** player_missions
Definition mission.c:45
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition naev.h:37
lua_State * naevL
Definition nlua.c:54
Commodity ** lua_pushcommodity(lua_State *L, Commodity *commodity)
Pushes a commodity on the stack.
void event_runStart(unsigned int eventid, const char *func)
Starts running a function, allows programmer to set up arguments.
Definition nlua_evt.c:67
int event_runFunc(unsigned int eventid, const char *func, int nargs)
Runs a function previously set up with event_runStart.
Definition nlua_evt.c:120
LuaFaction * lua_pushfaction(lua_State *L, LuaFaction faction)
Pushes a faction on the stack.
void hookL_unsetarg(unsigned int hook)
Unsets a Lua argument.
Definition nlua_hook.c:201
int hookL_getarg(unsigned int hook)
Gets a Lua argument for a hook.
Definition nlua_hook.c:224
LuaJump * lua_pushjump(lua_State *L, LuaJump jump)
Pushes a jump on the stack.
Definition nlua_jump.c:186
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
const Ship ** lua_pushship(lua_State *L, const Ship *ship)
Pushes a ship on the stack.
Definition nlua_ship.c:193
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:203
LuaSystem * lua_pushsystem(lua_State *L, LuaSystem sys)
Pushes a system on the stack.
void pilots_rmHook(unsigned int hook)
Removes a hook from all the pilots.
Definition pilot_hook.c:181
void player_runHooks(void)
Runs hooks for the player.
Definition player.c:3248
Player_t player
Definition player.c:77
static const double c[]
Definition rng.c:256
StarSystem * cur_system
Definition space.c:110
Activated event structure.
Definition event.h:12
nlua_env env
Definition event.h:15
The actual hook parameter.
Definition hook.h:40
HookParamType type
Definition hook.h:41
Hook queue to delay execution.
Definition hook.c:50
struct HookQueue_s * next
Definition hook.c:51
HookParam hparam[HOOK_MAX_PARAM]
Definition hook.c:54
unsigned int id
Definition hook.c:53
char * stack
Definition hook.c:52
Internal representation of a hook.
Definition hook.c:75
int delete
Definition hook.c:81
struct Hook_ * next
Definition hook.c:76
unsigned int id
Definition hook.c:78
char * func
Definition hook.c:99
int once
Definition hook.c:84
ntime_t acc
Definition hook.c:93
int ran_once
Definition hook.c:82
int is_timer
Definition hook.c:87
int created
Definition hook.c:80
struct Hook::@264345225004242262323027051365150246323230242044::@070036032215365024117244330054177130317351256353 event
ntime_t res
Definition hook.c:92
unsigned int parent
Definition hook.c:98
char * stack
Definition hook.c:79
HookType_t type
Definition hook.c:95
double ms
Definition hook.c:88
int is_date
Definition hook.c:91
union Hook::@264345225004242262323027051365150246323230242044 u
struct Hook::@264345225004242262323027051365150246323230242044::@140054251303342262042302245035216014375144270033 misn
Represents an active mission.
Definition mission.h:83
Claim_t * claims
Definition mission.h:118
nlua_env env
Definition mission.h:120