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