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/config-manager.h"
24 #include "common/system.h"
25 #include "common/textconsole.h"
26 
27 #include "sky/control.h"
28 #include "sky/debug.h"
29 #include "sky/disk.h"
30 #include "sky/grid.h"
31 #include "sky/intro.h"
32 #include "sky/logic.h"
33 #include "sky/mouse.h"
34 #include "sky/music/adlibmusic.h"
35 #include "sky/music/gmmusic.h"
36 #include "sky/music/mt32music.h"
37 #include "sky/music/musicbase.h"
38 #include "sky/screen.h"
39 #include "sky/sky.h"
40 #include "sky/skydefs.h"
41 #include "sky/sound.h"
42 #include "sky/text.h"
43 #include "sky/compact.h"
44 
45 #include "audio/mididrv.h"
46 #include "audio/mixer.h"
47 
48 #include "engines/util.h"
49 
50 /*
51  At the beginning the reverse engineers were happy, and did rejoice at
52  their task, for the engine before them did shineth and was full of
53  promise. But then they did look closer and see'th the aweful truth;
54  its code was assembly and messy (rareth was its comments). And so large
55  were its includes that did at first seem small; queereth also was its
56  compact(s). Then they did findeth another version, and this was slightly
57  different from the first. Then a third, and this was different again.
58  All different, but not really better, for all were not really compatible.
59  But, eventually, it did come to pass that Steel Sky was implemented on
60  a modern platform. And the programmers looked and saw that it was indeed a
61  miracle. But they were not joyous and instead did weep for nobody knew
62  just what had been done. Except people who read the source. Hello.
63 
64  With apologies to the CD32 SteelSky file.
65 */
66 
67 namespace Sky {
68 
69 void *SkyEngine::_itemList[300];
70 
71 SystemVars SkyEngine::_systemVars = {0, 0, 0, 0, 4316, 0, 0, false, false };
72 
SkyEngine(OSystem * syst)73 SkyEngine::SkyEngine(OSystem *syst)
74 	: Engine(syst), _fastMode(0), _debugger(0) {
75 }
76 
~SkyEngine()77 SkyEngine::~SkyEngine() {
78 	delete _skyLogic;
79 	delete _skySound;
80 	delete _skyMusic;
81 	delete _skyText;
82 	delete _skyMouse;
83 	delete _skyScreen;
84 	delete _debugger;
85 	delete _skyDisk;
86 	delete _skyControl;
87 	delete _skyCompact;
88 
89 	for (int i = 0; i < 300; i++)
90 		if (_itemList[i])
91 			free(_itemList[i]);
92 }
93 
syncSoundSettings()94 void SkyEngine::syncSoundSettings() {
95 	Engine::syncSoundSettings();
96 
97 	bool mute = false;
98 	if (ConfMan.hasKey("mute"))
99 		mute = ConfMan.getBool("mute");
100 
101 	if (ConfMan.getBool("sfx_mute"))
102 		SkyEngine::_systemVars.systemFlags |= SF_FX_OFF;
103 
104 	if (ConfMan.getBool("music_mute"))
105 		SkyEngine::_systemVars.systemFlags |= SF_MUS_OFF;
106 
107 	_skyMusic->setVolume(mute ? 0: ConfMan.getInt("music_volume") >> 1);
108 }
109 
getDebugger()110 GUI::Debugger *SkyEngine::getDebugger() {
111 	return _debugger;
112 }
113 
initVirgin()114 void SkyEngine::initVirgin() {
115 	_skyScreen->setPalette(60111);
116 	_skyScreen->showScreen(60110);
117 }
118 
handleKey()119 void SkyEngine::handleKey() {
120 	if (_keyPressed.keycode && _systemVars.paused) {
121 		_skySound->fnUnPauseFx();
122 		_systemVars.paused = false;
123 		_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
124 	} else if (_keyPressed.hasFlags(Common::KBD_CTRL)) {
125 		if (_keyPressed.keycode == Common::KEYCODE_f)
126 			_fastMode ^= 1;
127 		else if (_keyPressed.keycode == Common::KEYCODE_g)
128 			_fastMode ^= 2;
129 		else if (_keyPressed.keycode == Common::KEYCODE_d)
130 			_debugger->attach();
131 	} else if (_keyPressed.keycode) {
132 		switch (_keyPressed.keycode) {
133 		case Common::KEYCODE_BACKQUOTE:
134 		case Common::KEYCODE_HASH:
135 			_debugger->attach();
136 			break;
137 		case Common::KEYCODE_F5:
138 			_skyControl->doControlPanel();
139 			break;
140 
141 		case Common::KEYCODE_ESCAPE:
142 			if (!_systemVars.pastIntro)
143 				_skyControl->restartGame();
144 			break;
145 
146 		case Common::KEYCODE_PERIOD:
147 			_skyMouse->logicClick();
148 			break;
149 
150 		case Common::KEYCODE_p:
151 			_skyScreen->halvePalette();
152 			_skySound->fnPauseFx();
153 			_systemVars.paused = true;
154 			break;
155 
156 		default:
157 			break;
158 		}
159 	}
160 	_keyPressed.reset();
161 }
162 
go()163 Common::Error SkyEngine::go() {
164 	_keyPressed.reset();
165 
166 	uint16 result = 0;
167 	if (ConfMan.hasKey("save_slot")) {
168 		int saveSlot = ConfMan.getInt("save_slot");
169 		if (saveSlot >= 0 && saveSlot <= 999)
170 			result = _skyControl->quickXRestore(ConfMan.getInt("save_slot"));
171 	}
172 
173 	if (result != GAME_RESTORED) {
174 		bool introSkipped = false;
175 		if (_systemVars.gameVersion > 272) { // don't do intro for floppydemos
176 			Intro *skyIntro = new Intro(_skyDisk, _skyScreen, _skyMusic, _skySound, _skyText, _mixer, _system);
177 			bool floppyIntro = ConfMan.getBool("alt_intro");
178 			introSkipped = !skyIntro->doIntro(floppyIntro);
179 			delete skyIntro;
180 		}
181 
182 		if (!shouldQuit()) {
183 			_skyScreen->clearScreen(true);
184 			// restartGame() takes us to the first scene, without showing the
185 			// initial animation where Foster is being chased. initScreen0()
186 			// shows the first scene together with that animation. We can't
187 			// call both, as they both load the same scene.
188 			if (introSkipped)
189 				_skyControl->restartGame();
190 			else
191 				_skyLogic->initScreen0();
192 		}
193 	}
194 
195 	_lastSaveTime = _system->getMillis();
196 
197 	uint32 delayCount = _system->getMillis();
198 	while (!shouldQuit()) {
199 		_debugger->onFrame();
200 
201 		if (shouldPerformAutoSave(_lastSaveTime)) {
202 			if (_skyControl->loadSaveAllowed()) {
203 				_lastSaveTime = _system->getMillis();
204 				_skyControl->doAutoSave();
205 			} else
206 				_lastSaveTime += 30 * 1000; // try again in 30 secs
207 		}
208 		_skySound->checkFxQueue();
209 		_skyMouse->mouseEngine();
210 		handleKey();
211 		if (_systemVars.paused) {
212 			do {
213 				_system->updateScreen();
214 				delay(50);
215 				handleKey();
216 			} while (_systemVars.paused);
217 			delayCount = _system->getMillis();
218 		}
219 
220 		_skyLogic->engine();
221 		_skyScreen->processSequence();
222 		_skyScreen->recreate();
223 		_skyScreen->spriteEngine();
224 		if (_debugger->showGrid()) {
225 			uint8 *grid = _skyLogic->_skyGrid->giveGrid(Logic::_scriptVariables[SCREEN]);
226 			if (grid) {
227 				_skyScreen->showGrid(grid);
228 				_skyScreen->forceRefresh();
229 			}
230 		}
231 		_skyScreen->flip();
232 
233 		if (_fastMode & 2)
234 			delay(0);
235 		else if (_fastMode & 1)
236 			delay(10);
237 		else {
238 			delayCount += _systemVars.gameSpeed;
239 			int needDelay = delayCount - (int)_system->getMillis();
240 			if ((needDelay < 0) || (needDelay > _systemVars.gameSpeed)) {
241 				needDelay = 0;
242 				delayCount = _system->getMillis();
243 			}
244 			delay(needDelay);
245 		}
246 	}
247 
248 	_skyControl->showGameQuitMsg();
249 	_skyMusic->stopMusic();
250 	ConfMan.flushToDisk();
251 	delay(1500);
252 	return Common::kNoError;
253 }
254 
init()255 Common::Error SkyEngine::init() {
256 	initGraphics(320, 200);
257 
258 	_skyDisk = new Disk();
259 	_skySound = new Sound(_mixer, _skyDisk, Audio::Mixer::kMaxChannelVolume);
260 
261 	_systemVars.gameVersion = _skyDisk->determineGameVersion();
262 
263 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32);
264 	if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
265 		_systemVars.systemFlags |= SF_SBLASTER;
266 		_skyMusic = new AdLibMusic(_mixer, _skyDisk);
267 	} else {
268 		_systemVars.systemFlags |= SF_ROLAND;
269 		if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"))
270 			_skyMusic = new MT32Music(MidiDriver::createMidi(dev), _mixer, _skyDisk);
271 		else
272 			_skyMusic = new GmMusic(MidiDriver::createMidi(dev), _mixer, _skyDisk);
273 	}
274 
275 	if (isCDVersion()) {
276 		if (ConfMan.hasKey("nosubtitles")) {
277 			warning("Configuration key 'nosubtitles' is deprecated. Use 'subtitles' instead");
278 			if (!ConfMan.getBool("nosubtitles"))
279 				_systemVars.systemFlags |= SF_ALLOW_TEXT;
280 		}
281 
282 		if (ConfMan.getBool("subtitles"))
283 			_systemVars.systemFlags |= SF_ALLOW_TEXT;
284 
285 		if (!ConfMan.getBool("speech_mute"))
286 			_systemVars.systemFlags |= SF_ALLOW_SPEECH;
287 
288 	} else
289 		_systemVars.systemFlags |= SF_ALLOW_TEXT;
290 
291 	_systemVars.systemFlags |= SF_PLAY_VOCS;
292 	_systemVars.gameSpeed = 80;
293 
294 	_skyCompact = new SkyCompact();
295 	_skyText = new Text(_skyDisk, _skyCompact);
296 	_skyMouse = new Mouse(_system, _skyDisk, _skyCompact);
297 	_skyScreen = new Screen(_system, _skyDisk, _skyCompact);
298 
299 	initVirgin();
300 	initItemList();
301 	loadFixedItems();
302 	_skyLogic = new Logic(_skyCompact, _skyScreen, _skyDisk, _skyText, _skyMusic, _skyMouse, _skySound);
303 	_skyMouse->useLogicInstance(_skyLogic);
304 
305 	_skyControl = new Control(_saveFileMan, _skyScreen, _skyDisk, _skyMouse, _skyText, _skyMusic, _skyLogic, _skySound, _skyCompact, _system);
306 	_skyLogic->useControlInstance(_skyControl);
307 
308 	switch (Common::parseLanguage(ConfMan.get("language"))) {
309 	case Common::EN_USA:
310 		_systemVars.language = SKY_USA;
311 		break;
312 	case Common::DE_DEU:
313 		_systemVars.language = SKY_GERMAN;
314 		break;
315 	case Common::FR_FRA:
316 		_systemVars.language = SKY_FRENCH;
317 		break;
318 	case Common::IT_ITA:
319 		_systemVars.language = SKY_ITALIAN;
320 		break;
321 	case Common::PT_BRA:
322 		_systemVars.language = SKY_PORTUGUESE;
323 		break;
324 	case Common::ES_ESP:
325 		_systemVars.language = SKY_SPANISH;
326 		break;
327 	case Common::SE_SWE:
328 		_systemVars.language = SKY_SWEDISH;
329 		break;
330 	case Common::EN_GRB:
331 		_systemVars.language = SKY_ENGLISH;
332 		break;
333 	default:
334 		_systemVars.language = SKY_ENGLISH;
335 		break;
336 	}
337 
338 	if (!_skyDisk->fileExists(60600 + SkyEngine::_systemVars.language * 8)) {
339 		warning("The language you selected does not exist in your BASS version");
340 		if (_skyDisk->fileExists(60600))
341 			SkyEngine::_systemVars.language = SKY_ENGLISH; // default to GB english if it exists..
342 		else if (_skyDisk->fileExists(60600 + SKY_USA * 8))
343 			SkyEngine::_systemVars.language = SKY_USA;		// try US english...
344 		else
345 			for (uint8 cnt = SKY_ENGLISH; cnt <= SKY_SPANISH; cnt++)
346 				if (_skyDisk->fileExists(60600 + cnt * 8)) {	// pick the first language we can find
347 					SkyEngine::_systemVars.language = cnt;
348 					break;
349 				}
350 	}
351 
352 	// Setup mixer
353 	syncSoundSettings();
354 
355 	_debugger = new Debugger(_skyLogic, _skyMouse, _skyScreen, _skyCompact);
356 	return Common::kNoError;
357 }
358 
initItemList()359 void SkyEngine::initItemList() {
360 	//See List.asm for (cryptic) item # descriptions
361 
362 	for (int i = 0; i < 300; i++)
363 		_itemList[i] = NULL;
364 }
365 
loadFixedItems()366 void SkyEngine::loadFixedItems() {
367 	_itemList[49] = _skyDisk->loadFile(49);
368 	_itemList[50] = _skyDisk->loadFile(50);
369 	_itemList[73] = _skyDisk->loadFile(73);
370 	_itemList[262] = _skyDisk->loadFile(262);
371 
372 	if (!isDemo()) {
373 		_itemList[36] = _skyDisk->loadFile(36);
374 		_itemList[263] = _skyDisk->loadFile(263);
375 		_itemList[264] = _skyDisk->loadFile(264);
376 		_itemList[265] = _skyDisk->loadFile(265);
377 		_itemList[266] = _skyDisk->loadFile(266);
378 		_itemList[267] = _skyDisk->loadFile(267);
379 		_itemList[269] = _skyDisk->loadFile(269);
380 		_itemList[271] = _skyDisk->loadFile(271);
381 		_itemList[272] = _skyDisk->loadFile(272);
382 	}
383 }
384 
fetchItem(uint32 num)385 void *SkyEngine::fetchItem(uint32 num) {
386 	return _itemList[num];
387 }
388 
delay(int32 amount)389 void SkyEngine::delay(int32 amount) {
390 	Common::Event event;
391 
392 	uint32 start = _system->getMillis();
393 	_keyPressed.reset();
394 
395 	if (amount < 0)
396 		amount = 0;
397 
398 	do {
399 		while (_eventMan->pollEvent(event)) {
400 			switch (event.type) {
401 			case Common::EVENT_KEYDOWN:
402 				_keyPressed = event.kbd;
403 				break;
404 			case Common::EVENT_MOUSEMOVE:
405 				if (!(_systemVars.systemFlags & SF_MOUSE_LOCKED))
406 					_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
407 				break;
408 			case Common::EVENT_LBUTTONDOWN:
409 				if (!(_systemVars.systemFlags & SF_MOUSE_LOCKED))
410 					_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
411 				_skyMouse->buttonPressed(2);
412 				break;
413 			case Common::EVENT_RBUTTONDOWN:
414 				if (!(_systemVars.systemFlags & SF_MOUSE_LOCKED))
415 					_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
416 				_skyMouse->buttonPressed(1);
417 				break;
418 			default:
419 				break;
420 			}
421 		}
422 
423 		_system->updateScreen();
424 
425 		if (amount > 0)
426 			_system->delayMillis((amount > 10) ? 10 : amount);
427 
428 	} while (_system->getMillis() < start + amount);
429 }
430 
isDemo()431 bool SkyEngine::isDemo() {
432 	switch (_systemVars.gameVersion) {
433 	case 109: // PC Gamer demo
434 	case 267: // English floppy demo
435 	case 272: // German floppy demo
436 	case 365: // CD demo
437 		return true;
438 	case 288:
439 	case 303:
440 	case 331:
441 	case 348:
442 	case 368:
443 	case 372:
444 		return false;
445 	default:
446 		error("Unknown game version %d", _systemVars.gameVersion);
447 	}
448 }
449 
isCDVersion()450 bool SkyEngine::isCDVersion() {
451 	switch (_systemVars.gameVersion) {
452 	case 109:
453 	case 267:
454 	case 272:
455 	case 288:
456 	case 303:
457 	case 331:
458 	case 348:
459 		return false;
460 	case 365:
461 	case 368:
462 	case 372:
463 		return true;
464 	default:
465 		error("Unknown game version %d", _systemVars.gameVersion);
466 	}
467 }
468 
469 } // End of namespace Sky
470