1 /* vi: set ts=2 shiftwidth=2 expandtab:
2  *
3  * Copyright (C) 2003-2008  Simon Baldwin and Mark J. Tilford
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of version 2 of the GNU General Public License
7  * as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
17  * USA
18  */
19 
20 /*
21  * Module notes:
22  *
23  * o Event pause and resume tasks need more testing.
24  */
25 
26 #include <assert.h>
27 #include <stdlib.h>
28 
29 #include "scare.h"
30 #include "scprotos.h"
31 #include "scgamest.h"
32 
33 
34 /* Trace flag, set before running. */
35 static sc_bool evt_trace = FALSE;
36 
37 
38 /*
39  * evt_any_task_in_state()
40  *
41  * Return TRUE if any task at all matches the given completion state.
42  */
43 static sc_bool
evt_any_task_in_state(sc_gameref_t game,sc_bool state)44 evt_any_task_in_state (sc_gameref_t game, sc_bool state)
45 {
46   sc_int task;
47 
48   /* Scan tasks for any whose completion matches input. */
49   for (task = 0; task < gs_task_count (game); task++)
50     {
51       if (gs_task_done (game, task) == state)
52         return TRUE;
53     }
54 
55   /* No tasks matched. */
56   return FALSE;
57 }
58 
59 
60 /*
61  * evt_can_see_event()
62  *
63  * Return TRUE if player is in the right room for event text.
64  */
65 sc_bool
evt_can_see_event(sc_gameref_t game,sc_int event)66 evt_can_see_event (sc_gameref_t game, sc_int event)
67 {
68   const sc_prop_setref_t bundle = gs_get_bundle (game);
69   sc_vartype_t vt_key[5];
70   sc_int type;
71 
72   /* Check room list for the event and return it. */
73   vt_key[0].string = "Events";
74   vt_key[1].integer = event;
75   vt_key[2].string = "Where";
76   vt_key[3].string = "Type";
77   type = prop_get_integer (bundle, "I<-siss", vt_key);
78   switch (type)
79     {
80     case ROOMLIST_NO_ROOMS:
81       return FALSE;
82     case ROOMLIST_ALL_ROOMS:
83       return TRUE;
84 
85     case ROOMLIST_ONE_ROOM:
86       vt_key[3].string = "Room";
87       return prop_get_integer (bundle, "I<-siss", vt_key)
88              == gs_playerroom (game);
89 
90     case ROOMLIST_SOME_ROOMS:
91       vt_key[3].string = "Rooms";
92       vt_key[4].integer = gs_playerroom (game);
93       return prop_get_boolean (bundle, "B<-sissi", vt_key);
94 
95     default:
96       sc_fatal ("evt_can_see_event: invalid type, %ld\n", type);
97       return FALSE;
98     }
99 }
100 
101 
102 /*
103  * evt_move_object()
104  *
105  * Move an object from within an event.
106  */
107 static void
evt_move_object(sc_gameref_t game,sc_int object,sc_int destination)108 evt_move_object (sc_gameref_t game, sc_int object, sc_int destination)
109 {
110   /* Ignore negative values of object. */
111   if (object >= 0)
112     {
113       if (evt_trace)
114         {
115           sc_trace ("Event: moving object %ld to room %ld\n",
116                     object, destination);
117         }
118 
119       /* Move object depending on destination. */
120       switch (destination)
121         {
122         case -1:               /* Hidden. */
123           gs_object_make_hidden (game, object);
124           break;
125 
126         case 0:                /* Held by player. */
127           gs_object_player_get (game, object);
128           break;
129 
130         case 1:                /* Same room as player. */
131           gs_object_to_room (game, object, gs_playerroom (game));
132           break;
133 
134         default:
135           if (destination < gs_room_count (game) + 2)
136             gs_object_to_room (game, object, destination - 2);
137           else
138             {
139               sc_int roomgroup, room;
140 
141               roomgroup = destination - gs_room_count (game) - 2;
142               room = lib_random_roomgroup_member (game, roomgroup);
143               gs_object_to_room (game, object, room);
144             }
145           break;
146         }
147 
148       /*
149        * If static, mark as no longer unmoved.
150        *
151        * TODO Is this the only place static objects can be moved?  And just
152        * how static is a static object if it's moveable, anyway?
153        */
154       if (obj_is_static (game, object))
155         gs_set_object_static_unmoved (game, object, FALSE);
156     }
157 }
158 
159 
160 /*
161  * evt_fixup_v390_v380_immediate_restart()
162  *
163  * Versions 3.9 and 3.8 differ from version 4.0 on immediate restart; they
164  * "miss" the event start actions and move one step into the event without
165  * comment.  It's arguable if this is a feature or a bug; nevertheless, we
166  * can do the same thing here, though it's ugly.
167  */
168 static sc_bool
evt_fixup_v390_v380_immediate_restart(sc_gameref_t game,sc_int event)169 evt_fixup_v390_v380_immediate_restart (sc_gameref_t game, sc_int event)
170 {
171   const sc_prop_setref_t bundle = gs_get_bundle (game);
172   sc_vartype_t vt_key[3];
173   sc_int version;
174 
175   vt_key[0].string = "Version";
176   version = prop_get_integer (bundle, "I<-s", vt_key);
177   if (version < TAF_VERSION_400)
178     {
179       sc_int time1, time2;
180 
181       if (evt_trace)
182         sc_trace ("Event: applying 3.9/3.8 restart fixup\n");
183 
184       /* Set to running state. */
185       gs_set_event_state (game, event, ES_RUNNING);
186 
187       /* Set up event time to be one less than a proper start. */
188       vt_key[0].string = "Events";
189       vt_key[1].integer = event;
190       vt_key[2].string = "Time1";
191       time1 = prop_get_integer (bundle, "I<-sis", vt_key);
192       vt_key[2].string = "Time2";
193       time2 = prop_get_integer (bundle, "I<-sis", vt_key);
194       gs_set_event_time (game, event, sc_randomint (time1, time2) - 1);
195     }
196 
197   /* Return TRUE if we applied the fixup. */
198   return version < TAF_VERSION_400;
199 }
200 
201 
202 /*
203  * evt_start_event()
204  *
205  * Change an event from WAITING to RUNNING.
206  */
207 static void
evt_start_event(sc_gameref_t game,sc_int event)208 evt_start_event (sc_gameref_t game, sc_int event)
209 {
210   const sc_filterref_t filter = gs_get_filter (game);
211   const sc_prop_setref_t bundle = gs_get_bundle (game);
212   sc_vartype_t vt_key[4];
213   sc_int time1, time2, obj1, obj1dest;
214 
215   if (evt_trace)
216     sc_trace ("Event: starting event %ld\n", event);
217 
218   /* If event is visible, print its start text. */
219   if (evt_can_see_event (game, event))
220     {
221       const sc_char *starttext;
222 
223       /* Get and print start text. */
224       vt_key[0].string = "Events";
225       vt_key[1].integer = event;
226       vt_key[2].string = "StartText";
227       starttext = prop_get_string (bundle, "S<-sis", vt_key);
228       if (!sc_strempty (starttext))
229         {
230           pf_buffer_string (filter, starttext);
231           pf_buffer_character (filter, '\n');
232         }
233 
234       /* Handle any associated resource. */
235       vt_key[2].string = "Res";
236       vt_key[3].integer = 0;
237       res_handle_resource (game, "sisi", vt_key);
238     }
239 
240   /* Move event object to destination. */
241   vt_key[0].string = "Events";
242   vt_key[1].integer = event;
243   vt_key[2].string = "Obj1";
244   obj1 = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
245   vt_key[2].string = "Obj1Dest";
246   obj1dest = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
247   evt_move_object (game, obj1, obj1dest);
248 
249   /* Set the event's state and time. */
250   gs_set_event_state (game, event, ES_RUNNING);
251 
252   vt_key[2].string = "Time1";
253   time1 = prop_get_integer (bundle, "I<-sis", vt_key);
254   vt_key[2].string = "Time2";
255   time2 = prop_get_integer (bundle, "I<-sis", vt_key);
256   gs_set_event_time (game, event, sc_randomint (time1, time2));
257 
258   if (evt_trace)
259     sc_trace ("Event: start event handling done, %ld\n", event);
260 }
261 
262 
263 /*
264  * evt_get_starter_type()
265  *
266  * Return the starter type for an event.
267  */
268 static sc_int
evt_get_starter_type(sc_gameref_t game,sc_int event)269 evt_get_starter_type (sc_gameref_t game, sc_int event)
270 {
271   const sc_prop_setref_t bundle = gs_get_bundle (game);
272   sc_vartype_t vt_key[3];
273   sc_int startertype;
274 
275   vt_key[0].string = "Events";
276   vt_key[1].integer = event;
277   vt_key[2].string = "StarterType";
278   startertype = prop_get_integer (bundle, "I<-sis", vt_key);
279 
280   return startertype;
281 }
282 
283 
284 /*
285  * evt_finish_event()
286  *
287  * Move an event to FINISHED, or restart it.
288  */
289 static void
evt_finish_event(sc_gameref_t game,sc_int event)290 evt_finish_event (sc_gameref_t game, sc_int event)
291 {
292   const sc_filterref_t filter = gs_get_filter (game);
293   const sc_prop_setref_t bundle = gs_get_bundle (game);
294   sc_vartype_t vt_key[4];
295   sc_int obj2, obj2dest, obj3, obj3dest;
296   sc_int task, startertype, restarttype;
297   sc_bool taskdir;
298 
299   if (evt_trace)
300     sc_trace ("Event: finishing event %ld\n", event);
301 
302   /* Set up invariant parts of the key. */
303   vt_key[0].string = "Events";
304   vt_key[1].integer = event;
305 
306   /* If event is visible, print its finish text. */
307   if (evt_can_see_event (game, event))
308     {
309       const sc_char *finishtext;
310 
311       /* Get and print finish text. */
312       vt_key[2].string = "FinishText";
313       finishtext = prop_get_string (bundle, "S<-sis", vt_key);
314       if (!sc_strempty (finishtext))
315         {
316           pf_buffer_string (filter, finishtext);
317           pf_buffer_character (filter, '\n');
318         }
319 
320       /* Handle any associated resource. */
321       vt_key[2].string = "Res";
322       vt_key[3].integer = 4;
323       res_handle_resource (game, "sisi", vt_key);
324     }
325 
326   /* Move event objects to destination. */
327   vt_key[2].string = "Obj2";
328   obj2 = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
329   vt_key[2].string = "Obj2Dest";
330   obj2dest = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
331   evt_move_object (game, obj2, obj2dest);
332 
333   vt_key[2].string = "Obj3";
334   obj3 = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
335   vt_key[2].string = "Obj3Dest";
336   obj3dest = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
337   evt_move_object (game, obj3, obj3dest);
338 
339   /* See if there is an affected task. */
340   vt_key[2].string = "TaskAffected";
341   task = prop_get_integer (bundle, "I<-sis", vt_key) - 1;
342   if (task >= 0)
343     {
344       vt_key[2].string = "TaskFinished";
345       taskdir = !prop_get_boolean (bundle, "B<-sis", vt_key);
346       if (task_can_run_task_directional (game, task, taskdir))
347         {
348           if (evt_trace)
349             {
350               sc_trace ("Event: event running task %ld, %s\n",
351                         task, taskdir ? "forwards" : "backwards");
352             }
353 
354           task_run_task (game, task, taskdir);
355         }
356       else
357         {
358           if (evt_trace)
359             sc_trace ("Event: event can't run task %ld\n", task);
360         }
361     }
362 
363   /* Handle possible restart. */
364   vt_key[2].string = "RestartType";
365   restarttype = prop_get_integer (bundle, "I<-sis", vt_key);
366   switch (restarttype)
367     {
368     case 0:                    /* Don't restart. */
369       startertype = evt_get_starter_type (game, event);
370       switch (startertype)
371         {
372         case 1:                /* Immediate. */
373         case 2:                /* Random delay. */
374         case 3:                /* After task. */
375           gs_set_event_state (game, event, ES_FINISHED);
376           gs_set_event_time (game, event, 0);
377           break;
378 
379         default:
380           sc_fatal ("evt_finish_event:"
381                     " unknown value for starter type, %ld\n", startertype);
382         }
383       break;
384 
385     case 1:                    /* Restart immediately. */
386       if (evt_fixup_v390_v380_immediate_restart (game, event))
387         break;
388       else
389         evt_start_event (game, event);
390       break;
391 
392     case 2:                    /* Restart after delay. */
393       startertype = evt_get_starter_type (game, event);
394       switch (startertype)
395         {
396         case 1:                /* Immediate. */
397           if (evt_fixup_v390_v380_immediate_restart (game, event))
398             break;
399           else
400             evt_start_event (game, event);
401           break;
402 
403         case 2:                /* Random delay. */
404           {
405             sc_int start, end;
406 
407             gs_set_event_state (game, event, ES_WAITING);
408             vt_key[2].string = "StartTime";
409             start = prop_get_integer (bundle, "I<-sis", vt_key);
410             vt_key[2].string = "EndTime";
411             end = prop_get_integer (bundle, "I<-sis", vt_key);
412             gs_set_event_time (game, event, sc_randomint (start, end));
413             break;
414           }
415 
416         case 3:                /* After task. */
417           gs_set_event_state (game, event, ES_AWAITING);
418           gs_set_event_time (game, event, 0);
419           break;
420 
421         default:
422           sc_fatal ("evt_finish_event: unknown StarterType\n");
423         }
424       break;
425 
426     default:
427       sc_fatal ("evt_finish_event: unknown RestartType\n");
428     }
429 
430   if (evt_trace)
431     sc_trace ("Event: finish event handling done, %ld\n", event);
432 }
433 
434 
435 /*
436  * evt_has_starter_task()
437  * evt_starter_task_is_complete()
438  * evt_pauser_task_is_complete()
439  * evt_resumer_task_is_complete()
440  *
441  * Return the status of start, pause and resume states of an event.
442  */
443 static sc_bool
evt_has_starter_task(sc_gameref_t game,sc_int event)444 evt_has_starter_task (sc_gameref_t game, sc_int event)
445 {
446   sc_int startertype;
447 
448   startertype = evt_get_starter_type (game, event);
449   return startertype == 3;
450 }
451 
452 static sc_bool
evt_starter_task_is_complete(sc_gameref_t game,sc_int event)453 evt_starter_task_is_complete (sc_gameref_t game, sc_int event)
454 {
455   const sc_prop_setref_t bundle = gs_get_bundle (game);
456   sc_vartype_t vt_key[3];
457   sc_int task;
458   sc_bool start;
459 
460   vt_key[0].string = "Events";
461   vt_key[1].integer = event;
462   vt_key[2].string = "TaskNum";
463   task = prop_get_integer (bundle, "I<-sis", vt_key);
464 
465   start = FALSE;
466   if (task == 0)
467     {
468       if (evt_any_task_in_state (game, TRUE))
469         start = TRUE;
470     }
471   else if (task > 0)
472     {
473       if (gs_task_done (game, task - 1))
474         start = TRUE;
475     }
476 
477   return start;
478 }
479 
480 static sc_bool
evt_pauser_task_is_complete(sc_gameref_t game,sc_int event)481 evt_pauser_task_is_complete (sc_gameref_t game, sc_int event)
482 {
483   const sc_prop_setref_t bundle = gs_get_bundle (game);
484   sc_vartype_t vt_key[3];
485   sc_int pausetask;
486   sc_bool completed, pause;
487 
488   vt_key[0].string = "Events";
489   vt_key[1].integer = event;
490 
491   vt_key[2].string = "PauseTask";
492   pausetask = prop_get_integer (bundle, "I<-sis", vt_key);
493   vt_key[2].string = "PauserCompleted";
494   completed = !prop_get_boolean (bundle, "B<-sis", vt_key);
495 
496   pause = FALSE;
497   if (pausetask == 1)
498     {
499       if (evt_any_task_in_state (game, completed))
500         pause = TRUE;
501     }
502   else if (pausetask > 1)
503     {
504       if (completed == gs_task_done (game, pausetask - 2))
505         pause = TRUE;
506     }
507 
508   return pause;
509 }
510 
511 static sc_bool
evt_resumer_task_is_complete(sc_gameref_t game,sc_int event)512 evt_resumer_task_is_complete (sc_gameref_t game, sc_int event)
513 {
514   const sc_prop_setref_t bundle = gs_get_bundle (game);
515   sc_vartype_t vt_key[3];
516   sc_int resumetask;
517   sc_bool completed, resume;
518 
519   vt_key[0].string = "Events";
520   vt_key[1].integer = event;
521 
522   vt_key[2].string = "ResumeTask";
523   resumetask = prop_get_integer (bundle, "I<-sis", vt_key);
524   vt_key[2].string = "ResumerCompleted";
525   completed = !prop_get_boolean (bundle, "B<-sis", vt_key);
526 
527   resume = FALSE;
528   if (resumetask == 1)
529     {
530       if (evt_any_task_in_state (game, completed))
531         resume = TRUE;
532     }
533   else if (resumetask > 1)
534     {
535       if (completed == gs_task_done (game, resumetask - 2))
536         resume = TRUE;
537     }
538 
539   return resume;
540 }
541 
542 
543 /*
544  * evt_handle_preftime_notifications()
545  *
546  * Print messages and handle resources for the event where we're in mid-event
547  * and getting close to some number of turns from its end.
548  */
549 static void
evt_handle_preftime_notifications(sc_gameref_t game,sc_int event)550 evt_handle_preftime_notifications (sc_gameref_t game, sc_int event)
551 {
552   const sc_filterref_t filter = gs_get_filter (game);
553   const sc_prop_setref_t bundle = gs_get_bundle (game);
554   sc_vartype_t vt_key[4];
555   sc_int preftime1, preftime2;
556   const sc_char *preftext;
557 
558   vt_key[0].string = "Events";
559   vt_key[1].integer = event;
560 
561   vt_key[2].string = "PrefTime1";
562   preftime1 = prop_get_integer (bundle, "I<-sis", vt_key);
563   if (preftime1 == gs_event_time (game, event))
564     {
565       vt_key[2].string = "PrefText1";
566       preftext = prop_get_string (bundle, "S<-sis", vt_key);
567       if (!sc_strempty (preftext))
568         {
569           pf_buffer_string (filter, preftext);
570           pf_buffer_character (filter, '\n');
571         }
572 
573       vt_key[2].string = "Res";
574       vt_key[3].integer = 2;
575       res_handle_resource (game, "sisi", vt_key);
576     }
577 
578   vt_key[2].string = "PrefTime2";
579   preftime2 = prop_get_integer (bundle, "I<-sis", vt_key);
580   if (preftime2 == gs_event_time (game, event))
581     {
582       vt_key[2].string = "PrefText2";
583       preftext = prop_get_string (bundle, "S<-sis", vt_key);
584       if (!sc_strempty (preftext))
585         {
586           pf_buffer_string (filter, preftext);
587           pf_buffer_character (filter, '\n');
588         }
589 
590       vt_key[2].string = "Res";
591       vt_key[3].integer = 3;
592       res_handle_resource (game, "sisi", vt_key);
593     }
594 }
595 
596 
597 /*
598  * evt_tick_event()
599  *
600  * Attempt to advance an event by one turn.
601  */
602 static void
evt_tick_event(sc_gameref_t game,sc_int event)603 evt_tick_event (sc_gameref_t game, sc_int event)
604 {
605   if (evt_trace)
606     {
607       sc_trace ("Event: ticking event %ld: state %ld, time %ld\n", event,
608                 gs_event_state (game, event), gs_event_time (game, event));
609     }
610 
611   /* Handle call based on current event state. */
612   switch (gs_event_state (game, event))
613     {
614     case ES_WAITING:
615       {
616         if (evt_trace)
617           sc_trace ("Event: ticking waiting event %ld\n", event);
618 
619         /*
620          * Because we also tick an event that goes from waiting to running,
621          * events started here will tick through RUNNING too, and have their
622          * time decremented.  To get around this, so that the timer for one-
623          * shot events doesn't look one lower than it should after this
624          * transition, we need to set the initial time for events that start
625          * as soon as the game starts to one greater than that set by
626          * evt_start_time().  Here's the hack to do that; if the event starts
627          * immediately, its time will already be zero, even before decrement,
628          * which is how we tell which events to apply this hack to.
629          *
630          * TODO This seems to work, but also seems very dodgy.
631          */
632         if (gs_event_time (game, event) == 0)
633           {
634             evt_start_event (game, event);
635 
636             /* If the event time was set to zero, finish immediately. */
637             if (gs_event_time (game, event) <= 0)
638               evt_finish_event (game, event);
639             else
640               gs_set_event_time (game, event, gs_event_time (game, event) + 1);
641             break;
642           }
643 
644         /*
645          * Decrement the event's time, and if it goes to zero, start running
646          * the event.
647          */
648         gs_decrement_event_time (game, event);
649 
650         if (gs_event_time (game, event) <= 0)
651           {
652             evt_start_event (game, event);
653 
654             /* If the event time was set to zero, finish immediately. */
655             if (gs_event_time (game, event) <= 0)
656               evt_finish_event (game, event);
657           }
658       }
659       break;
660 
661     case ES_RUNNING:
662       {
663         if (evt_trace)
664           sc_trace ("Event: ticking running event %ld\n", event);
665 
666         /*
667          * Re-check the starter task; if it's no longer completed, we need
668          * to set the event back to waiting on task.
669          */
670         if (evt_has_starter_task (game, event))
671           {
672             if (!evt_starter_task_is_complete (game, event))
673               {
674                 if (evt_trace)
675                   sc_trace ("Event: starter task not complete\n");
676 
677                 gs_set_event_state (game, event, ES_AWAITING);
678                 gs_set_event_time (game, event, 0);
679                 break;
680               }
681           }
682 
683         /* If the pauser has completed, but resumer not, pause this event. */
684         if (evt_pauser_task_is_complete (game, event)
685             && !evt_resumer_task_is_complete (game, event))
686           {
687             if (evt_trace)
688               sc_trace ("Event: pause complete\n");
689 
690             gs_set_event_state (game, event, ES_PAUSED);
691             break;
692           }
693 
694         /*
695          * Decrement the event's time, and print any notifications for a set
696          * number of turns from the event end.
697          */
698         gs_decrement_event_time (game, event);
699 
700         if (evt_can_see_event (game, event))
701           evt_handle_preftime_notifications (game, event);
702 
703         /* If the time goes to zero, finish running the event. */
704         if (gs_event_time (game, event) <= 0)
705           evt_finish_event (game, event);
706       }
707       break;
708 
709     case ES_AWAITING:
710       {
711         if (evt_trace)
712           sc_trace ("Event: ticking awaiting event %ld\n", event);
713 
714         /*
715          * Check the starter task.  If it's completed, start running the
716          * event.
717          */
718         if (evt_starter_task_is_complete (game, event))
719           {
720             evt_start_event (game, event);
721 
722             /* If the event time was set to zero, finish immediately. */
723             if (gs_event_time (game, event) <= 0)
724               evt_finish_event (game, event);
725             else
726               {
727                 /*
728                  * If the pauser has completed, but resumer not, immediately
729                  * also pause this event.
730                  */
731                 if (evt_pauser_task_is_complete (game, event)
732                     && !evt_resumer_task_is_complete (game, event))
733                   {
734                     if (evt_trace)
735                       sc_trace ("Event: pause complete, immediate pause\n");
736 
737                     gs_set_event_state (game, event, ES_PAUSED);
738                   }
739               }
740           }
741       }
742       break;
743 
744     case ES_FINISHED:
745       {
746         if (evt_trace)
747           sc_trace ("Event: ticking finished event %ld\n", event);
748 
749         /*
750          * Check the starter task; if it's not completed, we need to set the
751          * event back to waiting on task.
752          *
753          * A completed event needs to go back to waiting on its task, but we
754          * don't want to set it there as soon as the event finishes.  We need
755          * to wait for the starter task to first become undone, otherwise the
756          * event just cycles endlessly, and they don't in Adrift itself.  Here
757          * is where we wait for starter tasks to become undone.
758          */
759         if (evt_has_starter_task (game, event))
760           {
761             if (!evt_starter_task_is_complete (game, event))
762               {
763                 if (evt_trace)
764                   sc_trace ("Event: starter task not complete\n");
765 
766                 gs_set_event_state (game, event, ES_AWAITING);
767                 gs_set_event_time (game, event, 0);
768                 break;
769               }
770           }
771       }
772       break;
773 
774     case ES_PAUSED:
775       {
776         if (evt_trace)
777           sc_trace ("Event: ticking paused event %ld\n", event);
778 
779         /* If the resumer has completed, resume this event. */
780         if (evt_resumer_task_is_complete (game, event))
781           {
782             if (evt_trace)
783               sc_trace ("Event: resume complete\n");
784 
785             gs_set_event_state (game, event, ES_RUNNING);
786             break;
787           }
788       }
789       break;
790 
791     default:
792       sc_fatal ("evt_tick: invalid event state\n");
793     }
794 
795   if (evt_trace)
796     {
797       sc_trace ("Event: after ticking event %ld: state %ld, time %ld\n", event,
798                 gs_event_state (game, event), gs_event_time (game, event));
799     }
800 }
801 
802 
803 /*
804  * evt_tick_events()
805  *
806  * Attempt to advance each event by one turn.
807  */
808 void
evt_tick_events(sc_gameref_t game)809 evt_tick_events (sc_gameref_t game)
810 {
811   sc_int event;
812 
813   /*
814    * Tick all events.  If an event transitions into a running state from a
815    * paused or waiting state, tick that event again.
816    */
817   for (event = 0; event < gs_event_count (game); event++)
818     {
819       sc_int prior_state, state;
820 
821       /* Note current state, and tick event forwards. */
822       prior_state = gs_event_state (game, event);
823       evt_tick_event (game, event);
824 
825       /*
826        * If the event went from paused or waiting to running, tick again.
827        * This looks dodgy, and probably is, but it does keep timers correct
828        * by only re-ticking events that have transitioned from non-running
829        * states to a running one, and not already-running events.  This is
830        * in effect just adding a bit of turn processing to a tick that would
831        * otherwise change state alone; a bit of laziness, in other words.
832        */
833       state = gs_event_state (game, event);
834       if (state == ES_RUNNING
835           && (prior_state == ES_PAUSED || prior_state == ES_WAITING))
836         evt_tick_event (game, event);
837     }
838 }
839 
840 
841 /*
842  * evt_debug_trace()
843  *
844  * Set event tracing on/off.
845  */
846 void
evt_debug_trace(sc_bool flag)847 evt_debug_trace (sc_bool flag)
848 {
849   evt_trace = flag;
850 }
851