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 "common/scummsys.h"
24 #include "common/config-manager.h"
25 #include "common/debug-channels.h"
26 #include "common/events.h"
27 #include "engines/util.h"
28 #include "xeen/xeen.h"
29 #include "xeen/files.h"
30 #include "xeen/resources.h"
31
32 namespace Xeen {
33
34 XeenEngine *g_vm = nullptr;
35
XeenEngine(OSystem * syst,const XeenGameDescription * gameDesc)36 XeenEngine::XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc)
37 : Engine(syst), _gameDescription(gameDesc), _randomSource("Xeen") {
38 // Set up debug channels
39 DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level");
40 DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
41 DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
42 DebugMan.addDebugChannel(kDebugSound, "sound", "Sound processing");
43
44 _combat = nullptr;
45 _debugger = nullptr;
46 _events = nullptr;
47 _files = nullptr;
48 _interface = nullptr;
49 _locations = nullptr;
50 _map = nullptr;
51 _party = nullptr;
52 _patcher = nullptr;
53 _resources = nullptr;
54 _saves = nullptr;
55 _screen = nullptr;
56 _scripts = nullptr;
57 _sound = nullptr;
58 _spells = nullptr;
59 _windows = nullptr;
60 _noDirectionSense = false;
61 _startupWindowActive = false;
62 _gameMode = GMODE_STARTUP;
63 _mode = MODE_STARTUP;
64 _endingScore = 0;
65 _loadSaveSlot = -1;
66 _gameWon[0] = _gameWon[1] = _gameWon[2] = false;
67 _finalScore = 0;
68 g_vm = this;
69 }
70
~XeenEngine()71 XeenEngine::~XeenEngine() {
72 delete _combat;
73 delete _debugger;
74 delete _events;
75 delete _interface;
76 delete _locations;
77 delete _map;
78 delete _party;
79 delete _patcher;
80 delete _saves;
81 delete _screen;
82 delete _scripts;
83 delete _sound;
84 delete _spells;
85 delete _windows;
86 delete _resources;
87 delete _files;
88 g_vm = nullptr;
89 }
90
initialize()91 bool XeenEngine::initialize() {
92 // Create sub-objects of the engine
93 _files = new FileManager(this);
94 if (!_files->setup())
95 return false;
96
97 _resources = new Resources();
98 _combat = new Combat(this);
99 _debugger = new Debugger(this);
100 _events = new EventsManager(this);
101 _interface = new Interface(this);
102 _locations = new LocationManager();
103 _map = new Map(this);
104 _party = new Party(this);
105 _patcher = new Patcher();
106 _saves = new SavesManager(_targetName);
107 _screen = new Screen(this);
108 _scripts = new Scripts(this);
109 _sound = new Sound(_mixer);
110 _spells = new Spells(this);
111 _windows = new Windows();
112
113 // Set graphics mode
114 initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT);
115
116 // Setup mixer
117 syncSoundSettings();
118
119 // Load settings
120 loadSettings();
121
122 return true;
123 }
124
loadSettings()125 void XeenEngine::loadSettings() {
126 _gameWon[0] = ConfMan.hasKey("game_won") && ConfMan.getBool("game_won");
127 _gameWon[1] = ConfMan.hasKey("game_won2") && ConfMan.getBool("game_won2");
128 _gameWon[2] = ConfMan.hasKey("game_won3") && ConfMan.getBool("game_won3");
129 _finalScore = ConfMan.hasKey("final_score") ? ConfMan.getInt("final_score") : 0;
130
131 _extOptions._showItemCosts = ConfMan.hasKey("ShowItemCosts") && ConfMan.getBool("ShowItemCosts");
132 _extOptions._durableArmor = ConfMan.hasKey("DurableArmor") && ConfMan.getBool("DurableArmor");
133
134 // If requested, load a savegame instead of showing the intro
135 if (ConfMan.hasKey("save_slot")) {
136 int saveSlot = ConfMan.getInt("save_slot");
137 if (saveSlot >= 0 && saveSlot <= 999)
138 _loadSaveSlot = saveSlot;
139 }
140 }
141
run()142 Common::Error XeenEngine::run() {
143 if (initialize())
144 outerGameLoop();
145
146 return Common::kNoError;
147 }
148
outerGameLoop()149 void XeenEngine::outerGameLoop() {
150 if (_loadSaveSlot != -1)
151 // Loading savegame from launcher, so Skip menu and go straight to game
152 _gameMode = GMODE_PLAY_GAME;
153
154 while (!shouldQuit() && _gameMode != GMODE_QUIT) {
155 GameMode mode = _gameMode;
156 _gameMode = GMODE_NONE;
157 assert(mode != GMODE_NONE);
158
159 switch (mode) {
160 case GMODE_STARTUP:
161 showStartup();
162 break;
163
164 case GMODE_MENU:
165 showMainMenu();
166 break;
167
168 case GMODE_PLAY_GAME:
169 playGame();
170 break;
171
172 default:
173 break;
174 }
175 }
176 }
177
getRandomNumber(int maxNumber)178 int XeenEngine::getRandomNumber(int maxNumber) {
179 return _randomSource.getRandomNumber(maxNumber);
180 }
181
getRandomNumber(int minNumber,int maxNumber)182 int XeenEngine::getRandomNumber(int minNumber, int maxNumber) {
183 return getRandomNumber(maxNumber - minNumber) + minNumber;
184 }
185
saveGameState(int slot,const Common::String & desc)186 Common::Error XeenEngine::saveGameState(int slot, const Common::String &desc) {
187 return _saves->saveGameState(slot, desc);
188 }
189
loadGameState(int slot)190 Common::Error XeenEngine::loadGameState(int slot) {
191 _loadSaveSlot = slot;
192 return Common::kNoError;
193 }
194
canLoadGameStateCurrently()195 bool XeenEngine::canLoadGameStateCurrently() {
196 return _mode != MODE_STARTUP;
197 }
198
canSaveGameStateCurrently()199 bool XeenEngine::canSaveGameStateCurrently() {
200 return _mode != MODE_COMBAT && _mode != MODE_STARTUP && _mode != MODE_SCRIPT_IN_PROGRESS
201 && (_map->mazeData()._mazeFlags & RESTRICTION_SAVE) == 0;
202 }
203
playGame()204 void XeenEngine::playGame() {
205 _files->setGameCc(0);
206 _sound->stopAllAudio();
207 SpriteResource::setClippedBottom(140);
208
209 play();
210 _sound->stopAllAudio();
211 }
212
play()213 void XeenEngine::play() {
214 _interface->setup();
215 _screen->loadBackground("back.raw");
216 _screen->loadPalette("mm4.pal");
217
218 if (getGameID() == GType_DarkSide && !_map->_loadCcNum) {
219 _map->_loadCcNum = 1;
220 _party->_mazeId = 29;
221 _party->_mazeDirection = DIR_NORTH;
222 _party->_mazePosition.x = 25;
223 _party->_mazePosition.y = 21;
224 }
225
226 _map->clearMaze();
227 if (_loadSaveSlot >= 0) {
228 _saves->newGame();
229 _saves->loadGameState(_loadSaveSlot);
230 _loadSaveSlot = -1;
231 } else {
232 _map->load(_party->_mazeId);
233 }
234
235 _interface->startup();
236 if (_mode == MODE_STARTUP) {
237 // _screen->fadeOut();
238 }
239
240 (*_windows)[0].update();
241 _interface->mainIconsPrint();
242 (*_windows)[0].update();
243 _events->setCursor(0);
244
245 _combat->_moveMonsters = true;
246 if (_mode == MODE_STARTUP) {
247 _mode = MODE_INTERACTIVE;
248 _screen->fadeIn();
249 }
250
251 _combat->_moveMonsters = true;
252
253 gameLoop();
254
255 if (_party->_dead)
256 death();
257
258 _mode = MODE_STARTUP;
259 _gameMode = GMODE_MENU;
260 }
261
gameLoop()262 void XeenEngine::gameLoop() {
263 // Main game loop
264 while (isLoadPending() || !shouldExit()) {
265 if (isLoadPending()) {
266 // Load any pending savegame
267 int saveSlot = _loadSaveSlot;
268 _loadSaveSlot = -1;
269 (void)_saves->loadGameState(saveSlot);
270 _interface->drawParty(true);
271 }
272
273 _map->cellFlagLookup(_party->_mazePosition);
274 if (_map->_currentIsEvent) {
275 _gameMode = (GameMode)_scripts->checkEvents();
276 if (isLoadPending())
277 continue;
278 if (shouldExit())
279 return;
280 }
281 _party->giveTreasure();
282
283 // Main user interface handler for waiting for and processing user input
284 _interface->perform();
285
286 if (_party->_dead)
287 break;
288 }
289 }
290
printMil(uint value)291 Common::String XeenEngine::printMil(uint value) {
292 return (value >= 1000000) ? Common::String::format("%u mil", value / 1000000) :
293 Common::String::format("%u", value);
294 }
295
printK(uint value)296 Common::String XeenEngine::printK(uint value) {
297 return (value > 9999) ? Common::String::format("%uk", value / 1000) :
298 Common::String::format("%u", value);
299 }
300
printK2(uint value)301 Common::String XeenEngine::printK2(uint value) {
302 return (value > 999) ? Common::String::format("%uk", value / 1000) :
303 Common::String::format("%u", value);
304 }
305
syncSoundSettings()306 void XeenEngine::syncSoundSettings() {
307 Engine::syncSoundSettings();
308
309 if (_sound)
310 _sound->updateSoundSettings();
311 }
312
saveSettings()313 void XeenEngine::saveSettings() {
314 if (_gameWon[0])
315 ConfMan.setBool("game_won", true);
316 if (_gameWon[1])
317 ConfMan.setBool("game_won2", true);
318 if (_gameWon[2])
319 ConfMan.setBool("game_won3", true);
320
321 ConfMan.setInt("final_score", _finalScore);
322 ConfMan.flushToDisk();
323 }
324
GUIError(const Common::String & msg)325 void XeenEngine::GUIError(const Common::String &msg) {
326 GUIErrorMessage(msg);
327 }
328
autoSaveCheck(int & lastSaveTime)329 void XeenEngine::autoSaveCheck(int &lastSaveTime) {
330 if (shouldPerformAutoSave(lastSaveTime) && canSaveGameStateCurrently() &&
331 (_map && !(_map->mazeData()._mazeFlags & RESTRICTION_SAVE))) {
332 _saves->doAutosave();
333 lastSaveTime = g_system->getMillis();
334 }
335 }
336
337 } // End of namespace Xeen
338