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 "base/plugins.h"
24
25 #include "common/archive.h"
26 #include "common/config-manager.h"
27 #include "common/debug-channels.h"
28 #include "audio/mixer.h"
29
30 #include "engines/util.h"
31 #include "graphics/surface.h"
32
33 #include "ngi/ngi.h"
34 #include "ngi/detection.h"
35 #include "ngi/gameloader.h"
36 #include "ngi/messages.h"
37 #include "ngi/behavior.h"
38 #include "ngi/modal.h"
39 #include "ngi/input.h"
40 #include "ngi/motion.h"
41 #include "ngi/statics.h"
42 #include "ngi/scenes.h"
43 #include "ngi/floaters.h"
44 #include "ngi/console.h"
45 #include "ngi/constants.h"
46
47 namespace NGI {
48
49 NGIEngine *g_nmi = nullptr;
50 Vars *g_vars = nullptr;
51
NGIEngine(OSystem * syst,const NGIGameDescription * gameDesc)52 NGIEngine::NGIEngine(OSystem *syst, const NGIGameDescription *gameDesc) :
53 Engine(syst),
54 _gameDescription(gameDesc),
55 _rnd("ngi"),
56 _gameProject(nullptr),
57 _modalObject(nullptr),
58 _currSoundList1(),
59 _mapTable() {
60
61 // Setup mixer
62 if (!_mixer->isReady()) {
63 warning("Sound initialization failed.");
64 }
65
66 syncSoundSettings();
67 _sfxVolume = ConfMan.getInt("sfx_volume") * 39 - 10000;
68 _musicVolume = ConfMan.getInt("music_volume");
69
70 setDebugger(new Console(this));
71
72 _gameProjectVersion = 0;
73 _pictureScale = 8;
74 _scrollSpeed = 0;
75 _currSoundListCount = 0;
76
77 _updateTicks = 0;
78 _lastInputTicks = 0;
79 _lastButtonUpTicks = 0;
80
81 _currArchive = 0;
82
83 _soundEnabled = true;
84 _flgSoundList = true;
85
86 _inputController = 0;
87 _inputDisabled = false;
88
89 _normalSpeed = true;
90
91 _currentCheat = -1;
92 _currentCheatPos = 0;
93
94 _liftEnterMQ = 0;
95 _liftExitMQ = 0;
96 _lift = 0;
97 _lastLiftButton = nullptr;
98 _liftX = 0;
99 _liftY = 0;
100
101 _gameContinue = true;
102 _needRestart = false;
103 _flgPlayIntro = true;
104 _gamePaused = false;
105 _inputArFlag = false;
106 _recordEvents = false;
107 _mainMenu_debugEnabled = false;
108
109 _flgGameIsRunning = true;
110
111 _isProcessingMessages = false;
112
113 _musicAllowed = -1;
114 _musicGameVar = 0;
115 _musicMinDelay = 0;
116 _musicMaxDelay = 0;
117 _musicLocal = 0;
118 _trackStartDelay = 0;
119
120 _stream2playing = false;
121
122 _numSceneTracks = 0;
123 _sceneTrackHasSequence = false;
124 _sceneTrackIsPlaying = false;
125
126 _aniMan = nullptr;
127 _aniMan2 = nullptr;
128 _currentScene = nullptr;
129 _loaderScene = nullptr;
130 _scene2 = nullptr;
131 _scene3 = nullptr;
132 _messageHandlers = nullptr;
133
134 _updateScreenCallback = nullptr;
135 _updateCursorCallback = nullptr;
136
137 _msgX = 0;
138 _msgY = 0;
139 _msgObjectId2 = 0;
140 _msgId = 0;
141 _mouseVirtX = 0;
142 _mouseVirtY = 0;
143
144 _currSelectedInventoryItemId = 0;
145
146 _cursorId = 0;
147
148 _keyState = Common::KEYCODE_INVALID;
149 _buttonState = 0;
150
151 _updateFlag = true;
152 _flgCanOpenMap = true;
153
154 _sceneWidth = 1;
155 _sceneHeight = 1;
156
157 _inventoryScene = nullptr;
158 _inventory = nullptr;
159
160 _minCursorId = 0xffff;
161 _maxCursorId = 0;
162 _objectAtCursor = 0;
163 _objectIdAtCursor = 0;
164
165 _arcadeOverlay = nullptr;
166 _arcadeOverlayHelper = nullptr;
167 _arcadeOverlayX = 0;
168 _arcadeOverlayY = 0;
169 _arcadeOverlayMidX = 0;
170 _arcadeOverlayMidY = 0;
171
172 _isSaveAllowed = true;
173
174 g_nmi = this;
175 g_vars = new Vars;
176 }
177
~NGIEngine()178 NGIEngine::~NGIEngine() {
179 delete g_vars;
180 g_vars = nullptr;
181 }
182
restartGame()183 void NGIEngine::restartGame() {
184 _floaters->stopAll();
185
186 clearGlobalMessageQueueList();
187 clearMessages();
188
189 initObjectStates();
190
191 if (_scene2) {
192 _scene2->getAniMan();
193 _scene2 = nullptr;
194 }
195
196 if (_currentScene) {
197 _gameLoader->unloadScene(_currentScene->_sceneId);
198
199 _currentScene = nullptr;
200 }
201
202 _gameLoader->restoreDefPicAniInfos();
203
204 getGameLoaderInventory()->clear();
205 getGameLoaderInventory()->addItem(ANI_INV_MAP, 1);
206 getGameLoaderInventory()->rebuildItemRects();
207
208 initMap();
209
210 if (_flgPlayIntro) {
211 _gameLoader->loadScene(SC_INTRO1);
212 _gameLoader->gotoScene(SC_INTRO1, TrubaUp);
213 } else {
214 _gameLoader->loadScene(SC_1);
215 _gameLoader->gotoScene(SC_1, TrubaLeft);
216 }
217 }
218
shouldQuit()219 bool NGIEngine::shouldQuit() {
220 return !_gameContinue || Engine::shouldQuit();
221 }
222
loadGameState(int slot)223 Common::Error NGIEngine::loadGameState(int slot) {
224 deleteModalObject();
225
226 if (_gameLoader->readSavegame(getSavegameFile(slot)))
227 return Common::kNoError;
228 else
229 return Common::kUnknownError;
230 }
231
saveGameState(int slot,const Common::String & description,bool isAutosave)232 Common::Error NGIEngine::saveGameState(int slot, const Common::String &description, bool isAutosave) {
233 if (_gameLoader->writeSavegame(_currentScene, getSavegameFile(slot), description))
234 return Common::kNoError;
235 else
236 return Common::kUnknownError;
237 }
238
getSaveStateName(int slot) const239 Common::String NGIEngine::getSaveStateName(int slot) const {
240 return Common::String::format("%s.s%02d", getGameId(), slot);
241 }
242
run()243 Common::Error NGIEngine::run() {
244 const Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0);
245 // Initialize backend
246 initGraphics(800, 600, &format);
247
248 _backgroundSurface.create(800, 600, format);
249 _origFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
250
251 _globalMessageQueueList.reset(new GlobalMessageQueueList);
252 _behaviorManager.reset(new BehaviorManager);
253
254 _sceneRect.left = 0;
255 _sceneRect.top = 0;
256 _sceneRect.right = 799;
257 _sceneRect.bottom = 599;
258
259 _floaters.reset(new Floaters);
260 _aniHandler.reset(new AniHandler);
261 _globalPalette = &_defaultPalette;
262
263 _isSaveAllowed = false;
264
265 if (debugChannelSet(-1, kDebugXML))
266 loadGameObjH();
267
268 int scene = 0;
269 if (ConfMan.hasKey("boot_param"))
270 scene = convertScene(ConfMan.getInt("boot_param"));
271
272 if (ConfMan.hasKey("save_slot"))
273 scene = -1;
274
275 switch (getGameGID()) {
276 case GID_FULLPIPE:
277 if (!loadGam("fullpipe.gam", scene))
278 return Common::kNoGameDataFoundError;
279 break;
280 case GID_MDREAM:
281 if (!loadGam("new.gam", scene))
282 return Common::kNoGameDataFoundError;
283 break;
284 default:
285 warning("Unknown GID");
286 return Common::kUnsupportedGameidError;
287 }
288
289 if (ConfMan.hasKey("save_slot")) {
290 loadGameState(ConfMan.getInt("save_slot"));
291 }
292
293 #if 0
294 loadAllScenes();
295 #endif
296
297 int time1 = g_nmi->_system->getMillis();
298
299 // Center mouse
300 _system->warpMouse(400, 300);
301
302 for (;;) {
303 updateEvents();
304 if (shouldQuit()) {
305 break;
306 }
307
308 int time2 = g_nmi->_system->getMillis();
309
310 // 30fps
311 if (time2 - time1 >= 33 || !_normalSpeed) {
312 time1 = time2;
313 updateScreen();
314 }
315
316 if (_needRestart) {
317 delete _modalObject;
318 freeGameLoader();
319 _currentScene = nullptr;
320 _updateTicks = 0;
321 _globalPalette = &_defaultPalette;
322
323 loadGam("fullpipe.gam");
324 _needRestart = false;
325 }
326
327 _system->delayMillis(5);
328 _system->updateScreen();
329 }
330
331 delete _modalObject;
332 freeGameLoader();
333
334 cleanup();
335 _backgroundSurface.free();
336
337 return Common::kNoError;
338 }
339
updateEvents()340 void NGIEngine::updateEvents() {
341 Common::Event event;
342 Common::EventManager *eventMan = _system->getEventManager();
343 ExCommand *ex;
344
345 while (eventMan->pollEvent(event)) {
346 switch (event.type) {
347 case Common::EVENT_KEYDOWN:
348 _keyState = event.kbd.keycode;
349
350 switch (event.kbd.keycode) {
351 case Common::KEYCODE_SPACE:
352 if (_gamePaused) {
353 if (_modalObject) {
354 if (_modalObject->init(42)) {
355 _modalObject->update();
356 } else {
357 deleteModalObject();
358 }
359 } else {
360 _gameLoader->updateSystems(42);
361 }
362 return;
363 }
364
365 ex = new ExCommand(0, 17, 36, 0, 0, 0, 1, 0, 0, 0);
366 ex->_param = 32;
367 ex->_excFlags |= 3;
368 ex->handle();
369 break;
370 case Common::KEYCODE_s:
371 if (_gamePaused) {
372 _gamePaused = 0;
373 _flgGameIsRunning = true;
374 return;
375 }
376
377 ex = new ExCommand(0, 17, 36, 0, 0, 0, 1, 0, 0, 0);
378 ex->_param = event.kbd.keycode;
379 ex->_excFlags |= 3;
380 ex->handle();
381 break;
382 case Common::KEYCODE_q:
383 return;
384 break;
385 default:
386 ex = new ExCommand(0, 17, 36, 0, 0, 0, 1, 0, 0, 0);
387 ex->_param = event.kbd.keycode;
388 ex->_excFlags |= 3;
389 ex->handle();
390 break;
391 }
392 break;
393 case Common::EVENT_KEYUP:
394 if (!_inputArFlag) {
395 ex = new ExCommand(0, 17, 37, 0, 0, 0, 1, 0, 0, 0);
396 ex->_excFlags |= 3;
397 ex->handle();
398 }
399 _keyState = Common::KEYCODE_INVALID;
400 break;
401 case Common::EVENT_MOUSEMOVE:
402 if (_recordEvents) {
403 ex = new ExCommand(0, 17, 31, event.mouse.x, event.mouse.y, 0, 1, 0, 0, 0);
404 ex->_excFlags |= 3;
405 ex->handle();
406 }
407
408 _mouseScreenPos = event.mouse;
409 break;
410 case Common::EVENT_QUIT:
411 return;
412 case Common::EVENT_RBUTTONDOWN:
413 if (!_inputArFlag && (_updateTicks - _lastInputTicks) >= 2) {
414 ex = new ExCommand(0, 17, 107, event.mouse.x, event.mouse.y, 0, 1, 0, 0, 0);
415 ex->_excFlags |= 3;
416 _lastInputTicks = _updateTicks;
417 ex->handle();
418 }
419 _mouseScreenPos = event.mouse;
420 break;
421 case Common::EVENT_LBUTTONDOWN:
422 if (!_inputArFlag && (_updateTicks - _lastInputTicks) >= 2) {
423 ex = new ExCommand(0, 17, 29, event.mouse.x, event.mouse.y, 0, 1, 0, 0, 0);
424
425 ex->_sceneClickX = _sceneRect.left + ex->_x;
426 ex->_sceneClickY = _sceneRect.top + ex->_y;
427 ex->_param = getGameLoaderInventory()->getSelectedItemId();
428 ex->_excFlags |= 3;
429 _lastInputTicks = _updateTicks;
430 ex->handle();
431 }
432 _mouseScreenPos = event.mouse;
433 break;
434 case Common::EVENT_LBUTTONUP:
435 if (!_inputArFlag && (_updateTicks - _lastButtonUpTicks) >= 2) {
436 ex = new ExCommand(0, 17, 30, 0, 0, 0, 1, 0, 0, 0);
437 ex->_excFlags |= 3;
438 _lastButtonUpTicks = _updateTicks;
439 ex->handle();
440 }
441 _mouseScreenPos = event.mouse;
442 break;
443 default:
444 break;
445 }
446 }
447
448 // pollEvent() is implemented only for video player. So skip it.
449 //if (event.kbd.keycode == MSG_SC11_SHOWSWING && _modalObject) {
450 // _modalObject->pollEvent();
451 //}
452 }
453
freeGameLoader()454 void NGIEngine::freeGameLoader() {
455 setCursor(0);
456 _floaters->stopAll();
457 _gameLoader.reset();
458 _currentScene = 0;
459 _scene2 = 0;
460 _loaderScene = 0;
461 }
462
cleanup()463 void NGIEngine::cleanup() {
464 //cleanRecorder();
465 clearMessageHandlers();
466 clearMessages();
467 _globalMessageQueueList->compact();
468
469 for (uint i = 0; i < _globalMessageQueueList->size(); i++)
470 delete (*_globalMessageQueueList)[i];
471
472 stopAllSoundStreams();
473 }
474
deleteModalObject()475 void NGIEngine::deleteModalObject() {
476 if (!_modalObject)
477 return;
478
479 _modalObject->saveload();
480 BaseModalObject *tmp = _modalObject->_parentObj;
481
482 delete _modalObject;
483
484 _modalObject = tmp;
485 }
486
updateScreen()487 void NGIEngine::updateScreen() {
488 debugC(4, kDebugDrawing, "NGIEngine::updateScreen()");
489
490 _mouseVirtX = _mouseScreenPos.x + _sceneRect.left;
491 _mouseVirtY = _mouseScreenPos.y + _sceneRect.top;
492
493 //if (inputArFlag)
494 // updateGame_inputArFlag();
495
496 if (_modalObject || (_flgGameIsRunning && (_gameLoader->updateSystems(42), _modalObject != 0))) {
497 if (_flgGameIsRunning) {
498 if (_modalObject->init(42)) {
499 _modalObject->update();
500 } else {
501 deleteModalObject();
502 }
503 }
504 } else if (_currentScene) {
505 _currentScene->draw();
506
507 if (_inventoryScene)
508 _inventory->draw();
509
510 if (_updateScreenCallback)
511 _updateScreenCallback();
512
513 //if (inputArFlag && _currentScene) {
514 // vrtTextOut(*(_DWORD *)g_vrtHandle, smallNftData, "DEMO", 4, 380, 580);
515 // vrtTextOut(*(_DWORD *)g_vrtHandle, smallNftData, "Alt+F4 - exit", 14, 695, 580);
516 //}
517 } else {
518 //vrtRectangle(*(_DWORD *)g_vrtHandle, 0, 0, 0, 800, 600);
519 }
520 _inputController->drawCursor(_mouseScreenPos.x, _mouseScreenPos.y);
521
522 ++_updateTicks;
523 }
524
getObjectEnumState(const Common::String & name,const char * state)525 int NGIEngine::getObjectEnumState(const Common::String &name, const char *state) {
526 GameVar *var = _gameLoader->_gameVar->getSubVarByName("OBJSTATES");
527
528 if (!var) {
529 var = _gameLoader->_gameVar->addSubVarAsInt("OBJSTATES", 0);
530 }
531
532 var = var->getSubVarByName(name);
533 if (var) {
534 var = var->getSubVarByName("ENUMSTATES");
535 if (var)
536 return var->getSubVarAsInt(state);
537 }
538
539 return 0;
540 }
541
getObjectState(const Common::String & objname)542 int NGIEngine::getObjectState(const Common::String &objname) {
543 GameVar *var = _gameLoader->_gameVar->getSubVarByName("OBJSTATES");
544
545 if (var)
546 return var->getSubVarAsInt(objname);
547
548 return 0;
549 }
550
setObjectState(const Common::String & name,int state)551 void NGIEngine::setObjectState(const Common::String &name, int state) {
552 GameVar *var = _gameLoader->_gameVar->getSubVarByName("OBJSTATES");
553
554 if (!var) {
555 var = _gameLoader->_gameVar->addSubVarAsInt("OBJSTATES", 0);
556 }
557
558 var->setSubVarAsInt(name, state);
559 }
560
disableSaves(ExCommand * ex)561 void NGIEngine::disableSaves(ExCommand *ex) {
562 if (_isSaveAllowed) {
563 _isSaveAllowed = false;
564
565 if (_globalMessageQueueList->size() && (*_globalMessageQueueList)[0] != 0) {
566 for (uint i = 0; i < _globalMessageQueueList->size(); i++) {
567 if ((*_globalMessageQueueList)[i]->_flags & 1)
568 if ((*_globalMessageQueueList)[i]->_id != ex->_parId && !(*_globalMessageQueueList)[i]->_isFinished)
569 return;
570 }
571 }
572
573 // Original was makeing a save on every room entering
574 if (_currentScene) {
575 _gameLoader->saveScenePicAniInfos(_currentScene->_sceneId);
576 // _gameLoader->writeSavegame(_currentScene, "savetmp.sav");
577 }
578 }
579 }
580
isSaveAllowed()581 bool NGIEngine::isSaveAllowed() {
582 if (!g_nmi->_isSaveAllowed)
583 return false;
584
585 bool allowed = true;
586
587 for (Common::Array<MessageQueue *>::iterator s = g_nmi->_globalMessageQueueList->begin(); s != g_nmi->_globalMessageQueueList->end(); ++s) {
588 if (!(*s)->_isFinished && ((*s)->getFlags() & 1))
589 allowed = false;
590 }
591
592 return allowed;
593 }
594
595
596 } // End of namespace NGI
597