1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/core/game.h"
25 #include "ultima/nuvie/actors/actor.h"
26 #include "ultima/nuvie/gui/widgets/map_window.h"
27 #include "ultima/nuvie/core/map.h"
28 #include "ultima/nuvie/core/party.h"
29 #include "ultima/nuvie/core/events.h"
30 #include "ultima/nuvie/usecode/usecode.h"
31 #include "ultima/nuvie/core/u6_objects.h"
32 #include "ultima/nuvie/actors/u6_work_types.h"
33 #include "ultima/nuvie/misc/u6_llist.h"
34 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
35 #include "ultima/nuvie/core/game_clock.h"
36 #include "ultima/nuvie/gui/widgets/command_bar.h"
37 #include "ultima/nuvie/views/view_manager.h"
38 #include "ultima/nuvie/views/party_view.h"
39 #include "ultima/nuvie/actors/actor_manager.h"
40 // FIXME: effects use timers, not the other way around (make a movement effect?)
41 #include "ultima/nuvie/core/effect_manager.h"
42 #include "ultima/nuvie/core/effect.h"
43
44 #include "ultima/nuvie/core/timed_event.h"
45
46 namespace Ultima {
47 namespace Nuvie {
48
49 #define MESG_TIMED CB_TIMED
50
51 /* Activate all events for the current time, deleting those that have fired
52 * and are of no more use. Repeated timers are requeued.
53 */
call_timers(uint32 now)54 void TimeQueue::call_timers(uint32 now) {
55 while (!empty() && call_timer(now)) {
56
57 }
58 }
59
clear()60 void TimeQueue::clear() {
61 while (!empty()) {
62 TimedEvent *event = pop_timer();
63 if (event->tq_can_delete)
64 delete event;
65 }
66 }
67
68 /* Add new timed event to queue, which will activate `event' when time is
69 * `evtime'.
70 */
add_timer(TimedEvent * tevent)71 void TimeQueue::add_timer(TimedEvent *tevent) {
72 Std::list<TimedEvent *>::iterator t;
73 if (tq.empty()) {
74 tq.push_front(tevent);
75 return;
76 }
77 // in case it's already queued, remove the earlier instance(s)
78 remove_timer(tevent);
79 // add after events with earlier/equal time
80 t = tq.begin();
81 while (t != tq.end() && (*t)->time <= tevent->time) t++;
82 tq.insert(t, tevent);
83 }
84
85
86 /* Remove timed event from queue.
87 */
remove_timer(TimedEvent * tevent)88 void TimeQueue::remove_timer(TimedEvent *tevent) {
89 Std::list<TimedEvent *>::iterator t;
90 t = tq.begin();
91 while (t != tq.end()) {
92 if (*t == tevent) {
93 t = tq.erase(t);
94 } else ++t; // this deletes all duplicates
95 }
96 }
97
98
99 /* Remove and return timed event at front of queue, or NULL if empty.
100 */
pop_timer()101 TimedEvent *TimeQueue::pop_timer() {
102 TimedEvent *first = NULL;
103 if (!empty()) {
104 first = tq.front();
105 tq.pop_front(); // remove it
106 }
107 return (first);
108 }
109
110
111 /* Call timed event at front of queue, whose time is <= `now'.
112 * Returns true if an event handler was called. (false if time isn't up yet)
113 */
call_timer(uint32 now)114 bool TimeQueue::call_timer(uint32 now) {
115 if (empty())
116 return (false);
117 TimedEvent *tevent = tq.front();
118 if (tevent->defunct) {
119 assert(pop_timer() == tevent);
120 delete_timer(tevent);
121 return (false);
122 }
123 if (tevent->time > now)
124 return (false);
125
126 //dequeue event here
127 pop_timer(); // remove timer in case we have recursion in the timed() call.
128
129 tevent->timed(now); // fire
130
131 //re-queue if repeating timer.
132
133 if (tevent->repeat_count != 0) { // repeat! same delay, add time
134 // use updated time so it isn't repeated too soon
135 tevent->set_time();
136 // tevent->time = _clock->get_ticks() + tevent->delay;
137 // tevent->time = now + tevent->delay;
138 add_timer(tevent);
139 if (tevent->repeat_count > 0) // don't reduce count if infinite (-1)
140 --tevent->repeat_count;
141 } else
142 delete_timer(tevent); // if not repeated, safe to delete
143
144 return (true);
145 }
146
147
148 /* Delete a timer, if its can_delete flag is true. Remove from the queue first!
149 */
delete_timer(TimedEvent * tevent)150 bool TimeQueue::delete_timer(TimedEvent *tevent) {
151 if (tevent->tq_can_delete) {
152 delete tevent;
153 return true;
154 }
155
156 return false;
157 }
158
159
160 /* Accepts delay, immediate toggle(false), and realtime switch(true). It must
161 * be queued afterwards to start.
162 */
TimedEvent(uint32 reltime,bool immediate,bool realtime)163 TimedEvent::TimedEvent(uint32 reltime, bool immediate, bool realtime)
164 : delay(reltime), repeat_count(0), ignore_pause(false),
165 real_time(realtime), tq_can_delete(true), defunct(false) {
166 tq = NULL;
167
168 if (immediate) // start now (useful if repeat is true)
169 time = 0;
170 else
171 set_time();
172 }
173
174
175 /* Add myself to the TimeQueue.
176 */
queue()177 void TimedEvent::queue() {
178 Events *event = Game::get_game()->get_event();
179 if (tq == NULL) {
180 if (real_time)
181 tq = event->get_time_queue();
182 else
183 tq = event->get_game_time_queue();
184 tq->add_timer(this);
185 }
186 }
187
188
189 /* Remove myself from the TimeQueue.
190 */
dequeue()191 void TimedEvent::dequeue() {
192 if (tq) {
193 tq->remove_timer(this);
194 tq = NULL;
195 }
196 }
197
198
199 /* Add delay to current time and set absolute time.
200 */
set_time()201 void TimedEvent::set_time() {
202 GameClock *_clock = Game::get_game()->get_clock();
203 time = delay + (real_time ? _clock->get_ticks()
204 : _clock->get_game_ticks());
205
206 }
207
208
209 /*** TimedPartyMove ***/
210
211 /* Party movement to/from dungeon or moongate, with a certain number of
212 * milliseconds between each step.
213 */
TimedPartyMove(MapCoord * d,MapCoord * t,uint32 step_delay)214 TimedPartyMove::TimedPartyMove(MapCoord *d, MapCoord *t, uint32 step_delay)
215 : TimedEvent(step_delay, true) {
216 init(d, t, NULL);
217 }
218
219 /* Movement through temporary moongate.
220 */
TimedPartyMove(MapCoord * d,MapCoord * t,Obj * use_obj,uint32 step_delay)221 TimedPartyMove::TimedPartyMove(MapCoord *d, MapCoord *t, Obj *use_obj, uint32 step_delay)
222 : TimedEvent(step_delay, true) {
223 init(d, t, use_obj);
224 }
225
TimedPartyMove(uint32 step_delay)226 TimedPartyMove::TimedPartyMove(uint32 step_delay)
227 : TimedEvent(step_delay, true) {
228 map_window = NULL;
229 party = NULL;
230 dest = NULL;
231 target = NULL;
232 moongate = NULL;
233 actor_to_hide = NULL;
234 moves_left = 0;
235 wait_for_effect = 0;
236 falling_in = false;
237 }
238
~TimedPartyMove()239 TimedPartyMove::~TimedPartyMove() {
240 delete dest;
241 delete target;
242 }
243
244 /* Set destination.
245 */
init(MapCoord * d,MapCoord * t,Obj * use_obj)246 void TimedPartyMove::init(MapCoord *d, MapCoord *t, Obj *use_obj) {
247 map_window = Game::get_game()->get_map_window();
248 party = Game::get_game()->get_party();
249 target = NULL;
250 moves_left = party->get_party_size() * 2; // step timeout
251 wait_for_effect = 0;
252 actor_to_hide = NULL;
253 falling_in = false;
254
255 dest = new MapCoord(*d);
256 if (t)
257 target = new MapCoord(*t);
258 moongate = use_obj;
259
260 queue(); // start
261 }
262
263 /* Party movement to/from dungeon or to moongate. Repeated until everyone has
264 * entered, then the entire party is moved to the destination, and this waits
265 * until the visual effects complete.
266 */
timed(uint32 evtime)267 void TimedPartyMove::timed(uint32 evtime) {
268 if (wait_for_effect != 0) { // ignores "falling_in"
269 repeat(); // repeat once more (callback() must call stop())
270 return;
271 }
272 stop(); // cancelled further down with repeat(), if still moving
273
274 if (moves_left) {
275 if (((falling_in == false) && move_party())
276 || ((falling_in == true) && fall_in()))
277 repeat(); // still moving
278 } else // timed out, make sure nobody is walking
279 for (uint32 m = 0; m < party->get_party_size(); m++)
280 party->get_actor(m)->delete_pathfinder();
281
282 // NOTE: set by repeat() or stop()
283 if (repeat_count == 0) { // everyone is in position
284 if (falling_in == false) { // change location, get in formation
285 change_location(); // fade map, move and show party
286 party->stop_walking(true); // return control (and change viewpoint)
287
288 // wait for effect or line up now; Party called unpause_user()
289 Game::get_game()->pause_user();
290 if (wait_for_effect == 0) {
291 delay = 50;
292 set_time(); // fall-in as fast as possible (but visibly)
293 moves_left = party->get_party_size() - 1; // followers
294 falling_in = true;
295 }
296 repeat(); // don't stop yet!
297 } else { // already changed location
298 Game::get_game()->unpause_user();
299 stop_timer(); // done
300 }
301 }
302 if (moves_left > 0)
303 --moves_left;
304 }
305
306 /* Assume the teleport-effect is complete. (don't bother checking msg)
307 */
callback(uint16 msg,CallBack * caller,void * data)308 uint16 TimedPartyMove::callback(uint16 msg, CallBack *caller, void *data) {
309 if (wait_for_effect == 1) { // map-change
310 wait_for_effect = 0;
311 Game::get_game()->unpause_anims();
312
313 delay = 50;
314 set_time(); // fall-in as fast as possible (but visibly)
315 moves_left = party->get_party_size() - 1; // followers
316 falling_in = true;
317 } else if (wait_for_effect == 2) { // vanish
318 wait_for_effect = 0;
319 Game::get_game()->unpause_anims();
320 // move_party();
321 }
322 return (0);
323 }
324
325 /* Returns true if people are still walking.
326 */
move_party()327 bool TimedPartyMove::move_party() {
328 bool moving = false; // moving or waiting
329 Actor *used_gate = NULL; // someone just stepped into the gate (for effect)
330
331 if (actor_to_hide) {
332 hide_actor(actor_to_hide);
333 moving = true; // allow at least one more tick so we see last actor hide
334 }
335 actor_to_hide = NULL;
336
337 for (uint32 a = 0; a < party->get_party_size(); a++) {
338 Actor *person = party->get_actor(a);
339
340 if (person->is_visible()) {
341 MapCoord loc(person->get_location());
342 bool really_visible = map_window->in_window(loc.x, loc.y, loc.z);
343
344 if (a == 0) // FIXME: should be done automatically, but world is frozen
345 map_window->centerMapOnActor(person);
346
347 if (loc != *dest && really_visible) {
348 // nobody has just used the gate (who may still be vanishing)
349 if (!used_gate || loc.distance(*dest) > 1) { // or we aren't close to gate yet
350 if (!person->get_pathfinder())
351 person->pathfind_to(*dest); // start (or continue) going to gate
352 person->update(); // ActorManager is paused
353 loc = person->get_location(); // don't use the old location
354 } else
355 person->delete_pathfinder(); // wait for whoever is at gate
356 }
357 if (loc == *dest // actor may have just arrived this turn
358 || !really_visible) {
359 person->delete_pathfinder();
360 if (moongate) used_gate = person; // hide after this turn
361 else if (!actor_to_hide) actor_to_hide = person; // hide before next turn
362 }
363 moving = true; // even if at gate, might not be hidden yet
364 }
365 }
366
367 if (used_gate) // wait until now (instead of in loop) so others can catch up before effect
368 hide_actor(used_gate);
369 return (moving);
370 }
371
372 /* Start a visual effect and hide the party member.
373 */
hide_actor(Actor * person)374 void TimedPartyMove::hide_actor(Actor *person) {
375 EffectManager *effect_mgr = Game::get_game()->get_effect_manager();
376 if (wait_for_effect != 2) {
377 if (moongate) { // vanish
378 effect_mgr->watch_effect(this, new VanishEffect()); // wait for callback
379 wait_for_effect = 2;
380 delay = 1;
381 set_time(); // effect will be longer than original delay
382 }
383 person->hide();
384 person->move(target->x, target->y, target->z);
385 }
386 }
387
388 /* Start a visual effect and move the party to the target.
389 */
change_location()390 void TimedPartyMove::change_location() {
391 EffectManager *effect_mgr = Game::get_game()->get_effect_manager();
392 Graphics::ManagedSurface *mapwindow_capture = NULL;
393 if (wait_for_effect != 1) {
394 bool is_moongate = moongate != NULL;
395 if (moongate && moongate->obj_n == OBJ_U6_RED_GATE) { // leave blue moongates
396 // get image before deleting moongate
397 mapwindow_capture = map_window->get_sdl_surface();
398 // must delete moongate here because dest may be the same as target...
399 // remove moongate before moving so the tempobj cleanup doesn't bite us
400 Game::get_game()->get_obj_manager()->remove_obj_from_map(moongate);
401 delete_obj(moongate);
402 }
403
404 if (is_moongate)
405 Game::get_game()->get_player()->move(target->x, target->y, target->z, true);
406 else
407 party->move(target->x, target->y, target->z);
408 party->show(); // unhide everyone
409
410 Game::get_game()->get_view_manager()->update(); // we do this to update party view sun moon display if visible.
411
412 if (mapwindow_capture) { // could check this or moongate again
413 // start fade-to
414 effect_mgr->watch_effect(this, /* call me */
415 new FadeEffect(FADE_PIXELATED, FADE_OUT, mapwindow_capture));
416 SDL_FreeSurface(mapwindow_capture);
417
418 Game::get_game()->pause_anims();
419 wait_for_effect = 1;
420 }
421 }
422 }
423
424
425 /* Pass a few times so everyone in the party can get into formation.
426 * Returns true if party needs to move more to get into formation. */
fall_in()427 bool TimedPartyMove::fall_in() {
428 bool not_in_position = false; // assume false until someone checks true
429 party->follow(0, 0);
430 for (uint8 p = 1; p < party->get_party_size(); p++) {
431 Actor *follower = party->get_actor(p);
432 MapCoord at = follower->get_location(),
433 desired = party->get_formation_coords(p);
434 follower->update();
435 if (at != desired)
436 not_in_position = true;
437 }
438 return (not_in_position);
439 }
440
441
442 /*** TimedPartyMoveToVehicle ***/
443
444
445 /* Party movement to vehicle. Second target is unused.
446 */
TimedPartyMoveToVehicle(MapCoord * d,Obj * obj,uint32 step_delay)447 TimedPartyMoveToVehicle::TimedPartyMoveToVehicle(MapCoord *d, Obj *obj,
448 uint32 step_delay)
449 : TimedPartyMove(d, NULL, step_delay) {
450 ship_obj = obj;
451 }
452
453
454 /* Repeat until everyone is in the boat, then start it up.
455 */
timed(uint32 evtime)456 void TimedPartyMoveToVehicle::timed(uint32 evtime) {
457 stop(); // cancelled further down with repeat(), if still moving
458 for (uint32 a = 0; a < party->get_party_size(); a++) {
459 Actor *person = party->get_actor(a);
460 MapCoord loc(person->get_location());
461 // not at boat location
462 if (loc != *dest) {
463 // offscreen (or timed out), teleport to target
464 MapWindow *mapWindow = Game::get_game()->get_map_window();
465 if (!mapWindow->in_window(loc.x, loc.y, loc.z) || moves_left == 0)
466 person->move(dest->x, dest->y, dest->z, ACTOR_FORCE_MOVE);
467 else // keep walking
468 person->pathfind_to(*dest);
469 person->update();
470 repeat(); // repeat once more
471 } else { // at destination
472 person->delete_pathfinder();
473 person->hide(); // set in-vehicle
474 }
475 }
476
477 if (repeat_count == 0) { // everyone is in the boat
478 Game::get_game()->get_usecode()->use_obj(ship_obj);
479 party->stop_walking(false); // return control to player
480 }
481 if (moves_left > 0)
482 --moves_left;
483 }
484
485
486 /* Dump one item at a time out of a container, and print it's name to MsgScroll.
487 */
TimedContainerSearch(Obj * obj)488 TimedContainerSearch::TimedContainerSearch(Obj *obj) : TimedEvent(500, TIMER_DELAYED) {
489 Game *game = Game::get_game();
490 scroll = game->get_scroll();
491 uc = game->get_usecode();
492 om = game->get_obj_manager();
493
494 container_obj = obj;
495 prev_obj = NULL;
496
497 //game->set_pause_flags((GamePauseState)(game->get_pause_flags() | PAUSE_USER));
498 game->pause_user();
499 queue(); // start
500 }
501
502
timed(uint32 evtime)503 void TimedContainerSearch::timed(uint32 evtime) {
504 prev_obj = uc->get_obj_from_container(container_obj);
505 if (prev_obj) {
506 scroll->display_string(om->look_obj(prev_obj, true));
507 if (container_obj->container->end()) // more objects left
508 scroll->display_string(container_obj->container->end()->prev
509 ? ", " : ", and ");
510 repeat();
511 } else {
512 Game::get_game()->unpause_user();
513 stop();
514 }
515 }
516
517
518
519 /*** TimedCallback ***/
TimedCallback(CallBack * t,void * d,uint32 wait_time,bool repeat)520 TimedCallback::TimedCallback(CallBack *t, void *d, uint32 wait_time, bool repeat)
521 : TimedEvent(wait_time, TIMER_DELAYED, TIMER_REALTIME) {
522 set_target(t);
523 set_user_data(d);
524 repeat_count = repeat ? -1 : 0;
525
526 queue(); // start
527 }
528
529
timed(uint32 evtime)530 void TimedCallback::timed(uint32 evtime) {
531 if (callback_target)
532 message(MESG_TIMED, &evtime);
533 else
534 stop();
535 }
536
537
GameTimedCallback(CallBack * t,void * d,uint32 wait_time,bool repeating)538 GameTimedCallback::GameTimedCallback(CallBack *t, void *d, uint32 wait_time, bool repeating)
539 : TimedCallback(t, d, wait_time, repeating) {
540 // re-queue timer using game ticks
541 dequeue();
542 real_time = TIMER_GAMETIME;
543 set_time();// change to game time
544 queue(); // start
545 }
546
547
548
549 /*** TimedAdvance: Advance game time by rate until hours has passed. **/
550 #define TIMEADVANCE_PER_SECOND 1000 /* frequency of timer calls */
TimedAdvance(uint8 hours,uint16 r)551 TimedAdvance::TimedAdvance(uint8 hours, uint16 r)
552 : TimedCallback(NULL, NULL, 1000 / TIMEADVANCE_PER_SECOND, true),
553 _clock(Game::get_game()->get_clock()),
554 minutes_this_hour(0), minutes(0) {
555 init(hours * 60, r);
556 }
557
558
559 /* Advance to time indicated by timestring, of the format "HH:MM".
560 */
TimedAdvance(Std::string timestring,uint16 r)561 TimedAdvance::TimedAdvance(Std::string timestring, uint16 r)
562 : TimedCallback(NULL, NULL, 1000 / TIMEADVANCE_PER_SECOND, true),
563 _clock(Game::get_game()->get_clock()),
564 minutes_this_hour(0), minutes(0) {
565 uint8 hour = 0, minute = 0;
566
567 get_time_from_string(hour, minute, timestring); // set stop time
568
569 // set number of hours and minutes to advance
570 uint16 advance_h = (_clock->get_hour() == hour) ? 24
571 : (_clock->get_hour() < hour) ? (hour - _clock->get_hour())
572 : (24 - (_clock->get_hour() - hour));
573 uint16 advance_m;
574 if (_clock->get_minute() <= minute)
575 advance_m = minute - _clock->get_minute();
576 else {
577 advance_m = (60 - (_clock->get_minute() - minute));
578 if (advance_h > 0)
579 advance_h -= 1;
580 else
581 advance_h = 23;
582 }
583 // go
584 init((advance_h * 60) + advance_m, r);
585 }
586
587 /* Set time advance.
588 */
init(uint16 min,uint16 r)589 void TimedAdvance::init(uint16 min, uint16 r) {
590 advance = min;
591 rate = r;
592 prev_evtime = _clock->get_ticks();
593 DEBUG(0, LEVEL_DEBUGGING, "TimedAdvance(): %02d:%02d + %02d:%02d (rate=%d)\n",
594 _clock->get_hour(), _clock->get_minute(), advance / 60, advance % 60, rate);
595 }
596
597
598 /* Advance game time by rate each second. Timer is stopped after after the time
599 * has been advanced as requested.
600 */
timed(uint32 evtime)601 void TimedAdvance::timed(uint32 evtime) {
602 uint32 milliseconds = (evtime - prev_evtime) > 0 ? (evtime - prev_evtime) : 1;
603 uint32 fraction = 1000 / milliseconds; // % of second
604 uint32 minutes_per_fraction = rate / (fraction > 0 ? fraction : 1);
605 bool hour_passed = false; // another hour has passed
606 prev_evtime = evtime;
607
608 for (uint32 m = 0; m < minutes_per_fraction; m++) {
609 _clock->inc_minute();
610 minutes += 1;
611 if (++minutes_this_hour > 59) {
612 minutes_this_hour = 0;
613 hour_passed = true;
614 }
615 if (time_passed())
616 break;
617 }
618 Game::get_game()->time_changed();
619
620 if (hour_passed && callback_target) // another hour has passed
621 message(MESG_TIMED, &evtime);
622
623 if (time_passed()) {
624 DEBUG(0, LEVEL_DEBUGGING, "~TimedAdvance(): now %02d:%02d\n", _clock->get_hour(), _clock->get_minute());
625 if (callback_target && !hour_passed) // make sure to call target
626 message(MESG_TIMED, &evtime);
627 stop(); // done
628 }
629 }
630
631
632 /* Returns true when the requested amount of time has passed.
633 */
time_passed()634 bool TimedAdvance::time_passed() {
635 return (minutes >= advance);
636 }
637
638
639 /* Set hour and minute from "HH:MM" string.
640 */
get_time_from_string(uint8 & hour,uint8 & minute,Std::string timestring)641 void TimedAdvance::get_time_from_string(uint8 &hour, uint8 &minute, Std::string timestring) {
642 char *hour_s = NULL, *minute_s = NULL;
643 hour_s = scumm_strdup(timestring.c_str());
644 for (uint32 c = 0; c < strlen(hour_s); c++)
645 if (hour_s[c] == ':') { // get minutes
646 minute_s = scumm_strdup(&hour_s[c + 1]);
647 hour_s[c] = '\0';
648 break;
649 }
650
651 if (hour_s) {
652 hour = strtol(hour_s, NULL, 10);
653 free(hour_s);
654 }
655 if (minute_s) {
656 minute = strtol(minute_s, NULL, 10);
657 free(minute_s);
658 }
659 }
660
661
TimedRestGather(uint16 x,uint16 y)662 TimedRestGather::TimedRestGather(uint16 x, uint16 y)
663 : TimedPartyMove(50) {
664 MapCoord center = MapCoord(x, y);
665 init(¢er, 0, 0); // set dest to campfire location
666 Game::get_game()->get_map_window()->updateAmbience();
667 check_campfire();
668 }
669
670 /* Repeat until everyone is in the circle. */
timed(uint32 evtime)671 void TimedRestGather::timed(uint32 evtime) {
672 stop(); // cancelled further down with repeat(), if still moving
673
674 if (moves_left) {
675 if (move_party())
676 repeat(); // still moving
677 } else // timed out, make sure nobody is walking
678 for (uint32 m = 0; m < party->get_party_size(); m++)
679 party->get_actor(m)->delete_pathfinder();
680
681 if (repeat_count == 0) {
682 check_campfire();
683 Game::get_game()->get_event()->rest();
684 }
685
686 if (moves_left > 0)
687 --moves_left;
688 }
689
check_campfire()690 void TimedRestGather::check_campfire() {
691 ActorManager *actor_manager = Game::get_game()->get_actor_manager();
692 for (sint32 a = 0; a < party->get_party_size(); a++) {
693 Actor *actor = party->get_actor(a);
694 MapCoord loc = actor->get_location();
695 if (loc.x == dest->x && loc.y == dest->y) {
696 for (int x = 0; x < 3; x++)
697 for (int y = 0; y < 3; y++) {
698 if (x == 1 && y == 1)
699 continue;
700 if (actor_manager->get_actor(dest->x + x - 1, dest->y + y - 1, loc.z) == NULL) {
701 actor->move(dest->x + x - 1, dest->y + y - 1, loc.z);
702
703 }
704 }
705
706 }
707 actor->face_location(dest->x, dest->y);
708 }
709 }
710
move_party()711 bool TimedRestGather::move_party() {
712 bool moving = false; // moving or waiting
713 const sint16 positions[3 * 3] = {
714 7, 0, 4, // list of party members arranged by location
715 3, -1, 2, // campfire is at positions[1][1]
716 5, 1, 6
717 };
718
719 // check everyone in party because they might not be in the positions list
720 for (sint32 a = 0; a < party->get_party_size(); a++) {
721 for (int x = 0; x < 3; x++)
722 for (int y = 0; y < 3; y++)
723 if (positions[x + y * 3] == a) {
724 Actor *actor = party->get_actor(a);
725 MapCoord loc = actor->get_location();
726 MapCoord actor_dest(dest->x + x - 1, dest->y + y - 1, loc.z);
727 if (actor_dest == loc) {
728 actor->face_location(dest->x, dest->y); // look at camp
729 actor->delete_pathfinder();
730 } else {
731 moving = true; // still moving to circle
732 if (actor->get_pathfinder() == 0)
733 actor->pathfind_to(actor_dest.x, actor_dest.y);
734 actor->set_moves_left(actor->get_dexterity());
735 actor->update(); // ActorManager is paused
736 }
737 x = 3;
738 y = 3;
739 break; // break to first loop
740 }
741 }
742 return moving;
743 }
744
TimedRest(uint8 hours,Actor * who_will_guard,Obj * campfire_obj)745 TimedRest::TimedRest(uint8 hours, Actor *who_will_guard, Obj *campfire_obj)
746 : TimedAdvance(hours, 80), party(Game::get_game()->get_party()),
747 scroll(Game::get_game()->get_scroll()), sleeping(0),
748 print_message(0) {
749 lookout = who_will_guard;
750 campfire = campfire_obj;
751 number_that_had_food = 0;
752 }
753
754 /* This is the only place we know that the TimedAdvance has completed. */
~TimedRest()755 TimedRest::~TimedRest() {
756 //MapCoord loc = Game::get_game()->get_player()->get_actor()->get_location();
757 assert(campfire != 0);
758
759 campfire->frame_n = 0; // extinguish campfire
760
761 bool can_heal = (Game::get_game()->get_clock()->get_rest_counter() == 0); //only heal once every 12 hours.
762
763 for (int s = 0; s < party->get_party_size(); s++) {
764 Actor *actor = party->get_actor(s);
765
766 if (can_heal && actor->is_sleeping() && s < number_that_had_food) {
767 //heal actors.
768 uint8 hp_diff = actor->get_maxhp() - actor->get_hp();
769 if (hp_diff > 0) {
770 if (hp_diff == 1)
771 hp_diff = 2;
772
773 actor->set_hp(actor->get_hp() + NUVIE_RAND() % (hp_diff / 2) + hp_diff / 2);
774 scroll->display_fmt_string("%s has healed.\n", actor->get_name());
775 }
776
777 }
778 party->get_actor(s)->revert_worktype(); // "wake up"
779 }
780
781 if (can_heal)
782 Game::get_game()->get_clock()->set_rest_counter(12); //don't heal by resting for another 12 hours.
783
784 Game::get_game()->get_player()->set_mapwindow_centered(true);
785 Game::get_game()->unpause_user();
786 Game::get_game()->get_event()->endAction(true); // exit Rest mode
787 }
788
timed(uint32 evtime)789 void TimedRest::timed(uint32 evtime) {
790 if (sleeping == false) { // mealtime
791 if (evtime - prev_evtime > 500) { // print the next message
792 prev_evtime = evtime; // normally set by TimedAdvance::timed()
793
794 if (print_message == 0)
795 bard_play(); // Iolo plays a tune.
796 else if (print_message <= party->get_party_size())
797 eat(party->get_actor(print_message - 1)); // print each person's message
798 else {
799 sleeping = true; // finished eating
800 sleep();
801 }
802 ++print_message;
803 }
804 } else { // sleeping
805 TimedAdvance::timed(evtime);
806 for (int s = 0; s < party->get_party_size(); s++)
807 party->get_actor(s)->update_time(); // checks status effects
808
809 // FIXME: chance for random enemies to attack
810 }
811 }
812
813 /* Check if party has any food, and consume it, allowing the actor to heal. */
eat(Actor * actor)814 void TimedRest::eat(Actor *actor) {
815 Obj *food = actor->inventory_get_food(); // search actor's inventory first
816 if (!food)
817 food = party->get_food();
818
819 if (food) {
820 scroll->display_fmt_string("%s has food.\n", actor->get_name());
821 Game::get_game()->get_usecode()->destroy_obj(food, 1);
822 number_that_had_food++;
823 } else
824 scroll->display_fmt_string("%s has no food.\n", actor->get_name());
825 }
826
827 /* Look for a bard in the party and have them play a tune. */
bard_play()828 void TimedRest::bard_play() {
829 scroll->display_string("Mealtime!\n");
830 for (int b = 0; b < party->get_party_size(); b++)
831 if (party->get_actor(b)->get_obj_n() == OBJ_U6_MUSICIAN) {
832 Actor *bard = party->get_actor(b);
833 bard->morph(OBJ_U6_MUSICIAN_PLAYING);
834 scroll->display_fmt_string("%s plays a tune.\n", bard->get_name());
835 break;
836 }
837 }
838
839 /* Start sleeping until the requested time. One person can stand guard. */
sleep()840 void TimedRest::sleep() {
841 // FIXME: changing to SLEEP worktype should automatically do this
842 for (int b = 0; b < party->get_party_size(); b++)
843 if (party->get_actor(b)->get_obj_n() == OBJ_U6_MUSICIAN_PLAYING)
844 party->get_actor(b)->morph(OBJ_U6_MUSICIAN);
845
846 for (int s = 0; s < party->get_party_size(); s++) {
847 Actor *actor = party->get_actor(s);
848 if (actor == lookout) {
849 actor->set_worktype(WORKTYPE_U6_LOOKOUT);
850 scroll->display_fmt_string("\n%s stands guard while the party rests.\n", actor->get_name());
851 } else {
852 actor->set_worktype(WORKTYPE_U6_SLEEP);
853 }
854 }
855 }
856
857 } // End of namespace Nuvie
858 } // End of namespace Ultima
859