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/duckman/illusions_duckman.h"
24 #include "illusions/duckman/duckman_dialog.h"
25 #include "illusions/duckman/duckman_specialcode.h"
26 #include "illusions/duckman/gamestate_duckman.h"
27 #include "illusions/duckman/menusystem_duckman.h"
28 #include "illusions/duckman/scriptopcodes_duckman.h"
29 #include "illusions/duckman/duckman_videoplayer.h"
30 #include "illusions/actor.h"
31 #include "illusions/camera.h"
32 #include "illusions/cursor.h"
33 #include "illusions/dictionary.h"
34 #include "illusions/gamresourcereader.h"
35 #include "illusions/graphics.h"
36 #include "illusions/input.h"
37 #include "illusions/resources/actorresource.h"
38 #include "illusions/resources/backgroundresource.h"
39 #include "illusions/resources/fontresource.h"
40 #include "illusions/resources/genericresource.h"
41 #include "illusions/resources/midiresource.h"
42 #include "illusions/resources/scriptresource.h"
43 #include "illusions/resources/soundresource.h"
44 #include "illusions/resources/talkresource.h"
45 #include "illusions/resourcesystem.h"
46 #include "illusions/screen.h"
47 #include "illusions/screentext.h"
48 #include "illusions/scriptstack.h"
49 #include "illusions/sound.h"
50 #include "illusions/specialcode.h"
51 #include "illusions/textdrawer.h"
52 #include "illusions/thread.h"
53 #include "illusions/time.h"
54 #include "illusions/updatefunctions.h"
55 
56 #include "illusions/threads/abortablethread.h"
57 #include "illusions/threads/causethread_duckman.h"
58 #include "illusions/threads/scriptthread.h"
59 #include "illusions/threads/talkthread_duckman.h"
60 #include "illusions/threads/timerthread.h"
61 
62 #include "audio/audiostream.h"
63 #include "common/config-manager.h"
64 #include "common/debug-channels.h"
65 #include "common/error.h"
66 #include "common/fs.h"
67 #include "common/timer.h"
68 #include "engines/util.h"
69 #include "graphics/cursorman.h"
70 #include "graphics/font.h"
71 #include "graphics/fontman.h"
72 #include "graphics/palette.h"
73 #include "graphics/surface.h"
74 
75 namespace Illusions {
76 
77 // IllusionsEngine_Duckman
78 
IllusionsEngine_Duckman(OSystem * syst,const IllusionsGameDescription * gd)79 IllusionsEngine_Duckman::IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd)
80 	: IllusionsEngine(syst, gd) {
81 }
82 
run()83 Common::Error IllusionsEngine_Duckman::run() {
84 
85 	// Init search paths
86 	const Common::FSNode gameDataDir(ConfMan.get("path"));
87 	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
88 	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
89 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
90 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
91 
92 	_dict = new Dictionary();
93 
94 	_resReader = new ResourceReaderGamArchive("duckman.gam");
95 
96 	_resSys = new ResourceSystem(this);
97 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
98 	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
99 	_resSys->addResourceLoader(0x000A0000, new MidiGroupResourceLoader(this));
100 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
101 	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
102 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
103 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
104 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
105 	_resSys->addResourceLoader(0x00190000, new GenericResourceLoader(this));
106 
107 	_screen = new Screen8Bit(this, 320, 200);
108 	_screenPalette = new ScreenPalette(this);
109 	_screenText = new ScreenText(this);
110 	_input = new Input();
111 	_actorInstances = new ActorInstanceList(this);
112 	_backgroundInstances = new BackgroundInstanceList(this);
113 	_camera = new Camera(this);
114 	_controls = new Controls(this);
115 	_talkItems = new TalkInstanceList(this);
116 	_threads = new ThreadList(this);
117 	_updateFunctions = new UpdateFunctions();
118 	_soundMan = new SoundMan(this);
119 	_menuSystem = new DuckmanMenuSystem(this);
120 	_videoPlayer = new DuckmanVideoPlayer(this);
121 	_gameState = new Duckman_GameState(this);
122 
123 	_fader = new Fader();
124 
125 	_dialogSys = new DuckmanDialogSystem(this);
126 
127 	_screen->setColorKey1(0);
128 
129 	initInput();
130 
131 	initUpdateFunctions();
132 
133 	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
134 	_stack = new ScriptStack();
135 
136 	// TODO Move to own class
137 	_resGetCtr = 0;
138 	_unpauseControlActorFlag = false;
139 	_lastUpdateTime = 0;
140 
141 	_currWalkOverlappedControl = 0;
142 
143 	_pauseCtr = 0;
144 	_doScriptThreadInit = false;
145 	_field8 = 1;
146 	_fieldA = 0;
147 
148 	ConfMan.registerDefault("talkspeed", 240);
149 	_subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
150 	debug(0, "talkspeed: %d", _subtitleDuration);
151 
152 	_globalSceneId = 0x00010003;
153 
154 	_savedInventoryActorIndex = 0;
155 
156 	loadSpecialCode(0);
157 	setDefaultTextCoords();
158 	initCursor();
159 	initActiveScenes();
160 
161 	_resSys->loadResource(0x120001, 0x00010001, 0);
162 	_resSys->loadResource(0x120002, 0x00010001, 0);
163 	_resSys->loadResource(0x120003, 0x00010001, 0);
164 	_resSys->loadResource(0x000D0001, 0x00010001, 0);
165 
166 #if 0
167 	//DEBUG
168 	_scriptResource->_properties.set(0x000E003A, true);
169 	_scriptResource->_properties.set(0x000E0020, true);
170 	_scriptResource->_properties.set(0x000E003A, true);
171 	_scriptResource->_properties.set(0x000E003B, true);
172 	_scriptResource->_properties.set(0x000E0009, true);
173 	_scriptResource->_properties.set(0x000E003D, true);
174 	_scriptResource->_properties.set(0x000E0024, true);
175 #endif
176 
177 #if 0
178 	// DEBUG Enterprise
179 	_scriptResource->_blockCounters.set(238, 1);
180 #endif
181 
182 #if 0
183 	// DEBUG Map / special code 0016001A
184 	_scriptResource->_properties.set(0x000E0017, true);
185 	_scriptResource->_properties.set(0x000E0022, false);
186 #endif
187 
188 	if (ConfMan.hasKey("save_slot")) {
189 		_doScriptThreadInit = true;
190 		// Load global resources manually, usually done by the game script
191 		enterScene(0x00010003, 0);
192 		loadGameState(ConfMan.getInt("save_slot"));
193 	} else {
194 		startScriptThread(0x00020004, 0);
195 		_doScriptThreadInit = true;
196 	}
197 
198 	while (!shouldQuit()) {
199 		if (_resumeFromSavegameRequested) {
200 			activateSavegame(0);
201 			resumeFromSavegame(0);
202 			_resumeFromSavegameRequested = false;
203 		}
204 		runUpdateFunctions();
205 		_system->updateScreen();
206 		updateEvents();
207 	}
208 
209 	unloadSpecialCode(0);
210 
211 	delete _stack;
212 	delete _scriptOpcodes;
213 
214 	delete _dialogSys;
215 
216 	delete _fader;
217 
218 	delete _gameState;
219 	delete _menuSystem;
220 	delete _videoPlayer;
221 	delete _soundMan;
222 	delete _updateFunctions;
223 	delete _threads;
224 	delete _talkItems;
225 	delete _controls;
226 	delete _camera;
227 	delete _backgroundInstances;
228 	delete _actorInstances;
229 	delete _input;
230 	delete _screenText;
231 	delete _screenPalette;
232 	delete _screen;
233 	delete _resSys;
234 	delete _resReader;
235 	delete _dict;
236 
237 	debug("Ok");
238 
239 	return Common::kNoError;
240 }
241 
hasFeature(EngineFeature f) const242 bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
243 	return
244 		(f == kSupportsRTL) ||
245 		(f == kSupportsLoadingDuringRuntime) ||
246 		(f == kSupportsSavingDuringRuntime);
247 }
248 
initInput()249 void IllusionsEngine_Duckman::initInput() {
250 	_input->setInputEvent(kEventLeftClick, 0x01)
251 		.addMouseButton(MOUSE_LEFT_BUTTON)
252 		.addKey(Common::KEYCODE_RETURN);
253 	_input->setInputEvent(kEventRightClick, 0x02)
254 		.addMouseButton(MOUSE_RIGHT_BUTTON)
255 		.addKey(Common::KEYCODE_BACKSPACE);
256 	// 0x04 is unused
257 	_input->setInputEvent(kEventInventory, 0x08)
258 		.addKey(Common::KEYCODE_TAB);
259 	_input->setInputEvent(kEventAbort, 0x10)
260 		.addKey(Common::KEYCODE_ESCAPE);
261 	_input->setInputEvent(kEventSkip, 0x20)
262 		.addMouseButton(MOUSE_LEFT_BUTTON)
263 		.addKey(Common::KEYCODE_SPACE);
264 	_input->setInputEvent(kEventUp, 0x40)
265 		.addKey(Common::KEYCODE_UP);
266 	_input->setInputEvent(kEventDown, 0x80)
267 		.addMouseButton(MOUSE_RIGHT_BUTTON)
268 		.addKey(Common::KEYCODE_DOWN);
269 	_input->setInputEvent(kEventF1, 0x100)
270 		.addKey(Common::KEYCODE_F1);
271 }
272 
273 #define UPDATEFUNCTION(priority, sceneId, callback) \
274 	_updateFunctions->add(priority, sceneId, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
275 		(this, &IllusionsEngine_Duckman::callback));
276 
initUpdateFunctions()277 void IllusionsEngine_Duckman::initUpdateFunctions() {
278 	UPDATEFUNCTION(25, 0, updateVideoPlayer);
279 	UPDATEFUNCTION(30, 0, updateScript);
280 	UPDATEFUNCTION(50, 0, updateActors);
281 	UPDATEFUNCTION(60, 0, updateSequences);
282 	UPDATEFUNCTION(70, 0, updateGraphics);
283 	UPDATEFUNCTION(90, 0, updateSprites);
284 	UPDATEFUNCTION(120, 0, updateSoundMan);
285 }
286 
287 #undef UPDATEFUNCTION
288 
updateScript(uint flags)289 int IllusionsEngine_Duckman::updateScript(uint flags) {
290 	if (_screen->isDisplayOn() && !_screenPalette->isFaderActive() && _pauseCtr == 0) {
291 		if (_input->pollEvent(kEventAbort)) {
292 			startScriptThread(0x00020342, 0);
293 		} else if (_input->isCheatModeActive() && _input->pollEvent(kEventF1)) {
294 			startScriptThread(0x0002033F, 0);
295 		}
296 	}
297 	_threads->updateThreads();
298 
299 	//TODO need to call startScriptThread2(0x10002, 0x20001, 0);
300 	return kUFNext;
301 }
302 
startScreenShaker(uint pointsCount,uint32 duration,const ScreenShakerPoint * points,uint32 threadId)303 void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId) {
304 	_screenShaker = new ScreenShaker();
305 	_screenShaker->_pointsIndex = 0;
306 	_screenShaker->_pointsCount = pointsCount;
307 	_screenShaker->_finished = false;
308 	_screenShaker->_duration = duration;
309 	_screenShaker->_nextTime = duration + getCurrentTime();
310 	_screenShaker->_points = points;
311 	_screenShaker->_notifyThreadId = threadId;
312 	_updateFunctions->add(71, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
313 		(this, &IllusionsEngine_Duckman::updateScreenShaker));
314 }
315 
stopScreenShaker()316 void IllusionsEngine_Duckman::stopScreenShaker() {
317 	if (_screenShaker)
318 		_screenShaker->_finished = true;
319 }
320 
updateScreenShaker(uint flags)321 int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
322 	if (_pauseCtr > 0 || getCurrentScene() == 0x10038) {
323 		_screenShaker->_nextTime = getCurrentTime();
324 		return 1;
325 	}
326 
327 	if (flags & 1)
328 		_screenShaker->_finished = true;
329 
330 	if (!_screenShaker->_finished) {
331 		if (getCurrentTime() >= _screenShaker->_nextTime) {
332 			++_screenShaker->_pointsIndex;
333 			if (_screenShaker->_pointsIndex <= _screenShaker->_pointsCount) {
334 				ScreenShakerPoint shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
335 				if (shakePt.x == (int16)0x8000) {
336 					// Loop
337 					_screenShaker->_pointsIndex = 1;
338 					shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
339 				}
340 				_screenShaker->_nextTime = getCurrentTime() + _screenShaker->_duration;
341 				_screen->setScreenOffset(Common::Point(shakePt.x, shakePt.y));
342 			} else
343 				_screenShaker->_finished = true;
344 		}
345 	}
346 
347 	if (_screenShaker->_finished) {
348 		notifyThreadId(_screenShaker->_notifyThreadId);
349 		delete _screenShaker;
350 		_screenShaker = 0;
351 		_screen->setScreenOffset(Common::Point(0, 0));
352 		return 2;
353 	}
354 
355 	return 1;
356 }
357 
startFader(int duration,int minValue,int maxValue,int firstIndex,int lastIndex,uint32 threadId)358 void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
359 	_fader->_active = true;
360 	_fader->_currValue = minValue;
361 	_fader->_minValue = minValue;
362 	_fader->_maxValue = maxValue;
363 	_fader->_firstIndex = firstIndex;
364 	_fader->_lastIndex = lastIndex;
365 	_fader->_startTime = getCurrentTime();
366 	_fader->_duration = duration;
367 	_fader->_notifyThreadId = threadId;
368 }
369 
updateFader()370 void IllusionsEngine_Duckman::updateFader() {
371 	if (_fader && !_fader->_paused && _fader->_active) {
372 		int32 currTime = getCurrentTime();
373 		int32 currDuration = currTime - _fader->_startTime;
374 		if (currDuration) {
375 			int newValue;
376 			if (currDuration >= _fader->_duration) {
377 				newValue = _fader->_maxValue;
378 			} else {
379 				newValue = (currDuration * (_fader->_maxValue - _fader->_minValue) / _fader->_duration) + _fader->_minValue;
380 			}
381 			if (_fader->_currValue != newValue) {
382 				_fader->_currValue = newValue;
383 				_screenPalette->setFader(newValue, _fader->_firstIndex, _fader->_lastIndex);
384 			}
385 			if (_fader->_currValue == _fader->_maxValue) {
386 				_fader->_active = false;
387 				notifyThreadId(_fader->_notifyThreadId);
388 			}
389 		}
390 	}
391 }
392 
clearFader()393 void IllusionsEngine_Duckman::clearFader() {
394 	_fader->_active = false;
395 	_fader->_currValue = 255;
396 	_fader->_minValue = 255;
397 	_fader->_maxValue = 255;
398 	_fader->_firstIndex = 1;
399 	_fader->_lastIndex = 256;
400 	_fader->_startTime = 0;
401 	_fader->_duration = 0;
402 	_fader->_notifyThreadId = 0;
403 }
404 
pauseFader()405 void IllusionsEngine_Duckman::pauseFader() {
406 	_fader->_paused = true;
407 	_fader->_startTime = getCurrentTime() - _fader->_startTime;
408 }
409 
unpauseFader()410 void IllusionsEngine_Duckman::unpauseFader() {
411 	_fader->_startTime = getCurrentTime() - _fader->_startTime;
412 	_fader->_paused = false;
413 }
414 
updateVideoPlayer(uint flags)415 int IllusionsEngine_Duckman::updateVideoPlayer(uint flags) {
416 	if (_videoPlayer->isPlaying())
417 		_videoPlayer->update();
418 	return kUFNext;
419 }
420 
playVideo(uint32 videoId,uint32 callingThreadId)421 void IllusionsEngine_Duckman::playVideo(uint32 videoId, uint32 callingThreadId) {
422 	_videoPlayer->start(videoId, callingThreadId);
423 }
424 
isVideoPlaying()425 bool IllusionsEngine_Duckman::isVideoPlaying() {
426 	return _videoPlayer->isPlaying();
427 }
428 
setDefaultTextCoords()429 void IllusionsEngine_Duckman::setDefaultTextCoords() {
430 	WidthHeight dimensions;
431 	dimensions._width = 300;
432 	dimensions._height = 32;
433 	Common::Point pt(160, 176);
434 	setDefaultTextDimensions(dimensions);
435 	setDefaultTextPosition(pt);
436 }
437 
loadSpecialCode(uint32 resId)438 void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
439 	_specialCode = new DuckmanSpecialCode(this);
440 	_specialCode->init();
441 }
442 
unloadSpecialCode(uint32 resId)443 void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
444 	delete _specialCode;
445 }
446 
notifyThreadId(uint32 & threadId)447 void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
448 	if (threadId) {
449 		uint32 tempThreadId = threadId;
450 		threadId = 0;
451 		_threads->notifyId(tempThreadId);
452 	}
453 }
454 
testMainActorFastWalk(Control * control)455 bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
456 	return
457 		control->_objectId == _scriptResource->getMainActorObjectId() &&
458 		_input->pollEvent(kEventSkip);
459 }
460 
testMainActorCollision(Control * control)461 bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
462 	bool result = false;
463 	Control *overlappedControl;
464 	if (_controls->getOverlappedWalkObject(control, control->_actor->_position, &overlappedControl)) {
465 		if (_currWalkOverlappedControl != overlappedControl) {
466 			_currWalkOverlappedControl = overlappedControl;
467 			if (runTriggerCause(9, 0, overlappedControl->_objectId)) {
468 				delete control->_actor->_pathNode;
469 				control->_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
470 				control->_actor->_pathNode = 0;
471 				control->_actor->_pathPoints = 0;
472 				control->_actor->_pathPointsCount = 0;
473 				_threads->terminateThreadChain(control->_actor->_walkCallerThreadId1);
474 				if (control->_actor->_notifyId3C) {
475 					notifyThreadId(control->_actor->_notifyId3C);
476 					control->_actor->_walkCallerThreadId1 = 0;
477 				}
478 				result = true;
479 			}
480 		}
481 	} else {
482 		_currWalkOverlappedControl = 0;
483 	}
484 	return result;
485 }
486 
getObjectControl(uint32 objectId)487 Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
488 	return _dict->getObjectControl(objectId);
489 }
490 
491 static const uint8 kPointConversionTable[] = {
492 		0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
493 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
494 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
495 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
496 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
497 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
498 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
499 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 1, 2, 3,
500 		4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 35,
501 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
502 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
503 		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
504 		35, 35, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
505 		33, 34
506 };
getNamedPointPosition(uint32 namedPointId)507 Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
508 	Common::Point pt;
509 	Common::Point currPan = _camera->getCurrentPan();
510 	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt)) {
511 		return pt;
512 	} else if (namedPointId - 0x00070001 > 209) {
513 		if (_controls->findNamedPoint(namedPointId, pt)) {
514 			return pt;
515 		} else {
516 			return currPan;
517 		}
518 	} else {
519 		debug(1, "getNamedPointPosition(%02d)", kPointConversionTable[namedPointId - 0x00070001]);
520 		switch(kPointConversionTable[namedPointId - 0x00070001]) {
521 			case 0 : return Common::Point(160, 100);
522 			case 1 : return currPan;
523 			case 2 : return Common::Point(currPan.x - 160, currPan.y);
524 			case 3 : return Common::Point(currPan.x + 160, currPan.y);
525 			case 4 : return Common::Point(currPan.x, currPan.y - 100);
526 			case 5 : return Common::Point(currPan.x, currPan.y + 100);
527 			case 6 : return Common::Point(currPan.x - 160, currPan.y - 100);
528 			case 7 : return Common::Point((uint16)(currPan.x + 160), currPan.y - 100);
529 			case 8 : return Common::Point(currPan.x - 160, currPan.y + 100);
530 			case 9 : return Common::Point(currPan.x + 160, currPan.y + 100);
531 			/* TODO implement these
532 			case 10 : return Common::Point(0, 0);
533 			case 11 : return Common::Point(0, 0);
534 			case 12 : return Common::Point(0, 0);
535 			case 13 : return Common::Point(0, 0);
536 			case 14 : return Common::Point(0, 0);
537 			*/
538 			case 15 : return Common::Point(0, 0);
539 			/* TODO implement these
540 			case 16 : return Common::Point(0, 0);
541 			case 17 : return Common::Point(0, 0);
542 			case 18 : return Common::Point(0, 0);
543 			 */
544 			case 19 : return Common::Point(0, 0);
545 			case 20 : return Common::Point(320, 0);
546 			case 21 : return Common::Point(640, 0);
547 			case 22 : return Common::Point(960, 0);
548 			case 23 : return Common::Point(0, 200);
549 			case 24 : return Common::Point(320, 200);
550 			case 25 : return Common::Point(640, 200);
551 			case 26 : return Common::Point(960, 200);
552 			case 27 : return Common::Point(0, 400);
553 			case 28 : return Common::Point(320, 400);
554 			case 29 : return Common::Point(640, 400);
555 			case 30 : return Common::Point(960, 400);
556 			case 31 : return Common::Point(0, 600);
557 			case 32 : return Common::Point(320, 0);
558 			case 33 : return Common::Point(640, 0);
559 			case 34 : return Common::Point(960, 0);
560 			default : break;
561 		}
562 		error("getNamedPointPosition(%02d) UNKNOWN", kPointConversionTable[namedPointId - 0x00070001]);
563 		return Common::Point(0, 0);
564 	}
565 }
566 
getPriorityFromBase(int16 priority)567 uint32 IllusionsEngine_Duckman::getPriorityFromBase(int16 priority) {
568 	return priority << 16;
569 }
570 
getCurrentScene()571 uint32 IllusionsEngine_Duckman::getCurrentScene() {
572 	return _activeScenes[_activeScenesCount];
573 }
574 
getPrevScene()575 uint32 IllusionsEngine_Duckman::getPrevScene() {
576 	uint index = _activeScenesCount - 1;
577 	if (_activeScenesCount == 1)
578 		index = 5;
579 	return _activeScenes[index];
580 }
581 
isCursorObject(uint32 actorTypeId,uint32 objectId)582 bool IllusionsEngine_Duckman::isCursorObject(uint32 actorTypeId, uint32 objectId) {
583 	return actorTypeId == 0x50001;
584 }
585 
setCursorControlRoutine(Control * control)586 void IllusionsEngine_Duckman::setCursorControlRoutine(Control *control) {
587 	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_Duckman>
588 		(this, &IllusionsEngine_Duckman::cursorControlRoutine));
589 }
590 
placeCursorControl(Control * control,uint32 sequenceId)591 void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequenceId) {
592 	_cursor._gameState = 2;
593 	_cursor._control = control;
594 	_cursor._actorIndex = 1;
595 	_cursor._savedActorIndex = 1;
596 	_cursor._currOverlappedControl = 0;
597 	_cursor._sequenceId1 = sequenceId;
598 	_cursor._field14[0] = true;
599 	_cursor._field14[1] = true;
600 	_cursor._field14[2] = false;
601 	_cursor._field14[3] = false;
602 	_cursor._field14[4] = false;
603 	_cursor._field14[5] = false;
604 	_cursor._field14[9] = false;
605 	_cursor._field14[10] = false;
606 	_cursor._field14[11] = false;
607 	_cursor._field14[12] = false;
608 	_cursor._field14[6] = _cursor._sequenceId2 != 0 && _cursor._objectId != 0;
609 	_cursor._field14[7] = false;
610 	_cursor._field14[8] = false;
611 	_cursor._op113_choiceOfsPtr = 0;
612 	_cursor._notifyThreadId30 = 0;
613 	_cursor._dialogItemsCount = 0;
614 	_cursor._overlappedObjectId = 0;
615 	_cursor._field40 = 0;
616 	control->_flags |= 8;
617 	setCursorActorIndex(_cursor._actorIndex, 1, 0);
618 	// TODO Input_setMousePos(cursorControl->actor->position);
619 	// TODO
620 	//control->_actor->_actorIndex = 2;
621 	// TODO _cursor->place(control, sequenceId);
622 }
623 
setCursorControl(Control * control)624 void IllusionsEngine_Duckman::setCursorControl(Control *control) {
625 	_cursor._control = control;
626 }
627 
showCursor()628 void IllusionsEngine_Duckman::showCursor() {
629 	// TODO
630 }
631 
hideCursor()632 void IllusionsEngine_Duckman::hideCursor() {
633 	// TODO
634 }
635 
initCursor()636 void IllusionsEngine_Duckman::initCursor() {
637 	_cursor._gameState = 1;
638 	_cursor._control = 0;
639 	_cursor._position.x = 160;
640 	_cursor._position.y = 100;
641 	_cursor._objectId = 0;
642 	_cursor._actorIndex = 1;
643 	_cursor._savedActorIndex = 1;
644 	_cursor._currOverlappedControl = 0;
645 	_cursor._sequenceId1 = 0;
646 	_cursor._sequenceId2 = 0;
647 	_cursor._field14[0] = true;
648 	_cursor._field14[1] = true;
649 	_cursor._field14[2] = false;
650 	_cursor._field14[3] = false;
651 	_cursor._field14[4] = false;
652 	_cursor._field14[5] = false;
653 	_cursor._field14[6] = false;
654 	_cursor._field14[7] = false;
655 	_cursor._field14[8] = false;
656 	_cursor._field14[9] = false;
657 	_cursor._field14[10] = false;
658 	_cursor._field14[11] = false;
659 	_cursor._field14[12] = false;
660 	_cursor._op113_choiceOfsPtr = 0;
661 	_cursor._notifyThreadId30 = 0;
662 	_cursor._dialogItemsCount = 0;
663 	_cursor._overlappedObjectId = 0;
664 	_cursor._field40 = 0;
665 }
666 
setCursorActorIndex(int actorIndex,int a,int b)667 void IllusionsEngine_Duckman::setCursorActorIndex(int actorIndex, int a, int b) {
668 	static int kCursorMap[13][2][2] = {
669 		{{ 1,  2}, { 0,  0}},
670 		{{ 3,  4}, { 0,  0}},
671 		{{ 5,  6}, {13, 14}},
672 		{{ 7,  8}, { 0,  0}},
673 		{{ 9, 10}, { 0,  0}},
674 		{{11, 12}, { 0,  0}},
675 		{{ 1,  2}, { 0,  0}},
676 		{{ 0,  0}, { 0,  0}},
677 		{{ 0,  0}, { 0,  0}},
678 		{{15, 16}, { 0,  0}},
679 		{{17, 18}, { 0,  0}},
680 		{{19, 20}, { 0,  0}},
681 		{{21, 22}, { 0,  0}}
682 	};
683 	_cursor._control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
684 }
685 
enableCursorVerb(int verbNum)686 void IllusionsEngine_Duckman::enableCursorVerb(int verbNum) {
687 	if (verbNum != 7 || _cursor._sequenceId2)
688 		_cursor._field14[verbNum - 1] = true;
689 }
690 
disableCursorVerb(int verbNum)691 void IllusionsEngine_Duckman::disableCursorVerb(int verbNum) {
692 	_cursor._field14[verbNum - 1] = false;
693 	if (_cursor._actorIndex == verbNum) {
694 		_cursor._actorIndex = getCursorActorIndex();
695 		setCursorActorIndex(_cursor._actorIndex, 1, 0);
696 		startCursorSequence();
697 		_cursor._currOverlappedControl = 0;
698 	}
699 }
700 
setCursorHandMode(int mode)701 void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
702 	if (mode == 1) {
703 		enableCursorVerb(4);
704 		disableCursorVerb(1);
705 		disableCursorVerb(2);
706 		disableCursorVerb(7);
707 		_cursor._actorIndex = 4;
708 	} else {
709 		enableCursorVerb(1);
710 		enableCursorVerb(2);
711 		enableCursorVerb(7);
712 		disableCursorVerb(4);
713 		_cursor._actorIndex = 1;
714 	}
715 	_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
716 	if (_cursor._currOverlappedControl)
717 		setCursorActorIndex(_cursor._actorIndex, 2, 0);
718 	else
719 		setCursorActorIndex(_cursor._actorIndex, 1, 0);
720 }
721 
setCursorInventoryMode(int mode,int value)722 void IllusionsEngine_Duckman::setCursorInventoryMode(int mode, int value) {
723 	_cursor._control = _cursor._control;
724 	if (mode == 1) {
725 		_savedInventoryActorIndex = _cursor._actorIndex;
726 		if (_cursor._actorIndex == 3 || _cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
727 			_cursor._savedActorIndex = _cursor._savedActorIndex;
728 			if (_cursor._savedActorIndex == 1 || _cursor._savedActorIndex == 2 || _cursor._savedActorIndex == 7)
729 				_savedInventoryActorIndex = _cursor._savedActorIndex;
730 			else
731 				_savedInventoryActorIndex = 0;
732 		}
733 		if (value == 1 && _cursor._objectId && _savedInventoryActorIndex != 7) {
734 			_cursor._actorIndex = 7;
735 			stopCursorHoldingObject();
736 			_cursor._actorIndex = _savedInventoryActorIndex;
737 		}
738 	} else if (mode == 2) {
739 		if (_savedInventoryActorIndex)
740 			_cursor._actorIndex = _savedInventoryActorIndex;
741 		else
742 			_cursor._actorIndex = 1;
743 		if (_cursor._actorIndex == 7)
744 			_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
745 		else
746 			_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
747 		if (_cursor._currOverlappedControl)
748 			setCursorActorIndex(_cursor._actorIndex, 2, 0);
749 		else
750 			setCursorActorIndex(_cursor._actorIndex, 1, 0);
751 		_savedInventoryActorIndex = 0;
752 	}
753 }
754 
startCursorHoldingObject(uint32 objectId,uint32 sequenceId)755 void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 sequenceId) {
756 	_cursor._objectId = objectId;
757 	_cursor._sequenceId2 = sequenceId;
758 	_cursor._actorIndex = 7;
759 	_cursor._savedActorIndex = 7;
760 	_cursor._field14[6] = true;
761 	_cursor._control->startSequenceActor(sequenceId, 2, 0);
762 	setCursorActorIndex(_cursor._actorIndex, 1, 0);
763 	_cursor._currOverlappedControl = 0;
764 }
765 
stopCursorHoldingObject()766 void IllusionsEngine_Duckman::stopCursorHoldingObject() {
767 	_cursor._field14[6] = false;
768 	_cursor._objectId = 0;
769 	_cursor._sequenceId2 = 0;
770 	if (_cursor._actorIndex == 7) {
771 		_cursor._actorIndex = getCursorActorIndex();
772 		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
773 		if (_cursor._currOverlappedControl)
774 			setCursorActorIndex(_cursor._actorIndex, 2, 0);
775 		else
776 			setCursorActorIndex(_cursor._actorIndex, 1, 0);
777 	}
778 }
779 
cursorControlRoutine(Control * control,uint32 deltaTime)780 void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
781 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
782 	if (control->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
783 		switch (_cursor._gameState) {
784 		case 2:
785 			updateGameState2();
786 			break;
787 		case 3:
788 			_dialogSys->updateDialogState();
789 			break;
790 		case 4:
791 			_menuSystem->update(_cursor._control);
792 			break;
793 		}
794 	}
795 }
796 
startScriptThreadSimple(uint32 threadId,uint32 callingThreadId)797 void IllusionsEngine_Duckman::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
798 	startScriptThread(threadId, callingThreadId);
799 }
800 
startScriptThread(uint32 threadId,uint32 callingThreadId)801 void IllusionsEngine_Duckman::startScriptThread(uint32 threadId, uint32 callingThreadId) {
802 	debug(2, "Starting script thread %08X", threadId);
803 	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
804 	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp);
805 }
806 
startScriptThread2(uint32 sceneId,uint32 threadId,uint32 callingThreadId)807 void IllusionsEngine_Duckman::startScriptThread2(uint32 sceneId, uint32 threadId, uint32 callingThreadId) {
808 	debug(2, "Starting script thread2");
809 	_savegameSceneId = sceneId;
810 	_savegameThreadId = threadId;
811 	startScriptThread(0x20002, callingThreadId);
812 }
813 
startAbortableTimerThread(uint32 duration,uint32 threadId)814 uint32 IllusionsEngine_Duckman::startAbortableTimerThread(uint32 duration, uint32 threadId) {
815 	return newTimerThread(duration, threadId, true);
816 }
817 
startTimerThread(uint32 duration,uint32 threadId)818 uint32 IllusionsEngine_Duckman::startTimerThread(uint32 duration, uint32 threadId) {
819 	return newTimerThread(duration, threadId, false);
820 }
821 
startAbortableThread(byte * scriptCodeIp1,byte * scriptCodeIp2,uint32 callingThreadId)822 uint32 IllusionsEngine_Duckman::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
823 	uint32 tempThreadId = newTempThreadId();
824 	debug(2, "Starting abortable thread %08X", tempThreadId);
825 	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
826 	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
827 		scriptThreadId, scriptCodeIp2);
828 	_threads->startThread(abortableThread);
829 	return tempThreadId;
830 }
831 
startTalkThread(uint32 objectId,uint32 talkId,uint32 sequenceId1,uint32 sequenceId2,uint32 callingThreadId)832 uint32 IllusionsEngine_Duckman::startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
833 	uint32 sequenceId2, uint32 callingThreadId) {
834 	debug(2, "Starting talk thread");
835 	uint32 tempThreadId = newTempThreadId();
836 	TalkThread_Duckman *talkThread = new TalkThread_Duckman(this, tempThreadId, callingThreadId, 0,
837 		objectId, talkId, sequenceId1, sequenceId2);
838 	_threads->startThread(talkThread);
839 	return tempThreadId;
840 }
841 
startTempScriptThread(byte * scriptCodeIp,uint32 callingThreadId,uint32 value8,uint32 valueC,uint32 value10)842 uint32 IllusionsEngine_Duckman::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
843 	uint32 value8, uint32 valueC, uint32 value10) {
844 	uint32 tempThreadId = newTempThreadId();
845 	debug(2, "Starting temp script thread %08X", tempThreadId);
846 	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp);
847 	return tempThreadId;
848 }
849 
resumeFromSavegame(uint32 callingThreadId)850 void IllusionsEngine_Duckman::resumeFromSavegame(uint32 callingThreadId) {
851 	_input->discardAllEvents();
852 	if (changeScene(_savegameSceneId, _savegameThreadId, callingThreadId)) {
853 		_savegameSceneId = 0;
854 		_savegameThreadId = 0;
855 	}
856 }
857 
newScriptThread(uint32 threadId,uint32 callingThreadId,uint notifyFlags,byte * scriptCodeIp)858 void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
859 	byte *scriptCodeIp) {
860 	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
861 		scriptCodeIp, 0, 0, 0);
862 	_threads->startThread(scriptThread);
863 }
864 
newTimerThread(uint32 duration,uint32 callingThreadId,bool isAbortable)865 uint32 IllusionsEngine_Duckman::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
866 	uint32 tempThreadId = newTempThreadId();
867 	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
868 		duration, isAbortable);
869 	_threads->startThread(timerThread);
870 	return tempThreadId;
871 }
872 
newTempThreadId()873 uint32 IllusionsEngine_Duckman::newTempThreadId() {
874 	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
875 	if (threadId > 65535) {
876 		_nextTempThreadId = 0;
877 		threadId = 2 * _scriptResource->_codeCount;
878 	}
879 	++_nextTempThreadId;
880 	return 0x00020000 | threadId;
881 }
882 
initActiveScenes()883 void IllusionsEngine_Duckman::initActiveScenes() {
884 	_activeScenesCount = 0;
885 	_activeScenes[0] = 0xEFEF;
886 	pushActiveScene(0x10000);
887 }
888 
pushActiveScene(uint32 sceneId)889 void IllusionsEngine_Duckman::pushActiveScene(uint32 sceneId) {
890 	++_activeScenesCount;
891 	if (_activeScenesCount >= 6)
892 		_activeScenesCount = 1;
893 	_activeScenes[_activeScenesCount] = sceneId;
894 }
895 
popActiveScene()896 void IllusionsEngine_Duckman::popActiveScene() {
897 	--_activeScenesCount;
898 	if (_activeScenesCount == 0)
899 		_activeScenesCount = 5;
900 }
901 
loadScene(uint32 sceneId)902 bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
903 	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
904 	if (!sceneInfo)
905 		return false;
906 	pushActiveScene(sceneId);
907 	uint resourcesCount;
908 	uint32 *resources;
909 	sceneInfo->getResources(resourcesCount, resources);
910 	for (uint i = 0; i < resourcesCount; ++i) {
911 		_resSys->loadResource(resources[i], sceneId, 0);
912 	}
913 	return true;
914 }
915 
enterScene(uint32 sceneId,uint32 threadId)916 bool IllusionsEngine_Duckman::enterScene(uint32 sceneId, uint32 threadId) {
917 	if (loadScene(sceneId)) {
918 		if (threadId)
919 			startScriptThread(threadId, 0);
920 		return true;
921 	}
922 	startScriptThread2(0x10002, 0x20001, 0);
923 	return false;
924 }
925 
exitScene()926 void IllusionsEngine_Duckman::exitScene() {
927 	popActiveScene();
928 }
929 
changeScene(uint32 sceneId,uint32 threadId,uint32 callerThreadId)930 bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId) {
931 	uint32 currSceneId = getCurrentScene();
932 	if (currSceneId != 0x10003)
933 		dumpCurrSceneFiles(currSceneId, callerThreadId);
934 	_soundMan->stopLoopingSounds(); //Fix for global looping sound not stopping in falling scene.
935 	_threads->terminateThreads(callerThreadId);
936 	_controls->destroyControls();
937 	_resSys->unloadSceneResources(0x10003, 0x10001);
938 	if (enterScene(sceneId, threadId)) {
939 		_gameState->writeState(sceneId, threadId);
940 		return true;
941 	}
942 	return false;
943 }
944 
enterPause(uint32 sceneId,uint32 threadId)945 void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
946 	_threads->suspendThreads(threadId);
947 	_controls->pauseControls();
948 	_actorInstances->pauseBySceneId(sceneId);
949 	_backgroundInstances->pauseBySceneId(sceneId);
950 }
951 
leavePause(uint32 sceneId,uint32 threadId)952 void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
953 	_backgroundInstances->unpauseBySceneId(sceneId);
954 	_actorInstances->unpauseBySceneId(sceneId);
955 	_controls->unpauseControls();
956 	_threads->notifyThreads(threadId);
957 }
958 
dumpActiveScenes(uint32 sceneId,uint32 threadId)959 void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
960 	// TODO?
961 }
962 
dumpCurrSceneFiles(uint32 sceneId,uint32 threadId)963 void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
964 	_updateFunctions->terminateByScene(sceneId);
965 	_threads->terminateActiveThreads(threadId);
966 	_threads->terminateThreadsBySceneId(sceneId, threadId);
967 	_controls->destroyActiveControls();
968 	_resSys->unloadResourcesBySceneId(sceneId);
969 }
970 
pause(uint32 callerThreadId)971 void IllusionsEngine_Duckman::pause(uint32 callerThreadId) {
972 	if (++_pauseCtr == 1) {
973 		_threads->pauseThreads(callerThreadId);
974 		_camera->pause();
975 		pauseFader();
976 		_controls->pauseActors(Illusions::CURSOR_OBJECT_ID);
977 	}
978 }
979 
unpause(uint32 callerThreadId)980 void IllusionsEngine_Duckman::unpause(uint32 callerThreadId) {
981 	if (--_pauseCtr == 0) {
982 		_controls->unpauseActors(Illusions::CURSOR_OBJECT_ID);
983 		unpauseFader();
984 		_camera->unpause();
985 		_threads->unpauseThreads(callerThreadId);
986 	}
987 }
988 
setSceneIdThreadId(uint32 theSceneId,uint32 theThreadId)989 void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
990 	_theSceneId = theSceneId;
991 	_theThreadId = theThreadId;
992 }
993 
findTriggerCause(uint32 sceneId,uint32 verbId,uint32 objectId2,uint32 objectId,uint32 & codeOffs)994 bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
995 	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
996 	if (sceneInfo)
997 		return sceneInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
998 	return false;
999 }
1000 
reset()1001 void IllusionsEngine_Duckman::reset() {
1002 	_scriptResource->_blockCounters.clear();
1003 	_scriptResource->_properties.clear();
1004 	setTextDuration(1, 0);
1005 	// TODO resetCursor();
1006 }
1007 
getObjectActorTypeId(uint32 objectId)1008 uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
1009 	return _scriptResource->getObjectActorTypeId(objectId);
1010 }
1011 
convertMousePos(Common::Point mousePos)1012 Common::Point IllusionsEngine_Duckman::convertMousePos(Common::Point mousePos) {
1013 	return mousePos + _camera->getScreenOffset();
1014 }
1015 
startCursorSequence()1016 void IllusionsEngine_Duckman::startCursorSequence() {
1017 	// NOTE Calls to startCursorSequence were put after calls to setCursorActorIndex
1018 	// to make the cursor switch more immediate. In the original these calls are swapped.
1019 	if (_cursor._actorIndex == 7)
1020 		_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
1021 	else
1022 		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
1023 }
1024 
getCursorActorIndex()1025 int IllusionsEngine_Duckman::getCursorActorIndex() {
1026 	int result = _cursor._actorIndex;
1027 	do {
1028 		++result;
1029 		if (result > 13)
1030 			result = 1;
1031 	} while (!_cursor._field14[result - 1]);
1032 	return result;
1033 }
1034 
updateGameState2()1035 void IllusionsEngine_Duckman::updateGameState2() {
1036 	Common::Point cursorPos = _input->getCursorPosition();
1037 	Common::Point convMousePos = convertMousePos(cursorPos);
1038 	int trackCursorIndex = -1;
1039 	bool foundOverlapped;
1040 	Control *overlappedControl;
1041 
1042 	_cursor._control->_actor->_position = cursorPos;
1043 
1044 	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
1045 
1046 	if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
1047 		trackCursorIndex = 10;
1048 	} else if (cursorPos.y >= 192 && !_camera->isAtPanLimit(2)) {
1049 		trackCursorIndex = 11;
1050 	} else if (cursorPos.x < 8 && !_camera->isAtPanLimit(3)) {
1051 		trackCursorIndex = 12;
1052 	} else if (cursorPos.x >= 312 && !_camera->isAtPanLimit(4)) {
1053 		trackCursorIndex = 13;
1054 	} else if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
1055 		_cursor._actorIndex = _cursor._savedActorIndex;
1056 		if (_cursor._currOverlappedControl)
1057 			setCursorActorIndex(_cursor._actorIndex, 2, 0);
1058 		else
1059 			setCursorActorIndex(_cursor._actorIndex, 1, 0);
1060 		startCursorSequence();
1061 	}
1062 
1063 	if (trackCursorIndex >= 0) {
1064 		if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
1065 			_cursor._savedActorIndex = _cursor._actorIndex;
1066 		if (_cursor._actorIndex != trackCursorIndex) {
1067 			_cursor._actorIndex = trackCursorIndex;
1068 			setCursorActorIndex(_cursor._actorIndex, 1, 0);
1069 			startCursorSequence();
1070 		}
1071 		_cursor._currOverlappedControl = 0;
1072 		foundOverlapped = false;
1073 	}
1074 
1075 	if (foundOverlapped) {
1076 		if (_cursor._currOverlappedControl != overlappedControl) {
1077 			int cursorValue2 = 0;
1078 			if (overlappedControl->_flags & 2) {
1079 				if (_cursor._actorIndex != 3) {
1080 					_cursor._savedActorIndex = _cursor._actorIndex;
1081 					_cursor._actorIndex = 3;
1082 				}
1083 				if (overlappedControl->_flags & 0x40)
1084 					cursorValue2 = 1;
1085 			} else if (_cursor._actorIndex == 3) {
1086 				_cursor._actorIndex = _cursor._savedActorIndex;
1087 			}
1088 			setCursorActorIndex(_cursor._actorIndex, 2, cursorValue2);
1089 			startCursorSequence();
1090 			_cursor._currOverlappedControl = overlappedControl;
1091 		}
1092 	} else if (_cursor._currOverlappedControl) {
1093 		if (_cursor._actorIndex == 3)
1094 			_cursor._actorIndex = _cursor._savedActorIndex;
1095 		setCursorActorIndex(_cursor._actorIndex, 1, 0);
1096 		startCursorSequence();
1097 		_cursor._currOverlappedControl = 0;
1098 	}
1099 
1100 	if (_input->pollEvent(kEventLeftClick)) {
1101 		if (_cursor._currOverlappedControl) {
1102 			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
1103 		} else {
1104 			_cursor._position = convertMousePos(_cursor._control->_actor->_position);
1105 			// TODO clipMousePos(&_cursor._position);
1106 			if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13)
1107 				runTriggerCause(1, _cursor._objectId, 0x40003);
1108 			else
1109 				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
1110 		}
1111 	} else if (_input->pollEvent(kEventRightClick)) {
1112 		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
1113 			int newActorIndex = getCursorActorIndex();
1114 			if (newActorIndex != _cursor._actorIndex) {
1115 				_cursor._actorIndex = newActorIndex;
1116 				if (_cursor._currOverlappedControl)
1117 					setCursorActorIndex(_cursor._actorIndex, 2, 0);
1118 				else
1119 					setCursorActorIndex(_cursor._actorIndex, 1, 0);
1120 				startCursorSequence();
1121 			}
1122 		}
1123 	} else if (_input->pollEvent(kEventInventory)) {
1124 		if (_cursor._field14[0] == 1) {
1125 			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
1126 		} else if (_cursor._field14[1] == 1) {
1127 			runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
1128 		}
1129 	}
1130 
1131 }
1132 
playSoundEffect(int index)1133 void IllusionsEngine_Duckman::playSoundEffect(int index) {
1134 	uint32 soundEffectId = 0;
1135 	uint32 *soundIds = _scriptResource->_soundIds;
1136 	switch (index) {
1137 	case 1:
1138 		soundEffectId = soundIds[0];
1139 		break;
1140 	case 2:
1141 		soundEffectId = soundIds[1];
1142 		break;
1143 	case 3:
1144 		soundEffectId = soundIds[2];
1145 		break;
1146 	case 4:
1147 		soundEffectId = soundIds[3];
1148 		break;
1149 	case 5:
1150 		soundEffectId = soundIds[4];
1151 		break;
1152 	case 6:
1153 		soundEffectId = soundIds[getRandom(4) + 5];
1154 		break;
1155 	case 7:
1156 		soundEffectId = soundIds[getRandom(4) + 9];
1157 		break;
1158 	case 8:
1159 		soundEffectId = soundIds[13];
1160 		break;
1161 	case 9:
1162 		soundEffectId = soundIds[14];
1163 		break;
1164 	case 10:
1165 		soundEffectId = soundIds[15];
1166 		break;
1167 	case 11:
1168 		soundEffectId = soundIds[16];
1169 		break;
1170 	case 12:
1171 		soundEffectId = soundIds[getRandom(4) + 17];
1172 		break;
1173 	case 13:
1174 		soundEffectId = soundIds[21];
1175 		break;
1176 	case 14:
1177 		soundEffectId = soundIds[22];
1178 		break;
1179 	case 15:
1180 		soundEffectId = soundIds[23];
1181 		break;
1182 	case 16:
1183 		soundEffectId = soundIds[24];
1184 		break;
1185 	case 17:
1186 		soundEffectId = soundIds[25];
1187 		break;
1188 	case 18:
1189 		soundEffectId = soundIds[26];
1190 		break;
1191 	}
1192 	if (soundEffectId)
1193 		_soundMan->playSound(soundEffectId, 255, 0);
1194 }
1195 
getTriggerCause(uint32 verbId,uint32 objectId2,uint32 objectId,uint32 & outThreadId)1196 bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
1197 	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(getCurrentScene() & 0xFFFF);
1198 	bool found =
1199 		sceneInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
1200 		sceneInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
1201 	if (!found) {
1202 		sceneInfo = _scriptResource->getSceneInfo(3);
1203 		found =
1204 			sceneInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
1205 			sceneInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
1206 	}
1207 	return found;
1208 }
1209 
runTriggerCause(uint32 verbId,uint32 objectId2,uint32 objectId)1210 uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
1211 	debug(1, "runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
1212 	uint32 triggerThreadId;
1213 
1214 	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
1215 		return 0;
1216 
1217 	playTriggerCauseSound(verbId, objectId2, objectId);
1218 
1219 	uint32 tempThreadId = newTempThreadId();
1220 	debug(1, "Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
1221 	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
1222 		triggerThreadId);
1223 	_threads->startThread(causeThread);
1224 
1225 	return tempThreadId;
1226 }
1227 
playTriggerCauseSound(uint32 verbId,uint32 objectId2,uint32 objectId)1228 void IllusionsEngine_Duckman::playTriggerCauseSound(uint32 verbId, uint32 objectId2, uint32 objectId) {
1229 	bool soundWasPlayed = false;
1230 	if (_scriptResource->_properties.get(0x000E003C)) {
1231 		if (verbId == 7 && objectId == 0x40003) {
1232 			playSoundEffect(7);
1233 			soundWasPlayed = true;
1234 		} else if (objectId == 0x40003) {
1235 			playSoundEffect(14);
1236 			soundWasPlayed = true;
1237 		} else if (verbId == 3) {
1238 			playSoundEffect(16);
1239 			soundWasPlayed = true;
1240 		} else if (verbId == 2) {
1241 			soundWasPlayed = true;
1242 		}
1243 	}
1244 	if (!soundWasPlayed) {
1245 		if (objectId == 0x40003) {
1246 			playSoundEffect(14);
1247 		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) {
1248 			playSoundEffect(15);
1249 		} else if (verbId == 7 && _scriptResource->getMainActorObjectId() == objectId) {
1250 			playSoundEffect(15);
1251 		} else if (verbId == 1) {
1252 			playSoundEffect(1);
1253 		} else if (verbId == 2) {
1254 			playSoundEffect(2);
1255 		} else if (verbId == 3) {
1256 			playSoundEffect(3);
1257 		} else if (verbId == 4 || verbId == 7) {
1258 			playSoundEffect(4);
1259 		} else if (verbId == 9) {
1260 			playSoundEffect(5);
1261 		}
1262 	}
1263 }
1264 
loadSavegameFromScript(int16 slotNum,uint32 callingThreadId)1265 bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
1266 	if (_savegameSlotNum < 0) {
1267 		return false; // TODO need to handle reset from new game (without exising savegame).
1268 	}
1269 
1270 	const char *fileName = getSavegameFilename(_savegameSlotNum);
1271 	bool success = loadgame(fileName);
1272 	if (success)
1273 		activateSavegame(callingThreadId);
1274 	_gameState->deleteReadStream();
1275 	return success;
1276 }
1277 
saveSavegameFromScript(int16 slotNum,uint32 callingThreadId)1278 bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
1279 	const char *fileName = getSavegameFilename(_savegameSlotNum);
1280 	return savegame(fileName, _savegameDescription.c_str());
1281 }
1282 
activateSavegame(uint32 callingThreadId)1283 void IllusionsEngine_Duckman::activateSavegame(uint32 callingThreadId) {
1284 	// TODO _screen->setDisplayOn(false);
1285 	uint32 currSceneId = getCurrentScene();
1286 	if (currSceneId != 0x10003)
1287 		dumpCurrSceneFiles(currSceneId, callingThreadId);
1288 	reset();
1289 	// TODO stopMidi();
1290 	// TODO clearMidiPlayList();
1291 	_gameState->readState(_savegameSceneId, _savegameThreadId);
1292 	pushActiveScene(0x10000);
1293 	_gameState->deleteReadStream();
1294 }
1295 
1296 } // End of namespace Illusions
1297