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/conf/configuration.h"
25 #include "ultima/nuvie/misc/u6_misc.h"
26 #include "ultima/nuvie/gui/gui.h"
27 #include "ultima/nuvie/gui/widgets/console.h"
28 #include "ultima/nuvie/screen/dither.h"
29 #include "ultima/nuvie/sound/sound_manager.h"
30 #include "ultima/nuvie/actors/actor.h"
31 #include "ultima/nuvie/script/script.h"
32 #include "ultima/nuvie/screen/screen.h"
33 #include "ultima/nuvie/screen/game_palette.h"
34 #include "ultima/nuvie/core/game_clock.h"
35 #include "ultima/nuvie/core/egg_manager.h"
36 #include "ultima/nuvie/core/obj_manager.h"
37 #include "ultima/nuvie/actors/actor_manager.h"
38 #include "ultima/nuvie/core/player.h"
39 #include "ultima/nuvie/core/party.h"
40 #include "ultima/nuvie/core/converse.h"
41 #include "ultima/nuvie/gui/widgets/converse_gump.h"
42 #include "ultima/nuvie/gui/widgets/converse_gump_wou.h"
43 #include "ultima/nuvie/fonts/font_manager.h"
44 #include "ultima/nuvie/views/view_manager.h"
45 #include "ultima/nuvie/core/effect_manager.h"
46
47 #include "ultima/nuvie/core/magic.h"
48 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
49 #include "ultima/nuvie/gui/widgets/msg_scroll_new_ui.h"
50 #include "ultima/nuvie/core/map.h"
51 #include "ultima/nuvie/gui/widgets/map_window.h"
52 #include "ultima/nuvie/core/events.h"
53 #include "ultima/nuvie/portraits/portrait.h"
54 #include "ultima/nuvie/gui/widgets/background.h"
55 #include "ultima/nuvie/gui/widgets/command_bar.h"
56 #include "ultima/nuvie/gui/widgets/command_bar_new_ui.h"
57 #include "ultima/nuvie/views/party_view.h"
58 #include "ultima/nuvie/views/actor_view.h"
59 #include "ultima/nuvie/usecode/usecode.h"
60 #include "ultima/nuvie/usecode/u6_usecode.h"
61 #include "ultima/nuvie/core/cursor.h"
62 #include "ultima/nuvie/core/weather.h"
63 #include "ultima/nuvie/core/book.h"
64 #include "ultima/nuvie/keybinding/keys.h"
65 #include "ultima/nuvie/files/utils.h"
66 #include "ultima/nuvie/core/game.h"
67 #include "ultima/nuvie/nuvie.h"
68
69 #include "common/system.h"
70
71 namespace Ultima {
72 namespace Nuvie {
73
74 Game *Game::game = NULL;
75
Game(Configuration * cfg,Events * evt,Screen * scr,GUI * g,nuvie_game_t type,SoundManager * sm)76 Game::Game(Configuration *cfg, Events *evt, Screen *scr, GUI *g, nuvie_game_t type, SoundManager *sm) {
77 game = this;
78 config = cfg;
79 event = evt;
80
81 gui = g;
82
83 screen = scr;
84 game_type = type;
85 sound_manager = sm;
86
87 script = NULL;
88 background = NULL;
89 cursor = NULL;
90 dither = NULL;
91 tile_manager = NULL;
92 obj_manager = NULL;
93 palette = NULL;
94 font_manager = NULL;
95 scroll = NULL;
96 game_map = NULL;
97 map_window = NULL;
98 actor_manager = NULL;
99 player = NULL;
100 converse = NULL;
101 conv_gump = NULL;
102 command_bar = NULL;
103 new_command_bar = NULL;
104 clock = NULL;
105 party = NULL;
106 portrait = NULL;
107 view_manager = NULL;
108 egg_manager = NULL;
109 usecode = NULL;
110 effect_manager = NULL;
111 weather = NULL;
112 magic = NULL;
113 book = NULL;
114 keybinder = NULL;
115
116 _playing = true;
117 converse_gump_type = CONVERSE_GUMP_DEFAULT;
118 pause_flags = PAUSE_UNPAUSED;
119 pause_user_count = 0;
120 ignore_event_delay = 0;
121 unlimited_casting = false;
122 god_mode_enabled = false;
123 armageddon = false;
124 ethereal = false;
125 free_balloon_movement = false;
126 converse_gump_width = 0;
127 min_converse_gump_width = 0;
128 force_solid_converse_bg = false;
129
130 config->value("config/cheats/enabled", cheats_enabled, false);
131 config->value("config/cheats/enable_hackmove", is_using_hackmove, false);
132 config->value("config/input/enabled_dragging", dragging_enabled, true);
133 config->value("config/general/use_text_gumps", using_text_gumps, false);
134 config->value(config_get_game_key(config) + "/roof_mode", roof_mode, false);
135 config->value("config/input/doubleclick_opens_containers", open_containers, false);
136 int value;
137 uint16 screen_width = gui->get_width();
138 uint16 screen_height = gui->get_height();
139
140 init_game_style();
141 if (is_orig_style()) {
142 game_width = 320;
143 game_height = 200;
144 } else {
145 config->value("config/video/game_width", value, 320);
146 game_width = (value < screen_width) ? value : screen_width;
147 config->value("config/video/game_height", value, 200);
148 game_height = (value < screen_height) ? value : screen_height;
149 if (game_width < 320)
150 game_width = 320;
151 if (game_height < 200)
152 game_height = 200;
153 if (is_original_plus_full_map() && screen_height <= 200) // not tall enough to show extra map space
154 game_style = NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP;
155 }
156
157 string game_position;
158 config->value("config/video/game_position", game_position, "center");
159
160 if (game_position == "upper_left")
161 game_x_offset = game_y_offset = 0;
162 else { // center
163 game_x_offset = (screen_width - game_width) / 2;
164 game_y_offset = (screen_height - game_height) / 2;
165 }
166
167 effect_manager = new EffectManager;
168
169 init_cursor();
170
171 keybinder = new KeyBinder(config);
172 }
173
~Game()174 Game::~Game() {
175 // note: don't delete objects that are added to the GUI object via
176 // AddWidget()!
177 if (dither) delete dither;
178 if (tile_manager) delete tile_manager;
179 if (obj_manager) delete obj_manager;
180 if (palette) delete palette;
181 if (font_manager) delete font_manager;
182 //delete scroll;
183 if (game_map) delete game_map;
184 if (actor_manager) delete actor_manager;
185 //delete map_window;
186 if (player) delete player;
187 //delete background;
188 if (converse) delete converse;
189 if (clock) delete clock;
190 if (party) delete party;
191 if (portrait) delete portrait;
192 if (view_manager) delete view_manager;
193 if (sound_manager) delete sound_manager;
194 if (gui) delete gui;
195 if (usecode) delete usecode;
196 if (effect_manager) delete effect_manager;
197 if (cursor) delete cursor;
198 if (egg_manager) delete egg_manager;
199 if (weather) delete weather;
200 if (magic) delete magic;
201 if (book) delete book;
202 if (keybinder) delete keybinder;
203 }
204
shouldQuit() const205 bool Game::shouldQuit() const {
206 return !_playing || g_engine->shouldQuit();
207 }
208
loadGame(Script * s)209 bool Game::loadGame(Script *s) {
210 dither = new Dither(config);
211
212 script = s;
213 //sound_manager->LoadSongs(NULL);
214 //sound_manager->LoadObjectSamples(NULL);
215
216 palette = new GamePalette(screen, config);
217
218 clock = new GameClock(config, game_type);
219
220
221 background = new Background(config);
222 background->init();
223 background->Hide();
224 if (is_original_plus_full_map() == false) // need to render before map window
225 gui->AddWidget(background);
226
227 font_manager = new FontManager(config);
228 font_manager->init(game_type);
229
230 if (!is_new_style()) {
231 scroll = new MsgScroll(config, font_manager->get_font(0));
232 } else {
233 scroll = new MsgScrollNewUI(config, screen);
234 }
235 game_map = new Map(config);
236
237 egg_manager = new EggManager(config, game_type, game_map);
238
239 tile_manager = new TileManager(config);
240 if (tile_manager->loadTiles() == false)
241 return false;
242
243 ConsoleAddInfo("Loading ObjManager()");
244 obj_manager = new ObjManager(config, tile_manager, egg_manager);
245
246 if (game_type == NUVIE_GAME_U6) {
247 book = new Book(config);
248 if (book->init() == false)
249 return false;
250 config->value(config_get_game_key(config) + "/free_balloon_movement", free_balloon_movement, false);
251 }
252
253 // Correct usecode class for each game
254 switch (game_type) {
255 case NUVIE_GAME_U6 :
256 usecode = (UseCode *) new U6UseCode(this, config);
257 break;
258 case NUVIE_GAME_MD :
259 usecode = (UseCode *) new UseCode(this, config);
260 break;
261 case NUVIE_GAME_SE :
262 usecode = (UseCode *) new UseCode(this, config);
263 break;
264 }
265
266 obj_manager->set_usecode(usecode);
267 //obj_manager->loadObjs();
268
269 ConsoleAddInfo("Loading map data.");
270 game_map->loadMap(tile_manager, obj_manager);
271 egg_manager->set_obj_manager(obj_manager);
272
273 ConsoleAddInfo("Loading actor data.");
274 actor_manager = new ActorManager(config, game_map, tile_manager, obj_manager, clock);
275
276 game_map->set_actor_manager(actor_manager);
277 egg_manager->set_actor_manager(actor_manager);
278
279 map_window = new MapWindow(config, game_map);
280 map_window->init(tile_manager, obj_manager, actor_manager);
281 map_window->Hide();
282 gui->AddWidget(map_window);
283 if (is_original_plus_full_map()) // need to render after map window
284 gui->AddWidget(background);
285
286 weather = new Weather(config, clock, game_type);
287
288 // if(!is_new_style()) // Everyone always uses original style command bar now.
289 {
290 command_bar = new CommandBar(this);
291 bool using_new_command_bar;
292 config->value("config/input/new_command_bar", using_new_command_bar, false);
293 if (using_new_command_bar) {
294 init_new_command_bar();
295 }
296 }
297 // else
298 // command_bar = new CommandBarNewUI(this);
299 command_bar->Hide();
300 gui->AddWidget(command_bar);
301
302
303 player = new Player(config);
304 party = new Party(config);
305 player->init(obj_manager, actor_manager, map_window, clock, party);
306 party->init(this, actor_manager);
307
308 portrait = newPortrait(game_type, config);
309 if (portrait->init() == false)
310 return false;
311
312 view_manager = new ViewManager(config);
313 view_manager->init(gui, font_manager->get_font(0), party, player, tile_manager, obj_manager, portrait);
314 scroll->Hide();
315 gui->AddWidget(scroll);
316
317
318 //map_window->set_windowSize(11,11);
319
320 init_converse_gump_settings();
321 init_converse();
322
323 usecode->init(obj_manager, game_map, player, scroll);
324
325
326
327 if (game_type == NUVIE_GAME_U6) {
328 magic = new Magic();
329 }
330
331 event->init(obj_manager, map_window, scroll, player, magic, clock, view_manager, usecode, gui, keybinder);
332 if (game_type == NUVIE_GAME_U6) {
333 magic->init(event);
334 }
335
336 if (g_engine->journeyOnwards() == false) {
337 return false;
338 }
339
340 ConsoleAddInfo("Polishing Anhk");
341
342 //ConsolePause();
343 ConsoleHide();
344
345 // if(!is_new_style())
346 {
347 if (is_orig_style())
348 command_bar->Show();
349 else {
350 bool show;
351 Std::string show_cb;
352 config->value(config_get_game_key(config) + "/show_orig_style_cb", show_cb, "default");
353 if (show_cb == "default") {
354 if (is_new_style())
355 show = false;
356 else
357 show = true;
358 } else if (show_cb == "no")
359 show = false;
360 else
361 show = true;
362 if (show)
363 command_bar->Show();
364 }
365 }
366
367 if (!is_new_style() || screen->get_width() != get_game_width() || screen->get_height() != get_game_height()) {
368 background->Show();
369 }
370
371 map_window->Show();
372 scroll->Show();
373 view_manager->update();
374
375 if (cursor)
376 cursor->show();
377
378 return true;
379 }
380
init_converse_gump_settings()381 void Game::init_converse_gump_settings() {
382 if (is_new_style())
383 converse_gump_type = CONVERSE_GUMP_DEFAULT;
384 else {
385 converse_gump_type = get_converse_gump_type_from_config(config);
386 }
387 Std::string width_str;
388 int gump_w = get_game_width();
389
390 if (game_type == NUVIE_GAME_MD)
391 min_converse_gump_width = 298;
392 else if (game_type == NUVIE_GAME_SE)
393 min_converse_gump_width = 301;
394 else // U6
395 min_converse_gump_width = 286;
396
397 config->value(config_get_game_key(config) + "/converse_width", width_str, "default");
398 if (!game->is_orig_style()) {
399 if (width_str == "default") {
400 int map_width = get_game_width();
401 if (is_original_plus())
402 map_width += - background->get_border_width() - 1;
403 if (map_width > min_converse_gump_width * 1.5) // big enough that we probably don't want to take up the whole screen
404 gump_w = min_converse_gump_width;
405 else if (game->is_original_plus() && map_width >= min_converse_gump_width) // big enough to draw without going over the UI
406 gump_w = map_width;
407 } else {
408 config->value(config_get_game_key(config) + "/converse_width", gump_w, gump_w);
409 if (gump_w < min_converse_gump_width)
410 gump_w = min_converse_gump_width;
411 else if (gump_w > get_game_width())
412 gump_w = get_game_width();
413 }
414 }
415 converse_gump_width = (uint16)gump_w;
416
417 if ((is_original_plus_cutoff_map() && get_game_width() - background->get_border_width() < min_converse_gump_width)
418 || game->is_orig_style())
419 force_solid_converse_bg = true;
420 else
421 force_solid_converse_bg = false;
422 }
423
init_converse()424 void Game::init_converse() {
425 converse = new Converse();
426 if (using_new_converse_gump()) {
427 conv_gump = new ConverseGump(config, font_manager->get_font(0), screen);
428 conv_gump->Hide();
429 gui->AddWidget(conv_gump);
430
431 converse->init(config, game_type, conv_gump, actor_manager, clock, player, view_manager, obj_manager);
432 } else if (game_type == NUVIE_GAME_U6 && converse_gump_type == CONVERSE_GUMP_DEFAULT) {
433 converse->init(config, game_type, scroll, actor_manager, clock, player, view_manager, obj_manager);
434 } else {
435 ConverseGumpWOU *gump = new ConverseGumpWOU(config, font_manager->get_font(0), screen);
436 gump->Hide();
437 gui->AddWidget(gump);
438 converse->init(config, game_type, gump, actor_manager, clock, player, view_manager, obj_manager);
439 }
440
441 }
442
set_converse_gump_type(uint8 new_type)443 void Game::set_converse_gump_type(uint8 new_type) {
444 if (converse)
445 delete converse;
446 converse_gump_type = new_type;
447 init_converse();
448 }
449
using_new_converse_gump()450 bool Game::using_new_converse_gump() {
451 return (is_new_style() || converse_gump_type == CONVERSE_GUMP_U7_STYLE);
452 }
453
delete_new_command_bar()454 void Game::delete_new_command_bar() {
455 if (new_command_bar == NULL)
456 return;
457 new_command_bar->Delete();
458 new_command_bar = NULL;
459 }
460
init_new_command_bar()461 void Game::init_new_command_bar() {
462 if (new_command_bar != NULL)
463 return;
464 new_command_bar = new CommandBarNewUI(this);
465 new_command_bar->Hide();
466 gui->AddWidget(new_command_bar);
467 }
468
init_cursor()469 void Game::init_cursor() {
470 if (!cursor)
471 cursor = new Cursor();
472
473 if (cursor->init(config, screen, game_type))
474 SDL_ShowCursor(false); // won't need the system default
475 else {
476 delete cursor;
477 cursor = NULL; // no game cursor
478 }
479 }
480
init_game_style()481 void Game::init_game_style() {
482 string game_style_str;
483 config->value("config/video/game_style", game_style_str, "original");
484 if (game_style_str == "new")
485 game_style = NUVIE_STYLE_NEW;
486 else if (game_style_str == "original+")
487 game_style = NUVIE_STYLE_ORIG_PLUS_CUTOFF_MAP;
488 else if (game_style_str == "original+_full_map")
489 game_style = NUVIE_STYLE_ORIG_PLUS_FULL_MAP;
490 else
491 game_style = NUVIE_STYLE_ORIG;
492
493 }
494
doubleclick_opens_containers()495 bool Game::doubleclick_opens_containers() {
496 if (open_containers || is_new_style())
497 return true;
498 else
499 return false;
500 }
501
using_hackmove()502 bool Game::using_hackmove() {
503 if (cheats_enabled)
504 return is_using_hackmove;
505 else
506 return false;
507 }
508
set_hackmove(bool hackmove)509 void Game::set_hackmove(bool hackmove) {
510 is_using_hackmove = hackmove;
511 map_window->set_interface();
512 }
513
set_mouse_pointer(uint8 ptr_num)514 bool Game::set_mouse_pointer(uint8 ptr_num) {
515 return (cursor && cursor->set_pointer(ptr_num));
516 }
517
518 //FIXME pausing inside a script function causes problems with yield/resume logic.
set_pause_flags(GamePauseState state)519 void Game::set_pause_flags(GamePauseState state) {
520 pause_flags = state; // set
521 }
522
unpause_all()523 void Game::unpause_all() {
524 // DEBUG(0, LEVEL_DEBUGGING,"Unpause ALL!\n");
525 unpause_user();
526 unpause_anims();
527 unpause_world();
528 }
529
unpause_user()530 void Game::unpause_user() {
531 if (pause_user_count > 0)
532 pause_user_count--;
533
534 if (pause_user_count == 0) {
535 set_pause_flags((GamePauseState)(pause_flags & ~PAUSE_USER));
536
537
538 //if(event->get_mode() == WAIT_MODE)
539 // event->endAction(); // change to MOVE_MODE, hide cursors
540 if (gui->get_block_input())
541 gui->unblock();
542
543 }
544
545 // DEBUG(0, LEVEL_DEBUGGING, "unpause user count=%d!\n", pause_user_count);
546 }
547
unpause_anims()548 void Game::unpause_anims() {
549 set_pause_flags((GamePauseState)(pause_flags & ~PAUSE_ANIMS));
550 }
551
unpause_world()552 void Game::unpause_world() {
553 set_pause_flags((GamePauseState)(pause_flags & ~PAUSE_WORLD));
554
555 if (actor_manager->get_update() == false) // ActorMgr is not running
556 game->get_actor_manager()->set_update(true); // resume
557
558 //if(clock->get_active() == false) // start time
559 // clock->set_active(true);
560 }
561
pause_all()562 void Game::pause_all() {
563 pause_user();
564 pause_anims();
565 pause_world();
566 }
567
pause_user()568 void Game::pause_user() {
569 set_pause_flags((GamePauseState)(pause_flags | PAUSE_USER));
570
571 if (!gui->get_block_input() && pause_user_count == 0)
572 gui->block();
573
574 pause_user_count++;
575
576 // DEBUG(0, LEVEL_DEBUGGING, "Pause user count=%d!\n", pause_user_count);
577 }
578
pause_anims()579 void Game::pause_anims() {
580 set_pause_flags((GamePauseState)(pause_flags | PAUSE_ANIMS));
581 }
582
pause_world()583 void Game::pause_world() {
584 set_pause_flags((GamePauseState)(pause_flags | PAUSE_WORLD));
585
586 if (actor_manager->get_update() == true) // ActorMgr is running
587 game->get_actor_manager()->set_update(false); // pause
588
589 //if(clock->get_active() == true) // stop time
590 // clock->set_active(false);
591 }
592
593
dont_wait_for_interval()594 void Game::dont_wait_for_interval() {
595 if (ignore_event_delay < 255)
596 ++ignore_event_delay;
597 event->set_ignore_timeleft(true);
598 }
599
600
wait_for_interval()601 void Game::wait_for_interval() {
602 if (ignore_event_delay > 0)
603 --ignore_event_delay;
604 if (ignore_event_delay == 0)
605 event->set_ignore_timeleft(false);
606 }
607
608
time_changed()609 void Game::time_changed() {
610 if (!is_new_style()) {
611 if (game->is_orig_style()) // others constantly update
612 get_command_bar()->update(); // date & wind
613 get_view_manager()->get_party_view()->update(); // sky
614 }
615 get_map_window()->updateAmbience();
616 }
617
618 // FIXME: should this be in ViewManager?
stats_changed()619 void Game::stats_changed() {
620 if (!is_new_style()) {
621 get_view_manager()->get_actor_view()->update();
622 get_view_manager()->get_party_view()->update();
623 }
624 }
625
626
play()627 void Game::play() {
628 pause_flags = PAUSE_UNPAUSED;
629
630 //view_manager->set_inventory_mode(1); //FIX
631
632 screen->update();
633
634 //map_window->drawMap();
635
636 map_window->updateBlacking();
637
638 while (!shouldQuit()) {
639 if (cursor) cursor->clear(); // restore cursor area before GUI events
640
641 event->update();
642 if (clock->get_timer(GAMECLOCK_TIMER_U6_TIME_STOP) == 0) {
643 palette->rotatePalette();
644 tile_manager->update();
645 actor_manager->twitchActors();
646 }
647 actor_manager->moveActors(); // update/move actors for this turn
648 map_window->update();
649 //map_window->drawMap();
650 converse->continue_script();
651 //scroll->updateScroll();
652 effect_manager->update_effects();
653
654 gui->Display();
655 if (cursor) cursor->display();
656
657 screen->preformUpdate();
658 sound_manager->update();
659 event->wait();
660 }
661 return;
662 }
663
update_until_converse_finished()664 void Game::update_until_converse_finished() {
665 while (converse->running()) {
666 update_once(true, true);
667 update_once_display();
668 }
669 }
670
update_once(bool process_gui_input)671 void Game::update_once(bool process_gui_input) {
672 update_once(process_gui_input, false);
673 }
674
update_once(bool process_gui_input,bool run_converse)675 void Game::update_once(bool process_gui_input, bool run_converse) {
676 if (cursor) cursor->clear(); // restore cursor area before GUI events
677
678 event->update_timers();
679
680 Common::Event evt;
681 while (Events::get()->pollEvent(evt)) {
682 if (process_gui_input)
683 gui->HandleEvent(&evt);
684 }
685
686 if (clock->get_timer(GAMECLOCK_TIMER_U6_TIME_STOP) == 0) {
687 palette->rotatePalette();
688 tile_manager->update();
689 actor_manager->twitchActors();
690 }
691 map_window->update();
692 if (run_converse) {
693 converse->continue_script();
694 }
695 effect_manager->update_effects();
696 }
697
update_once_display()698 void Game::update_once_display() {
699 gui->Display();
700 if (cursor) cursor->display();
701
702 screen->preformUpdate();
703 sound_manager->update();
704 event->wait();
705 }
706
707 /* return the fullpath to the datafile. First look for it in the savegame directory.
708 * Then in the app data directory.
709 */
get_data_file_path(Std::string datafile)710 Std::string Game::get_data_file_path(Std::string datafile) {
711 Std::string path;
712 build_path("data", datafile, path);
713
714 if (!file_exists(path.c_str())) {
715 build_path(gui->get_data_dir(), datafile, path);
716 }
717
718 return path;
719 }
720
getRandom(uint maxVal)721 uint getRandom(uint maxVal) {
722 return g_engine->getRandomNumber(maxVal);
723 }
724
725 } // End of namespace Nuvie
726 } // End of namespace Ultima
727