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 "illusions/bbdou/illusions_bbdou.h"
24 #include "illusions/bbdou/bbdou_menukeys.h"
25 #include "illusions/bbdou/bbdou_videoplayer.h"
26 #include "illusions/bbdou/gamestate_bbdou.h"
27 #include "illusions/bbdou/menusystem_bbdou.h"
28 #include "illusions/actor.h"
29 #include "illusions/camera.h"
30 #include "illusions/cursor.h"
31 #include "illusions/dictionary.h"
32 #include "illusions/fileresourcereader.h"
33 #include "illusions/graphics.h"
34 #include "illusions/input.h"
35 #include "illusions/resources/actorresource.h"
36 #include "illusions/resources/backgroundresource.h"
37 #include "illusions/resources/fontresource.h"
38 #include "illusions/resources/scriptresource.h"
39 #include "illusions/resources/soundresource.h"
40 #include "illusions/resources/talkresource.h"
41 #include "illusions/resourcesystem.h"
42 #include "illusions/screen.h"
43 #include "illusions/screentext.h"
44 #include "illusions/scriptstack.h"
45 #include "illusions/bbdou/scriptopcodes_bbdou.h"
46 #include "illusions/sound.h"
47 #include "illusions/specialcode.h"
48 #include "illusions/bbdou/bbdou_specialcode.h"
49 #include "illusions/thread.h"
50 #include "illusions/time.h"
51 #include "illusions/updatefunctions.h"
52 
53 #include "illusions/threads/abortablethread.h"
54 #include "illusions/threads/scriptthread.h"
55 #include "illusions/threads/talkthread.h"
56 #include "illusions/threads/timerthread.h"
57 
58 #include "audio/audiostream.h"
59 #include "common/config-manager.h"
60 #include "common/debug-channels.h"
61 #include "common/error.h"
62 #include "common/fs.h"
63 #include "common/timer.h"
64 #include "engines/util.h"
65 #include "graphics/cursorman.h"
66 #include "graphics/font.h"
67 #include "graphics/fontman.h"
68 #include "graphics/palette.h"
69 #include "graphics/surface.h"
70 
71 namespace Illusions {
72 
73 // ActiveScenes
74 
ActiveScenes()75 ActiveScenes::ActiveScenes() {
76 	clear();
77 }
78 
clear()79 void ActiveScenes::clear() {
80 	_stack.clear();
81 }
82 
push(uint32 sceneId)83 void ActiveScenes::push(uint32 sceneId) {
84 	ActiveScene activeScene;
85 	activeScene._sceneId = sceneId;
86 	activeScene._pauseCtr = 0;
87 	_stack.push(activeScene);
88 }
89 
pop()90 void ActiveScenes::pop() {
91 	_stack.pop();
92 }
93 
pauseActiveScene()94 void ActiveScenes::pauseActiveScene() {
95 	++_stack.top()._pauseCtr;
96 }
97 
unpauseActiveScene()98 void ActiveScenes::unpauseActiveScene() {
99 	--_stack.top()._pauseCtr;
100 }
101 
getActiveScenesCount()102 uint ActiveScenes::getActiveScenesCount() {
103 	return _stack.size();
104 }
105 
getActiveSceneInfo(uint index,uint32 * sceneId,int * pauseCtr)106 void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
107 	if (sceneId)
108 		*sceneId = _stack[index - 1]._sceneId;
109 	if (pauseCtr)
110 		*pauseCtr = _stack[index - 1]._pauseCtr;
111 }
112 
getCurrentScene()113 uint32 ActiveScenes::getCurrentScene() {
114 	if (_stack.size() > 0)
115 		return _stack.top()._sceneId;
116 	return 0;
117 }
118 
isSceneActive(uint32 sceneId)119 bool ActiveScenes::isSceneActive(uint32 sceneId) {
120 	for (uint i = 0; i < _stack.size(); ++i) {
121 		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
122 			return true;
123 	}
124 	return false;
125 }
126 
127 // IllusionsEngine_BBDOU
128 
IllusionsEngine_BBDOU(OSystem * syst,const IllusionsGameDescription * gd)129 IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd)
130 	: IllusionsEngine(syst, gd) {
131 }
132 
run()133 Common::Error IllusionsEngine_BBDOU::run() {
134 
135 	// Init search paths
136 	const Common::FSNode gameDataDir(ConfMan.get("path"));
137 	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
138 	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
139 	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
140 	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
141 	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
142 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
143 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
144 
145 	_dict = new Dictionary();
146 
147 	_resReader = new ResourceReaderFileReader();
148 
149 	_resSys = new ResourceSystem(this);
150 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
151 	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
152 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
153 	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
154 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
155 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
156 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
157 	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
158 
159 	_screen = new Screen16Bit(this, 640, 480);
160 	_screenPalette = new NullScreenPalette();
161 	_screenText = new ScreenText(this);
162 	_input = new Input();
163 	_actorInstances = new ActorInstanceList(this);
164 	_backgroundInstances = new BackgroundInstanceList(this);
165 	_camera = new Camera(this);
166 	_controls = new Controls(this);
167 	_cursor = new Cursor(this);
168 	_talkItems = new TalkInstanceList(this);
169 	_triggerFunctions = new TriggerFunctions();
170 	_threads = new ThreadList(this);
171 	_updateFunctions = new UpdateFunctions();
172 	_soundMan = new SoundMan(this);
173 	_menuSystem = new BBDOUMenuSystem(this);
174 	_videoPlayer = new BBDOUVideoPlayer(this);
175 	_gameState = new BBDOU_GameState(this);
176 	_menuKeys = new BBDOUMenuKeys(this);
177 
178 	_screen->setColorKey1(0xF81F);
179 
180 	initInput();
181 
182 	initUpdateFunctions();
183 
184 	_fader = 0;
185 
186 	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
187 	_stack = new ScriptStack();
188 
189 	_resGetCtr = 0;
190 	_unpauseControlActorFlag = false;
191 	_lastUpdateTime = 0;
192 
193 	_pauseCtr = 0;
194 	_field8 = 1;
195 	_fieldA = 0;
196 	ConfMan.registerDefault("talkspeed", 240);
197 	_subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
198 
199 	_globalSceneId = 0x00010003;
200 
201 	setDefaultTextCoords();
202 
203 	_resSys->loadResource(0x000D0001, 0, 0);
204 
205 	_doScriptThreadInit = false;
206 	startScriptThread(0x00020004, 0, 0, 0, 0);
207 	_doScriptThreadInit = true;
208 
209 	if (ConfMan.hasKey("save_slot")) {
210 		loadGameState(ConfMan.getInt("save_slot"));
211 	}
212 
213 	_walkthroughStarted = false;
214 	_canResumeFromSavegame = false;
215 
216 	while (!shouldQuit()) {
217 		if (_walkthroughStarted) {
218 			//enterScene(0x10003, 0);
219 			startScriptThread(0x00020404, 0, 0, 0, 0);
220 			_walkthroughStarted = false;
221 		}
222 		if (_resumeFromSavegameRequested && _canResumeFromSavegame) {
223 			resumeFromSavegame();
224 			_resumeFromSavegameRequested = false;
225 		}
226 		runUpdateFunctions();
227 		_system->updateScreen();
228 		updateEvents();
229 	}
230 
231 	delete _stack;
232 	delete _scriptOpcodes;
233 
234 	delete _menuKeys;
235 	delete _gameState;
236 	delete _videoPlayer;
237 	delete _menuSystem;
238 	delete _soundMan;
239 	delete _updateFunctions;
240 	delete _threads;
241 	delete _triggerFunctions;
242 	delete _talkItems;
243 	delete _cursor;
244 	delete _controls;
245 	delete _camera;
246 	delete _backgroundInstances;
247 	delete _actorInstances;
248 	delete _input;
249 	delete _screenText;
250 	delete _screen;
251 	delete _resSys;
252 	delete _resReader;
253 	delete _dict;
254 
255 	debug("Ok");
256 
257 	return Common::kNoError;
258 }
259 
hasFeature(EngineFeature f) const260 bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
261 	return
262 		(f == kSupportsRTL) ||
263 		(f == kSupportsLoadingDuringRuntime) ||
264 		(f == kSupportsSavingDuringRuntime);
265 }
266 
initInput()267 void IllusionsEngine_BBDOU::initInput() {
268 	_input->setInputEvent(kEventLeftClick, 0x01)
269 		.addMouseButton(MOUSE_LEFT_BUTTON)
270 		.addKey(Common::KEYCODE_RETURN);
271 	_input->setInputEvent(kEventRightClick, 0x02)
272 		.addMouseButton(MOUSE_RIGHT_BUTTON);
273 	_input->setInputEvent(kEventInventory, 0x04)
274 		.addMouseButton(MOUSE_RIGHT_BUTTON)
275 		.addKey(Common::KEYCODE_TAB);
276 	_input->setInputEvent(kEventAbort, 0x08)
277 		.addKey(Common::KEYCODE_ESCAPE);
278 	_input->setInputEvent(kEventSkip, 0x10)
279 		.addKey(Common::KEYCODE_SPACE);
280 	_input->setInputEvent(kEventF1, 0x20)
281 		.addKey(Common::KEYCODE_F1);
282 	_input->setInputEvent(kEventUp, 0x40)
283 		.addKey(Common::KEYCODE_UP);
284 	_input->setInputEvent(kEventDown, 0x80)
285 		.addMouseButton(MOUSE_RIGHT_BUTTON)
286 		.addKey(Common::KEYCODE_DOWN);
287 }
288 
289 #define UPDATEFUNCTION(priority, sceneId, callback) \
290 	_updateFunctions->add(priority, sceneId, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
291 		(this, &IllusionsEngine_BBDOU::callback));
292 
initUpdateFunctions()293 void IllusionsEngine_BBDOU::initUpdateFunctions() {
294 	UPDATEFUNCTION(30, 0, updateScript);
295 	UPDATEFUNCTION(50, 0, updateActors);
296 	UPDATEFUNCTION(60, 0, updateMenuKeys);
297 	UPDATEFUNCTION(60, 0, updateSequences);
298 	UPDATEFUNCTION(70, 0, updateGraphics);
299 	UPDATEFUNCTION(70, 0, updateVideoPlayer);
300 	UPDATEFUNCTION(90, 0, updateSprites);
301 	UPDATEFUNCTION(120, 0, updateSoundMan);
302 }
303 
304 #undef UPDATEFUNCTION
305 
updateScript(uint flags)306 int IllusionsEngine_BBDOU::updateScript(uint flags) {
307 	_threads->updateThreads();
308 	return kUFNext;
309 }
310 
updateMenuKeys(uint flags)311 int IllusionsEngine_BBDOU::updateMenuKeys(uint flags) {
312 	_menuKeys->update();
313 	return kUFNext;
314 }
315 
causeIsDeclared(uint32 sceneId,uint32 verbId,uint32 objectId2,uint32 objectId)316 bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
317 	uint32 codeOffs;
318 	return
319 		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
320 		findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
321 }
322 
causeDeclare(uint32 verbId,uint32 objectId2,uint32 objectId,TriggerFunctionCallback * callback)323 void IllusionsEngine_BBDOU::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
324 	_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
325 }
326 
causeTrigger(uint32 sceneId,uint32 verbId,uint32 objectId2,uint32 objectId,uint32 callingThreadId)327 uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
328 	uint32 codeOffs;
329 	uint32 causeThreadId = 0;
330 	TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
331 	if (triggerFunction) {
332 		triggerFunction->run(callingThreadId);
333 	} else if (findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
334 		causeThreadId = startTempScriptThread(_scriptResource->getCode(codeOffs),
335 			callingThreadId, verbId, objectId2, objectId);
336 	}
337 	return causeThreadId;
338 }
339 
updateVideoPlayer(uint flags)340 int IllusionsEngine_BBDOU::updateVideoPlayer(uint flags) {
341 	if (_videoPlayer->isPlaying())
342 		_videoPlayer->update();
343 	return kUFNext;
344 }
345 
playVideo(uint32 videoId,uint32 objectId,uint32 priority,uint32 callingThreadId)346 void IllusionsEngine_BBDOU::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId) {
347 	_videoPlayer->start(videoId, objectId, priority, callingThreadId);
348 }
349 
isVideoPlaying()350 bool IllusionsEngine_BBDOU::isVideoPlaying() {
351 	return _videoPlayer->isPlaying();
352 }
353 
setDefaultTextCoords()354 void IllusionsEngine_BBDOU::setDefaultTextCoords() {
355 	WidthHeight dimensions;
356 	dimensions._width = 480;
357 	dimensions._height = 48;
358 	Common::Point pt(320, 448);
359 	setDefaultTextDimensions(dimensions);
360 	setDefaultTextPosition(pt);
361 }
362 
loadSpecialCode(uint32 resId)363 void IllusionsEngine_BBDOU::loadSpecialCode(uint32 resId) {
364 	_specialCode = new BbdouSpecialCode(this);
365 	_specialCode->init();
366 }
367 
unloadSpecialCode(uint32 resId)368 void IllusionsEngine_BBDOU::unloadSpecialCode(uint32 resId) {
369 	delete _specialCode;
370 	_specialCode = 0;
371 }
372 
notifyThreadId(uint32 & threadId)373 void IllusionsEngine_BBDOU::notifyThreadId(uint32 &threadId) {
374 	if (threadId) {
375 		uint32 tempThreadId = threadId;
376 		threadId = 0;
377 		_threads->notifyId(tempThreadId);
378 	}
379 }
380 
testMainActorFastWalk(Control * control)381 bool IllusionsEngine_BBDOU::testMainActorFastWalk(Control *control) {
382 	return false;
383 }
384 
testMainActorCollision(Control * control)385 bool IllusionsEngine_BBDOU::testMainActorCollision(Control *control) {
386 	// Not used in BBDOU
387 	return false;
388 }
389 
getObjectControl(uint32 objectId)390 Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
391 	return _dict->getObjectControl(objectId);
392 }
393 
getNamedPointPosition(uint32 namedPointId)394 Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
395 	Common::Point pt;
396 	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt) ||
397 		_actorInstances->findNamedPoint(namedPointId, pt) ||
398 		_controls->findNamedPoint(namedPointId, pt))
399 		return pt;
400 	// TODO
401 	switch (namedPointId) {
402 	case 0x70001:
403 		return Common::Point(0, 0);
404 	case 0x70002:
405 		return Common::Point(640, 0);
406 	case 0x70023:
407 		return Common::Point(320, 240);
408 	}
409 	debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
410 	return Common::Point(0, 0);
411 }
412 
getPriorityFromBase(int16 priority)413 uint32 IllusionsEngine_BBDOU::getPriorityFromBase(int16 priority) {
414 	return 32000000 * priority;
415 }
416 
getCurrentScene()417 uint32 IllusionsEngine_BBDOU::getCurrentScene() {
418 	return _activeScenes.getCurrentScene();
419 }
420 
getPrevScene()421 uint32 IllusionsEngine_BBDOU::getPrevScene() {
422 	return _prevSceneId;
423 }
424 
isCursorObject(uint32 actorTypeId,uint32 objectId)425 bool IllusionsEngine_BBDOU::isCursorObject(uint32 actorTypeId, uint32 objectId) {
426 	return actorTypeId == 0x50001 && objectId == Illusions::CURSOR_OBJECT_ID;
427 }
428 
setCursorControlRoutine(Control * control)429 void IllusionsEngine_BBDOU::setCursorControlRoutine(Control *control) {
430 	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_BBDOU>
431 		(this, &IllusionsEngine_BBDOU::cursorControlRoutine));
432 }
433 
placeCursorControl(Control * control,uint32 sequenceId)434 void IllusionsEngine_BBDOU::placeCursorControl(Control *control, uint32 sequenceId) {
435 	_cursor->place(control, sequenceId);
436 }
437 
setCursorControl(Control * control)438 void IllusionsEngine_BBDOU::setCursorControl(Control *control) {
439 	_cursor->setControl(control);
440 }
441 
showCursor()442 void IllusionsEngine_BBDOU::showCursor() {
443 	_cursor->show();
444 }
445 
hideCursor()446 void IllusionsEngine_BBDOU::hideCursor() {
447 	_cursor->hide();
448 }
449 
cursorControlRoutine(Control * control,uint32 deltaTime)450 void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
451 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
452 	if (control->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
453 		switch (_cursor->_status) {
454 		case 2:
455 			// Unused nullsub_1(control);
456 			break;
457 		case 3:
458 			_menuSystem->update(control);
459 			break;
460 		}
461 	}
462 }
463 
startScriptThreadSimple(uint32 threadId,uint32 callingThreadId)464 void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
465 	startScriptThread(threadId, callingThreadId, 0, 0, 0);
466 }
467 
startScriptThread(uint32 threadId,uint32 callingThreadId,uint32 value8,uint32 valueC,uint32 value10)468 void IllusionsEngine_BBDOU::startScriptThread(uint32 threadId, uint32 callingThreadId,
469 	uint32 value8, uint32 valueC, uint32 value10) {
470 	if (threadId == 0x0002041E && ConfMan.hasKey("save_slot")) {
471 		// Skip intro videos when loading a savegame from the launcher (kludge)
472 		notifyThreadId(callingThreadId);
473 		return;
474 	}
475 	debug(2, "Starting script thread %08X", threadId);
476 	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
477 	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
478 }
479 
startAnonScriptThread(int32 threadId,uint32 callingThreadId,uint32 value8,uint32 valueC,uint32 value10)480 void IllusionsEngine_BBDOU::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
481 	uint32 value8, uint32 valueC, uint32 value10) {
482 	debug(2, "Starting anonymous script thread %08X", threadId);
483 	uint32 tempThreadId = newTempThreadId();
484 	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
485 	scriptCodeIp = _scriptResource->getThreadCode(threadId);
486 	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
487 }
488 
startAbortableTimerThread(uint32 duration,uint32 threadId)489 uint32 IllusionsEngine_BBDOU::startAbortableTimerThread(uint32 duration, uint32 threadId) {
490 	return newTimerThread(duration, threadId, true);
491 }
492 
startTimerThread(uint32 duration,uint32 threadId)493 uint32 IllusionsEngine_BBDOU::startTimerThread(uint32 duration, uint32 threadId) {
494 	return newTimerThread(duration, threadId, false);
495 }
496 
startAbortableThread(byte * scriptCodeIp1,byte * scriptCodeIp2,uint32 callingThreadId)497 uint32 IllusionsEngine_BBDOU::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
498 	uint32 tempThreadId = newTempThreadId();
499 	debug(2, "Starting abortable thread %08X", tempThreadId);
500 	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
501 	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
502 		scriptThreadId, scriptCodeIp2);
503 	_threads->startThread(abortableThread);
504 	return tempThreadId;
505 }
506 
startTalkThread(int16 duration,uint32 objectId,uint32 talkId,uint32 sequenceId1,uint32 sequenceId2,uint32 namedPointId,uint32 callingThreadId)507 uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
508 	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
509 	debug(2, "Starting talk thread");
510 	uint32 tempThreadId = newTempThreadId();
511 	_threads->endTalkThreadsNoNotify();
512 	TalkThread *talkThread = new TalkThread(this, tempThreadId, callingThreadId, 0,
513 		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
514 	_threads->startThread(talkThread);
515 	return tempThreadId;
516 }
517 
startTempScriptThread(byte * scriptCodeIp,uint32 callingThreadId,uint32 value8,uint32 valueC,uint32 value10)518 uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
519 	uint32 value8, uint32 valueC, uint32 value10) {
520 	uint32 tempThreadId = newTempThreadId();
521 	debug(2, "Starting temp script thread %08X", tempThreadId);
522 	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
523 	return tempThreadId;
524 }
525 
newScriptThread(uint32 threadId,uint32 callingThreadId,uint notifyFlags,byte * scriptCodeIp,uint32 value8,uint32 valueC,uint32 value10)526 void IllusionsEngine_BBDOU::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
527 	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
528 	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
529 		scriptCodeIp, value8, valueC, value10);
530 	_threads->startThread(scriptThread);
531 	if (_pauseCtr > 0)
532 		scriptThread->pause();
533 	if (_doScriptThreadInit) {
534 		int updateResult = kTSRun;
535 		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield) {
536 			updateResult = scriptThread->update();
537 		}
538 	}
539 }
540 
newTimerThread(uint32 duration,uint32 callingThreadId,bool isAbortable)541 uint32 IllusionsEngine_BBDOU::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
542 	uint32 tempThreadId = newTempThreadId();
543 	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
544 		duration, isAbortable);
545 	_threads->startThread(timerThread);
546 	return tempThreadId;
547 }
548 
newTempThreadId()549 uint32 IllusionsEngine_BBDOU::newTempThreadId() {
550 	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
551 	if (threadId > 65535) {
552 		_nextTempThreadId = 0;
553 		threadId = 2 * _scriptResource->_codeCount;
554 	}
555 	++_nextTempThreadId;
556 	return 0x00020000 | threadId;
557 }
558 
enterScene(uint32 sceneId,uint32 threadId)559 bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
560 	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
561 	if (!sceneInfo) {
562 		dumpActiveScenes(_globalSceneId, threadId);
563 		sceneId = _theSceneId;
564 	}
565 	_activeScenes.push(sceneId);
566 	if (sceneId == 0x0001007D) {
567 		// Savegame loading from the ScummVM GUI or command line is only
568 		// possible after resources have been initialized by the startup script.
569 		// Once that script is done, it switches to the start menu scene.
570 		// After that the game is ready and a savegame can finally be loaded.
571 		_canResumeFromSavegame = true;
572 	}
573 	return sceneInfo != 0;
574 }
575 
exitScene(uint32 threadId)576 void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
577 	uint32 sceneId = _activeScenes.getCurrentScene();
578 	_updateFunctions->terminateByScene(sceneId);
579 	_threads->terminateThreadsBySceneId(sceneId, threadId);
580 	_controls->destroyControlsBySceneId(sceneId);
581 	_triggerFunctions->removeBySceneId(sceneId);
582 	_resSys->unloadResourcesBySceneId(sceneId);
583 	_activeScenes.pop();
584 }
585 
enterPause(uint32 threadId)586 void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
587 	uint32 sceneId = _activeScenes.getCurrentScene();
588 	_camera->pushCameraMode();
589 	_threads->suspendThreadsBySceneId(sceneId, threadId);
590 	_controls->pauseControlsBySceneId(sceneId);
591 	_actorInstances->pauseBySceneId(sceneId);
592 	_backgroundInstances->pauseBySceneId(sceneId);
593 	_activeScenes.pauseActiveScene();
594 }
595 
leavePause(uint32 threadId)596 void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
597 	uint32 sceneId = _activeScenes.getCurrentScene();
598 	_backgroundInstances->unpauseBySceneId(sceneId);
599 	_actorInstances->unpauseBySceneId(sceneId);
600 	_controls->unpauseControlsBySceneId(sceneId);
601 	_threads->notifyThreadsBySceneId(sceneId, threadId);
602 	_camera->popCameraMode();
603 	_activeScenes.unpauseActiveScene();
604 }
605 
dumpActiveScenes(uint32 sceneId,uint32 threadId)606 void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
607 	uint activeScenesCount = _activeScenes.getActiveScenesCount();
608 	while (activeScenesCount > 0) {
609 		uint32 activeSceneId;
610 		_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, 0);
611 		if (activeSceneId == sceneId)
612 			break;
613 		exitScene(threadId);
614 		--activeScenesCount;
615 	}
616 	_camera->clearCameraModeStack();
617 }
618 
pause(uint32 callerThreadId)619 void IllusionsEngine_BBDOU::pause(uint32 callerThreadId) {
620 	if (++_pauseCtr == 1) {
621 		_threads->pauseThreads(callerThreadId);
622 		_camera->pause();
623 		pauseFader();
624 		_controls->pauseActors(0x40004);
625 	}
626 }
627 
unpause(uint32 callerThreadId)628 void IllusionsEngine_BBDOU::unpause(uint32 callerThreadId) {
629 	if (--_pauseCtr == 0) {
630 		_controls->unpauseActors(0x40004);
631 		unpauseFader();
632 		_camera->unpause();
633 		_threads->unpauseThreads(callerThreadId);
634 	}
635 }
636 
enterMenuPause()637 void IllusionsEngine_BBDOU::enterMenuPause() {
638 	// TODO suspendAudio();
639 	_screenText->clearText();
640 }
641 
leaveMenuPause()642 void IllusionsEngine_BBDOU::leaveMenuPause() {
643 	_screenText->removeText();
644 	// TODO unsuspendAudio();
645 }
646 
setSceneIdThreadId(uint32 theSceneId,uint32 theThreadId)647 void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
648 	_theSceneId = theSceneId;
649 	_theThreadId = theThreadId;
650 }
651 
findTriggerCause(uint32 sceneId,uint32 verbId,uint32 objectId2,uint32 objectId,uint32 & codeOffs)652 bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
653 	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
654 	if (sceneInfo)
655 		return sceneInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
656 	return false;
657 }
658 
reset()659 void IllusionsEngine_BBDOU::reset() {
660 	_scriptResource->_blockCounters.clear();
661 	_scriptResource->_properties.clear();
662 	setTextDuration(1, 0);
663 }
664 
loadSavegameFromScript(int16 slotNum,uint32 callingThreadId)665 void IllusionsEngine_BBDOU::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
666 	// NOTE Just loads the savegame, doesn't activate it yet
667 	const char *fileName = getSavegameFilename(_savegameSlotNum);
668 	_loadGameResult = loadgame(fileName);
669 }
670 
saveSavegameFromScript(int16 slotNum,uint32 callingThreadId)671 void IllusionsEngine_BBDOU::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
672 	// TODO
673 	// const char *fileName = getSavegameFilename(slotNum);
674 	_saveGameResult = false;//savegame(fileName, _savegameDescription.c_str());
675 }
676 
activateSavegame(uint32 callingThreadId)677 void IllusionsEngine_BBDOU::activateSavegame(uint32 callingThreadId) {
678 	uint32 sceneId, threadId;
679 	_prevSceneId = 0x10000;
680 	_gameState->readState(sceneId, threadId);
681 	enterScene(sceneId, callingThreadId);
682 	// TODO Check if value8, valueC, value10 are needed at all
683 	startAnonScriptThread(threadId, 0, 0, 0, 0);
684 	_gameState->deleteReadStream();
685 }
686 
resumeFromSavegame()687 void IllusionsEngine_BBDOU::resumeFromSavegame() {
688 	// Resetting the game is usually done by the script, when loading from the ScummVM menu or
689 	// command line this has to be done manually.
690 	_specialCode->resetBeforeResumeSavegame();
691 	dumpActiveScenes(0x00010003, 0);
692 	activateSavegame(0);
693 }
694 
695 } // End of namespace Illusions
696