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(&center, 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