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