2 * See Licensing and Copyright notice in naev.h
8 * @brief Handles internal events.
10 * Events are a lot like missions except the player has no control over when
11 * or how they happen. They can simple do something simple or actually lead up
12 * to and open an entire set of missions.
28 #include "nlua_hook.h"
39 #define XML_EVENT_ID "Events" /**< XML document identifier */
40 #define XML_EVENT_TAG "event" /**< XML mission tag. */
42 #define EVENT_DATA "dat/event.xml" /**< Path to missions XML. */
43 #define EVENT_LUA_PATH "dat/events/" /**< Path to Lua files. */
45 #define EVENT_CHUNK 32 /**< Size to grow event data by. */
48 #define EVENT_FLAG_UNIQUE (1<<0) /**< Unique event. */
52 * @brief Event data structure.
54 typedef struct EventData_s
{
55 char *name
; /**< Name of the event. */
56 char *lua
; /**< Name of Lua file to use. */
57 unsigned int flags
; /**< Bit flags. */
59 EventTrigger_t trigger
; /**< What triggers the event. */
60 char *cond
; /**< Conditional Lua code to execute. */
61 double chance
; /**< Chance of appearing. */
68 static EventData_t
*event_data
= NULL
; /**< Allocated event data. */
69 static int event_ndata
= 0; /**< Number of actual event data. */
75 static unsigned int event_genid
= 0; /**< Event ID generator. */
76 static Event_t
*event_active
= NULL
; /**< Active events. */
77 static int event_nactive
= 0; /**< Number of active events. */
78 static int event_mactive
= 0; /**< Allocated space for active events. */
84 static Event_t
*event_get( unsigned int eventid
);
85 static int event_alreadyRunning( int data
);
86 static int event_parse( EventData_t
*temp
, const xmlNodePtr parent
);
87 static void event_freeData( EventData_t
*event
);
88 static int event_create( int dataid
);
92 * @brief Gets an event.
94 static Event_t
*event_get( unsigned int eventid
)
100 for (i
=0; i
<event_nactive
; i
++) {
101 ev
= &event_active
[i
];
102 if (ev
->id
== eventid
)
106 WARN( "Event '%u' not found in stack.", eventid
);
112 * @brief Starts running a function, allows programmer to set up arguments.
114 lua_State
*event_runStart( unsigned int eventid
, const char *func
)
118 ev
= event_get( eventid
);
122 return event_setupLua( ev
, func
);
127 * @brief Runs a function previously set up with event_runStart.
129 int event_runFunc( unsigned int eventid
, const char *func
, int nargs
)
133 ev
= event_get( eventid
);
137 return event_runLuaFunc( ev
, func
, nargs
);
142 * @brief Runs the event function.
144 * @param eventid ID of the event to run Lua function on.
145 * @param func Name of the function to run.
146 * @return 0 on success.
148 int event_run( unsigned int eventid
, const char *func
)
152 ev
= event_get( eventid
);
156 return event_runLua( ev
, func
);
161 * @brief Gets the name of the event data.
163 * @param ev Event to get name of data from.
164 * @return Name of data ev has.
166 const char *event_getData( unsigned int eventid
)
170 ev
= event_get( eventid
);
174 return event_data
[ ev
->data
].name
;
179 * @brief Checks to see if an event is unique.
181 * @param eventid ID of event to see if is unique.
182 * @return 0 if isn't unique, 1 if is.
184 int event_isUnique( unsigned int eventid
)
188 ev
= event_get( eventid
);
192 return !!(event_data
[ ev
->data
].flags
& EVENT_FLAG_UNIQUE
);
197 * @brief Creates an event.
199 * @param data Data to base event off of.
201 static int event_create( int dataid
)
209 /* Create the event. */
211 if (event_nactive
> event_mactive
) {
212 event_mactive
+= EVENT_CHUNK
;
213 event_active
= realloc( event_active
, sizeof(Event_t
) * event_mactive
);
215 ev
= &event_active
[ event_nactive
-1 ];
216 memset( ev
, 0, sizeof(Event_t
) );
217 ev
->id
= ++event_genid
; /* Create unique ID. */
221 data
= &event_data
[dataid
];
223 /* Open the new state. */
224 ev
->L
= nlua_newState();
226 nlua_loadStandard(L
,0);
232 buf
= ndata_read( data
->lua
, &bufsize
);
234 WARN("Event '%s' Lua script not found.", data
->lua
);
237 if (luaL_dobuffer(L
, buf
, bufsize
, data
->lua
) != 0) {
238 WARN("Error loading event file: %s\n"
240 "Most likely Lua file has improper syntax, please check",
241 data
->lua
, lua_tostring(L
,-1));
247 event_runLua( ev
, "create" );
254 * @brief Cleans up an event.
256 * @param ev Event to clean up.
258 static void event_cleanup( Event_t
*ev
)
266 hook_rmEventParent(ev
->id
);
269 npc_rm_parentEvent(ev
->id
);
272 for (i
=0; i
<EVENT_TIMER_MAX
; i
++) {
273 if (ev
->tfunc
[i
] != NULL
) {
282 * @brief Removes an event by ID.
284 * @param eventid ID of the event to remove.
286 void event_remove( unsigned int eventid
)
291 /* Find the event. */
292 for (i
=0; i
<event_nactive
; i
++) {
293 ev
= &event_active
[i
];
294 if (ev
->id
== eventid
) {
295 /* Clean up event. */
299 memmove( &event_active
[i
], &event_active
[i
+1],
300 sizeof(Event_t
) * (event_nactive
-i
-1) );
306 WARN("Event ID '%u' not valid.", eventid
);
311 * @brief Runs event timer stuff.
313 void events_update( double dt
)
319 for (i
=0; i
<event_nactive
; i
++) {
320 ev
= &event_active
[i
];
322 /* Decrement timers see if must run. */
323 for (j
=0; j
<EVENT_TIMER_MAX
; j
++) {
325 if (ev
->timer
[j
] > 0.) {
329 /* Timer is up - trigger function. */
330 if (ev
->timer
[j
] < 0.) {
334 tfunc
= ev
->tfunc
[j
];
338 event_runLua( ev
, tfunc
);
340 /* Free remainder stuff. */
350 * @brief Check to see if an event is already running.
352 * @param data ID of data event to check if is already running.
354 static int event_alreadyRunning( int data
)
360 for (i
=0; i
<event_nactive
; i
++) {
361 ev
= &event_active
[i
];
362 if (ev
->data
== data
) {
372 * @brief Runs all the events matching a trigger.
374 * @param trigger Trigger to match.
376 void events_trigger( EventTrigger_t trigger
)
380 for (i
=0; i
<event_ndata
; i
++) {
381 /* Make sure trigger matches. */
382 if (event_data
[i
].trigger
!= trigger
)
385 /* Make sure chance is succeeded. */
386 if (RNGF() > event_data
[i
].chance
)
389 /* Test uniqueness. */
390 if ((event_data
[i
].flags
& EVENT_FLAG_UNIQUE
) &&
391 (player_eventAlreadyDone( i
) || event_alreadyRunning(i
)))
394 /* Test conditional. */
395 if (event_data
[i
].cond
!= NULL
) {
396 c
= cond_check(event_data
[i
].cond
);
398 WARN("Conditional for event '%s' failed to run.", event_data
[i
].name
);
405 /* Create the event. */
412 * @brief Loads up an event from an XML node.
414 * @param temp Event to load up.
415 * @param parent Event parent node.
416 * @return 0 on success.
418 static int event_parse( EventData_t
*temp
, const xmlNodePtr parent
)
420 xmlNodePtr node
, cur
;
421 char str
[PATH_MAX
] = "\0";
425 /* To check if mission is valid. */
429 #endif /* DEBUGGING */
431 memset( temp
, 0, sizeof(EventData_t
) );
434 temp
->name
= xml_nodeProp(parent
,"name");
435 if (temp
->name
== NULL
)
436 WARN("Event in "EVENT_DATA
" has invalid or no name");
438 node
= parent
->xmlChildrenNode
;
440 do { /* load all the data */
442 /* Only check nodes. */
445 if (xml_isNode(node
,"lua")) {
446 snprintf( str
, PATH_MAX
, EVENT_LUA_PATH
"%s.lua", xml_get(node
) );
447 temp
->lua
= strdup( str
);
451 /* Check to see if syntax is valid. */
453 buf
= ndata_read( temp
->lua
, &len
);
454 ret
= luaL_loadbuffer(L
, buf
, len
, temp
->name
);
455 if (ret
== LUA_ERRSYNTAX
) {
456 WARN("Event Lua '%s' of mission '%s' syntax error: %s",
457 temp
->name
, temp
->lua
, lua_tostring(L
,-1) );
461 #endif /* DEBUGGING */
467 else if (xml_isNode(node
,"trigger")) {
470 WARN("Event '%s': Null trigger type.", temp
->name
);
471 else if (strcmp(buf
,"enter")==0)
472 temp
->trigger
= EVENT_TRIGGER_ENTER
;
473 else if (strcmp(buf
,"land")==0)
474 temp
->trigger
= EVENT_TRIGGER_LAND
;
476 WARN("Event '%s' has invalid 'trigger' parameter: %s", temp
->name
, buf
);
482 else if (xml_isNode(node
,"flags")) { /* set the various flags */
483 cur
= node
->children
;
485 if (xml_isNode(cur
,"unique"))
486 temp
->flags
|= EVENT_FLAG_UNIQUE
;
487 } while (xml_nextNode(cur
));
492 xmlr_strd(node
,"cond",temp
->cond
);
495 xmlr_float(node
,"chance",temp
->chance
);
497 DEBUG("Unknown node '%s' in event '%s'", node
->name
, temp
->name
);
498 } while (xml_nextNode(node
));
501 temp
->chance
/= 100.;
503 #define MELEMENT(o,s) \
504 if (o) WARN("Mission '%s' missing/invalid '"s"' element", temp->name)
505 MELEMENT(temp
->lua
==NULL
,"lua");
506 MELEMENT(temp
->chance
==0.,"chance");
507 MELEMENT(temp
->trigger
==EVENT_TRIGGER_NULL
,"trigger");
515 * @brief Loads all the events.
517 * @return 0 on success.
519 int events_load (void)
528 buf
= ndata_read( EVENT_DATA
, &bufsize
);
530 WARN("Unable to read data from '%s'", EVENT_DATA
);
534 /* Load the document. */
535 doc
= xmlParseMemory( buf
, bufsize
);
537 WARN("Unable to parse document '%s'", EVENT_DATA
);
541 /* Get the root node. */
542 node
= doc
->xmlChildrenNode
;
543 if (!xml_isNode(node
,XML_EVENT_ID
)) {
544 WARN("Malformed '"EVENT_DATA
"' file: missing root element '"XML_EVENT_ID
"'");
548 /* Get the first node. */
549 node
= node
->xmlChildrenNode
; /* first mission node */
551 WARN("Malformed '"EVENT_DATA
"' file: does not contain elements");
557 if (xml_isNode(node
,XML_EVENT_TAG
)) {
559 /* See if must grow. */
561 if (event_ndata
> m
) {
563 event_data
= realloc(event_data
, sizeof(EventData_t
)*m
);
567 event_parse( &event_data
[event_ndata
-1], node
);
569 } while (xml_nextNode(node
));
571 /* Shrink to minimum. */
572 event_data
= realloc(event_data
, sizeof(EventData_t
)*event_ndata
);
578 DEBUG("Loaded %d Event%s", event_ndata
, (event_ndata
==1) ? "" : "s" );
585 * @brief Frees an EventData structure.
587 * @param event Event Data to free.
589 static void event_freeData( EventData_t
*event
)
607 * @brief Cleans up and removes active events.
609 void events_cleanup (void)
613 /* Free active events. */
614 for (i
=0; i
<event_nactive
; i
++)
615 event_cleanup( &event_active
[i
] );
616 if (event_active
!= NULL
) {
626 * @brief Exits the event subsystem.
628 void events_exit (void)
635 for (i
=0; i
<event_ndata
; i
++)
636 event_freeData(&event_data
[i
]);
637 if (event_data
!= NULL
) {
638 event_freeData(event_data
);
647 * @brief Gets the event data id from name.
649 * @param evdata Name of the data.
650 * @return ID matching dataname.
652 int event_dataID( const char *evdata
)
656 for (i
=0; i
<event_ndata
; i
++)
657 if (strcmp(event_data
[i
].name
, evdata
)==0)
659 WARN("No event data found matching name '%s'.", evdata
);
665 * @brief Gets the event data name from id.
667 * @param dataid ID of the event data to get name of.
668 * @return Name of the event data.
670 const char *event_dataName( int dataid
)
672 return event_data
[dataid
].name
;