naev 0.11.5
hook.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
23#include <assert.h>
24#include <stdlib.h>
25
26#include "naev.h"
29#include "hook.h"
30
31#include "array.h"
32#include "claim.h"
33#include "event.h"
34#include "gatherable.h"
35#include "log.h"
36#include "menu.h"
37#include "mission.h"
38#include "nlua_evt.h"
39#include "nlua_hook.h"
40#include "nlua_commodity.h"
41#include "nlua_pilot.h"
42#include "nlua_outfit.h"
43#include "nlua_ship.h"
44#include "nstring.h"
45#include "nxml.h"
46#include "player.h"
47#include "space.h"
48
52typedef struct HookQueue_s {
53 struct HookQueue_s *next;
54 char *stack;
55 unsigned int id;
56 HookParam hparam[ HOOK_MAX_PARAM ];
58static HookQueue_t *hook_queue = NULL;
59static int hook_atomic = 0;
60static ntime_t hook_time_accum = 0;
71
77typedef struct Hook_ {
78 struct Hook_ *next;
80 unsigned int id;
81 char *stack;
82 int created;
83 int delete;
85 int once;
87 /* Timer information. */
89 double ms;
91 /* Date information. */
92 int is_date;
93 ntime_t res;
94 ntime_t acc;
97 union {
98 struct {
99 unsigned int parent;
100 char *func;
101 } misn;
102 struct {
103 unsigned int parent;
104 char *func;
105 } event;
106 struct {
107 int (*func)( void* );
108 void *data;
109 } func;
110 } u;
111} Hook;
112
113/*
114 * the stack
115 */
116static unsigned int hook_id = 0;
117static Hook* hook_list = NULL;
118static int hook_runningstack = 0;
119static int hook_loadingstack = 0;
121/*
122 * prototypes
123 */
124/* Execution. */
125static int hooks_executeParam( const char* stack, const HookParam *param );
126static void hooks_updateDateExecute( ntime_t change );
127/* intern */
128static void hook_rmRaw( Hook *h );
129static void hooks_purgeList (void);
130static Hook* hook_get( unsigned int id );
131static unsigned int hook_genID (void);
132static Hook* hook_new( HookType_t type, const char *stack );
133static int hook_parseParam( const HookParam *param );
134static int hook_runMisn( Hook *hook, const HookParam *param, int claims );
135static int hook_runEvent( Hook *hook, const HookParam *param, int claims );
136static int hook_run( Hook *hook, const HookParam *param, int claims );
137static void hook_free( Hook *h );
138static int hook_needSave( Hook *h );
139static int hook_parse( xmlNodePtr base );
140/* externed */
141int hook_save( xmlTextWriterPtr writer );
142int hook_load( xmlNodePtr parent );
143/* Misc. */
144static Mission *hook_getMission( Hook *hook );
145
149static int hq_add( HookQueue_t *hq )
150{
151 HookQueue_t *c;
152
153 /* Set as head. */
154 if (hook_queue == NULL) {
155 hook_queue = hq;
156 return 0;
157 }
158
159 /* Find tail. */
160 for (c=hook_queue; c->next != NULL; c=c->next);
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
191{
192 hook_atomic = 1;
193}
194
198void hook_exclusionEnd( double dt )
199{
200 HookQueue_t *hq;
201 ntime_t temp;
202 hook_atomic = 0;
203
204 /* Handle hook queue. */
205 while (hook_queue != NULL) {
206 /* Move hook down. */
207 hq = hook_queue;
208 hook_queue = hq->next;
209
210 /* Execute. */
211 hooks_executeParam( hq->stack, hq->hparam );
212
213 /* Clean up. */
214 hq_free( hq );
215 }
216
217 /* Update timer hooks. */
218 hooks_update( dt );
219
220 /* Run assorted hooks. */
221 if (player.p != NULL) /* All these hooks rely on player actually existing. */
223
224 /* Time hooks. */
225 temp = hook_time_accum;
226 hook_time_accum = 0;
228
229 /* Purge the dead. */
231}
232
239static int hook_parseParam( const HookParam *param )
240{
241 int n;
242
243 if (param == NULL)
244 return 0;
245
246 n = 0;
247 while (param[n].type != HOOK_PARAM_SENTINEL) {
248 switch (param[n].type) {
249 case HOOK_PARAM_NIL:
250 lua_pushnil( naevL );
251 break;
252 case HOOK_PARAM_NUMBER:
253 lua_pushnumber( naevL, param[n].u.num );
254 break;
255 case HOOK_PARAM_STRING:
256 lua_pushstring( naevL, param[n].u.str );
257 break;
258 case HOOK_PARAM_BOOL:
259 lua_pushboolean( naevL, param[n].u.b );
260 break;
261 case HOOK_PARAM_PILOT:
262 lua_pushpilot( naevL, param[n].u.lp );
263 break;
264 case HOOK_PARAM_SHIP:
265 lua_pushship( naevL, param[n].u.ship );
266 break;
267 case HOOK_PARAM_OUTFIT:
268 lua_pushoutfit( naevL, param[n].u.outfit );
269 break;
270 case HOOK_PARAM_COMMODITY:
271 lua_pushcommodity( naevL, param[n].u.commodity );
272 break;
273 case HOOK_PARAM_FACTION:
274 lua_pushfaction( naevL, param[n].u.lf );
275 break;
276 case HOOK_PARAM_SPOB:
277 lua_pushspob( naevL, param[n].u.la );
278 break;
279 case HOOK_PARAM_JUMP:
280 lua_pushjump( naevL, param[n].u.lj );
281 break;
282 case HOOK_PARAM_REF:
283 lua_rawgeti( naevL, LUA_REGISTRYINDEX, param[n].u.ref );
284 break;
285
286 default:
287 WARN( _("Unknown Lua parameter type.") );
288 lua_pushnil( naevL );
289 break;
290 }
291 n++;
292 }
293
294 return n;
295}
296
305static int hook_runMisn( Hook *hook, const HookParam *param, int claims )
306{
307 unsigned int id;
308 Mission* misn;
309 int n;
310
311 /* Simplicity. */
312 id = hook->id;
313
314 /* Make sure it's valid. */
315 if (hook->u.misn.parent == 0) {
316 WARN(_("Trying to run hook with nonexistent parent: deleting"));
317 hook->delete = 1; /* so we delete it */
318 return -1;
319 }
320
321 /* Locate the mission */
322 misn = hook_getMission( hook );
323 if (misn == NULL) {
324 WARN(_("Trying to run hook with parent not in player mission stack: deleting"));
325 hook->delete = 1; /* so we delete it. */
326 return -1;
327 }
328
329 /* Make sure it's claimed. */
330 if ((claims > 0) && (claim_testSys(misn->claims, cur_system->id) != claims))
331 return 1;
332
333 /* Delete if only supposed to run once. */
334 if (hook->once)
335 hook_rmRaw( hook );
336
337 /* Set up hook parameters. */
338 misn_runStart( misn, hook->u.misn.func );
339 n = hook_parseParam( param );
340
341 /* Add hook parameters. */
342 n += hookL_getarg( id );
343
344 /* Run mission code. */
345 hook->ran_once = 1;
346 if (misn_runFunc( misn, hook->u.misn.func, n ) < 0) { /* error has occurred */
347 WARN(_("Hook [%s] '%d' -> '%s' failed"), hook->stack,
348 hook->id, hook->u.misn.func);
349 hook_rmRaw( hook );
350 return -1;
351 }
352 return 0;
353}
354
363static int hook_runEvent( Hook *hook, const HookParam *param, int claims )
364{
365 int ret, n, id;
366
367 /* Simplicity. */
368 id = hook->id;
369
370 /* Must match claims. */
371 if ((claims > 0) && (event_testClaims( hook->u.event.parent, cur_system->id ) != claims))
372 return 1;
373
374 /* Delete if only supposed to run once. */
375 if (hook->once)
376 hook_rmRaw( hook );
377
378 /* Set up hook parameters. */
379 if (event_get(hook->u.event.parent) == NULL) {
380 WARN(_("Hook [%s] '%d' -> '%s' failed, event does not exist. Deleting hook."), hook->stack,
381 id, hook->u.event.func);
382 hook->delete = 1; /* Set for deletion. */
383 return -1;
384 }
385
386 event_runStart( hook->u.event.parent, hook->u.event.func );
387 n = hook_parseParam( param );
388
389 /* Add hook parameters. */
390 n += hookL_getarg( id );
391
392 /* Run the hook. */
393 ret = event_runFunc( hook->u.event.parent, hook->u.event.func, n );
394 hook->ran_once = 1;
395 if (ret < 0) {
396 WARN(_("Hook [%s] '%d' -> '%s' failed"), hook->stack,
397 hook->id, hook->u.event.func);
398 hook_rmRaw( hook );
399 return -1;
400 }
401 return 0;
402}
403
412static int hook_run( Hook *hook, const HookParam *param, int claims )
413{
414 int ret;
415
416 /* Do not run if pending deletion. */
417 if (hook->delete)
418 return 0;
419
420 /* Don't run anything when main menu is open. */
422 return 0;
423
424 switch (hook->type) {
425 case HOOK_TYPE_MISN:
426 ret = hook_runMisn(hook, param, claims);
427 break;
428
429 case HOOK_TYPE_EVENT:
430 ret = hook_runEvent(hook, param, claims);
431 break;
432
433 case HOOK_TYPE_FUNC:
434 /* We have to remove the hook first, so it doesn't get run again.
435 * Note that the function will not do any checks nor has arguments, since it is C-side. */
436 if (hook->once)
437 hook->delete = 1;
438 ret = hook->u.func.func( hook->u.func.data );
439 break;
440
441 default:
442 WARN(_("Invalid hook type '%d', deleting."), hook->type);
443 hook->delete = 1;
444 return -1;
445 }
446
447 return ret;
448}
449
455static unsigned int hook_genID (void)
456{
457 unsigned int id = ++hook_id; /* default id, not safe if loading */
458
459 /* If not loading we can just return. */
461 return id;
462
463 /* Must check ids for collisions. */
464 for (Hook *h=hook_list; h!=NULL; h=h->next)
465 if (id == h->id) /* Check for collision. */
466 return hook_genID(); /* recursively try again */
467
468 return id;
469}
470
478static Hook* hook_new( HookType_t type, const char *stack )
479{
480 /* Get and create new hook. */
481 Hook *new_hook = calloc( 1, sizeof(Hook) );
482 if (hook_list == NULL)
483 hook_list = new_hook;
484 else {
485 /* Put at front, O(1). */
486 new_hook->next = hook_list;
487 hook_list = new_hook;
488 }
489
490 /* Fill out generic details. */
491 new_hook->type = type;
492 new_hook->id = hook_genID();
493 new_hook->stack = strdup(stack);
494 new_hook->created = 1;
495
497 if (strcmp(stack,"safe")==0)
498 new_hook->once = 1;
499
500 return new_hook;
501}
502
511unsigned int hook_addMisn( unsigned int parent, const char *func, const char *stack )
512{
513 /* Create the new hook. */
514 Hook *new_hook = hook_new( HOOK_TYPE_MISN, stack );
515
516 /* Put mission specific details. */
517 new_hook->u.misn.parent = parent;
518 new_hook->u.misn.func = strdup(func);
519
520 return new_hook->id;
521}
522
531unsigned int hook_addEvent( unsigned int parent, const char *func, const char *stack )
532{
533 /* Create the new hook. */
534 Hook *new_hook = hook_new( HOOK_TYPE_EVENT, stack );
535
536 /* Put event specific details. */
537 new_hook->u.event.parent = parent;
538 new_hook->u.event.func = strdup(func);
539
540 return new_hook->id;
541}
542
551unsigned int hook_addTimerMisn( unsigned int parent, const char *func, double ms )
552{
553 /* Create the new hook. */
554 Hook *new_hook = hook_new( HOOK_TYPE_MISN, "timer" );
555
556 /* Put mission specific details. */
557 new_hook->u.misn.parent = parent;
558 new_hook->u.misn.func = strdup(func);
559
560 /* Timer information. */
561 new_hook->is_timer = 1;
562 new_hook->ms = ms;
563
564 return new_hook->id;
565}
566
575unsigned int hook_addTimerEvt( unsigned int parent, const char *func, double ms )
576{
577 /* Create the new hook. */
578 Hook *new_hook = hook_new( HOOK_TYPE_EVENT, "timer" );
579
580 /* Put event specific details. */
581 new_hook->u.event.parent = parent;
582 new_hook->u.event.func = strdup(func);
583
584 /* Timer information. */
585 new_hook->is_timer = 1;
586 new_hook->ms = ms;
587
588 return new_hook->id;
589}
590
594unsigned int hook_addFunc( int (*func)(void*), void *data, const char *stack )
595{
596 Hook *new_hook = hook_new( HOOK_TYPE_FUNC, stack );
597
598 /* Function special stuff. */
599 new_hook->u.func.func = func;
600 new_hook->u.func.data = data;
601
602 return new_hook->id;
603}
604
608static void hooks_purgeList (void)
609{
610 Hook *h, *hl;
611
612 /* Do not run while stack is being run. */
614 return;
615
616 /* Second pass to delete. */
617 hl = NULL;
618 h = hook_list;
619 while (h != NULL) {
620 /* Find valid timer hooks. */
621 if (h->delete) {
622
623 if (hl == NULL)
624 hook_list = h->next;
625 else
626 hl->next = h->next;
627
628 /* Free. */
629 h->next = NULL;
630 hook_free( h );
631
632 /* Last. */
633 h = hl;
634 }
635
636 hl = h;
637 if (h == NULL)
638 h = hook_list;
639 else
640 h = h->next;
641 }
642}
643
647void hooks_updateDate( ntime_t change )
648{
649 if (change > 0)
650 hook_time_accum += change;
651}
652
656static void hooks_updateDateExecute( ntime_t change )
657{
658 /* Don't update without player. */
659 if ((player.p == NULL) || player_isFlag(PLAYER_CREATING))
660 return;
661
662 /* Clear creation flags. */
663 for (Hook *h=hook_list; h!=NULL; h=h->next)
664 h->created = 0;
665
666 /* On j=0 we increment all timers and try to run, then on j=1 we update the timers. */
667 hook_runningstack++; /* running hooks */
668 for (int j=1; j>=0; j--) {
669 for (Hook *h=hook_list; h!=NULL; h=h->next) {
670 /* Not be deleting. */
671 if (h->delete)
672 continue;
673 /* Find valid date hooks. */
674 if (h->is_date == 0)
675 continue;
676 /* Don't update newly created hooks. */
677 if (h->created != 0)
678 continue;
679
680 /* Decrement timer and check to see if should run. */
681 if (j==1)
682 h->acc += change;
683 if (h->acc < h->res)
684 continue;
685
686 /* Run the timer hook. */
687 hook_run( h, NULL, j );
688 /* Date hooks are not deleted. */
689
690 /* Time is modified at the end. */
691 if (j==0)
692 h->acc %= h->res; /* We'll skip all buggers. */
693 }
694 }
695 hook_runningstack--; /* not running hooks anymore */
696
697 /* Second pass to delete. */
699}
700
701unsigned int hook_addDateMisn( unsigned int parent, const char *func, ntime_t resolution )
702{
703 /* Create the new hook. */
704 Hook *new_hook = hook_new( HOOK_TYPE_MISN, "date" );
705
706 /* Put mission specific details. */
707 new_hook->u.misn.parent = parent;
708 new_hook->u.misn.func = strdup(func);
709
710 /* Timer information. */
711 new_hook->is_date = 1;
712 new_hook->res = resolution;
713 new_hook->acc = 0;
714
715 return new_hook->id;
716}
717
718unsigned int hook_addDateEvt( unsigned int parent, const char *func, ntime_t resolution )
719{
720 /* Create the new hook. */
721 Hook *new_hook = hook_new( HOOK_TYPE_EVENT, "date" );
722
723 /* Put event specific details. */
724 new_hook->u.event.parent = parent;
725 new_hook->u.event.func = strdup(func);
726
727 /* Timer information. */
728 new_hook->is_date = 1;
729 new_hook->res = resolution;
730 new_hook->acc = 0;
731
732 return new_hook->id;
733}
734
738void hooks_update( double dt )
739{
740 Hook *h;
741
742 /* Don't update without player. */
743 if ((player.p == NULL) || player_isFlag(PLAYER_CREATING) || player_isFlag(PLAYER_DESTROYED))
744 return;
745
746 /* Clear creation flags. */
747 for (h=hook_list; h!=NULL; h=h->next)
748 h->created = 0;
749
750 hook_runningstack++; /* running hooks */
751 for (int j=1; j>=0; j--) {
752 for (h=hook_list; h!=NULL; h=h->next) {
753 /* Not be deleting. */
754 if (h->delete)
755 continue;
756 /* Find valid timer hooks. */
757 if (h->is_timer == 0)
758 continue;
759 /* Don't update newly created hooks. */
760 if (h->created != 0)
761 continue;
762
763 /* Decrement timer and check to see if should run. */
764 if (j==1)
765 h->ms -= dt;
766 if (h->ms > 0.)
767 continue;
768
769 /* Run the timer hook. */
770 hook_run( h, NULL, j );
771 if (h->ran_once) /* Remove when run. */
772 hook_rmRaw( h );
773 }
774 }
775 hook_runningstack--; /* not running hooks anymore */
776
777 /* Second pass to delete. */
779}
780
785{
786 for (int i=0; i<array_size(player_missions); i++)
787 if (player_missions[i]->id == hook->u.misn.parent)
788 return player_missions[i];
789
790 return NULL;
791}
792
798void hook_rm( unsigned int id )
799{
800 Hook *h = hook_get( id );
801 if (h==NULL)
802 return;
803
804 hook_rmRaw( h );
805}
806
810static void hook_rmRaw( Hook *h )
811{
812 h->delete = 1;
813 hookL_unsetarg( h->id );
814}
815
821void hook_rmMisnParent( unsigned int parent )
822{
823 for (Hook *h=hook_list; h!=NULL; h=h->next)
824 if ((h->type==HOOK_TYPE_MISN) && (parent == h->u.misn.parent))
825 h->delete = 1;
826}
827
833void hook_rmEventParent( unsigned int parent )
834{
835 for (Hook *h=hook_list; h!=NULL; h=h->next)
836 if ((h->type==HOOK_TYPE_EVENT) && (parent == h->u.event.parent))
837 h->delete = 1;
838}
839
846int hook_hasMisnParent( unsigned int parent )
847{
848 int num = 0;
849 for (Hook *h=hook_list; h!=NULL; h=h->next)
850 if ((h->type==HOOK_TYPE_MISN) && (parent == h->u.misn.parent))
851 num++;
852
853 return num;
854}
855
862int hook_hasEventParent( unsigned int parent )
863{
864 int num = 0;
865 for (Hook *h=hook_list; h!=NULL; h=h->next)
866 if ((h->type==HOOK_TYPE_EVENT) && (parent == h->u.event.parent))
867 num++;
868
869 return num;
870}
871
872static int hooks_executeParam( const char* stack, const HookParam *param )
873{
874 int run;
875
876 /* Don't update if player is dead. */
877 if ((player.p == NULL) || player_isFlag(PLAYER_DESTROYED))
878 return 0;
879
880 /* Reset the current stack's ran and creation flags. */
881 for (Hook *h=hook_list; h!=NULL; h=h->next)
882 if (strcmp(stack, h->stack)==0) {
883 h->ran_once = 0;
884 h->created = 0;
885 }
886
887 run = 0;
888 hook_runningstack++; /* running hooks */
889 for (int j=1; j>=0; j--) {
890 for (Hook *h=hook_list; h!=NULL; h=h->next) {
891 /* Should be deleted. */
892 if (h->delete)
893 continue;
894 /* Don't run again. */
895 if (h->ran_once)
896 continue;
897 /* Don't update newly created hooks. */
898 if (h->created != 0)
899 continue;
900 /* Doesn't match stack. */
901 if (strcmp(stack, h->stack) != 0)
902 continue;
903
904 /* Run hook. */
905 hook_run( h, param, j );
906 run++;
907
908 /* If hook_cleanup was run, hook_list will be NULL */
909 if (hook_list==NULL)
910 break;
911 }
912 if (hook_list==NULL)
913 break;
914 }
915 hook_runningstack--; /* not running hooks anymore */
916
917 /* Free reference parameters. */
918 if (param != NULL) {
919 int n = 0;
920 while (param[n].type != HOOK_PARAM_SENTINEL) {
921 switch (param[n].type) {
922 case HOOK_PARAM_REF:
923 luaL_unref( naevL, LUA_REGISTRYINDEX, param[n].u.ref );
924 break;
925
926 default:
927 break;
928 }
929 n++;
930 }
931 }
932
933 /* Check claims. */
934 if (run)
936
937 return run;
938}
939
947int hooks_runParamDeferred( const char* stack, const HookParam *param )
948{
949 int i;
950 HookQueue_t *hq;
951
952 /* Don't update if player is dead. */
953 if ((player.p == NULL) || player_isFlag(PLAYER_DESTROYED))
954 return 0;
955
956 hq = calloc( 1, sizeof(HookQueue_t) );
957 hq->stack = strdup(stack);
958 i = 0;
959 if (param != NULL) {
960 for (; param[i].type != HOOK_PARAM_SENTINEL; i++)
961 hq->hparam[i] = param[i];
962 }
963#ifdef DEBUGGING
964 if (i >= HOOK_MAX_PARAM)
965 WARN( _("HOOK_MAX_PARAM is set too low (%d), need at least %d!"), HOOK_MAX_PARAM, i );
966#endif /* DEBUGGING */
967 hq->hparam[i].type = HOOK_PARAM_SENTINEL;
968 hq_add( hq );
969 return 0;
970}
971
979int hooks_runParam( const char* stack, const HookParam *param )
980{
981 /* Don't update if player is dead. */
982 if ((player.p == NULL) || player_isFlag(PLAYER_DESTROYED))
983 return 0;
984
985 /* Not time to run hooks, so queue them. */
986 if (hook_atomic)
987 return hooks_runParamDeferred( stack, param );
988
989 /* Execute. */
990 return hooks_executeParam( stack, param );
991}
992
999int hooks_run( const char* stack )
1000{
1001 return hooks_runParam( stack, NULL );
1002}
1003
1007static Hook* hook_get( unsigned int id )
1008{
1009 for (Hook *h=hook_list; h!=NULL; h=h->next)
1010 if (h->id == id)
1011 return h;
1012
1013 return NULL;
1014}
1015
1019nlua_env hook_env( unsigned int hook )
1020{
1021 Mission *misn;
1022 Event_t *evt;
1023
1024 Hook *h = hook_get(hook);
1025 if (h == NULL)
1026 return LUA_NOREF;
1027
1028 switch (h->type) {
1029 case HOOK_TYPE_MISN:
1030 misn = hook_getMission( h );
1031 if (misn != NULL)
1032 return misn->env;
1033 break;
1034 case HOOK_TYPE_EVENT:
1035 evt = event_get( h->u.event.parent );
1036 if (evt != NULL)
1037 return evt->env;
1038 break;
1039 default:
1040 break;
1041 }
1042
1043 return LUA_NOREF;
1044}
1045
1053int hook_runIDparam( unsigned int id, const HookParam *param )
1054{
1055 Hook *h;
1056
1057 /* Don't update if player is dead. */
1058 if ((player.p == NULL) || player_isFlag(PLAYER_DESTROYED))
1059 return 0;
1060
1061 /* Try to find the hook and run it. */
1062 h = hook_get( id );
1063 if (h == NULL) {
1064 WARN(_("Attempting to run hook of id '%d' which is not in the stack"), id);
1065 return -1;
1066 }
1067 hook_run( h, param, -1 );
1068
1069 return 0;
1070}
1071
1078int hook_runID( unsigned int id )
1079{
1080 return hook_runIDparam( id, 0 );
1081}
1082
1088static void hook_free( Hook *h )
1089{
1090 /* Remove from all the pilots. */
1091 pilots_rmHook( h->id );
1092
1093 /* Generic freeing. */
1094 free( h->stack );
1095
1096 /* Free type specific. */
1097 switch (h->type) {
1098 case HOOK_TYPE_MISN:
1099 free( h->u.misn.func );
1100 break;
1101
1102 case HOOK_TYPE_EVENT:
1103 free( h->u.event.func );
1104 break;
1105
1106 default:
1107 break;
1108 }
1109
1110 free( h );
1111}
1112
1116void hook_cleanup (void)
1117{
1118 Hook *h;
1119
1120 /* Clear queued hooks. */
1121 hq_clear();
1122
1123 h = hook_list;
1124 while (h != NULL) {
1125 Hook *hn = h->next;
1126 hook_free( h );
1127 h = hn;
1128 }
1129 /* safe defaults just in case */
1130 hook_list = NULL;
1131}
1132
1136void hook_clear (void)
1137{
1138 for (Hook *h=hook_list; h!=NULL; h=h->next) {
1139 if (h->delete)
1140 continue;
1141 hook_rmRaw( h );
1142 }
1143}
1144
1151static int hook_needSave( Hook *h )
1152{
1153 const char *nosave[] = {
1154 "p_death", "p_board", "p_disable", "p_jump", "p_attacked", "p_idle", /* pilot hooks */
1155 "timer", /* timers */
1156 "end" };
1157
1158 /* Impossible to save functions. */
1159 if (h->type == HOOK_TYPE_FUNC)
1160 return 0;
1161
1162 /* Events must need saving. */
1163 if ((h->type == HOOK_TYPE_EVENT) && !event_save(h->u.event.parent))
1164 return 0;
1165
1166 /* Must not be pending deletion. */
1167 if (h->delete)
1168 return 0;
1169
1170 /* Make sure it's in the proper stack. */
1171 for (int i=0; strcmp(nosave[i],"end") != 0; i++)
1172 if (strcmp(nosave[i],h->stack)==0) return 0;
1173
1174 return 1;
1175}
1176
1183int hook_save( xmlTextWriterPtr writer )
1184{
1185 xmlw_startElem(writer,"hooks");
1186 for (Hook *h=hook_list; h!=NULL; h=h->next) {
1187
1188 if (!hook_needSave(h))
1189 continue; /* no need to save it */
1190
1191 xmlw_startElem(writer,"hook");
1192
1193 switch (h->type) {
1194 case HOOK_TYPE_MISN:
1195 xmlw_attr(writer,"type","misn"); /* Save attribute. */
1196 xmlw_elem(writer,"parent","%u",h->u.misn.parent);
1197 xmlw_elem(writer,"func","%s",h->u.misn.func);
1198 break;
1199
1200 case HOOK_TYPE_EVENT:
1201 xmlw_attr(writer,"type","event"); /* Save attribute. */
1202 xmlw_elem(writer,"parent","%u",h->u.event.parent);
1203 xmlw_elem(writer,"func","%s",h->u.event.func);
1204 break;
1205
1206 default:
1207 WARN(_("Something has gone screwy here..."));
1208 break;
1209 }
1210
1211 /* Generic information. */
1212 xmlw_elem(writer,"id","%u",h->id);
1213 xmlw_elem(writer,"stack","%s",h->stack);
1214
1215 /* Store additional date information. */
1216 if (h->is_date)
1217 xmlw_elem(writer,"resolution","%"PRId64,h->res);
1218
1219 xmlw_endElem(writer); /* "hook" */
1220 }
1221 xmlw_endElem(writer); /* "hooks" */
1222
1223 return 0;
1224}
1225
1232int hook_load( xmlNodePtr parent )
1233{
1234 xmlNodePtr node;
1235
1236 /* We're loading. */
1238
1239 node = parent->xmlChildrenNode;
1240 do {
1241 if (xml_isNode(node,"hooks"))
1242 hook_parse(node);
1243 } while (xml_nextNode(node));
1244
1245 /* Done loading. */
1247
1248 /* Set ID gen to highest hook. */
1249 for (Hook *h=hook_list; h!=NULL; h=h->next)
1250 hook_id = MAX( h->id, hook_id );
1251
1252 return 0;
1253}
1254
1261static int hook_parse( xmlNodePtr base )
1262{
1263 xmlNodePtr node, cur;
1264 char *func, *stack, *stype;
1265 unsigned int parent, id, new_id;
1266 HookType_t type;
1267 Hook *h;
1268 int is_date;
1269 ntime_t res = 0;
1270
1271 node = base->xmlChildrenNode;
1272 do {
1273 if (xml_isNode(node, "hook")) {
1274 id = 0;
1275 parent = 0;
1276 func = NULL;
1277 stack = NULL;
1278 is_date = 0;
1279
1280 /* Handle the type. */
1281 xmlr_attr_strd(node, "type", stype);
1282 /* Default to mission for old saves. */
1283 if (stype == NULL)
1284 type = HOOK_TYPE_MISN;
1285 /* Mission type. */
1286 else if (strcmp(stype, "misn") == 0) {
1287 type = HOOK_TYPE_MISN;
1288 free(stype);
1289 }
1290 /* Event type. */
1291 else if (strcmp(stype, "event") == 0) {
1292 type = HOOK_TYPE_EVENT;
1293 free(stype);
1294 }
1295 /* Unknown. */
1296 else {
1297 WARN(_("Hook of unknown type '%s' found, skipping."), stype);
1298 free(stype);
1299 continue;
1300 }
1301
1302 /* Handle the data. */
1303 cur = node->xmlChildrenNode;
1304 do {
1305 xml_onlyNodes(cur);
1306
1307 /* ID. */
1308 xmlr_long(cur, "id", id);
1309
1310 /* Generic. */
1311 xmlr_str(cur, "stack", stack);
1312
1313 /* Type specific. */
1314 if ((type == HOOK_TYPE_MISN) || (type == HOOK_TYPE_EVENT)) {
1315 xmlr_uint(cur, "parent", parent);
1316 xmlr_str(cur, "func", func);
1317 }
1318
1319 /* Check for date hook. */
1320 if (xml_isNode( cur, "resolution" )) {
1321 is_date = 1;
1322 res = xml_getLong( cur );
1323 continue;
1324 }
1325
1326 WARN(_("Save has unknown hook node '%s'."), cur->name);
1327 } while (xml_nextNode(cur));
1328
1329 /* Check for validity. */
1330 if ((parent == 0) || (func == NULL) || (stack == NULL)) {
1331 WARN(_("Invalid hook."));
1332 continue;
1333 }
1334
1335 /* Create the hook. */
1336 switch (type) {
1337 case HOOK_TYPE_MISN:
1338 new_id = hook_addMisn( parent, func, stack );
1339 break;
1340 case HOOK_TYPE_EVENT:
1341 new_id = hook_addEvent( parent, func, stack );
1342 break;
1343 default:
1344 WARN(_("Save has unsupported hook type."));
1345 continue;
1346 }
1347
1348 /* Set the id. */
1349 if (id != 0) {
1350 h = hook_get( new_id );
1351 h->id = id;
1352
1353 /* Additional info. */
1354 if (is_date) {
1355 h->is_date = 1;
1356 h->res = res;
1357 }
1358 }
1359 }
1360 } while (xml_nextNode(node));
1361
1362 return 0;
1363}
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:168
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:239
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:779
static unsigned int hook_id
Definition hook.c:116
static int hook_runningstack
Definition hook.c:118
static ntime_t hook_time_accum
Definition hook.c:60
unsigned int hook_addTimerEvt(unsigned int parent, const char *func, double ms)
Adds a new event type hook timer.
Definition hook.c:575
int hook_runIDparam(unsigned int id, const HookParam *param)
Runs a single hook by id.
Definition hook.c:1053
static int hook_parseParam(const HookParam *param)
Parses hook parameters.
Definition hook.c:239
static Hook * hook_new(HookType_t type, const char *stack)
Generates and allocates a new hook.
Definition hook.c:478
int hooks_runParam(const char *stack, const HookParam *param)
Runs all the hooks of stack.
Definition hook.c:979
static int hook_runMisn(Hook *hook, const HookParam *param, int claims)
Runs a mission hook.
Definition hook.c:305
void hook_rmMisnParent(unsigned int parent)
Removes all hooks belonging to parent mission.
Definition hook.c:821
static void hq_free(HookQueue_t *hq)
Frees a queued hook.
Definition hook.c:168
static int hook_loadingstack
Definition hook.c:119
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:1232
static Hook * hook_get(unsigned int id)
Gets a hook by ID.
Definition hook.c:1007
void hooks_update(double dt)
Updates all the hook timer related stuff.
Definition hook.c:738
int hook_runID(unsigned int id)
Runs a single hook by id.
Definition hook.c:1078
HookType_t
Types of hook.
Definition hook.c:65
@ HOOK_TYPE_EVENT
Definition hook.c:68
@ HOOK_TYPE_NULL
Definition hook.c:66
@ HOOK_TYPE_MISN
Definition hook.c:67
@ HOOK_TYPE_FUNC
Definition hook.c:69
int hook_hasEventParent(unsigned int parent)
Checks to see how many hooks there are with the same event parent.
Definition hook.c:862
static void hooks_updateDateExecute(ntime_t change)
Updates date hooks and runs them if necessary.
Definition hook.c:656
static void hooks_purgeList(void)
Purges the list of deletable hooks.
Definition hook.c:608
static int hq_add(HookQueue_t *hq)
Definition hook.c:149
void hook_clear(void)
Clears the hooks.
Definition hook.c:1136
static HookQueue_t * hook_queue
Definition hook.c:58
void hook_cleanup(void)
Gets rid of all current hooks.
Definition hook.c:1116
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:947
static int hook_runEvent(Hook *hook, const HookParam *param, int claims)
Runs a Event function hook.
Definition hook.c:363
static int hook_atomic
Definition hook.c:59
nlua_env hook_env(unsigned int hook)
Gets the lua env for a hook.
Definition hook.c:1019
void hooks_updateDate(ntime_t change)
Updates the time to see if it should be updated.
Definition hook.c:647
static int hook_run(Hook *hook, const HookParam *param, int claims)
Runs a hook.
Definition hook.c:412
static Mission * hook_getMission(Hook *hook)
Gets the mission of a hook.
Definition hook.c:784
unsigned int hook_addEvent(unsigned int parent, const char *func, const char *stack)
Adds a new event type hook.
Definition hook.c:531
static void hook_free(Hook *h)
Frees a hook.
Definition hook.c:1088
void hook_rm(unsigned int id)
Removes a hook.
Definition hook.c:798
static Hook * hook_list
Definition hook.c:117
static int hook_needSave(Hook *h)
Checks if a hook needs to be saved.
Definition hook.c:1151
void hook_exclusionEnd(double dt)
Ends exclusion zone and runs all the queued hooks.
Definition hook.c:198
int hooks_run(const char *stack)
Runs all the hooks of stack.
Definition hook.c:999
void hook_rmEventParent(unsigned int parent)
Removes all hooks belonging to parent event.
Definition hook.c:833
unsigned int hook_addFunc(int(*func)(void *), void *data, const char *stack)
Adds a function hook to be run.
Definition hook.c:594
static unsigned int hook_genID(void)
Generates a new hook id.
Definition hook.c:455
int hook_hasMisnParent(unsigned int parent)
Checks to see how many hooks there are with the same mission parent.
Definition hook.c:846
void hook_exclusionStart(void)
Starts the hook exclusion zone, this makes hooks queue until exclusion is done.
Definition hook.c:190
unsigned int hook_addTimerMisn(unsigned int parent, const char *func, double ms)
Adds a new mission type hook timer hook.
Definition hook.c:551
static int hook_parse(xmlNodePtr base)
Parses an individual hook.
Definition hook.c:1261
unsigned int hook_addMisn(unsigned int parent, const char *func, const char *stack)
Adds a new mission type hook.
Definition hook.c:511
static void hook_rmRaw(Hook *h)
Removes a hook.
Definition hook.c:810
int hook_save(xmlTextWriterPtr writer)
Saves all the hooks.
Definition hook.c:1183
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:47
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition naev.h:39
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:78
int event_runFunc(unsigned int eventid, const char *func, int nargs)
Runs a function previously set up with event_runStart.
Definition nlua_evt.c:131
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:185
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:563
const Ship ** lua_pushship(lua_State *L, const Ship *ship)
Pushes a ship on the stack.
Definition nlua_ship.c:166
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:201
void pilots_rmHook(unsigned int hook)
Removes a hook from all the pilots.
Definition pilot_hook.c:175
void player_runHooks(void)
Runs hooks for the player.
Definition player.c:3077
Player_t player
Definition player.c:74
static const double c[]
Definition rng.c:264
StarSystem * cur_system
Definition space.c:106
Activated event structure.
Definition event.h:12
nlua_env env
Definition event.h:15
The actual hook parameter.
Definition hook.h:38
HookParamType type
Definition hook.h:39
Hook queue to delay execution.
Definition hook.c:52
struct HookQueue_s * next
Definition hook.c:53
HookParam hparam[HOOK_MAX_PARAM]
Definition hook.c:56
unsigned int id
Definition hook.c:55
char * stack
Definition hook.c:54
Internal representation of a hook.
Definition hook.c:77
int delete
Definition hook.c:83
struct Hook_ * next
Definition hook.c:78
unsigned int id
Definition hook.c:80
char * func
Definition hook.c:100
int once
Definition hook.c:85
ntime_t acc
Definition hook.c:94
int ran_once
Definition hook.c:84
int is_timer
Definition hook.c:88
struct Hook::@32::@34 event
int created
Definition hook.c:82
ntime_t res
Definition hook.c:93
unsigned int parent
Definition hook.c:99
char * stack
Definition hook.c:81
HookType_t type
Definition hook.c:96
double ms
Definition hook.c:89
struct Hook::@32::@33 misn
int is_date
Definition hook.c:92
union Hook::@32 u
Represents an active mission.
Definition mission.h:81
Claim_t * claims
Definition mission.h:105
nlua_env env
Definition mission.h:107
Pilot * p
Definition player.h:101