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