[Scummvm-git-logs] scummvm master -> 848b5a5ff5c317a92ba26254b94d273f2aa6bf97

sev- sev at scummvm.org
Fri Jul 20 08:45:44 CEST 2018


This automated email contains information about 208 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
9a58385088 ILLUSIONS: Skeleton engine with detection for BBDOU
7300dd09cd ILLUSIONS: Resource loader skeleton
26487555b1 ILLUSIONS: Start with BackgroundResource
5833ceda4b ILLUSIONS: More work on BackgroundResource
a06895ad59 ILLUSIONS: Start with Camera and Input classes
bf09487df0 ILLUSIONS: Add UpdateFunctions class
ee59e736d3 ILLUSIONS: Start with SpriteDrawQueue
971c8a0a1d ILLUSIONS: Start with SpriteDecompressQueue
d92e713dea ILLUSIONS: Start with Actor
e0a3db43c3 ILLUSIONS: Keep backgrounds in new BackgroundItems class instead of the engine class
12645cbb8c ILLUSIONS: Some work on the Camera class; start with time functions
21e1b08b83 ILLUSIONS: More work on the camera and background classes
08e8f39945 ILLUSIONS: More work on the Camera class
4211f8ffcd ILLUSIONS: Start with ActorResource and related classes
d2b036fa5b ILLUSIONS: Start with thread classes
bb67c2c2ff ILLUSIONS: Start with ScriptResource and related code
fc47ac41ae ILLUSIONS: More work on the script system
9696eb9a54 ILLUSIONS: More work on the script system
9d35f807ec ILLUSIONS: More work on the script system
ad440a1723 ILLUSIONS: More work on actor and control related code
9a63f0bd17 ILLUSIONS: Add background scale and priority classes
9385238a59 ILLUSIONS: More work on actor related code
f98c0defe5 ILLUSIONS: Add Screen class and ajust code to use it
f880c606f7 ILLUSIONS: Work on Actor and Control classes
70f0b48aaf ILLUSIONS: Add Dictionary class and use it
b3b0bd884d ILLUSIONS: Work on Actor and Control classes; fix bug in sprite decompression
3fc592df49 ILLUSIONS: Add Control::calcPosition and Control::readPointsConfig
18540a5e38 ILLUSIONS: Add SequenceOpcodes skeleton class
e881db0732 ILLUSIONS: Implement more sequence opcodes and work on the graphics system
48ef46c02d ILLUSIONS: Implement more script opcodes and related functions
c99f40c13d ILLUSIONS: Implement TimerThread and script opcode
f47575ca90 ILLUSIONS: Add more script opcodes and related
43cd806f17 ILLUSIONS: Add more script opcodes
762be35a36 ILLUSIONS: Add more script opcodes and fix/add stuff along the way
812c7fc3a8 ILLUSIONS: Add TalkResource and related
fc4266bcad ILLUSIONS: Add TalkThread
a6a4a3dc1c ILLUSIONS: Implement more script opcodes
28cb39eb2b ILLUSIONS: Start with BBDOU special code
e05a789975 ILLUSIONS: More work on BBDOU specific code (cursor, bubble)
22e898f7eb ILLUSIONS: Work on interaction; work on Cause related code
33d28deb69 ILLUSIONS: Additions in various places
3b3f84c764 ILLUSIONS: Implement inventory
babe997295 ILLUSIONS: Add more script opcodes
e0e4d2ffa9 ILLUSIONS: Implement sprite scaling
f2c48e3ae0 ILLUSIONS: Implement pathwalking (actual pathfinding todo)
d77d4ed4a6 ILLUSIONS: Fix bug which occured when trying to walk while talking
d67021b32c ILLUSIONS: Implement talkitem pausing/unpausing
7dc8533f73 ILLUSIONS: Fix thread camera panning
8d7d6599b9 ILLUSIONS: Fix special code functions thread notifying (used wrong thread value)
08899f5e9b ILLUSIONS: Implement calcPointDirection
6d17b40796 ILLUSIONS: Use screen pixel format when creating surfaces
54bce3d37a ILLUSIONS: Move SpriteDecompressQueue to screen
6ba5570de5 ILLUSIONS: Move SpriteDrawQueue to screen
3f15233f78 ILLUSIONS: Refactor code in preparation for the Duckman engine
67366aa04b ILLUSIONS: More work on Duckman
2e149cf651 ILLUSIONS: More work on Duckman
36c7ec4d34 ILLUSIONS: Delete obsolete files
998744608f ILLUSIONS: More work on Duckman
e131362590 ILLUSIONS: More work on Duckman
097d130e7d ILLUSIONS: Implement RegionLayer and related code
e9a443fcd9 ILLUSIONS: Implement palette shifting and color shadow table
b1927ca304 ILLUSIONS: Add more script and sequence for Duckman
1f74de6a46 ILLUSIONS: More work on Duckman
60600191a0 ILLUSIONS: Use the UpdateFunctions class for update routines
44c566b51e ILLUSIONS: Add screen shaking effect
b94b4c28ba ILLUSIONS: Implement pathfinding
d431d35214 ILLUSIONS: BBDOU: Add microphone radar
d57ae26179 ILLUSIONS: Add SoundMan and MusicPlayer
9885a050f2 ILLUSIONS: Implement VoicePlayer
ad2c0aaf3d ILLUSIONS: Add sound effects
297996a090 ILLUSIONS: Add sound effects (still buggy)
50d340de00 ILLUSIONS: Rename file scriptman,* to scriptstack.* and remove unneccessary includes of it
d7dd74cbe9 ILLUSIONS: Very minor comment fixes
e4ce8ad4c9 ILLUSIONS: Start refactoring the resource system (not done yet)
ace0d042ec ILLUSIONS: Refactoring the resource system (not done yet)
faf7b31ff8 ILLUSIONS: Move thread-related files into threads subdirectory
a173329ae9 ILLUSIONS: Move resource-related files into resources subdirectory
e4a85c7da6 ILLUSIONS: Move game-specific files into own subdirectories
70f83137b1 ILLUSIONS: Move Duckman special code to own class and file
18553cb17a ILLUSIONS: Move Duckman property timer code to own class and file
dec9ef3123 ILLUSIONS: Move Duckman inventory code to own class and file
d6e2f61155 ILLUSIONS: Move Duckman dialog code to own file
a078073e88 ILLUSIONS: Move script opcode files to game specific directories
36ec0fafdb ILLUSIONS: Refactor the input system
9d98f92298 ILLUSIONS: Add text drawing to BBDOU
e63eaabbdb ILLUSIONS: Move trigger functions code to seperate file
88ea891184 ILLUSIONS: Use ARRAYSIZE instead of hardcoded values in screen effect constants
a30a31868b ILLUSIONS: Move screen shaker effects into separate file
a5ad1bc106 ILLUSIONS: Remove unneeded includes
41978f466d ILLUSIONS: Add GAM archive reader for Duckman and adjust the resource reader/system
61a0b5badb ILLUSIONS: Rename tag -> sceneId
8b48f2f63f ILLUSIONS: Rename ProgInfo -> SceneInfo
601c6f4082 ILLUSIONS: Terminate update functions on scene exit; fix bugs and use constants
09bbb482a8 ILLUSIONS: DUCKMAN: Implement opcodes 70/71 for pausing/unpausing during the menu
fa17f684da ILLUSIONS: DUCKMAN: Start implementing the menu system
2b9af91d8f ILLUSIONS: DUCKMAN: Implement opcode 8
15498a231a ILLUSIONS: DUCKMAN: Implement opcode 21
5a69f73561 ILLUSIONS: DUCKMAN: Implement opcode 73
bbbb0053e0 ILLUSIONS: DUCKMAN: Implement opcode 83
9a84c0a62e ILLUSIONS: DUCKMAN: Implement opcode 85 and 86, actual save/load not implemented yet
449d243c8f ILLUSIONS: DUCKMAN: Implement opcode 100 and 101
9c0ef1bb11 ILLUSIONS: DUCKMAN: Remove if'd out opcode code (used by me as scratch pad); now all opcodes should be callable, though 
f692e0acfb ILLUSIONS: DUCKMAN: Implement special opcodes 16001D, 16001E, 16001F and related code
d16ebff390 ILLUSIONS: DUCKMAN: Implement special opcode 160012 and related code
11ec6d2258 ILLUSIONS: DUCKMAN: Implement special opcode 160013, also fix Input and reduce debug output
d8e86249c7 ILLUSIONS: DUCKMAN: Implement special opcode 16001A
6b36b750c2 ILLUSIONS: DUCKMAN: Implement special opcodes 160017, 16001B, 160020 and 160021
aed3852701 ILLUSIONS: Implement save/load functionality
2de38e3469 ILLUSIONS: DUCKMAN: Extract trigger cause sound playing to method
823ba2f462 ILLUSIONS: DUCKMAN: Implement load game from the game's menu system
88e2b3fd9e ILLUSIONS: BBDOU: Implement special code 160030
c6f2c6ba4e ILLUSIONS: BBDOU: Implement special code 16000F
c0c25691e0 ILLUSIONS: BBDOU: Implement missing script opcodes and special opcodes; fix ActorType bugs and more
27a5e93268 ILLUSIONS: BBDOU: Start implementing crosshair cursor (used in shooting minigame)
869d342e9f ILLUSIONS: BBDOU: Implement getOverlappedObjectAccurate and related functions
6ca6fb605d ILLUSIONS: BBDOU: Implement more special opcodes and finish putBackInventoryItem
0589588b7b ILLUSIONS: BBDOU: Implement credits code and adjust existing code
4aed366334 ILLUSIONS: BBDOU: Implement BbdouBubble::calcBubbles
48011948d6 ILLUSIONS: BBDOU: Implement food/cafeteria minigame
c9b0a8452a ILLUSIONS: BBDOU: Implement remaining special opcodes 160018, 160028, 16002B
6b80f5b853 ILLUSIONS: DUCKMAN: Move game credits into own file and class
2f80bd0e2b ILLUSIONS: BBDOU: Implement opcodes 18, 19
3abe3d759c ILLUSIONS: BBDOU: Implement opcodes 22, 23
1e2e5d636c ILLUSIONS: BBDOU: Implement opcode 54
47b94b1396 ILLUSIONS: Minor cleanup: Remove empty methods in thread classes
e31c454cfa ILLUSIONS: BBDOU: Rename stuff
41c7a99262 ILLUSIONS: BBDOU: Extract code to new class ObjectInteractModeMap
ad558f8c68 ILLUSIONS: BBDOU: Rename more
9447d42fd7 ILLUSIONS: BBDOU: Remove experimental sprite smoothing code
2bd1386528 ILLUSIONS: Move palette code from Screen to new ScreenPalette class
5e919fd308 ILLUSIONS: Split Screen class into variants for 8bit and 16bit (i.e. Duckman and BBDOU)
02063f60d0 Getting it to compile after merging master
95d6171b56 Disable debug scene override
783fd02893 Work on save support
92c979e99a Fixed save continue dialog menu
13d5c2fa26 Save description with savegame
23ae468542 Work on options menu
b3e9c972f1 ILLUSIONS: Improve naming of variables and methods in menu system.
a6cd908e0e ILLUSIONS: Replace text flag magic numbers with defines
8e462f313f ILLUSIONS: Work on menu border decoration
a36cd2e39f ILLUSIONS: Name menu border color variables to add readability
43ba5f6327 ILLUSIONS: Fill in background for text in menus
28b0acc6c2 ILLUSIONS: Fix bug when setting property timers.
6ae9555086 ILLUSIONS: Fix valgrind warnings about usage of uninitialised variables
2d836d4ec0 VIDEO: Warn instead of error when unhandled TXTS stream found in AVI
9be0a7b085 ILLUSIONS: Play video files
39798c63d1 ILLUSIONS: Fix static buffer overrun in debug log function.
a7d78df98c ILLUSIONS: Fix clang warnings. Work on menu keyboard control
5dd96b6fbe ILLUSIONS: Add midi music player
2f551cabba ILLUSIONS: Change video skip key from escape to space
d3fbb0e8b9 ILLUSIONS: Replace actor flag magic values with enum definitions
2be0275221 ILLUSIONS: Connect up midi fade to MidiPlayer object
c66b2208e0 ILLUSIONS: silence clang warning about not operation in expression
6e09cd7e08 ILLUSIONS: Rename some actor flags
7c46d891c6 ILLUSIONS: Print walk rectangle point on background to aid in debugging
bdc477bef9 ILLUSIONS: Implement convertPanXCoord() to support audio panning.
423a8ec433 ILLUSIONS: Replace 0x40004 with CURSOR_OBJECT_ID constant
7b88bd8efe ILLUSIONS: Add more logging
88ef190569 ILLUSIONS: Add logic to enable palette cycling sequence
83994972de ILLUSIONS: Add debug cheat code support
4a42aba1fd ILLUSIONS: Start on debug menu
0793272dfa ILLUSIONS: Wire up sfx to menus
ced1ff2356 ILLUSIONS: Move sliders in option menu
0303b83ead ILLUSIONS: Pause actors when entering menu
09281b85f5 ILLUSIONS: loop music tracks
102dd46273 ILLUSIONS: Wire up audio sliders on option menu.
7e4a1c2bf1 ILLUSIONS: Add ability to restore options sliders to defaults
dc9dc0324e ILLUSIONS: Wire up subtitle text duration config slider
dc79026a31 ILLUSIONS: Enable illusions engine by default
a5319cbce6 ILLUSIONS: Wire up restart savegame menu item.
2846311402 ILLUSIONS: Add support for German version.
92e74327e6 ILLUSIONS: Initialise save slot variable
d77dd6c14a ILLUSIONS: Pause voice when entering in-game menu
3d9f5ed20f ILLUSIONS: Replace spaces with tabs
aa241b8ef3 ILLUSIONS: Mark bbdou as unstable
7cadb7ad0c ILLUSIONS: Formatting
989d8d9b8a ILLUSIONS: Formatting, remove trailing whitespace.
d36eae4c17 ILLUSIONS: Fix typo in palette cycle logic.
9ad048f8a6 ILLUSIONS: Fix formatting
608f2f1f1a ILLUSIONS: Refactor duckman inventory code to remove verbose init logic
fee1f3d8cb ILLUSIONS: always use braces for loops
8e43261d13 ILLUSIONS: Formatting fixes
02eaa4c83d ILLUSIONS: Refactor savegame thumbnail logic to be inline with rest of tree
c9a3377408 ILLUSIONS: Standardized fall through comments
24a4c6367c ILLUSIONS: update _vm->_unpauseControlActorFlag when unpausing actors
c01a1269b6 ILLUSIONS: DUCKMAN: Clear fader when loading/unpausing backgrounds
960d79ed5a ILLUSIONS: DUCKMAN: Implement video player
f15335db0f ILLUSIONS: Remove old video logic
d5690d6025 ILLUSIONS: BBDOU: Fix text drawing colors
25a303a529 ILLUSIONS: BBDOU: Implement video player
b787b06156 ILLUSIONS: BBDOU: Formatting
4637104e6d ILLUSIONS: BBDOU: Implement missing talkthread events
a76612ec5a ILLUSIONS: Implement voice pausing/unpausing
54dd381441 ILLUSIONS: BBDOU: Play sound in BbdouSpecialCode::playSoundEffect
94fdd597d9 ILLUSIONS: BBDOU: Add menu system class, adjust existing code (actual menus not done yet)
71edfa3f0a ILLUSIONS: BBDOU: Add quit opcode
3fca484ae5 ILLUSIONS: Pan talk thread audio when using a named position
38577221c0 ILLUSIONS: BBDOU: Implement savegame saving/loading from GUI and the launcher
96a862536d ILLUSIONS: Implement sequence opcode 32
8ca26ea2a0 ILLUSIONS: Use actor flag instead of magic number
1f6aee019c ILLUSIONS: BBDOU: Rename variables (bubble and special code)
65049228a8 ILLUSIONS: Remove old TODOs
33ece271fd ILLUSIONS: Implement cursor movement via arrow keys
aee54f4bca ILLUSIONS: Minor cleanup
617e9439cf ILLUSIONS: DUCKMAN: Implement MIDI music fading
565de60234 ILLUSIONS: Clean up/fix some TODOs
baf8011b07 ILLUSIONS: Fix merge error.
848b5a5ff5 ILLUSIONS: Formatting fixes


Commit: 9a58385088ccf89393f71909eb3c87f3111006fa
    https://github.com/scummvm/scummvm/commit/9a58385088ccf89393f71909eb3c87f3111006fa
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Skeleton engine with detection for BBDOU

Changed paths:
  A engines/illusions/configure.engine
  A engines/illusions/detection.cpp
  A engines/illusions/illusions.cpp
  A engines/illusions/illusions.h
  A engines/illusions/module.mk


diff --git a/engines/illusions/configure.engine b/engines/illusions/configure.engine
new file mode 100644
index 0000000..c037c53
--- /dev/null
+++ b/engines/illusions/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine illusions "Illusions Engine" no
diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
new file mode 100644
index 0000000..88d63b4
--- /dev/null
+++ b/engines/illusions/detection.cpp
@@ -0,0 +1,172 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+
+#include "common/config-manager.h"
+#include "engines/advancedDetector.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "base/plugins.h"
+#include "graphics/thumbnail.h"
+
+static const PlainGameDescriptor illusionsGames[] = {
+	{ "illusions", "Illusions engine game" },
+	{ "bbdou", "Beavis and Butthead Do U" },
+	{ 0, 0 }
+};
+
+namespace Illusions {
+
+static const ADGameDescription gameDescriptions[] = {
+	{
+		"bbdou",
+		0,
+		AD_ENTRY1s("000D0001.scr", "d0c846d5dccc5607a482c7dcbdf06973", 601980),
+		Common::EN_ANY,
+		Common::kPlatformWindows,
+		ADGF_NO_FLAGS,
+		GUIO0()
+	},
+
+	AD_TABLE_END_MARKER
+};
+
+} // End of namespace Illusions
+
+static const char * const directoryGlobs[] = {
+	"resource",
+	0
+};
+
+class IllusionsMetaEngine : public AdvancedMetaEngine {
+public:
+	IllusionsMetaEngine() : AdvancedMetaEngine(Illusions::gameDescriptions, sizeof(ADGameDescription), illusionsGames) {
+		_singleid = "illusions";
+		_maxScanDepth = 2;
+		_directoryGlobs = directoryGlobs;
+	}
+
+	virtual const char *getName() const {
+		return "Illusions Engine";
+	}
+
+	virtual const char *getOriginalCopyright() const {
+		return "(C) The Illusions Gaming Company";
+	}
+
+	virtual bool hasFeature(MetaEngineFeature f) const;
+	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+#if 0
+	virtual int getMaximumSaveSlot() const;
+	virtual SaveStateList listSaves(const char *target) const;
+	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+	virtual void removeSaveState(const char *target, int slot) const;
+#endif
+};
+
+bool IllusionsMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+		false;
+		/*
+	    (f == kSupportsListSaves) ||
+	    (f == kSupportsDeleteSave) ||
+	    (f == kSupportsLoadingDuringStartup) ||
+	    (f == kSavesSupportMetaInfo) ||
+	    (f == kSavesSupportThumbnail) ||
+	    (f == kSavesSupportCreationDate);
+	    */
+}
+
+#if 0
+
+void IllusionsMetaEngine::removeSaveState(const char *target, int slot) const {
+	Common::String fileName = Common::String::format("%s.%03d", target, slot);
+	g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+int IllusionsMetaEngine::getMaximumSaveSlot() const {
+	return 999;
+}
+
+SaveStateList IllusionsMetaEngine::listSaves(const char *target) const {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Illusions::IllusionsEngine::SaveHeader header;
+	Common::String pattern = target;
+	pattern += ".???";
+	Common::StringArray filenames;
+	filenames = saveFileMan->listSavefiles(pattern.c_str());
+	Common::sort(filenames.begin(), filenames.end());	// Sort (hopefully ensuring we are sorted numerically..)
+	SaveStateList saveList;
+	for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+		// Obtain the last 3 digits of the filename, since they correspond to the save slot
+		int slotNum = atoi(file->c_str() + file->size() - 3);
+		if (slotNum >= 0 && slotNum <= 999) {
+			Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
+			if (in) {
+				if (Illusions::IllusionsEngine::readSaveHeader(in, false, header) == Illusions::IllusionsEngine::kRSHENoError) {
+					saveList.push_back(SaveStateDescriptor(slotNum, header.description));
+				}
+				delete in;
+			}
+		}
+	}
+	return saveList;
+}
+
+SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	Common::String filename = Illusions::IllusionsEngine::getSavegameFilename(target, slot);
+	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
+	if (in) {
+		Illusions::IllusionsEngine::SaveHeader header;
+		Illusions::IllusionsEngine::kReadSaveHeaderError error;
+		error = Illusions::IllusionsEngine::readSaveHeader(in, true, header);
+		delete in;
+		if (error == Illusions::IllusionsEngine::kRSHENoError) {		
+			SaveStateDescriptor desc(slot, header.description);
+			// Slot 0 is used for the "Continue" save
+			desc.setDeletableFlag(slot != 0);
+			desc.setWriteProtectedFlag(slot == 0);
+			desc.setThumbnail(header.thumbnail);
+			desc.setSaveDate(header.saveDate & 0xFFFF, (header.saveDate >> 16) & 0xFF, (header.saveDate >> 24) & 0xFF);
+			desc.setSaveTime((header.saveTime >> 16) & 0xFF, (header.saveTime >> 8) & 0xFF);
+			desc.setPlayTime(header.playTime * 1000);
+			return desc;
+		}
+	}
+	return SaveStateDescriptor();
+}
+
+#endif
+
+bool IllusionsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	if (desc) {
+		*engine = new Illusions::IllusionsEngine(syst, desc);
+	}
+	return desc != 0;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(ILLUSIONS)
+	REGISTER_PLUGIN_DYNAMIC(ILLUSIONS, PLUGIN_TYPE_ENGINE, IllusionsMetaEngine);
+#else
+	REGISTER_PLUGIN_STATIC(ILLUSIONS, PLUGIN_TYPE_ENGINE, IllusionsMetaEngine);
+#endif
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
new file mode 100644
index 0000000..da291ba
--- /dev/null
+++ b/engines/illusions/illusions.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+
+#include "audio/audiostream.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/timer.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/font.h"
+#include "graphics/fontman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+IllusionsEngine::IllusionsEngine(OSystem *syst, const ADGameDescription *gd) :
+	Engine(syst), _gameDescription(gd) {
+	
+	_random = new Common::RandomSource("illusions");
+	
+	Engine::syncSoundSettings();
+
+}
+
+IllusionsEngine::~IllusionsEngine() {
+
+	delete _random;
+
+}
+
+Common::Error IllusionsEngine::run() {
+
+	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	initGraphics(640, 480, true, &pixelFormat16);
+
+	while (!shouldQuit()) {
+		updateEvents();
+	}
+	
+	return Common::kNoError;
+}
+
+bool IllusionsEngine::hasFeature(EngineFeature f) const {
+	return
+		false;
+		/*
+		(f == kSupportsRTL) ||
+		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsSavingDuringRuntime);
+		*/
+}
+
+void IllusionsEngine::updateEvents() {
+	Common::Event event;
+
+	while (_eventMan->pollEvent(event)) {
+		switch (event.type) {
+		case Common::EVENT_KEYDOWN:
+			break;
+		case Common::EVENT_KEYUP:
+			break;
+		case Common::EVENT_MOUSEMOVE:
+  			break;
+		case Common::EVENT_LBUTTONDOWN:
+  			break;
+		case Common::EVENT_LBUTTONUP:
+  			break;
+		case Common::EVENT_RBUTTONDOWN:
+  			break;
+		case Common::EVENT_RBUTTONUP:
+  			break;
+		case Common::EVENT_QUIT:
+			quitGame();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
new file mode 100644
index 0000000..8027fc7
--- /dev/null
+++ b/engines/illusions/illusions.h
@@ -0,0 +1,103 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ILLUSIONS_H
+#define ILLUSIONS_ILLUSIONS_H
+
+#include "audio/mixer.h"
+#include "audio/decoders/aiff.h"
+#include "common/array.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/memstream.h"
+#include "common/random.h"
+#include "common/str.h"
+#include "common/substream.h"
+#include "common/system.h"
+#include "common/winexe.h"
+#include "common/winexe_pe.h"
+#include "engines/engine.h"
+
+struct ADGameDescription;
+
+namespace Illusions {
+
+#define ILLUSIONS_SAVEGAME_VERSION 0
+
+class IllusionsEngine : public Engine {
+protected:
+	Common::Error run();
+	virtual bool hasFeature(EngineFeature f) const;
+public:
+	IllusionsEngine(OSystem *syst, const ADGameDescription *gd);
+	~IllusionsEngine();
+	const Common::String getTargetName() { return _targetName; }
+private:
+	const ADGameDescription *_gameDescription;
+	Graphics::PixelFormat _pixelFormat;
+public:	
+	Common::RandomSource *_random;
+
+	void updateEvents();
+
+#if 0
+
+	// Savegame API
+
+	enum kReadSaveHeaderError {
+		kRSHENoError = 0,
+		kRSHEInvalidType = 1,
+		kRSHEInvalidVersion = 2,
+		kRSHEIoError = 3
+	};
+
+	struct SaveHeader {
+		Common::String description;
+		uint32 version;
+		byte gameID;
+		uint32 flags;
+		uint32 saveDate;
+		uint32 saveTime;
+		uint32 playTime;
+		Graphics::Surface *thumbnail;
+	};
+
+	bool _isSaveAllowed;
+
+	bool canLoadGameStateCurrently() { return _isSaveAllowed; }
+	bool canSaveGameStateCurrently() { return _isSaveAllowed; }
+	Common::Error loadGameState(int slot);
+	Common::Error saveGameState(int slot, const Common::String &description);
+	void savegame(const char *filename, const char *description);
+	void loadgame(const char *filename);
+	const char *getSavegameFilename(int num);
+	bool existsSavegame(int num);
+	static Common::String getSavegameFilename(const Common::String &target, int num);
+	static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header);
+
+#endif
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
new file mode 100644
index 0000000..5b08231
--- /dev/null
+++ b/engines/illusions/module.mk
@@ -0,0 +1,13 @@
+MODULE := engines/illusions
+
+MODULE_OBJS := \
+	illusions.o \
+	detection.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_ILLUSIONS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk


Commit: 7300dd09cd5d2b70e87d3a29faa562801b0767c5
    https://github.com/scummvm/scummvm/commit/7300dd09cd5d2b70e87d3a29faa562801b0767c5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Resource loader skeleton

Changed paths:
  A engines/illusions/resourcesystem.cpp
  A engines/illusions/resourcesystem.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index da291ba..5cd335e 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/illusions.h"
+#include "illusions/resourcesystem.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 5b08231..17ff3f4 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -2,7 +2,8 @@ MODULE := engines/illusions
 
 MODULE_OBJS := \
 	illusions.o \
-	detection.o
+	detection.o \
+	resourcesystem.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_ILLUSIONS), DYNAMIC_PLUGIN)
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
new file mode 100644
index 0000000..b5b7f80
--- /dev/null
+++ b/engines/illusions/resourcesystem.cpp
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+ResourceSystem::ResourceSystem() {
+}
+
+ResourceSystem::~ResourceSystem() {
+	// Delete all registered resource loaders
+	for (ResourceLoadersMapIterator it = _resourceLoaders.begin(); it != _resourceLoaders.end(); ++it)
+		delete (*it)._value;
+}
+
+void ResourceSystem::addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader) {
+	_resourceLoaders[resTypeId] = resourceLoader;
+}
+
+void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
+	BaseResourceLoader *resourceLoader = getResourceLoader(resId);
+
+	Resource *resource = new Resource();
+	resource->_loaded = 0;
+	resource->_resId = resId;
+	resource->_tag = tag;
+	resource->_threadId = threadId;
+	resource->_resourceLoader = resourceLoader;
+
+	resourceLoader->buildFilename(resource);
+
+	if (resourceLoader->isFlag(kRlfLoadFile)) {
+		// TODO Move to Resource class?
+		Common::File fd;
+		if (!fd.open(resource->filename))
+			error("ResourceSystem::loadResource() Could not open %s for reading", resource->filename.c_str());
+		resource->_dataSize = fd.size();
+		resource->_data = (byte*)malloc(resource->_dataSize);
+		fd.read(resource->_data, resource->_dataSize);
+	}
+	
+	resourceLoader->load(resource);
+	
+	if (resourceLoader->isFlag(kRlfFreeDataAfterUse)) {
+		// TODO Move to Resource class?
+		delete resource->_data;
+		resource->_data = 0;
+		resource->_dataSize = 0;
+	}
+	
+	resource->_loaded = true;
+
+	_resources.push_back(resource);
+	// TODO? Not sure if this is needed krnfileAdd(filenameb, taga);
+
+}
+
+BaseResourceLoader *ResourceSystem::getResourceLoader(uint32 resId) {
+	ResourceLoadersMapIterator it = _resourceLoaders.find(ResourceTypeId(resId));
+	if (it != _resourceLoaders.end())
+		return (*it)._value;
+	error("ResourceSystem::getResourceLoader() Could not find resource loader for resource id %08X", resId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
new file mode 100644
index 0000000..f5e2dfe
--- /dev/null
+++ b/engines/illusions/resourcesystem.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_RESOURCESYSTEM_H
+#define ILLUSIONS_RESOURCESYSTEM_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/hashmap.h"
+#include "common/memstream.h"
+#include "common/str.h"
+#include "common/substream.h"
+#include "common/system.h"
+
+namespace Illusions {
+
+#define ResourceTypeId(x) ((x) & 0xFFFF0000)
+
+class BaseResourceLoader;
+
+struct Resource {
+	bool _loaded;
+	uint32 _resId;
+	uint32 _tag;
+	uint32 _threadId;
+	byte *_data;
+	uint32 _dataSize;
+	BaseResourceLoader *_resourceLoader;
+	Common::String filename; // TODO Check if this is needed
+	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0),
+	_resourceLoader(0) {}
+};
+
+struct ResourceLoaderInfo {
+	Resource *_res;
+	byte *_data;
+	uint32 _dataSize;
+};
+
+enum {
+	kRlfLoadFile,
+	kRlfFreeDataAfterUse
+};
+
+class BaseResourceLoader {
+public:
+	virtual ~BaseResourceLoader() {}
+	virtual void load(Resource *resource) = 0;
+	virtual void unload(Resource *resource) = 0;
+	virtual void buildFilename(Resource *resource) = 0;
+	virtual bool isFlag(int flag) = 0;
+};
+
+// TODO Possibly split resource loaders from the system?
+
+class ResourceSystem {
+public:
+	ResourceSystem();
+	~ResourceSystem();
+
+	void addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader);
+	
+	// TODO Handle threadId in caller as well as pausing of timer
+	void loadResource(uint32 resId, uint32 tag, uint32 threadId);
+
+protected:
+	typedef Common::HashMap<uint32, BaseResourceLoader*> ResourceLoadersMap;
+	typedef ResourceLoadersMap::iterator ResourceLoadersMapIterator;
+	ResourceLoadersMap _resourceLoaders;
+
+	Common::Array<Resource*> _resources;
+	
+	BaseResourceLoader *getResourceLoader(uint32 resId);
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H


Commit: 26487555b1c49fa95c3ebcc20c9b8228629c0552
    https://github.com/scummvm/scummvm/commit/26487555b1c49fa95c3ebcc20c9b8228629c0552
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with BackgroundResource

Changed paths:
  A engines/illusions/backgroundresource.cpp
  A engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h


diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
new file mode 100644
index 0000000..34d1e98
--- /dev/null
+++ b/engines/illusions/backgroundresource.cpp
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/backgroundresource.h"
+#include "common/str.h"
+
+namespace Illusions {
+
+// BackgroundResourceLoader
+
+void BackgroundResourceLoader::load(Resource *resource) {
+	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
+}
+
+void BackgroundResourceLoader::unload(Resource *resource) {
+}
+
+void BackgroundResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.bg", resource->_resId);
+}
+
+bool BackgroundResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
new file mode 100644
index 0000000..58eed03
--- /dev/null
+++ b/engines/illusions/backgroundresource.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BACKGROUNDRESOURCE_H
+#define ILLUSIONS_BACKGROUNDRESOURCE_H
+
+#include "illusions/resourcesystem.h"
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/memstream.h"
+#include "common/substream.h"
+#include "common/system.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class BackgroundResourceLoader : public BaseResourceLoader {
+public:
+	BackgroundResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~BackgroundResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BACKGROUNDRESOURCE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 5cd335e..a2e8ee4 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/resourcesystem.h"
+#include "illusions/backgroundresource.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -55,13 +56,30 @@ IllusionsEngine::~IllusionsEngine() {
 
 Common::Error IllusionsEngine::run() {
 
+	// Init search paths
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
+
 	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
 	initGraphics(640, 480, true, &pixelFormat16);
+	
+	_resSys = new ResourceSystem();
+	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	
+	_resSys->loadResource(0x00110002, 0, 0);
 
 	while (!shouldQuit()) {
 		updateEvents();
 	}
 	
+	delete _resSys;
+	
 	return Common::kNoError;
 }
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 8027fc7..f35f1bb 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -43,6 +43,8 @@ namespace Illusions {
 
 #define ILLUSIONS_SAVEGAME_VERSION 0
 
+class ResourceSystem;
+
 class IllusionsEngine : public Engine {
 protected:
 	Common::Error run();
@@ -56,6 +58,7 @@ private:
 	Graphics::PixelFormat _pixelFormat;
 public:	
 	Common::RandomSource *_random;
+	ResourceSystem *_resSys;
 
 	void updateEvents();
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 17ff3f4..3cf4e1f 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/illusions
 
 MODULE_OBJS := \
+	backgroundresource.o \
 	illusions.o \
 	detection.o \
 	resourcesystem.o
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index b5b7f80..f30f46b 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -22,8 +22,29 @@
 
 #include "illusions/resourcesystem.h"
 
+#include "common/algorithm.h"
+
 namespace Illusions {
 
+// Resource
+
+void Resource::loadData() {
+	Common::File fd;
+	if (!fd.open(_filename))
+		error("Resource::loadData() Could not open %s for reading", _filename.c_str());
+	_dataSize = fd.size();
+	_data = (byte*)malloc(_dataSize);
+	fd.read(_data, _dataSize);
+}
+
+void Resource::unloadData() {
+	delete _data;
+	_data = 0;
+	_dataSize = 0;
+}
+
+// ResourceSystem
+
 ResourceSystem::ResourceSystem() {
 }
 
@@ -41,7 +62,7 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 	BaseResourceLoader *resourceLoader = getResourceLoader(resId);
 
 	Resource *resource = new Resource();
-	resource->_loaded = 0;
+	resource->_loaded = false;
 	resource->_resId = resId;
 	resource->_tag = tag;
 	resource->_threadId = threadId;
@@ -49,24 +70,13 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 
 	resourceLoader->buildFilename(resource);
 
-	if (resourceLoader->isFlag(kRlfLoadFile)) {
-		// TODO Move to Resource class?
-		Common::File fd;
-		if (!fd.open(resource->filename))
-			error("ResourceSystem::loadResource() Could not open %s for reading", resource->filename.c_str());
-		resource->_dataSize = fd.size();
-		resource->_data = (byte*)malloc(resource->_dataSize);
-		fd.read(resource->_data, resource->_dataSize);
-	}
+	if (resourceLoader->isFlag(kRlfLoadFile))
+		resource->loadData();
 	
 	resourceLoader->load(resource);
 	
-	if (resourceLoader->isFlag(kRlfFreeDataAfterUse)) {
-		// TODO Move to Resource class?
-		delete resource->_data;
-		resource->_data = 0;
-		resource->_dataSize = 0;
-	}
+	if (resourceLoader->isFlag(kRlfFreeDataAfterUse))
+		resource->unloadData();
 	
 	resource->_loaded = true;
 
@@ -75,6 +85,20 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 
 }
 
+void ResourceSystem::unloadResourceById(uint32 resId) {
+	Resource *resource = getResource(resId);
+	if (resource) 
+		unloadResource(resource);
+}
+
+void ResourceSystem::unloadResourcesByTag(uint32 tag) {
+	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByTag(tag));
+	while (it != _resources.end()) {
+		unloadResource(*it);
+		it = Common::find_if(it, _resources.end(), ResourceEqualByTag(tag));
+	}
+}
+
 BaseResourceLoader *ResourceSystem::getResourceLoader(uint32 resId) {
 	ResourceLoadersMapIterator it = _resourceLoaders.find(ResourceTypeId(resId));
 	if (it != _resourceLoaders.end())
@@ -82,4 +106,17 @@ BaseResourceLoader *ResourceSystem::getResourceLoader(uint32 resId) {
 	error("ResourceSystem::getResourceLoader() Could not find resource loader for resource id %08X", resId);
 }
 
+Resource *ResourceSystem::getResource(uint32 resId) {
+	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualById(resId));
+	return it != _resources.end() ? *it : 0;
+}
+
+void ResourceSystem::unloadResource(Resource *resource) {
+	resource->_resourceLoader->unload(resource);
+	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByValue(resource));
+	if (it != _resources.end())
+		_resources.remove_at(it - _resources.begin());
+	delete resource;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index f5e2dfe..18feb51 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -45,9 +45,14 @@ struct Resource {
 	byte *_data;
 	uint32 _dataSize;
 	BaseResourceLoader *_resourceLoader;
-	Common::String filename; // TODO Check if this is needed
+	Common::String _filename; // TODO Check if this is needed
 	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0),
 	_resourceLoader(0) {}
+	~Resource() {
+		unloadData();
+	}
+	void loadData();
+	void unloadData();
 };
 
 struct ResourceLoaderInfo {
@@ -81,18 +86,48 @@ public:
 	
 	// TODO Handle threadId in caller as well as pausing of timer
 	void loadResource(uint32 resId, uint32 tag, uint32 threadId);
+	void unloadResourceById(uint32 resId);
+	void unloadResourcesByTag(uint32 tag);
 
 protected:
 	typedef Common::HashMap<uint32, BaseResourceLoader*> ResourceLoadersMap;
 	typedef ResourceLoadersMap::iterator ResourceLoadersMapIterator;
 	ResourceLoadersMap _resourceLoaders;
-
-	Common::Array<Resource*> _resources;
-	
 	BaseResourceLoader *getResourceLoader(uint32 resId);
+
+	typedef Common::Array<Resource*> ResourcesArray;
+	typedef ResourcesArray::iterator ResourcesArrayIterator;
+	ResourcesArray _resources;
+
+	struct ResourceEqualById : public Common::UnaryFunction<const Resource*, bool> {
+		uint32 _resId;
+		ResourceEqualById(uint32 resId) : _resId(resId) {}
+		bool operator()(const Resource *resource) const {
+			return resource->_resId == _resId;
+		}
+	};
+
+	struct ResourceEqualByValue : public Common::UnaryFunction<const Resource*, bool> {
+		const Resource *_resource;
+		ResourceEqualByValue(const Resource *resource) : _resource(resource) {}
+		bool operator()(const Resource *resource) const {
+			return resource == _resource;
+		}
+	};
+
+	struct ResourceEqualByTag : public Common::UnaryFunction<const Resource*, bool> {
+		uint32 _tag;
+		ResourceEqualByTag(uint32 tag) : _tag(tag) {}
+		bool operator()(const Resource *resource) const {
+			return resource->_tag == _tag;
+		}
+	};
+
+	Resource *getResource(uint32 resId);
+	void unloadResource(Resource *resource);
 	
 };
 
 } // End of namespace Illusions
 
-#endif // ILLUSIONS_ILLUSIONS_H
+#endif // ILLUSIONS_RESOURCESYSTEM_H


Commit: 5833ceda4b7e6eaf77ef5c51f7a47c1c7f92add1
    https://github.com/scummvm/scummvm/commit/5833ceda4b7e6eaf77ef5c51f7a47c1c7f92add1
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on BackgroundResource

Changed paths:
  A engines/illusions/graphics.cpp
  A engines/illusions/graphics.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/resourcesystem.cpp


diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 34d1e98..246172f 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -29,7 +29,26 @@ namespace Illusions {
 // BackgroundResourceLoader
 
 void BackgroundResourceLoader::load(Resource *resource) {
+	// TODO
 	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+	BackgroundResource *backgroundResource = new BackgroundResource();
+	backgroundResource->load(resource->_data, resource->_dataSize);
+
+	BackgroundItem *backgroundItem = _vm->allocBackgroundItem();
+	backgroundItem->_bgRes = backgroundResource;
+	backgroundItem->_tag = resource->_tag;
+	
+	backgroundItem->initSurface();
+	
+	// TODO Insert objects from item44s
+	// TODO Insert IDs from item48s
+
+	// TODO camera_fadeClear();
+	// TODO bgInfo = &bgResourceb->bgInfos[(unsigned __int16)BgResource_findMasterBgIndex(bgResourceb)];
+	// TODO camera_set(bgInfo[-1].panPoint, bgInfo[-1].surfInfo.dimensions);
+	
+	// NOTE Skipped palette loading (not used in BBDOU)
 }
 
 void BackgroundResourceLoader::unload(Resource *resource) {
@@ -44,4 +63,122 @@ bool BackgroundResourceLoader::isFlag(int flag) {
 		flag == kRlfLoadFile;
 }
 
+// BackgroundItem
+
+BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
+}
+
+void BackgroundItem::initSurface() {
+
+	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
+		_surfaces[i] = 0;
+
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
+		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
+		_panPoints[i] = bgInfo->_panPoint;
+		_surfaces[i] = _vm->allocSurface(bgInfo->_surfInfo);
+		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
+	}
+
+}
+
+void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	const int kTileWidth = 32;
+	const int kTileHeight = 8;
+	const int kTileSize = kTileWidth * kTileHeight * 2;
+	uint tileMapIndex = 0;
+	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
+		int tileDestY = tileY * kTileHeight;
+		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
+		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
+			int tileDestX = tileX * kTileWidth;
+			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
+			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
+			++tileMapIndex;
+			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
+			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
+			for (int h = 0; h < tileDestH; ++h) {
+				for (int w = 0; w < tileDestW; ++w) {
+					uint16 pixel = READ_LE_UINT16(src + w * 2);
+					WRITE_LE_UINT16(dst + w * 2, pixel);
+				}
+				dst += surface->pitch;
+				src += kTileWidth * 2;
+			}
+		}
+	}
+
+	/*	
+	Common::DumpFile d;
+	d.open("dump.000");
+	d.write(surface->getPixels(), surface->h * surface->pitch);
+	d.close();
+	*/
+
+}
+
+// TileMap
+
+void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_width = stream.readSint16LE();
+	_height = stream.readSint16LE();
+	stream.skip(4); // Unknown
+	uint32 mapOffs = stream.pos();
+	_map = dataStart + mapOffs;
+	
+	debug("TileMap::load() _width: %d; _height: %d",
+		_width, _height);
+}
+
+// BgInfo
+
+void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_flags = stream.readUint32LE();
+	stream.skip(2); // Unknown
+	_priorityBase = stream.readSint16LE();
+	_surfInfo.load(stream);
+	loadPoint(stream, _panPoint);
+	uint32 tileMapOffs = stream.readUint32LE();
+	uint32 tilePixelsOffs = stream.readUint32LE();
+	stream.seek(tileMapOffs);
+	_tileMap.load(dataStart, stream);
+	_tilePixels = dataStart + tilePixelsOffs;
+	
+	debug("BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
+		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
+}
+
+// BackgroundResource
+
+BackgroundResource::BackgroundResource() {
+}
+
+BackgroundResource::~BackgroundResource() {
+	// TODO Free stuff
+}
+
+void BackgroundResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+	// TODO A lot
+	
+	// Load background pixels
+	stream.seek(0x0A);
+	_bgInfosCount = stream.readUint16LE();
+	_bgInfos = new BgInfo[_bgInfosCount];
+	stream.seek(0x20);
+	uint32 bgInfosOffs = stream.readUint32LE();
+	for (uint i = 0; i < _bgInfosCount; ++i) {
+		stream.seek(bgInfosOffs + i * 0x1C);
+		_bgInfos[i].load(data, stream);
+	}
+
+}
+
+int BackgroundResource::findMasterBgIndex() {
+	int index = 1;
+	while (!_bgInfos[index - 1]._flags & 1)
+		++index;
+	return index;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 58eed03..ab82687 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -23,11 +23,14 @@
 #ifndef ILLUSIONS_BACKGROUNDRESOURCE_H
 #define ILLUSIONS_BACKGROUNDRESOURCE_H
 
+#include "illusions/graphics.h"
 #include "illusions/resourcesystem.h"
+#include "graphics/surface.h"
 
 #include "common/array.h"
 #include "common/file.h"
 #include "common/memstream.h"
+#include "common/rect.h"
 #include "common/substream.h"
 #include "common/system.h"
 
@@ -47,6 +50,55 @@ protected:
 	IllusionsEngine *_vm;
 };
 
+struct TileMap {
+	int16 _width, _height;
+	//field_4 dd
+	byte *_map;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct BgInfo {
+	uint32 _flags;
+	//field_4 dw
+	int16 _priorityBase;
+	SurfInfo _surfInfo;
+	Common::Point _panPoint;
+	TileMap _tileMap;
+	byte *_tilePixels;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class BackgroundResource {
+public:
+	BackgroundResource();
+	~BackgroundResource();
+	void load(byte *data, uint32 dataSize);
+	int findMasterBgIndex();
+public:
+
+	uint _bgInfosCount;
+	BgInfo *_bgInfos;
+
+};
+
+const uint kMaxBackgroundItemSurfaces = 3;
+
+class BackgroundItem {
+public:
+	BackgroundItem(IllusionsEngine *vm);
+	~BackgroundItem();
+	void initSurface();
+	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
+public:
+	IllusionsEngine *_vm;
+	uint32 _tag;
+	int _pauseCtr;
+	BackgroundResource *_bgRes;
+	Common::Point _panPoints[kMaxBackgroundItemSurfaces];
+	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
+	// TODO SavedCamera savedCamera;
+	// TODO? byte *savedPalette;
+};
 
 } // End of namespace Illusions
 
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
new file mode 100644
index 0000000..0f27a36
--- /dev/null
+++ b/engines/illusions/graphics.cpp
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/graphics.h"
+
+namespace Illusions {
+
+void WidthHeight::load(Common::SeekableReadStream &stream) {
+	_width = stream.readSint16LE();
+	_height = stream.readSint16LE();
+	
+	debug("WidthHeight::load() _width: %d; _height: %d",
+		_width, _height);
+}
+
+void SurfInfo::load(Common::SeekableReadStream &stream) {
+	_pixelSize = stream.readUint32LE();
+	_dimensions.load(stream);
+	
+	debug("SurfInfo::load() _pixelSize: %d",
+		_pixelSize);
+}
+
+void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
+	pt.x = stream.readSint16LE();
+	pt.y = stream.readSint16LE();
+	
+	debug("loadPoint() x: %d; y: %d",
+		pt.x, pt.y);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/graphics.h b/engines/illusions/graphics.h
new file mode 100644
index 0000000..0b16a0c
--- /dev/null
+++ b/engines/illusions/graphics.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_GRAPHICS_H
+#define ILLUSIONS_GRAPHICS_H
+
+#include "common/rect.h"
+#include "common/stream.h"
+
+namespace Illusions {
+
+struct WidthHeight {
+	int16 _width, _height;
+	void load(Common::SeekableReadStream &stream);
+};
+
+struct SurfInfo {
+	uint32 _pixelSize;
+	WidthHeight _dimensions;
+	void load(Common::SeekableReadStream &stream);
+};
+
+void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt);
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_GRAPHICS_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index a2e8ee4..d0e5b59 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/graphics.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -72,7 +73,12 @@ Common::Error IllusionsEngine::run() {
 	_resSys = new ResourceSystem();
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 	
-	_resSys->loadResource(0x00110002, 0, 0);
+	_resSys->loadResource(0x0011000B, 0, 0);
+
+	BackgroundItem *backgroundItem = *(_backgroundItems.begin());
+	_system->copyRectToScreen(backgroundItem->_surfaces[0]->getPixels(), backgroundItem->_surfaces[0]->pitch,
+		0, 0, 640, 480);
+	_system->updateScreen();
 
 	while (!shouldQuit()) {
 		updateEvents();
@@ -121,4 +127,22 @@ void IllusionsEngine::updateEvents() {
 	}
 }
 
+BackgroundItem *IllusionsEngine::allocBackgroundItem() {
+	BackgroundItem *backgroundItem = new BackgroundItem(this);
+	_backgroundItems.push_back(backgroundItem);
+	return backgroundItem;
+}
+
+Graphics::Surface *IllusionsEngine::allocSurface(int16 width, int16 height) {
+	// TODO Use screen pixel format?
+	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->create(width, height, pixelFormat16);
+	return surface; 
+}
+
+Graphics::Surface *IllusionsEngine::allocSurface(SurfInfo &surfInfo) {
+	return allocSurface(surfInfo._dimensions._width, surfInfo._dimensions._height);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index f35f1bb..4ab829a 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -36,6 +36,7 @@
 #include "common/winexe.h"
 #include "common/winexe_pe.h"
 #include "engines/engine.h"
+#include "graphics/surface.h"
 
 struct ADGameDescription;
 
@@ -45,6 +46,11 @@ namespace Illusions {
 
 class ResourceSystem;
 
+struct SurfInfo;
+
+class BackgroundItem;
+class BackgroundResource;
+
 class IllusionsEngine : public Engine {
 protected:
 	Common::Error run();
@@ -53,15 +59,22 @@ public:
 	IllusionsEngine(OSystem *syst, const ADGameDescription *gd);
 	~IllusionsEngine();
 	const Common::String getTargetName() { return _targetName; }
+	
 private:
 	const ADGameDescription *_gameDescription;
 	Graphics::PixelFormat _pixelFormat;
 public:	
 	Common::RandomSource *_random;
 	ResourceSystem *_resSys;
-
+	
 	void updateEvents();
 
+	Common::List<BackgroundItem*> _backgroundItems;
+	BackgroundItem *allocBackgroundItem();
+	
+	Graphics::Surface *allocSurface(int16 width, int16 height);
+	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
+
 #if 0
 
 	// Savegame API
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 3cf4e1f..1799409 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -2,8 +2,9 @@ MODULE := engines/illusions
 
 MODULE_OBJS := \
 	backgroundresource.o \
-	illusions.o \
 	detection.o \
+	graphics.o \
+	illusions.o \
 	resourcesystem.o
 
 # This module can be built as a plugin
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index f30f46b..01a076b 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -23,12 +23,15 @@
 #include "illusions/resourcesystem.h"
 
 #include "common/algorithm.h"
+#include "common/debug.h"
 
 namespace Illusions {
 
 // Resource
 
 void Resource::loadData() {
+	debug("Resource::loadData()");
+	
 	Common::File fd;
 	if (!fd.open(_filename))
 		error("Resource::loadData() Could not open %s for reading", _filename.c_str());
@@ -38,6 +41,8 @@ void Resource::loadData() {
 }
 
 void Resource::unloadData() {
+	debug("Resource::unloadData()");
+	
 	delete _data;
 	_data = 0;
 	_dataSize = 0;
@@ -70,13 +75,17 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 
 	resourceLoader->buildFilename(resource);
 
-	if (resourceLoader->isFlag(kRlfLoadFile))
+	if (resourceLoader->isFlag(kRlfLoadFile)) {
+		debug("ResourceSystem::loadResource() kRlfLoadFile");
 		resource->loadData();
+	}
 	
 	resourceLoader->load(resource);
 	
-	if (resourceLoader->isFlag(kRlfFreeDataAfterUse))
+	if (resourceLoader->isFlag(kRlfFreeDataAfterUse)) {
+		debug("ResourceSystem::loadResource() kRlfFreeDataAfterUse");
 		resource->unloadData();
+	}
 	
 	resource->_loaded = true;
 


Commit: a06895ad594c7dd8b56bbb96993fa7af4b4e2d59
    https://github.com/scummvm/scummvm/commit/a06895ad594c7dd8b56bbb96993fa7af4b4e2d59
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with Camera and Input classes

Changed paths:
  A engines/illusions/camera.cpp
  A engines/illusions/camera.h
  A engines/illusions/input.cpp
  A engines/illusions/input.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
new file mode 100644
index 0000000..5f8b2a0
--- /dev/null
+++ b/engines/illusions/camera.cpp
@@ -0,0 +1,31 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/camera.h"
+
+namespace Illusions {
+
+Common::Point Camera::getCurrentPan() {
+	return _currPan;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
new file mode 100644
index 0000000..d55ab8e
--- /dev/null
+++ b/engines/illusions/camera.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_CAMERA_H
+#define ILLUSIONS_CAMERA_H
+
+#include "common/rect.h"
+
+namespace Illusions {
+
+class Camera {
+public:
+	Common::Point getCurrentPan();
+protected:
+	Common::Point _currPan;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_CAMERA_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index d0e5b59..0798303 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -23,7 +23,9 @@
 #include "illusions/illusions.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/camera.h"
 #include "illusions/graphics.h"
+#include "illusions/input.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
new file mode 100644
index 0000000..fd922ec
--- /dev/null
+++ b/engines/illusions/input.cpp
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/input.h"
+
+namespace Illusions {
+
+Input::Input() {
+	_buttonStates = 0;
+	_newButtons = 0;
+	// TODO? _buttonsDown = 0;
+	// TODO? _unk6 = 0;
+	_enabledButtons = 0xFFFF;
+	_cursorPos.x = 0;
+	_cursorPos.y = 0;
+	_prevCursorPos.x = 0;
+	_prevCursorPos.y = 0;
+	// TODO Not sure if this is still needed newTimer(40, 0, 0, Input_onTimer);
+}
+
+void Input::processEvent(Common::Event event) {
+	// TODO
+}
+
+bool Input::pollButton(uint buttons) {
+	if (lookButtonStates(buttons)) {
+		_buttonStates &= ~buttons;
+		return true;
+	}
+	return false;
+}
+
+bool Input::lookButtonStates(uint buttons) {
+	return (buttons & (_buttonStates & _enabledButtons)) != 0;
+}
+
+bool Input::lookNewButtons(uint buttons) {
+	return (buttons & (_newButtons & _enabledButtons)) != 0;
+}
+
+void Input::setButtonState(uint buttons) {
+	_buttonStates |= _enabledButtons & buttons;
+}
+
+void Input::discardButtons(uint buttons) {
+	_buttonStates &= ~buttons;
+}
+
+void Input::activateButton(uint buttons) {
+	_enabledButtons |= buttons;
+	_buttonStates &= ~buttons;
+}
+
+void Input::deactivateButton(uint buttons) {
+	_enabledButtons &= ~buttons;
+}
+
+Common::Point Input::getCursorPosition() {
+	return _cursorPos;
+}
+
+void Input::setCursorPosition(Common::Point mousePos) {
+	_prevCursorPos = _cursorPos = mousePos;
+}
+
+Common::Point Input::getCursorDelta() {
+	Common::Point deltaPos;
+	deltaPos.x = _prevCursorPos.x - _cursorPos.x;
+	deltaPos.y = _prevCursorPos.y - _cursorPos.y;
+	_prevCursorPos = _cursorPos;
+	return deltaPos;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
new file mode 100644
index 0000000..700d9ed
--- /dev/null
+++ b/engines/illusions/input.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_INPUT_H
+#define ILLUSIONS_INPUT_H
+
+#include "common/events.h"
+#include "common/rect.h"
+
+namespace Illusions {
+
+class Input {
+public:
+	Input();
+	void processEvent(Common::Event event);
+	bool pollButton(uint buttons);
+	bool lookButtonStates(uint buttons);
+	bool lookNewButtons(uint buttons);
+	void setButtonState(uint buttons);
+	void discardButtons(uint buttons);
+	void activateButton(uint buttons);
+	void deactivateButton(uint buttons);
+	Common::Point getCursorPosition();
+	void setCursorPosition(Common::Point mousePos);
+	Common::Point getCursorDelta();
+protected:
+	uint _buttonStates, _newButtons;
+	uint _enabledButtons;
+	Common::Point _cursorPos, _prevCursorPos;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_INPUT_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 1799409..bfe521b 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -2,9 +2,11 @@ MODULE := engines/illusions
 
 MODULE_OBJS := \
 	backgroundresource.o \
+	camera.o \
 	detection.o \
 	graphics.o \
 	illusions.o \
+	input.o \
 	resourcesystem.o
 
 # This module can be built as a plugin


Commit: bf09487df06e86923d6a4fe9765e9bd6d013e9d4
    https://github.com/scummvm/scummvm/commit/bf09487df06e86923d6a4fe9765e9bd6d013e9d4
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add UpdateFunctions class

Changed paths:
  A engines/illusions/updatefunctions.cpp
  A engines/illusions/updatefunctions.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 0798303..091b381 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -26,6 +26,7 @@
 #include "illusions/camera.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
+#include "illusions/updatefunctions.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index bfe521b..0fb336c 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -7,7 +7,8 @@ MODULE_OBJS := \
 	graphics.o \
 	illusions.o \
 	input.o \
-	resourcesystem.o
+	resourcesystem.o \
+	updatefunctions.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_ILLUSIONS), DYNAMIC_PLUGIN)
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
new file mode 100644
index 0000000..9eb626c
--- /dev/null
+++ b/engines/illusions/updatefunctions.cpp
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/updatefunctions.h"
+#include "common/algorithm.h"
+#include "common/system.h"
+
+namespace Illusions {
+
+uint32 getCurrentTime() {
+	// TODO, move to own file with other time related code
+	return g_system->getMillis();
+}
+
+// UpdateFunctions
+
+UpdateFunctions::UpdateFunctions() {
+}
+
+UpdateFunctions::~UpdateFunctions() {
+	// Free update functions
+	for (UpdateFunctionListIterator it = _updateFunctions.begin(); it != _updateFunctions.end(); ++it)
+		delete *it;
+}
+
+void UpdateFunctions::add(int priority, uint32 tag, UpdateFunctionCallback *callback) {
+	UpdateFunction *updateFunction = new UpdateFunction();
+	updateFunction->_priority = priority;
+	updateFunction->_tag = tag;
+	updateFunction->_callback = callback;
+	UpdateFunctionListIterator insertionPos = Common::find_if(_updateFunctions.begin(), _updateFunctions.end(),
+		FindInsertionPosition(priority));
+	_updateFunctions.insert(insertionPos, updateFunction);
+}
+
+void UpdateFunctions::update() {
+
+	// Avoid running updates multiple times in the current time slice
+	while (_lastTimerUpdateTime == getCurrentTime())
+		g_system->delayMillis(10); // CHECKME Timer resolution
+	_lastTimerUpdateTime = getCurrentTime();
+
+	UpdateFunctionListIterator it = _updateFunctions.begin();
+	while (it != _updateFunctions.end()) {
+		int r = (*it)->run();
+		switch (r) {
+		case kUFNext:
+			++it;
+			break;
+		case kUFTerminate:
+			delete *it;
+			it = _updateFunctions.erase(it);
+			break;
+		default:
+			break;
+		}
+	}
+
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/updatefunctions.h b/engines/illusions/updatefunctions.h
new file mode 100644
index 0000000..bd8aef3
--- /dev/null
+++ b/engines/illusions/updatefunctions.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_UPDATEFUNCTIONS_H
+#define ILLUSIONS_UPDATEFUNCTIONS_H
+
+#include "common/func.h"
+#include "common/list.h"
+
+namespace Illusions {
+
+enum {
+	kUFNext         = 1,  // Run next update funtion
+	kUFTerminate    = 2   // Terminate update function
+};
+
+typedef Common::Functor0<int> UpdateFunctionCallback;
+
+class UpdateFunction {
+public:
+	int _priority;
+	uint32 _tag;
+	UpdateFunctionCallback *_callback;
+	UpdateFunction() : _priority(0), _tag(0), _callback(0) {}
+	~UpdateFunction() { delete _callback; }
+	int run() { return (*_callback)(); }
+};
+
+class UpdateFunctions {
+public:
+	UpdateFunctions();
+	~UpdateFunctions();
+	void add(int priority, uint32 tag, UpdateFunctionCallback *callback);
+	void update();
+protected:
+	typedef Common::List<UpdateFunction*> UpdateFunctionList;
+	typedef UpdateFunctionList::iterator UpdateFunctionListIterator;
+
+	struct FindInsertionPosition : public Common::UnaryFunction<const UpdateFunction*, bool> {
+		int _priority;
+		FindInsertionPosition(int priority) : _priority(priority) {}
+		bool operator()(const UpdateFunction *updateFunction) const {
+			return updateFunction->_priority > _priority;
+		}
+	};
+
+	Common::List<UpdateFunction*> _updateFunctions;
+	uint32 _lastTimerUpdateTime;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_UPDATEFUNCTIONS_H


Commit: ee59e736d38be2c2478af0587ec5e8ee5c992119
    https://github.com/scummvm/scummvm/commit/ee59e736d38be2c2478af0587ec5e8ee5c992119
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with SpriteDrawQueue

Changed paths:
  A engines/illusions/spritedrawqueue.cpp
  A engines/illusions/spritedrawqueue.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 091b381..cc8a92e 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -27,6 +27,7 @@
 #include "illusions/graphics.h"
 #include "illusions/input.h"
 #include "illusions/updatefunctions.h"
+#include "illusions/spritedrawqueue.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -148,4 +149,19 @@ Graphics::Surface *IllusionsEngine::allocSurface(SurfInfo &surfInfo) {
 	return allocSurface(surfInfo._dimensions._width, surfInfo._dimensions._height);
 }
 
+bool IllusionsEngine::isDisplayOn() {
+	// TODO Move this outside into a screen class
+	return true;
+}
+
+uint16 IllusionsEngine::getColorKey2() {
+	// TODO Move this outside into a screen class
+	return 0;
+}
+
+Graphics::Surface *IllusionsEngine::getBackSurface() {
+	// TODO Move this outside into a screen class
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 4ab829a..39e1b86 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -71,9 +71,13 @@ public:
 
 	Common::List<BackgroundItem*> _backgroundItems;
 	BackgroundItem *allocBackgroundItem();
-	
+
+	// Screen functions	
 	Graphics::Surface *allocSurface(int16 width, int16 height);
 	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
+	bool isDisplayOn();
+	uint16 getColorKey2();
+	Graphics::Surface *getBackSurface();
 
 #if 0
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 0fb336c..0e095bd 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
 	illusions.o \
 	input.o \
 	resourcesystem.o \
+	spritedrawqueue.o \
 	updatefunctions.o
 
 # This module can be built as a plugin
diff --git a/engines/illusions/spritedrawqueue.cpp b/engines/illusions/spritedrawqueue.cpp
new file mode 100644
index 0000000..a21ca15
--- /dev/null
+++ b/engines/illusions/spritedrawqueue.cpp
@@ -0,0 +1,196 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/spritedrawqueue.h"
+
+namespace Illusions {
+
+SpriteDrawQueue::SpriteDrawQueue(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+SpriteDrawQueue::~SpriteDrawQueue() {
+}
+
+bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
+
+	// Check if the sprite has finished decompressing
+	if (item->_kind != 0 && (*item->_drawFlags & 1)) {		
+		insert(item, item->_priority);
+		return false;
+	}
+
+	if (!_vm->isDisplayOn()) {
+		if (item->_drawFlags)
+			*item->_drawFlags &= ~4;
+		return true;			
+	}	
+
+	Common::Rect srcRect, dstRect;
+	
+	// Check if the sprite is on-screen
+	if (!calcItemRect(item, srcRect, dstRect))
+		return true;
+
+	_backSurface = _vm->getBackSurface();
+
+	if (item->_scale == 100) {
+		if (item->_flags & 1)
+			drawSurface10(dstRect.left, dstRect.top, item->_surface, srcRect, _vm->getColorKey2());
+		else
+			drawSurface11(dstRect.left, dstRect.top, item->_surface, srcRect);
+	} else {
+		if (item->_flags & 1)
+			drawSurface20(dstRect, item->_surface, srcRect, _vm->getColorKey2());
+		else
+			drawSurface21(dstRect, item->_surface, srcRect);
+	}
+	
+	if (item->_drawFlags)
+		*item->_drawFlags &= ~4;
+
+	return true;
+}
+
+void SpriteDrawQueue::insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
+	Common::Point &drawPosition, Common::Point &controlPosition, int priority, int16 scale, uint16 flags) {
+	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
+	item->_drawFlags = drawFlags;
+	*item->_drawFlags &= 4;
+	item->_surface = surface;
+	item->_dimensions = dimensions;
+	item->_controlPosition = controlPosition;
+	item->_scale = scale;
+	item->_priority = priority;
+	item->_drawPosition = drawPosition;
+	item->_kind = 1;
+	item->_flags = flags;
+	insert(item, priority);
+}
+
+void SpriteDrawQueue::insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+	Common::Point &drawPosition, int priority) {
+	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
+	item->_surface = surface;
+	item->_dimensions = dimensions;
+	item->_drawFlags = 0;
+	item->_kind = 0;
+	item->_drawPosition.x = -drawPosition.x;
+	item->_drawPosition.y = -drawPosition.y;
+	item->_controlPosition.x = 0;
+	item->_controlPosition.y = 0;
+	item->_flags = 0;
+	item->_scale = 100;
+	item->_priority = priority << 16;
+	insert(item, priority);
+}
+
+void SpriteDrawQueue::insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+	Common::Point &drawPosition, int priority) {
+	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
+	item->_surface = surface;
+	item->_drawPosition = drawPosition;
+	item->_dimensions = dimensions;
+	item->_drawFlags = 0;
+	item->_kind = 0;
+	item->_controlPosition.x = 0;
+	item->_controlPosition.y = 0;
+	item->_flags = 0;
+	item->_priority = priority;
+	item->_scale = 100;
+	insert(item, priority);
+}
+
+void SpriteDrawQueue::insert(SpriteDrawQueueItem *item, int priority) {
+	SpriteDrawQueueListIterator insertionPos = Common::find_if(_queue.begin(), _queue.end(),
+		FindInsertionPosition(priority));
+	_queue.insert(insertionPos, item);
+}
+
+bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect) {
+
+	srcRect.left = 0;
+	srcRect.top = 0;
+	srcRect.right = item->_dimensions._width;
+	srcRect.bottom = item->_dimensions._height;
+
+	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
+	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
+	dstRect.right = item->_drawPosition.x + item->_scale * (item->_dimensions._width - item->_controlPosition.x) / 100;
+	dstRect.bottom = item->_drawPosition.y + item->_scale * (item->_dimensions._height - item->_controlPosition.y) / 100;
+	
+	/* CHECKME This seems to be unused basically and only called from debug code
+		Left here just in case...
+	if (gfx_seemsAlways0) {
+		dstRect.left += screenOffsetPt.x;
+		dstRect.right = screenOffsetPt.x + dstRect.right;
+		dstRect.top = screenOffsetPt.y + dstRect.top;
+		dstRect.bottom = screenOffsetPt.y + dstRect.bottom;
+	}
+	*/
+
+	// Check if the sprite is on-screen
+	if (dstRect.left >= 640 || dstRect.right <= 0 || dstRect.top >= 480 || dstRect.bottom <= 0)
+		return false;
+
+	// Clip the sprite rect if neccessary
+
+	if (dstRect.left < 0) {
+		srcRect.left += -100 * dstRect.left / item->_scale;
+		dstRect.left = 0;
+	}
+
+	if (dstRect.top < 0) {
+		srcRect.top += -100 * dstRect.top / item->_scale;
+		dstRect.top = 0;
+	}
+
+	if (dstRect.right > 640) {
+		srcRect.right += 100 * (640 - dstRect.right) / item->_scale;
+		dstRect.right = 640;
+	}
+
+	if (dstRect.bottom > 480) {
+		srcRect.bottom += 100 * (480 - dstRect.bottom) / item->_scale;
+		dstRect.bottom = 480;
+	}
+
+	return true;
+}
+
+void SpriteDrawQueue::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+	// TODO
+}
+
+void SpriteDrawQueue::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// TODO
+}
+
+void SpriteDrawQueue::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+	// TODO
+}
+
+void SpriteDrawQueue::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// TODO
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/spritedrawqueue.h b/engines/illusions/spritedrawqueue.h
new file mode 100644
index 0000000..83dd352
--- /dev/null
+++ b/engines/illusions/spritedrawqueue.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SPRITEDRAWQUEUE_H
+#define ILLUSIONS_SPRITEDRAWQUEUE_H
+
+#include "illusions/illusions.h"
+#include "illusions/graphics.h"
+#include "common/list.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+struct SpriteDrawQueueItem {
+	byte *_drawFlags;
+	int16 _kind;
+	int16 _scale;
+	uint16 _flags;
+	//field_A dw
+	int _priority;
+	Graphics::Surface *_surface;
+	WidthHeight _dimensions;
+	Common::Point _drawPosition;
+	Common::Point _controlPosition;
+};
+
+class SpriteDrawQueue {
+public:
+	SpriteDrawQueue(IllusionsEngine *vm);
+	~SpriteDrawQueue();
+	bool draw(SpriteDrawQueueItem *item);
+	void insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
+		Common::Point &drawPosition, Common::Point &controlPosition, int priority, int16 scale, uint16 flags);
+	void insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+		Common::Point &drawPosition, int priority);
+	void insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+		Common::Point &drawPosition, int priority);
+protected:
+	typedef Common::List<SpriteDrawQueueItem*> SpriteDrawQueueList;
+	typedef SpriteDrawQueueList::iterator SpriteDrawQueueListIterator;
+
+	struct FindInsertionPosition : public Common::UnaryFunction<const SpriteDrawQueueItem*, bool> {
+		int _priority;
+		FindInsertionPosition(int priority) : _priority(priority) {}
+		bool operator()(const SpriteDrawQueueItem *item) const {
+			return item->_priority >= _priority;
+		}
+	};
+
+	IllusionsEngine *_vm;
+	Graphics::Surface *_backSurface;
+	SpriteDrawQueueList _queue;	
+	void insert(SpriteDrawQueueItem *item, int priority);
+	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
+	// TODO Possibly move these into a Screen class
+	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
+	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
+	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
+	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SPRITEDRAWQUEUE_H


Commit: 971c8a0a1ddbdeaee33cd3b0f7307a0db2b73b0a
    https://github.com/scummvm/scummvm/commit/971c8a0a1ddbdeaee33cd3b0f7307a0db2b73b0a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with SpriteDecompressQueue

Changed paths:
  A engines/illusions/spritedecompressqueue.cpp
  A engines/illusions/spritedecompressqueue.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index cc8a92e..67887ad 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -28,6 +28,7 @@
 #include "illusions/input.h"
 #include "illusions/updatefunctions.h"
 #include "illusions/spritedrawqueue.h"
+#include "illusions/spritedecompressqueue.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 0e095bd..f470e88 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
 	illusions.o \
 	input.o \
 	resourcesystem.o \
+	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	updatefunctions.o
 
diff --git a/engines/illusions/spritedecompressqueue.cpp b/engines/illusions/spritedecompressqueue.cpp
new file mode 100644
index 0000000..01ebc74
--- /dev/null
+++ b/engines/illusions/spritedecompressqueue.cpp
@@ -0,0 +1,123 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/spritedecompressqueue.h"
+
+namespace Illusions {
+
+SpriteDecompressQueue::SpriteDecompressQueue() {
+}
+
+SpriteDecompressQueue::~SpriteDecompressQueue() {
+}
+
+void SpriteDecompressQueue::insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
+	byte *compressedPixels, Graphics::Surface *surface) {
+	SpriteDecompressQueueItem *item = new SpriteDecompressQueueItem();
+	item->_drawFlags = drawFlags;
+	*item->_drawFlags &= 1;
+	item->_flags = flags;
+	item->_dimensions = dimensions;
+	item->_compressedPixels = compressedPixels;
+	item->_field8 = field8;
+	item->_surface = surface;
+	_queue.push_back(item);
+}
+
+void SpriteDecompressQueue::decompressAll() {
+	SpriteDecompressQueueListIterator it = _queue.begin();
+	while (it != _queue.end()) {
+		decompress(*it);
+		delete *it;
+		it = _queue.erase(it);
+	}
+}
+
+void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
+	byte *src = item->_compressedPixels;
+	Graphics::Surface *dstSurface = item->_surface;
+	int dstSize = item->_dimensions._height * item->_dimensions._width;
+	int processedSize = 0;
+	int xincr, x, xstart;
+	int yincr, y;
+
+	if (item->_flags & 1) {
+		x = xstart = item->_dimensions._width - 1;
+		xincr = -1;
+	} else {
+		x = xstart = 0;
+		xincr = 1;
+	}
+
+	if (item->_flags & 2) {
+		y = item->_dimensions._height - 1;
+		yincr = -1;
+	} else {
+		y = 0;
+		yincr = 1;
+	}
+	
+	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
+
+	while (processedSize < dstSize) {
+		int16 op = READ_LE_UINT16(src);
+		src += 2;
+		if (op & 0x8000) {
+			int runCount = (op & 0x7FFF) + 1;
+			uint16 runColor = READ_LE_UINT16(src);
+			src += 2;
+			while (runCount--) {
+				WRITE_LE_UINT16(dst, runColor);
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += 2 * xincr;
+				}
+			}
+			processedSize += runCount;
+		} else {
+			int copyCount = op + 1;
+			while (copyCount--) {
+				uint16 color = READ_LE_UINT16(src);
+				src += 2;
+				WRITE_LE_UINT16(dst, color);
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += 2 * xincr;
+				}
+			}
+			processedSize += copyCount;
+		}
+	}
+
+	*item->_drawFlags &= ~1;
+ 
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/spritedecompressqueue.h b/engines/illusions/spritedecompressqueue.h
new file mode 100644
index 0000000..ede1cf8
--- /dev/null
+++ b/engines/illusions/spritedecompressqueue.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SPRITEDECOMPRESSQUEUE_H
+#define ILLUSIONS_SPRITEDECOMPRESSQUEUE_H
+
+#include "illusions/illusions.h"
+#include "illusions/graphics.h"
+#include "common/list.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+struct SpriteDecompressQueueItem {
+	byte *_drawFlags;
+	uint32 _flags;
+	uint32 _field8;
+	WidthHeight _dimensions;
+	byte *_compressedPixels;
+	Graphics::Surface *_surface;
+};
+
+class SpriteDecompressQueue {
+public:
+	SpriteDecompressQueue();
+	~SpriteDecompressQueue();
+	void insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
+		byte *compressedPixels, Graphics::Surface *surface);
+	void decompressAll();
+protected:
+	typedef Common::List<SpriteDecompressQueueItem*> SpriteDecompressQueueList;
+	typedef SpriteDecompressQueueList::iterator SpriteDecompressQueueListIterator;
+	
+	SpriteDecompressQueueList _queue;
+	
+	void decompress(SpriteDecompressQueueItem *item);
+
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SPRITEDRAWQUEUE_H


Commit: d92e713dea1dacce2bcc5acc842c8450191116e0
    https://github.com/scummvm/scummvm/commit/d92e713dea1dacce2bcc5acc842c8450191116e0
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with Actor

Changed paths:
  A engines/illusions/actor.cpp
  A engines/illusions/actor.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
new file mode 100644
index 0000000..5185fda
--- /dev/null
+++ b/engines/illusions/actor.cpp
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/actor.h"
+
+namespace Illusions {
+
+Actor::Actor()
+	: _pauseCtr(0) {
+	
+}
+
+void Actor::pause() {
+	++_pauseCtr;
+}
+
+void Actor::unpause() {
+	--_pauseCtr;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
new file mode 100644
index 0000000..2817b1d
--- /dev/null
+++ b/engines/illusions/actor.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ACTOR_H
+#define ILLUSIONS_ACTOR_H
+
+namespace Illusions {
+
+class Actor {
+public:
+	Actor();
+	void pause();
+	void unpause();
+protected:
+	int _pauseCtr;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ACTOR_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 67887ad..69d57bd 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -29,6 +29,7 @@
 #include "illusions/updatefunctions.h"
 #include "illusions/spritedrawqueue.h"
 #include "illusions/spritedecompressqueue.h"
+#include "illusions/actor.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index f470e88..4a409e1 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/illusions
 
 MODULE_OBJS := \
+	actor.o \
 	backgroundresource.o \
 	camera.o \
 	detection.o \


Commit: e0a3db43c3eed0c5bb7f94f408e5534aff1ea17a
    https://github.com/scummvm/scummvm/commit/e0a3db43c3eed0c5bb7f94f408e5534aff1ea17a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Keep backgrounds in new BackgroundItems class instead of the engine class

Changed paths:
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h


diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 246172f..11a5957 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -35,7 +35,7 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	BackgroundResource *backgroundResource = new BackgroundResource();
 	backgroundResource->load(resource->_data, resource->_dataSize);
 
-	BackgroundItem *backgroundItem = _vm->allocBackgroundItem();
+	BackgroundItem *backgroundItem = _vm->_backgroundItems->allocBackgroundItem();
 	backgroundItem->_bgRes = backgroundResource;
 	backgroundItem->_tag = resource->_tag;
 	
@@ -63,60 +63,6 @@ bool BackgroundResourceLoader::isFlag(int flag) {
 		flag == kRlfLoadFile;
 }
 
-// BackgroundItem
-
-BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
-}
-
-void BackgroundItem::initSurface() {
-
-	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
-		_surfaces[i] = 0;
-
-	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
-		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
-		_panPoints[i] = bgInfo->_panPoint;
-		_surfaces[i] = _vm->allocSurface(bgInfo->_surfInfo);
-		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
-	}
-
-}
-
-void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
-	const int kTileWidth = 32;
-	const int kTileHeight = 8;
-	const int kTileSize = kTileWidth * kTileHeight * 2;
-	uint tileMapIndex = 0;
-	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
-		int tileDestY = tileY * kTileHeight;
-		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
-		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
-			int tileDestX = tileX * kTileWidth;
-			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
-			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
-			++tileMapIndex;
-			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
-			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
-			for (int h = 0; h < tileDestH; ++h) {
-				for (int w = 0; w < tileDestW; ++w) {
-					uint16 pixel = READ_LE_UINT16(src + w * 2);
-					WRITE_LE_UINT16(dst + w * 2, pixel);
-				}
-				dst += surface->pitch;
-				src += kTileWidth * 2;
-			}
-		}
-	}
-
-	/*	
-	Common::DumpFile d;
-	d.open("dump.000");
-	d.write(surface->getPixels(), surface->h * surface->pitch);
-	d.close();
-	*/
-
-}
-
 // TileMap
 
 void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
@@ -181,4 +127,142 @@ int BackgroundResource::findMasterBgIndex() {
 	return index;
 }
 
+// BackgroundItem
+
+BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
+}
+
+void BackgroundItem::initSurface() {
+	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
+		_surfaces[i] = 0;
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
+		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
+		_panPoints[i] = bgInfo->_panPoint;
+		_surfaces[i] = _vm->allocSurface(bgInfo->_surfInfo);
+		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
+	}
+}
+
+void BackgroundItem::freeSurface() {
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
+		_surfaces[i]->free();
+		delete _surfaces[i];
+		_surfaces[i] = 0;
+	}
+}
+
+void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	const int kTileWidth = 32;
+	const int kTileHeight = 8;
+	const int kTileSize = kTileWidth * kTileHeight * 2;
+	uint tileMapIndex = 0;
+	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
+		int tileDestY = tileY * kTileHeight;
+		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
+		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
+			int tileDestX = tileX * kTileWidth;
+			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
+			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
+			++tileMapIndex;
+			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
+			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
+			for (int h = 0; h < tileDestH; ++h) {
+				for (int w = 0; w < tileDestW; ++w) {
+					uint16 pixel = READ_LE_UINT16(src + w * 2);
+					WRITE_LE_UINT16(dst + w * 2, pixel);
+				}
+				dst += surface->pitch;
+				src += kTileWidth * 2;
+			}
+		}
+	}
+}
+
+void BackgroundItem::pause() {
+	// TODO
+	++_pauseCtr;
+	if (_pauseCtr <= 1) {
+    	/* TODO
+		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
+			krndictRemoveID(_bgRes->_item48s[i].id);
+		*/
+		// TODO _vm->setDefPointDimensions1();
+		// TODO memcpy(&_savedCamera, &_vm->camera, sizeof(backgroundItem->savedCamera));
+		/* Unused
+		_savedPalette = malloc(1024);
+		savePalette(_savedPalette);
+		*/
+		freeSurface();
+	}
+}
+
+void BackgroundItem::unpause() {
+	// TODO
+	--_pauseCtr;
+	if (_pauseCtr <= 0) {
+    	/* TODO
+		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
+			krndictAddID(_bgRes->_item48s[i].id, _bgRes->_item48s[i]);
+		*/
+		initSurface();
+		/* Unused
+		restorePalette(_savedPalette, 1, 256);
+		free(_savedPalette);
+		_savedPalette = 0;
+		*/
+		// TODO _vm->_screen->_fadeClear();
+		// TODO memcpy(&_vm->camera, &_savedCamera, sizeof(SavedCamera));
+		/* TODO
+		currTime = krnxxxGetCurrentTime();
+		_vm->_camera.panStartTime = currTime;
+		_vm->backgroundItem_refreshPan();
+		*/
+	}
+}
+
+// BackgroundItems
+
+BackgroundItems::BackgroundItems(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+BackgroundItems::~BackgroundItems() {
+}
+
+BackgroundItem *BackgroundItems::allocBackgroundItem() {
+	BackgroundItem *backgroundItem = new BackgroundItem(_vm);
+	_items.push_back(backgroundItem);
+	return backgroundItem;
+}
+
+void BackgroundItems::pauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->pause();
+}
+
+void BackgroundItems::unpauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->unpause();
+}
+
+BackgroundItem *BackgroundItems::findActiveBackground() {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_pauseCtr == 0)
+			return (*it);
+	return 0;
+}
+
+BackgroundResource *BackgroundItems::getActiveBgResource() {
+	BackgroundItem *background = findActiveBackground();
+	if (background)
+		return background->_bgRes;
+	return 0;
+}
+
+BackgroundItem *BackgroundItems::debugFirst() {
+	return *(_items.begin());
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index ab82687..7fc70ae 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -29,6 +29,7 @@
 
 #include "common/array.h"
 #include "common/file.h"
+#include "common/list.h"
 #include "common/memstream.h"
 #include "common/rect.h"
 #include "common/substream.h"
@@ -88,7 +89,10 @@ public:
 	BackgroundItem(IllusionsEngine *vm);
 	~BackgroundItem();
 	void initSurface();
+	void freeSurface();
 	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
+	void pause();
+	void unpause();
 public:
 	IllusionsEngine *_vm;
 	uint32 _tag;
@@ -100,6 +104,23 @@ public:
 	// TODO? byte *savedPalette;
 };
 
+class BackgroundItems {
+public:
+	BackgroundItems(IllusionsEngine *vm);
+	~BackgroundItems();
+	BackgroundItem *allocBackgroundItem();
+	void pauseByTag(uint32 tag);
+	void unpauseByTag(uint32 tag);
+	BackgroundItem *findActiveBackground();
+	BackgroundResource *getActiveBgResource();
+	BackgroundItem *debugFirst();
+protected:
+	typedef Common::List<BackgroundItem*> Items;
+	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
+	Items _items;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_BACKGROUNDRESOURCE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 69d57bd..6bfecd3 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -78,10 +78,11 @@ Common::Error IllusionsEngine::run() {
 	
 	_resSys = new ResourceSystem();
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	_backgroundItems = new BackgroundItems(this);
 	
 	_resSys->loadResource(0x0011000B, 0, 0);
 
-	BackgroundItem *backgroundItem = *(_backgroundItems.begin());
+	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
 	_system->copyRectToScreen(backgroundItem->_surfaces[0]->getPixels(), backgroundItem->_surfaces[0]->pitch,
 		0, 0, 640, 480);
 	_system->updateScreen();
@@ -90,6 +91,7 @@ Common::Error IllusionsEngine::run() {
 		updateEvents();
 	}
 	
+	delete _backgroundItems;
 	delete _resSys;
 	
 	return Common::kNoError;
@@ -133,12 +135,6 @@ void IllusionsEngine::updateEvents() {
 	}
 }
 
-BackgroundItem *IllusionsEngine::allocBackgroundItem() {
-	BackgroundItem *backgroundItem = new BackgroundItem(this);
-	_backgroundItems.push_back(backgroundItem);
-	return backgroundItem;
-}
-
 Graphics::Surface *IllusionsEngine::allocSurface(int16 width, int16 height) {
 	// TODO Use screen pixel format?
 	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 39e1b86..6ebf2e7 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -49,6 +49,7 @@ class ResourceSystem;
 struct SurfInfo;
 
 class BackgroundItem;
+class BackgroundItems;
 class BackgroundResource;
 
 class IllusionsEngine : public Engine {
@@ -69,8 +70,7 @@ public:
 	
 	void updateEvents();
 
-	Common::List<BackgroundItem*> _backgroundItems;
-	BackgroundItem *allocBackgroundItem();
+	BackgroundItems *_backgroundItems;
 
 	// Screen functions	
 	Graphics::Surface *allocSurface(int16 width, int16 height);


Commit: 12645cbb8cb90adb5917b554e14142bf33d0f70a
    https://github.com/scummvm/scummvm/commit/12645cbb8cb90adb5917b554e14142bf33d0f70a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Some work on the Camera class; start with time functions

Changed paths:
  A engines/illusions/time.cpp
  A engines/illusions/time.h
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/graphics.h
    engines/illusions/module.mk
    engines/illusions/updatefunctions.cpp


diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 5f8b2a0..76addb6 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -21,11 +21,65 @@
  */
 
 #include "illusions/camera.h"
+#include "illusions/time.h"
 
 namespace Illusions {
 
+Camera::Camera() {
+	_activeState._cameraMode = 6;
+	_activeState._paused = 0;
+	_activeState._panStartTime = getCurrentTime();
+	_activeState._panSpeed = 1;
+	_activeState._bounds._topLeft.x = 320;
+	_activeState._bounds._topLeft.y = 240;
+	_activeState._bounds._bottomRight.x = 320;
+	_activeState._bounds._bottomRight.y = 240;
+	_activeState._currPan.x = 320;
+	_activeState._currPan.y = 240;
+	_activeState._panXShl = 320 << 16;
+	_activeState._panYShl = 240 << 16;
+	_activeState._panTargetPoint.x = 320;
+	_activeState._panTargetPoint.y = 240;
+	_activeState._panToPositionPtr = 0;
+	_activeState._panNotifyId = 0;
+	_activeState._trackingLimits.x = 0;
+	_activeState._trackingLimits.y = 0;
+	_activeState._pt.x = 320;
+	_activeState._pt.y = 240;
+	_activeState._pointFlags = 0;
+}
+
+void Camera::clearStack() {
+	_stack.clear();
+}
+
+void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
+	_activeState._cameraMode = 6;
+	_activeState._paused = 0;
+	_activeState._panStartTime = getCurrentTime();
+	_activeState._panSpeed = 1;
+	_activeState._bounds._topLeft.x = 320;
+	_activeState._bounds._topLeft.y = 240;
+	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - 640) + 320;
+	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - 480) + 240;
+	_activeState._panTargetPoint = panPoint;
+	// TODO camera_clipPanTargetPoint();
+	_activeState._currPan = _activeState._panTargetPoint;
+	_activeState._panXShl = _activeState._currPan.x << 16;
+	_activeState._panYShl = _activeState._currPan.y << 16;
+	// TODO largeObj_backgroundItem_refreshPan();
+	_activeState._panToPositionPtr = 0;
+	_activeState._panObjectId = 0;
+	_activeState._panNotifyId = 0;
+	_activeState._trackingLimits.x = 0;
+	_activeState._trackingLimits.y = 0;
+	_activeState._pointFlags = 0;
+	_activeState._pt.x = 320;
+	_activeState._pt.y = 240;
+}
+
 Common::Point Camera::getCurrentPan() {
-	return _currPan;
+	return _activeState._currPan;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index d55ab8e..fae3c32 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -23,15 +23,45 @@
 #ifndef ILLUSIONS_CAMERA_H
 #define ILLUSIONS_CAMERA_H
 
+#include "illusions/graphics.h"
 #include "common/rect.h"
+#include "common/stack.h"
 
 namespace Illusions {
 
+struct CameraState {
+	int _cameraMode;
+	//field_2 dw
+	int16 _paused;
+	int16 _panSpeed;
+	int _someX, _someY;
+	Common::Point _currPan;
+	int _panXShl, _panYShl;
+	WRect _bounds;
+	uint32 _panNotifyId;
+	uint32 _time28;
+	uint32 _panStartTime;
+	uint32 _pauseStartTime;
+	uint32 _time2E;
+	Common::Point _currPan2;
+	Common::Point _panTargetPoint;
+	Common::Point _trackingLimits;
+	Common::Point _pt;
+	uint32 _panObjectId;
+	Common::Point *_panToPositionPtr;
+	uint _pointFlags;
+	//field_4A dw
+};
+
 class Camera {
 public:
+	Camera();
+	void clearStack();
+	void set(Common::Point &panPoint, WidthHeight &dimensions);
 	Common::Point getCurrentPan();
 protected:
-	Common::Point _currPan;
+	CameraState _activeState;
+	Common::FixedStack<CameraState, 8> _stack;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/graphics.h b/engines/illusions/graphics.h
index 0b16a0c..5a5cb02 100644
--- a/engines/illusions/graphics.h
+++ b/engines/illusions/graphics.h
@@ -39,6 +39,11 @@ struct SurfInfo {
 	void load(Common::SeekableReadStream &stream);
 };
 
+struct WRect {
+	Common::Point _topLeft;
+	Common::Point _bottomRight;
+};
+
 void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt);
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 4a409e1..504f636 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	resourcesystem.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
+	time.o \
 	updatefunctions.o
 
 # This module can be built as a plugin
diff --git a/engines/illusions/time.cpp b/engines/illusions/time.cpp
new file mode 100644
index 0000000..7873db6
--- /dev/null
+++ b/engines/illusions/time.cpp
@@ -0,0 +1,33 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/time.h"
+#include "common/system.h"
+
+namespace Illusions {
+
+uint32 getCurrentTime() {
+	// TODO, move to own file with other time related code
+	return g_system->getMillis();
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/time.h b/engines/illusions/time.h
new file mode 100644
index 0000000..3083af2
--- /dev/null
+++ b/engines/illusions/time.h
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TIME_H
+#define ILLUSIONS_TIME_H
+
+#include "illusions/illusions.h"
+
+namespace Illusions {
+
+uint32 getCurrentTime();
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TIME_H
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index 9eb626c..3a3aadf 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -21,16 +21,12 @@
  */
 
 #include "illusions/updatefunctions.h"
+#include "illusions/time.h"
 #include "common/algorithm.h"
 #include "common/system.h"
 
 namespace Illusions {
 
-uint32 getCurrentTime() {
-	// TODO, move to own file with other time related code
-	return g_system->getMillis();
-}
-
 // UpdateFunctions
 
 UpdateFunctions::UpdateFunctions() {


Commit: 21e1b08b8399bad5a8b88cf6e30b2db9edd0ad60
    https://github.com/scummvm/scummvm/commit/21e1b08b8399bad5a8b88cf6e30b2db9edd0ad60
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on the camera and background classes

Changed paths:
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h


diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 11a5957..3ac2a58 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/camera.h"
 #include "common/str.h"
 
 namespace Illusions {
@@ -215,8 +216,27 @@ void BackgroundItem::unpause() {
 		/* TODO
 		currTime = krnxxxGetCurrentTime();
 		_vm->_camera.panStartTime = currTime;
-		_vm->backgroundItem_refreshPan();
 		*/
+		_vm->_backgroundItems->refreshPan();
+	}
+}
+
+void BackgroundItem::refreshPan(WidthHeight &dimensions) {
+	Common::Point screenOffs = _vm->_camera->getScreenOffset();
+	int x = dimensions._width - 640;
+	int y = dimensions._height - 480;
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
+		const BgInfo &bgInfo = _bgRes->_bgInfos[i];
+		if (bgInfo._flags & 1) {
+			_panPoints[i] = screenOffs;
+		} else {
+			Common::Point newOffs(0, 0);
+			if (x > 0 && bgInfo._surfInfo._dimensions._width - 640 > 0)
+				newOffs.x = screenOffs.x * (bgInfo._surfInfo._dimensions._width - 640) / x;
+			if (y > 0 && bgInfo._surfInfo._dimensions._height - 480 > 0)
+				newOffs.y = screenOffs.y * (bgInfo._surfInfo._dimensions._height - 480) / y;
+			_panPoints[i] = newOffs;
+		}
 	}
 }
 
@@ -261,6 +281,20 @@ BackgroundResource *BackgroundItems::getActiveBgResource() {
 	return 0;
 }
 
+WidthHeight BackgroundItems::getMasterBgDimensions() {
+	BackgroundItem *backgroundItem = findActiveBackground();
+	int16 index = backgroundItem->_bgRes->findMasterBgIndex();
+	return backgroundItem->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
+}
+
+void BackgroundItems::refreshPan() {
+	BackgroundItem *backgroundItem = findActiveBackground();
+	if (backgroundItem) {
+		WidthHeight dimensions = getMasterBgDimensions();
+		backgroundItem->refreshPan(dimensions);
+	}
+}
+
 BackgroundItem *BackgroundItems::debugFirst() {
 	return *(_items.begin());
 }
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 7fc70ae..a12804b 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -93,6 +93,7 @@ public:
 	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 	void pause();
 	void unpause();
+	void refreshPan(WidthHeight &dimensions);
 public:
 	IllusionsEngine *_vm;
 	uint32 _tag;
@@ -113,6 +114,8 @@ public:
 	void unpauseByTag(uint32 tag);
 	BackgroundItem *findActiveBackground();
 	BackgroundResource *getActiveBgResource();
+	WidthHeight getMasterBgDimensions();
+	void refreshPan();
 	BackgroundItem *debugFirst();
 protected:
 	typedef Common::List<BackgroundItem*> Items;
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 76addb6..df5de4b 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -20,14 +20,17 @@
  *
  */
 
+#include "illusions/illusions.h"
 #include "illusions/camera.h"
+#include "illusions/backgroundresource.h"
 #include "illusions/time.h"
 
 namespace Illusions {
 
-Camera::Camera() {
+Camera::Camera(IllusionsEngine *vm)
+	: _vm(vm) {
 	_activeState._cameraMode = 6;
-	_activeState._paused = 0;
+	_activeState._paused = false;
 	_activeState._panStartTime = getCurrentTime();
 	_activeState._panSpeed = 1;
 	_activeState._bounds._topLeft.x = 320;
@@ -55,7 +58,7 @@ void Camera::clearStack() {
 
 void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._cameraMode = 6;
-	_activeState._paused = 0;
+	_activeState._paused = false;
 	_activeState._panStartTime = getCurrentTime();
 	_activeState._panSpeed = 1;
 	_activeState._bounds._topLeft.x = 320;
@@ -67,7 +70,7 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._currPan = _activeState._panTargetPoint;
 	_activeState._panXShl = _activeState._currPan.x << 16;
 	_activeState._panYShl = _activeState._currPan.y << 16;
-	// TODO largeObj_backgroundItem_refreshPan();
+	_vm->_backgroundItems->refreshPan();
 	_activeState._panToPositionPtr = 0;
 	_activeState._panObjectId = 0;
 	_activeState._panNotifyId = 0;
@@ -78,8 +81,137 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._pt.y = 240;
 }
 
+void Camera::pause() {
+	_activeState._pauseStartTime = getCurrentTime();
+	_activeState._paused = true;
+}
+
+void Camera::unpause() {
+	_activeState._paused = false;
+	uint32 pauseDuration = getCurrentTime() - _activeState._pauseStartTime;
+	_activeState._time28 += pauseDuration;
+	_activeState._panStartTime += pauseDuration;
+}
+
+void Camera::update(uint32 currTime) {
+
+	if (_activeState._paused)
+		return;
+
+	switch (_activeState._cameraMode) {
+	case 1:
+		updateMode1(currTime);
+		break;
+	case 2:
+		updateMode2(currTime);
+		break;
+	case 3:
+		updateMode3(currTime);
+		break;
+	}
+	
+	if (_activeState._cameraMode != 6) {
+
+		if (!isPanFinished() &&	updatePan(currTime)) {
+			/* Unused
+			if (_activeState._cameraMode == 1 || _activeState._cameraMode == 5)
+				nullsub_2();
+			*/
+			_vm->_backgroundItems->refreshPan();
+		}
+
+		if (isPanFinished()) {
+			if (_activeState._cameraMode == 5) {
+				// Notify a thread that the camera panning has finished
+				if (_activeState._panNotifyId) {
+					// TODO scrmgrNotifyID(_activeState._panNotifyId);
+					_activeState._panNotifyId = 0;
+				}
+				_activeState._cameraMode = 6;
+			} else if (_activeState._cameraMode == 4) {
+				_activeState._cameraMode = 3;
+			}
+		}
+
+	}
+
+}
+
+void Camera::setBounds(Common::Point &minPt, Common::Point &maxPt) {
+	_activeState._bounds._topLeft = minPt;
+	_activeState._bounds._bottomRight = maxPt;
+}
+
+void Camera::setBoundsToDimensions(WidthHeight &dimensions) {
+	// NOTE For the caller dimensions = artdispGetMasterBGDimensions();
+	_activeState._bounds._topLeft.x = 320;
+	_activeState._bounds._topLeft.y = 240;
+	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - 640) + 320;
+	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - 480) + 240;
+	// TODO camera_clipPanTargetPoint();
+}
+
 Common::Point Camera::getCurrentPan() {
 	return _activeState._currPan;
 }
 
+Common::Point Camera::getScreenOffset() {
+	Common::Point screenOffs = getCurrentPan();
+	screenOffs.x -= 320;
+	screenOffs.y -= 240;
+	return screenOffs;
+}
+
+void Camera::updateMode1(uint32 currTime) {
+	Common::Point ptOffs = getPtOffset(*_activeState._panToPositionPtr);
+	int deltaX = ptOffs.x - _activeState._currPan.x + 320 - _activeState._pt.x;
+	int deltaY = ptOffs.y - _activeState._currPan.y + 240 - _activeState._pt.y;
+	int deltaXAbs = ABS(deltaX);
+	int deltaYAbs = ABS(deltaY);
+
+	if (deltaXAbs > _activeState._trackingLimits.x) {
+		_activeState._panTargetPoint.x = _activeState._currPan.x + ABS(deltaXAbs - _activeState._trackingLimits.x) * (deltaX >= 0 ? 1 : -1);
+	} else {
+		_activeState._panTargetPoint.x = _activeState._currPan.x;
+	}
+
+	if (deltaYAbs > _activeState._trackingLimits.y) {
+		_activeState._panTargetPoint.y = _activeState._currPan.y + ABS(deltaYAbs - _activeState._trackingLimits.y) * (deltaY >= 0 ? 1 : -1);
+	} else {
+		_activeState._panTargetPoint.y = _activeState._currPan.y;
+	}
+
+	// TODO Camera_clipPanTargetPoint();
+
+	if (!isPanFinished()) {
+		uint32 oldPanTime = _activeState._panStartTime;
+		_activeState._panStartTime = _activeState._time28;
+		// TODO Camera_recalcPan(oldPanTime);
+	}
+	
+}
+
+void Camera::updateMode2(uint32 currTime) {
+	// TODO
+}
+
+void Camera::updateMode3(uint32 currTime) {
+	// TODO
+}
+
+bool Camera::updatePan(uint32 currTime) {
+	// TODO
+	return false;
+}
+
+bool Camera::isPanFinished() {
+	return _activeState._currPan.x == _activeState._panTargetPoint.x && _activeState._currPan.y == _activeState._panTargetPoint.y;
+}
+
+Common::Point Camera::getPtOffset(Common::Point pt) {
+	pt.x = pt.x - _activeState._pt.x + 320;
+	pt.y = pt.y - _activeState._pt.y + 240;
+	return pt;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index fae3c32..816c5dd 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -32,7 +32,7 @@ namespace Illusions {
 struct CameraState {
 	int _cameraMode;
 	//field_2 dw
-	int16 _paused;
+	bool _paused;
 	int16 _panSpeed;
 	int _someX, _someY;
 	Common::Point _currPan;
@@ -55,13 +55,26 @@ struct CameraState {
 
 class Camera {
 public:
-	Camera();
+	Camera(IllusionsEngine *vm);
 	void clearStack();
 	void set(Common::Point &panPoint, WidthHeight &dimensions);
+	void pause();
+	void unpause();
+	void update(uint32 currTime);
+	void setBounds(Common::Point &minPt, Common::Point &maxPt);
+	void setBoundsToDimensions(WidthHeight &dimensions);
 	Common::Point getCurrentPan();
+	Common::Point getScreenOffset();
 protected:
+	IllusionsEngine *_vm;
 	CameraState _activeState;
 	Common::FixedStack<CameraState, 8> _stack;
+	void updateMode1(uint32 currTime);
+	void updateMode2(uint32 currTime);
+	void updateMode3(uint32 currTime);
+	bool updatePan(uint32 currTime);
+	bool isPanFinished();
+	Common::Point getPtOffset(Common::Point pt);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 6bfecd3..47ed48c 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -79,6 +79,7 @@ Common::Error IllusionsEngine::run() {
 	_resSys = new ResourceSystem();
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 	_backgroundItems = new BackgroundItems(this);
+	_camera = new Camera(this);
 	
 	_resSys->loadResource(0x0011000B, 0, 0);
 
@@ -91,6 +92,7 @@ Common::Error IllusionsEngine::run() {
 		updateEvents();
 	}
 	
+	delete _camera;
 	delete _backgroundItems;
 	delete _resSys;
 	
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 6ebf2e7..f734867 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -51,6 +51,7 @@ struct SurfInfo;
 class BackgroundItem;
 class BackgroundItems;
 class BackgroundResource;
+class Camera;
 
 class IllusionsEngine : public Engine {
 protected:
@@ -71,6 +72,7 @@ public:
 	void updateEvents();
 
 	BackgroundItems *_backgroundItems;
+	Camera *_camera;
 
 	// Screen functions	
 	Graphics::Surface *allocSurface(int16 width, int16 height);


Commit: 08e8f39945c6c89ffc4fa20d6fe50ca39183b00d
    https://github.com/scummvm/scummvm/commit/08e8f39945c6c89ffc4fa20d6fe50ca39183b00d
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on the Camera class

- Add fixed point code (untested and probably wrong)

Changed paths:
  A engines/illusions/fixedpoint.cpp
  A engines/illusions/fixedpoint.h
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk


diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index df5de4b..d81c746 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/camera.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/fixedpoint.h"
 #include "illusions/time.h"
 
 namespace Illusions {
@@ -47,8 +48,8 @@ Camera::Camera(IllusionsEngine *vm)
 	_activeState._panNotifyId = 0;
 	_activeState._trackingLimits.x = 0;
 	_activeState._trackingLimits.y = 0;
-	_activeState._pt.x = 320;
-	_activeState._pt.y = 240;
+	_activeState._centerPt.x = 320;
+	_activeState._centerPt.y = 240;
 	_activeState._pointFlags = 0;
 }
 
@@ -66,7 +67,7 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - 640) + 320;
 	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - 480) + 240;
 	_activeState._panTargetPoint = panPoint;
-	// TODO camera_clipPanTargetPoint();
+	clipPanTargetPoint();
 	_activeState._currPan = _activeState._panTargetPoint;
 	_activeState._panXShl = _activeState._currPan.x << 16;
 	_activeState._panYShl = _activeState._currPan.y << 16;
@@ -77,8 +78,98 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._trackingLimits.x = 0;
 	_activeState._trackingLimits.y = 0;
 	_activeState._pointFlags = 0;
-	_activeState._pt.x = 320;
-	_activeState._pt.y = 240;
+	_activeState._centerPt.x = 320;
+	_activeState._centerPt.y = 240;
+}
+
+void Camera::panCenterObject(uint32 objectId, int16 panSpeed) {
+	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
+	_activeState._cameraMode = 1;
+	_activeState._panSpeed = panSpeed;
+	_activeState._trackingLimits.x = 8;
+	_activeState._trackingLimits.y = 8;
+	_activeState._pointFlags = 0;
+	_activeState._panToPositionPtr = actorPosition;
+	_activeState._panObjectId = objectId;
+	_activeState._panTargetPoint = *actorPosition;
+	_activeState._panNotifyId = 0;
+	clipPanTargetPoint();
+	_activeState._panStartTime = getCurrentTime();
+	recalcPan(_activeState._panStartTime);
+}
+
+void Camera::panTrackObject(uint32 objectId) {
+	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
+	_activeState._cameraMode = 3;
+	_activeState._panObjectId = objectId;
+	_activeState._trackingLimits.x = 160;
+	_activeState._trackingLimits.y = 120;
+	_activeState._pointFlags = 0;
+	_activeState._panSpeed = 710;
+	_activeState._panToPositionPtr = actorPosition;
+	_activeState._panNotifyId = 0;
+	_activeState._panTargetPoint = *actorPosition;
+	clipPanTargetPoint();
+	_activeState._panStartTime = getCurrentTime();
+	recalcPan(_activeState._panStartTime);
+}
+
+void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
+
+	if (_activeState._panNotifyId) {
+		// TODO scrmgrNotifyID(_activeState._panNotifyId);
+		_activeState._panNotifyId = 0;
+	}
+
+	_activeState._panTargetPoint = Camera::getPtOffset(pt);
+	clipPanTargetPoint();
+	
+	if (panSpeed) {
+		_activeState._cameraMode = 5;
+		_activeState._panSpeed = panSpeed;
+		_activeState._trackingLimits.x = 0;
+		_activeState._trackingLimits.y = 0;
+		_activeState._pointFlags = 0;
+		_activeState._panToPositionPtr = 0;
+		_activeState._panNotifyId = panNotifyId;
+		_activeState._panStartTime = getCurrentTime();
+		recalcPan(_activeState._panStartTime);
+	} else {
+		_activeState._currPan = _activeState._panTargetPoint;
+		// TODO stopPan();
+		if (panNotifyId) {
+			// TODO scrmgrNotifyID(panNotifyId);
+		}
+	}
+}
+
+void Camera::panEdgeFollow(uint32 objectId, int16 panSpeed) {
+	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
+	_activeState._cameraMode = 2;
+	_activeState._trackingLimits.x = 318;
+	_activeState._trackingLimits.y = 238;
+	_activeState._pointFlags = 0;
+	_activeState._panSpeed = panSpeed;
+	_activeState._panToPositionPtr = actorPosition;
+	_activeState._panObjectId = objectId;
+	_activeState._panTargetPoint = _activeState._currPan;
+	_activeState._panNotifyId = 0;
+	clipPanTargetPoint();
+	_activeState._panStartTime = getCurrentTime();
+	recalcPan(_activeState._panStartTime);
+}
+
+void Camera::stopPan() {
+	_activeState._cameraMode = 6;
+	_activeState._panTargetPoint = _activeState._currPan;
+	_activeState._panSpeed = 1;
+	_activeState._panXShl = _activeState._currPan.x << 16;
+	_activeState._panYShl = _activeState._currPan.y << 16;
+	_activeState._panToPositionPtr = 0;
+	_activeState._panObjectId = 0;
+	_activeState._panNotifyId = 0;
+	_activeState._pointFlags = 0;
+	_vm->_backgroundItems->refreshPan();
 }
 
 void Camera::pause() {
@@ -93,6 +184,65 @@ void Camera::unpause() {
 	_activeState._panStartTime += pauseDuration;
 }
 
+void Camera::pushCameraMode() {
+	CameraModeStackItem item;
+	item._cameraMode = _activeState._cameraMode;
+	item._panSpeed = _activeState._panSpeed;
+	item._panObjectId = 0;
+	item._panNotifyId = 0;
+	item._panTargetPoint.x = 0;
+	item._panTargetPoint.y = 0;
+	switch (_activeState._cameraMode) {
+	case 4:
+		item._cameraMode = 3;
+		item._panObjectId = _activeState._panObjectId;
+		break;
+	case 1:
+	case 2:
+	case 3:
+		item._panObjectId = _activeState._panObjectId;
+		break;
+	case 5:
+		item._panTargetPoint = _activeState._panTargetPoint;
+		item._panNotifyId = _activeState._panNotifyId;
+		break;
+	}
+	_stack.push(item);
+}
+
+void Camera::popCameraMode() {
+	CameraModeStackItem item = _stack.pop();
+
+	if (item._panObjectId && !_vm->getObjectActorPositionPtr(item._panObjectId)) {
+		// Tracking object doesn't exist any longer
+		stopPan();
+		return;
+	}
+
+	switch (item._cameraMode) {
+	case 1:
+		panCenterObject(item._panObjectId, item._panSpeed);
+		break;
+	case 2:
+		panEdgeFollow(item._panObjectId, item._panSpeed);
+		break;
+	case 3:
+		panTrackObject(item._panObjectId);
+		break;
+	case 5:
+		panToPoint(item._panTargetPoint, item._panSpeed, item._panNotifyId);
+		break;
+	case 6:
+		stopPan();
+		break;
+	}
+
+}
+
+void Camera::clearCameraModeStack() {
+	_stack.clear();
+}
+
 void Camera::update(uint32 currTime) {
 
 	if (_activeState._paused)
@@ -148,7 +298,7 @@ void Camera::setBoundsToDimensions(WidthHeight &dimensions) {
 	_activeState._bounds._topLeft.y = 240;
 	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - 640) + 320;
 	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - 480) + 240;
-	// TODO camera_clipPanTargetPoint();
+	clipPanTargetPoint();
 }
 
 Common::Point Camera::getCurrentPan() {
@@ -164,43 +314,106 @@ Common::Point Camera::getScreenOffset() {
 
 void Camera::updateMode1(uint32 currTime) {
 	Common::Point ptOffs = getPtOffset(*_activeState._panToPositionPtr);
-	int deltaX = ptOffs.x - _activeState._currPan.x + 320 - _activeState._pt.x;
-	int deltaY = ptOffs.y - _activeState._currPan.y + 240 - _activeState._pt.y;
+	int deltaX = ptOffs.x - _activeState._currPan.x + 320 - _activeState._centerPt.x;
+	int deltaY = ptOffs.y - _activeState._currPan.y + 240 - _activeState._centerPt.y;
 	int deltaXAbs = ABS(deltaX);
 	int deltaYAbs = ABS(deltaY);
 
-	if (deltaXAbs > _activeState._trackingLimits.x) {
+	if (deltaXAbs > _activeState._trackingLimits.x)
 		_activeState._panTargetPoint.x = _activeState._currPan.x + ABS(deltaXAbs - _activeState._trackingLimits.x) * (deltaX >= 0 ? 1 : -1);
-	} else {
+	else
 		_activeState._panTargetPoint.x = _activeState._currPan.x;
-	}
 
-	if (deltaYAbs > _activeState._trackingLimits.y) {
+	if (deltaYAbs > _activeState._trackingLimits.y)
 		_activeState._panTargetPoint.y = _activeState._currPan.y + ABS(deltaYAbs - _activeState._trackingLimits.y) * (deltaY >= 0 ? 1 : -1);
-	} else {
+	else
 		_activeState._panTargetPoint.y = _activeState._currPan.y;
-	}
 
-	// TODO Camera_clipPanTargetPoint();
+	clipPanTargetPoint();
 
 	if (!isPanFinished()) {
 		uint32 oldPanTime = _activeState._panStartTime;
 		_activeState._panStartTime = _activeState._time28;
-		// TODO Camera_recalcPan(oldPanTime);
+		recalcPan(oldPanTime);
 	}
 	
 }
 
 void Camera::updateMode2(uint32 currTime) {
-	// TODO
+	Common::Point panToPosition = *_activeState._panToPositionPtr;
+	uint pointFlags = 0;
+	WRect rect;
+
+	rect._topLeft.x = 320 - _activeState._trackingLimits.x;
+	rect._topLeft.y = 240 - _activeState._trackingLimits.y;
+	rect._bottomRight.x = 320 + _activeState._trackingLimits.x;
+	rect._bottomRight.y = 240 + _activeState._trackingLimits.y;
+
+	if (calcPointFlags(panToPosition, rect, pointFlags)) {
+		if (pointFlags != _activeState._pointFlags) {
+			_activeState._pointFlags = pointFlags;
+			if (pointFlags & 1)
+				_activeState._panTargetPoint.x = _activeState._bounds._topLeft.x;
+			else if (pointFlags & 2)
+				_activeState._panTargetPoint.x = _activeState._bounds._bottomRight.x;
+			else
+				_activeState._panTargetPoint.x = _activeState._currPan.x;
+			if (pointFlags & 4)
+				_activeState._panTargetPoint.y = _activeState._bounds._topLeft.y;
+			else if (pointFlags & 8)
+				_activeState._panTargetPoint.y = _activeState._bounds._bottomRight.y;
+			else
+				_activeState._panTargetPoint.y = _activeState._currPan.y;
+			clipPanTargetPoint();
+			_activeState._panStartTime = currTime;
+			recalcPan(currTime);
+		}
+	} else if (_activeState._pointFlags) {
+	    _activeState._pointFlags = 0;
+	    _activeState._panTargetPoint = _activeState._currPan;
+  	}
+
 }
 
 void Camera::updateMode3(uint32 currTime) {
-	// TODO
+	Common::Point panToPosition = *_activeState._panToPositionPtr;
+	int deltaX = panToPosition.x - _activeState._currPan.x;
+	int deltaY = panToPosition.y - _activeState._currPan.y;
+
+	if (ABS(deltaX) > _activeState._trackingLimits.x)
+		_activeState._panTargetPoint.x = _activeState._currPan.x + 2 * _activeState._trackingLimits.x * (deltaX >= 0 ? 1 : -1);
+	else
+		_activeState._panTargetPoint.x = _activeState._currPan.x;
+
+	if (ABS(deltaY) > _activeState._trackingLimits.y)
+		_activeState._panTargetPoint.y = _activeState._currPan.y + 2 * _activeState._trackingLimits.y * (deltaY >= 0 ? 1 : -1);
+	else
+		_activeState._panTargetPoint.y = _activeState._currPan.y;
+
+	clipPanTargetPoint();
+
+	if (!isPanFinished()) {
+		_activeState._panStartTime = currTime;
+		recalcPan(currTime);
+		_activeState._cameraMode = 4;
+	}
+	
 }
 
 bool Camera::updatePan(uint32 currTime) {
-	// TODO
+	if (currTime - _activeState._time28 >= _activeState._time2E) {
+		_activeState._panXShl = _activeState._panTargetPoint.x << 16;
+		_activeState._panYShl = _activeState._panTargetPoint.y << 16;
+	} else {
+		_activeState._panXShl += fixedMul(_activeState._someX, (currTime - _activeState._panStartTime) << 16);
+		_activeState._panYShl += fixedMul(_activeState._someY, (currTime - _activeState._panStartTime) << 16);
+	}
+	_activeState._panStartTime = currTime;
+	Common::Point newPan(_activeState._panXShl >> 16, _activeState._panYShl >> 16);
+	if (_activeState._currPan.x != newPan.x || _activeState._currPan.y != newPan.y) {
+		_activeState._currPan = newPan;
+		return true;
+	}
 	return false;
 }
 
@@ -209,9 +422,60 @@ bool Camera::isPanFinished() {
 }
 
 Common::Point Camera::getPtOffset(Common::Point pt) {
-	pt.x = pt.x - _activeState._pt.x + 320;
-	pt.y = pt.y - _activeState._pt.y + 240;
+	pt.x = pt.x - _activeState._centerPt.x + 320;
+	pt.y = pt.y - _activeState._centerPt.y + 240;
 	return pt;
 }
 
+void Camera::recalcPan(uint32 currTime) {
+	_activeState._currPan2 = getCurrentPan();
+	_activeState._time28 = currTime;
+
+	if (_activeState._panSpeed == 0) {
+		_activeState._time2E = 0;
+	} else {
+		FP16 x1 = _activeState._currPan2.x << 16;
+		FP16 y1 = _activeState._currPan2.y << 16;
+		FP16 x2 = _activeState._panTargetPoint.x << 16;
+		FP16 y2 = _activeState._panTargetPoint.y << 16;
+		FP16 distance = fixedDistance(x1, y1, x2, y2);
+		_activeState._time2E = 60 * fixedTrunc(distance) / _activeState._panSpeed;
+	}
+
+	if (_activeState._time2E != 0) {
+		_activeState._someX = fixedDiv((_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16, _activeState._time2E << 16);
+		_activeState._someY = fixedDiv((_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16, _activeState._time2E << 16);
+	} else {
+		_activeState._someX = (_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16;
+		_activeState._someY = (_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16;
+	}
+
+}
+
+bool Camera::calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags) {
+	bool result = false;
+	if (pt.x < rect._topLeft.x) {
+		outFlags |= 1;
+		result = true;
+	} else if (pt.x > rect._bottomRight.x) {
+		outFlags |= 2;
+		result = true;
+	}
+	if (pt.y < rect._topLeft.y) {
+		outFlags |= 4;
+		result = true;
+	} else if (pt.y > rect._bottomRight.y) {
+		outFlags |= 8;
+		result = true;
+	}
+	return result;
+}
+
+void Camera::clipPanTargetPoint() {
+	_activeState._panTargetPoint.x = CLIP(_activeState._panTargetPoint.x,
+		_activeState._bounds._topLeft.x, _activeState._bounds._bottomRight.x);
+	_activeState._panTargetPoint.y = CLIP(_activeState._panTargetPoint.y,
+		_activeState._bounds._topLeft.y, _activeState._bounds._bottomRight.y);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index 816c5dd..6c9556c 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -46,20 +46,36 @@ struct CameraState {
 	Common::Point _currPan2;
 	Common::Point _panTargetPoint;
 	Common::Point _trackingLimits;
-	Common::Point _pt;
+	Common::Point _centerPt;
 	uint32 _panObjectId;
 	Common::Point *_panToPositionPtr;
 	uint _pointFlags;
 	//field_4A dw
 };
 
+struct CameraModeStackItem {
+	int _cameraMode;
+	uint32 _panObjectId;
+	int16 _panSpeed;
+	Common::Point _panTargetPoint;
+	uint32 _panNotifyId;
+};
+
 class Camera {
 public:
 	Camera(IllusionsEngine *vm);
 	void clearStack();
 	void set(Common::Point &panPoint, WidthHeight &dimensions);
+	void panCenterObject(uint32 objectId, int16 panSpeed);
+	void panTrackObject(uint32 objectId);
+	void panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId);
+	void panEdgeFollow(uint32 objectId, int16 panSpeed);
+	void stopPan();
 	void pause();
 	void unpause();
+	void pushCameraMode();
+	void popCameraMode();
+	void clearCameraModeStack();
 	void update(uint32 currTime);
 	void setBounds(Common::Point &minPt, Common::Point &maxPt);
 	void setBoundsToDimensions(WidthHeight &dimensions);
@@ -68,13 +84,16 @@ public:
 protected:
 	IllusionsEngine *_vm;
 	CameraState _activeState;
-	Common::FixedStack<CameraState, 8> _stack;
+	Common::FixedStack<CameraModeStackItem, 8> _stack;
 	void updateMode1(uint32 currTime);
 	void updateMode2(uint32 currTime);
 	void updateMode3(uint32 currTime);
 	bool updatePan(uint32 currTime);
 	bool isPanFinished();
 	Common::Point getPtOffset(Common::Point pt);
+	void recalcPan(uint32 currTime);
+	bool calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags);
+	void clipPanTargetPoint();
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/fixedpoint.cpp b/engines/illusions/fixedpoint.cpp
new file mode 100644
index 0000000..ee3818d
--- /dev/null
+++ b/engines/illusions/fixedpoint.cpp
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/fixedpoint.h"
+
+namespace Illusions {
+
+FP16 floatToFixed(float value) {
+	return value * 65536.0;
+}
+
+float fixedToFloat(FP16 value) {
+	return value / 65536.0;
+}
+
+FP16 fixedMul(FP16 a, FP16 b) {
+	// CHECKME Not sure if this correct
+	return (a * b) >> 16;
+}
+
+FP16 fixedDiv(FP16 a, FP16 b) {
+	// CHECKME Not sure if this correct
+	return (a << 16) / b;
+}
+
+int fixedTrunc(FP16 value) {
+	// CHECKME Not sure if this correct
+	int result = value >> 16;
+	if ((value & 0xFFFF) >= 0x8000)
+		++result;
+	return result;
+}
+
+FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2) {
+	float xd = ABS(fixedToFloat(x1) - fixedToFloat(x2));
+	float yd = ABS(fixedToFloat(y1) - fixedToFloat(y2));
+	if (xd != 0.0 || yd != 0.0)
+		return floatToFixed(sqrt(xd * xd + yd * yd));
+	return 0;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/fixedpoint.h b/engines/illusions/fixedpoint.h
new file mode 100644
index 0000000..a14eb8b
--- /dev/null
+++ b/engines/illusions/fixedpoint.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_FIXEDPOINT_H
+#define ILLUSIONS_FIXEDPOINT_H
+
+#include "common/rect.h"
+
+namespace Illusions {
+
+typedef int32 FP16;
+
+struct FPRect {
+	FP16 x1, y1, x2, y2;
+};
+
+FP16 floatToFixed(float value);
+float fixedToFloat(FP16 value);
+FP16 fixedMul(FP16 a, FP16 b);
+FP16 fixedDiv(FP16 a, FP16 b);
+int fixedTrunc(FP16 value);
+FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2);
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_FIXEDPOINT_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 47ed48c..e36518d 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -164,4 +164,9 @@ Graphics::Surface *IllusionsEngine::getBackSurface() {
 	return 0;
 }
 
+Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
+	// TODO Dummy, to be replaced later
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index f734867..8978d8d 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -80,6 +80,8 @@ public:
 	bool isDisplayOn();
 	uint16 getColorKey2();
 	Graphics::Surface *getBackSurface();
+	
+	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 
 #if 0
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 504f636..30fd9b9 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	backgroundresource.o \
 	camera.o \
 	detection.o \
+	fixedpoint.o \
 	graphics.o \
 	illusions.o \
 	input.o \


Commit: 4211f8ffcd74563c7f7a17c34cc14ebc7bdeee59
    https://github.com/scummvm/scummvm/commit/4211f8ffcd74563c7f7a17c34cc14ebc7bdeee59
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with ActorResource and related classes

Changed paths:
  A engines/illusions/actorresource.cpp
  A engines/illusions/actorresource.h
    engines/illusions/graphics.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk


diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
new file mode 100644
index 0000000..59123bb
--- /dev/null
+++ b/engines/illusions/actorresource.cpp
@@ -0,0 +1,245 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/actorresource.h"
+
+namespace Illusions {
+
+// ActorResourceLoader
+
+void ActorResourceLoader::load(Resource *resource) {
+	// TODO
+	debug("ActorResourceLoader::load() Loading actor %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+	ActorResource *actorResource = new ActorResource();
+	actorResource->load(resource->_data, resource->_dataSize);
+	
+	ActorItem *actorItem = _vm->_actorItems->allocActorItem();
+	actorItem->_tag = resource->_tag;
+	actorItem->_pauseCtr = 0;
+	actorItem->_actRes = actorResource;
+	
+	for (uint i = 0; i < actorResource->_actorTypes.size(); ++i) {
+		ActorType *actorType = &actorResource->_actorTypes[i];
+		ActorType *actorType2 = 0;// TODO _vm->getActorType(actorType->_actorTypeId);
+		if (actorType2) {
+			actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
+				actorType2->_surfInfo._dimensions._width);
+			actorType->_surfInfo._dimensions._height = MAX(actorType->_surfInfo._dimensions._height,
+				actorType2->_surfInfo._dimensions._height);
+			if (actorType->_color.r == 255 && actorType->_color.g == 255 && actorType->_color.b == 255)
+				actorType->_color = actorType2->_color;
+			if (actorType->_value1E == 0)
+				actorType->_value1E = actorType2->_value1E;
+		}
+		// TODO _vm->addActorType(actorType->_actorTypeId, actorType);
+	}
+
+	for (uint i = 0; i < actorResource->_sequences.size(); ++i) {
+		Sequence *sequence = &actorResource->_sequences[i];
+		// TODO _vm->addSequence(sequence->_sequence, sequence);
+	}
+	
+}
+
+void ActorResourceLoader::unload(Resource *resource) {
+}
+
+void ActorResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.act", resource->_resId);
+}
+
+bool ActorResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	stream.readUint32LE(); //field_0 dd
+	stream.readUint32LE(); // TODO config dd
+	_pixelSize = stream.readUint32LE();
+	_dimensions.load(stream);
+	uint32 compressedPixelsOffs = stream.readUint32LE();
+	_compressedPixels = dataStart + compressedPixelsOffs;
+	
+	debug("Frame::load() _pixelSize: %d; compressedPixelsOffs: %08X",
+		_pixelSize, compressedPixelsOffs);
+}
+
+void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_sequenceId = stream.readUint32LE();
+	_unk4 = stream.readUint32LE();
+	uint32 sequenceCodeOffs = stream.readUint32LE();
+	_sequenceCode = dataStart + sequenceCodeOffs;
+	
+	debug("Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
+		_sequenceId, _unk4, sequenceCodeOffs);
+}
+
+void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_actorTypeId = stream.readUint32LE();
+	_surfInfo.load(stream);
+	stream.readUint32LE(); // TODO config dd
+	stream.readUint16LE(); // TODO namedPointsCount dw
+	stream.skip(2); // Skip padding
+	stream.readUint32LE(); // TODO namedPoints dd
+	_color.r = stream.readByte();
+	_color.g = stream.readByte();
+	_color.b = stream.readByte();
+	stream.readByte(); // Skip padding
+	_scale = stream.readByte();
+	_priority = stream.readByte();
+	_value1E = stream.readUint16LE();
+	_bgItem28sIndex = stream.readUint16LE();
+	_bgItem22sIndex = stream.readUint16LE();
+	_bgItem30sIndex = stream.readUint16LE();
+	_bgItem26sIndex = stream.readUint16LE();
+	_bgItem38sIndex = stream.readUint16LE();
+	_flags = stream.readUint16LE();
+	
+	debug("ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
+		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
+	debug("ActorType::load() _bgItem28sIndex: %d; _bgItem22sIndex: %d; _bgItem30sIndex: %d",
+		_bgItem28sIndex, _bgItem22sIndex, _bgItem30sIndex);
+	debug("ActorType::load() _bgItem26sIndex: %d; _bgItem38sIndex: %d; _flags: %04X",
+		_bgItem26sIndex, _bgItem38sIndex,_flags);
+}
+
+// ActorResource
+
+ActorResource::ActorResource() {
+}
+
+ActorResource::~ActorResource() {
+}
+
+void ActorResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	_totalSize = stream.readUint32LE();
+
+	// Load actor types	
+	stream.seek(0x06);
+	uint actorTypesCount = stream.readUint16LE();
+	stream.seek(0x10);
+	uint32 actorTypesOffs = stream.readUint32LE();
+	stream.seek(actorTypesOffs);
+	_actorTypes.reserve(actorTypesCount);
+	for (uint i = 0; i < actorTypesCount; ++i) {
+		ActorType actorType;
+		actorType.load(data, stream);
+		_actorTypes.push_back(actorType);
+	}
+
+	// Load sequences	
+	stream.seek(0x08);
+	uint sequencesCount = stream.readUint16LE();
+	stream.seek(0x14);
+	uint32 sequencesOffs = stream.readUint32LE();
+	stream.seek(sequencesOffs);
+	_actorTypes.reserve(sequencesCount);
+	for (uint i = 0; i < sequencesCount; ++i) {
+		Sequence sequence;
+		sequence.load(data, stream);
+		_sequences.push_back(sequence);
+	}
+
+	// Load frames	
+	stream.seek(0x0A);
+	uint framesCount = stream.readUint16LE();
+	stream.seek(0x18);
+	uint32 framesOffs = stream.readUint32LE();
+	stream.seek(framesOffs);
+	_actorTypes.reserve(framesCount);
+	for (uint i = 0; i < framesCount; ++i) {
+		Frame frame;
+		frame.load(data, stream);
+		_frames.push_back(frame);
+	}
+
+}
+
+// ActorItem
+
+ActorItem::ActorItem() {
+}
+
+ActorItem::~ActorItem() {
+}
+
+void ActorItem::pause() {
+	++_pauseCtr;
+	if (_pauseCtr == 1) {
+		/* TODO
+		for (uint i = 0; i < _actRes->_actorTypes.size(); ++i)
+			// TODO _vm->removeActorType(_actRes->_actorTypes[i]._actorTypeId);
+		for (uint i = 0; i < actorResource->_sequences.size(); ++i)
+			// TODO _vm->removeSequence(_actRes->_sequences[i]._sequence);
+		*/
+	}
+}
+
+void ActorItem::unpause() {
+	--_pauseCtr;
+	if (_pauseCtr == 0) {
+		/* TODO
+		for (uint i = 0; i < _actRes->_actorTypes.size(); ++i) {
+			ActorType *actorType = &_actRes->_actorTypes[i];
+			// TODO _vm->addActorType(actorType->_actorTypeId, actorType);
+		}
+		for (uint i = 0; i < _actRes->_sequences.size(); ++i) {
+			Sequence *sequence = &_actRes->_sequences[i];
+			// TODO _vm->addSequence(sequence->_sequence, sequence);
+		}
+		*/
+	}
+}
+
+// ActorItems
+
+ActorItems::ActorItems(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+ActorItems::~ActorItems() {
+}
+
+ActorItem *ActorItems::allocActorItem() {
+	ActorItem *actorItem = new ActorItem();
+	_items.push_back(actorItem);
+	return actorItem;
+}
+
+void ActorItems::pauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->pause();
+}
+
+void ActorItems::unpauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->pause();
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
new file mode 100644
index 0000000..196d72d
--- /dev/null
+++ b/engines/illusions/actorresource.h
@@ -0,0 +1,121 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ACTORRESOURCE_H
+#define ILLUSIONS_ACTORRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class ActorResourceLoader : public BaseResourceLoader {
+public:
+	ActorResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~ActorResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+struct Frame {
+	//field_0 dd
+	// TODO config dd
+	uint32 _pixelSize;
+	WidthHeight _dimensions;
+	byte *_compressedPixels;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct Sequence {
+	uint32 _sequenceId;
+	uint32 _unk4;
+	byte *_sequenceCode;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct ActorType {
+	uint32 _actorTypeId;
+	SurfInfo _surfInfo;
+	// TODO config dd
+	// TODO namedPointsCount dw
+	// TODO namedPoints dd
+	RGB _color;
+	byte _scale;
+	byte _priority;
+	int16 _value1E;
+	uint16 _bgItem28sIndex;
+	uint16 _bgItem22sIndex;
+	uint16 _bgItem30sIndex;
+	uint16 _bgItem26sIndex;
+	uint16 _bgItem38sIndex;
+	uint16 _flags;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class ActorResource {
+public:
+	ActorResource();
+	~ActorResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	uint32 _totalSize;
+	Common::Array<ActorType> _actorTypes;
+	Common::Array<Sequence> _sequences;
+	Common::Array<Frame> _frames;
+};
+
+class ActorItem {
+public:
+	ActorItem();
+	~ActorItem();
+	void pause();
+	void unpause();
+public:
+	uint32 _tag;
+	int _pauseCtr;
+	ActorResource *_actRes;
+};
+
+class ActorItems {
+public:
+	ActorItems(IllusionsEngine *vm);
+	~ActorItems();
+	ActorItem *allocActorItem();
+	void pauseByTag(uint32 tag);
+	void unpauseByTag(uint32 tag);
+protected:
+	typedef Common::List<ActorItem*> Items;
+	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
+	Items _items;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ACTORRESOURCE_H
diff --git a/engines/illusions/graphics.h b/engines/illusions/graphics.h
index 5a5cb02..4f50f15 100644
--- a/engines/illusions/graphics.h
+++ b/engines/illusions/graphics.h
@@ -44,6 +44,10 @@ struct WRect {
 	Common::Point _bottomRight;
 };
 
+struct RGB {
+	byte r, g, b;
+};
+
 void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt);
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index e36518d..8cfd29e 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -30,6 +30,7 @@
 #include "illusions/spritedrawqueue.h"
 #include "illusions/spritedecompressqueue.h"
 #include "illusions/actor.h"
+#include "illusions/actorresource.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -77,16 +78,23 @@ Common::Error IllusionsEngine::run() {
 	initGraphics(640, 480, true, &pixelFormat16);
 	
 	_resSys = new ResourceSystem();
+	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+
+    _actorItems = new ActorItems(this);
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
 	
-	_resSys->loadResource(0x0011000B, 0, 0);
+	_resSys->loadResource(0x00100006, 0, 0);
 
+	/*
+	_resSys->loadResource(0x0011000B, 0, 0);
 	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
 	_system->copyRectToScreen(backgroundItem->_surfaces[0]->getPixels(), backgroundItem->_surfaces[0]->pitch,
 		0, 0, 640, 480);
 	_system->updateScreen();
+	*/
 
 	while (!shouldQuit()) {
 		updateEvents();
@@ -94,6 +102,7 @@ Common::Error IllusionsEngine::run() {
 	
 	delete _camera;
 	delete _backgroundItems;
+	delete _actorItems;
 	delete _resSys;
 	
 	return Common::kNoError;
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 8978d8d..f375477 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -48,6 +48,8 @@ class ResourceSystem;
 
 struct SurfInfo;
 
+class ActorItem;
+class ActorItems;
 class BackgroundItem;
 class BackgroundItems;
 class BackgroundResource;
@@ -71,6 +73,7 @@ public:
 	
 	void updateEvents();
 
+    ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 30fd9b9..722a552 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/illusions
 
 MODULE_OBJS := \
 	actor.o \
+	actorresource.o \
 	backgroundresource.o \
 	camera.o \
 	detection.o \


Commit: d2b036fa5b22f7a4802443b5b72adb6d444893c3
    https://github.com/scummvm/scummvm/commit/d2b036fa5b22f7a4802443b5b72adb6d444893c3
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with thread classes

Changed paths:
  A engines/illusions/thread.cpp
  A engines/illusions/thread.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 8cfd29e..06662fb 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -31,6 +31,7 @@
 #include "illusions/spritedecompressqueue.h"
 #include "illusions/actor.h"
 #include "illusions/actorresource.h"
+#include "illusions/thread.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 722a552..99e84f5 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS := \
 	resourcesystem.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
+	thread.o \
 	time.o \
 	updatefunctions.o
 
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
new file mode 100644
index 0000000..8a1c805
--- /dev/null
+++ b/engines/illusions/thread.cpp
@@ -0,0 +1,134 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+// Thread
+
+Thread::Thread(IllusionsEngine *vm)
+	: _vm(vm), _pauseCtr(0), _terminated(false) {
+}
+
+void Thread::pause() {
+	if (!_terminated) {
+		++_pauseCtr;
+		if (_pauseCtr == 1)
+			onPause();
+	}
+}
+
+void Thread::resume() {
+	if (!_terminated) {
+		--_pauseCtr;
+		if (_pauseCtr == 0)
+			onResume();
+	}
+}
+
+void Thread::suspend() {
+	if (!_terminated) {
+		++_pauseCtr;
+		if (_pauseCtr == 1)
+			onSuspend();
+	}
+}
+
+void Thread::notify() {
+	if (!_terminated) {
+		--_pauseCtr;
+		if (_pauseCtr == 0)
+			onNotify();
+	}
+}
+
+int Thread::update() {
+	// NOTE Deletion of removed threads handled in caller
+	int result = 2;
+	if (!_terminated) {
+		if (_pauseCtr > 0)
+			result = 2;
+		else
+			result = onUpdate();
+		if (result == 1)
+			terminate();
+		else if (result == 3)
+			suspend();
+	}
+	return result;
+}
+
+void Thread::terminate() {
+	if (!_terminated) {
+		if (_callingThreadId) {
+			if (!(_notifyFlags & 1)) {
+				// TODO scrmgrNotifyID(_callingThreadId);
+			}
+			_callingThreadId = 0;
+		}
+		onTerminated();
+		// TODO _vm->removeThread(_threadId, this);
+		_terminated = true;
+	}
+}
+
+// ThreadList
+
+ThreadList::ThreadList(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void ThreadList::startThread(Thread *thread) {
+	// TODO tag has to be set by the Thread class scrmgrGetCurrentScene();
+	_threads.push_back(thread);
+	// TODO _vm->addThread(thread->_threadId, thread);
+}
+
+void ThreadList::updateThreads() {
+	while (1) {
+		Iterator it = _threads.begin();
+		while (it != _threads.end()) {
+			Thread *thread = *it;
+			if (thread->_terminated) {
+				it = _threads.erase(it);
+				delete thread;
+			} else {
+				while (!thread->_terminated) {
+					int updateResult = thread->update();
+					if (updateResult == 1 || updateResult == 2)
+						break;
+				}
+				++it;
+			}
+		}
+		/* TODO
+		if (script->threadUpdateContinueFlag)
+			script->_threadUpdateContinueFlag = false;
+		else
+		*/
+			break;		
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
new file mode 100644
index 0000000..27d4c41
--- /dev/null
+++ b/engines/illusions/thread.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_THREAD_H
+#define ILLUSIONS_THREAD_H
+
+#include "common/list.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class Thread {
+public:
+	Thread(IllusionsEngine *vm);
+	virtual ~Thread();
+	virtual int onUpdate() = 0;
+	virtual void onSuspend() = 0;
+	virtual void onNotify() = 0;
+	virtual void onPause() = 0;
+	virtual void onResume() = 0;
+	virtual void onTerminated() = 0;
+	void pause();
+	void resume();
+	void suspend();
+	void notify();
+	int update();
+	void terminate();
+public:
+	IllusionsEngine *_vm;
+	//field_0 dw
+	int _pauseCtr;
+	bool _terminated;
+	//field_6 dw
+	uint _type;
+	uint32 _threadId;
+	uint32 _callingThreadId;
+	uint32 _tag;
+	uint _notifyFlags;
+};
+
+class ThreadList {
+public:
+	ThreadList(IllusionsEngine *vm);
+	void startThread(Thread *thread);
+	void updateThreads();
+protected:
+	typedef Common::List<Thread*> List;
+	typedef List::iterator Iterator;
+	IllusionsEngine *_vm;
+	List _threads;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_THREAD_H


Commit: bb67c2c2ffdb8794d08fd1937a533caec2465a62
    https://github.com/scummvm/scummvm/commit/bb67c2c2ffdb8794d08fd1937a533caec2465a62
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with ScriptResource and related code

Changed paths:
  A engines/illusions/scriptresource.cpp
  A engines/illusions/scriptresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 06662fb..17fb730 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -32,6 +32,7 @@
 #include "illusions/actor.h"
 #include "illusions/actorresource.h"
 #include "illusions/thread.h"
+#include "illusions/scriptresource.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -80,6 +81,7 @@ Common::Error IllusionsEngine::run() {
 	
 	_resSys = new ResourceSystem();
 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 
@@ -87,15 +89,22 @@ Common::Error IllusionsEngine::run() {
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
 	
+	/*
+	// ActorResource test
 	_resSys->loadResource(0x00100006, 0, 0);
+	*/
 
 	/*
+	// BackgroundResource test
 	_resSys->loadResource(0x0011000B, 0, 0);
 	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
 	_system->copyRectToScreen(backgroundItem->_surfaces[0]->getPixels(), backgroundItem->_surfaces[0]->pitch,
 		0, 0, 640, 480);
 	_system->updateScreen();
 	*/
+	
+	// ScriptResource test
+	_resSys->loadResource(0x000D0001, 0, 0);
 
 	while (!shouldQuit()) {
 		updateEvents();
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index f375477..f283f17 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -54,6 +54,7 @@ class BackgroundItem;
 class BackgroundItems;
 class BackgroundResource;
 class Camera;
+class ScriptResource;
 
 class IllusionsEngine : public Engine {
 protected:
@@ -76,6 +77,8 @@ public:
     ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
+	
+	ScriptResource *_scriptResource;
 
 	// Screen functions	
 	Graphics::Surface *allocSurface(int16 width, int16 height);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 99e84f5..e8d83ef 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	illusions.o \
 	input.o \
 	resourcesystem.o \
+	scriptresource.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	thread.o \
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
new file mode 100644
index 0000000..27f5cad
--- /dev/null
+++ b/engines/illusions/scriptresource.cpp
@@ -0,0 +1,197 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/scriptresource.h"
+
+namespace Illusions {
+
+// ScriptResourceLoader
+
+void ScriptResourceLoader::load(Resource *resource) {
+	debug("ScriptResourceLoader::load() Loading script %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+	ScriptResource *scriptResource = new ScriptResource();
+	scriptResource->load(resource->_data, resource->_dataSize);
+	
+	
+	
+	_vm->_scriptResource = scriptResource;
+	
+}
+
+void ScriptResourceLoader::unload(Resource *resource) {
+}
+
+void ScriptResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.scr", resource->_resId);
+}
+
+bool ScriptResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+Properties::Properties()
+	: _count(0), _properties(0) {
+}
+
+void Properties::init(uint count, byte *properties) {
+	_count = count;
+	_properties = properties;
+}
+
+// BlockCounters
+
+BlockCounters::BlockCounters()
+	: _count(0), _blockCounters(0) {
+}
+
+void BlockCounters::init(uint count, byte *blockCounters) {
+	_count = count;
+	_blockCounters = blockCounters;
+}
+
+
+// TriggerCause
+
+void TriggerCause::load(Common::SeekableReadStream &stream) {
+	_verbId = stream.readUint32LE();
+	_objectId2 = stream.readUint32LE();
+	_codeOffs = stream.readUint32LE();
+	
+	debug("TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
+		_verbId, _objectId2, _codeOffs);
+}
+
+// TriggerObject
+
+TriggerObject::TriggerObject()
+	: _causesCount(0), _causes(0) {
+}
+
+TriggerObject::~TriggerObject() {
+	delete[] _causes;
+}
+
+void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_objectId = stream.readUint32LE();
+	_causesCount = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	debug("TriggerObject::load() _objectId: %08X; _causesCount: %d",
+		_objectId, _causesCount);
+	_causes = new TriggerCause[_causesCount];
+	for (uint i = 0; i < _causesCount; ++i)
+		_causes[i].load(stream);
+}
+
+// ProgInfo
+
+ProgInfo::ProgInfo()
+	: _triggerObjectsCount(0), _triggerObjects(0) {
+}
+
+ProgInfo::~ProgInfo() {
+	delete[] _triggerObjects;
+}
+
+char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	while (*wstr != 0) {
+		*p++ = *wstr;
+		wstr += 2;
+	}
+	*p = 0;
+	return buf;
+}
+
+void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_id = stream.readUint16LE();
+	_unk = stream.readUint16LE();
+	_name = dataStart + stream.pos();
+	stream.skip(128);
+	_triggerObjectsCount = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	debug("\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
+		_id, _unk, debugW2I(_name));
+	uint32 triggerObjectsListOffs = stream.readUint32LE();
+	if (_triggerObjectsCount > 0) {
+		_triggerObjects = new TriggerObject[_triggerObjectsCount];
+		for (uint i = 0; i < _triggerObjectsCount; ++i) {
+			stream.seek(triggerObjectsListOffs + i * 4);
+			uint32 triggerObjectOffs = stream.readUint32LE();
+			stream.seek(triggerObjectOffs);
+			_triggerObjects[i].load(dataStart, stream);
+		}
+	}
+}
+
+// ScriptResource
+
+ScriptResource::ScriptResource()
+	: _codeOffsets(0) {
+}
+
+ScriptResource::~ScriptResource() {
+	delete[] _codeOffsets;
+}
+
+void ScriptResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+	
+	stream.skip(4); // Skip unused
+	uint propertiesCount = stream.readUint16LE();
+	uint blockCountersCount = stream.readUint16LE();
+	uint codeCount = stream.readUint16LE();
+	_progInfosCount = stream.readUint16LE();
+	uint32 propertiesOffs = stream.readUint32LE();
+	uint32 blockCountersOffs = stream.readUint32LE();
+	uint32 codeTblOffs = stream.readUint32LE();
+	
+	// Init properties
+	_properties.init(propertiesCount, data + propertiesOffs);
+	
+	// Init blockcounters
+	_blockCounters.init(blockCountersCount, data + blockCountersOffs);
+	
+	_codeOffsets = new uint32[codeCount];
+	stream.seek(codeTblOffs);
+	for (uint i = 0; i < codeCount; ++i)
+		_codeOffsets[i] = stream.readUint32LE();
+
+	_progInfos = new ProgInfo[_progInfosCount];
+	for (uint i = 0; i < _progInfosCount; ++i) {
+		stream.seek(0x18 + i * 4);
+		uint32 progInfoOffs = stream.readUint32LE();
+		stream.seek(progInfoOffs);
+		_progInfos[i].load(data, stream);
+	}
+
+	debug("ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; codeCount: %d; _progInfosCount: %d",
+		propertiesCount, blockCountersCount, codeCount, _progInfosCount);
+	debug("ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X",
+		propertiesOffs, blockCountersOffs, codeTblOffs);
+
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
new file mode 100644
index 0000000..4e010e9
--- /dev/null
+++ b/engines/illusions/scriptresource.h
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTRESOURCE_H
+#define ILLUSIONS_SCRIPTRESOURCE_H
+
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class ScriptResourceLoader : public BaseResourceLoader {
+public:
+	ScriptResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~ScriptResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+class Properties {
+public:
+	Properties();
+	void init(uint count, byte *properties);
+public:
+	uint _count;
+	byte *_properties;
+};
+
+class BlockCounters {
+public:
+	BlockCounters();
+	void init(uint count, byte *blockCounters);
+public:
+	uint _count;
+	byte *_blockCounters;
+};
+
+struct TriggerCause {
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _codeOffs;
+	void load(Common::SeekableReadStream &stream);
+};
+
+class TriggerObject {
+public:
+	TriggerObject();
+	~TriggerObject();
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+public:
+	uint32 _objectId;
+	uint _causesCount;
+	TriggerCause *_causes;
+};
+
+class ProgInfo {
+public:
+	ProgInfo();
+	~ProgInfo();
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+protected:
+	uint16 _id;
+	uint16 _unk;
+	byte *_name;
+	uint _triggerObjectsCount;
+	TriggerObject *_triggerObjects;
+};
+
+class ScriptResource {
+public:
+	ScriptResource();
+	~ScriptResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	Properties _properties;
+	BlockCounters _blockCounters;
+	uint32 *_codeOffsets;
+	uint _progInfosCount;
+	ProgInfo *_progInfos;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ACTORRESOURCE_H


Commit: fc47ac41ae0b5a7fab6a7dfa29b3e33e1e5df2ac
    https://github.com/scummvm/scummvm/commit/fc47ac41ae0b5a7fab6a7dfa29b3e33e1e5df2ac
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on the script system

Changed paths:
  A engines/illusions/scriptman.cpp
  A engines/illusions/scriptman.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 17fb730..3ff6750 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -33,6 +33,7 @@
 #include "illusions/actorresource.h"
 #include "illusions/thread.h"
 #include "illusions/scriptresource.h"
+#include "illusions/scriptman.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index e8d83ef..5ba65d7 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	illusions.o \
 	input.o \
 	resourcesystem.o \
+	scriptman.o \
 	scriptresource.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
new file mode 100644
index 0000000..922ce60
--- /dev/null
+++ b/engines/illusions/scriptman.cpp
@@ -0,0 +1,126 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/scriptman.h"
+
+namespace Illusions {
+
+// ActiveScenes
+
+void ActiveScenes::clear() {
+	_stack.clear();
+}
+
+void ActiveScenes::push(uint32 sceneId) {
+	ActiveScene activeScene;
+	activeScene._sceneId = sceneId;
+	activeScene._pauseCtr = 0;
+	_stack.push(activeScene);
+}
+
+void ActiveScenes::pop() {
+	_stack.pop();
+}
+
+void ActiveScenes::pauseActiveScene() {
+	++_stack.top()._pauseCtr;
+}
+
+void ActiveScenes::unpauseActiveScene() {
+	--_stack.top()._pauseCtr;
+}
+
+int ActiveScenes::getActiveScenesCount() {
+	return _stack.size();
+}
+
+void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
+	if (sceneId)
+		*sceneId = _stack[index]._sceneId;
+	if (pauseCtr)
+		*pauseCtr = _stack[index]._pauseCtr;
+}
+
+uint32 ActiveScenes::getCurrentScene() {
+	if (_stack.size() > 0)
+		return _stack.top()._sceneId;
+	return 0;
+}
+
+bool ActiveScenes::isSceneActive(uint32 sceneId) {
+	for (uint i = 0; i < _stack.size(); ++i)
+		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
+			return true;
+	return false;
+}
+
+// ScriptStack
+
+ScriptStack::ScriptStack() {
+	clear();
+}
+
+void ScriptStack::clear() {
+	for (uint i = 0; i < 256; ++i)
+		_stack[i] = (int16)0xEEEE;
+	_stackPos = 256;
+}
+
+void ScriptStack::push(int16 value) {
+	--_stackPos;
+	if (_stackPos > 0)
+		_stack[_stackPos] = value;
+}
+
+int16 ScriptStack::pop() {
+	int16 value = 0;
+	if (_stackPos < 256) {
+		value = _stack[_stackPos];
+		_stack[_stackPos] = (int16)0xEEEE;
+		++_stackPos;
+	}
+	return value;
+}
+
+int16 ScriptStack::peek() {
+	int16 value = 0;
+	if (_stackPos < 256)
+		value = _stack[_stackPos];
+	return value;
+}
+
+// ScriptMan
+
+ScriptMan::ScriptMan(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+ScriptMan::~ScriptMan() {
+}
+
+void ScriptMan::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
+	_theSceneId = theSceneId;
+	_theThreadId = theThreadId;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
new file mode 100644
index 0000000..18b6090
--- /dev/null
+++ b/engines/illusions/scriptman.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTMAN_H
+#define ILLUSIONS_SCRIPTMAN_H
+
+#include "illusions/scriptresource.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+struct ActiveScene {
+	uint32 _sceneId;
+	int _pauseCtr;
+};
+
+class ActiveScenes {
+public:
+	ActiveScenes();
+	void clear();
+	void push(uint32 sceneId);
+	void pop();
+	void pauseActiveScene();
+	void unpauseActiveScene();
+	int getActiveScenesCount();
+	void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
+	uint32 getCurrentScene();
+	bool isSceneActive(uint32 sceneId);
+protected:
+	Common::FixedStack<ActiveScene, 16> _stack;
+};
+
+class ScriptStack {
+public:
+	ScriptStack();
+	void clear();
+	void push(int16 value);
+	int16 pop();
+	int16 peek();
+protected:
+	int _stackPos;
+	int16 _stack[256];
+};
+
+class ScriptMan {
+public:
+	ScriptMan(IllusionsEngine *vm);
+	~ScriptMan();
+	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
+public:
+
+	IllusionsEngine *_vm;
+	ActiveScenes _activeScenes;
+	ScriptStack _stack;
+	
+	uint32 _theSceneId;
+	uint32 _theThreadId;
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTMAN_H
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 27f5cad..5d9b86a 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -71,6 +71,18 @@ void BlockCounters::init(uint count, byte *blockCounters) {
 	_blockCounters = blockCounters;
 }
 
+void BlockCounters::clear() {
+	for (uint i = 0; i < _count; ++i)
+		_blockCounters[i] = 0;
+}
+
+byte BlockCounters::get(uint index) {
+	return _blockCounters[index] & 0x3F;
+}
+
+void BlockCounters::set(uint index, byte value) {
+	_blockCounters[index] = (get(index) ^ value) & 0x3F;
+}
 
 // TriggerCause
 
@@ -159,6 +171,9 @@ ScriptResource::~ScriptResource() {
 void ScriptResource::load(byte *data, uint32 dataSize) {
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
 	
+	_data = data;
+	_dataSize = dataSize;
+	
 	stream.skip(4); // Skip unused
 	uint propertiesCount = stream.readUint16LE();
 	uint blockCountersCount = stream.readUint16LE();
@@ -194,4 +209,8 @@ void ScriptResource::load(byte *data, uint32 dataSize) {
 
 }
 
+byte *ScriptResource::getThreadCode(uint32 threadId) {
+	return _data + _codeOffsets[threadId & 0xFFFF];
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 4e010e9..206aa4b 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -54,6 +54,9 @@ class BlockCounters {
 public:
 	BlockCounters();
 	void init(uint count, byte *blockCounters);
+	void clear();
+	byte get(uint index);
+	void set(uint index, byte value);
 public:
 	uint _count;
 	byte *_blockCounters;
@@ -95,7 +98,10 @@ public:
 	ScriptResource();
 	~ScriptResource();
 	void load(byte *data, uint32 dataSize);
+	byte *getThreadCode(uint32 threadId);
 public:
+	byte *_data;
+	uint32 _dataSize;
 	Properties _properties;
 	BlockCounters _blockCounters;
 	uint32 *_codeOffsets;


Commit: 9696eb9a546891bf7ff601d94f7a8a2ff6730349
    https://github.com/scummvm/scummvm/commit/9696eb9a546891bf7ff601d94f7a8a2ff6730349
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on the script system

Changed paths:
    engines/illusions/scriptman.h
    engines/illusions/thread.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index 18b6090..acfd514 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -24,6 +24,7 @@
 #define ILLUSIONS_SCRIPTMAN_H
 
 #include "illusions/scriptresource.h"
+#include "illusions/thread.h"
 #include "common/algorithm.h"
 #include "common/stack.h"
 
@@ -78,6 +79,8 @@ public:
 	uint32 _theSceneId;
 	uint32 _theThreadId;
 	
+	
+	
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 8a1c805..8c2f585 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -64,13 +64,10 @@ void Thread::notify() {
 }
 
 int Thread::update() {
-	// NOTE Deletion of removed threads handled in caller
+	// NOTE Deletion of terminated threads handled in caller
 	int result = 2;
-	if (!_terminated) {
-		if (_pauseCtr > 0)
-			result = 2;
-		else
-			result = onUpdate();
+	if (!_terminated && _pauseCtr <= 0) {
+		result = onUpdate();
 		if (result == 1)
 			terminate();
 		else if (result == 3)
@@ -114,11 +111,9 @@ void ThreadList::updateThreads() {
 				it = _threads.erase(it);
 				delete thread;
 			} else {
-				while (!thread->_terminated) {
-					int updateResult = thread->update();
-					if (updateResult == 1 || updateResult == 2)
-						break;
-				}
+				int updateResult = 4;
+				while (!thread->_terminated && updateResult != 1 && updateResult != 2)
+					updateResult = thread->update();
 				++it;
 			}
 		}
@@ -131,4 +126,113 @@ void ThreadList::updateThreads() {
 	}
 }
 
+Thread *ThreadList::findThread(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it)
+		if ((*it)->_threadId == threadId && !(*it)->_terminated)
+			return (*it);
+	return 0;
+}
+
+void ThreadList::suspendId(uint32 threadId) {
+	Thread *thread = findThread(threadId);
+	if (thread)
+		thread->suspend();
+}
+
+void ThreadList::notifyId(uint32 threadId) {
+	Thread *thread = findThread(threadId);
+	if (thread)
+		thread->notify();
+}
+
+void ThreadList::notifyTimerThreads(uint32 callingThreadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_type == kTTTimerThread && thread->_callingThreadId == callingThreadId)
+			thread->notify();
+	}
+}
+
+void ThreadList::suspendTimerThreads(uint32 callingThreadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_type == kTTTimerThread && thread->_callingThreadId == callingThreadId)
+			thread->suspend();
+	}
+}
+
+void ThreadList::terminateThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_threadId != threadId)
+			thread->terminate();
+	}
+}
+
+void ThreadList::terminateThreadsByTag(uint32 tag, uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_tag == tag && thread->_threadId != threadId)
+			thread->terminate();
+	}
+}
+
+void ThreadList::suspendThreadsByTag(uint32 tag, uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_tag == tag && thread->_threadId != threadId)
+			thread->suspend();
+	}
+}
+
+void ThreadList::notifyThreadsByTag(uint32 tag, uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_tag == tag && thread->_threadId != threadId)
+			thread->notify();
+	}
+}
+
+void ThreadList::pauseThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_threadId != threadId)
+			thread->pause();
+	}
+}
+
+void ThreadList::resumeThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_threadId != threadId)
+			thread->resume();
+	}
+}
+
+void ThreadList::killThread(uint32 threadId) {
+
+	if (!threadId)
+		return;
+	
+	Thread *thread = findThread(threadId);
+	if (!thread)
+		return;
+		
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *childThread = *it;
+		if (childThread->_callingThreadId == threadId)
+			killThread(childThread->_threadId);
+	}
+	
+	if (thread->_type == kTTTalkThread) {
+		thread->_callingThreadId = 0;
+		// TODO script_TalkThreads_sub_417F60(thread->_threadId, 0);
+		// TODO script_TalkThreads_sub_417FA0(thread->_threadId, 0);
+	} else {
+		// TODO artmgrThreadIsDead(thread->threadId);
+		thread->terminate();
+	}
+
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index 27d4c41..2a2c0f1 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -29,6 +29,13 @@ namespace Illusions {
 
 class IllusionsEngine;
 
+enum ThreadType {
+	kTTScriptThread  = 1,
+	kTTTimerThread   = 2,
+	kTTTalkThread    = 3,
+	kTTSpecialThread = 5
+};
+
 class Thread {
 public:
 	Thread(IllusionsEngine *vm);
@@ -63,6 +70,18 @@ public:
 	ThreadList(IllusionsEngine *vm);
 	void startThread(Thread *thread);
 	void updateThreads();
+	Thread *findThread(uint32 threadId);
+	void suspendId(uint32 threadId);
+	void notifyId(uint32 threadId);
+	void notifyTimerThreads(uint32 callingThreadId);
+	void suspendTimerThreads(uint32 callingThreadId);
+	void terminateThreads(uint32 threadId);
+	void terminateThreadsByTag(uint32 tag, uint32 threadId);
+	void suspendThreadsByTag(uint32 tag, uint32 threadId);
+	void notifyThreadsByTag(uint32 tag, uint32 threadId);
+	void pauseThreads(uint32 threadId);
+	void resumeThreads(uint32 threadId);
+	void killThread(uint32 threadId);
 protected:
 	typedef Common::List<Thread*> List;
 	typedef List::iterator Iterator;


Commit: 9d35f807ecc0cbc7a98a987c02d58d795706ed1f
    https://github.com/scummvm/scummvm/commit/9d35f807ecc0cbc7a98a987c02d58d795706ed1f
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on the script system

Changed paths:
  A engines/illusions/scriptopcodes.cpp
  A engines/illusions/scriptopcodes.h
  A engines/illusions/scriptthread.cpp
  A engines/illusions/scriptthread.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/thread.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 3ff6750..a3ed76e 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -85,35 +85,51 @@ Common::Error IllusionsEngine::run() {
 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	
+	_scriptMan = new ScriptMan(this);
 
-    _actorItems = new ActorItems(this);
+	_actorItems = new ActorItems(this);
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
 	
-	/*
+#if 0
 	// ActorResource test
 	_resSys->loadResource(0x00100006, 0, 0);
-	*/
+#endif
 
-	/*
+#if 0
 	// BackgroundResource test
 	_resSys->loadResource(0x0011000B, 0, 0);
 	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
 	_system->copyRectToScreen(backgroundItem->_surfaces[0]->getPixels(), backgroundItem->_surfaces[0]->pitch,
 		0, 0, 640, 480);
 	_system->updateScreen();
-	*/
+#endif
 	
+#if 1
 	// ScriptResource test
 	_resSys->loadResource(0x000D0001, 0, 0);
+	
+	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
+
+	while (!shouldQuit()) {
+		updateEvents();
+		_scriptMan->_threads->updateThreads();
+	}
+	
+	
+#endif
 
+#if 0
 	while (!shouldQuit()) {
 		updateEvents();
 	}
+#endif
 	
 	delete _camera;
 	delete _backgroundItems;
 	delete _actorItems;
+	delete _scriptMan;
 	delete _resSys;
 	
 	return Common::kNoError;
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index f283f17..bfb7335 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -55,6 +55,7 @@ class BackgroundItems;
 class BackgroundResource;
 class Camera;
 class ScriptResource;
+class ScriptMan;
 
 class IllusionsEngine : public Engine {
 protected:
@@ -74,11 +75,10 @@ public:
 	
 	void updateEvents();
 
-    ActorItems *_actorItems;
+	ScriptMan *_scriptMan;
+	ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
-	
-	ScriptResource *_scriptResource;
 
 	// Screen functions	
 	Graphics::Surface *allocSurface(int16 width, int16 height);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 5ba65d7..8dd462c 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -12,7 +12,9 @@ MODULE_OBJS := \
 	input.o \
 	resourcesystem.o \
 	scriptman.o \
+	scriptopcodes.o \
 	scriptresource.o \
+	scriptthread.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	thread.o \
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 922ce60..25b8d2e 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -22,11 +22,17 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptman.h"
+#include "illusions/scriptthread.h"
+#include "illusions/scriptopcodes.h"
 
 namespace Illusions {
 
 // ActiveScenes
 
+ActiveScenes::ActiveScenes() {
+	clear();
+}
+
 void ActiveScenes::clear() {
 	_stack.clear();
 }
@@ -112,10 +118,14 @@ int16 ScriptStack::peek() {
 // ScriptMan
 
 ScriptMan::ScriptMan(IllusionsEngine *vm)
-	: _vm(vm) {
+	: _vm(vm), _pauseCtr(0), _doScriptThreadInit(false) {
+	_threads = new ThreadList(vm);
+	_scriptOpcodes = new ScriptOpcodes(vm);
 }
 
 ScriptMan::~ScriptMan() {
+	delete _threads;
+	delete _scriptOpcodes;
 }
 
 void ScriptMan::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
@@ -123,4 +133,52 @@ void ScriptMan::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
 	_theThreadId = theThreadId;
 }
 
+void ScriptMan::startScriptThread(uint32 threadId, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug("Starting script thread %08X", threadId);
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+}
+
+void ScriptMan::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug("Starting anonymous script thread %08X", threadId);
+	uint32 tempThreadId = newTempThreadId();
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+}
+
+uint32 ScriptMan::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug("Starting temp script thread");
+	uint32 tempThreadId = newTempThreadId();
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+	return tempThreadId;
+}
+
+void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
+	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId,
+		notifyFlags, scriptCodeIp, value8, valueC, value10);
+	_threads->startThread(scriptThread);
+	if (_pauseCtr > 0)
+		scriptThread->pause();
+	if (_doScriptThreadInit) {
+		int updateResult = 4;
+		while (scriptThread->_pauseCtr <= 0 && updateResult != 1 && updateResult != 2)
+			updateResult = scriptThread->update();
+	}
+}
+
+uint32 ScriptMan::newTempThreadId() {
+	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
+	if (threadId > 65535) {
+		_nextTempThreadId = 0;
+		threadId = 2 * _scriptResource->_codeCount;
+	}
+	++_nextTempThreadId;
+	return 0x00020000 | threadId;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index acfd514..031fa19 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -31,6 +31,7 @@
 namespace Illusions {
 
 class IllusionsEngine;
+class ScriptOpcodes;
 
 struct ActiveScene {
 	uint32 _sceneId;
@@ -70,17 +71,34 @@ public:
 	ScriptMan(IllusionsEngine *vm);
 	~ScriptMan();
 	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
+	void startScriptThread(uint32 threadId, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void startAnonScriptThread(int32 threadId, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
 public:
 
 	IllusionsEngine *_vm;
+	ScriptResource *_scriptResource;
+
 	ActiveScenes _activeScenes;
 	ScriptStack _stack;
 	
+	int _pauseCtr;
+	
 	uint32 _theSceneId;
 	uint32 _theThreadId;
+	bool _doScriptThreadInit;
+	uint32 _nextTempThreadId;
 	
+	ThreadList *_threads;
+	ScriptOpcodes *_scriptOpcodes;
 	
-	
+	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
+	uint32 newTempThreadId();
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
new file mode 100644
index 0000000..ac38548
--- /dev/null
+++ b/engines/illusions/scriptopcodes.cpp
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/scriptopcodes.h"
+#include "illusions/scriptman.h"
+#include "illusions/scriptresource.h"
+#include "illusions/scriptthread.h"
+
+namespace Illusions {
+
+// ScriptOpcodes
+
+ScriptOpcodes::ScriptOpcodes(IllusionsEngine *vm)
+	: _vm(vm) {
+	initOpcodes();
+}
+
+ScriptOpcodes::~ScriptOpcodes() {
+	freeOpcodes();
+}
+
+void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
+	if (!_opcodes[opCall._op])
+		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
+	debug("execOpcode(%d)", opCall._op);
+	(*_opcodes[opCall._op])(scriptThread, opCall);
+}
+
+typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes> ScriptOpcodeI;
+#define OPCODE(op, func) _opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes::func);
+
+void ScriptOpcodes::initOpcodes() {
+	// First clear everything
+	for (uint i = 0; i < 256; ++i)
+		_opcodes[i] = 0;
+	// Register opcodes
+	OPCODE(42, opIncBlockCounter);
+	OPCODE(126, opDebug126);
+}
+
+#undef OPCODE
+
+void ScriptOpcodes::freeOpcodes() {
+	for (uint i = 0; i < 256; ++i)
+		delete _opcodes[i];
+}
+
+// Opcodes
+
+// Convenience macros
+#define	ARG_SKIP(x) opCall.skip(x); 
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %d)", name);
+
+void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index)	
+	byte value = _vm->_scriptMan->_scriptResource->_blockCounters.get(index + 1);
+	if (value <= 63)
+		_vm->_scriptMan->_scriptResource->_blockCounters.set(index + 1, value);
+}
+
+void ScriptOpcodes::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
new file mode 100644
index 0000000..78b68e5
--- /dev/null
+++ b/engines/illusions/scriptopcodes.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTOPCODES_H
+#define ILLUSIONS_SCRIPTOPCODES_H
+
+#include "common/func.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+class ScriptThread;
+struct OpCall;
+
+typedef Common::Functor2<ScriptThread*, OpCall&, void> ScriptOpcode;
+
+class ScriptOpcodes {
+public:
+	ScriptOpcodes(IllusionsEngine *vm);
+	~ScriptOpcodes();
+	void execOpcode(ScriptThread *scriptThread, OpCall &opCall);
+protected:
+	IllusionsEngine *_vm;
+	ScriptOpcode *_opcodes[256];
+	void initOpcodes();
+	void freeOpcodes();
+
+	// Opcodes	
+	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTOPCODES_H
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 5d9b86a..545e79b 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -22,20 +22,19 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptresource.h"
+#include "illusions/scriptman.h"
 
 namespace Illusions {
 
 // ScriptResourceLoader
 
 void ScriptResourceLoader::load(Resource *resource) {
-	debug("ScriptResourceLoader::load() Loading script %08X from %s...", resource->_resId, resource->_filename.c_str());
+	debug(2, "ScriptResourceLoader::load() Loading script %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	ScriptResource *scriptResource = new ScriptResource();
 	scriptResource->load(resource->_data, resource->_dataSize);
 	
-	
-	
-	_vm->_scriptResource = scriptResource;
+	_vm->_scriptMan->_scriptResource = scriptResource;
 	
 }
 
@@ -77,11 +76,11 @@ void BlockCounters::clear() {
 }
 
 byte BlockCounters::get(uint index) {
-	return _blockCounters[index] & 0x3F;
+	return _blockCounters[index - 1] & 0x3F;
 }
 
 void BlockCounters::set(uint index, byte value) {
-	_blockCounters[index] = (get(index) ^ value) & 0x3F;
+	_blockCounters[index - 1] = (get(index - 1) ^ value) & 0x3F;
 }
 
 // TriggerCause
@@ -91,7 +90,7 @@ void TriggerCause::load(Common::SeekableReadStream &stream) {
 	_objectId2 = stream.readUint32LE();
 	_codeOffs = stream.readUint32LE();
 	
-	debug("TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
+	debug(2, "TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
 		_verbId, _objectId2, _codeOffs);
 }
 
@@ -109,7 +108,7 @@ void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_objectId = stream.readUint32LE();
 	_causesCount = stream.readUint16LE();
 	stream.skip(2); // Skip padding
-	debug("TriggerObject::load() _objectId: %08X; _causesCount: %d",
+	debug(2, "TriggerObject::load() _objectId: %08X; _causesCount: %d",
 		_objectId, _causesCount);
 	_causes = new TriggerCause[_causesCount];
 	for (uint i = 0; i < _causesCount; ++i)
@@ -144,7 +143,7 @@ void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	stream.skip(128);
 	_triggerObjectsCount = stream.readUint16LE();
 	stream.skip(2); // Skip padding
-	debug("\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
+	debug(2, "\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
 		_id, _unk, debugW2I(_name));
 	uint32 triggerObjectsListOffs = stream.readUint32LE();
 	if (_triggerObjectsCount > 0) {
@@ -177,7 +176,7 @@ void ScriptResource::load(byte *data, uint32 dataSize) {
 	stream.skip(4); // Skip unused
 	uint propertiesCount = stream.readUint16LE();
 	uint blockCountersCount = stream.readUint16LE();
-	uint codeCount = stream.readUint16LE();
+	_codeCount = stream.readUint16LE();
 	_progInfosCount = stream.readUint16LE();
 	uint32 propertiesOffs = stream.readUint32LE();
 	uint32 blockCountersOffs = stream.readUint32LE();
@@ -189,9 +188,9 @@ void ScriptResource::load(byte *data, uint32 dataSize) {
 	// Init blockcounters
 	_blockCounters.init(blockCountersCount, data + blockCountersOffs);
 	
-	_codeOffsets = new uint32[codeCount];
+	_codeOffsets = new uint32[_codeCount];
 	stream.seek(codeTblOffs);
-	for (uint i = 0; i < codeCount; ++i)
+	for (uint i = 0; i < _codeCount; ++i)
 		_codeOffsets[i] = stream.readUint32LE();
 
 	_progInfos = new ProgInfo[_progInfosCount];
@@ -202,15 +201,15 @@ void ScriptResource::load(byte *data, uint32 dataSize) {
 		_progInfos[i].load(data, stream);
 	}
 
-	debug("ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; codeCount: %d; _progInfosCount: %d",
-		propertiesCount, blockCountersCount, codeCount, _progInfosCount);
-	debug("ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X",
+	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _progInfosCount: %d",
+		propertiesCount, blockCountersCount, _codeCount, _progInfosCount);
+	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X",
 		propertiesOffs, blockCountersOffs, codeTblOffs);
 
 }
 
 byte *ScriptResource::getThreadCode(uint32 threadId) {
-	return _data + _codeOffsets[threadId & 0xFFFF];
+	return _data + _codeOffsets[(threadId & 0xFFFF) - 1];
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 206aa4b..d659f43 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -104,6 +104,7 @@ public:
 	uint32 _dataSize;
 	Properties _properties;
 	BlockCounters _blockCounters;
+	uint _codeCount;
 	uint32 *_codeOffsets;
 	uint _progInfosCount;
 	ProgInfo *_progInfos;
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
new file mode 100644
index 0000000..a594101
--- /dev/null
+++ b/engines/illusions/scriptthread.cpp
@@ -0,0 +1,103 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/scriptthread.h"
+#include "illusions/scriptman.h"
+#include "illusions/scriptopcodes.h"
+
+namespace Illusions {
+
+// OpCall
+
+void OpCall::skip(uint size) {
+	_scriptCode += size;
+}
+
+byte OpCall::readByte() {
+	return *_scriptCode++;
+}
+
+int16 OpCall::readSint16() {
+	int16 value = READ_LE_UINT16(_scriptCode);
+	_scriptCode += 2;
+	return value;
+}
+
+uint32 OpCall::readUint32() {
+	uint32 value = READ_LE_UINT32(_scriptCode);
+	_scriptCode += 4;
+	return value;
+}
+
+// ScriptThread
+
+ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptCodeIp(scriptCodeIp), _value8(value8),
+	_valueC(valueC), _value10(value10), _sequenceStalled(0) {
+	_type = kTTScriptThread;
+}
+
+int ScriptThread::onUpdate() {
+	OpCall opCall;
+	opCall._result = kTSRun;
+	while (!_terminated && opCall._result == 4) {
+		opCall._op = _scriptCodeIp[0];
+		opCall._opSize = _scriptCodeIp[1] >> 1;
+		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
+		opCall._scriptCode = _scriptCodeIp + 2;
+		opCall._deltaOfs = 0;
+		execOpcode(opCall);
+		_scriptCodeIp += opCall._opSize + opCall._deltaOfs;
+	}
+	if (_terminated)
+		opCall._result = kTSTerminate;
+	return opCall._result;
+}
+
+void ScriptThread::onSuspend() {
+	// TODO
+}
+
+void ScriptThread::onNotify() {
+	// TODO
+}
+
+void ScriptThread::onPause() {
+	// TODO
+}
+
+void ScriptThread::onResume() {
+	// TODO
+}
+
+void ScriptThread::onTerminated() {
+	// TODO
+}
+
+void ScriptThread::execOpcode(OpCall &opCall) {
+	// TODO Clean this up
+	_vm->_scriptMan->_scriptOpcodes->execOpcode(this, opCall);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptthread.h b/engines/illusions/scriptthread.h
new file mode 100644
index 0000000..201e598
--- /dev/null
+++ b/engines/illusions/scriptthread.h
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTTHREAD_H
+#define ILLUSIONS_SCRIPTTHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+struct OpCall {
+	byte _op;
+	byte _opSize;
+	uint32 _threadId;
+	int16 _deltaOfs;
+	byte *_scriptCode;
+	int _result;
+	void skip(uint size);
+	byte readByte();
+	int16 readSint16();
+	uint32 readUint32();
+};
+
+class ScriptThread : public Thread {
+public:
+	ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	int16 _sequenceStalled;
+	byte *_scriptCodeIp;
+	uint32 _value8;
+	uint32 _valueC;
+	uint32 _value10;
+	void execOpcode(OpCall &opCall);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTTHREAD_H
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 8c2f585..0bfb82e 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -27,8 +27,31 @@ namespace Illusions {
 
 // Thread
 
-Thread::Thread(IllusionsEngine *vm)
-	: _vm(vm), _pauseCtr(0), _terminated(false) {
+Thread::Thread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags)
+	: _vm(vm), _threadId(threadId), _callingThreadId(callingThreadId), _notifyFlags(notifyFlags),
+	_pauseCtr(0), _terminated(false) {
+}
+
+Thread::~Thread() {
+}
+
+int Thread::onUpdate() {
+	return kTSTerminate;
+}
+
+void Thread::onSuspend() {
+}
+
+void Thread::onNotify() {
+}
+
+void Thread::onPause() {
+}
+
+void Thread::onResume() {
+}
+
+void Thread::onTerminated() {
 }
 
 void Thread::pause() {
@@ -65,15 +88,15 @@ void Thread::notify() {
 
 int Thread::update() {
 	// NOTE Deletion of terminated threads handled in caller
-	int result = 2;
+	int status = kTSYield;
 	if (!_terminated && _pauseCtr <= 0) {
-		result = onUpdate();
-		if (result == 1)
+		status = onUpdate();
+		if (status == kTSTerminate)
 			terminate();
-		else if (result == 3)
+		else if (status == kTSSuspend)
 			suspend();
 	}
-	return result;
+	return status;
 }
 
 void Thread::terminate() {
@@ -111,9 +134,9 @@ void ThreadList::updateThreads() {
 				it = _threads.erase(it);
 				delete thread;
 			} else {
-				int updateResult = 4;
-				while (!thread->_terminated && updateResult != 1 && updateResult != 2)
-					updateResult = thread->update();
+				int status = kTSRun;
+				while (!thread->_terminated && status != kTSTerminate && status != kTSYield)
+					status = thread->update();
 				++it;
 			}
 		}
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index 2a2c0f1..eab92ae 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -36,16 +36,23 @@ enum ThreadType {
 	kTTSpecialThread = 5
 };
 
+enum ThreadStatus {
+	kTSTerminate     = 1,
+	kTSYield         = 2,
+	kTSSuspend       = 3,
+	kTSRun           = 4
+};
+
 class Thread {
 public:
-	Thread(IllusionsEngine *vm);
+	Thread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags);
 	virtual ~Thread();
-	virtual int onUpdate() = 0;
-	virtual void onSuspend() = 0;
-	virtual void onNotify() = 0;
-	virtual void onPause() = 0;
-	virtual void onResume() = 0;
-	virtual void onTerminated() = 0;
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
 	void pause();
 	void resume();
 	void suspend();


Commit: ad440a17232f88cf3623434bab75135dfcb21ac1
    https://github.com/scummvm/scummvm/commit/ad440a17232f88cf3623434bab75135dfcb21ac1
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on actor and control related code

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 5185fda..9f5975f 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -20,12 +20,35 @@
  *
  */
 
+#include "illusions/illusions.h"
 #include "illusions/actor.h"
+#include "illusions/actorresource.h"
+#include "illusions/camera.h"
+#include "illusions/input.h"
 
 namespace Illusions {
 
-Actor::Actor()
-	: _pauseCtr(0) {
+// DefaultSequences
+
+uint32 DefaultSequences::use(uint32 sequenceId) {
+	ItemsIterator it = Common::find_if(_items.begin(), _items.end(), DefaultSequenceEqual(sequenceId));
+	return it != _items.end() ? (*it)._newSequenceId : sequenceId;
+}
+
+void DefaultSequences::set(uint32 sequenceId, uint32 newSequenceId) {
+	ItemsIterator it = Common::find_if(_items.begin(), _items.end(), DefaultSequenceEqual(sequenceId));
+	if (it == _items.end())
+		_items.push_back(DefaultSequence(sequenceId, newSequenceId));
+	else if (sequenceId == newSequenceId)
+		_items.remove_at(it - _items.begin());
+	else
+		(*it)._newSequenceId = newSequenceId;
+}
+
+// Actor
+
+Actor::Actor(IllusionsEngine *vm)
+	: _vm(vm), _pauseCtr(0) {
 	
 }
 
@@ -37,4 +60,281 @@ void Actor::unpause() {
 	--_pauseCtr;
 }
 
+void Actor::createSurface(SurfInfo &surfInfo) {
+	_surface = _vm->allocSurface(surfInfo);
+	if (_frameIndex) {
+		if (_surfaceTextFlag) {
+			/* TODO
+			Font *font = _vm->findFont(_fontId);
+			_surface->fillRect(Common::Rect(surfInfo._dimensions._width, surfInfo._dimensions._height), 0);
+			gfx_sub_40CA70(_surface, font, _field18C, _surfInfo._dimensions, _field198);
+			*/
+			_flags |= 0x4000;
+		}
+		else {
+			_flags |= 0x2000;
+			_flags |= 0x4000;
+		}
+	}
+}
+
+void Actor::destroySurface() {
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+		_surface = 0;
+	}
+}
+
+// Control
+
+Control::Control(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+Control::~Control() {
+}
+
+void Control::pause() {
+
+	// TODO scrmgrSetObjectArtThread(control->objectId, 0);
+
+	/* TODO
+	if (_objectId == 0x40004)
+		_cursor.control = 0;
+	*/
+
+	if (_actor && !(_actor->_flags & 0x0200))
+		_actor->destroySurface();
+
+}
+
+void Control::unpause() {
+
+	// TODO scrmgrSetObjectArtThread(control->objectId, control);
+
+	/* TODO
+	if (_objectId == 0x40004)
+		_cursor.control = this;
+	*/
+  
+	if (_actor && !(_actor->_flags & 0x0200)) {
+		SurfInfo surfInfo;
+		ActorType *actorType = _vm->findActorType(_actorTypeId);
+		if (actorType)
+			surfInfo = actorType->_surfInfo;
+		else
+			surfInfo = _actor->_surfInfo;
+		_actor->createSurface(surfInfo);
+	}
+
+}
+
+void Control::appearActor() {
+	if (_objectId == 0x40004) {
+		// TODO ++cursor._visibleCtr;
+		// TODO if (cursor._visibleCtr > 0) 
+		{
+			_flags |= 1;
+			_actor->_flags |= 1;
+			if (_actor->_frameIndex) {
+				_actor->_flags |= 0x2000;
+				_actor->_flags |= 0x4000;
+			}
+			_vm->_input->discardButtons(0xFFFF);
+		}
+	} else {
+		if (_actor->_frameIndex || _actorTypeId == 0x50004)
+			_actor->_flags |= 1;
+		else
+			_actor->_flags |= 0x1000;
+		for (uint i = 0; i < kSubObjectsCount; ++i)
+			if (_actor->_subobjects[i]) {
+				Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+				subControl->appearActor();
+			}
+	}
+}
+
+void Control::disappearActor() {
+	if (_objectId == 0x40004) {
+		// TODO --cursor.visibleCtr;
+		// TODO if (cursor.visibleCtr <= 0) 
+		{
+			_flags &= 0xFFFEu;
+			_actor->_flags &= 0xFFFE;
+		}
+	} else {
+		_actor->_flags |= ~1;
+		_actor->_flags |= ~0x1000;
+		for (uint i = 0; i < kSubObjectsCount; ++i)
+			if (_actor->_subobjects[i]) {
+				Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+				subControl->disappearActor();
+			}
+	}
+}
+
+bool Control::isActorVisible() {
+	return (_actor->_flags & 1) != 0;
+}
+
+void Control::activateObject() {
+	_flags |= 1;
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		if (_actor->_subobjects[i]) {
+			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			subControl->activateObject();
+		}
+}
+
+void Control::deactivateObject() {
+	_flags |= ~1;
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		if (_actor->_subobjects[i]) {
+			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			subControl->deactivateObject();
+		}
+}
+
+void Control::setActorPosition(Common::Point position) {
+	_actor->_position = position;
+}
+
+Common::Point Control::getActorPosition() {
+	if (_actor)
+		return _actor->_position;
+	return _position;
+}
+
+void Control::setActorScale(int scale) {
+	_actor->_scale = scale;
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		if (_actor->_subobjects[i]) {
+			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			subControl->activateObject();
+		}
+}
+
+void Control::faceActor(uint facing) {
+	_actor->_facing = facing;
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		if (_actor->_subobjects[i]) {
+			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			subControl->faceActor(facing);
+		}
+}
+
+void Control::linkToObject(uint32 parentObjectId, uint32 linkedObjectValue) {
+	_actor->_parentObjectId = parentObjectId;
+	_actor->_linkIndex = linkedObjectValue;
+}
+
+void Control::unlinkObject() {
+	_actor->_parentObjectId = 0;
+	_actor->_linkIndex = 0;
+}
+
+void Control::clearNotifyThreadId1() {
+	_actor->_notifyThreadId1 = 0;
+}
+
+void Control::clearNotifyThreadId2() {
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		if (_actor->_subobjects[i]) {
+			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			subControl->_actor->_flags &= ~0x80;
+			subControl->_actor->_field30 = 0;
+			subControl->_actor->_notifyThreadId2 = 0;
+		}
+	_actor->_flags &= ~0x80;
+	_actor->_field30 = 0;
+	_actor->_notifyThreadId2 = 0;
+}
+
+void Control::setPriority(int16 priority) {
+	_priority = priority;
+}
+
+int Control::getPriority() {
+	uint32 objectId;
+	int16 positionY, priority, priority1;
+	if (_actor) {
+		if (_actor->_parentObjectId && (_actor->_flags & 0x40)) {
+			uint32 objectId2 = getSubActorParent();
+			Control *control2 = _vm->findControl(objectId2);
+			objectId = control2->_objectId;
+			priority = control2->_priority;
+			positionY = control2->_actor->_position.y;
+			priority1 = _priority;
+		} else {
+			objectId = _objectId;
+			positionY = _actor->_position.y;
+			priority = _priority;
+			priority1 = 50;
+		}
+	} else {
+		positionY = _position.y;
+		objectId = _objectId;
+		priority = _priority;
+		priority1 = 1;
+	}
+
+	priority -= 1;
+	int p = 50 * priority1 / 100;
+	if (p)
+		--p;
+
+	positionY = CLIP<int16>(positionY, -5000, 5000);
+
+	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));
+}
+
+uint32 Control::getSubActorParent() {
+	uint32 parentObjectId = _objectId;
+	while (1) {
+		Actor *actor = _vm->findControl(parentObjectId)->_actor;
+		if (actor->_parentObjectId && (actor->_flags & 0x40))
+			parentObjectId = actor->_parentObjectId;
+		else
+			break;
+	}
+	return parentObjectId;
+}
+
+void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {
+
+	if (_actor && _actor->_frameIndex) {
+		collisionRect = Common::Rect(-_position.x, -_position.y,
+			-_position.x + _actor->_surfInfo._dimensions._width - 1,
+			-_position.y + _actor->_surfInfo._dimensions._height - 1);
+	} else {
+		collisionRect = Common::Rect(_unkPt.x, _unkPt.y, _pt.x, _pt.y);
+	}
+
+	if (_actor) {
+		if (_actor->_scale != 100 ) {
+			// scaledValue = value * scale div 100
+			collisionRect.left = collisionRect.left * _actor->_scale / 100;
+			collisionRect.top = collisionRect.top * _actor->_scale / 100;
+			collisionRect.right = collisionRect.right * _actor->_scale / 100;
+			collisionRect.bottom = collisionRect.bottom * _actor->_scale / 100;
+		}
+		collisionRect.translate(_actor->_position.x, _actor->_position.y);
+	}
+
+	if (_flags & 8) {
+		Common::Point screenOffs = _vm->_camera->getScreenOffset();
+		collisionRect.translate(screenOffs.x, screenOffs.y);
+	}
+
+}
+
+void Control::setActorUsePan(int usePan) {
+	if (usePan == 1)
+		_flags &= ~8;
+	else
+		_flags |= 8;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 2817b1d..43e7ef4 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -23,15 +23,120 @@
 #ifndef ILLUSIONS_ACTOR_H
 #define ILLUSIONS_ACTOR_H
 
+#include "illusions/graphics.h"
+#include "common/algorithm.h"
+#include "graphics/surface.h"
+
 namespace Illusions {
 
+class IllusionsEngine;
+
+const uint kSubObjectsCount = 15;
+
+struct DefaultSequence {
+	uint32 _sequenceId;
+	uint32 _newSequenceId;
+	DefaultSequence()
+		: _sequenceId(0), _newSequenceId(0) {}
+	DefaultSequence(uint32 sequenceId, uint32 newSequenceId)
+		: _sequenceId(sequenceId), _newSequenceId(newSequenceId) {}
+};
+
+class DefaultSequences {
+public:
+	uint32 use(uint32 sequenceId);
+	void set(uint32 sequenceId, uint32 newSequenceId);
+protected:
+	typedef Common::Array<DefaultSequence> Items;
+	typedef Items::iterator ItemsIterator;
+	struct DefaultSequenceEqual : public Common::UnaryFunction<const DefaultSequence&, bool> {
+		uint32 _sequenceId;
+		DefaultSequenceEqual(uint32 sequenceId) : _sequenceId(sequenceId) {}
+		bool operator()(const DefaultSequence &defaultSequence) const {
+			return defaultSequence._sequenceId == _sequenceId;
+		}
+	};
+	Common::Array<DefaultSequence> _items;
+};
+
 class Actor {
 public:
-	Actor();
+	Actor(IllusionsEngine *vm);
 	void pause();
 	void unpause();
-protected:
+	void createSurface(SurfInfo &surfInfo);
+	void destroySurface();
+public:
+	IllusionsEngine *_vm;
+	
+	int _pauseCtr;
+	uint _flags;
+
+	int _scale;
+	int16 _frameIndex;
+	int16 _newFrameIndex;
+	SurfInfo _surfInfo;
+	Graphics::Surface *_surface;
+	
+	Common::Point _position;
+	uint _facing;
+	
+	DefaultSequences _defaultSequences;
+
+	uint32 _parentObjectId;
+	int _linkIndex;
+	int _linkIndex2;
+	uint32 _subobjects[kSubObjectsCount];
+	
+	uint32 _notifyThreadId1;
+
+	uint32 _notifyThreadId2;
+	int _field30;
+	
+	int _surfaceTextFlag;
+
+};
+
+class Control {
+public:
+	Control(IllusionsEngine *vm);
+	~Control();
+	void pause();
+	void unpause();
+	void appearActor();
+	void disappearActor();
+	bool isActorVisible();
+	void activateObject();
+	void deactivateObject();
+	void setActorPosition(Common::Point position);
+	Common::Point getActorPosition();
+	void setActorScale(int scale);
+	void faceActor(uint facing);
+	void linkToObject(uint32 parentObjectId, uint32 linkedObjectValue);
+	void unlinkObject();
+	void clearNotifyThreadId1();
+	void clearNotifyThreadId2();
+	void setPriority(int16 priority);
+	int getPriority();
+	uint32 getSubActorParent();
+	void getCollisionRectAccurate(Common::Rect &collisionRect);
+	void setActorUsePan(int usePan);
+public:
+	IllusionsEngine *_vm;
+	uint _flags;
 	int _pauseCtr;
+	int16 _priority;
+	Actor *_actor;
+	//field_6 dw
+	uint32 _tag;
+	uint32 _objectId;
+	uint32 _actorTypeId;
+	// TODO Move points into own struct
+	Common::Point _unkPt;
+	Common::Point _pt;
+	Common::Point _feetPt;
+	Common::Point _position;
+	// TODO 0000001C - 00000054 unknown
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index a3ed76e..f0bbc09 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -85,9 +85,9 @@ Common::Error IllusionsEngine::run() {
 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
-	
-	_scriptMan = new ScriptMan(this);
 
+	_input = new Input();	
+	_scriptMan = new ScriptMan(this);
 	_actorItems = new ActorItems(this);
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
@@ -130,6 +130,7 @@ Common::Error IllusionsEngine::run() {
 	delete _backgroundItems;
 	delete _actorItems;
 	delete _scriptMan;
+	delete _input;
 	delete _resSys;
 	
 	return Common::kNoError;
@@ -205,4 +206,14 @@ Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
 	return 0;
 }
 
+Control *IllusionsEngine::findControl(uint32 objectId) {
+	// TODO Dummy, to be replaced later
+	return 0;
+}
+
+ActorType *IllusionsEngine::findActorType(uint32 actorTypeId) {
+	// TODO Dummy, to be replaced later
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index bfb7335..4b5a168 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -50,10 +50,13 @@ struct SurfInfo;
 
 class ActorItem;
 class ActorItems;
+class ActorType;
 class BackgroundItem;
 class BackgroundItems;
 class BackgroundResource;
 class Camera;
+class Control;
+class Input;
 class ScriptResource;
 class ScriptMan;
 
@@ -75,6 +78,7 @@ public:
 	
 	void updateEvents();
 
+	Input *_input;
 	ScriptMan *_scriptMan;
 	ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
@@ -88,6 +92,8 @@ public:
 	Graphics::Surface *getBackSurface();
 	
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
+	Control *findControl(uint32 objectId);
+	ActorType *findActorType(uint32 actorTypeId);
 
 #if 0
 


Commit: 9a63f0bd17b5c8fab468a61c09cdc29cedf287a5
    https://github.com/scummvm/scummvm/commit/9a63f0bd17b5c8fab468a61c09cdc29cedf287a5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add background scale and priority classes

Changed paths:
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp


diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 3ac2a58..89f9165 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -95,6 +95,47 @@ void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
 }
 
+// PriorityLayer
+
+void PriorityLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_width = stream.readUint16LE();
+	_height = stream.readUint16LE();
+	uint32 mapOffs = stream.readUint32LE();
+	uint32 valuesOffs = stream.readUint32LE();
+	_map = dataStart + mapOffs;
+	_mapWidth = READ_LE_UINT16(_map + 0);
+	_mapHeight = READ_LE_UINT16(_map + 2);
+	_map += 8;
+	_values = dataStart + valuesOffs;
+	
+	debug("PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
+		_width, _height, mapOffs, valuesOffs, _mapWidth, _mapHeight);
+}
+
+int PriorityLayer::getPriority(Common::Point pos) {
+	pos.x = CLIP<int16>(pos.x, 0, _width - 1);
+	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
+	const int16 tx = pos.x / 32, sx = pos.x % 32;
+	const int16 ty = pos.y / 8, sy = pos.y % 8;
+	uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
+	return _values[mapIndex * 32 * 8 + sx + sy * 32];
+}
+
+void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_height = stream.readUint16LE();
+	stream.skip(2);
+	uint32 valuesOffs = stream.readUint32LE();
+	_values = dataStart + valuesOffs;
+	
+	debug("ScaleLayer::load() _height: %d; valuesOffs: %08X",
+		_height, valuesOffs);
+}
+
+int ScaleLayer::getScale(Common::Point pos) {
+	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
+	return _values[pos.y];
+}
+
 // BackgroundResource
 
 BackgroundResource::BackgroundResource() {
@@ -119,6 +160,18 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 		_bgInfos[i].load(data, stream);
 	}
 
+	// Load scale layers
+	stream.seek(0x10);
+	_scaleLayersCount = stream.readUint16LE();
+	_scaleLayers = new ScaleLayer[_scaleLayersCount];
+	stream.seek(0x2C);
+	uint32 scaleLayersOffs = stream.readUint32LE();
+	debug("_scaleLayersCount: %d", _scaleLayersCount);
+	for (uint i = 0; i < _scaleLayersCount; ++i) {
+		stream.seek(scaleLayersOffs + i * 8);
+		_scaleLayers[i].load(data, stream);
+	}
+
 }
 
 int BackgroundResource::findMasterBgIndex() {
@@ -183,7 +236,7 @@ void BackgroundItem::pause() {
 	// TODO
 	++_pauseCtr;
 	if (_pauseCtr <= 1) {
-    	/* TODO
+		/* TODO
 		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
 			krndictRemoveID(_bgRes->_item48s[i].id);
 		*/
@@ -201,7 +254,7 @@ void BackgroundItem::unpause() {
 	// TODO
 	--_pauseCtr;
 	if (_pauseCtr <= 0) {
-    	/* TODO
+		/* TODO
 		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
 			krndictAddID(_bgRes->_item48s[i].id, _bgRes->_item48s[i]);
 		*/
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index a12804b..fd80253 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -69,6 +69,37 @@ struct BgInfo {
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 };
 
+class PriorityLayer {
+public:
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	int getPriority(Common::Point pos);
+protected:
+	int16 _width, _height;
+	int16 _mapWidth, _mapHeight;
+	byte *_map, *_values;
+};
+
+class ScaleLayer {
+public:
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	int getScale(Common::Point pos);
+protected:
+	int16 _height;
+	byte *_values;
+};
+
+#if 0
+BgResource_PathWalkRects struc ; (sizeof=0x8)
+count           dd ?
+rects           dd ?
+BgResource_PathWalkRects ends
+
+BgResource_PathWalkPoints struc ; (sizeof=0x8)
+count           dd ?
+points          dd ?
+BgResource_PathWalkPoints ends
+#endif
+
 class BackgroundResource {
 public:
 	BackgroundResource();
@@ -79,6 +110,12 @@ public:
 
 	uint _bgInfosCount;
 	BgInfo *_bgInfos;
+	
+	uint _priorityLayersCount;
+	PriorityLayer *_priorityLayers;
+
+	uint _scaleLayersCount;
+	ScaleLayer *_scaleLayers;
 
 };
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index f0bbc09..2b44141 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -97,16 +97,18 @@ Common::Error IllusionsEngine::run() {
 	_resSys->loadResource(0x00100006, 0, 0);
 #endif
 
-#if 0
+#if 1
 	// BackgroundResource test
-	_resSys->loadResource(0x0011000B, 0, 0);
+	_resSys->loadResource(0x0011001C, 0, 0);
 	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
-	_system->copyRectToScreen(backgroundItem->_surfaces[0]->getPixels(), backgroundItem->_surfaces[0]->pitch,
-		0, 0, 640, 480);
+	_system->copyRectToScreen((byte*)backgroundItem->_surfaces[0]->getBasePtr(0, 0), backgroundItem->_surfaces[0]->pitch, 0, 0, 640, 480);
 	_system->updateScreen();
+	while (!shouldQuit()) {
+		updateEvents();
+	}
 #endif
 	
-#if 1
+#if 0
 	// ScriptResource test
 	_resSys->loadResource(0x000D0001, 0, 0);
 	
@@ -120,12 +122,6 @@ Common::Error IllusionsEngine::run() {
 	
 #endif
 
-#if 0
-	while (!shouldQuit()) {
-		updateEvents();
-	}
-#endif
-	
 	delete _camera;
 	delete _backgroundItems;
 	delete _actorItems;


Commit: 9385238a59dc5e1790a21a8f9eaf761fc93a660e
    https://github.com/scummvm/scummvm/commit/9385238a59dc5e1790a21a8f9eaf761fc93a660e
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on actor related code

- Add isTimerExpired function

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/camera.cpp
    engines/illusions/fixedpoint.cpp
    engines/illusions/illusions.cpp
    engines/illusions/time.cpp
    engines/illusions/time.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 9f5975f..ee4f677 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -22,7 +22,6 @@
 
 #include "illusions/illusions.h"
 #include "illusions/actor.h"
-#include "illusions/actorresource.h"
 #include "illusions/camera.h"
 #include "illusions/input.h"
 
@@ -337,4 +336,16 @@ void Control::setActorUsePan(int usePan) {
 		_flags |= 8;
 }
 
+void Control::setActorFrameIndex(int16 frameIndex) {
+	if (frameIndex) {
+		_actor->_frameIndex = frameIndex;
+		const Frame &frame = _actor->_frames[frameIndex - 1];
+		_actor->_surfInfo = frame._surfInfo;
+		// TODO memcpy(&control->unkPt, (const void *)frame->config, 0x4Cu);
+		_actor->_flags |= 0x2000;
+		_actor->_flags |= 0x4000;
+		_actor->_newFrameIndex = 0;
+	}
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 43e7ef4..3dc87e2 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -23,6 +23,7 @@
 #ifndef ILLUSIONS_ACTOR_H
 #define ILLUSIONS_ACTOR_H
 
+#include "illusions/actorresource.h"
 #include "illusions/graphics.h"
 #include "common/algorithm.h"
 #include "graphics/surface.h"
@@ -77,6 +78,7 @@ public:
 	int16 _newFrameIndex;
 	SurfInfo _surfInfo;
 	Graphics::Surface *_surface;
+	Frame *_frames;
 	
 	Common::Point _position;
 	uint _facing;
@@ -121,6 +123,7 @@ public:
 	uint32 getSubActorParent();
 	void getCollisionRectAccurate(Common::Rect &collisionRect);
 	void setActorUsePan(int usePan);
+	void setActorFrameIndex(int16 frameIndex);
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index 59123bb..e200fae 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -77,13 +77,12 @@ bool ActorResourceLoader::isFlag(int flag) {
 void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	stream.readUint32LE(); //field_0 dd
 	stream.readUint32LE(); // TODO config dd
-	_pixelSize = stream.readUint32LE();
-	_dimensions.load(stream);
+	_surfInfo.load(stream);
 	uint32 compressedPixelsOffs = stream.readUint32LE();
 	_compressedPixels = dataStart + compressedPixelsOffs;
 	
-	debug("Frame::load() _pixelSize: %d; compressedPixelsOffs: %08X",
-		_pixelSize, compressedPixelsOffs);
+	debug("Frame::load() compressedPixelsOffs: %08X",
+		compressedPixelsOffs);
 }
 
 void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index 196d72d..78ce89a 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -46,8 +46,7 @@ protected:
 struct Frame {
 	//field_0 dd
 	// TODO config dd
-	uint32 _pixelSize;
-	WidthHeight _dimensions;
+	SurfInfo _surfInfo;
 	byte *_compressedPixels;
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 };
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 89f9165..aef2a4c 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -46,8 +46,8 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	// TODO Insert IDs from item48s
 
 	// TODO camera_fadeClear();
-	// TODO bgInfo = &bgResourceb->bgInfos[(unsigned __int16)BgResource_findMasterBgIndex(bgResourceb)];
-	// TODO camera_set(bgInfo[-1].panPoint, bgInfo[-1].surfInfo.dimensions);
+	int index = backgroundItem->_bgRes->findMasterBgIndex();
+	_vm->_camera->set(backgroundItem->_bgRes->_bgInfos[index - 1]._panPoint, backgroundItem->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
 	
 	// NOTE Skipped palette loading (not used in BBDOU)
 }
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index d81c746..fceb6b6 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -121,7 +121,7 @@ void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
 		_activeState._panNotifyId = 0;
 	}
 
-	_activeState._panTargetPoint = Camera::getPtOffset(pt);
+	_activeState._panTargetPoint = getPtOffset(pt);
 	clipPanTargetPoint();
 	
 	if (panSpeed) {
@@ -136,7 +136,7 @@ void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
 		recalcPan(_activeState._panStartTime);
 	} else {
 		_activeState._currPan = _activeState._panTargetPoint;
-		// TODO stopPan();
+		stopPan();
 		if (panNotifyId) {
 			// TODO scrmgrNotifyID(panNotifyId);
 		}
@@ -193,15 +193,15 @@ void Camera::pushCameraMode() {
 	item._panTargetPoint.x = 0;
 	item._panTargetPoint.y = 0;
 	switch (_activeState._cameraMode) {
-	case 4:
-		item._cameraMode = 3;
-		item._panObjectId = _activeState._panObjectId;
-		break;
 	case 1:
 	case 2:
 	case 3:
 		item._panObjectId = _activeState._panObjectId;
 		break;
+	case 4:
+		item._cameraMode = 3;
+		item._panObjectId = _activeState._panObjectId;
+		break;
 	case 5:
 		item._panTargetPoint = _activeState._panTargetPoint;
 		item._panNotifyId = _activeState._panNotifyId;
@@ -248,6 +248,8 @@ void Camera::update(uint32 currTime) {
 	if (_activeState._paused)
 		return;
 
+	//debug("_activeState._cameraMode = %d", _activeState._cameraMode);
+
 	switch (_activeState._cameraMode) {
 	case 1:
 		updateMode1(currTime);
@@ -402,14 +404,23 @@ void Camera::updateMode3(uint32 currTime) {
 
 bool Camera::updatePan(uint32 currTime) {
 	if (currTime - _activeState._time28 >= _activeState._time2E) {
+debug("#1 updatePan");
 		_activeState._panXShl = _activeState._panTargetPoint.x << 16;
 		_activeState._panYShl = _activeState._panTargetPoint.y << 16;
 	} else {
+debug("#2 updatePan");
+debug("_activeState._someX = %d", _activeState._someX);
+debug("1) _activeState._panXShl = %08X", _activeState._panXShl);
+debug("currTime - _activeState._panStartTime = %d", currTime - _activeState._panStartTime);
 		_activeState._panXShl += fixedMul(_activeState._someX, (currTime - _activeState._panStartTime) << 16);
 		_activeState._panYShl += fixedMul(_activeState._someY, (currTime - _activeState._panStartTime) << 16);
+debug("2) _activeState._panXShl = %08X", _activeState._panXShl);
 	}
 	_activeState._panStartTime = currTime;
 	Common::Point newPan(_activeState._panXShl >> 16, _activeState._panYShl >> 16);
+	
+	debug("newPan = %d, %d", newPan.x, newPan.y);
+	
 	if (_activeState._currPan.x != newPan.x || _activeState._currPan.y != newPan.y) {
 		_activeState._currPan = newPan;
 		return true;
@@ -439,13 +450,24 @@ void Camera::recalcPan(uint32 currTime) {
 		FP16 x2 = _activeState._panTargetPoint.x << 16;
 		FP16 y2 = _activeState._panTargetPoint.y << 16;
 		FP16 distance = fixedDistance(x1, y1, x2, y2);
+		
+		debug("(%08X, %08X), (%08X, %08X) %08X", x1, y1, x2, y2, distance);
+		
 		_activeState._time2E = 60 * fixedTrunc(distance) / _activeState._panSpeed;
+		
+		debug("_activeState._time2E = %d", _activeState._time2E);
+		
 	}
 
 	if (_activeState._time2E != 0) {
+debug("#1 recalcPan");
+debug("_activeState._panTargetPoint.x = %d; _activeState._currPan2.x = %d", _activeState._panTargetPoint.x, _activeState._currPan2.x);
+debug("_activeState._panTargetPoint.x - _activeState._currPan2.x = %d", _activeState._panTargetPoint.x - _activeState._currPan2.x);
+debug("_activeState._time2E = %d", _activeState._time2E);
 		_activeState._someX = fixedDiv((_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16, _activeState._time2E << 16);
 		_activeState._someY = fixedDiv((_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16, _activeState._time2E << 16);
 	} else {
+debug("#2 recalcPan");	
 		_activeState._someX = (_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16;
 		_activeState._someY = (_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16;
 	}
@@ -472,10 +494,19 @@ bool Camera::calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags) {
 }
 
 void Camera::clipPanTargetPoint() {
+
+	debug("clip in (%d, %d)", _activeState._panTargetPoint.x, _activeState._panTargetPoint.y);
+
 	_activeState._panTargetPoint.x = CLIP(_activeState._panTargetPoint.x,
 		_activeState._bounds._topLeft.x, _activeState._bounds._bottomRight.x);
 	_activeState._panTargetPoint.y = CLIP(_activeState._panTargetPoint.y,
 		_activeState._bounds._topLeft.y, _activeState._bounds._bottomRight.y);
+		
+	debug("clip rect (%d, %d, %d, %d)", _activeState._bounds._topLeft.x, _activeState._bounds._topLeft.y,
+		_activeState._bounds._bottomRight.x, _activeState._bounds._bottomRight.y);		
+
+	debug("clip out (%d, %d)", _activeState._panTargetPoint.x, _activeState._panTargetPoint.y);
+
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/fixedpoint.cpp b/engines/illusions/fixedpoint.cpp
index ee3818d..7fc15e4 100644
--- a/engines/illusions/fixedpoint.cpp
+++ b/engines/illusions/fixedpoint.cpp
@@ -34,13 +34,11 @@ float fixedToFloat(FP16 value) {
 }
 
 FP16 fixedMul(FP16 a, FP16 b) {
-	// CHECKME Not sure if this correct
-	return (a * b) >> 16;
+	return ((float)a * b) / 65536.0;
 }
 
 FP16 fixedDiv(FP16 a, FP16 b) {
-	// CHECKME Not sure if this correct
-	return (a << 16) / b;
+	return ((float)a / b) * 65536.0;
 }
 
 int fixedTrunc(FP16 value) {
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 2b44141..3e1758d 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -34,6 +34,7 @@
 #include "illusions/thread.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
+#include "illusions/time.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -99,11 +100,20 @@ Common::Error IllusionsEngine::run() {
 
 #if 1
 	// BackgroundResource test
-	_resSys->loadResource(0x0011001C, 0, 0);
+	_resSys->loadResource(0x00110007, 0, 0);
 	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
 	_system->copyRectToScreen((byte*)backgroundItem->_surfaces[0]->getBasePtr(0, 0), backgroundItem->_surfaces[0]->pitch, 0, 0, 640, 480);
 	_system->updateScreen();
+	_camera->panToPoint(Common::Point(800, 0), 500, 0);
 	while (!shouldQuit()) {
+		//debug("panPoints[0] = %d, %d", backgroundItem->_panPoints[0].x, backgroundItem->_panPoints[0].y);
+		uint32 t = getCurrentTime();
+		//debug("t = %d", t);
+		_camera->update(t);
+		_system->delayMillis(10);
+		_system->copyRectToScreen((byte*)backgroundItem->_surfaces[0]->getBasePtr(backgroundItem->_panPoints[0].x, 0),
+			backgroundItem->_surfaces[0]->pitch, 0, 0, 640, 480);
+		_system->updateScreen();
 		updateEvents();
 	}
 #endif
diff --git a/engines/illusions/time.cpp b/engines/illusions/time.cpp
index 7873db6..f190035 100644
--- a/engines/illusions/time.cpp
+++ b/engines/illusions/time.cpp
@@ -26,8 +26,14 @@
 namespace Illusions {
 
 uint32 getCurrentTime() {
-	// TODO, move to own file with other time related code
-	return g_system->getMillis();
+	return g_system->getMillis() / 60;
+}
+
+bool isTimerExpired(uint32 startTime, uint32 endTime) {
+	uint32 currTime = getCurrentTime();
+	return !(
+		(startTime > endTime && (currTime <= endTime || currTime >= startTime)) ||
+		(startTime < endTime && currTime <= endTime && currTime >= startTime));
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/time.h b/engines/illusions/time.h
index 3083af2..c09b436 100644
--- a/engines/illusions/time.h
+++ b/engines/illusions/time.h
@@ -28,6 +28,7 @@
 namespace Illusions {
 
 uint32 getCurrentTime();
+bool isTimerExpired(uint32 startTime, uint32 endTime);
 
 } // End of namespace Illusions
 


Commit: f98c0defe561889fa40694484ee251174c59f5a4
    https://github.com/scummvm/scummvm/commit/f98c0defe561889fa40694484ee251174c59f5a4
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add Screen class and ajust code to use it

Changed paths:
  A engines/illusions/screen.cpp
  A engines/illusions/screen.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/spritedrawqueue.cpp
    engines/illusions/spritedrawqueue.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index ee4f677..d7d7fb5 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -24,6 +24,7 @@
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/input.h"
+#include "illusions/screen.h"
 
 namespace Illusions {
 
@@ -60,7 +61,7 @@ void Actor::unpause() {
 }
 
 void Actor::createSurface(SurfInfo &surfInfo) {
-	_surface = _vm->allocSurface(surfInfo);
+	_surface = _vm->_screen->allocSurface(surfInfo);
 	if (_frameIndex) {
 		if (_surfaceTextFlag) {
 			/* TODO
@@ -348,4 +349,7 @@ void Control::setActorFrameIndex(int16 frameIndex) {
 	}
 }
 
+// Controls
+
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 3dc87e2..6216b31 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -26,6 +26,7 @@
 #include "illusions/actorresource.h"
 #include "illusions/graphics.h"
 #include "common/algorithm.h"
+#include "common/list.h"
 #include "graphics/surface.h"
 
 namespace Illusions {
@@ -142,6 +143,14 @@ public:
 	// TODO 0000001C - 00000054 unknown
 };
 
+class Controls {
+public:
+public:
+	typedef Common::List<Control*> Items;
+	typedef Items::iterator ItemsIterator;
+	Items _controls;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_ACTOR_H
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index aef2a4c..66f987a 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/backgroundresource.h"
 #include "illusions/camera.h"
+#include "illusions/screen.h"
 #include "common/str.h"
 
 namespace Illusions {
@@ -192,7 +193,7 @@ void BackgroundItem::initSurface() {
 	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
 		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
 		_panPoints[i] = bgInfo->_panPoint;
-		_surfaces[i] = _vm->allocSurface(bgInfo->_surfInfo);
+		_surfaces[i] = _vm->_screen->allocSurface(bgInfo->_surfInfo);
 		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
 	}
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 3e1758d..5a0c2ea 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -27,11 +27,10 @@
 #include "illusions/graphics.h"
 #include "illusions/input.h"
 #include "illusions/updatefunctions.h"
-#include "illusions/spritedrawqueue.h"
-#include "illusions/spritedecompressqueue.h"
 #include "illusions/actor.h"
 #include "illusions/actorresource.h"
 #include "illusions/thread.h"
+#include "illusions/screen.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
 #include "illusions/time.h"
@@ -87,6 +86,7 @@ Common::Error IllusionsEngine::run() {
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 
+    _screen = new Screen(this);
 	_input = new Input();	
 	_scriptMan = new ScriptMan(this);
 	_actorItems = new ActorItems(this);
@@ -137,6 +137,7 @@ Common::Error IllusionsEngine::run() {
 	delete _actorItems;
 	delete _scriptMan;
 	delete _input;
+	delete _screen;
 	delete _resSys;
 	
 	return Common::kNoError;
@@ -180,33 +181,6 @@ void IllusionsEngine::updateEvents() {
 	}
 }
 
-Graphics::Surface *IllusionsEngine::allocSurface(int16 width, int16 height) {
-	// TODO Use screen pixel format?
-	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
-	Graphics::Surface *surface = new Graphics::Surface();
-	surface->create(width, height, pixelFormat16);
-	return surface; 
-}
-
-Graphics::Surface *IllusionsEngine::allocSurface(SurfInfo &surfInfo) {
-	return allocSurface(surfInfo._dimensions._width, surfInfo._dimensions._height);
-}
-
-bool IllusionsEngine::isDisplayOn() {
-	// TODO Move this outside into a screen class
-	return true;
-}
-
-uint16 IllusionsEngine::getColorKey2() {
-	// TODO Move this outside into a screen class
-	return 0;
-}
-
-Graphics::Surface *IllusionsEngine::getBackSurface() {
-	// TODO Move this outside into a screen class
-	return 0;
-}
-
 Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
 	// TODO Dummy, to be replaced later
 	return 0;
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 4b5a168..efae321 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -57,6 +57,7 @@ class BackgroundResource;
 class Camera;
 class Control;
 class Input;
+class Screen;
 class ScriptResource;
 class ScriptMan;
 
@@ -78,19 +79,13 @@ public:
 	
 	void updateEvents();
 
+	Screen *_screen;
 	Input *_input;
 	ScriptMan *_scriptMan;
 	ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
 
-	// Screen functions	
-	Graphics::Surface *allocSurface(int16 width, int16 height);
-	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
-	bool isDisplayOn();
-	uint16 getColorKey2();
-	Graphics::Surface *getBackSurface();
-	
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 	Control *findControl(uint32 objectId);
 	ActorType *findActorType(uint32 actorTypeId);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 8dd462c..e40a676 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	illusions.o \
 	input.o \
 	resourcesystem.o \
+	screen.o \
 	scriptman.o \
 	scriptopcodes.o \
 	scriptresource.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
new file mode 100644
index 0000000..1b33b0c
--- /dev/null
+++ b/engines/illusions/screen.cpp
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/screen.h"
+#include "illusions/graphics.h"
+#include "illusions/spritedrawqueue.h"
+#include "illusions/spritedecompressqueue.h"
+
+namespace Illusions {
+
+// Screen
+
+Screen::Screen(IllusionsEngine *vm)
+	: _vm(vm) {
+	_displayOn = true;
+	_backSurface = allocSurface(640, 480);
+	_decompressQueue = new SpriteDecompressQueue();
+	_drawQueue = new SpriteDrawQueue(this);
+}
+
+Screen::~Screen() {
+	delete _drawQueue;
+	delete _decompressQueue;
+	_backSurface->free();
+	delete _backSurface;
+}
+
+Graphics::Surface *Screen::allocSurface(int16 width, int16 height) {
+	// TODO Use screen pixel format?
+	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->create(width, height, pixelFormat16);
+	return surface; 
+}
+
+Graphics::Surface *Screen::allocSurface(SurfInfo &surfInfo) {
+	return allocSurface(surfInfo._dimensions._width, surfInfo._dimensions._height);
+}
+
+bool Screen::isDisplayOn() {
+	return _displayOn;
+}
+
+uint16 Screen::getColorKey2() {
+	return _colorKey2;
+}
+
+Graphics::Surface *Screen::getBackSurface() {
+	// TODO Move this outside into a screen class
+	return 0;
+}
+
+void Screen::updateSprites() {
+	_decompressQueue->decompressAll();
+	// NOTE Skipped doShiftBrightness and related as it seems to be unused
+	_drawQueue->drawAll();
+	if (!_displayOn) // TODO Check if a video is playing then don't do it
+		_backSurface->fillRect(Common::Rect(_backSurface->w, _backSurface->h), 0);
+	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
+}
+
+void Screen::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+	// TODO
+}
+
+void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// TODO
+}
+
+void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+	// TODO
+}
+
+void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// TODO
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
new file mode 100644
index 0000000..5d5ead2
--- /dev/null
+++ b/engines/illusions/screen.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCREEN_H
+#define ILLUSIONS_SCREEN_H
+
+#include "illusions/spritedrawqueue.h"
+#include "illusions/spritedecompressqueue.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class Screen {
+public:
+	Screen(IllusionsEngine *vm);
+	~Screen();
+	Graphics::Surface *allocSurface(int16 width, int16 height);
+	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
+	bool isDisplayOn();
+	uint16 getColorKey2();
+	Graphics::Surface *getBackSurface();
+	void updateSprites();
+	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
+	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
+	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
+	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+public:
+	IllusionsEngine *_vm;
+	bool _displayOn;
+	uint16 _colorKey2;
+	SpriteDecompressQueue *_decompressQueue;
+	SpriteDrawQueue *_drawQueue;
+	Graphics::Surface *_backSurface;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCREEN_H
diff --git a/engines/illusions/spritedrawqueue.cpp b/engines/illusions/spritedrawqueue.cpp
index a21ca15..2cffb0c 100644
--- a/engines/illusions/spritedrawqueue.cpp
+++ b/engines/illusions/spritedrawqueue.cpp
@@ -21,11 +21,12 @@
  */
 
 #include "illusions/spritedrawqueue.h"
+#include "illusions/screen.h"
 
 namespace Illusions {
 
-SpriteDrawQueue::SpriteDrawQueue(IllusionsEngine *vm)
-	: _vm(vm) {
+SpriteDrawQueue::SpriteDrawQueue(Screen *screen)
+	: _screen(screen) {
 }
 
 SpriteDrawQueue::~SpriteDrawQueue() {
@@ -39,7 +40,7 @@ bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 		return false;
 	}
 
-	if (!_vm->isDisplayOn()) {
+	if (!_screen->isDisplayOn()) {
 		if (item->_drawFlags)
 			*item->_drawFlags &= ~4;
 		return true;			
@@ -51,18 +52,16 @@ bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 	if (!calcItemRect(item, srcRect, dstRect))
 		return true;
 
-	_backSurface = _vm->getBackSurface();
-
 	if (item->_scale == 100) {
 		if (item->_flags & 1)
-			drawSurface10(dstRect.left, dstRect.top, item->_surface, srcRect, _vm->getColorKey2());
+			_screen->drawSurface10(dstRect.left, dstRect.top, item->_surface, srcRect, _screen->getColorKey2());
 		else
-			drawSurface11(dstRect.left, dstRect.top, item->_surface, srcRect);
+			_screen->drawSurface11(dstRect.left, dstRect.top, item->_surface, srcRect);
 	} else {
 		if (item->_flags & 1)
-			drawSurface20(dstRect, item->_surface, srcRect, _vm->getColorKey2());
+			_screen->drawSurface20(dstRect, item->_surface, srcRect, _screen->getColorKey2());
 		else
-			drawSurface21(dstRect, item->_surface, srcRect);
+			_screen->drawSurface21(dstRect, item->_surface, srcRect);
 	}
 	
 	if (item->_drawFlags)
@@ -71,6 +70,17 @@ bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 	return true;
 }
 
+void SpriteDrawQueue::drawAll() {
+	SpriteDrawQueueListIterator it = _queue.begin();
+	while (it != _queue.end()) {
+		if (draw(*it)) {
+			delete *it;
+			it = _queue.erase(it);
+		} else
+			++it;
+	}
+}
+
 void SpriteDrawQueue::insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
 	Common::Point &drawPosition, Common::Point &controlPosition, int priority, int16 scale, uint16 flags) {
 	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
@@ -177,20 +187,4 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	return true;
 }
 
-void SpriteDrawQueue::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
-	// TODO
-}
-
-void SpriteDrawQueue::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// TODO
-}
-
-void SpriteDrawQueue::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
-	// TODO
-}
-
-void SpriteDrawQueue::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// TODO
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/spritedrawqueue.h b/engines/illusions/spritedrawqueue.h
index 83dd352..30b999a 100644
--- a/engines/illusions/spritedrawqueue.h
+++ b/engines/illusions/spritedrawqueue.h
@@ -31,6 +31,8 @@
 
 namespace Illusions {
 
+class Screen;
+
 struct SpriteDrawQueueItem {
 	byte *_drawFlags;
 	int16 _kind;
@@ -46,9 +48,10 @@ struct SpriteDrawQueueItem {
 
 class SpriteDrawQueue {
 public:
-	SpriteDrawQueue(IllusionsEngine *vm);
+	SpriteDrawQueue(Screen *screen);
 	~SpriteDrawQueue();
 	bool draw(SpriteDrawQueueItem *item);
+	void drawAll();
 	void insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
 		Common::Point &drawPosition, Common::Point &controlPosition, int priority, int16 scale, uint16 flags);
 	void insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
@@ -67,16 +70,10 @@ protected:
 		}
 	};
 
-	IllusionsEngine *_vm;
-	Graphics::Surface *_backSurface;
+	Screen *_screen;
 	SpriteDrawQueueList _queue;	
 	void insert(SpriteDrawQueueItem *item, int priority);
 	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
-	// TODO Possibly move these into a Screen class
-	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
-	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
-	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
-	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
 };
 
 } // End of namespace Illusions


Commit: f880c606f747ebdd8bdd6027ea26fda7d1eabda3
    https://github.com/scummvm/scummvm/commit/f880c606f747ebdd8bdd6027ea26fda7d1eabda3
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Work on Actor and Control classes

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/camera.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index d7d7fb5..04d7636 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -25,6 +25,7 @@
 #include "illusions/camera.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
+#include "illusions/scriptman.h"
 
 namespace Illusions {
 
@@ -49,7 +50,72 @@ void DefaultSequences::set(uint32 sequenceId, uint32 newSequenceId) {
 
 Actor::Actor(IllusionsEngine *vm)
 	: _vm(vm), _pauseCtr(0) {
-	
+	_pauseCtr = 0;
+	_flags = 0;
+	_scale = 100;
+	_frameIndex = 0;
+	_newFrameIndex = 0;
+	_surfInfo._pixelSize = 0;
+	_surfInfo._dimensions._width = 0;
+	_surfInfo._dimensions._height = 0;
+	_surface = 0;
+	_frames = 0;
+	_scaleLayer = 0;
+	_priorityLayer = 0;
+	_position.x = 0;
+	_position.y = 0;
+	_position2.x = 0;
+	_position2.y = 0;
+	_facing = 64;
+	_fontId = 0;
+	_parentObjectId = 0;
+	_linkIndex = 0;
+	_linkIndex2 = 0;
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		_subobjects[i] = 0;
+	_notifyThreadId1 = 0;
+	_notifyThreadId2 = 0;
+	_surfaceTextFlag = 0;
+	_field30 = 0;
+	_seqCodeIp = 0;
+	_sequenceId = 0;
+	_seqCodeValue1 = 0;
+	_seqCodeValue2 = 600;
+	_seqCodeValue3 = 0;
+
+	_pathCtrY = 0;
+
+#if 0 // TODO
+	_field2 = 0;
+	_spriteFlags = 0;
+	_drawFlags = 0;
+	_controlRoutine = Actor_defaultControlRoutine;
+	_notifyId3C = 0;
+	_path40 = 0;
+	_path4C = 0;
+	_pathFlag50 = 0;
+	_pathCtrX = 0;
+	_pathInitialPosFlag = 1;
+	_pathInitialPos.x = 0;
+	_pathInitialPos.y = 0;
+	_actorIndex = 0;
+	_namedPointsCount = 0;
+	_namedPoints = 0;
+	_field164 = 0;
+	_pathWalkRects = 0;
+	_pathWalkPoints = 0;
+	_pathNode = 0;
+	_pathPoints = 0;
+	_pathPointIndex = 0;
+	_pathPointsCount = 0;
+	_regionLayer = 0;
+	_transitionRegionId = 0;
+	_field18C = 0;
+	_field190 = 0;
+	_field192 = 0;
+	_field198 = 0;
+#endif
+
 }
 
 void Actor::pause() {
@@ -90,6 +156,22 @@ void Actor::destroySurface() {
 
 Control::Control(IllusionsEngine *vm)
 	: _vm(vm) {
+	_flags = 0;
+	_pauseCtr = 0;
+	_priority = 0;
+	_objectId = 0;
+	_unkPt.x = 0;
+	_unkPt.y = 0;
+	_pt.x = 0;
+	_pt.y = 0;
+	_feetPt.x = 0;
+	_feetPt.y = 0;
+	_position.x = 0;
+	_position.y = 0;
+	_actorTypeId = 0;
+	_actor = 0;
+	// TODO _buf = 0;
+	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
 }
 
 Control::~Control() {
@@ -99,10 +181,8 @@ void Control::pause() {
 
 	// TODO scrmgrSetObjectArtThread(control->objectId, 0);
 
-	/* TODO
 	if (_objectId == 0x40004)
-		_cursor.control = 0;
-	*/
+		_vm->setCursorControl(0);
 
 	if (_actor && !(_actor->_flags & 0x0200))
 		_actor->destroySurface();
@@ -113,10 +193,8 @@ void Control::unpause() {
 
 	// TODO scrmgrSetObjectArtThread(control->objectId, control);
 
-	/* TODO
 	if (_objectId == 0x40004)
-		_cursor.control = this;
-	*/
+		_vm->setCursorControl(this);
   
 	if (_actor && !(_actor->_flags & 0x0200)) {
 		SurfInfo surfInfo;
@@ -132,9 +210,7 @@ void Control::unpause() {
 
 void Control::appearActor() {
 	if (_objectId == 0x40004) {
-		// TODO ++cursor._visibleCtr;
-		// TODO if (cursor._visibleCtr > 0) 
-		{
+		if (_vm->showCursor()) {
 			_flags |= 1;
 			_actor->_flags |= 1;
 			if (_actor->_frameIndex) {
@@ -158,11 +234,9 @@ void Control::appearActor() {
 
 void Control::disappearActor() {
 	if (_objectId == 0x40004) {
-		// TODO --cursor.visibleCtr;
-		// TODO if (cursor.visibleCtr <= 0) 
-		{
-			_flags &= 0xFFFEu;
-			_actor->_flags &= 0xFFFE;
+		if (_vm->hideCursor()) {
+			_flags &= ~1;
+			_actor->_flags &= ~1;
 		}
 	} else {
 		_actor->_flags |= ~1;
@@ -340,7 +414,7 @@ void Control::setActorUsePan(int usePan) {
 void Control::setActorFrameIndex(int16 frameIndex) {
 	if (frameIndex) {
 		_actor->_frameIndex = frameIndex;
-		const Frame &frame = _actor->_frames[frameIndex - 1];
+		const Frame &frame = (*_actor->_frames)[frameIndex - 1];
 		_actor->_surfInfo = frame._surfInfo;
 		// TODO memcpy(&control->unkPt, (const void *)frame->config, 0x4Cu);
 		_actor->_flags |= 0x2000;
@@ -349,7 +423,250 @@ void Control::setActorFrameIndex(int16 frameIndex) {
 	}
 }
 
+void Control::stopActor() {
+	_actor->_seqCodeIp = 0;
+	/* TODO
+	if (_actor->_pathNode) {
+		if (_actor->_flags & 0x0400) {
+			// TODO delete _actor->_pathNode;
+			_actor->_flags &= ~0x0400;
+		}
+		_actor->_pathNode = 0;
+		_actor->_pathPoints = 0;
+		_actor->_pathPointsCount = 0;
+		_actor->_pathPointIndex = 0;
+		_actor->_path40 = 0;
+	}
+	*/
+	
+	_vm->notifyThreadId(_actor->_notifyThreadId1);
+	_vm->notifyThreadId(_actor->_notifyId3C);
+	
+}
+
+void Control::startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId) {
+	startSequenceActorIntern(sequenceId, value, 0, notifyThreadId);
+}
+
+void Control::stopSequenceActor() {
+	if (_actor->_flags & 0x40) {
+		stopActor();
+		_actor->_frameIndex = 0;
+		if ((_actor->_flags & 1) || (_actor->_flags & 0x1000)) {
+			_actor->_flags &= ~1;
+			_actor->_flags |= 0x1000;
+		}
+	}
+	for (uint i = 0; i < kSubObjectsCount; ++i)
+		if (_actor->_subobjects[i]) {
+			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			subControl->stopSequenceActor();
+		}
+}
+
+void Control::sequenceActor() {
+	// TODO
+}
+
+void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId) {
+
+	stopActor();
+	
+	_actor->_flags &= ~0x80;
+	_actor->_flags &= ~0x0400;
+	_actor->_flags |= 0x0100;
+
+	sequenceId = _actor->_defaultSequences.use(sequenceId);
+	
+	_actor->_sequenceId = sequenceId;
+	_actor->_notifyThreadId1 = notifyThreadId;
+	_actor->_notifyId3C = 0;
+	_actor->_path40 = 0;
+	
+	Sequence *sequence = _vm->findSequence(sequenceId);
+	
+	_actor->_seqCodeIp = sequence->_sequenceCode;
+	_actor->_frames = _vm->findSequenceFrames(sequence);
+	
+	_actor->_seqCodeValue3 = 0;
+	_actor->_seqCodeValue1 = 0;
+	_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
+	// TODO _actor->initSequenceStack();
+	stopSequenceActor();
+	_actor->_linkIndex2 = 0;
+	if (value2) {
+		_actor->_flags |= 0x80;
+		_actor->_field30 = value2;
+		_actor->_notifyThreadId1 = 0;
+		_actor->_notifyThreadId2 = notifyThreadId;
+	}
+
+	// TODO sequenceActor();
+	
+}
+
 // Controls
 
+Controls::Controls(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId) {
+	Control *control = newControl();
+	Actor *actor = newActor();
+
+	ActorType *actorType = _vm->findActorType(actorTypeId);
+	control->_flags = actorType->_flags;
+	control->_priority = actorType->_priority;
+	control->_objectId = objectId;
+	// TODO memcpy(&control->unkPt, (const void *)actorType->_config, 0x4Cu);
+	control->_actorTypeId = actorTypeId;
+	control->_actor = actor;
+	/* TODO
+	if (actorTypeId == 0x50001 && objectId == 0x40004)
+		actor->setControlRoutine(Cursor_controlRoutine);
+	*/
+	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
+		actor->createSurface(actorType->_surfInfo);
+	} else {
+		actor->_flags |= 0x0200;
+	}
+	actor->_position = placePt;
+	actor->_position2 = placePt;
+	Common::Point currPan = _vm->_camera->getCurrentPan();
+	// TODO if (!artcntrl_calcPointDirection(placePt, panPos, &actor->facing))
+	actor->_facing = 64;
+	actor->_scale = actorType->_scale;
+	// TODO actor->_namedPointsCount = actorType->_namedPointsCount;
+	// TODO actor->_namedPoints = actorType->_namedPoints;
+	
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	if (actorType->_pathWalkPointsIndex) {
+		// TODO actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
+		actor->_flags |= 0x02;
+	}
+
+	if (actorType->_scaleLayerIndex) {
+		actor->_scaleLayer = bgRes->getScaleLayer(actorType->_scaleLayerIndex - 1);
+		actor->_flags |= 0x04;
+	}
+
+	if (actorType->_pathWalkRectIndex) {
+		// TODO actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
+		actor->_flags |= 0x10;
+	}
+	
+	if (actorType->_priorityLayerIndex) {
+		actor->_priorityLayer = bgRes->getPriorityLayer(actorType->_priorityLayerIndex - 1);
+		actor->_flags |= 0x08;
+	}
+	
+	if (actorType->_regionLayerIndex) {
+		// TODO actor->_regionLayer = bgRes->getPriorityLayer(actorType->_regionLayerIndex - 1);
+		actor->_flags |= 0x20;
+	}
+	
+	actor->_pathCtrY = 140;
+	
+	_controls.push_back(control);
+	// TODO scrmgrSetObjectArtThread(objectId, controlb);
+
+	if (actorTypeId == 0x50001 && objectId == 0x40004)
+		_vm->placeCursor(control, sequenceId);
+
+	control->_flags |= 0x01;
+	actor->_flags |= 0x1000;
+
+	control->startSequenceActor(sequenceId, 2, notifyThreadId);
+}
+
+void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority) {
+	Control *control = newControl();
+	Actor *actor = newActor();
+	control->_flags = 0;
+	control->_priority = priority;
+	control->_objectId = objectId;
+	control->_unkPt.x = 0;
+	control->_unkPt.y = 0;
+	control->_pt.y = dimensions._height - 1;
+	control->_pt.x = dimensions._width - 1;
+	control->_feetPt.x = dimensions._width / 2;
+	control->_feetPt.y = dimensions._height / 2;
+	control->_position.x = 0;
+	control->_position.y = 0;
+	control->_actorTypeId = 0x50004;
+	control->_actor = actor;
+	// TODO actor->setControlRoutine(0);
+	actor->_surfInfo._pixelSize = dimensions._width * dimensions._height;
+	actor->_surfInfo._dimensions = dimensions;
+	actor->createSurface(actor->_surfInfo);
+	actor->_position = placePt;
+	actor->_position2 = placePt;
+	actor->_facing = 64;
+	actor->_scale = 100;
+	// TODO actor->_namedPointsCount = 0;
+	// TODO actor->_namedPoints = 0;
+	actor->_pathCtrY = 140;
+
+	_controls.push_back(control);
+	// TODO scrmgrSetObjectArtThread(objectId, controlb);
+	control->appearActor();
+}
+
+void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags) {
+	Control *control = newControl();
+	control->_flags = flags;
+	control->_unkPt = feetPt;
+	control->_feetPt = feetPt;
+	control->_priority = priority;
+	control->_objectId = objectId;
+	control->_pt = pt;
+	control->_position.x = 0;
+	control->_position.y = 0;
+	control->_actorTypeId = 0;
+	control->_actor = 0;
+	_controls.push_back(control);
+	// TODO scrmgrSetObjectArtThread(objectId, controlb);
+}
+
+Actor *Controls::newActor() {
+	return new Actor(_vm);
+}
+
+Control *Controls::newControl() {
+	return new Control(_vm);
+}
+
+void Controls::destroyControl(Control *control) {
+	_controls.remove(control);
+
+	/* TODO
+	if (control->_pauseCtr <= 0)
+		scrmgrSetObjectArtThread(control->objectId, 0);
+	*/
+	
+	if (control->_objectId == 0x40004 && control->_pauseCtr <= 0)
+		_vm->setCursorControl(0);
+	
+	if (control->_actor) {
+		/* TODO
+		if (actor->_pathNode && (actor->_flags & 0x400))
+			delete actor->_pathNode;
+		*/
+		if (!(control->_actor->_flags & 0x200))
+			control->_actor->destroySurface();
+		/* TODO
+		if (control->_actor->_field2)
+			largeObj_sub_4061E0();
+		*/
+		delete control->_actor;
+		control->_actor = 0;
+	}
+	/* TODO
+	if (control->_buf)
+		free(control->_buf);
+	*/
+	delete control;
+}
 
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 6216b31..e70efab 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -24,6 +24,7 @@
 #define ILLUSIONS_ACTOR_H
 
 #include "illusions/actorresource.h"
+#include "illusions/backgroundresource.h"
 #include "illusions/graphics.h"
 #include "common/algorithm.h"
 #include "common/list.h"
@@ -79,11 +80,18 @@ public:
 	int16 _newFrameIndex;
 	SurfInfo _surfInfo;
 	Graphics::Surface *_surface;
-	Frame *_frames;
+	
+	FramesList *_frames;
+	
+	ScaleLayer *_scaleLayer;
+	PriorityLayer *_priorityLayer;
 	
 	Common::Point _position;
+	Common::Point _position2;
 	uint _facing;
 	
+	uint32 _fontId;
+	
 	DefaultSequences _defaultSequences;
 
 	uint32 _parentObjectId;
@@ -92,11 +100,22 @@ public:
 	uint32 _subobjects[kSubObjectsCount];
 	
 	uint32 _notifyThreadId1;
+	uint32 _notifyId3C;
 
 	uint32 _notifyThreadId2;
 	int _field30;
 	
 	int _surfaceTextFlag;
+	
+	byte *_seqCodeIp;
+	uint32 _sequenceId;
+	int _seqCodeValue1;
+	int _seqCodeValue2;
+	int _seqCodeValue3;
+	
+	int _pathCtrY;
+	int _path40;
+	
 
 };
 
@@ -125,6 +144,10 @@ public:
 	void getCollisionRectAccurate(Common::Rect &collisionRect);
 	void setActorUsePan(int usePan);
 	void setActorFrameIndex(int16 frameIndex);
+	void stopActor();
+	void startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId);
+	void stopSequenceActor();
+	void sequenceActor();
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
@@ -141,14 +164,23 @@ public:
 	Common::Point _feetPt;
 	Common::Point _position;
 	// TODO 0000001C - 00000054 unknown
+	void startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId);
 };
 
 class Controls {
 public:
+	Controls(IllusionsEngine *vm);
+	void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
+	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
+	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);	
 public:
 	typedef Common::List<Control*> Items;
 	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
 	Items _controls;
+	Actor *newActor();
+	Control *newControl();
+	void destroyControl(Control *control);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index e200fae..fc4968e 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -109,19 +109,19 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_scale = stream.readByte();
 	_priority = stream.readByte();
 	_value1E = stream.readUint16LE();
-	_bgItem28sIndex = stream.readUint16LE();
-	_bgItem22sIndex = stream.readUint16LE();
-	_bgItem30sIndex = stream.readUint16LE();
-	_bgItem26sIndex = stream.readUint16LE();
-	_bgItem38sIndex = stream.readUint16LE();
+	_pathWalkPointsIndex = stream.readUint16LE();
+	_scaleLayerIndex = stream.readUint16LE();
+	_pathWalkRectIndex = stream.readUint16LE();
+	_priorityLayerIndex = stream.readUint16LE();
+	_regionLayerIndex = stream.readUint16LE();
 	_flags = stream.readUint16LE();
-	
+
 	debug("ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
 		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
-	debug("ActorType::load() _bgItem28sIndex: %d; _bgItem22sIndex: %d; _bgItem30sIndex: %d",
-		_bgItem28sIndex, _bgItem22sIndex, _bgItem30sIndex);
-	debug("ActorType::load() _bgItem26sIndex: %d; _bgItem38sIndex: %d; _flags: %04X",
-		_bgItem26sIndex, _bgItem38sIndex,_flags);
+	debug("ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
+		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
+	debug("ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
+		_priorityLayerIndex, _regionLayerIndex,_flags);
 }
 
 // ActorResource
@@ -178,6 +178,13 @@ void ActorResource::load(byte *data, uint32 dataSize) {
 
 }
 
+bool ActorResource::containsSequence(Sequence *sequence) {
+	for (uint i = 0; i < _sequences.size(); ++i)
+		if (sequence == &_sequences[i])
+			return true;
+	return false;
+}
+
 // ActorItem
 
 ActorItem::ActorItem() {
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index 78ce89a..c62f8a9 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -68,25 +68,29 @@ struct ActorType {
 	byte _scale;
 	byte _priority;
 	int16 _value1E;
-	uint16 _bgItem28sIndex;
-	uint16 _bgItem22sIndex;
-	uint16 _bgItem30sIndex;
-	uint16 _bgItem26sIndex;
-	uint16 _bgItem38sIndex;
+	uint16 _pathWalkPointsIndex;
+	uint16 _scaleLayerIndex;
+	uint16 _pathWalkRectIndex;
+	uint16 _priorityLayerIndex;
+	uint16 _regionLayerIndex;
 	uint16 _flags;
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 };
 
+class FramesList : public Common::Array<Frame> {
+};
+
 class ActorResource {
 public:
 	ActorResource();
 	~ActorResource();
 	void load(byte *data, uint32 dataSize);
+	bool containsSequence(Sequence *sequence);
 public:
 	uint32 _totalSize;
 	Common::Array<ActorType> _actorTypes;
 	Common::Array<Sequence> _sequences;
-	Common::Array<Frame> _frames;
+	FramesList _frames;
 };
 
 class ActorItem {
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 66f987a..bdce1c4 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -182,6 +182,14 @@ int BackgroundResource::findMasterBgIndex() {
 	return index;
 }
 
+PriorityLayer *BackgroundResource::getPriorityLayer(uint index) {
+	return &_priorityLayers[index];
+}
+
+ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
+	return &_scaleLayers[index];
+}
+
 // BackgroundItem
 
 BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index fd80253..de6204e 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -106,6 +106,8 @@ public:
 	~BackgroundResource();
 	void load(byte *data, uint32 dataSize);
 	int findMasterBgIndex();
+	PriorityLayer *getPriorityLayer(uint index);
+	ScaleLayer *getScaleLayer(uint index);
 public:
 
 	uint _bgInfosCount;
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index fceb6b6..0aff84f 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -116,10 +116,7 @@ void Camera::panTrackObject(uint32 objectId) {
 
 void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
 
-	if (_activeState._panNotifyId) {
-		// TODO scrmgrNotifyID(_activeState._panNotifyId);
-		_activeState._panNotifyId = 0;
-	}
+	_vm->notifyThreadId(_activeState._panNotifyId);
 
 	_activeState._panTargetPoint = getPtOffset(pt);
 	clipPanTargetPoint();
@@ -137,9 +134,7 @@ void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
 	} else {
 		_activeState._currPan = _activeState._panTargetPoint;
 		stopPan();
-		if (panNotifyId) {
-			// TODO scrmgrNotifyID(panNotifyId);
-		}
+		_vm->notifyThreadId(_activeState._panNotifyId);
 	}
 }
 
@@ -275,10 +270,7 @@ void Camera::update(uint32 currTime) {
 		if (isPanFinished()) {
 			if (_activeState._cameraMode == 5) {
 				// Notify a thread that the camera panning has finished
-				if (_activeState._panNotifyId) {
-					// TODO scrmgrNotifyID(_activeState._panNotifyId);
-					_activeState._panNotifyId = 0;
-				}
+				_vm->notifyThreadId(_activeState._panNotifyId);
 				_activeState._cameraMode = 6;
 			} else if (_activeState._cameraMode == 4) {
 				_activeState._cameraMode = 3;
@@ -371,8 +363,8 @@ void Camera::updateMode2(uint32 currTime) {
 			recalcPan(currTime);
 		}
 	} else if (_activeState._pointFlags) {
-	    _activeState._pointFlags = 0;
-	    _activeState._panTargetPoint = _activeState._currPan;
+		_activeState._pointFlags = 0;
+		_activeState._panTargetPoint = _activeState._currPan;
   	}
 
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 5a0c2ea..3592ea9 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -86,7 +86,7 @@ Common::Error IllusionsEngine::run() {
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 
-    _screen = new Screen(this);
+	_screen = new Screen(this);
 	_input = new Input();	
 	_scriptMan = new ScriptMan(this);
 	_actorItems = new ActorItems(this);
@@ -196,4 +196,44 @@ ActorType *IllusionsEngine::findActorType(uint32 actorTypeId) {
 	return 0;
 }
 
+Sequence *IllusionsEngine::findSequence(uint32 sequenceId) {
+	// TODO Dummy, to be replaced later
+	return 0;
+}
+
+void IllusionsEngine::notifyThreadId(uint32 &threadId) {
+	if (threadId) {
+		uint32 tempThreadId = threadId;
+		threadId = 0;
+		_scriptMan->_threads->notifyId(tempThreadId);
+	}
+}
+
+FramesList *IllusionsEngine::findSequenceFrames(Sequence *sequence) {
+	// TODO Dummy, to be replaced later
+	return 0;
+}
+
+void IllusionsEngine::setCursorControl(Control *control) {
+	// TODO Dummy, to be replaced later
+}
+
+void IllusionsEngine::placeCursor(Control *control, uint32 sequenceId) {
+	// TODO Dummy, to be replaced later
+}
+
+bool IllusionsEngine::showCursor() {
+	// TODO Dummy, to be replaced later
+	// TODO ++cursor._visibleCtr;
+	// TODO if (cursor._visibleCtr > 0)
+	return false;
+}
+
+bool IllusionsEngine::hideCursor() {
+	// TODO Dummy, to be replaced later
+	// TODO --cursor._visibleCtr;
+	// TODO if (cursor.visibleCtr <= 0) 
+	return false;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index efae321..3e19ecc 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -60,6 +60,8 @@ class Input;
 class Screen;
 class ScriptResource;
 class ScriptMan;
+class Sequence;
+class FramesList;
 
 class IllusionsEngine : public Engine {
 protected:
@@ -89,6 +91,15 @@ public:
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 	Control *findControl(uint32 objectId);
 	ActorType *findActorType(uint32 actorTypeId);
+	Sequence *findSequence(uint32 sequenceId);
+	FramesList *findSequenceFrames(Sequence *sequence);
+	
+	void notifyThreadId(uint32 &threadId);
+	
+	void setCursorControl(Control *control);
+	void placeCursor(Control *control, uint32 sequenceId);
+	bool showCursor();
+	bool hideCursor();
 
 #if 0
 


Commit: 70f0b48aaf1cf5fd294f530e23eb861405caee5c
    https://github.com/scummvm/scummvm/commit/70f0b48aaf1cf5fd294f530e23eb861405caee5c
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add Dictionary class and use it

Changed paths:
  A engines/illusions/dictionary.cpp
  A engines/illusions/dictionary.h
    engines/illusions/actor.cpp
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 04d7636..2de6f48 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
+#include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
 #include "illusions/scriptman.h"
@@ -198,7 +199,7 @@ void Control::unpause() {
   
 	if (_actor && !(_actor->_flags & 0x0200)) {
 		SurfInfo surfInfo;
-		ActorType *actorType = _vm->findActorType(_actorTypeId);
+		ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
 		if (actorType)
 			surfInfo = actorType->_surfInfo;
 		else
@@ -483,7 +484,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 	_actor->_notifyId3C = 0;
 	_actor->_path40 = 0;
 	
-	Sequence *sequence = _vm->findSequence(sequenceId);
+	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 	
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->findSequenceFrames(sequence);
@@ -515,7 +516,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	Control *control = newControl();
 	Actor *actor = newActor();
 
-	ActorType *actorType = _vm->findActorType(actorTypeId);
+	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
 	control->_flags = actorType->_flags;
 	control->_priority = actorType->_priority;
 	control->_objectId = objectId;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index fc4968e..b58a338 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/actorresource.h"
+#include "illusions/dictionary.h"
 
 namespace Illusions {
 
@@ -41,7 +42,7 @@ void ActorResourceLoader::load(Resource *resource) {
 	
 	for (uint i = 0; i < actorResource->_actorTypes.size(); ++i) {
 		ActorType *actorType = &actorResource->_actorTypes[i];
-		ActorType *actorType2 = 0;// TODO _vm->getActorType(actorType->_actorTypeId);
+		ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
 		if (actorType2) {
 			actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
 				actorType2->_surfInfo._dimensions._width);
@@ -52,12 +53,12 @@ void ActorResourceLoader::load(Resource *resource) {
 			if (actorType->_value1E == 0)
 				actorType->_value1E = actorType2->_value1E;
 		}
-		// TODO _vm->addActorType(actorType->_actorTypeId, actorType);
+		_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
 	}
 
 	for (uint i = 0; i < actorResource->_sequences.size(); ++i) {
 		Sequence *sequence = &actorResource->_sequences[i];
-		// TODO _vm->addSequence(sequence->_sequence, sequence);
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
 	}
 	
 }
@@ -187,7 +188,8 @@ bool ActorResource::containsSequence(Sequence *sequence) {
 
 // ActorItem
 
-ActorItem::ActorItem() {
+ActorItem::ActorItem(IllusionsEngine *vm)
+	: _vm(vm) {
 }
 
 ActorItem::~ActorItem() {
@@ -196,28 +198,24 @@ ActorItem::~ActorItem() {
 void ActorItem::pause() {
 	++_pauseCtr;
 	if (_pauseCtr == 1) {
-		/* TODO
 		for (uint i = 0; i < _actRes->_actorTypes.size(); ++i)
-			// TODO _vm->removeActorType(_actRes->_actorTypes[i]._actorTypeId);
-		for (uint i = 0; i < actorResource->_sequences.size(); ++i)
-			// TODO _vm->removeSequence(_actRes->_sequences[i]._sequence);
-		*/
+			_vm->_dict->removeActorType(_actRes->_actorTypes[i]._actorTypeId);
+		for (uint i = 0; i < _actRes->_sequences.size(); ++i)
+			_vm->_dict->removeSequence(_actRes->_sequences[i]._sequenceId);
 	}
 }
 
 void ActorItem::unpause() {
 	--_pauseCtr;
 	if (_pauseCtr == 0) {
-		/* TODO
 		for (uint i = 0; i < _actRes->_actorTypes.size(); ++i) {
 			ActorType *actorType = &_actRes->_actorTypes[i];
-			// TODO _vm->addActorType(actorType->_actorTypeId, actorType);
+			_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
 		}
 		for (uint i = 0; i < _actRes->_sequences.size(); ++i) {
 			Sequence *sequence = &_actRes->_sequences[i];
-			// TODO _vm->addSequence(sequence->_sequence, sequence);
+			_vm->_dict->addSequence(sequence->_sequenceId, sequence);
 		}
-		*/
 	}
 }
 
@@ -231,7 +229,7 @@ ActorItems::~ActorItems() {
 }
 
 ActorItem *ActorItems::allocActorItem() {
-	ActorItem *actorItem = new ActorItem();
+	ActorItem *actorItem = new ActorItem(_vm);
 	_items.push_back(actorItem);
 	return actorItem;
 }
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index c62f8a9..af535d5 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -95,11 +95,12 @@ public:
 
 class ActorItem {
 public:
-	ActorItem();
+	ActorItem(IllusionsEngine *vm);              
 	~ActorItem();
 	void pause();
 	void unpause();
 public:
+	IllusionsEngine *_vm;
 	uint32 _tag;
 	int _pauseCtr;
 	ActorResource *_actRes;
diff --git a/engines/illusions/dictionary.cpp b/engines/illusions/dictionary.cpp
new file mode 100644
index 0000000..bf0a4c8
--- /dev/null
+++ b/engines/illusions/dictionary.cpp
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/dictionary.h"
+#include "illusions/actorresource.h"
+#include "illusions/backgroundresource.h"
+
+namespace Illusions {
+
+void Dictionary::addActorType(uint32 id, ActorType *actorType) {
+	_actorTypes.add(id, actorType);
+}
+
+void Dictionary::removeActorType(uint32 id) {
+	_actorTypes.remove(id);
+}
+
+ActorType *Dictionary::findActorType(uint32 id) {
+	return _actorTypes.find(id);
+}
+
+void Dictionary::addSequence(uint32 id, Sequence *sequence) {
+	_sequences.add(id, sequence);
+}
+
+void Dictionary::removeSequence(uint32 id) {
+	_sequences.remove(id);
+}
+
+Sequence *Dictionary::findSequence(uint32 id) {
+	return _sequences.find(id);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
new file mode 100644
index 0000000..9205fa6
--- /dev/null
+++ b/engines/illusions/dictionary.h
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DICTIONARY_H
+#define ILLUSIONS_DICTIONARY_H
+
+#include "common/hashmap.h"
+
+namespace Illusions {
+
+class ActorType;
+
+template<class T>
+class DictionaryHashMap {
+public:
+
+	void add(uint32 id, T *value) {
+		_map[id] = value;
+	}
+
+	void remove(uint32 id) {
+		_map.erase(id);
+	}
+
+	T *find(uint32 id) {
+		typename Common::HashMap<uint32, T*>::iterator it = _map.find(id);
+		if (it != _map.end())
+			return it->_value;
+		return 0;
+	}
+
+protected:
+	Common::HashMap<uint32, T*> _map;
+};
+
+class Dictionary {
+public:
+
+	void addActorType(uint32 id, ActorType *actorType);
+	void removeActorType(uint32 id);
+	ActorType *findActorType(uint32 id);
+
+    void addSequence(uint32 id, Sequence *sequence);
+	void removeSequence(uint32 id);
+	Sequence *findSequence(uint32 id);
+
+protected:
+	DictionaryHashMap<ActorType> _actorTypes;
+	DictionaryHashMap<Sequence> _sequences;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DICTIONARY_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 3592ea9..e2ae418 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -34,6 +34,7 @@
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
 #include "illusions/time.h"
+#include "illusions/dictionary.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -80,6 +81,8 @@ Common::Error IllusionsEngine::run() {
 	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
 	initGraphics(640, 480, true, &pixelFormat16);
 	
+	_dict = new Dictionary();
+
 	_resSys = new ResourceSystem();
 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
@@ -139,6 +142,7 @@ Common::Error IllusionsEngine::run() {
 	delete _input;
 	delete _screen;
 	delete _resSys;
+	delete _dict;
 	
 	return Common::kNoError;
 }
@@ -191,16 +195,6 @@ Control *IllusionsEngine::findControl(uint32 objectId) {
 	return 0;
 }
 
-ActorType *IllusionsEngine::findActorType(uint32 actorTypeId) {
-	// TODO Dummy, to be replaced later
-	return 0;
-}
-
-Sequence *IllusionsEngine::findSequence(uint32 sequenceId) {
-	// TODO Dummy, to be replaced later
-	return 0;
-}
-
 void IllusionsEngine::notifyThreadId(uint32 &threadId) {
 	if (threadId) {
 		uint32 tempThreadId = threadId;
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 3e19ecc..56bd7d9 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -56,6 +56,7 @@ class BackgroundItems;
 class BackgroundResource;
 class Camera;
 class Control;
+class Dictionary;
 class Input;
 class Screen;
 class ScriptResource;
@@ -77,6 +78,7 @@ private:
 	Graphics::PixelFormat _pixelFormat;
 public:	
 	Common::RandomSource *_random;
+	Dictionary *_dict;
 	ResourceSystem *_resSys;
 	
 	void updateEvents();
@@ -90,8 +92,6 @@ public:
 
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 	Control *findControl(uint32 objectId);
-	ActorType *findActorType(uint32 actorTypeId);
-	Sequence *findSequence(uint32 sequenceId);
 	FramesList *findSequenceFrames(Sequence *sequence);
 	
 	void notifyThreadId(uint32 &threadId);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index e40a676..149ed7d 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	backgroundresource.o \
 	camera.o \
 	detection.o \
+	dictionary.o \
 	fixedpoint.o \
 	graphics.o \
 	illusions.o \


Commit: b3b0bd884dc0cec008cf050f7023bbcdb20f5999
    https://github.com/scummvm/scummvm/commit/b3b0bd884dc0cec008cf050f7023bbcdb20f5999
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Work on Actor and Control classes; fix bug in sprite decompression

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/dictionary.cpp
    engines/illusions/dictionary.h
    engines/illusions/graphics.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/spritedecompressqueue.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 2de6f48..01222f3 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -153,6 +153,18 @@ void Actor::destroySurface() {
 	}
 }
 
+void Actor::initSequenceStack() {
+	_seqStackCount = 0;
+}
+
+void Actor::pushSequenceStack(int16 value) {
+	_seqStack[_seqStackCount++] = value;
+}
+
+int16 Actor::popSequenceStack() {
+	return _seqStack[--_seqStackCount];
+}
+
 // Control
 
 Control::Control(IllusionsEngine *vm)
@@ -180,7 +192,7 @@ Control::~Control() {
 
 void Control::pause() {
 
-	// TODO scrmgrSetObjectArtThread(control->objectId, 0);
+	_vm->_dict->setObjectControl(_objectId, 0);
 
 	if (_objectId == 0x40004)
 		_vm->setCursorControl(0);
@@ -192,7 +204,7 @@ void Control::pause() {
 
 void Control::unpause() {
 
-	// TODO scrmgrSetObjectArtThread(control->objectId, control);
+	_vm->_dict->setObjectControl(_objectId, this);
 
 	if (_objectId == 0x40004)
 		_vm->setCursorControl(this);
@@ -227,7 +239,7 @@ void Control::appearActor() {
 			_actor->_flags |= 0x1000;
 		for (uint i = 0; i < kSubObjectsCount; ++i)
 			if (_actor->_subobjects[i]) {
-				Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 				subControl->appearActor();
 			}
 	}
@@ -244,7 +256,7 @@ void Control::disappearActor() {
 		_actor->_flags |= ~0x1000;
 		for (uint i = 0; i < kSubObjectsCount; ++i)
 			if (_actor->_subobjects[i]) {
-				Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 				subControl->disappearActor();
 			}
 	}
@@ -258,7 +270,7 @@ void Control::activateObject() {
 	_flags |= 1;
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->activateObject();
 		}
 }
@@ -267,7 +279,7 @@ void Control::deactivateObject() {
 	_flags |= ~1;
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->deactivateObject();
 		}
 }
@@ -286,7 +298,7 @@ void Control::setActorScale(int scale) {
 	_actor->_scale = scale;
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->activateObject();
 		}
 }
@@ -295,7 +307,7 @@ void Control::faceActor(uint facing) {
 	_actor->_facing = facing;
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->faceActor(facing);
 		}
 }
@@ -317,7 +329,7 @@ void Control::clearNotifyThreadId1() {
 void Control::clearNotifyThreadId2() {
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->_actor->_flags &= ~0x80;
 			subControl->_actor->_field30 = 0;
 			subControl->_actor->_notifyThreadId2 = 0;
@@ -337,7 +349,7 @@ int Control::getPriority() {
 	if (_actor) {
 		if (_actor->_parentObjectId && (_actor->_flags & 0x40)) {
 			uint32 objectId2 = getSubActorParent();
-			Control *control2 = _vm->findControl(objectId2);
+			Control *control2 = _vm->_dict->getObjectControl(objectId2);
 			objectId = control2->_objectId;
 			priority = control2->_priority;
 			positionY = control2->_actor->_position.y;
@@ -368,7 +380,7 @@ int Control::getPriority() {
 uint32 Control::getSubActorParent() {
 	uint32 parentObjectId = _objectId;
 	while (1) {
-		Actor *actor = _vm->findControl(parentObjectId)->_actor;
+		Actor *actor = _vm->_dict->getObjectControl(parentObjectId)->_actor;
 		if (actor->_parentObjectId && (actor->_flags & 0x40))
 			parentObjectId = actor->_parentObjectId;
 		else
@@ -460,7 +472,7 @@ void Control::stopSequenceActor() {
 	}
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->findControl(_actor->_subobjects[i]);
+			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->stopSequenceActor();
 		}
 }
@@ -485,14 +497,17 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 	_actor->_path40 = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
+	debug("Control::startSequenceActorIntern() sequence = %p", (void*)sequence);
 	
 	_actor->_seqCodeIp = sequence->_sequenceCode;
-	_actor->_frames = _vm->findSequenceFrames(sequence);
+	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
+	debug("Control::startSequenceActorIntern() _actor->_seqCodeIp = %p", (void*)_actor->_seqCodeIp);
+	debug("Control::startSequenceActorIntern() _actor->_frames = %p", (void*)_actor->_frames);
 	
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
 	_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
-	// TODO _actor->initSequenceStack();
+	_actor->initSequenceStack();
 	stopSequenceActor();
 	_actor->_linkIndex2 = 0;
 	if (value2) {
@@ -570,7 +585,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	actor->_pathCtrY = 140;
 	
 	_controls.push_back(control);
-	// TODO scrmgrSetObjectArtThread(objectId, controlb);
+	_vm->_dict->setObjectControl(objectId, control);
 
 	if (actorTypeId == 0x50001 && objectId == 0x40004)
 		_vm->placeCursor(control, sequenceId);
@@ -610,7 +625,7 @@ void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, Wi
 	actor->_pathCtrY = 140;
 
 	_controls.push_back(control);
-	// TODO scrmgrSetObjectArtThread(objectId, controlb);
+	_vm->_dict->setObjectControl(objectId, control);
 	control->appearActor();
 }
 
@@ -627,7 +642,7 @@ void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Commo
 	control->_actorTypeId = 0;
 	control->_actor = 0;
 	_controls.push_back(control);
-	// TODO scrmgrSetObjectArtThread(objectId, controlb);
+	_vm->_dict->setObjectControl(objectId, control);
 }
 
 Actor *Controls::newActor() {
@@ -641,11 +656,9 @@ Control *Controls::newControl() {
 void Controls::destroyControl(Control *control) {
 	_controls.remove(control);
 
-	/* TODO
 	if (control->_pauseCtr <= 0)
-		scrmgrSetObjectArtThread(control->objectId, 0);
-	*/
-	
+		_vm->_dict->setObjectControl(control->_objectId, 0);
+
 	if (control->_objectId == 0x40004 && control->_pauseCtr <= 0)
 		_vm->setCursorControl(0);
 	
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index e70efab..abe746d 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -69,8 +69,13 @@ public:
 	void unpause();
 	void createSurface(SurfInfo &surfInfo);
 	void destroySurface();
+	void initSequenceStack();
+	void pushSequenceStack(int16 value);
+	int16 popSequenceStack();
 public:
 	IllusionsEngine *_vm;
+	byte _drawFlags;
+	uint _spriteFlags;
 	
 	int _pauseCtr;
 	uint _flags;
@@ -86,6 +91,9 @@ public:
 	ScaleLayer *_scaleLayer;
 	PriorityLayer *_priorityLayer;
 	
+	uint _seqStackCount;
+	int16 _seqStack[5];
+	
 	Common::Point _position;
 	Common::Point _position2;
 	uint _facing;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index b58a338..541e4b4 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -29,7 +29,6 @@ namespace Illusions {
 // ActorResourceLoader
 
 void ActorResourceLoader::load(Resource *resource) {
-	// TODO
 	debug("ActorResourceLoader::load() Loading actor %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	ActorResource *actorResource = new ActorResource();
@@ -76,13 +75,14 @@ bool ActorResourceLoader::isFlag(int flag) {
 }
 
 void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	stream.readUint32LE(); //field_0 dd
+	_flags = stream.readUint16LE();
+	stream.skip(2); // Skip padding
 	stream.readUint32LE(); // TODO config dd
 	_surfInfo.load(stream);
 	uint32 compressedPixelsOffs = stream.readUint32LE();
 	_compressedPixels = dataStart + compressedPixelsOffs;
 	
-	debug("Frame::load() compressedPixelsOffs: %08X",
+	debug(5, "Frame::load() compressedPixelsOffs: %08X",
 		compressedPixelsOffs);
 }
 
@@ -92,7 +92,7 @@ void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	uint32 sequenceCodeOffs = stream.readUint32LE();
 	_sequenceCode = dataStart + sequenceCodeOffs;
 	
-	debug("Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
+	debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
 		_sequenceId, _unk4, sequenceCodeOffs);
 }
 
@@ -117,11 +117,11 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_regionLayerIndex = stream.readUint16LE();
 	_flags = stream.readUint16LE();
 
-	debug("ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
+	debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
 		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
-	debug("ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
+	debug(5, "ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
 		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
-	debug("ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
+	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
 		_priorityLayerIndex, _regionLayerIndex,_flags);
 }
 
@@ -246,4 +246,13 @@ void ActorItems::unpauseByTag(uint32 tag) {
 			(*it)->pause();
 }
 
+FramesList *ActorItems::findSequenceFrames(Sequence *sequence) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
+		ActorItem *actorItem = *it;
+		if (actorItem->_pauseCtr <= 0 && actorItem->_actRes->containsSequence(sequence))
+			return &actorItem->_actRes->_frames;
+	}
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index af535d5..1b6e271 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -44,7 +44,7 @@ protected:
 };
 
 struct Frame {
-	//field_0 dd
+	uint16 _flags;
 	// TODO config dd
 	SurfInfo _surfInfo;
 	byte *_compressedPixels;
@@ -113,6 +113,7 @@ public:
 	ActorItem *allocActorItem();
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
+	FramesList *findSequenceFrames(Sequence *sequence);
 protected:
 	typedef Common::List<ActorItem*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/dictionary.cpp b/engines/illusions/dictionary.cpp
index bf0a4c8..2d3de2a 100644
--- a/engines/illusions/dictionary.cpp
+++ b/engines/illusions/dictionary.cpp
@@ -51,4 +51,15 @@ Sequence *Dictionary::findSequence(uint32 id) {
 	return _sequences.find(id);
 }
 
+void Dictionary::setObjectControl(uint32 objectId, Control *control) {
+	if (control)
+		_controls.add(objectId, control);
+	else
+		_controls.remove(objectId);
+}
+
+Control *Dictionary::getObjectControl(uint32 objectId) {
+	return _controls.find(objectId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index 9205fa6..e8863f4 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -63,9 +63,13 @@ public:
 	void removeSequence(uint32 id);
 	Sequence *findSequence(uint32 id);
 
+    void setObjectControl(uint32 objectId, Control *control);
+    Control *getObjectControl(uint32 objectId);
+
 protected:
 	DictionaryHashMap<ActorType> _actorTypes;
 	DictionaryHashMap<Sequence> _sequences;
+	DictionaryHashMap<Control> _controls;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index 0f27a36..6113098 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -28,7 +28,7 @@ void WidthHeight::load(Common::SeekableReadStream &stream) {
 	_width = stream.readSint16LE();
 	_height = stream.readSint16LE();
 	
-	debug("WidthHeight::load() _width: %d; _height: %d",
+	debug(5, "WidthHeight::load() _width: %d; _height: %d",
 		_width, _height);
 }
 
@@ -36,7 +36,7 @@ void SurfInfo::load(Common::SeekableReadStream &stream) {
 	_pixelSize = stream.readUint32LE();
 	_dimensions.load(stream);
 	
-	debug("SurfInfo::load() _pixelSize: %d",
+	debug(5, "SurfInfo::load() _pixelSize: %d",
 		_pixelSize);
 }
 
@@ -44,7 +44,7 @@ void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
 	pt.x = stream.readSint16LE();
 	pt.y = stream.readSint16LE();
 	
-	debug("loadPoint() x: %d; y: %d",
+	debug(5, "loadPoint() x: %d; y: %d",
 		pt.x, pt.y);
 }
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index e2ae418..3d18227 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -95,13 +95,14 @@ Common::Error IllusionsEngine::run() {
 	_actorItems = new ActorItems(this);
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
+	_controls = new Controls(this);
 	
 #if 0
 	// ActorResource test
 	_resSys->loadResource(0x00100006, 0, 0);
 #endif
 
-#if 1
+#if 0
 	// BackgroundResource test
 	_resSys->loadResource(0x00110007, 0, 0);
 	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
@@ -124,17 +125,35 @@ Common::Error IllusionsEngine::run() {
 #if 0
 	// ScriptResource test
 	_resSys->loadResource(0x000D0001, 0, 0);
-	
 	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
-
 	while (!shouldQuit()) {
 		updateEvents();
 		_scriptMan->_threads->updateThreads();
 	}
+#endif
+
+#if 1
+	// Actor/graphics test
+	_resSys->loadResource(0x00110007, 0, 0);
+	_resSys->loadResource(0x00100006, 0, 0);
 	
+	_controls->placeActor(0x00050008, Common::Point(200, 200), 0x00060136, 0x00040001, 0);
 	
+	Control *control = *_controls->_controls.begin();
+	control->setActorFrameIndex(1);
+	control->appearActor();
+
+	while (!shouldQuit()) {
+		updateGraphics();
+		_screen->updateSprites();
+		_system->updateScreen();
+		updateEvents();
+		
+		//break;
+	}
 #endif
 
+	delete _controls;
 	delete _camera;
 	delete _backgroundItems;
 	delete _actorItems;
@@ -190,11 +209,6 @@ Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
 	return 0;
 }
 
-Control *IllusionsEngine::findControl(uint32 objectId) {
-	// TODO Dummy, to be replaced later
-	return 0;
-}
-
 void IllusionsEngine::notifyThreadId(uint32 &threadId) {
 	if (threadId) {
 		uint32 tempThreadId = threadId;
@@ -203,11 +217,6 @@ void IllusionsEngine::notifyThreadId(uint32 &threadId) {
 	}
 }
 
-FramesList *IllusionsEngine::findSequenceFrames(Sequence *sequence) {
-	// TODO Dummy, to be replaced later
-	return 0;
-}
-
 void IllusionsEngine::setCursorControl(Control *control) {
 	// TODO Dummy, to be replaced later
 }
@@ -230,4 +239,69 @@ bool IllusionsEngine::hideCursor() {
 	return false;
 }
 
+int IllusionsEngine::updateGraphics() {
+	Common::Point panPoint(0, 0);
+
+	uint32 currTime = getCurrentTime();
+    _camera->update(currTime);
+    
+    // TODO Move to BackgroundItems class
+    BackgroundItem *backgroundItem = _backgroundItems->findActiveBackground();
+    if (backgroundItem) {
+    	BackgroundResource *bgRes = backgroundItem->_bgRes;
+    	for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
+    		BgInfo *bgInfo = &bgRes->_bgInfos[i];
+			// TODO int16 priority = artcntrlGetPriorityFromBase(bgInfos[v7].priorityBase);
+			int16 priority = -1;
+			_screen->_drawQueue->insertSurface(backgroundItem->_surfaces[i],
+				bgInfo->_surfInfo._dimensions, backgroundItem->_panPoints[i], priority);
+			if (bgInfo->_flags & 1)
+				panPoint = backgroundItem->_panPoints[i];
+		}
+	}
+	
+	// TODO Move to Controls class
+	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
+		Control *control = *it;
+		Actor *actor = control->_actor;
+		
+		debug("control->_pauseCtr: %d; actor->_flags: %04X", control->_pauseCtr, actor->_flags);
+		
+		if (control->_pauseCtr == 0 && actor && (actor->_flags & 1) && !(actor->_flags & 0x0200)) {
+			// TODO Common::Point drawPosition = control->calcPosition(panPoint);
+			Common::Point drawPosition(200, 200);//DEBUG
+			if (actor->_flags & 0x2000) {
+				Frame *frame = &(*actor->_frames)[actor->_frameIndex - 1];
+				_screen->_decompressQueue->insert(&actor->_drawFlags, frame->_flags,
+					frame->_surfInfo._pixelSize, frame->_surfInfo._dimensions,
+					frame->_compressedPixels, actor->_surface);
+				actor->_flags &= ~0x2000;
+			}
+			/* Unused
+			if (actor->_flags & 0x4000) {
+				nullsub_1(&actor->drawFlags);
+				actor->flags &= ~0x4000;
+			}
+			*/
+			if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
+				// TODO int16 priority = control->getPriority();
+				int16 priority = 2;
+				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
+					actor->_surfInfo._dimensions, drawPosition, control->_position,
+					priority, actor->_scale, actor->_spriteFlags);
+			}
+		}
+	}
+
+#if 0 // TODO
+	if (_textInfo._surface) {
+		int16 priority = getPriorityFromBase(99);
+		_screen->_drawQueue->insertTextSurface(_textInfo._surface, _textInfo._dimensions,
+			_textInfo._position, priority);
+	}
+#endif
+    
+	return 1;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 56bd7d9..d262690 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -56,6 +56,7 @@ class BackgroundItems;
 class BackgroundResource;
 class Camera;
 class Control;
+class Controls;
 class Dictionary;
 class Input;
 class Screen;
@@ -89,10 +90,9 @@ public:
 	ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
+	Controls *_controls;
 
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
-	Control *findControl(uint32 objectId);
-	FramesList *findSequenceFrames(Sequence *sequence);
 	
 	void notifyThreadId(uint32 &threadId);
 	
@@ -100,6 +100,7 @@ public:
 	void placeCursor(Control *control, uint32 sequenceId);
 	bool showCursor();
 	bool hideCursor();
+	int updateGraphics();
 
 #if 0
 
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 1b33b0c..6f14f8d 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -31,7 +31,7 @@ namespace Illusions {
 // Screen
 
 Screen::Screen(IllusionsEngine *vm)
-	: _vm(vm) {
+	: _vm(vm), _colorKey2(0) {
 	_displayOn = true;
 	_backSurface = allocSurface(640, 480);
 	_decompressQueue = new SpriteDecompressQueue();
@@ -65,11 +65,6 @@ uint16 Screen::getColorKey2() {
 	return _colorKey2;
 }
 
-Graphics::Surface *Screen::getBackSurface() {
-	// TODO Move this outside into a screen class
-	return 0;
-}
-
 void Screen::updateSprites() {
 	_decompressQueue->decompressAll();
 	// NOTE Skipped doShiftBrightness and related as it seems to be unused
@@ -80,19 +75,33 @@ void Screen::updateSprites() {
 }
 
 void Screen::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+	// Unscaled, transparent
 	// TODO
+	debug("Screen::drawSurface10");
 }
 
 void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// TODO
+	// Unscaled, non-transparent
+	debug(1, "Screen::drawSurface11() destX: %d; destY: %d; srcRect: (%d, %d, %d, %d)", destX, destY, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom);
+	const int16 w = srcRect.width();
+	const int16 h = srcRect.height();
+	for (int16 yc = 0; yc < h; ++yc) {
+		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcRect.top + yc);
+		byte *dst = (byte*)_backSurface->getBasePtr(destX, destY + yc);
+		memcpy(dst, src, w * 2);
+	}
 }
 
 void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+	// Scaled, transparent
 	// TODO
+	debug("Screen::drawSurface20");
 }
 
 void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// Scaled, non-transparent
 	// TODO
+	debug("Screen::drawSurface21");
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 5d5ead2..f0e1dbf 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -39,7 +39,6 @@ public:
 	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
 	bool isDisplayOn();
 	uint16 getColorKey2();
-	Graphics::Surface *getBackSurface();
 	void updateSprites();
 	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
 	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
diff --git a/engines/illusions/spritedecompressqueue.cpp b/engines/illusions/spritedecompressqueue.cpp
index 01ebc74..acb4547 100644
--- a/engines/illusions/spritedecompressqueue.cpp
+++ b/engines/illusions/spritedecompressqueue.cpp
@@ -77,12 +77,13 @@ void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
 	}
 	
 	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
-
+	
 	while (processedSize < dstSize) {
 		int16 op = READ_LE_UINT16(src);
 		src += 2;
 		if (op & 0x8000) {
 			int runCount = (op & 0x7FFF) + 1;
+			processedSize += runCount;
 			uint16 runColor = READ_LE_UINT16(src);
 			src += 2;
 			while (runCount--) {
@@ -96,9 +97,9 @@ void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
 					dst += 2 * xincr;
 				}
 			}
-			processedSize += runCount;
 		} else {
 			int copyCount = op + 1;
+			processedSize += copyCount;
 			while (copyCount--) {
 				uint16 color = READ_LE_UINT16(src);
 				src += 2;
@@ -112,7 +113,6 @@ void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
 					dst += 2 * xincr;
 				}
 			}
-			processedSize += copyCount;
 		}
 	}
 


Commit: 3fc592df497b957b42aa4eec27f8f77f899e0700
    https://github.com/scummvm/scummvm/commit/3fc592df497b957b42aa4eec27f8f77f899e0700
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add Control::calcPosition and Control::readPointsConfig

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/illusions.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 01222f3..c0a42a1 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -284,6 +284,26 @@ void Control::deactivateObject() {
 		}
 }
 
+void Control::readPointsConfig(byte *pointsConfig) {
+	_unkPt.x = READ_LE_UINT16(pointsConfig + 0);
+	_unkPt.y = READ_LE_UINT16(pointsConfig + 2);
+	pointsConfig += 2;
+	_pt.x = READ_LE_UINT16(pointsConfig + 0);
+	_pt.y = READ_LE_UINT16(pointsConfig + 2);
+	pointsConfig += 2;
+	_feetPt.x = READ_LE_UINT16(pointsConfig + 0);
+	_feetPt.y = READ_LE_UINT16(pointsConfig + 2);
+	pointsConfig += 2;
+	_position.x = READ_LE_UINT16(pointsConfig + 0);
+	_position.y = READ_LE_UINT16(pointsConfig + 2);
+	pointsConfig += 2;
+	for (uint i = 0; i < kSubObjectsCount; ++i) {
+		_subobjectsPos[i].x = READ_LE_UINT16(pointsConfig + 0);
+		_subobjectsPos[i].y = READ_LE_UINT16(pointsConfig + 2);
+		pointsConfig += 2;
+	}
+}
+
 void Control::setActorPosition(Common::Point position) {
 	_actor->_position = position;
 }
@@ -377,6 +397,35 @@ int Control::getPriority() {
 	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));
 }
 
+Common::Point Control::calcPosition(Common::Point posDelta) {
+	Common::Point pos;
+	if (_actor->_parentObjectId) {
+		int16 accuX = 0, accuY = 0;
+		Actor *actor = _actor;
+		while (actor->_parentObjectId) {
+			Control *parentControl = _vm->_dict->getObjectControl(actor->_parentObjectId);
+			accuX += parentControl->_subobjectsPos[actor->_linkIndex - 1].x;
+			accuY += parentControl->_subobjectsPos[actor->_linkIndex - 1].y;
+			actor = parentControl->_actor;
+		}
+		pos = actor->_position;
+		pos.x += accuX * actor->_scale / 100;
+		pos.y += accuY * actor->_scale / 100;
+		_actor->_position = pos;
+		if (!(_actor->_flags & 8)) {
+			pos.x -= posDelta.x;
+			pos.y -= posDelta.y;
+		}
+	} else {
+		pos = _actor->_position;
+		if (!(_actor->_flags & 8)) {
+			pos.x -= posDelta.x;
+			pos.y -= posDelta.y;
+		}
+	}
+	return pos;
+}
+
 uint32 Control::getSubActorParent() {
 	uint32 parentObjectId = _objectId;
 	while (1) {
@@ -400,7 +449,7 @@ void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {
 	}
 
 	if (_actor) {
-		if (_actor->_scale != 100 ) {
+		if (_actor->_scale != 100) {
 			// scaledValue = value * scale div 100
 			collisionRect.left = collisionRect.left * _actor->_scale / 100;
 			collisionRect.top = collisionRect.top * _actor->_scale / 100;
@@ -429,7 +478,7 @@ void Control::setActorFrameIndex(int16 frameIndex) {
 		_actor->_frameIndex = frameIndex;
 		const Frame &frame = (*_actor->_frames)[frameIndex - 1];
 		_actor->_surfInfo = frame._surfInfo;
-		// TODO memcpy(&control->unkPt, (const void *)frame->config, 0x4Cu);
+		readPointsConfig(frame._pointsConfig);
 		_actor->_flags |= 0x2000;
 		_actor->_flags |= 0x4000;
 		_actor->_newFrameIndex = 0;
@@ -535,7 +584,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	control->_flags = actorType->_flags;
 	control->_priority = actorType->_priority;
 	control->_objectId = objectId;
-	// TODO memcpy(&control->unkPt, (const void *)actorType->_config, 0x4Cu);
+	control->readPointsConfig(actorType->_pointsConfig);
 	control->_actorTypeId = actorTypeId;
 	control->_actor = actor;
 	/* TODO
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index abe746d..d913710 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -138,6 +138,7 @@ public:
 	bool isActorVisible();
 	void activateObject();
 	void deactivateObject();
+	void readPointsConfig(byte *pointsConfig);
 	void setActorPosition(Common::Point position);
 	Common::Point getActorPosition();
 	void setActorScale(int scale);
@@ -148,6 +149,7 @@ public:
 	void clearNotifyThreadId2();
 	void setPriority(int16 priority);
 	int getPriority();
+	Common::Point calcPosition(Common::Point posDelta);
 	uint32 getSubActorParent();
 	void getCollisionRectAccurate(Common::Rect &collisionRect);
 	void setActorUsePan(int usePan);
@@ -171,6 +173,7 @@ public:
 	Common::Point _pt;
 	Common::Point _feetPt;
 	Common::Point _position;
+	Common::Point _subobjectsPos[kSubObjectsCount];
 	// TODO 0000001C - 00000054 unknown
 	void startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId);
 };
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index 541e4b4..426fff6 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -77,10 +77,11 @@ bool ActorResourceLoader::isFlag(int flag) {
 void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_flags = stream.readUint16LE();
 	stream.skip(2); // Skip padding
-	stream.readUint32LE(); // TODO config dd
+	uint32 pointsConfigOffs = stream.readUint32LE();
 	_surfInfo.load(stream);
 	uint32 compressedPixelsOffs = stream.readUint32LE();
 	_compressedPixels = dataStart + compressedPixelsOffs;
+	_pointsConfig = dataStart + pointsConfigOffs;
 	
 	debug(5, "Frame::load() compressedPixelsOffs: %08X",
 		compressedPixelsOffs);
@@ -99,7 +100,7 @@ void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_actorTypeId = stream.readUint32LE();
 	_surfInfo.load(stream);
-	stream.readUint32LE(); // TODO config dd
+	uint32 pointsConfigOffs = stream.readUint32LE();
 	stream.readUint16LE(); // TODO namedPointsCount dw
 	stream.skip(2); // Skip padding
 	stream.readUint32LE(); // TODO namedPoints dd
@@ -116,6 +117,7 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_priorityLayerIndex = stream.readUint16LE();
 	_regionLayerIndex = stream.readUint16LE();
 	_flags = stream.readUint16LE();
+	_pointsConfig = dataStart + pointsConfigOffs;
 
 	debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
 		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index 1b6e271..b15f3a0 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -45,7 +45,7 @@ protected:
 
 struct Frame {
 	uint16 _flags;
-	// TODO config dd
+	byte *_pointsConfig;
 	SurfInfo _surfInfo;
 	byte *_compressedPixels;
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
@@ -61,7 +61,7 @@ struct Sequence {
 struct ActorType {
 	uint32 _actorTypeId;
 	SurfInfo _surfInfo;
-	// TODO config dd
+	byte *_pointsConfig;
 	// TODO namedPointsCount dw
 	// TODO namedPoints dd
 	RGB _color;
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 3d18227..ddb4d60 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -136,20 +136,16 @@ Common::Error IllusionsEngine::run() {
 	// Actor/graphics test
 	_resSys->loadResource(0x00110007, 0, 0);
 	_resSys->loadResource(0x00100006, 0, 0);
-	
 	_controls->placeActor(0x00050008, Common::Point(200, 200), 0x00060136, 0x00040001, 0);
-	
 	Control *control = *_controls->_controls.begin();
 	control->setActorFrameIndex(1);
 	control->appearActor();
-
+	//_camera->panToPoint(Common::Point(800, 0), 500, 0);
 	while (!shouldQuit()) {
 		updateGraphics();
 		_screen->updateSprites();
 		_system->updateScreen();
 		updateEvents();
-		
-		//break;
 	}
 #endif
 
@@ -268,8 +264,8 @@ int IllusionsEngine::updateGraphics() {
 		debug("control->_pauseCtr: %d; actor->_flags: %04X", control->_pauseCtr, actor->_flags);
 		
 		if (control->_pauseCtr == 0 && actor && (actor->_flags & 1) && !(actor->_flags & 0x0200)) {
-			// TODO Common::Point drawPosition = control->calcPosition(panPoint);
-			Common::Point drawPosition(200, 200);//DEBUG
+			Common::Point drawPosition = control->calcPosition(panPoint);
+			debug("drawPosition: %d, %d", drawPosition.x, drawPosition.y);
 			if (actor->_flags & 0x2000) {
 				Frame *frame = &(*actor->_frames)[actor->_frameIndex - 1];
 				_screen->_decompressQueue->insert(&actor->_drawFlags, frame->_flags,


Commit: 18540a5e385997815f5a7c237328d7bc0b10c174
    https://github.com/scummvm/scummvm/commit/18540a5e385997815f5a7c237328d7bc0b10c174
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add SequenceOpcodes skeleton class

Changed paths:
  A engines/illusions/sequenceopcodes.cpp
  A engines/illusions/sequenceopcodes.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptthread.cpp
    engines/illusions/scriptthread.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index c0a42a1..74443c3 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -27,6 +27,7 @@
 #include "illusions/input.h"
 #include "illusions/screen.h"
 #include "illusions/scriptman.h"
+#include "illusions/sequenceopcodes.h"
 
 namespace Illusions {
 
@@ -570,10 +571,20 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 	
 }
 
+void Control::execSequenceOpcode(OpCall &opCall) {
+	// TODO Clean this up
+	_vm->_controls->_sequenceOpcodes->execOpcode(this, opCall);
+}
+
 // Controls
 
 Controls::Controls(IllusionsEngine *vm)
 	: _vm(vm) {
+	_sequenceOpcodes = new SequenceOpcodes(_vm);
+}
+
+Controls::~Controls() {
+	delete _sequenceOpcodes;
 }
 
 void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId) {
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index d913710..0684ce1 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -33,6 +33,8 @@
 namespace Illusions {
 
 class IllusionsEngine;
+class SequenceOpcodes;
+struct OpCall;
 
 const uint kSubObjectsCount = 15;
 
@@ -176,11 +178,13 @@ public:
 	Common::Point _subobjectsPos[kSubObjectsCount];
 	// TODO 0000001C - 00000054 unknown
 	void startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId);
+	void execSequenceOpcode(OpCall &opCall);
 };
 
 class Controls {
 public:
 	Controls(IllusionsEngine *vm);
+	~Controls();
 	void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);	
@@ -189,6 +193,7 @@ public:
 	typedef Items::iterator ItemsIterator;
 	IllusionsEngine *_vm;
 	Items _controls;
+	SequenceOpcodes *_sequenceOpcodes;
 	Actor *newActor();
 	Control *newControl();
 	void destroyControl(Control *control);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 149ed7d..eab667e 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -17,6 +17,7 @@ MODULE_OBJS := \
 	scriptopcodes.o \
 	scriptresource.o \
 	scriptthread.o \
+	sequenceopcodes.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	thread.o \
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index ac38548..392170d 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -28,6 +28,28 @@
 
 namespace Illusions {
 
+// OpCall
+
+void OpCall::skip(uint size) {
+	_code += size;
+}
+
+byte OpCall::readByte() {
+	return *_code++;
+}
+
+int16 OpCall::readSint16() {
+	int16 value = READ_LE_UINT16(_code);
+	_code += 2;
+	return value;
+}
+
+uint32 OpCall::readUint32() {
+	uint32 value = READ_LE_UINT32(_code);
+	_code += 4;
+	return value;
+}
+
 // ScriptOpcodes
 
 ScriptOpcodes::ScriptOpcodes(IllusionsEngine *vm)
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 78b68e5..3f79ea5 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -29,7 +29,19 @@ namespace Illusions {
 
 class IllusionsEngine;
 class ScriptThread;
-struct OpCall;
+
+struct OpCall {
+	byte _op;
+	byte _opSize;
+	uint32 _threadId;
+	int16 _deltaOfs;
+	byte *_code;
+	int _result;
+	void skip(uint size);
+	byte readByte();
+	int16 readSint16();
+	uint32 readUint32();
+};
 
 typedef Common::Functor2<ScriptThread*, OpCall&, void> ScriptOpcode;
 
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index a594101..6455cfb 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -27,28 +27,6 @@
 
 namespace Illusions {
 
-// OpCall
-
-void OpCall::skip(uint size) {
-	_scriptCode += size;
-}
-
-byte OpCall::readByte() {
-	return *_scriptCode++;
-}
-
-int16 OpCall::readSint16() {
-	int16 value = READ_LE_UINT16(_scriptCode);
-	_scriptCode += 2;
-	return value;
-}
-
-uint32 OpCall::readUint32() {
-	uint32 value = READ_LE_UINT32(_scriptCode);
-	_scriptCode += 4;
-	return value;
-}
-
 // ScriptThread
 
 ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
@@ -65,7 +43,7 @@ int ScriptThread::onUpdate() {
 		opCall._op = _scriptCodeIp[0];
 		opCall._opSize = _scriptCodeIp[1] >> 1;
 		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
-		opCall._scriptCode = _scriptCodeIp + 2;
+		opCall._code = _scriptCodeIp + 2;
 		opCall._deltaOfs = 0;
 		execOpcode(opCall);
 		_scriptCodeIp += opCall._opSize + opCall._deltaOfs;
diff --git a/engines/illusions/scriptthread.h b/engines/illusions/scriptthread.h
index 201e598..f0d122b 100644
--- a/engines/illusions/scriptthread.h
+++ b/engines/illusions/scriptthread.h
@@ -28,19 +28,7 @@
 namespace Illusions {
 
 class IllusionsEngine;
-
-struct OpCall {
-	byte _op;
-	byte _opSize;
-	uint32 _threadId;
-	int16 _deltaOfs;
-	byte *_scriptCode;
-	int _result;
-	void skip(uint size);
-	byte readByte();
-	int16 readSint16();
-	uint32 readUint32();
-};
+struct OpCall;
 
 class ScriptThread : public Thread {
 public:
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
new file mode 100644
index 0000000..22845f0
--- /dev/null
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/sequenceopcodes.h"
+#include "illusions/actor.h"
+#include "illusions/actorresource.h"
+#include "illusions/scriptopcodes.h"
+
+namespace Illusions {
+
+// SequenceOpcodes
+
+SequenceOpcodes::SequenceOpcodes(IllusionsEngine *vm)
+	: _vm(vm) {
+	initOpcodes();
+}
+
+SequenceOpcodes::~SequenceOpcodes() {
+	freeOpcodes();
+}
+
+void SequenceOpcodes::execOpcode(Control *control, OpCall &opCall) {
+	if (!_opcodes[opCall._op])
+		error("SequenceOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
+	debug("execOpcode(%d)", opCall._op);
+	(*_opcodes[opCall._op])(control, opCall);
+}
+
+typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, SequenceOpcodes> SequenceOpcodeI;
+#define OPCODE(op, func) _opcodes[op] = new SequenceOpcodeI(this, &SequenceOpcodes::func);
+
+void SequenceOpcodes::initOpcodes() {
+	// First clear everything
+	for (uint i = 0; i < 256; ++i)
+		_opcodes[i] = 0;
+	// Register opcodes
+	//OPCODE(42, opIncBlockCounter);
+}
+
+#undef OPCODE
+
+void SequenceOpcodes::freeOpcodes() {
+	for (uint i = 0; i < 256; ++i)
+		delete _opcodes[i];
+}
+
+// Opcodes
+
+// Convenience macros
+#define	ARG_SKIP(x) opCall.skip(x); 
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %d)", name);
+
+} // End of namespace Illusions
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
new file mode 100644
index 0000000..aa31a76
--- /dev/null
+++ b/engines/illusions/sequenceopcodes.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SEQUENCEOPCODES_H
+#define ILLUSIONS_SEQUENCEOPCODES_H
+
+#include "common/func.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+class Control;
+struct OpCall;
+
+typedef Common::Functor2<Control*, OpCall&, void> SequenceOpcode;
+
+class SequenceOpcodes {
+public:
+	SequenceOpcodes(IllusionsEngine *vm);
+	~SequenceOpcodes();
+	void execOpcode(Control *control, OpCall &opCall);
+protected:
+	IllusionsEngine *_vm;
+	SequenceOpcode *_opcodes[256];
+	void initOpcodes();
+	void freeOpcodes();
+
+	// Opcodes	
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SEQUENCEOPCODES_H


Commit: e881db073200fb2b3d7087c076ff3da77135516a
    https://github.com/scummvm/scummvm/commit/e881db073200fb2b3d7087c076ff3da77135516a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement more sequence opcodes and work on the graphics system

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/spritedecompressqueue.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 74443c3..e1aa5f7 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -27,6 +27,7 @@
 #include "illusions/input.h"
 #include "illusions/screen.h"
 #include "illusions/scriptman.h"
+#include "illusions/scriptopcodes.h"
 #include "illusions/sequenceopcodes.h"
 
 namespace Illusions {
@@ -53,6 +54,8 @@ void DefaultSequences::set(uint32 sequenceId, uint32 newSequenceId) {
 Actor::Actor(IllusionsEngine *vm)
 	: _vm(vm), _pauseCtr(0) {
 	_pauseCtr = 0;
+	_spriteFlags = 0;
+	_drawFlags = 0;
 	_flags = 0;
 	_scale = 100;
 	_frameIndex = 0;
@@ -84,15 +87,16 @@ Actor::Actor(IllusionsEngine *vm)
 	_seqCodeValue1 = 0;
 	_seqCodeValue2 = 600;
 	_seqCodeValue3 = 0;
+	
+	_notifyId3C = 0;
 
 	_pathCtrY = 0;
+	
+	_controlRoutine = 0;
+	setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(_vm->_controls, &Controls::actorControlRouine));
 
 #if 0 // TODO
 	_field2 = 0;
-	_spriteFlags = 0;
-	_drawFlags = 0;
-	_controlRoutine = Actor_defaultControlRoutine;
-	_notifyId3C = 0;
 	_path40 = 0;
 	_path4C = 0;
 	_pathFlag50 = 0;
@@ -166,6 +170,16 @@ int16 Actor::popSequenceStack() {
 	return _seqStack[--_seqStackCount];
 }
 
+void Actor::setControlRoutine(ActorControlRoutine *controlRoutine) {
+	delete _controlRoutine;
+	_controlRoutine = controlRoutine;
+}
+
+void Actor::runControlRoutine(Control *control, uint32 deltaTime) {
+	if (_controlRoutine)
+		(*_controlRoutine)(control, deltaTime);
+}
+
 // Control
 
 Control::Control(IllusionsEngine *vm)
@@ -501,10 +515,8 @@ void Control::stopActor() {
 		_actor->_path40 = 0;
 	}
 	*/
-	
 	_vm->notifyThreadId(_actor->_notifyThreadId1);
 	_vm->notifyThreadId(_actor->_notifyId3C);
-	
 }
 
 void Control::startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId) {
@@ -528,7 +540,52 @@ void Control::stopSequenceActor() {
 }
 
 void Control::sequenceActor() {
-	// TODO
+
+	if (_actor->_pauseCtr > 0)
+		return;
+
+	OpCall opCall;
+  	bool sequenceFinished = false;
+
+	opCall._result = 0;
+	_actor->_seqCodeValue3 -= _actor->_seqCodeValue1;
+	
+	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
+		bool breakInner = false;
+		while (!breakInner) {
+			debug("op: %08X", _actor->_seqCodeIp[0]);
+			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
+			opCall._opSize = _actor->_seqCodeIp[1];
+			opCall._code = _actor->_seqCodeIp + 2;
+			opCall._deltaOfs = opCall._opSize;
+			if (_actor->_seqCodeIp[0] & 0x80)
+				breakInner = true;
+			execSequenceOpcode(opCall);
+			if (opCall._result == 1) {
+				sequenceFinished = true;
+				breakInner = true;
+			} else if (opCall._result == 2) {
+				breakInner = true;
+			}
+			_actor->_seqCodeIp += opCall._deltaOfs;
+		}
+		_actor->_seqCodeValue3 += _actor->_seqCodeValue2;
+	}
+
+	if (_actor->_newFrameIndex != 0) {
+		debug("New frame %d", _actor->_newFrameIndex);
+		setActorFrameIndex(_actor->_newFrameIndex);
+		if (!(_actor->_flags & 1) && (_actor->_flags & 0x1000) && (_objectId != 0x40004)) {
+			appearActor();
+			_actor->_flags &= ~0x1000;
+		}
+	}
+	
+	if (sequenceFinished) {
+		debug("Sequence has finished");
+		_actor->_seqCodeIp = 0;
+	}
+	
 }
 
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId) {
@@ -567,7 +624,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 		_actor->_notifyThreadId2 = notifyThreadId;
 	}
 
-	// TODO sequenceActor();
+	sequenceActor();
 	
 }
 
@@ -672,7 +729,7 @@ void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, Wi
 	control->_position.y = 0;
 	control->_actorTypeId = 0x50004;
 	control->_actor = actor;
-	// TODO actor->setControlRoutine(0);
+	actor->setControlRoutine(0);
 	actor->_surfInfo._pixelSize = dimensions._width * dimensions._height;
 	actor->_surfInfo._dimensions = dimensions;
 	actor->createSurface(actor->_surfInfo);
@@ -743,4 +800,35 @@ void Controls::destroyControl(Control *control) {
 	delete control;
 }
 
+void Controls::actorControlRouine(Control *control, uint32 deltaTime) {
+	//debug("Controls::actorControlRouine()");
+
+	Actor *actor = control->_actor;
+
+	if (actor->_pauseCtr > 0)
+		return;
+
+	if (false/*actor->_pathNode*/) {
+		// TODO Update pathwalking
+	} else {
+		actor->_seqCodeValue1 = 100 * deltaTime;
+	}
+
+	if (actor->_flags & 4) {
+		int scale = actor->_scaleLayer->getScale(actor->_position);
+		control->setActorScale(scale);
+	}
+
+	if (actor->_flags & 8) {
+		int16 priority = actor->_priorityLayer->getPriority(actor->_position);
+		if (priority)
+			control->setPriority(priority + 1);
+	}
+
+	if (actor->_flags & 0x20) {
+		// TODO Update transition sequence (seems to be unused in BBDOU?)
+	}
+
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 0684ce1..6a682c6 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -27,6 +27,7 @@
 #include "illusions/backgroundresource.h"
 #include "illusions/graphics.h"
 #include "common/algorithm.h"
+#include "common/func.h"
 #include "common/list.h"
 #include "graphics/surface.h"
 
@@ -64,6 +65,8 @@ protected:
 	Common::Array<DefaultSequence> _items;
 };
 
+typedef Common::Functor2<Control*, uint32, void> ActorControlRoutine;
+
 class Actor {
 public:
 	Actor(IllusionsEngine *vm);
@@ -74,6 +77,8 @@ public:
 	void initSequenceStack();
 	void pushSequenceStack(int16 value);
 	int16 popSequenceStack();
+	void setControlRoutine(ActorControlRoutine *controlRoutine);
+	void runControlRoutine(Control *control, uint32 deltaTime);
 public:
 	IllusionsEngine *_vm;
 	byte _drawFlags;
@@ -117,6 +122,8 @@ public:
 	
 	int _surfaceTextFlag;
 	
+	ActorControlRoutine *_controlRoutine;
+	
 	byte *_seqCodeIp;
 	uint32 _sequenceId;
 	int _seqCodeValue1;
@@ -187,7 +194,8 @@ public:
 	~Controls();
 	void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
-	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);	
+	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
+	void actorControlRouine(Control *control, uint32 deltaTime);	
 public:
 	typedef Common::List<Control*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index 426fff6..831a932 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -95,6 +95,8 @@ void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	
 	debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
 		_sequenceId, _unk4, sequenceCodeOffs);
+
+	debug("_sequenceId: %08X", _sequenceId);
 }
 
 void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
@@ -125,6 +127,9 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
 	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
 		_priorityLayerIndex, _regionLayerIndex,_flags);
+
+	debug("_actorTypeId: %08X; dimensions: (%d, %d)", _actorTypeId, _surfInfo._dimensions._width, _surfInfo._dimensions._height);
+
 }
 
 // ActorResource
@@ -159,7 +164,7 @@ void ActorResource::load(byte *data, uint32 dataSize) {
 	stream.seek(0x14);
 	uint32 sequencesOffs = stream.readUint32LE();
 	stream.seek(sequencesOffs);
-	_actorTypes.reserve(sequencesCount);
+	_sequences.reserve(sequencesCount);
 	for (uint i = 0; i < sequencesCount; ++i) {
 		Sequence sequence;
 		sequence.load(data, stream);
@@ -172,7 +177,7 @@ void ActorResource::load(byte *data, uint32 dataSize) {
 	stream.seek(0x18);
 	uint32 framesOffs = stream.readUint32LE();
 	stream.seek(framesOffs);
-	_actorTypes.reserve(framesCount);
+	_frames.reserve(framesCount);
 	for (uint i = 0; i < framesCount; ++i) {
 		Frame frame;
 		frame.load(data, stream);
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index bdce1c4..6c7808c 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -173,6 +173,18 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 		_scaleLayers[i].load(data, stream);
 	}
 
+	// Load priority layers
+	stream.seek(0x14);
+	_priorityLayersCount = stream.readUint16LE();
+	_priorityLayers = new PriorityLayer[_priorityLayersCount];
+	stream.seek(0x34);
+	uint32 priorityLayersOffs = stream.readUint32LE();
+	debug("_priorityLayersCount: %d", _priorityLayersCount);
+	for (uint i = 0; i < _priorityLayersCount; ++i) {
+		stream.seek(priorityLayersOffs + i * 12);
+		_priorityLayers[i].load(data, stream);
+	}
+
 }
 
 int BackgroundResource::findMasterBgIndex() {
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index de6204e..d3865d1 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -90,13 +90,13 @@ protected:
 
 #if 0
 BgResource_PathWalkRects struc ; (sizeof=0x8)
-count           dd ?
-rects           dd ?
+count dd ?
+rects dd ?
 BgResource_PathWalkRects ends
 
 BgResource_PathWalkPoints struc ; (sizeof=0x8)
-count           dd ?
-points          dd ?
+count dd ?
+points dd ?
 BgResource_PathWalkPoints ends
 #endif
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index ddb4d60..2a92d94 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -97,6 +97,11 @@ Common::Error IllusionsEngine::run() {
 	_camera = new Camera(this);
 	_controls = new Controls(this);
 	
+	// TODO Move to own class
+	_resGetCtr = 0;
+	_unpauseControlActorFlag = false;
+	_lastUpdateTime = 0;
+	
 #if 0
 	// ActorResource test
 	_resSys->loadResource(0x00100006, 0, 0);
@@ -134,18 +139,25 @@ Common::Error IllusionsEngine::run() {
 
 #if 1
 	// Actor/graphics test
-	_resSys->loadResource(0x00110007, 0, 0);
-	_resSys->loadResource(0x00100006, 0, 0);
-	_controls->placeActor(0x00050008, Common::Point(200, 200), 0x00060136, 0x00040001, 0);
+
+	/* TODO 0x0010000B LinkIndex 0x00060AAB 0x00060556
+	*/	
+	
+	_resSys->loadResource(0x0011000B, 0, 0);
+	_resSys->loadResource(0x0010000B, 0, 0);
+	_controls->placeActor(0x00050009, Common::Point(0, 0), 0x00060573, 0x00040001, 0);
 	Control *control = *_controls->_controls.begin();
 	control->setActorFrameIndex(1);
 	control->appearActor();
 	//_camera->panToPoint(Common::Point(800, 0), 500, 0);
 	while (!shouldQuit()) {
+		updateActors();
+		updateSequences();
 		updateGraphics();
 		_screen->updateSprites();
 		_system->updateScreen();
 		updateEvents();
+		_system->delayMillis(10);
 	}
 #endif
 
@@ -235,18 +247,58 @@ bool IllusionsEngine::hideCursor() {
 	return false;
 }
 
+uint32 IllusionsEngine::getElapsedUpdateTime() {
+	uint32 result = 0;
+	uint32 currTime = getCurrentTime();
+	if (_resGetCtr <= 0 ) {
+		if (_unpauseControlActorFlag) {
+			_unpauseControlActorFlag = false;
+			result = 0;
+		} else {
+			result = currTime - _lastUpdateTime;
+		}
+		_lastUpdateTime = currTime;
+	} else {
+		result = _resGetTime - _lastUpdateTime;
+		_lastUpdateTime = _resGetTime;
+	}
+	return result;
+}
+
+int IllusionsEngine::updateActors() {
+	// TODO Move to Controls class
+	uint32 deltaTime = getElapsedUpdateTime();
+	//debug("deltaTime: %d", deltaTime);
+	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_controlRoutine)
+			control->_actor->runControlRoutine(control, deltaTime);
+	}
+	return 1;
+}
+
+int IllusionsEngine::updateSequences() {
+	// TODO Move to Controls class
+	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_seqCodeIp)
+			control->sequenceActor();
+	}
+	return 1;
+}
+
 int IllusionsEngine::updateGraphics() {
 	Common::Point panPoint(0, 0);
 
 	uint32 currTime = getCurrentTime();
-    _camera->update(currTime);
-    
-    // TODO Move to BackgroundItems class
-    BackgroundItem *backgroundItem = _backgroundItems->findActiveBackground();
-    if (backgroundItem) {
-    	BackgroundResource *bgRes = backgroundItem->_bgRes;
-    	for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
-    		BgInfo *bgInfo = &bgRes->_bgInfos[i];
+	_camera->update(currTime);
+
+	// TODO Move to BackgroundItems class
+	BackgroundItem *backgroundItem = _backgroundItems->findActiveBackground();
+	if (backgroundItem) {
+		BackgroundResource *bgRes = backgroundItem->_bgRes;
+		for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
+			BgInfo *bgInfo = &bgRes->_bgInfos[i];
 			// TODO int16 priority = artcntrlGetPriorityFromBase(bgInfos[v7].priorityBase);
 			int16 priority = -1;
 			_screen->_drawQueue->insertSurface(backgroundItem->_surfaces[i],
@@ -261,11 +313,8 @@ int IllusionsEngine::updateGraphics() {
 		Control *control = *it;
 		Actor *actor = control->_actor;
 		
-		debug("control->_pauseCtr: %d; actor->_flags: %04X", control->_pauseCtr, actor->_flags);
-		
 		if (control->_pauseCtr == 0 && actor && (actor->_flags & 1) && !(actor->_flags & 0x0200)) {
 			Common::Point drawPosition = control->calcPosition(panPoint);
-			debug("drawPosition: %d, %d", drawPosition.x, drawPosition.y);
 			if (actor->_flags & 0x2000) {
 				Frame *frame = &(*actor->_frames)[actor->_frameIndex - 1];
 				_screen->_decompressQueue->insert(&actor->_drawFlags, frame->_flags,
@@ -296,8 +345,17 @@ int IllusionsEngine::updateGraphics() {
 			_textInfo._position, priority);
 	}
 #endif
-    
+
 	return 1;
 }
 
+int IllusionsEngine::getRandom(int max) {
+	return _random->getRandomNumber(max - 1);
+}
+
+int IllusionsEngine::convertPanXCoord(int16 x) {
+	// TODO
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index d262690..33e0a70 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -91,6 +91,11 @@ public:
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
 	Controls *_controls;
+	
+	int _resGetCtr;
+	uint32 _resGetTime;
+	bool _unpauseControlActorFlag;
+	uint32 _lastUpdateTime;
 
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 	
@@ -100,7 +105,12 @@ public:
 	void placeCursor(Control *control, uint32 sequenceId);
 	bool showCursor();
 	bool hideCursor();
+	uint32 getElapsedUpdateTime();
+	int updateActors();
+	int updateSequences();
 	int updateGraphics();
+	int getRandom(int max);
+	int convertPanXCoord(int16 x);
 
 #if 0
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 22845f0..e19d8cd 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -46,7 +46,7 @@ void SequenceOpcodes::execOpcode(Control *control, OpCall &opCall) {
 	(*_opcodes[opCall._op])(control, opCall);
 }
 
-typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, SequenceOpcodes> SequenceOpcodeI;
+typedef Common::Functor2Mem<Control*, OpCall&, void, SequenceOpcodes> SequenceOpcodeI;
 #define OPCODE(op, func) _opcodes[op] = new SequenceOpcodeI(this, &SequenceOpcodes::func);
 
 void SequenceOpcodes::initOpcodes() {
@@ -54,7 +54,24 @@ void SequenceOpcodes::initOpcodes() {
 	for (uint i = 0; i < 256; ++i)
 		_opcodes[i] = 0;
 	// Register opcodes
-	//OPCODE(42, opIncBlockCounter);
+	OPCODE(2, opSetFrameIndex);
+	OPCODE(3, opEndSequence);
+	OPCODE(5, opSetRandomFrameDelay);
+	OPCODE(6, opSetFrameSpeed);
+	OPCODE(7, opJump);
+	OPCODE(9, opGotoSequence);
+	OPCODE(11, opBeginLoop);
+	OPCODE(12, opNextLoop);
+	OPCODE(15, opJumpIfNotFacing);
+	OPCODE(28, opNotifyThreadId1);
+	OPCODE(29, opSetPathCtrY);
+	OPCODE(33, opSetPathWalkPoints);
+	OPCODE(36, opSetScaleLayer);
+	OPCODE(38, opSetPathWalkRects);
+	OPCODE(39, opSetPriority);
+	OPCODE(40, opSetPriorityLayer);
+	OPCODE(50, opPlaySound);
+	OPCODE(51, opStopSound);
 }
 
 #undef OPCODE
@@ -69,6 +86,165 @@ void SequenceOpcodes::freeOpcodes() {
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
 #define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %08X)", name);
+
+void SequenceOpcodes::opSetFrameIndex(Control *control, OpCall &opCall) {
+	ARG_INT16(frameIndex);
+	if (control->_actor->_flags & 0x80) {
+		debug("opSetFrameIndex TODO");
+		/* TODO
+		v9 = actor->field30;
+		if (*(_WORD *)v9) {
+			LOWORD(flag) = *(_WORD *)v9;
+			v6 = v6 + flag - 1;
+			actor->field30 = v9 + 2;
+		} else {
+			actor->flags &= 0xFF7Fu;
+			v10 = actor->notifyThreadId1;
+			actor->field30 = 0;
+			actor->notifyThreadId2 = 0;
+			if (v10) {
+				actor->notifyThreadId1 = 0;
+				ThreadList_notifyId__(v10);
+			}
+			actorDead = 1;
+			breakInner = 1;
+		}
+		*/
+	}
+	control->_actor->_flags &= ~0x0100;
+	if (control->_actor->_flags & 0x8000) {
+		control->appearActor();
+		control->_actor->_flags &= ~0x800;
+	}
+	control->_actor->_newFrameIndex = frameIndex;
+}
+
+void SequenceOpcodes::opEndSequence(Control *control, OpCall &opCall) {
+	control->_actor->_seqCodeIp = 0;
+	if (control->_actor->_flags & 0x0800) {
+		control->_actor->_flags &= ~0x0800;
+		control->_actor->_frames = 0;
+		control->_actor->_frameIndex = 0;
+		control->_actor->_newFrameIndex = 0;
+		// TODO _vm->_resSys->unloadResourceById(control->_actor->_sequenceId);
+	}
+	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
+	opCall._result = 1;
+}
+
+void SequenceOpcodes::opSetRandomFrameDelay(Control *control, OpCall &opCall) {
+	ARG_INT16(minFrameDelay);
+	ARG_INT16(maxFrameDelay);
+	control->_actor->_seqCodeValue3 += 0;//DEBUG minFrameDelay + _vm->getRandom(maxFrameDelay);
+}
+
+void SequenceOpcodes::opSetFrameSpeed(Control *control, OpCall &opCall) {
+	ARG_INT16(frameSpeed);
+	control->_actor->_seqCodeValue2 = frameSpeed;
+}
+
+void SequenceOpcodes::opJump(Control *control, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
+void SequenceOpcodes::opGotoSequence(Control *control, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(nextSequenceId);
+	uint32 notifyThreadId1 = control->_actor->_notifyThreadId1;
+	control->clearNotifyThreadId1();
+	if (false/*TODO control->_actor->_pathNode*/) {
+		control->startSequenceActor(nextSequenceId, 1, notifyThreadId1);
+	} else {
+		control->startSequenceActor(nextSequenceId, 2, notifyThreadId1);
+	}
+	opCall._deltaOfs = 0;
+}
+
+void SequenceOpcodes::opBeginLoop(Control *control, OpCall &opCall) {
+	ARG_INT16(loopCount);
+	control->_actor->pushSequenceStack(loopCount);
+}
+
+void SequenceOpcodes::opNextLoop(Control *control, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	int16 currLoopCount = control->_actor->popSequenceStack();
+	if (currLoopCount > 0) {
+		control->_actor->pushSequenceStack(currLoopCount - 1);
+		opCall._deltaOfs = -jumpOffs;
+	}
+}
+
+void SequenceOpcodes::opJumpIfNotFacing(Control *control, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_INT16(jumpOffs);
+	if (!(control->_actor->_facing & facing))
+		opCall._deltaOfs += jumpOffs;
+}
+
+void SequenceOpcodes::opNotifyThreadId1(Control *control, OpCall &opCall) {
+	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
+}
+
+void SequenceOpcodes::opSetPathCtrY(Control *control, OpCall &opCall) {
+	ARG_INT16(pathCtrY);
+	control->_actor->_pathCtrY = pathCtrY;
+}
+
+void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
+	ARG_INT16(pathWalkPointsIndex);
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	control->_actor->_flags |= 2;
+	// TODO control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
+}
+
+void SequenceOpcodes::opSetScaleLayer(Control *control, OpCall &opCall) {
+	ARG_INT16(scaleLayerIndex);
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	control->_actor->_flags |= 4;
+	control->_actor->_scaleLayer = bgRes->getScaleLayer(scaleLayerIndex - 1);
+	int scale = control->_actor->_scaleLayer->getScale(control->_actor->_position);
+	control->setActorScale(scale);
+}
+
+void SequenceOpcodes::opSetPathWalkRects(Control *control, OpCall &opCall) {
+	ARG_INT16(pathWalkRectsIndex);
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	control->_actor->_flags |= 0x10;
+	// TODO control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
+}
+
+void SequenceOpcodes::opSetPriority(Control *control, OpCall &opCall) {
+	ARG_INT16(priority);
+	control->_actor->_flags &= ~8;
+	control->setPriority(priority);
+}
+
+void SequenceOpcodes::opSetPriorityLayer(Control *control, OpCall &opCall) {
+	ARG_INT16(priorityLayerIndex);
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	control->_actor->_flags |= 8;
+	control->_actor->_priorityLayer = bgRes->getPriorityLayer(priorityLayerIndex - 1);
+	int priority = control->_actor->_priorityLayer->getPriority(control->_actor->_position);
+	control->setPriority(priority);
+}
+
+void SequenceOpcodes::opPlaySound(Control *control, OpCall &opCall) {
+	ARG_INT16(flags);
+	ARG_INT16(volume);
+	ARG_INT16(pan);
+	ARG_UINT32(soundEffectId);
+	if (!(flags & 1))
+		volume = 255;
+	if (!(flags & 2))
+		pan = _vm->convertPanXCoord(control->_actor->_position.x);
+	// TODO _vm->startSound(soundEffectId, volume, pan);
+}
+
+void SequenceOpcodes::opStopSound(Control *control, OpCall &opCall) {
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->stopSound(soundEffectId);
+}
 
 } // End of namespace Illusions
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index aa31a76..366fbba 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -44,7 +44,25 @@ protected:
 	void initOpcodes();
 	void freeOpcodes();
 
-	// Opcodes	
+	// Opcodes
+	void opSetFrameIndex(Control *control, OpCall &opCall);
+	void opEndSequence(Control *control, OpCall &opCall);
+	void opSetRandomFrameDelay(Control *control, OpCall &opCall);
+	void opSetFrameSpeed(Control *control, OpCall &opCall);	
+	void opJump(Control *control, OpCall &opCall);
+	void opGotoSequence(Control *control, OpCall &opCall);
+	void opBeginLoop(Control *control, OpCall &opCall);
+	void opNextLoop(Control *control, OpCall &opCall);
+	void opJumpIfNotFacing(Control *control, OpCall &opCall);
+	void opNotifyThreadId1(Control *control, OpCall &opCall);
+	void opSetPathCtrY(Control *control, OpCall &opCall);
+	void opSetPathWalkPoints(Control *control, OpCall &opCall);
+	void opSetScaleLayer(Control *control, OpCall &opCall);
+	void opSetPathWalkRects(Control *control, OpCall &opCall);
+	void opSetPriority(Control *control, OpCall &opCall);
+	void opSetPriorityLayer(Control *control, OpCall &opCall);
+	void opPlaySound(Control *control, OpCall &opCall);
+	void opStopSound(Control *control, OpCall &opCall);	
 	
 };
 
diff --git a/engines/illusions/spritedecompressqueue.cpp b/engines/illusions/spritedecompressqueue.cpp
index acb4547..c20b0db 100644
--- a/engines/illusions/spritedecompressqueue.cpp
+++ b/engines/illusions/spritedecompressqueue.cpp
@@ -55,11 +55,20 @@ void SpriteDecompressQueue::decompressAll() {
 void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
 	byte *src = item->_compressedPixels;
 	Graphics::Surface *dstSurface = item->_surface;
-	int dstSize = item->_dimensions._height * item->_dimensions._width;
+	int dstSize = item->_dimensions._width * item->_dimensions._height;
 	int processedSize = 0;
 	int xincr, x, xstart;
 	int yincr, y;
-
+	
+	// Safeguard
+	if (item->_dimensions._width > item->_surface->w ||
+		item->_dimensions._height > item->_surface->h) {
+		debug("Incorrect frame dimensions (%d, %d <> %d, %d)",
+			item->_dimensions._width, item->_dimensions._height,
+			item->_surface->w, item->_surface->h);
+		return;
+	}
+	
 	if (item->_flags & 1) {
 		x = xstart = item->_dimensions._width - 1;
 		xincr = -1;


Commit: 48ef46c02dfeb34706f1060f9443bb31c1a56093
    https://github.com/scummvm/scummvm/commit/48ef46c02dfeb34706f1060f9443bb31c1a56093
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement more script opcodes and related functions

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/scriptthread.cpp
    engines/illusions/thread.cpp


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 2a92d94..50e1bfa 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -138,19 +138,28 @@ Common::Error IllusionsEngine::run() {
 #endif
 
 #if 1
-	// Actor/graphics test
+	// Actor/graphics/script test
 
 	/* TODO 0x0010000B LinkIndex 0x00060AAB 0x00060556
 	*/	
 	
+	_resSys->loadResource(0x000D0001, 0, 0);
 	_resSys->loadResource(0x0011000B, 0, 0);
 	_resSys->loadResource(0x0010000B, 0, 0);
+
+#if 0
 	_controls->placeActor(0x00050009, Common::Point(0, 0), 0x00060573, 0x00040001, 0);
 	Control *control = *_controls->_controls.begin();
 	control->setActorFrameIndex(1);
 	control->appearActor();
+#endif
+
+	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
+
 	//_camera->panToPoint(Common::Point(800, 0), 500, 0);
+
 	while (!shouldQuit()) {
+		_scriptMan->_threads->updateThreads();
 		updateActors();
 		updateSequences();
 		updateGraphics();
@@ -358,4 +367,13 @@ int IllusionsEngine::convertPanXCoord(int16 x) {
 	return 0;
 }
 
+Common::Point IllusionsEngine::getNamedPointPosition(uint32 namedPointId) {
+	// TODO
+	return Common::Point(320, 240);
+}
+
+void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
+	
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 33e0a70..797d8a3 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -111,6 +111,8 @@ public:
 	int updateGraphics();
 	int getRandom(int max);
 	int convertPanXCoord(int16 x);
+	Common::Point getNamedPointPosition(uint32 namedPointId);
+	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
 
 #if 0
 
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 6f14f8d..6cb55ec 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -61,6 +61,11 @@ bool Screen::isDisplayOn() {
 	return _displayOn;
 }
 
+void Screen::setDisplayOn(bool isOn) {
+	_displayOn = isOn;
+	// TODO Clear screen when off
+}
+
 uint16 Screen::getColorKey2() {
 	return _colorKey2;
 }
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index f0e1dbf..ef0f50a 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -38,6 +38,7 @@ public:
 	Graphics::Surface *allocSurface(int16 width, int16 height);
 	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
 	bool isDisplayOn();
+	void setDisplayOn(bool isOn);
 	uint16 getColorKey2();
 	void updateSprites();
 	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 25b8d2e..95e4ec4 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -157,6 +157,20 @@ uint32 ScriptMan::startTempScriptThread(byte *scriptCodeIp, uint32 callingThread
 	return tempThreadId;
 }
 
+void ScriptMan::setCurrFontId(uint32 fontId) {
+	_fontId = fontId;
+}
+
+bool ScriptMan::enterScene(uint32 sceneId, uint32 threadId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (!progInfo) {
+		// TODO dumpActiveScenes(_someSceneId2, threadId);
+		sceneId = _theSceneId;
+	}
+	_activeScenes.push(sceneId);
+	return progInfo != 0;
+}
+
 void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
 	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId,
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index 031fa19..61ad18e 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -77,6 +77,8 @@ public:
 		uint32 value8, uint32 valueC, uint32 value10);
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
+	void setCurrFontId(uint32 fontId);
+	bool enterScene(uint32 sceneId, uint32 threadId);
 public:
 
 	IllusionsEngine *_vm;
@@ -92,6 +94,8 @@ public:
 	bool _doScriptThreadInit;
 	uint32 _nextTempThreadId;
 	
+	uint32 _fontId;
+	
 	ThreadList *_threads;
 	ScriptOpcodes *_scriptOpcodes;
 	
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 392170d..1f6c354 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -22,6 +22,9 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptopcodes.h"
+#include "illusions/actor.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
 #include "illusions/scriptman.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
@@ -76,8 +79,20 @@ void ScriptOpcodes::initOpcodes() {
 	for (uint i = 0; i < 256; ++i)
 		_opcodes[i] = 0;
 	// Register opcodes
+	OPCODE(2, opSuspend);
+	OPCODE(3, opYield);
+	OPCODE(6, opStartScriptThread);
+	OPCODE(16, opLoadResource);
+	OPCODE(20, opEnterScene);
+	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
+	OPCODE(46, opPlaceActor);
+	OPCODE(87, opDeactivateButton);
+	OPCODE(88, opActivateButton);
 	OPCODE(126, opDebug126);
+	OPCODE(144, opPlayVideo);
+	OPCODE(175, opSetSceneIdThreadId);
+	OPCODE(177, opSetFontId);
 }
 
 #undef OPCODE
@@ -92,7 +107,48 @@ void ScriptOpcodes::freeOpcodes() {
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
 #define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %08X)", name);
+
+void ScriptOpcodes::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes::opYield(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_scriptMan->startScriptThread(threadId, opCall._threadId,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	uint32 sceneId = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
+}
+
+void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	uint scenesCount = _vm->_scriptMan->_activeScenes.getActiveScenesCount();
+	if (scenesCount > 0) {
+		uint32 currSceneId;
+		_vm->_scriptMan->_activeScenes.getActiveSceneInfo(scenesCount - 1, &currSceneId, 0);
+		// TODO krnfileDump(currSceneId);
+	}
+	if (!_vm->_scriptMan->enterScene(sceneId, opCall._threadId))
+		opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flag);
+	_vm->_screen->setDisplayOn(flag != 0);
+}
 
 void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(index)	
@@ -101,8 +157,53 @@ void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall
 		_vm->_scriptMan->_scriptResource->_blockCounters.set(index + 1, value);
 }
 
+void ScriptOpcodes::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(actorTypeId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
+}
+
+void ScriptOpcodes::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->deactivateButton(button);
+}
+
+void ScriptOpcodes::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->activateButton(button);
+}
+
 void ScriptOpcodes::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Prints some debug text
 }
 
+void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(videoId);
+	ARG_UINT32(priority);
+	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
+	
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._threadId);
+	
+}
+
+void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->_scriptMan->setSceneIdThreadId(sceneId, threadId);
+}
+
+void ScriptOpcodes::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(fontId);
+	_vm->_scriptMan->setCurrFontId(fontId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 3f79ea5..008108e 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -56,9 +56,21 @@ protected:
 	void initOpcodes();
 	void freeOpcodes();
 
-	// Opcodes	
+	// Opcodes
+	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
+	void opYield(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
 	
 };
 
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 545e79b..3a08e50 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -212,4 +212,10 @@ byte *ScriptResource::getThreadCode(uint32 threadId) {
 	return _data + _codeOffsets[(threadId & 0xFFFF) - 1];
 }
 
+ProgInfo *ScriptResource::getProgInfo(uint32 index) {
+	if (index > 0 && index <= _progInfosCount)
+		return &_progInfos[index - 1];
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index d659f43..8a689a9 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -99,6 +99,7 @@ public:
 	~ScriptResource();
 	void load(byte *data, uint32 dataSize);
 	byte *getThreadCode(uint32 threadId);
+	ProgInfo *getProgInfo(uint32 index);
 public:
 	byte *_data;
 	uint32 _dataSize;
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 6455cfb..7a37633 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -39,14 +39,14 @@ ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingT
 int ScriptThread::onUpdate() {
 	OpCall opCall;
 	opCall._result = kTSRun;
-	while (!_terminated && opCall._result == 4) {
+	while (!_terminated && opCall._result == kTSRun) {
 		opCall._op = _scriptCodeIp[0];
 		opCall._opSize = _scriptCodeIp[1] >> 1;
 		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
 		opCall._code = _scriptCodeIp + 2;
-		opCall._deltaOfs = 0;
+		opCall._deltaOfs = opCall._opSize;
 		execOpcode(opCall);
-		_scriptCodeIp += opCall._opSize + opCall._deltaOfs;
+		_scriptCodeIp += opCall._deltaOfs;
 	}
 	if (_terminated)
 		opCall._result = kTSTerminate;
@@ -55,22 +55,27 @@ int ScriptThread::onUpdate() {
 
 void ScriptThread::onSuspend() {
 	// TODO
+	debug("ScriptThread::onSuspend()");
 }
 
 void ScriptThread::onNotify() {
 	// TODO
+	debug("ScriptThread::onNotify()");
 }
 
 void ScriptThread::onPause() {
 	// TODO
+	debug("ScriptThread::onPause()");
 }
 
 void ScriptThread::onResume() {
 	// TODO
+	debug("ScriptThread::onResume()");
 }
 
 void ScriptThread::onTerminated() {
 	// TODO
+	debug("ScriptThread::onTerminated()");
 }
 
 void ScriptThread::execOpcode(OpCall &opCall) {
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 0bfb82e..a875585 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -91,6 +91,7 @@ int Thread::update() {
 	int status = kTSYield;
 	if (!_terminated && _pauseCtr <= 0) {
 		status = onUpdate();
+		debug("Thread status: %d", status);
 		if (status == kTSTerminate)
 			terminate();
 		else if (status == kTSSuspend)


Commit: c99f40c13d945060f489693d3f533b6ce0b54adb
    https://github.com/scummvm/scummvm/commit/c99f40c13d945060f489693d3f533b6ce0b54adb
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement TimerThread and script opcode

Changed paths:
  A engines/illusions/timerthread.cpp
  A engines/illusions/timerthread.h
    engines/illusions/module.mk
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptthread.cpp
    engines/illusions/thread.cpp
    engines/illusions/time.cpp
    engines/illusions/time.h


diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index eab667e..de801b3 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS := \
 	spritedrawqueue.o \
 	thread.o \
 	time.o \
+	timerthread.o \
 	updatefunctions.o
 
 # This module can be built as a plugin
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 95e4ec4..d94a323 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -24,6 +24,7 @@
 #include "illusions/scriptman.h"
 #include "illusions/scriptthread.h"
 #include "illusions/scriptopcodes.h"
+#include "illusions/timerthread.h"
 
 namespace Illusions {
 
@@ -157,6 +158,14 @@ uint32 ScriptMan::startTempScriptThread(byte *scriptCodeIp, uint32 callingThread
 	return tempThreadId;
 }
 
+uint32 ScriptMan::startAbortableTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, true);
+}
+
+uint32 ScriptMan::startTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, false);
+}
+
 void ScriptMan::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
@@ -173,8 +182,8 @@ bool ScriptMan::enterScene(uint32 sceneId, uint32 threadId) {
 
 void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
-	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId,
-		notifyFlags, scriptCodeIp, value8, valueC, value10);
+	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId, notifyFlags,
+		scriptCodeIp, value8, valueC, value10);
 	_threads->startThread(scriptThread);
 	if (_pauseCtr > 0)
 		scriptThread->pause();
@@ -185,6 +194,14 @@ void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint no
 	}
 }
 
+uint32 ScriptMan::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
+	uint32 tempThreadId = newTempThreadId();
+	TimerThread *timerThread = new TimerThread(_vm, tempThreadId, callingThreadId, 0,
+		duration, isAbortable);
+	_threads->startThread(timerThread);
+	return tempThreadId;
+}
+
 uint32 ScriptMan::newTempThreadId() {
 	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
 	if (threadId > 65535) {
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index 61ad18e..f80101f 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -77,6 +77,8 @@ public:
 		uint32 value8, uint32 valueC, uint32 value10);
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
+	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
+	uint32 startTimerThread(uint32 duration, uint32 threadId);
 	void setCurrFontId(uint32 fontId);
 	bool enterScene(uint32 sceneId, uint32 threadId);
 public:
@@ -101,6 +103,7 @@ public:
 	
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
+	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
 	uint32 newTempThreadId();
 
 };
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 1f6c354..13cb8aa 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -82,6 +82,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(2, opSuspend);
 	OPCODE(3, opYield);
 	OPCODE(6, opStartScriptThread);
+	OPCODE(9, opStartTimerThread);
 	OPCODE(16, opLoadResource);
 	OPCODE(20, opEnterScene);
 	OPCODE(39, opSetDisplay);
@@ -124,6 +125,18 @@ void ScriptOpcodes::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCa
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
 }
 
+void ScriptOpcodes::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(isAbortable);
+	ARG_INT16(duration);
+	ARG_INT16(maxDuration);
+	if (maxDuration)
+		duration += _vm->getRandom(maxDuration);
+	if (isAbortable)
+		_vm->_scriptMan->startAbortableTimerThread(duration, opCall._threadId);
+	else
+		_vm->_scriptMan->startTimerThread(duration, opCall._threadId);
+}
+
 void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(resourceId);
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 008108e..d812d58 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -60,6 +60,7 @@ protected:
 	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
 	void opYield(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 7a37633..0f32a6f 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -34,6 +34,7 @@ ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingT
 	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptCodeIp(scriptCodeIp), _value8(value8),
 	_valueC(valueC), _value10(value10), _sequenceStalled(0) {
 	_type = kTTScriptThread;
+	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
 }
 
 int ScriptThread::onUpdate() {
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index a875585..e398c33 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -102,12 +102,9 @@ int Thread::update() {
 
 void Thread::terminate() {
 	if (!_terminated) {
-		if (_callingThreadId) {
-			if (!(_notifyFlags & 1)) {
-				// TODO scrmgrNotifyID(_callingThreadId);
-			}
-			_callingThreadId = 0;
-		}
+		if (!(_notifyFlags & 1))
+			_vm->notifyThreadId(_callingThreadId);
+		_callingThreadId = 0;
 		onTerminated();
 		// TODO _vm->removeThread(_threadId, this);
 		_terminated = true;
diff --git a/engines/illusions/time.cpp b/engines/illusions/time.cpp
index f190035..34ceaf4 100644
--- a/engines/illusions/time.cpp
+++ b/engines/illusions/time.cpp
@@ -26,7 +26,7 @@
 namespace Illusions {
 
 uint32 getCurrentTime() {
-	return g_system->getMillis() / 60;
+	return g_system->getMillis() / 16;
 }
 
 bool isTimerExpired(uint32 startTime, uint32 endTime) {
@@ -36,4 +36,15 @@ bool isTimerExpired(uint32 startTime, uint32 endTime) {
 		(startTime < endTime && currTime <= endTime && currTime >= startTime));
 }
 
+uint32 getDurationElapsed(uint32 startTime, uint32 endTime) {
+	uint32 currTime = getCurrentTime();
+	uint32 elapsed = endTime - startTime;
+	if (isTimerExpired(startTime, endTime))
+		return elapsed;
+	else if (startTime < endTime || currTime > startTime)
+		return currTime - startTime;
+	else
+		return currTime + elapsed - endTime;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/time.h b/engines/illusions/time.h
index c09b436..67ff427 100644
--- a/engines/illusions/time.h
+++ b/engines/illusions/time.h
@@ -29,6 +29,7 @@ namespace Illusions {
 
 uint32 getCurrentTime();
 bool isTimerExpired(uint32 startTime, uint32 endTime);
+uint32 getDurationElapsed(uint32 startTime, uint32 endTime);
 
 } // End of namespace Illusions
 
diff --git a/engines/illusions/timerthread.cpp b/engines/illusions/timerthread.cpp
new file mode 100644
index 0000000..5263305
--- /dev/null
+++ b/engines/illusions/timerthread.cpp
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/timerthread.h"
+#include "illusions/input.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// TimerThread
+
+TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 duration, bool isAbortable)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _duration(duration), _isAbortable(isAbortable) {
+	_type = kTTTimerThread;
+	_startTime = getCurrentTime();
+	_endTime = _startTime + _duration;
+	// TODO _tag = *(_DWORD *)(krndictGetIDValue(callingThreadId) + 20);
+}
+
+int TimerThread::onUpdate() {
+debug("startTime: %d; endTime: %d; currTime: %d", _startTime, _endTime, getCurrentTime());
+	if (isTimerExpired(_startTime, _endTime) ||
+		(_isAbortable && _vm->_input->pollButton(8)))
+		return kTSTerminate;
+	return kTSYield;
+}
+
+void TimerThread::onSuspend() {
+	_durationElapsed = getDurationElapsed(_startTime, _endTime);
+}
+
+void TimerThread::onNotify() {
+	uint32 currTime = getCurrentTime();
+	_startTime = currTime;
+	if (_duration <= _durationElapsed)
+		_endTime = currTime;
+	else
+		_endTime = currTime + _duration - _durationElapsed;
+	_durationElapsed = 0;
+}
+
+void TimerThread::onPause() {
+	onSuspend();
+}
+
+void TimerThread::onResume() {
+	onNotify();
+}
+
+void TimerThread::onTerminated() {
+	// Empty
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/timerthread.h b/engines/illusions/timerthread.h
new file mode 100644
index 0000000..d283dc4
--- /dev/null
+++ b/engines/illusions/timerthread.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TIMERTHREAD_H
+#define ILLUSIONS_TIMERTHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class TimerThread : public Thread {
+public:
+	TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 duration, bool isAbortable);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	uint32 _startTime, _endTime;
+	uint32 _duration, _durationElapsed;
+	bool _isAbortable;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TIMERTHREAD_H


Commit: f47575ca906682bab942d19a36cc33ea7465a4b2
    https://github.com/scummvm/scummvm/commit/f47575ca906682bab942d19a36cc33ea7465a4b2
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add more script opcodes and related

Changed paths:
  A engines/illusions/fontresource.cpp
  A engines/illusions/fontresource.h
  A engines/illusions/soundresource.cpp
  A engines/illusions/soundresource.h
  A engines/illusions/talkresource.cpp
  A engines/illusions/talkresource.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptthread.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/spritedrawqueue.cpp
    engines/illusions/thread.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index e1aa5f7..ad91b02 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -73,6 +73,7 @@ Actor::Actor(IllusionsEngine *vm)
 	_position2.y = 0;
 	_facing = 64;
 	_fontId = 0;
+	_actorIndex = 0;
 	_parentObjectId = 0;
 	_linkIndex = 0;
 	_linkIndex2 = 0;
@@ -104,7 +105,6 @@ Actor::Actor(IllusionsEngine *vm)
 	_pathInitialPosFlag = 1;
 	_pathInitialPos.x = 0;
 	_pathInitialPos.y = 0;
-	_actorIndex = 0;
 	_namedPointsCount = 0;
 	_namedPoints = 0;
 	_field164 = 0;
@@ -267,8 +267,8 @@ void Control::disappearActor() {
 			_actor->_flags &= ~1;
 		}
 	} else {
-		_actor->_flags |= ~1;
-		_actor->_flags |= ~0x1000;
+		_actor->_flags &= ~1;
+		_actor->_flags &= ~0x1000;
 		for (uint i = 0; i < kSubObjectsCount; ++i)
 			if (_actor->_subobjects[i]) {
 				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
@@ -302,20 +302,20 @@ void Control::deactivateObject() {
 void Control::readPointsConfig(byte *pointsConfig) {
 	_unkPt.x = READ_LE_UINT16(pointsConfig + 0);
 	_unkPt.y = READ_LE_UINT16(pointsConfig + 2);
-	pointsConfig += 2;
+	pointsConfig += 4;
 	_pt.x = READ_LE_UINT16(pointsConfig + 0);
 	_pt.y = READ_LE_UINT16(pointsConfig + 2);
-	pointsConfig += 2;
+	pointsConfig += 4;
 	_feetPt.x = READ_LE_UINT16(pointsConfig + 0);
 	_feetPt.y = READ_LE_UINT16(pointsConfig + 2);
-	pointsConfig += 2;
+	pointsConfig += 4;
 	_position.x = READ_LE_UINT16(pointsConfig + 0);
 	_position.y = READ_LE_UINT16(pointsConfig + 2);
-	pointsConfig += 2;
+	pointsConfig += 4;
 	for (uint i = 0; i < kSubObjectsCount; ++i) {
 		_subobjectsPos[i].x = READ_LE_UINT16(pointsConfig + 0);
 		_subobjectsPos[i].y = READ_LE_UINT16(pointsConfig + 2);
-		pointsConfig += 2;
+		pointsConfig += 4;
 	}
 }
 
@@ -553,7 +553,7 @@ void Control::sequenceActor() {
 	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
 		bool breakInner = false;
 		while (!breakInner) {
-			debug("op: %08X", _actor->_seqCodeIp[0]);
+			debug("SEQ op: %08X", _actor->_seqCodeIp[0]);
 			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
 			opCall._opSize = _actor->_seqCodeIp[1];
 			opCall._code = _actor->_seqCodeIp + 2;
@@ -608,8 +608,6 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 	
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
-	debug("Control::startSequenceActorIntern() _actor->_seqCodeIp = %p", (void*)_actor->_seqCodeIp);
-	debug("Control::startSequenceActorIntern() _actor->_frames = %p", (void*)_actor->_frames);
 	
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
@@ -801,7 +799,6 @@ void Controls::destroyControl(Control *control) {
 }
 
 void Controls::actorControlRouine(Control *control, uint32 deltaTime) {
-	//debug("Controls::actorControlRouine()");
 
 	Actor *actor = control->_actor;
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 6a682c6..b37e131 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -106,6 +106,7 @@ public:
 	uint _facing;
 	
 	uint32 _fontId;
+	int16 _actorIndex;
 	
 	DefaultSequences _defaultSequences;
 
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
new file mode 100644
index 0000000..e0f48d0
--- /dev/null
+++ b/engines/illusions/fontresource.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/fontresource.h"
+
+namespace Illusions {
+
+// FontResourceLoader
+
+void FontResourceLoader::load(Resource *resource) {
+	debug("FontResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+    // TODO
+	
+}
+
+void FontResourceLoader::unload(Resource *resource) {
+}
+
+void FontResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
+}
+
+bool FontResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/fontresource.h b/engines/illusions/fontresource.h
new file mode 100644
index 0000000..66860bb
--- /dev/null
+++ b/engines/illusions/fontresource.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_FONTRESOURCE_H
+#define ILLUSIONS_FONTRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class FontResourceLoader : public BaseResourceLoader {
+public:
+	FontResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~FontResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_FONTRESOURCE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 50e1bfa..1d591c7 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -21,20 +21,23 @@
  */
 
 #include "illusions/illusions.h"
-#include "illusions/resourcesystem.h"
+#include "illusions/actor.h"
+#include "illusions/actorresource.h"
 #include "illusions/backgroundresource.h"
 #include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/fontresource.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
-#include "illusions/updatefunctions.h"
-#include "illusions/actor.h"
-#include "illusions/actorresource.h"
-#include "illusions/thread.h"
+#include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
+#include "illusions/soundresource.h"
+#include "illusions/talkresource.h"
+#include "illusions/thread.h"
 #include "illusions/time.h"
-#include "illusions/dictionary.h"
+#include "illusions/updatefunctions.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
@@ -85,9 +88,12 @@ Common::Error IllusionsEngine::run() {
 
 	_resSys = new ResourceSystem();
 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
+	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
 
 	_screen = new Screen(this);
 	_input = new Input();	
@@ -180,6 +186,8 @@ Common::Error IllusionsEngine::run() {
 	delete _resSys;
 	delete _dict;
 	
+	debug("Ok");
+	
 	return Common::kNoError;
 }
 
@@ -369,7 +377,7 @@ int IllusionsEngine::convertPanXCoord(int16 x) {
 
 Common::Point IllusionsEngine::getNamedPointPosition(uint32 namedPointId) {
 	// TODO
-	return Common::Point(320, 240);
+	return Common::Point(0, 0);
 }
 
 void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index de801b3..9121343 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
 	detection.o \
 	dictionary.o \
 	fixedpoint.o \
+	fontresource.o \
 	graphics.o \
 	illusions.o \
 	input.o \
@@ -18,8 +19,10 @@ MODULE_OBJS := \
 	scriptresource.o \
 	scriptthread.o \
 	sequenceopcodes.o \
+	soundresource.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
+	talkresource.o \
 	thread.o \
 	time.o \
 	timerthread.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 6cb55ec..1a80a33 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -87,7 +87,7 @@ void Screen::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface,
 
 void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
 	// Unscaled, non-transparent
-	debug(1, "Screen::drawSurface11() destX: %d; destY: %d; srcRect: (%d, %d, %d, %d)", destX, destY, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom);
+	debug("Screen::drawSurface11() destX: %d; destY: %d; srcRect: (%d, %d, %d, %d)", destX, destY, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom);
 	const int16 w = srcRect.width();
 	const int16 h = srcRect.height();
 	for (int16 yc = 0; yc < h; ++yc) {
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index f80101f..da0f6b6 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -101,6 +101,8 @@ public:
 	ThreadList *_threads;
 	ScriptOpcodes *_scriptOpcodes;
 	
+	uint32 _callerThreadId;
+	
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
 	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 13cb8aa..72c455a 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/actor.h"
+#include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
 #include "illusions/scriptman.h"
@@ -67,7 +68,7 @@ ScriptOpcodes::~ScriptOpcodes() {
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug("execOpcode(%d)", opCall._op);
+	debug("\nexecOpcode(%d)", opCall._op);
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
@@ -81,19 +82,38 @@ void ScriptOpcodes::initOpcodes() {
 	// Register opcodes
 	OPCODE(2, opSuspend);
 	OPCODE(3, opYield);
+	OPCODE(4, opTerminate);
+	OPCODE(5, opJump);
 	OPCODE(6, opStartScriptThread);
+	OPCODE(8, opStartTempScriptThread);
 	OPCODE(9, opStartTimerThread);
+	OPCODE(14, opSetThreadSceneId);
 	OPCODE(16, opLoadResource);
 	OPCODE(20, opEnterScene);
 	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
-	OPCODE(46, opPlaceActor);
+	OPCODE(45, opSetProperty);
+	OPCODE(46, opPlaceActor);	
+	OPCODE(49, opStartSequenceActor);
+	OPCODE(57, opAppearActor);
+	OPCODE(58, opDisappearActor);
+	OPCODE(63, opSetSelectSfx);
+	OPCODE(64, opSetMoveSfx);
+	OPCODE(65, opSetDenySfx);
+	OPCODE(66, opSetAdjustUpSfx);
+	OPCODE(67, opSetAdjustDnSfx);
+	OPCODE(75, opStartMusic);
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
+	OPCODE(103, opJumpIf);
 	OPCODE(126, opDebug126);
 	OPCODE(144, opPlayVideo);
+	OPCODE(148, opLoadSpecialCodeModule);
+	OPCODE(150, opRunSpecialCode);
 	OPCODE(175, opSetSceneIdThreadId);
+	OPCODE(176, opStackPush0);
 	OPCODE(177, opSetFontId);
+	OPCODE(178, opAddMenuKey);
 }
 
 #undef OPCODE
@@ -118,6 +138,15 @@ void ScriptOpcodes::opYield(ScriptThread *scriptThread, OpCall &opCall) {
 	opCall._result = kTSYield;
 }
 
+void ScriptOpcodes::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes::opJump(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
 void ScriptOpcodes::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(threadId);
@@ -125,18 +154,33 @@ void ScriptOpcodes::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCa
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
 }
 
+void ScriptOpcodes::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(codeOffs);
+	_vm->_scriptMan->startTempScriptThread(opCall._code + codeOffs,
+		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
 void ScriptOpcodes::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(isAbortable);
 	ARG_INT16(duration);
 	ARG_INT16(maxDuration);
 	if (maxDuration)
 		duration += _vm->getRandom(maxDuration);
+		
+duration = 5;//DEBUG Speeds up things		
+		
 	if (isAbortable)
 		_vm->_scriptMan->startAbortableTimerThread(duration, opCall._threadId);
 	else
 		_vm->_scriptMan->startTimerThread(duration, opCall._threadId);
 }
 
+void ScriptOpcodes::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_scriptMan->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
+}
+
 void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(resourceId);
@@ -170,6 +214,12 @@ void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall
 		_vm->_scriptMan->_scriptResource->_blockCounters.set(index + 1, value);
 }
 
+void ScriptOpcodes::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value)	
+	ARG_INT16(propertyId)	
+	// TODO _vm->_scriptMan->_scriptResource->_properties.set(propertyId, value);
+}
+
 void ScriptOpcodes::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -180,6 +230,73 @@ void ScriptOpcodes::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
 }
 
+void ScriptOpcodes::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+void ScriptOpcodes::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (!control) {
+		Common::Point pos = _vm->getNamedPointPosition(0x70023);
+        _vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
+        control = _vm->_dict->getObjectControl(objectId);
+        control->startSequenceActor(0x60001, 2, 0);
+	}
+	control->appearActor();
+}
+
+void ScriptOpcodes::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->disappearActor();
+}
+
+void ScriptOpcodes::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setSelectSfx(soundEffectId);
+}
+
+void ScriptOpcodes::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setMoveSfx(soundEffectId);
+}
+
+void ScriptOpcodes::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setDenySfx(soundEffectId);
+}
+
+void ScriptOpcodes::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustUpSfx(soundEffectId);
+}
+
+void ScriptOpcodes::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustDnSfx(soundEffectId);
+}
+void ScriptOpcodes::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(volume)
+	ARG_INT16(pan)
+	ARG_UINT32(musicId);
+	ARG_UINT32(type);
+	// TODO _vm->playMusic(musicId, type, volume, pan);
+}
+
 void ScriptOpcodes::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(button)
 	_vm->_input->deactivateButton(button);
@@ -190,8 +307,16 @@ void ScriptOpcodes::opActivateButton(ScriptThread *scriptThread, OpCall &opCall)
 	_vm->_input->activateButton(button);
 }
 
+void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs)
+	int16 value = _vm->_scriptMan->_stack.pop();
+	if (!value)
+		opCall._deltaOfs += jumpOffs;
+}
+
 void ScriptOpcodes::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Prints some debug text
+	debug("[DBG] %s", (char*)opCall._code);
 }
 
 void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
@@ -206,6 +331,24 @@ void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
 	
 }
 
+void ScriptOpcodes::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeModuleId);
+	// TODO _vm->loadSpecialCodeModule(specialCodeModuleId);
+}
+
+void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeId);
+	_vm->_scriptMan->_callerThreadId = opCall._callerThreadId;
+	// TODO _vm->runSpecialCode(specialCodeId, opCall._code + 8, opCall._threadId);
+	_vm->_scriptMan->_callerThreadId = 0;
+
+	//DEBUG Resume calling thread, later done by the special code
+	_vm->notifyThreadId(opCall._threadId);
+
+}
+
 void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -213,10 +356,21 @@ void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opC
 	_vm->_scriptMan->setSceneIdThreadId(sceneId, threadId);
 }
 
+void ScriptOpcodes::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_scriptMan->_stack.push(0);
+}
+
 void ScriptOpcodes::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(fontId);
 	_vm->_scriptMan->setCurrFontId(fontId);
 }
 
+void ScriptOpcodes::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(key);
+	ARG_UINT32(threadId);
+	// TODO _vm->addMenuKey(key, threadId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index d812d58..4d14010 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -34,6 +34,7 @@ struct OpCall {
 	byte _op;
 	byte _opSize;
 	uint32 _threadId;
+	uint32 _callerThreadId;
 	int16 _deltaOfs;
 	byte *_code;
 	int _result;
@@ -59,19 +60,38 @@ protected:
 	// Opcodes
 	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
 	void opYield(ScriptThread *scriptThread, OpCall &opCall);
+	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
+	void opJump(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
+	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
 	
 };
 
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 0f32a6f..b21b003 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -40,6 +40,7 @@ ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingT
 int ScriptThread::onUpdate() {
 	OpCall opCall;
 	opCall._result = kTSRun;
+	opCall._callerThreadId = _threadId;
 	while (!_terminated && opCall._result == kTSRun) {
 		opCall._op = _scriptCodeIp[0];
 		opCall._opSize = _scriptCodeIp[1] >> 1;
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index e19d8cd..4eaa7a9 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -62,7 +62,8 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(9, opGotoSequence);
 	OPCODE(11, opBeginLoop);
 	OPCODE(12, opNextLoop);
-	OPCODE(15, opJumpIfNotFacing);
+	OPCODE(14, opSwitchActorIndex);
+	OPCODE(15, opSwitchFacing);
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
 	OPCODE(33, opSetPathWalkPoints);
@@ -176,7 +177,15 @@ void SequenceOpcodes::opNextLoop(Control *control, OpCall &opCall) {
 	}
 }
 
-void SequenceOpcodes::opJumpIfNotFacing(Control *control, OpCall &opCall) {
+void SequenceOpcodes::opSwitchActorIndex(Control *control, OpCall &opCall) {
+	ARG_INT16(actorIndex);
+	ARG_INT16(jumpOffs);
+	debug("control->_objectId = %08X", control->_objectId);
+	if (control->_actor->_actorIndex != actorIndex)
+		opCall._deltaOfs += jumpOffs;
+}
+
+void SequenceOpcodes::opSwitchFacing(Control *control, OpCall &opCall) {
 	ARG_INT16(facing);
 	ARG_INT16(jumpOffs);
 	if (!(control->_actor->_facing & facing))
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 366fbba..31827aa 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -53,7 +53,8 @@ protected:
 	void opGotoSequence(Control *control, OpCall &opCall);
 	void opBeginLoop(Control *control, OpCall &opCall);
 	void opNextLoop(Control *control, OpCall &opCall);
-	void opJumpIfNotFacing(Control *control, OpCall &opCall);
+	void opSwitchActorIndex(Control *control, OpCall &opCall);
+	void opSwitchFacing(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
 	void opSetPathWalkPoints(Control *control, OpCall &opCall);
diff --git a/engines/illusions/soundresource.cpp b/engines/illusions/soundresource.cpp
new file mode 100644
index 0000000..e5cc5dd
--- /dev/null
+++ b/engines/illusions/soundresource.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/soundresource.h"
+
+namespace Illusions {
+
+// SoundGroupResourceLoader
+
+void SoundGroupResourceLoader::load(Resource *resource) {
+	debug("SoundGroupResourceLoader::load() Loading sound group %08X...", resource->_resId);
+
+    // TODO
+    // Load all sounds in sfx/{SoundGroupId}/
+	
+}
+
+void SoundGroupResourceLoader::unload(Resource *resource) {
+}
+
+void SoundGroupResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
+}
+
+bool SoundGroupResourceLoader::isFlag(int flag) {
+	return false;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/soundresource.h b/engines/illusions/soundresource.h
new file mode 100644
index 0000000..d278a30
--- /dev/null
+++ b/engines/illusions/soundresource.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SOUNDRESOURCE_H
+#define ILLUSIONS_SOUNDRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class SoundGroupResourceLoader : public BaseResourceLoader {
+public:
+	SoundGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~SoundGroupResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/spritedrawqueue.cpp b/engines/illusions/spritedrawqueue.cpp
index 2cffb0c..1e42d18 100644
--- a/engines/illusions/spritedrawqueue.cpp
+++ b/engines/illusions/spritedrawqueue.cpp
@@ -142,6 +142,9 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	srcRect.top = 0;
 	srcRect.right = item->_dimensions._width;
 	srcRect.bottom = item->_dimensions._height;
+	
+	debug("item->_drawPosition.x: %d; item->_drawPosition.y: %d", item->_drawPosition.x, item->_drawPosition.y);
+	debug("item->_controlPosition.x: %d; item->_controlPosition.y: %d", item->_controlPosition.x, item->_controlPosition.y);
 
 	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
 	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
new file mode 100644
index 0000000..d0fa607
--- /dev/null
+++ b/engines/illusions/talkresource.cpp
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/talkresource.h"
+
+namespace Illusions {
+
+// TalkResourceLoader
+
+void TalkResourceLoader::load(Resource *resource) {
+	debug("TalkResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+    // TODO
+	
+}
+
+void TalkResourceLoader::unload(Resource *resource) {
+}
+
+void TalkResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.tlk", resource->_resId);
+}
+
+bool TalkResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/talkresource.h b/engines/illusions/talkresource.h
new file mode 100644
index 0000000..982d5fd
--- /dev/null
+++ b/engines/illusions/talkresource.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TALKRESOURCE_H
+#define ILLUSIONS_TALKRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class TalkResourceLoader : public BaseResourceLoader {
+public:
+	TalkResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~TalkResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKRESOURCE_H
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index e398c33..c16ecd4 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -256,4 +256,15 @@ void ThreadList::killThread(uint32 threadId) {
 
 }
 
+void ThreadList::setThreadSceneId(uint32 threadId, uint32 sceneId) {
+	Thread *thread = findThread(threadId);
+	if (thread)
+		thread->_tag = sceneId;
+}
+
+uint32 ThreadList::getThreadSceneId(uint32 threadId) {
+	Thread *thread = findThread(threadId);
+	return thread ? thread->_tag : 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index eab92ae..977262a 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -89,6 +89,8 @@ public:
 	void pauseThreads(uint32 threadId);
 	void resumeThreads(uint32 threadId);
 	void killThread(uint32 threadId);
+	void setThreadSceneId(uint32 threadId, uint32 sceneId);
+	uint32 getThreadSceneId(uint32 threadId);
 protected:
 	typedef Common::List<Thread*> List;
 	typedef List::iterator Iterator;


Commit: 43cd806f17377d5244e575b863519f013e5f033c
    https://github.com/scummvm/scummvm/commit/43cd806f17377d5244e575b863519f013e5f033c
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add more script opcodes

Changed paths:
  A engines/illusions/cursor.cpp
  A engines/illusions/cursor.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index ad91b02..5ba576b 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
+#include "illusions/cursor.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
@@ -210,7 +211,7 @@ void Control::pause() {
 	_vm->_dict->setObjectControl(_objectId, 0);
 
 	if (_objectId == 0x40004)
-		_vm->setCursorControl(0);
+		_vm->_cursor->setControl(0);
 
 	if (_actor && !(_actor->_flags & 0x0200))
 		_actor->destroySurface();
@@ -222,7 +223,7 @@ void Control::unpause() {
 	_vm->_dict->setObjectControl(_objectId, this);
 
 	if (_objectId == 0x40004)
-		_vm->setCursorControl(this);
+		_vm->_cursor->setControl(this);
   
 	if (_actor && !(_actor->_flags & 0x0200)) {
 		SurfInfo surfInfo;
@@ -238,15 +239,7 @@ void Control::unpause() {
 
 void Control::appearActor() {
 	if (_objectId == 0x40004) {
-		if (_vm->showCursor()) {
-			_flags |= 1;
-			_actor->_flags |= 1;
-			if (_actor->_frameIndex) {
-				_actor->_flags |= 0x2000;
-				_actor->_flags |= 0x4000;
-			}
-			_vm->_input->discardButtons(0xFFFF);
-		}
+		_vm->_cursor->show();
 	} else {
 		if (_actor->_frameIndex || _actorTypeId == 0x50004)
 			_actor->_flags |= 1;
@@ -262,10 +255,7 @@ void Control::appearActor() {
 
 void Control::disappearActor() {
 	if (_objectId == 0x40004) {
-		if (_vm->hideCursor()) {
-			_flags &= ~1;
-			_actor->_flags &= ~1;
-		}
+		_vm->_cursor->hide();
 	} else {
 		_actor->_flags &= ~1;
 		_actor->_flags &= ~0x1000;
@@ -653,10 +643,8 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	control->readPointsConfig(actorType->_pointsConfig);
 	control->_actorTypeId = actorTypeId;
 	control->_actor = actor;
-	/* TODO
 	if (actorTypeId == 0x50001 && objectId == 0x40004)
-		actor->setControlRoutine(Cursor_controlRoutine);
-	*/
+		actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Cursor>(_vm->_cursor, &Cursor::cursorControlRoutine));
 	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
 		actor->createSurface(actorType->_surfInfo);
 	} else {
@@ -703,7 +691,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	_vm->_dict->setObjectControl(objectId, control);
 
 	if (actorTypeId == 0x50001 && objectId == 0x40004)
-		_vm->placeCursor(control, sequenceId);
+		_vm->_cursor->place(control, sequenceId);
 
 	control->_flags |= 0x01;
 	actor->_flags |= 0x1000;
@@ -760,6 +748,17 @@ void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Commo
 	_vm->_dict->setObjectControl(objectId, control);
 }
 
+void Controls::destroyControlsByTag(uint32 tag) {
+	ItemsIterator it = _controls.begin();
+	while (it != _controls.end()) {
+		if ((*it)->_tag == tag) {
+			destroyControl(*it);
+			it = _controls.erase(it);
+		} else
+			++it;			
+	}
+}
+
 Actor *Controls::newActor() {
 	return new Actor(_vm);
 }
@@ -769,13 +768,12 @@ Control *Controls::newControl() {
 }
 
 void Controls::destroyControl(Control *control) {
-	_controls.remove(control);
 
 	if (control->_pauseCtr <= 0)
 		_vm->_dict->setObjectControl(control->_objectId, 0);
 
 	if (control->_objectId == 0x40004 && control->_pauseCtr <= 0)
-		_vm->setCursorControl(0);
+		_vm->_cursor->setControl(0);
 	
 	if (control->_actor) {
 		/* TODO
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index b37e131..5966053 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -196,6 +196,7 @@ public:
 	void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
+	void destroyControlsByTag(uint32 tag);
 	void actorControlRouine(Control *control, uint32 deltaTime);	
 public:
 	typedef Common::List<Control*> Items;
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
new file mode 100644
index 0000000..36ab97a
--- /dev/null
+++ b/engines/illusions/cursor.cpp
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/actor.h"
+#include "illusions/cursor.h"
+#include "illusions/input.h"
+
+namespace Illusions {
+
+Cursor::Cursor(IllusionsEngine *vm)
+	: _vm(vm) {
+	_status = 1;
+	_control = 0;
+	_x = 320;
+	_y = 240;
+	_cursorNum = 1;
+	_field_10 = 0;
+	_sequenceId = 0;
+}
+
+void Cursor::place(Control *control, uint32 sequenceId) {
+	_status = 2;
+	_control = control;
+	_cursorNum = 1;
+	_field_10 = 0;
+	_sequenceId = sequenceId;
+	_visibleCtr = 0;
+	_control->_flags |= 8;
+	setActorIndex(_cursorNum, 1, 0);
+	_vm->_input->setCursorPosition(_control->_actor->_position);
+}
+
+void Cursor::setActorIndex(int a2, int a3, int a4) {
+	_control->_actor->_actorIndex = 1;// TODO?!? *((_BYTE *)&stru_42C040[30].y + 2 * ((always0 != 0) + 2 * a2) + a3 + 1);
+}
+
+void Cursor::setControl(Control *control) {
+	_control = control;
+}
+
+void Cursor::show() {
+	++_visibleCtr;
+	if (_visibleCtr > 0) {
+		_control->_flags |= 1;
+		_control->_actor->_flags |= 1;
+		if (_control->_actor->_frameIndex) {
+			_control->_actor->_flags |= 0x2000;
+			_control->_actor->_flags |= 0x4000;
+		}
+		_vm->_input->discardButtons(0xFFFF);
+	}
+}
+
+void Cursor::hide() {
+	--_visibleCtr;
+	if (_visibleCtr < 0) {
+		_control->_flags &= ~1;
+		_control->_actor->_flags &= ~1;
+	}
+}
+
+void Cursor::cursorControlRoutine(Control *control, uint32 deltaTime) {
+	_control->_actor->_seqCodeValue1 = 100 * deltaTime;
+	if (_control->_actor->_flags & 1) {
+		if (_status == 2) {
+			// Unused nullsub_1(control);
+		} else if (_status == 3) {
+			// TODO _vm->_shellMgr->handleMouse(_control);
+		}
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/cursor.h b/engines/illusions/cursor.h
new file mode 100644
index 0000000..3179b71
--- /dev/null
+++ b/engines/illusions/cursor.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_CURSOR_H
+#define ILLUSIONS_CURSOR_H
+
+namespace Illusions {
+
+class Control;
+class IllusionsEngine;
+
+class Cursor {
+public:
+	Cursor(IllusionsEngine *vm);
+	void place(Control *control, uint32 sequenceId);
+	void setActorIndex(int a2, int a3, int a4);
+	void setControl(Control *control);
+	void show();
+	void hide();
+	void cursorControlRoutine(Control *control, uint32 deltaTime);
+protected:
+	IllusionsEngine *_vm;
+	Control *_control;
+	uint32 _sequenceId;
+	int _status;
+	int _cursorNum;
+	int _field_10;
+	int _visibleCtr;
+	int16 _x, _y;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_CURSOR_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 1d591c7..8966814 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -25,6 +25,7 @@
 #include "illusions/actorresource.h"
 #include "illusions/backgroundresource.h"
 #include "illusions/camera.h"
+#include "illusions/cursor.h"
 #include "illusions/dictionary.h"
 #include "illusions/fontresource.h"
 #include "illusions/graphics.h"
@@ -102,6 +103,7 @@ Common::Error IllusionsEngine::run() {
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
 	_controls = new Controls(this);
+	_cursor = new Cursor(this);
 	
 	// TODO Move to own class
 	_resGetCtr = 0;
@@ -176,6 +178,7 @@ Common::Error IllusionsEngine::run() {
 	}
 #endif
 
+	delete _cursor;
 	delete _controls;
 	delete _camera;
 	delete _backgroundItems;
@@ -242,28 +245,6 @@ void IllusionsEngine::notifyThreadId(uint32 &threadId) {
 	}
 }
 
-void IllusionsEngine::setCursorControl(Control *control) {
-	// TODO Dummy, to be replaced later
-}
-
-void IllusionsEngine::placeCursor(Control *control, uint32 sequenceId) {
-	// TODO Dummy, to be replaced later
-}
-
-bool IllusionsEngine::showCursor() {
-	// TODO Dummy, to be replaced later
-	// TODO ++cursor._visibleCtr;
-	// TODO if (cursor._visibleCtr > 0)
-	return false;
-}
-
-bool IllusionsEngine::hideCursor() {
-	// TODO Dummy, to be replaced later
-	// TODO --cursor._visibleCtr;
-	// TODO if (cursor.visibleCtr <= 0) 
-	return false;
-}
-
 uint32 IllusionsEngine::getElapsedUpdateTime() {
 	uint32 result = 0;
 	uint32 currTime = getCurrentTime();
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 797d8a3..c82f2c1 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -57,6 +57,7 @@ class BackgroundResource;
 class Camera;
 class Control;
 class Controls;
+class Cursor;
 class Dictionary;
 class Input;
 class Screen;
@@ -91,6 +92,7 @@ public:
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
 	Controls *_controls;
+	Cursor *_cursor;
 	
 	int _resGetCtr;
 	uint32 _resGetTime;
@@ -101,10 +103,6 @@ public:
 	
 	void notifyThreadId(uint32 &threadId);
 	
-	void setCursorControl(Control *control);
-	void placeCursor(Control *control, uint32 sequenceId);
-	bool showCursor();
-	bool hideCursor();
 	uint32 getElapsedUpdateTime();
 	int updateActors();
 	int updateSequences();
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 9121343..edaa94f 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	actorresource.o \
 	backgroundresource.o \
 	camera.o \
+	cursor.o \
 	detection.o \
 	dictionary.o \
 	fixedpoint.o \
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index d94a323..527c0a5 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/illusions.h"
+#include "illusions/actor.h"
 #include "illusions/scriptman.h"
 #include "illusions/scriptthread.h"
 #include "illusions/scriptopcodes.h"
@@ -116,6 +117,10 @@ int16 ScriptStack::peek() {
 	return value;
 }
 
+int16 *ScriptStack::topPtr() {
+	return &_stack[_stackPos];
+}
+
 // ScriptMan
 
 ScriptMan::ScriptMan(IllusionsEngine *vm)
@@ -170,6 +175,12 @@ void ScriptMan::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
 
+void ScriptMan::reset() {
+	// TODO _scriptResource->_blockCounters.clear();
+	// TODO _scriptResource->_properties.clear();
+	// TODO script_sub_417FF0(1, 0);
+}
+
 bool ScriptMan::enterScene(uint32 sceneId, uint32 threadId) {
 	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
 	if (!progInfo) {
@@ -180,6 +191,17 @@ bool ScriptMan::enterScene(uint32 sceneId, uint32 threadId) {
 	return progInfo != 0;
 }
 
+void ScriptMan::exitScene(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	// TODO krnfileDump(sceneId);
+	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
+	_threads->terminateThreadsByTag(sceneId, threadId);
+	_vm->_controls->destroyControlsByTag(sceneId);
+	// TODO causeFunc_removeBySceneId(sceneId);
+	// TODO _vm->_resSys->unloadResourceByTag(sceneId);
+	_activeScenes.pop();
+}
+
 void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
 	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId, notifyFlags,
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index da0f6b6..4bbd669 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -61,6 +61,7 @@ public:
 	void push(int16 value);
 	int16 pop();
 	int16 peek();
+	int16 *topPtr();
 protected:
 	int _stackPos;
 	int16 _stack[256];
@@ -80,7 +81,9 @@ public:
 	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
 	uint32 startTimerThread(uint32 duration, uint32 threadId);
 	void setCurrFontId(uint32 fontId);
+	void reset();
 	bool enterScene(uint32 sceneId, uint32 threadId);
+	void exitScene(uint32 threadId);
 public:
 
 	IllusionsEngine *_vm;
@@ -97,11 +100,13 @@ public:
 	uint32 _nextTempThreadId;
 	
 	uint32 _fontId;
+	uint32 _prevSceneId;
 	
 	ThreadList *_threads;
 	ScriptOpcodes *_scriptOpcodes;
 	
 	uint32 _callerThreadId;
+	int16 _menuChoiceOfs;
 	
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 72c455a..a40429b 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -66,9 +66,9 @@ ScriptOpcodes::~ScriptOpcodes() {
 }
 
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
+	debug("\nexecOpcode([%08X] %d)", opCall._callerThreadId, opCall._op);
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug("\nexecOpcode(%d)", opCall._op);
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
@@ -90,6 +90,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(14, opSetThreadSceneId);
 	OPCODE(16, opLoadResource);
 	OPCODE(20, opEnterScene);
+	OPCODE(25, opChangeScene);
 	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
 	OPCODE(45, opSetProperty);
@@ -97,19 +98,27 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(49, opStartSequenceActor);
 	OPCODE(57, opAppearActor);
 	OPCODE(58, opDisappearActor);
+	OPCODE(61, opDeactivateObject);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
 	OPCODE(65, opSetDenySfx);
 	OPCODE(66, opSetAdjustUpSfx);
 	OPCODE(67, opSetAdjustDnSfx);
 	OPCODE(75, opStartMusic);
+	OPCODE(80, opAddMenuChoice);
+	OPCODE(81, opDisplayMenu);
+	OPCODE(82, opSwitchMenuChoice);
+	OPCODE(84, opResetGame);
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	OPCODE(103, opJumpIf);
+	OPCODE(110, opGetProperty);
+	OPCODE(111, opCompareBlockCounter);
 	OPCODE(126, opDebug126);
 	OPCODE(144, opPlayVideo);
 	OPCODE(148, opLoadSpecialCodeModule);
 	OPCODE(150, opRunSpecialCode);
+	OPCODE(161, opSetActorUsePan);
 	OPCODE(175, opSetSceneIdThreadId);
 	OPCODE(176, opStackPush0);
 	OPCODE(177, opSetFontId);
@@ -198,25 +207,39 @@ void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 		_vm->_scriptMan->_activeScenes.getActiveSceneInfo(scenesCount - 1, &currSceneId, 0);
 		// TODO krnfileDump(currSceneId);
 	}
-	if (!_vm->_scriptMan->enterScene(sceneId, opCall._threadId))
+	if (!_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId))
 		opCall._result = kTSTerminate;
 }
 
+void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_scriptMan->_prevSceneId = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_vm->_scriptMan->exitScene(opCall._callerThreadId);
+	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_scriptMan->_prevSceneId, sceneId, threadId);
+	_vm->_scriptMan->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
 void ScriptOpcodes::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(flag);
 	_vm->_screen->setDisplayOn(flag != 0);
 }
 
 void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index)	
-	byte value = _vm->_scriptMan->_scriptResource->_blockCounters.get(index + 1);
+	ARG_INT16(index);	
+	byte value = _vm->_scriptMan->_scriptResource->_blockCounters.get(index) + 1;
 	if (value <= 63)
-		_vm->_scriptMan->_scriptResource->_blockCounters.set(index + 1, value);
+		_vm->_scriptMan->_scriptResource->_blockCounters.set(index, value);
 }
 
 void ScriptOpcodes::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value)	
-	ARG_INT16(propertyId)	
+	ARG_INT16(value);	
+	ARG_UINT32(propertyId);	
 	// TODO _vm->_scriptMan->_scriptResource->_properties.set(propertyId, value);
 }
 
@@ -259,6 +282,14 @@ void ScriptOpcodes::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall)
 	control->disappearActor();
 }
 
+void ScriptOpcodes::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (control)
+		control->deactivateObject();
+}
+
 void ScriptOpcodes::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(soundEffectId);
@@ -290,13 +321,51 @@ void ScriptOpcodes::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall)
 }
 void ScriptOpcodes::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
-	ARG_INT16(volume)
-	ARG_INT16(pan)
+	ARG_INT16(volume);
+	ARG_INT16(pan);
 	ARG_UINT32(musicId);
 	ARG_UINT32(type);
 	// TODO _vm->playMusic(musicId, type, volume, pan);
 }
 
+void ScriptOpcodes::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(jumpOffs);
+	ARG_INT16(endMarker);
+	_vm->_scriptMan->_stack.push(endMarker);
+	_vm->_scriptMan->_stack.push(jumpOffs);
+}
+
+void ScriptOpcodes::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(unk1);
+	ARG_UINT32(menuId);
+	ARG_UINT32(unk2);
+	// TODO _vm->_shellMgr->displayMenu(_vm->_scriptMan->_stack.topPtr(), &_vm->_scriptMan->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
+	// Remove menu choices from the stack
+	do {
+		_vm->_scriptMan->_stack.pop();
+	} while (_vm->_scriptMan->_stack.pop() == 0);
+
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+
+}
+
+void ScriptOpcodes::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+_vm->_scriptMan->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
+
+	opCall._deltaOfs += _vm->_scriptMan->_menuChoiceOfs;
+debug("deltaOfs = %08X", opCall._deltaOfs);	
+}
+
+void ScriptOpcodes::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_scriptMan->_threads->terminateThreads(opCall._callerThreadId);
+	_vm->_scriptMan->reset();
+	_vm->_input->activateButton(0xFFFF);
+	// TODO _vm->stopMusic();
+	// TODO _vm->_gameStates->clear();
+}
+
 void ScriptOpcodes::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(button)
 	_vm->_input->deactivateButton(button);
@@ -314,6 +383,42 @@ void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 		opCall._deltaOfs += jumpOffs;
 }
 
+void ScriptOpcodes::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(propertyId)
+	int16 value = 0;// TODO _vm->_scriptMan->_scriptResource->_properties.get(propertyId);
+	_vm->_scriptMan->_stack.push(value);
+}
+
+void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	ARG_INT16(compareOp);	
+	ARG_INT16(rvalue);
+	int16 lvalue = _vm->_scriptMan->_scriptResource->_blockCounters.get(index);
+	bool compareResult = false;
+	switch (compareOp) {
+	case 1:
+		compareResult = lvalue == rvalue;
+		break;
+	case 2:
+		compareResult = lvalue != rvalue;
+		break;
+	case 3:
+		compareResult = lvalue < rvalue;
+		break;
+	case 4:
+		compareResult = lvalue > rvalue;
+		break;
+	case 5:
+		compareResult = lvalue >= rvalue;
+		break;
+	case 6:
+		compareResult = lvalue <= rvalue;
+		break;
+	}
+	_vm->_scriptMan->_stack.push(compareResult ? 1 : 0);
+}
+
 void ScriptOpcodes::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Prints some debug text
 	debug("[DBG] %s", (char*)opCall._code);
@@ -345,10 +450,17 @@ void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall)
 	_vm->_scriptMan->_callerThreadId = 0;
 
 	//DEBUG Resume calling thread, later done by the special code
-	_vm->notifyThreadId(opCall._threadId);
+	_vm->notifyThreadId(opCall._callerThreadId);
 
 }
 
+void ScriptOpcodes::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(usePan)
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorUsePan(usePan);
+}
+
 void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 4d14010..5187c9e 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -68,6 +68,7 @@ protected:
 	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
@@ -75,19 +76,27 @@ protected:
 	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
+	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 4eaa7a9..4dc3b29 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -180,7 +180,6 @@ void SequenceOpcodes::opNextLoop(Control *control, OpCall &opCall) {
 void SequenceOpcodes::opSwitchActorIndex(Control *control, OpCall &opCall) {
 	ARG_INT16(actorIndex);
 	ARG_INT16(jumpOffs);
-	debug("control->_objectId = %08X", control->_objectId);
 	if (control->_actor->_actorIndex != actorIndex)
 		opCall._deltaOfs += jumpOffs;
 }


Commit: 762be35a364fe66359d29b85b650c6e7add80e47
    https://github.com/scummvm/scummvm/commit/762be35a364fe66359d29b85b650c6e7add80e47
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add more script opcodes and fix/add stuff along the way

- Unload backgrounds
- Add transparent sprite drawing
- Add AbortableThread

Changed paths:
  A engines/illusions/abortablethread.cpp
  A engines/illusions/abortablethread.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp
    engines/illusions/module.mk
    engines/illusions/resourcesystem.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/scriptthread.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/spritedrawqueue.cpp
    engines/illusions/thread.cpp
    engines/illusions/thread.h
    engines/illusions/timerthread.cpp


diff --git a/engines/illusions/abortablethread.cpp b/engines/illusions/abortablethread.cpp
new file mode 100644
index 0000000..493b81e
--- /dev/null
+++ b/engines/illusions/abortablethread.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/abortablethread.h"
+#include "illusions/input.h"
+#include "illusions/scriptman.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// AbortableThread
+
+AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 scriptThreadId, byte *scriptCodeIp)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId), 
+	_scriptCodeIp(scriptCodeIp), _status(1) {
+	_type = kTTAbortableThread;
+	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_vm->_input->discardButtons(8);
+}
+
+int AbortableThread::onUpdate() {
+	if (_status != 1 || _pauseCtr < 0)
+		return kTSTerminate;
+	if (_vm->_input->pollButton(8)) {
+		_vm->_scriptMan->_threads->killThread(_scriptThreadId);
+		++_pauseCtr;
+		_vm->_scriptMan->startTempScriptThread(_scriptCodeIp, _threadId, 0, 0, 0);
+		_status = 2;
+		return kTSSuspend;
+	}
+	return kTSYield;
+}
+
+void AbortableThread::onSuspend() {
+}
+
+void AbortableThread::onNotify() {
+}
+
+void AbortableThread::onPause() {
+}
+
+void AbortableThread::onResume() {
+}
+
+void AbortableThread::onTerminated() {
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/abortablethread.h b/engines/illusions/abortablethread.h
new file mode 100644
index 0000000..8fc2a47
--- /dev/null
+++ b/engines/illusions/abortablethread.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ABORTABLETHREAD_H
+#define ILLUSIONS_ABORTABLETHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class AbortableThread : public Thread {
+public:
+	AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 scriptThreadId, byte *scriptCodeIp);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	int _status;
+	byte *_scriptCodeIp;
+	uint32 _scriptThreadId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ABORTABLETHREAD_H
diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 5ba576b..bcaf72b 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -273,20 +273,24 @@ bool Control::isActorVisible() {
 
 void Control::activateObject() {
 	_flags |= 1;
-	for (uint i = 0; i < kSubObjectsCount; ++i)
-		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
-			subControl->activateObject();
-		}
+	if (_actor) {
+		for (uint i = 0; i < kSubObjectsCount; ++i)
+			if (_actor->_subobjects[i]) {
+				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
+				subControl->activateObject();
+			}
+	}
 }
 
 void Control::deactivateObject() {
 	_flags |= ~1;
-	for (uint i = 0; i < kSubObjectsCount; ++i)
-		if (_actor->_subobjects[i]) {
-			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
-			subControl->deactivateObject();
-		}
+	if (_actor) {
+		for (uint i = 0; i < kSubObjectsCount; ++i)
+			if (_actor->_subobjects[i]) {
+				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
+				subControl->deactivateObject();
+			}
+	}
 }
 
 void Control::readPointsConfig(byte *pointsConfig) {
@@ -543,7 +547,7 @@ void Control::sequenceActor() {
 	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
 		bool breakInner = false;
 		while (!breakInner) {
-			debug("SEQ op: %08X", _actor->_seqCodeIp[0]);
+			debug(1, "SEQ op: %08X", _actor->_seqCodeIp[0]);
 			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
 			opCall._opSize = _actor->_seqCodeIp[1];
 			opCall._code = _actor->_seqCodeIp + 2;
@@ -563,7 +567,7 @@ void Control::sequenceActor() {
 	}
 
 	if (_actor->_newFrameIndex != 0) {
-		debug("New frame %d", _actor->_newFrameIndex);
+		debug(1, "New frame %d", _actor->_newFrameIndex);
 		setActorFrameIndex(_actor->_newFrameIndex);
 		if (!(_actor->_flags & 1) && (_actor->_flags & 0x1000) && (_objectId != 0x40004)) {
 			appearActor();
@@ -572,7 +576,7 @@ void Control::sequenceActor() {
 	}
 	
 	if (sequenceFinished) {
-		debug("Sequence has finished");
+		debug(1, "Sequence has finished");
 		_actor->_seqCodeIp = 0;
 	}
 	
@@ -594,7 +598,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 	_actor->_path40 = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
-	debug("Control::startSequenceActorIntern() sequence = %p", (void*)sequence);
+	debug(1, "Control::startSequenceActorIntern() sequence = %p", (void*)sequence);
 	
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
@@ -632,14 +636,25 @@ Controls::~Controls() {
 	delete _sequenceOpcodes;
 }
 
+void Controls::placeBackgroundObject(BackgroundObject *backgroundObject) {
+	Control *control = newControl();
+	control->_objectId = backgroundObject->_objectId;
+	control->_flags = backgroundObject->_flags;
+	control->_priority = backgroundObject->_priority;
+	control->readPointsConfig(backgroundObject->_pointsConfig);
+	control->activateObject();
+	_controls.push_back(control);
+	_vm->_dict->setObjectControl(control->_objectId, control);
+}
+
 void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId) {
 	Control *control = newControl();
 	Actor *actor = newActor();
 
 	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
+	control->_objectId = objectId;
 	control->_flags = actorType->_flags;
 	control->_priority = actorType->_priority;
-	control->_objectId = objectId;
 	control->readPointsConfig(actorType->_pointsConfig);
 	control->_actorTypeId = actorTypeId;
 	control->_actor = actor;
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 5966053..63ae4f8 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -193,6 +193,7 @@ class Controls {
 public:
 	Controls(IllusionsEngine *vm);
 	~Controls();
+	void placeBackgroundObject(BackgroundObject *backgroundObject);
 	void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 6c7808c..fd8ae4f 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/screen.h"
 #include "common/str.h"
@@ -31,19 +32,22 @@ namespace Illusions {
 // BackgroundResourceLoader
 
 void BackgroundResourceLoader::load(Resource *resource) {
-	// TODO
 	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	BackgroundResource *backgroundResource = new BackgroundResource();
 	backgroundResource->load(resource->_data, resource->_dataSize);
+	resource->_refId = backgroundResource;
 
+	// TODO Move to BackgroundItems
 	BackgroundItem *backgroundItem = _vm->_backgroundItems->allocBackgroundItem();
 	backgroundItem->_bgRes = backgroundResource;
 	backgroundItem->_tag = resource->_tag;
-	
 	backgroundItem->initSurface();
 	
-	// TODO Insert objects from item44s
+	// TODO Insert background objects
+	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
+		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
+
 	// TODO Insert IDs from item48s
 
 	// TODO camera_fadeClear();
@@ -54,6 +58,13 @@ void BackgroundResourceLoader::load(Resource *resource) {
 }
 
 void BackgroundResourceLoader::unload(Resource *resource) {
+	debug("BackgroundResourceLoader::unload() Unloading background %08X...", resource->_resId);
+	// TODO Move to BackgroundItems
+	BackgroundItem *backgroundItem = _vm->_backgroundItems->findBackgroundByResource((BackgroundResource*)resource->_refId);
+	backgroundItem->freeSurface();
+	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
+	// TODO Remove IDs from item48s
+	// TODO _vm->setDefPointDimensions1();
 }
 
 void BackgroundResourceLoader::buildFilename(Resource *resource) {
@@ -137,6 +148,19 @@ int ScaleLayer::getScale(Common::Point pos) {
 	return _values[pos.y];
 }
 
+// BackgroundObject
+
+void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_objectId = stream.readUint32LE();
+	_flags = stream.readUint16LE();
+	_priority = stream.readUint16LE();
+	uint32 pointsConfigOffs = stream.readUint32LE();
+	_pointsConfig = dataStart + pointsConfigOffs;
+	
+	debug("BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
+		_objectId, _flags, _priority, pointsConfigOffs);
+}
+
 // BackgroundResource
 
 BackgroundResource::BackgroundResource() {
@@ -185,6 +209,18 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 		_priorityLayers[i].load(data, stream);
 	}
 
+	// Load background objects
+	stream.seek(0x1C);
+	_backgroundObjectsCount = stream.readUint16LE();
+	_backgroundObjects = new BackgroundObject[_backgroundObjectsCount];
+	stream.seek(0x44);
+	uint32 backgroundObjectsOffs = stream.readUint32LE();
+	debug("_backgroundObjectsCount: %d", _backgroundObjectsCount);
+	for (uint i = 0; i < _backgroundObjectsCount; ++i) {
+		stream.seek(backgroundObjectsOffs + i * 12);
+		_backgroundObjects[i].load(data, stream);
+	}
+
 }
 
 int BackgroundResource::findMasterBgIndex() {
@@ -207,6 +243,9 @@ ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
 BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
 }
 
+BackgroundItem::~BackgroundItem() {
+}
+
 void BackgroundItem::initSurface() {
 	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
 		_surfaces[i] = 0;
@@ -329,6 +368,11 @@ BackgroundItem *BackgroundItems::allocBackgroundItem() {
 	return backgroundItem;
 }
 
+void BackgroundItems::freeBackgroundItem(BackgroundItem *backgroundItem) {
+	_items.remove(backgroundItem);
+	delete backgroundItem;
+}
+
 void BackgroundItems::pauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
@@ -348,6 +392,13 @@ BackgroundItem *BackgroundItems::findActiveBackground() {
 	return 0;
 }
 
+BackgroundItem *BackgroundItems::findBackgroundByResource(BackgroundResource *backgroundResource) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_bgRes == backgroundResource)
+			return (*it);
+	return 0;
+}
+
 BackgroundResource *BackgroundItems::getActiveBgResource() {
 	BackgroundItem *background = findActiveBackground();
 	if (background)
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index d3865d1..cfb9067 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -100,6 +100,14 @@ points dd ?
 BgResource_PathWalkPoints ends
 #endif
 
+struct BackgroundObject {
+	uint32 _objectId;
+	uint16 _flags;
+	int16 _priority;
+	byte *_pointsConfig;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
 class BackgroundResource {
 public:
 	BackgroundResource();
@@ -118,6 +126,9 @@ public:
 
 	uint _scaleLayersCount;
 	ScaleLayer *_scaleLayers;
+	
+	uint _backgroundObjectsCount;
+	BackgroundObject *_backgroundObjects;
 
 };
 
@@ -149,14 +160,17 @@ public:
 	BackgroundItems(IllusionsEngine *vm);
 	~BackgroundItems();
 	BackgroundItem *allocBackgroundItem();
+	void freeBackgroundItem(BackgroundItem *backgroundItem);
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
 	BackgroundItem *findActiveBackground();
+	BackgroundItem *findBackgroundByResource(BackgroundResource *backgroundResource);
 	BackgroundResource *getActiveBgResource();
 	WidthHeight getMasterBgDimensions();
 	void refreshPan();
 	BackgroundItem *debugFirst();
-protected:
+//protected:
+public:
 	typedef Common::List<BackgroundItem*> Items;
 	typedef Items::iterator ItemsIterator;
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 8966814..265833a 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -152,6 +152,8 @@ Common::Error IllusionsEngine::run() {
 	*/	
 	
 	_resSys->loadResource(0x000D0001, 0, 0);
+
+#if 0	
 	_resSys->loadResource(0x0011000B, 0, 0);
 	_resSys->loadResource(0x0010000B, 0, 0);
 
@@ -161,6 +163,7 @@ Common::Error IllusionsEngine::run() {
 	control->setActorFrameIndex(1);
 	control->appearActor();
 #endif
+#endif
 
 	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index edaa94f..de705dd 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/illusions
 
 MODULE_OBJS := \
+    abortablethread.o \
 	actor.o \
 	actorresource.o \
 	backgroundresource.o \
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index 18feb51..833a7db 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -45,9 +45,10 @@ struct Resource {
 	byte *_data;
 	uint32 _dataSize;
 	BaseResourceLoader *_resourceLoader;
+	void *_refId;
 	Common::String _filename; // TODO Check if this is needed
 	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0),
-	_resourceLoader(0) {}
+	_resourceLoader(0), _refId(0) {}
 	~Resource() {
 		unloadData();
 	}
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 1a80a33..694382d 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -36,6 +36,7 @@ Screen::Screen(IllusionsEngine *vm)
 	_backSurface = allocSurface(640, 480);
 	_decompressQueue = new SpriteDecompressQueue();
 	_drawQueue = new SpriteDrawQueue(this);
+	_colorKey1 = 0xF800 | 0x1F;
 }
 
 Screen::~Screen() {
@@ -80,33 +81,40 @@ void Screen::updateSprites() {
 }
 
 void Screen::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
-	// Unscaled, transparent
+	// Unscaled
 	// TODO
-	debug("Screen::drawSurface10");
+	//debug("Screen::drawSurface10");
 }
 
 void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// Unscaled, non-transparent
-	debug("Screen::drawSurface11() destX: %d; destY: %d; srcRect: (%d, %d, %d, %d)", destX, destY, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom);
+	// Unscaled
+	//debug("Screen::drawSurface11() destX: %d; destY: %d; srcRect: (%d, %d, %d, %d)", destX, destY, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom);
 	const int16 w = srcRect.width();
 	const int16 h = srcRect.height();
 	for (int16 yc = 0; yc < h; ++yc) {
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcRect.top + yc);
 		byte *dst = (byte*)_backSurface->getBasePtr(destX, destY + yc);
-		memcpy(dst, src, w * 2);
+		//memcpy(dst, src, w * 2);
+		for (int16 xc = 0; xc < w; ++xc) {
+			uint16 pixel = READ_LE_UINT16(src);
+			if (pixel != _colorKey1)
+				WRITE_LE_UINT16(dst, pixel);
+			src += 2;
+			dst += 2;
+		}
 	}
 }
 
 void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
-	// Scaled, transparent
+	// Scaled
 	// TODO
-	debug("Screen::drawSurface20");
+	//debug("Screen::drawSurface20");
 }
 
 void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// Scaled, non-transparent
+	// Scaled
 	// TODO
-	debug("Screen::drawSurface21");
+	//debug("Screen::drawSurface21");
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index ef0f50a..8fa95ec 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -48,6 +48,7 @@ public:
 public:
 	IllusionsEngine *_vm;
 	bool _displayOn;
+	uint16 _colorKey1;
 	uint16 _colorKey2;
 	SpriteDecompressQueue *_decompressQueue;
 	SpriteDrawQueue *_drawQueue;
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 527c0a5..2ba39c7 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/illusions.h"
+#include "illusions/abortablethread.h"
 #include "illusions/actor.h"
 #include "illusions/scriptman.h"
 #include "illusions/scriptthread.h"
@@ -171,6 +172,15 @@ uint32 ScriptMan::startTimerThread(uint32 duration, uint32 threadId) {
 	return newTimerThread(duration, threadId, false);
 }
 
+uint32 ScriptMan::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
+	debug("Starting abortable thread");
+	uint32 tempThreadId = newTempThreadId();
+	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
+	AbortableThread *abortableThread = new AbortableThread(_vm, tempThreadId, callingThreadId, 0, scriptThreadId, scriptCodeIp2);
+	_threads->startThread(abortableThread);
+	return tempThreadId;
+}
+
 void ScriptMan::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
@@ -198,7 +208,7 @@ void ScriptMan::exitScene(uint32 threadId) {
 	_threads->terminateThreadsByTag(sceneId, threadId);
 	_vm->_controls->destroyControlsByTag(sceneId);
 	// TODO causeFunc_removeBySceneId(sceneId);
-	// TODO _vm->_resSys->unloadResourceByTag(sceneId);
+	_vm->_resSys->unloadResourcesByTag(sceneId);
 	_activeScenes.pop();
 }
 
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index 4bbd669..0ce0ef1 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -80,6 +80,7 @@ public:
 		uint32 value8, uint32 valueC, uint32 value10);
 	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
 	uint32 startTimerThread(uint32 duration, uint32 threadId);
+	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
 	void setCurrFontId(uint32 fontId);
 	void reset();
 	bool enterScene(uint32 sceneId, uint32 threadId);
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index a40429b..6d32800 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -66,14 +66,16 @@ ScriptOpcodes::~ScriptOpcodes() {
 }
 
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
-	debug("\nexecOpcode([%08X] %d)", opCall._callerThreadId, opCall._op);
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
+	debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
 typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes> ScriptOpcodeI;
-#define OPCODE(op, func) _opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes::func);
+#define OPCODE(op, func) \
+	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes::func); \
+	_opcodeNames[op] = #func;
 
 void ScriptOpcodes::initOpcodes() {
 	// First clear everything
@@ -88,6 +90,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(8, opStartTempScriptThread);
 	OPCODE(9, opStartTimerThread);
 	OPCODE(14, opSetThreadSceneId);
+	OPCODE(15, opEndTalkThreads);
 	OPCODE(16, opLoadResource);
 	OPCODE(20, opEnterScene);
 	OPCODE(25, opChangeScene);
@@ -96,8 +99,10 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(45, opSetProperty);
 	OPCODE(46, opPlaceActor);	
 	OPCODE(49, opStartSequenceActor);
+	OPCODE(56, opStartTalkThread);
 	OPCODE(57, opAppearActor);
 	OPCODE(58, opDisappearActor);
+	OPCODE(60, opActivateObject);
 	OPCODE(61, opDeactivateObject);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
@@ -105,6 +110,8 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(66, opSetAdjustUpSfx);
 	OPCODE(67, opSetAdjustDnSfx);
 	OPCODE(75, opStartMusic);
+	OPCODE(78, opStackPushRandom);
+	OPCODE(79, opIfLte);
 	OPCODE(80, opAddMenuChoice);
 	OPCODE(81, opDisplayMenu);
 	OPCODE(82, opSwitchMenuChoice);
@@ -112,13 +119,17 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	OPCODE(103, opJumpIf);
+	OPCODE(107, opBoolNot);
 	OPCODE(110, opGetProperty);
 	OPCODE(111, opCompareBlockCounter);
 	OPCODE(126, opDebug126);
 	OPCODE(144, opPlayVideo);
+	OPCODE(146, opStackPop);
+	OPCODE(147, opStackDup);
 	OPCODE(148, opLoadSpecialCodeModule);
 	OPCODE(150, opRunSpecialCode);
 	OPCODE(161, opSetActorUsePan);
+	OPCODE(168, opStartAbortableThread);
 	OPCODE(175, opSetSceneIdThreadId);
 	OPCODE(176, opStackPush0);
 	OPCODE(177, opSetFontId);
@@ -190,6 +201,10 @@ void ScriptOpcodes::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCal
 	_vm->_scriptMan->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
 }
 
+void ScriptOpcodes::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_scriptMan->_threads->endTalkThreads();
+}
+
 void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(resourceId);
@@ -240,7 +255,7 @@ void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall
 void ScriptOpcodes::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(value);	
 	ARG_UINT32(propertyId);	
-	// TODO _vm->_scriptMan->_scriptResource->_properties.set(propertyId, value);
+	_vm->_scriptMan->_scriptResource->_properties.set(propertyId, value != 0);
 }
 
 void ScriptOpcodes::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
@@ -262,15 +277,29 @@ void ScriptOpcodes::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opC
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void ScriptOpcodes::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value);	
+	ARG_UINT32(objectId);
+	ARG_UINT32(talkId);
+	ARG_UINT32(sequenceId1);
+	ARG_UINT32(sequenceId2);
+	ARG_UINT32(value2);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	// TODO _vm->_scriptMan->startTalkThread(value, objectId, talkId, sequenceId1, sequenceId2, value2, opCall._callerThreadId);
+
+	//DEBUG Resume calling thread, later done after talking is finished
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void ScriptOpcodes::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	if (!control) {
 		Common::Point pos = _vm->getNamedPointPosition(0x70023);
-        _vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
-        control = _vm->_dict->getObjectControl(objectId);
-        control->startSequenceActor(0x60001, 2, 0);
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
+		control = _vm->_dict->getObjectControl(objectId);
+		control->startSequenceActor(0x60001, 2, 0);
 	}
 	control->appearActor();
 }
@@ -282,12 +311,19 @@ void ScriptOpcodes::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall)
 	control->disappearActor();
 }
 
-void ScriptOpcodes::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+void ScriptOpcodes::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	if (control)
-		control->deactivateObject();
+		control->activateObject();
+}
+
+void ScriptOpcodes::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->deactivateObject();
 }
 
 void ScriptOpcodes::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
@@ -328,6 +364,20 @@ void ScriptOpcodes::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	// TODO _vm->playMusic(musicId, type, volume, pan);
 }
 
+void ScriptOpcodes::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(maxValue);
+	_vm->_scriptMan->_stack.push(_vm->getRandom(maxValue) + 1);
+}
+
+void ScriptOpcodes::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(rvalue);
+	ARG_INT16(elseJumpOffs);
+	int16 lvalue = _vm->_scriptMan->_stack.pop();
+	if (!(lvalue <= rvalue))
+		opCall._deltaOfs += elseJumpOffs;
+}
+
 void ScriptOpcodes::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_INT16(jumpOffs);
@@ -383,11 +433,16 @@ void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 		opCall._deltaOfs += jumpOffs;
 }
 
+void ScriptOpcodes::opBoolNot(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_scriptMan->_stack.pop();
+	_vm->_scriptMan->_stack.push(value != 0 ? 0 : 1);
+}
+
 void ScriptOpcodes::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(propertyId)
-	int16 value = 0;// TODO _vm->_scriptMan->_scriptResource->_properties.get(propertyId);
-	_vm->_scriptMan->_stack.push(value);
+	bool value = _vm->_scriptMan->_scriptResource->_properties.get(propertyId);
+	_vm->_scriptMan->_stack.push(value ? 1 : 0);
 }
 
 void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
@@ -436,6 +491,15 @@ void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
 	
 }
 
+void ScriptOpcodes::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_scriptMan->_stack.pop(); 
+}
+
+void ScriptOpcodes::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_scriptMan->_stack.peek();
+	_vm->_scriptMan->_stack.push(value); 
+}
+
 void ScriptOpcodes::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeModuleId);
@@ -461,6 +525,14 @@ void ScriptOpcodes::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall)
 	control->setActorUsePan(usePan);
 }
 
+void ScriptOpcodes::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(codeOffs);
+	ARG_INT16(skipOffs);
+	_vm->_scriptMan->startAbortableThread(opCall._code + opCall._opSize + codeOffs,
+		opCall._code + opCall._opSize + skipOffs, opCall._callerThreadId);
+}
+
 void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 5187c9e..060171d 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -54,6 +54,7 @@ public:
 protected:
 	IllusionsEngine *_vm;
 	ScriptOpcode *_opcodes[256];
+	Common::String _opcodeNames[256];
 	void initOpcodes();
 	void freeOpcodes();
 
@@ -66,6 +67,7 @@ protected:
 	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
@@ -74,8 +76,10 @@ protected:
 	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
@@ -83,6 +87,8 @@ protected:
 	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
+	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
 	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
 	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
@@ -90,13 +96,17 @@ protected:
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opBoolNot(ScriptThread *scriptThread, OpCall &opCall);
 	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 3a08e50..29f123e 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -59,6 +59,29 @@ void Properties::init(uint count, byte *properties) {
 	_properties = properties;
 }
 
+bool Properties::get(uint32 propertyId) {
+	uint index;
+	byte mask;
+	getProperyPos(propertyId, index, mask);
+	return (_properties[index] & mask) != 0;
+}
+
+void Properties::set(uint32 propertyId, bool value) {
+	uint index;
+	byte mask;
+	getProperyPos(propertyId, index, mask);
+	if (value)
+		_properties[index] |= mask;
+	else
+		_properties[index] &= ~mask;
+}
+
+void Properties::getProperyPos(uint32 propertyId, uint &index, byte &mask) {
+	propertyId &= 0xFFFF;
+	index = propertyId >> 3;
+	mask = 1 << (propertyId & 7);
+}
+
 // BlockCounters
 
 BlockCounters::BlockCounters()
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 8a689a9..79c46c8 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -45,9 +45,12 @@ class Properties {
 public:
 	Properties();
 	void init(uint count, byte *properties);
+	bool get(uint32 propertyId);
+	void set(uint32 propertyId, bool value);
 public:
 	uint _count;
 	byte *_properties;
+	void getProperyPos(uint32 propertyId, uint &index, byte &mask);
 };
 
 class BlockCounters {
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index b21b003..69835d9 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -57,27 +57,27 @@ int ScriptThread::onUpdate() {
 
 void ScriptThread::onSuspend() {
 	// TODO
-	debug("ScriptThread::onSuspend()");
+	debug(1, "ScriptThread::onSuspend()");
 }
 
 void ScriptThread::onNotify() {
 	// TODO
-	debug("ScriptThread::onNotify()");
+	debug(1, "ScriptThread::onNotify()");
 }
 
 void ScriptThread::onPause() {
 	// TODO
-	debug("ScriptThread::onPause()");
+	debug(1, "ScriptThread::onPause()");
 }
 
 void ScriptThread::onResume() {
 	// TODO
-	debug("ScriptThread::onResume()");
+	debug(1, "ScriptThread::onResume()");
 }
 
 void ScriptThread::onTerminated() {
 	// TODO
-	debug("ScriptThread::onTerminated()");
+	debug(1, "ScriptThread::onTerminated()");
 }
 
 void ScriptThread::execOpcode(OpCall &opCall) {
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 4dc3b29..b550be6 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -42,7 +42,7 @@ SequenceOpcodes::~SequenceOpcodes() {
 void SequenceOpcodes::execOpcode(Control *control, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("SequenceOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug("execOpcode(%d)", opCall._op);
+	debug(1, "execOpcode(%d)", opCall._op);
 	(*_opcodes[opCall._op])(control, opCall);
 }
 
@@ -86,13 +86,13 @@ void SequenceOpcodes::freeOpcodes() {
 
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %08X)", name);
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(1, "ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(1, "ARG_UINT32(" #name " = %08X)", name);
 
 void SequenceOpcodes::opSetFrameIndex(Control *control, OpCall &opCall) {
 	ARG_INT16(frameIndex);
 	if (control->_actor->_flags & 0x80) {
-		debug("opSetFrameIndex TODO");
+		debug(1, "opSetFrameIndex TODO");
 		/* TODO
 		v9 = actor->field30;
 		if (*(_WORD *)v9) {
diff --git a/engines/illusions/spritedrawqueue.cpp b/engines/illusions/spritedrawqueue.cpp
index 1e42d18..f39eb5f 100644
--- a/engines/illusions/spritedrawqueue.cpp
+++ b/engines/illusions/spritedrawqueue.cpp
@@ -143,8 +143,8 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	srcRect.right = item->_dimensions._width;
 	srcRect.bottom = item->_dimensions._height;
 	
-	debug("item->_drawPosition.x: %d; item->_drawPosition.y: %d", item->_drawPosition.x, item->_drawPosition.y);
-	debug("item->_controlPosition.x: %d; item->_controlPosition.y: %d", item->_controlPosition.x, item->_controlPosition.y);
+	//debug("item->_drawPosition.x: %d; item->_drawPosition.y: %d", item->_drawPosition.x, item->_drawPosition.y);
+	//debug("item->_controlPosition.x: %d; item->_controlPosition.y: %d", item->_controlPosition.x, item->_controlPosition.y);
 
 	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
 	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index c16ecd4..83e0ce5 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -91,7 +91,6 @@ int Thread::update() {
 	int status = kTSYield;
 	if (!_terminated && _pauseCtr <= 0) {
 		status = onUpdate();
-		debug("Thread status: %d", status);
 		if (status == kTSTerminate)
 			terminate();
 		else if (status == kTSSuspend)
@@ -230,6 +229,14 @@ void ThreadList::resumeThreads(uint32 threadId) {
 	}
 }
 
+void ThreadList::endTalkThreads() {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_type == kTTTalkThread)
+			thread->terminate();
+	}
+}
+
 void ThreadList::killThread(uint32 threadId) {
 
 	if (!threadId)
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index 977262a..fc1a059 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -30,10 +30,11 @@ namespace Illusions {
 class IllusionsEngine;
 
 enum ThreadType {
-	kTTScriptThread  = 1,
-	kTTTimerThread   = 2,
-	kTTTalkThread    = 3,
-	kTTSpecialThread = 5
+	kTTScriptThread      = 1,
+	kTTTimerThread       = 2,
+	kTTTalkThread        = 3,
+	kTTAbortableThread   = 4,
+	kTTSpecialThread     = 5
 };
 
 enum ThreadStatus {
@@ -88,6 +89,7 @@ public:
 	void notifyThreadsByTag(uint32 tag, uint32 threadId);
 	void pauseThreads(uint32 threadId);
 	void resumeThreads(uint32 threadId);
+	void endTalkThreads();
 	void killThread(uint32 threadId);
 	void setThreadSceneId(uint32 threadId, uint32 sceneId);
 	uint32 getThreadSceneId(uint32 threadId);
diff --git a/engines/illusions/timerthread.cpp b/engines/illusions/timerthread.cpp
index 5263305..42dde6d 100644
--- a/engines/illusions/timerthread.cpp
+++ b/engines/illusions/timerthread.cpp
@@ -39,7 +39,6 @@ TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThr
 }
 
 int TimerThread::onUpdate() {
-debug("startTime: %d; endTime: %d; currTime: %d", _startTime, _endTime, getCurrentTime());
 	if (isTimerExpired(_startTime, _endTime) ||
 		(_isAbortable && _vm->_input->pollButton(8)))
 		return kTSTerminate;


Commit: 812c7fc3a83f57b9ba181c47d3699155c16ab379
    https://github.com/scummvm/scummvm/commit/812c7fc3a83f57b9ba181c47d3699155c16ab379
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add TalkResource and related

Changed paths:
    engines/illusions/backgroundresource.cpp
    engines/illusions/dictionary.cpp
    engines/illusions/dictionary.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/talkresource.cpp
    engines/illusions/talkresource.h


diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index fd8ae4f..d80a9e2 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -62,8 +62,9 @@ void BackgroundResourceLoader::unload(Resource *resource) {
 	// TODO Move to BackgroundItems
 	BackgroundItem *backgroundItem = _vm->_backgroundItems->findBackgroundByResource((BackgroundResource*)resource->_refId);
 	backgroundItem->freeSurface();
-	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
 	// TODO Remove IDs from item48s
+	delete backgroundItem->_bgRes;
+	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
 	// TODO _vm->setDefPointDimensions1();
 }
 
diff --git a/engines/illusions/dictionary.cpp b/engines/illusions/dictionary.cpp
index 2d3de2a..2d9d4b4 100644
--- a/engines/illusions/dictionary.cpp
+++ b/engines/illusions/dictionary.cpp
@@ -24,6 +24,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/actorresource.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/talkresource.h"
 
 namespace Illusions {
 
@@ -51,6 +52,18 @@ Sequence *Dictionary::findSequence(uint32 id) {
 	return _sequences.find(id);
 }
 
+void Dictionary::addTalkEntry(uint32 id, TalkEntry *talkEntry) {
+	_talkEntries.add(id, talkEntry);
+}
+
+void Dictionary::removeTalkEntry(uint32 id) {
+	_talkEntries.remove(id);
+}
+
+TalkEntry *Dictionary::findTalkEntry(uint32 id) {
+	return _talkEntries.find(id);
+}
+
 void Dictionary::setObjectControl(uint32 objectId, Control *control) {
 	if (control)
 		_controls.add(objectId, control);
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index e8863f4..6bdc539 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -28,6 +28,9 @@
 namespace Illusions {
 
 class ActorType;
+class Control;
+class Sequence;
+class TalkEntry;
 
 template<class T>
 class DictionaryHashMap {
@@ -63,12 +66,17 @@ public:
 	void removeSequence(uint32 id);
 	Sequence *findSequence(uint32 id);
 
+    void addTalkEntry(uint32 id, TalkEntry *talkEntry);
+	void removeTalkEntry(uint32 id);
+	TalkEntry *findTalkEntry(uint32 id);
+
     void setObjectControl(uint32 objectId, Control *control);
     Control *getObjectControl(uint32 objectId);
 
 protected:
 	DictionaryHashMap<ActorType> _actorTypes;
 	DictionaryHashMap<Sequence> _sequences;
+	DictionaryHashMap<TalkEntry> _talkEntries;
 	DictionaryHashMap<Control> _controls;
 };
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 265833a..d758c1f 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -104,6 +104,7 @@ Common::Error IllusionsEngine::run() {
 	_camera = new Camera(this);
 	_controls = new Controls(this);
 	_cursor = new Cursor(this);
+	_talkItems = new TalkItems(this);
 	
 	// TODO Move to own class
 	_resGetCtr = 0;
@@ -181,6 +182,7 @@ Common::Error IllusionsEngine::run() {
 	}
 #endif
 
+	delete _talkItems;
 	delete _cursor;
 	delete _controls;
 	delete _camera;
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index c82f2c1..2fa7631 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -64,6 +64,7 @@ class Screen;
 class ScriptResource;
 class ScriptMan;
 class Sequence;
+class TalkItems;
 class FramesList;
 
 class IllusionsEngine : public Engine {
@@ -93,6 +94,7 @@ public:
 	Camera *_camera;
 	Controls *_controls;
 	Cursor *_cursor;
+	TalkItems *_talkItems;
 	
 	int _resGetCtr;
 	uint32 _resGetTime;
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index d0fa607..fe14eff 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/talkresource.h"
+#include "illusions/dictionary.h"
 
 namespace Illusions {
 
@@ -30,11 +31,30 @@ namespace Illusions {
 void TalkResourceLoader::load(Resource *resource) {
 	debug("TalkResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
 
-    // TODO
+	TalkResource *talkResource = new TalkResource();
+	talkResource->load(resource->_data, resource->_dataSize);
+	resource->_refId = talkResource;
+
+	_vm->_talkItems->newTalkItem(resource->_resId, talkResource);
+
+	for (uint i = 0; i < talkResource->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &talkResource->_talkEntries[i];
+		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
+	}
 	
 }
 
 void TalkResourceLoader::unload(Resource *resource) {
+	TalkItem *talkItem = _vm->_talkItems->findTalkItem(resource->_resId);
+	TalkResource *talkResource = talkItem->_talkRes;
+
+	for (uint i = 0; i < talkResource->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &talkResource->_talkEntries[i];
+		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
+	}
+	
+	_vm->_talkItems->freeTalkItem(talkItem);
+	
 }
 
 void TalkResourceLoader::buildFilename(Resource *resource) {
@@ -46,4 +66,80 @@ bool TalkResourceLoader::isFlag(int flag) {
 		flag == kRlfLoadFile;
 }
 
+// TalkEntry
+
+void TalkEntry::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_talkId = stream.readUint32LE();
+	stream.readUint32LE(); // Skip unknown
+	uint32 textOffs = stream.readUint32LE();
+	uint32 tblOffs = stream.readUint32LE();
+	uint32 voiceNameOffs = stream.readUint32LE();
+	_text = dataStart + textOffs;
+	_tblPtr = dataStart + tblOffs;
+	_voiceName = dataStart + voiceNameOffs;
+	
+	debug("TalkEntry::load() _talkId: %08X; textOffs: %08X; tblOffs: %08X; voiceNameOffs: %08X",
+		_talkId, textOffs, tblOffs, voiceNameOffs);
+}
+
+// TalkResource
+
+TalkResource::TalkResource()
+	: _talkEntriesCount(0), _talkEntries(0) {
+}
+
+TalkResource::~TalkResource() {
+	delete[] _talkEntries;
+}
+
+void TalkResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	stream.skip(4); // Skip size
+	_talkEntriesCount = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	
+	_talkEntries = new TalkEntry[_talkEntriesCount];
+	for (uint i = 0; i < _talkEntriesCount; ++i) {
+		stream.seek(8 + i * 0x14);
+		_talkEntries[i].load(data, stream);
+	}
+
+}
+
+// TalkItem
+
+TalkItem::TalkItem(uint32 talkId, TalkResource *talkResource)
+	: _talkId(talkId), _talkRes(talkResource), _pauseCtr(0) {
+}
+
+TalkItem::~TalkItem() {
+}
+
+// TalkItems
+
+TalkItems::TalkItems(IllusionsEngine *vm) {
+}
+
+TalkItems::~TalkItems() {
+}
+
+TalkItem *TalkItems::newTalkItem(uint32 talkId, TalkResource *talkResource) {
+	TalkItem *talkItem = new TalkItem(talkId, talkResource);
+	_items.push_back(talkItem);
+	return talkItem;
+}
+
+void TalkItems::freeTalkItem(TalkItem *talkItem) {
+	_items.remove(talkItem);
+	delete talkItem;
+}
+
+TalkItem *TalkItems::findTalkItem(uint32 talkId) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_talkId == talkId)
+			return (*it);
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/talkresource.h b/engines/illusions/talkresource.h
index 982d5fd..beb859c 100644
--- a/engines/illusions/talkresource.h
+++ b/engines/illusions/talkresource.h
@@ -42,6 +42,50 @@ protected:
 	IllusionsEngine *_vm;
 };
 
+struct TalkEntry {
+	uint32 _talkId;
+	//field_4 dd
+	byte *_text;
+	byte *_tblPtr;
+	byte *_voiceName;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class TalkResource {
+public:
+	TalkResource();
+	~TalkResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	uint _talkEntriesCount;
+	TalkEntry *_talkEntries;
+};
+
+class TalkItem {
+public:
+	TalkItem(uint32 talkId, TalkResource *talkResource);
+	~TalkItem();
+public:
+	uint32 _talkId;
+	TalkResource *_talkRes;
+	int _pauseCtr;
+};
+
+class TalkItems {
+public:
+	TalkItems(IllusionsEngine *vm);
+	~TalkItems();
+	TalkItem *newTalkItem(uint32 talkId, TalkResource *talkResource);
+	void freeTalkItem(TalkItem *talkItem);
+	TalkItem *findTalkItem(uint32 talkId);
+//protected:
+public:
+	typedef Common::List<TalkItem*> Items;
+	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
+	Items _items;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_TALKRESOURCE_H


Commit: fc4266bcadaf7c01bf558ef78a9702254803ca77
    https://github.com/scummvm/scummvm/commit/fc4266bcadaf7c01bf558ef78a9702254803ca77
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add TalkThread

Changed paths:
  A engines/illusions/talkthread.cpp
  A engines/illusions/talkthread.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/thread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index bcaf72b..933ebee 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -83,7 +83,7 @@ Actor::Actor(IllusionsEngine *vm)
 	_notifyThreadId1 = 0;
 	_notifyThreadId2 = 0;
 	_surfaceTextFlag = 0;
-	_field30 = 0;
+	_entryTblPtr = 0;
 	_seqCodeIp = 0;
 	_sequenceId = 0;
 	_seqCodeValue1 = 0;
@@ -360,11 +360,11 @@ void Control::clearNotifyThreadId2() {
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->_actor->_flags &= ~0x80;
-			subControl->_actor->_field30 = 0;
+			subControl->_actor->_entryTblPtr = 0;
 			subControl->_actor->_notifyThreadId2 = 0;
 		}
 	_actor->_flags &= ~0x80;
-	_actor->_field30 = 0;
+	_actor->_entryTblPtr = 0;
 	_actor->_notifyThreadId2 = 0;
 }
 
@@ -582,7 +582,7 @@ void Control::sequenceActor() {
 	
 }
 
-void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId) {
+void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 
 	stopActor();
 	
@@ -609,9 +609,9 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, int value2,
 	_actor->initSequenceStack();
 	stopSequenceActor();
 	_actor->_linkIndex2 = 0;
-	if (value2) {
+	if (entryTblPtr) {
 		_actor->_flags |= 0x80;
-		_actor->_field30 = value2;
+		_actor->_entryTblPtr = entryTblPtr;
 		_actor->_notifyThreadId1 = 0;
 		_actor->_notifyThreadId2 = notifyThreadId;
 	}
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 63ae4f8..28c539a 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -119,7 +119,7 @@ public:
 	uint32 _notifyId3C;
 
 	uint32 _notifyThreadId2;
-	int _field30;
+	byte *_entryTblPtr;
 	
 	int _surfaceTextFlag;
 	
@@ -185,7 +185,7 @@ public:
 	Common::Point _position;
 	Common::Point _subobjectsPos[kSubObjectsCount];
 	// TODO 0000001C - 00000054 unknown
-	void startSequenceActorIntern(uint32 sequenceId, int value, int value2, uint32 notifyThreadId);
+	void startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId);
 	void execSequenceOpcode(OpCall &opCall);
 };
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index d758c1f..45c587d 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -367,7 +367,35 @@ Common::Point IllusionsEngine::getNamedPointPosition(uint32 namedPointId) {
 }
 
 void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
-	
+	// TODO
+}
+
+bool IllusionsEngine::isSoundActive() {
+	// TODO
+	return false;
+}
+
+bool IllusionsEngine::cueVoice(byte *voiceName) {
+	// TODO
+	return true;
+}
+
+bool IllusionsEngine::isVoiceCued() {
+	// TODO
+	return false;
+}
+
+void IllusionsEngine::startVoice(int volume, int panX) {
+	// TODO
+}
+
+void IllusionsEngine::stopVoice() {
+	// TODO
+}
+
+bool IllusionsEngine::isVoicePlaying() {
+	// TODO
+	return false;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 2fa7631..1b44a07 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -113,6 +113,13 @@ public:
 	int convertPanXCoord(int16 x);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
+	
+	bool isSoundActive();
+	bool cueVoice(byte *voiceName);
+	bool isVoiceCued();
+	void startVoice(int volume, int panX);
+	void stopVoice();
+	bool isVoicePlaying();	
 
 #if 0
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index de705dd..49a6e2a 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -25,6 +25,7 @@ MODULE_OBJS := \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	talkresource.o \
+	talkthread.o \
 	thread.o \
 	time.o \
 	timerthread.o \
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 2ba39c7..eaaeaa5 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -26,6 +26,7 @@
 #include "illusions/scriptman.h"
 #include "illusions/scriptthread.h"
 #include "illusions/scriptopcodes.h"
+#include "illusions/talkthread.h"
 #include "illusions/timerthread.h"
 
 namespace Illusions {
@@ -128,6 +129,9 @@ ScriptMan::ScriptMan(IllusionsEngine *vm)
 	: _vm(vm), _pauseCtr(0), _doScriptThreadInit(false) {
 	_threads = new ThreadList(vm);
 	_scriptOpcodes = new ScriptOpcodes(vm);
+	_field8 = 1;
+	_fieldA = 0;
+	_fieldE = 240;
 }
 
 ScriptMan::~ScriptMan() {
@@ -158,8 +162,8 @@ void ScriptMan::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
 
 uint32 ScriptMan::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
-	debug("Starting temp script thread");
 	uint32 tempThreadId = newTempThreadId();
+	debug("Starting temp script thread %08X", tempThreadId);
 	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
 	return tempThreadId;
 }
@@ -173,21 +177,56 @@ uint32 ScriptMan::startTimerThread(uint32 duration, uint32 threadId) {
 }
 
 uint32 ScriptMan::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
-	debug("Starting abortable thread");
 	uint32 tempThreadId = newTempThreadId();
+	debug("Starting abortable thread %08X", tempThreadId);
 	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
-	AbortableThread *abortableThread = new AbortableThread(_vm, tempThreadId, callingThreadId, 0, scriptThreadId, scriptCodeIp2);
+	AbortableThread *abortableThread = new AbortableThread(_vm, tempThreadId, callingThreadId, 0,
+		scriptThreadId, scriptCodeIp2);
 	_threads->startThread(abortableThread);
 	return tempThreadId;
 }
 
+uint32 ScriptMan::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
+	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
+	debug("Starting talk thread");
+	uint32 tempThreadId = newTempThreadId();
+	// TODO endTalkThreadsNoNotify();
+	TalkThread *talkThread = new TalkThread(_vm, tempThreadId, callingThreadId, 0,
+		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
+	_threads->startThread(talkThread);
+	return tempThreadId;
+}
+
 void ScriptMan::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
 
+bool ScriptMan::checkActiveTalkThreads() {
+	// TODO
+	return false;
+}
+
+uint32 ScriptMan::clipTextDuration(uint32 duration) {
+	switch (_field8) {
+	case 2:
+		if (duration == 0)
+			duration = 240;
+		break;
+	case 3:
+		if (duration < _fieldA)
+			duration = _fieldA;
+		break;
+	case 4:
+		if (duration > _fieldA)
+			duration = _fieldA;
+		break;
+	}
+	return duration;
+}
+
 void ScriptMan::reset() {
-	// TODO _scriptResource->_blockCounters.clear();
-	// TODO _scriptResource->_properties.clear();
+	_scriptResource->_blockCounters.clear();
+	_scriptResource->_properties.clear();
 	// TODO script_sub_417FF0(1, 0);
 }
 
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index 0ce0ef1..fc11cdf 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -81,7 +81,11 @@ public:
 	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
 	uint32 startTimerThread(uint32 duration, uint32 threadId);
 	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
+	uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
+		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
 	void setCurrFontId(uint32 fontId);
+	bool checkActiveTalkThreads();
+	uint32 clipTextDuration(uint32 duration);
 	void reset();
 	bool enterScene(uint32 sceneId, uint32 threadId);
 	void exitScene(uint32 threadId);
@@ -101,6 +105,9 @@ public:
 	uint32 _nextTempThreadId;
 	
 	uint32 _fontId;
+	int _field8;
+	uint32 _fieldA, _fieldE;
+	
 	uint32 _prevSceneId;
 	
 	ThreadList *_threads;
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 6d32800..0212b10 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -130,6 +130,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(150, opRunSpecialCode);
 	OPCODE(161, opSetActorUsePan);
 	OPCODE(168, opStartAbortableThread);
+	OPCODE(169, opKillThread);
 	OPCODE(175, opSetSceneIdThreadId);
 	OPCODE(176, opStackPush0);
 	OPCODE(177, opSetFontId);
@@ -278,17 +279,17 @@ void ScriptOpcodes::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opC
 }
 
 void ScriptOpcodes::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value);	
+	ARG_INT16(duration);	
 	ARG_UINT32(objectId);
 	ARG_UINT32(talkId);
 	ARG_UINT32(sequenceId1);
 	ARG_UINT32(sequenceId2);
-	ARG_UINT32(value2);
+	ARG_UINT32(namedPointId);
 	// NOTE Skipped checking for stalled sequence, not sure if needed
-	// TODO _vm->_scriptMan->startTalkThread(value, objectId, talkId, sequenceId1, sequenceId2, value2, opCall._callerThreadId);
+	_vm->_scriptMan->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._callerThreadId);
 
 	//DEBUG Resume calling thread, later done after talking is finished
-	_vm->notifyThreadId(opCall._threadId);
+	//_vm->notifyThreadId(opCall._threadId);
 }
 
 void ScriptOpcodes::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
@@ -429,7 +430,7 @@ void ScriptOpcodes::opActivateButton(ScriptThread *scriptThread, OpCall &opCall)
 void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(jumpOffs)
 	int16 value = _vm->_scriptMan->_stack.pop();
-	if (!value)
+	if (value == 0)
 		opCall._deltaOfs += jumpOffs;
 }
 
@@ -450,10 +451,11 @@ void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &op
 	ARG_INT16(compareOp);	
 	ARG_INT16(rvalue);
 	int16 lvalue = _vm->_scriptMan->_scriptResource->_blockCounters.get(index);
+	debug("lvalue = %d", lvalue);
 	bool compareResult = false;
 	switch (compareOp) {
 	case 1:
-		compareResult = lvalue == rvalue;
+		compareResult = false;//lvalue == rvalue;
 		break;
 	case 2:
 		compareResult = lvalue != rvalue;
@@ -471,6 +473,7 @@ void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &op
 		compareResult = lvalue <= rvalue;
 		break;
 	}
+	debug("  compareResult -> %d", compareResult);
 	_vm->_scriptMan->_stack.push(compareResult ? 1 : 0);
 }
 
@@ -529,8 +532,14 @@ void ScriptOpcodes::opStartAbortableThread(ScriptThread *scriptThread, OpCall &o
 	ARG_SKIP(2);
 	ARG_INT16(codeOffs);
 	ARG_INT16(skipOffs);
-	_vm->_scriptMan->startAbortableThread(opCall._code + opCall._opSize + codeOffs,
-		opCall._code + opCall._opSize + skipOffs, opCall._callerThreadId);
+	_vm->_scriptMan->startAbortableThread(opCall._code + codeOffs,
+		opCall._code + skipOffs, opCall._callerThreadId);
+}
+
+void ScriptOpcodes::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_scriptMan->_threads->killThread(threadId);
 }
 
 void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 060171d..209aa78 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -107,6 +107,7 @@ protected:
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 29f123e..6aa8ec6 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -59,6 +59,12 @@ void Properties::init(uint count, byte *properties) {
 	_properties = properties;
 }
 
+void Properties::clear() {
+	uint size = (_count >> 3) + 1;
+	for (uint i = 0; i < size; ++i)
+		_properties[i] = 0;
+}
+
 bool Properties::get(uint32 propertyId) {
 	uint index;
 	byte mask;
@@ -103,7 +109,7 @@ byte BlockCounters::get(uint index) {
 }
 
 void BlockCounters::set(uint index, byte value) {
-	_blockCounters[index - 1] = (get(index - 1) ^ value) & 0x3F;
+	_blockCounters[index - 1] ^= (_blockCounters[index - 1] ^ value) & 0x3F;
 }
 
 // TriggerCause
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 79c46c8..e2df45a 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -45,6 +45,7 @@ class Properties {
 public:
 	Properties();
 	void init(uint count, byte *properties);
+	void clear();
 	bool get(uint32 propertyId);
 	void set(uint32 propertyId, bool value);
 public:
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
new file mode 100644
index 0000000..50c6266
--- /dev/null
+++ b/engines/illusions/talkthread.cpp
@@ -0,0 +1,301 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/talkthread.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/scriptman.h"
+#include "illusions/talkresource.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// TalkThread
+
+TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
+	uint32 namedPointId)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _objectId(objectId), _talkId(talkId),
+	_sequenceId1(0), _sequenceId2(0) {
+	_type = kTTTalkThread;
+
+	if (sequenceId1 && _vm->_dict->getObjectControl(objectId)) {
+		_sequenceId1 = sequenceId1;
+		_sequenceId2 = sequenceId2;
+	}
+
+	if (!callingThreadId)
+		_sequenceId2 = 0;
+
+	_namedPointId = namedPointId;
+
+	if (duration)
+		_status = 1;
+	else if (_vm->_scriptMan->checkActiveTalkThreads())
+		_status = 2;
+	else
+		_status = 3;
+	
+	_flags = 0x0E;
+	
+	_durationMult = _vm->_scriptMan->clipTextDuration(_vm->_scriptMan->_fieldE);
+	_textDuration = _durationMult;
+	_defDurationMult = _vm->_scriptMan->clipTextDuration(240);
+	_textStartTime = 0;
+	_textEndTime = 0;
+	_textDurationElapsed = 0;
+	_entryText = 0;
+	_currEntryText = 0;
+	_voiceDurationElapsed = 0;
+	_voiceDuration = duration;
+	_voiceStartTime = getCurrentTime();
+	_voiceEndTime = _voiceStartTime + duration;
+	_entryTblPtr = 0;
+	
+	/* TODO
+	if (callingThreadId)
+		thread->tag = *(_DWORD *)(krndictGetIDValue(callingThreadId) + 20);
+	*/
+	
+}
+
+int TalkThread::onUpdate() {
+
+	TalkEntry *talkEntry;
+
+	switch (_status) {
+
+	case 1:
+		if (isTimerExpired(_voiceStartTime, _voiceEndTime)) {
+			if (_vm->_scriptMan->checkActiveTalkThreads())
+				_status = 2;
+			else
+				_status = 3;
+		}
+		return kTSYield;
+
+	case 2:
+		if (_vm->_scriptMan->checkActiveTalkThreads())
+			return kTSYield;
+		_status = 3;
+		// Fallthrough to status 3
+
+	case 3:
+		talkEntry = getTalkResourceEntry(_talkId);
+		_flags = 0;
+		_currEntryText = 0;
+		_entryText = talkEntry->_text;
+		_entryTblPtr = talkEntry->_tblPtr;
+		if (_sequenceId1) {
+			_pauseCtr = 0;
+			// TODO _field30 = v6;
+		} else {
+			_flags = 3;
+			// TODO _field30 = 0;
+		}
+		if (_vm->isSoundActive()) {
+			if (!_vm->cueVoice(talkEntry->_voiceName) && !_durationMult)
+				_durationMult = _defDurationMult;
+		} else {
+			_flags |= 4;
+			if (_durationMult == 0)
+				_durationMult = _defDurationMult;
+		}
+		if (_objectId == 0 || _durationMult == 0)
+			_flags |= 8;
+		_status = 4;
+		// Fallthrough to status 4
+
+	case 4:
+		if (!(_flags & 4) && !_vm->isVoiceCued())
+			return kTSYield;
+		_status = 5;
+		// Fallthrough to status 5
+		
+	case 5:
+		if (!(_flags & 8))
+			refreshText();
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			// TODOcontrol->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
+		}
+		if (!(_flags & 4)) {
+			int16 panX = 0;
+			if (_namedPointId) {
+				// TODO pt.x = (unsigned int)artcntrlGetNamedPointPosition((Point)_namedPointId);
+				// TODO panX = convertPanXCoord(pt.x);
+			}
+			_vm->startVoice(255, panX);
+		}
+		_vm->_input->discardButtons(16);
+		_status = 6;
+		return kTSYield;
+
+	case 6:
+		if (!(_flags & 4) && !_vm->isVoicePlaying())
+			_flags |= 4;
+		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
+			// TODO _vm->removeText();
+			if (_entryText && *_entryText) {
+				refreshText();
+				_vm->_input->discardButtons(16);
+			} else {
+				_flags |= 8;
+			}
+		}
+		if ((_flags & 4) && (_flags & 8)) {
+			if (_sequenceId2) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+			if (_sequenceId1) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->clearNotifyThreadId2();
+			}
+			_flags |= 2;
+		}
+		if (_objectId && _vm->_input->pollButton(0x10)) {
+			if (!(_flags & 8)) {
+				// TODO _vm->removeText();
+				if (_entryText && *_entryText)
+					refreshText();
+				else
+					_flags |= 8;
+			}
+			if (_flags & 8) {
+				if (!(_flags & 4)) {
+					_vm->stopVoice();
+					_flags |= 4;
+				}
+				if (!(_flags & 2)) {
+					if (_sequenceId2) {
+						Control *control = _vm->_dict->getObjectControl(_objectId);
+						control->startSequenceActor(_sequenceId2, 2, 0);
+					}
+					if (_sequenceId1) {
+						Control *control = _vm->_dict->getObjectControl(_objectId);
+						control->clearNotifyThreadId2();
+					}
+					_flags |= 2;
+				}
+			}
+		}
+		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
+			_vm->_input->discardButtons(0x10);
+			_status = 7;
+			return kTSTerminate;
+		}
+		return kTSYield;
+
+	case 7:
+		if (!(_flags & 2)) {
+			if (_sequenceId2) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+			if (_sequenceId1) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->clearNotifyThreadId2();
+			}
+			_flags |= 2;
+		}
+		if (!(_flags & 8)) {
+			// TODO _vm->removeText();
+			_flags |= 8;
+		}
+		if (!(_flags & 4)) {
+			_vm->stopVoice();
+			_flags |= 4;
+		}
+		return kTSTerminate;
+
+	}
+	
+	return kTSTerminate;
+
+}
+
+void TalkThread::onSuspend() {
+}
+
+void TalkThread::onNotify() {
+}
+
+void TalkThread::onPause() {
+}
+
+void TalkThread::onResume() {
+}
+
+void TalkThread::onTerminated() {
+}
+
+void TalkThread::refreshText() {
+	_currEntryText = _entryText;
+	int charCount = insertText();
+	uint32 duration = _durationMult;
+	if (charCount < 80) {
+		duration = _durationMult * charCount / 80;
+		if (duration < 25 * _durationMult / 100)
+			duration = 25 * _durationMult / 100;
+		if (duration < 60)
+			duration = 60;
+	}
+	_textDuration = duration;
+	_textStartTime = getCurrentTime();
+	_textEndTime = _textStartTime + _textDuration;
+}
+
+static char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	while (*wstr != 0) {
+		*p++ = *wstr;
+		wstr += 2;
+	}
+	*p = 0;
+	return buf;
+}
+
+int TalkThread::insertText() {
+	int charCount = 100;
+	
+	debug("[%s]", debugW2I(_currEntryText));
+	_entryText = 0;
+	
+	// TODO _vm->getDimensions1(&dimensions);
+	// TODO _vm->insertText(_currEntryText, _vm->_scriptMan->currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
+	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
+	// TODO _entryText = outTextPtr;
+	// TODO _vm->getPoint1(&pt);
+	// TODO _vm->updateTextInfoPosition(pt);
+	return charCount >> 1;
+}
+
+TalkEntry *TalkThread::getTalkResourceEntry(uint32 talkId) {
+	TalkEntry *talkEntry = _vm->_dict->findTalkEntry(talkId);
+	return talkEntry;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/talkthread.h b/engines/illusions/talkthread.h
new file mode 100644
index 0000000..fd0e3d0
--- /dev/null
+++ b/engines/illusions/talkthread.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TALKTHREAD_H
+#define ILLUSIONS_TALKTHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+struct TalkEntry;
+
+class TalkThread : public Thread {
+public:
+	TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
+		uint32 namedPointId);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	//field0 dw
+	int _status;
+	uint _flags;
+	uint32 _textStartTime;
+	uint32 _textEndTime;
+	uint32 _textDuration;
+	uint32 _defDurationMult;
+	uint32 _textDurationElapsed;
+	uint32 _durationMult;
+	//field12 dw
+	uint32 _objectId;
+	uint32 _talkId;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	byte *_entryTblPtr;
+	byte *_entryText;
+	byte *_currEntryText;
+	//field30 dd
+	uint32 _namedPointId;
+	uint32 _voiceStartTime;
+	uint32 _voiceEndTime;
+	uint32 _voiceDuration;
+	uint32 _voiceDurationElapsed;
+	void refreshText();
+	int insertText();
+	TalkEntry *getTalkResourceEntry(uint32 talkId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKTHREAD_H
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 83e0ce5..0a9b0d9 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -101,8 +101,10 @@ int Thread::update() {
 
 void Thread::terminate() {
 	if (!_terminated) {
-		if (!(_notifyFlags & 1))
+		if (!(_notifyFlags & 1)) {
+			debug("Thread::terminate() _callingThreadId: %08X", _callingThreadId);
 			_vm->notifyThreadId(_callingThreadId);
+		}
 		_callingThreadId = 0;
 		onTerminated();
 		// TODO _vm->removeThread(_threadId, this);


Commit: a6a4a3dc1cb346afc1f7b0491158d8b00ac063c5
    https://github.com/scummvm/scummvm/commit/a6a4a3dc1cb346afc1f7b0491158d8b00ac063c5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement more script opcodes

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/talkthread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 933ebee..b813ff0 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -533,6 +533,26 @@ void Control::stopSequenceActor() {
 		}
 }
 
+void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId) {
+	bool doSeq = true;
+	if (_actor->_linkIndex2) {
+		Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[_actor->_linkIndex2 - 1]);
+		if (subControl->_actor->_flags & 1) {
+			/* TODO
+			if (control->_actor->pathNode) {
+				doSeq = false;
+				subControl->_actor->notifyThreadId2 = threadId;
+				subControl->_actor->entryTblPtr = entryTblPtr;
+				subControl->_actor->flags |= 0x80;
+				script_TalkThreads_sub_417FA0(subControl->_actor->_notifyThreadId2, 0);
+			}
+			*/
+		}
+	}
+	if (doSeq)
+		startSequenceActorIntern(sequenceId, 2, entryTblPtr, threadId);
+}
+
 void Control::sequenceActor() {
 
 	if (_actor->_pauseCtr > 0)
@@ -591,15 +611,14 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_flags |= 0x0100;
 
 	sequenceId = _actor->_defaultSequences.use(sequenceId);
-	
+
 	_actor->_sequenceId = sequenceId;
 	_actor->_notifyThreadId1 = notifyThreadId;
 	_actor->_notifyId3C = 0;
 	_actor->_path40 = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
-	debug(1, "Control::startSequenceActorIntern() sequence = %p", (void*)sequence);
-	
+
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
 	
@@ -774,6 +793,58 @@ void Controls::destroyControlsByTag(uint32 tag) {
 	}
 }
 
+void Controls::pauseControlsByTag(uint32 tag) {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_tag == tag) {
+			++control->_pauseCtr;
+			if (control->_pauseCtr == 1)
+				control->pause();
+		}
+	}
+}
+
+void Controls::unpauseControlsByTag(uint32 tag) {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_tag == tag) {
+			--control->_pauseCtr;
+			if (control->_pauseCtr == 0)
+				control->unpause();
+		}
+	}
+}
+
+void Controls::actorControlRouine(Control *control, uint32 deltaTime) {
+
+	Actor *actor = control->_actor;
+
+	if (actor->_pauseCtr > 0)
+		return;
+
+	if (false/*actor->_pathNode*/) {
+		// TODO Update pathwalking
+	} else {
+		actor->_seqCodeValue1 = 100 * deltaTime;
+	}
+
+	if (actor->_flags & 4) {
+		int scale = actor->_scaleLayer->getScale(actor->_position);
+		control->setActorScale(scale);
+	}
+
+	if (actor->_flags & 8) {
+		int16 priority = actor->_priorityLayer->getPriority(actor->_position);
+		if (priority)
+			control->setPriority(priority + 1);
+	}
+
+	if (actor->_flags & 0x20) {
+		// TODO Update transition sequence (seems to be unused in BBDOU?)
+	}
+
+}
+
 Actor *Controls::newActor() {
 	return new Actor(_vm);
 }
@@ -811,34 +882,4 @@ void Controls::destroyControl(Control *control) {
 	delete control;
 }
 
-void Controls::actorControlRouine(Control *control, uint32 deltaTime) {
-
-	Actor *actor = control->_actor;
-
-	if (actor->_pauseCtr > 0)
-		return;
-
-	if (false/*actor->_pathNode*/) {
-		// TODO Update pathwalking
-	} else {
-		actor->_seqCodeValue1 = 100 * deltaTime;
-	}
-
-	if (actor->_flags & 4) {
-		int scale = actor->_scaleLayer->getScale(actor->_position);
-		control->setActorScale(scale);
-	}
-
-	if (actor->_flags & 8) {
-		int16 priority = actor->_priorityLayer->getPriority(actor->_position);
-		if (priority)
-			control->setPriority(priority + 1);
-	}
-
-	if (actor->_flags & 0x20) {
-		// TODO Update transition sequence (seems to be unused in BBDOU?)
-	}
-
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 28c539a..fe5564e 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -167,6 +167,7 @@ public:
 	void stopActor();
 	void startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId);
 	void stopSequenceActor();
+	void startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId);
 	void sequenceActor();
 public:
 	IllusionsEngine *_vm;
@@ -198,6 +199,8 @@ public:
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
 	void destroyControlsByTag(uint32 tag);
+	void pauseControlsByTag(uint32 tag);
+	void unpauseControlsByTag(uint32 tag);
 	void actorControlRouine(Control *control, uint32 deltaTime);	
 public:
 	typedef Common::List<Control*> Items;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index 831a932..bb3cd4d 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -33,6 +33,7 @@ void ActorResourceLoader::load(Resource *resource) {
 
 	ActorResource *actorResource = new ActorResource();
 	actorResource->load(resource->_data, resource->_dataSize);
+	resource->_refId = actorResource;
 	
 	ActorItem *actorItem = _vm->_actorItems->allocActorItem();
 	actorItem->_tag = resource->_tag;
@@ -63,6 +64,17 @@ void ActorResourceLoader::load(Resource *resource) {
 }
 
 void ActorResourceLoader::unload(Resource *resource) {
+	debug("ActorResourceLoader::unload() Unloading actor %08X...", resource->_resId);
+	// TODO Move to ActorItems
+	ActorItem *actorItem = _vm->_actorItems->findActorByResource((ActorResource*)resource->_refId);
+	if (actorItem->_pauseCtr <= 0) {
+		for (uint i = 0; i < actorItem->_actRes->_actorTypes.size(); ++i)
+			_vm->_dict->removeActorType(actorItem->_actRes->_actorTypes[i]._actorTypeId);
+		for (uint i = 0; i < actorItem->_actRes->_sequences.size(); ++i)
+			_vm->_dict->removeSequence(actorItem->_actRes->_sequences[i]._sequenceId);
+	}
+	_vm->_actorItems->freeActorItem(actorItem);
+	delete actorItem->_actRes;
 }
 
 void ActorResourceLoader::buildFilename(Resource *resource) {
@@ -241,6 +253,11 @@ ActorItem *ActorItems::allocActorItem() {
 	return actorItem;
 }
 
+void ActorItems::freeActorItem(ActorItem *actorItem) {
+	_items.remove(actorItem);
+	delete actorItem;
+}
+
 void ActorItems::pauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
@@ -250,7 +267,7 @@ void ActorItems::pauseByTag(uint32 tag) {
 void ActorItems::unpauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
-			(*it)->pause();
+			(*it)->unpause();
 }
 
 FramesList *ActorItems::findSequenceFrames(Sequence *sequence) {
@@ -262,4 +279,11 @@ FramesList *ActorItems::findSequenceFrames(Sequence *sequence) {
 	return 0;
 }
 
+ActorItem *ActorItems::findActorByResource(ActorResource *actorResource) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_actRes == actorResource)
+			return (*it);
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index b15f3a0..e22ed7e 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -111,9 +111,11 @@ public:
 	ActorItems(IllusionsEngine *vm);
 	~ActorItems();
 	ActorItem *allocActorItem();
+	void freeActorItem(ActorItem *actorItem);
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
 	FramesList *findSequenceFrames(Sequence *sequence);
+	ActorItem *findActorByResource(ActorResource *actorResource);
 protected:
 	typedef Common::List<ActorItem*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index eaaeaa5..de18dee 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -21,9 +21,10 @@
  */
 
 #include "illusions/illusions.h"
+#include "illusions/scriptman.h"
 #include "illusions/abortablethread.h"
 #include "illusions/actor.h"
-#include "illusions/scriptman.h"
+#include "illusions/camera.h"
 #include "illusions/scriptthread.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/talkthread.h"
@@ -251,6 +252,26 @@ void ScriptMan::exitScene(uint32 threadId) {
 	_activeScenes.pop();
 }
 
+void ScriptMan::enterPause(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	_vm->_camera->pushCameraMode();
+	_threads->suspendThreadsByTag(sceneId, threadId);
+	_vm->_controls->pauseControlsByTag(sceneId);
+	_vm->_actorItems->pauseByTag(sceneId);
+	_vm->_backgroundItems->pauseByTag(sceneId);
+	_activeScenes.pauseActiveScene();
+}
+
+void ScriptMan::leavePause(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	_vm->_backgroundItems->unpauseByTag(sceneId);
+	_vm->_actorItems->unpauseByTag(sceneId);
+	_vm->_controls->unpauseControlsByTag(sceneId);
+	_threads->notifyThreadsByTag(sceneId, threadId);
+	_vm->_camera->popCameraMode();
+	_activeScenes.unpauseActiveScene();
+}
+
 void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
 	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId, notifyFlags,
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index fc11cdf..bd9f2c7 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -89,6 +89,8 @@ public:
 	void reset();
 	bool enterScene(uint32 sceneId, uint32 threadId);
 	void exitScene(uint32 threadId);
+	void enterPause(uint32 threadId);
+	void leavePause(uint32 threadId);
 public:
 
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 0212b10..8d19fbf 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -92,8 +92,11 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(14, opSetThreadSceneId);
 	OPCODE(15, opEndTalkThreads);
 	OPCODE(16, opLoadResource);
+	OPCODE(17, opUnloadResource);
 	OPCODE(20, opEnterScene);
 	OPCODE(25, opChangeScene);
+	OPCODE(30, opEnterCloseUpScene);
+	OPCODE(31, opExitCloseUpScene);
 	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
 	OPCODE(45, opSetProperty);
@@ -109,7 +112,10 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(65, opSetDenySfx);
 	OPCODE(66, opSetAdjustUpSfx);
 	OPCODE(67, opSetAdjustDnSfx);
+	OPCODE(71, opStartSound);
+	OPCODE(74, opStopSound);
 	OPCODE(75, opStartMusic);
+	OPCODE(76, opStopMusic);
 	OPCODE(78, opStackPushRandom);
 	OPCODE(79, opIfLte);
 	OPCODE(80, opAddMenuChoice);
@@ -128,6 +134,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(147, opStackDup);
 	OPCODE(148, opLoadSpecialCodeModule);
 	OPCODE(150, opRunSpecialCode);
+	OPCODE(160, opStopActor);
 	OPCODE(161, opSetActorUsePan);
 	OPCODE(168, opStartAbortableThread);
 	OPCODE(169, opKillThread);
@@ -214,6 +221,13 @@ void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
 }
 
+void ScriptOpcodes::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_resSys->unloadResourceById(resourceId);
+}
+
 void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -241,6 +255,21 @@ void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
 }
 
+void ScriptOpcodes::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_scriptMan->enterPause(opCall._callerThreadId);
+	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_scriptMan->exitScene(opCall._callerThreadId);
+	_vm->_scriptMan->leavePause(opCall._callerThreadId);
+	opCall._result = kTSYield;
+}
+
 void ScriptOpcodes::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(flag);
 	_vm->_screen->setDisplayOn(flag != 0);
@@ -275,6 +304,7 @@ void ScriptOpcodes::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opC
 	ARG_UINT32(sequenceId);
 	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
+	debug("control: %p", (void*)control);
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
@@ -356,6 +386,20 @@ void ScriptOpcodes::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall)
 	ARG_UINT32(soundEffectId);
 	// TODO _vm->setAdjustDnSfx(soundEffectId);
 }
+
+void ScriptOpcodes::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(volume);
+	ARG_INT16(pan);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->startSound(soundEffectId, volume, pan);
+}
+void ScriptOpcodes::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->stopSound(soundEffectId);
+}
+
 void ScriptOpcodes::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_INT16(volume);
@@ -365,6 +409,10 @@ void ScriptOpcodes::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	// TODO _vm->playMusic(musicId, type, volume, pan);
 }
 
+void ScriptOpcodes::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	// TODO _vm->stopMusic();
+}
+
 void ScriptOpcodes::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(maxValue);
 	_vm->_scriptMan->_stack.push(_vm->getRandom(maxValue) + 1);
@@ -406,7 +454,6 @@ void ScriptOpcodes::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCal
 _vm->_scriptMan->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
 
 	opCall._deltaOfs += _vm->_scriptMan->_menuChoiceOfs;
-debug("deltaOfs = %08X", opCall._deltaOfs);	
 }
 
 void ScriptOpcodes::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
@@ -451,7 +498,6 @@ void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &op
 	ARG_INT16(compareOp);	
 	ARG_INT16(rvalue);
 	int16 lvalue = _vm->_scriptMan->_scriptResource->_blockCounters.get(index);
-	debug("lvalue = %d", lvalue);
 	bool compareResult = false;
 	switch (compareOp) {
 	case 1:
@@ -473,7 +519,6 @@ void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &op
 		compareResult = lvalue <= rvalue;
 		break;
 	}
-	debug("  compareResult -> %d", compareResult);
 	_vm->_scriptMan->_stack.push(compareResult ? 1 : 0);
 }
 
@@ -521,6 +566,13 @@ void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall)
 
 }
 
+void ScriptOpcodes::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->stopActor();
+}
+
 void ScriptOpcodes::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(usePan)
 	ARG_UINT32(objectId);
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 209aa78..0e75f78 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -69,8 +69,11 @@ protected:
 	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
@@ -86,7 +89,10 @@ protected:
 	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
 	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
 	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
@@ -105,6 +111,7 @@ protected:
 	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index b550be6..2495bca 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -64,6 +64,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(12, opNextLoop);
 	OPCODE(14, opSwitchActorIndex);
 	OPCODE(15, opSwitchFacing);
+	OPCODE(17, opDisappearActor);
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
 	OPCODE(33, opSetPathWalkPoints);
@@ -92,26 +93,17 @@ void SequenceOpcodes::freeOpcodes() {
 void SequenceOpcodes::opSetFrameIndex(Control *control, OpCall &opCall) {
 	ARG_INT16(frameIndex);
 	if (control->_actor->_flags & 0x80) {
-		debug(1, "opSetFrameIndex TODO");
-		/* TODO
-		v9 = actor->field30;
-		if (*(_WORD *)v9) {
-			LOWORD(flag) = *(_WORD *)v9;
-			v6 = v6 + flag - 1;
-			actor->field30 = v9 + 2;
+		int16 frameIncr = READ_LE_UINT16(control->_actor->_entryTblPtr);
+		if (frameIncr) {
+			frameIndex += frameIncr - 1;
+			control->_actor->_entryTblPtr += 2;
 		} else {
-			actor->flags &= 0xFF7Fu;
-			v10 = actor->notifyThreadId1;
-			actor->field30 = 0;
-			actor->notifyThreadId2 = 0;
-			if (v10) {
-				actor->notifyThreadId1 = 0;
-				ThreadList_notifyId__(v10);
-			}
-			actorDead = 1;
-			breakInner = 1;
+			control->_actor->_flags &= ~0x80;
+			control->_actor->_entryTblPtr = 0;
+			control->_actor->_notifyThreadId2 = 0;
+			_vm->notifyThreadId(control->_actor->_notifyThreadId1);
+			opCall._result = 1;
 		}
-		*/
 	}
 	control->_actor->_flags &= ~0x0100;
 	if (control->_actor->_flags & 0x8000) {
@@ -191,6 +183,11 @@ void SequenceOpcodes::opSwitchFacing(Control *control, OpCall &opCall) {
 		opCall._deltaOfs += jumpOffs;
 }
 
+void SequenceOpcodes::opDisappearActor(Control *control, OpCall &opCall) {
+	control->disappearActor();
+	control->_actor->_newFrameIndex = 0;
+}
+
 void SequenceOpcodes::opNotifyThreadId1(Control *control, OpCall &opCall) {
 	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
 }
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 31827aa..dd1302f 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -55,6 +55,7 @@ protected:
 	void opNextLoop(Control *control, OpCall &opCall);
 	void opSwitchActorIndex(Control *control, OpCall &opCall);
 	void opSwitchFacing(Control *control, OpCall &opCall);
+	void opDisappearActor(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
 	void opSetPathWalkPoints(Control *control, OpCall &opCall);
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index 50c6266..fc777b7 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -138,7 +138,7 @@ int TalkThread::onUpdate() {
 			refreshText();
 		if (!(_flags & 2)) {
 			Control *control = _vm->_dict->getObjectControl(_objectId);
-			// TODOcontrol->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
+			control->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
 		}
 		if (!(_flags & 4)) {
 			int16 panX = 0;


Commit: 28cb39eb2b48d8c6e80e35ef6e26e02cc209fc0f
    https://github.com/scummvm/scummvm/commit/28cb39eb2b48d8c6e80e35ef6e26e02cc209fc0f
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start with BBDOU special code

Changed paths:
  A engines/illusions/bbdou/bbdou_cursor.cpp
  A engines/illusions/bbdou/bbdou_cursor.h
  A engines/illusions/bbdou/bbdou_specialcode.cpp
  A engines/illusions/bbdou/bbdou_specialcode.h
  A engines/illusions/specialcode.cpp
  A engines/illusions/specialcode.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes.cpp


diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
new file mode 100644
index 0000000..9c0bcc8
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/bbdou/bbdou_cursor.h"
+#include "illusions/actor.h"
+
+namespace Illusions {
+
+BbdouCursor::BbdouCursor(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+BbdouCursor::~BbdouCursor() {
+}
+
+void BbdouCursor::init() {
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
new file mode 100644
index 0000000..df3ed82
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_CURSOR_H
+#define ILLUSIONS_BBDOU_BBDOU_CURSOR_H
+
+#include "illusions/specialcode.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class BbdouCursor {
+public:
+	BbdouCursor(IllusionsEngine *vm);
+	~BbdouCursor();
+	void init();
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_CURSOR_H
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
new file mode 100644
index 0000000..028fdb5
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/bbdou/bbdou_specialcode.h"
+#include "illusions/bbdou/bbdou_cursor.h"
+#include "illusions/scriptopcodes.h"
+
+namespace Illusions {
+
+// BbdouSpecialCode
+
+BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine *vm)
+	: SpecialCode(vm) {
+}
+
+BbdouSpecialCode::~BbdouSpecialCode() {
+}
+
+void BbdouSpecialCode::init() {
+}
+
+void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
new file mode 100644
index 0000000..98aef58
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
+#define ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
+
+#include "illusions/specialcode.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+class BbdouCursor;
+
+class BbdouSpecialCode : public SpecialCode {
+public:
+	BbdouSpecialCode(IllusionsEngine *vm);
+	virtual ~BbdouSpecialCode();
+	virtual void init();
+	virtual void run(uint32 specialCodeId, OpCall &opCall);
+public:
+	BbdouCursor *_cursor;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 45c587d..a59c568 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -35,6 +35,7 @@
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
 #include "illusions/soundresource.h"
+#include "illusions/specialcode.h"
 #include "illusions/talkresource.h"
 #include "illusions/thread.h"
 #include "illusions/time.h"
@@ -95,6 +96,7 @@ Common::Error IllusionsEngine::run() {
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
+	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
 
 	_screen = new Screen(this);
 	_input = new Input();	
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 1b44a07..e0f27b0 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -64,6 +64,7 @@ class Screen;
 class ScriptResource;
 class ScriptMan;
 class Sequence;
+class SpecialCode;
 class TalkItems;
 class FramesList;
 
@@ -95,6 +96,7 @@ public:
 	Controls *_controls;
 	Cursor *_cursor;
 	TalkItems *_talkItems;
+	SpecialCode *_specialCode;
 	
 	int _resGetCtr;
 	uint32 _resGetTime;
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 49a6e2a..77f089a 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -5,6 +5,8 @@ MODULE_OBJS := \
 	actor.o \
 	actorresource.o \
 	backgroundresource.o \
+	bbdou/bbdou_cursor.o \
+	bbdou/bbdou_specialcode.o \
 	camera.o \
 	cursor.o \
 	detection.o \
@@ -22,6 +24,7 @@ MODULE_OBJS := \
 	scriptthread.o \
 	sequenceopcodes.o \
 	soundresource.o \
+	specialcode.o \
 	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	talkresource.o \
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 8d19fbf..a191eff 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -551,6 +551,7 @@ void ScriptOpcodes::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
 void ScriptOpcodes::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeModuleId);
+	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
 	// TODO _vm->loadSpecialCodeModule(specialCodeModuleId);
 }
 
diff --git a/engines/illusions/specialcode.cpp b/engines/illusions/specialcode.cpp
new file mode 100644
index 0000000..7ac49a2
--- /dev/null
+++ b/engines/illusions/specialcode.cpp
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/specialcode.h"
+#include "illusions/bbdou/bbdou_specialcode.h"
+
+namespace Illusions {
+
+// SpecialCodeLoader
+
+void SpecialCodeLoader::load(Resource *resource) {
+	debug("SpecialCodeLoader::load() Loading special code %08X...", resource->_resId);
+	// TODO
+	_vm->_specialCode = new BbdouSpecialCode(_vm);
+	_vm->_specialCode->init();
+}
+
+void SpecialCodeLoader::unload(Resource *resource) {
+	debug("SpecialCodeLoader::unload() Unloading special code %08X...", resource->_resId);
+	// TODO
+	delete _vm->_specialCode;
+	_vm->_specialCode = 0;
+}
+
+void SpecialCodeLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.dll", resource->_resId);
+}
+
+bool SpecialCodeLoader::isFlag(int flag) {
+	return false;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/specialcode.h b/engines/illusions/specialcode.h
new file mode 100644
index 0000000..e3ecc8d
--- /dev/null
+++ b/engines/illusions/specialcode.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SPECIALCODE_H
+#define ILLUSIONS_SPECIALCODE_H
+
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+struct OpCall;
+
+class SpecialCodeLoader : public BaseResourceLoader {
+public:
+	SpecialCodeLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~SpecialCodeLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+class SpecialCode {
+public:
+	SpecialCode(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~SpecialCode() {}
+	virtual void init() = 0;
+	virtual void run(uint32 specialCodeId, OpCall &opCall) = 0;
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SPECIALCODE_H


Commit: e05a7899755011f860f2b09ce6d5b4e0a15107b9
    https://github.com/scummvm/scummvm/commit/e05a7899755011f860f2b09ce6d5b4e0a15107b9
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on BBDOU specific code (cursor, bubble)

- Add input handling code

Changed paths:
  A engines/illusions/bbdou/bbdou_bubble.cpp
  A engines/illusions/bbdou/bbdou_bubble.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/illusions.cpp
    engines/illusions/input.cpp
    engines/illusions/input.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index b813ff0..fbc5958 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -95,7 +95,7 @@ Actor::Actor(IllusionsEngine *vm)
 	_pathCtrY = 0;
 	
 	_controlRoutine = 0;
-	setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(_vm->_controls, &Controls::actorControlRouine));
+	setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(_vm->_controls, &Controls::actorControlRoutine));
 
 #if 0 // TODO
 	_field2 = 0;
@@ -283,7 +283,7 @@ void Control::activateObject() {
 }
 
 void Control::deactivateObject() {
-	_flags |= ~1;
+	_flags &= ~1;
 	if (_actor) {
 		for (uint i = 0; i < kSubObjectsCount; ++i)
 			if (_actor->_subobjects[i]) {
@@ -377,11 +377,11 @@ int Control::getPriority() {
 	int16 positionY, priority, priority1;
 	if (_actor) {
 		if (_actor->_parentObjectId && (_actor->_flags & 0x40)) {
-			uint32 objectId2 = getSubActorParent();
-			Control *control2 = _vm->_dict->getObjectControl(objectId2);
-			objectId = control2->_objectId;
-			priority = control2->_priority;
-			positionY = control2->_actor->_position.y;
+			uint32 parentObjectId = getSubActorParent();
+			Control *parentControl = _vm->_dict->getObjectControl(parentObjectId);
+			objectId = parentControl->_objectId;
+			priority = parentControl->_priority;
+			positionY = parentControl->_actor->_position.y;
 			priority1 = _priority;
 		} else {
 			objectId = _objectId;
@@ -475,6 +475,24 @@ void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {
 
 }
 
+void Control::getCollisionRect(Common::Rect &collisionRect) {
+	collisionRect = Common::Rect(_unkPt.x, _unkPt.y, _pt.x, _pt.y);
+	if (_actor) {
+		if (_actor->_scale != 100) {
+			// scaledValue = value * scale div 100
+			collisionRect.left = collisionRect.left * _actor->_scale / 100;
+			collisionRect.top = collisionRect.top * _actor->_scale / 100;
+			collisionRect.right = collisionRect.right * _actor->_scale / 100;
+			collisionRect.bottom = collisionRect.bottom * _actor->_scale / 100;
+		}
+		collisionRect.translate(_actor->_position.x, _actor->_position.y);
+	}
+	if (_flags & 8) {
+		Common::Point screenOffs = _vm->_camera->getScreenOffset();
+		collisionRect.translate(screenOffs.x, screenOffs.y);
+	}
+}
+
 void Control::setActorUsePan(int usePan) {
 	if (usePan == 1)
 		_flags &= ~8;
@@ -602,6 +620,14 @@ void Control::sequenceActor() {
 	
 }
 
+void Control::setActorIndexTo1() {
+	_actor->_actorIndex = 1;
+}
+
+void Control::setActorIndexTo2() {
+	_actor->_actorIndex = 2;
+}
+
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 
 	stopActor();
@@ -618,10 +644,18 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_path40 = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
+	//debug("sequence: %p", (void*)sequence);
 
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
 	
+	/*
+	for (int i = 0; i < 64; ++i) {
+		debugN("%02X ", sequence->_sequenceCode[i]);
+	}
+	debug(".");
+	*/
+	
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
 	_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
@@ -815,7 +849,48 @@ void Controls::unpauseControlsByTag(uint32 tag) {
 	}
 }
 
-void Controls::actorControlRouine(Control *control, uint32 deltaTime) {
+bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority) {
+	Control *foundControl = 0;
+	int foundPriority = 0;
+	// TODO minPriority = artcntrlGetPriorityFromBase(minPriority);
+
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *testControl = *it;
+		if (testControl != control && testControl->_pauseCtr == 0 &&
+			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
+			(!testControl->_actor || (testControl->_actor->_flags & 1))) {
+			Common::Rect collisionRect;
+			testControl->getCollisionRect(collisionRect);
+			debug("collisionRect(%d, %d, %d, %d)", collisionRect.left, collisionRect.top, collisionRect.right, collisionRect.bottom);
+			debug("pt(%d, %d)", pt.x, pt.y);
+			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
+				int testPriority = testControl->getPriority();
+				debug("testPriority: %d; minPriority: %d", testPriority, minPriority);
+				if ((!foundControl || foundPriority < testPriority) &&
+					testPriority >= minPriority) {
+		debug("overlapped() %08X; pauseCtr: %d; flags: %04X",
+			testControl->_objectId, testControl->_pauseCtr, testControl->_flags);
+					foundControl = testControl;
+					foundPriority = testPriority;
+				}
+			}		
+		}
+	}
+
+	debug("OVERLAPPED DONE\n");
+
+	if (foundControl) {
+		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & 0x40)) {
+			uint32 parentObjectId = foundControl->getSubActorParent();
+			foundControl = _vm->_dict->getObjectControl(parentObjectId);
+		}
+		*outOverlappedControl = foundControl;
+	}
+
+	return foundControl != 0;
+}
+
+void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 
 	Actor *actor = control->_actor;
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index fe5564e..9ddfc9c 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -134,7 +134,6 @@ public:
 	int _pathCtrY;
 	int _path40;
 	
-
 };
 
 class Control {
@@ -162,6 +161,7 @@ public:
 	Common::Point calcPosition(Common::Point posDelta);
 	uint32 getSubActorParent();
 	void getCollisionRectAccurate(Common::Rect &collisionRect);
+	void getCollisionRect(Common::Rect &collisionRect);
 	void setActorUsePan(int usePan);
 	void setActorFrameIndex(int16 frameIndex);
 	void stopActor();
@@ -169,6 +169,8 @@ public:
 	void stopSequenceActor();
 	void startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId);
 	void sequenceActor();
+	void setActorIndexTo1();
+	void setActorIndexTo2();
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
@@ -201,7 +203,8 @@ public:
 	void destroyControlsByTag(uint32 tag);
 	void pauseControlsByTag(uint32 tag);
 	void unpauseControlsByTag(uint32 tag);
-	void actorControlRouine(Control *control, uint32 deltaTime);	
+	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
+	void actorControlRoutine(Control *control, uint32 deltaTime);	
 public:
 	typedef Common::List<Control*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
new file mode 100644
index 0000000..3256bc4
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -0,0 +1,150 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/bbdou/bbdou_bubble.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+
+namespace Illusions {
+
+BbdouBubble::BbdouBubble(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
+	: _vm(vm), _bbdou(bbdou) {
+}
+
+BbdouBubble::~BbdouBubble() {
+}
+
+void BbdouBubble::init() {
+
+	static const uint32 kObjectIds3[] = {
+		0x0004003B, 0x0004003C, 0x0004003D, 0x0004003E,
+		0x0004003F, 0x00040040, 0x00040041, 0x00040042,
+		0x00040043, 0x00040044, 0x00040045, 0x00040046,
+		0x00040047, 0x00040048, 0x00040049, 0x0004004A,
+		0x0004004B, 0x0004004C, 0x0004004D, 0x0004004E,
+		0x0004004F, 0x00040050, 0x00040051, 0x00040052,
+		0x00040053, 0x00040054, 0x00040055, 0x00040056,
+		0x00040057, 0x00040058, 0x00040059, 0x0004005A
+	};
+
+	static const uint32 kObjectIds2[] = {
+		0x0004001B, 0x0004001C, 0x0004001D, 0x0004001E,
+		0x0004001F, 0x00040020, 0x00040021, 0x00040022,
+		0x00040023, 0x00040024, 0x00040025, 0x00040026,
+		0x00040027, 0x00040028, 0x00040029, 0x0004002A,
+		0x0004002B, 0x0004002C, 0x0004002D, 0x0004002E,
+		0x0004002F, 0x00040030, 0x00040031, 0x00040032,
+		0x00040033, 0x00040034, 0x00040035, 0x00040036,
+		0x00040037, 0x00040038, 0x00040039, 0x0004003A
+	};
+
+	_field1414 = 0x4005B;
+	_field1418 = 0x4005C;
+
+	for (uint i = 0; i < 32; ++i)
+		_objectIds[i] = kObjectIds3[i];
+
+	for (uint i = 0; i < 32; ++i) {
+		_items[i]._objectId = kObjectIds2[i];
+		_items[i]._enabled = 0;
+		_items[i]._position.x = 0;
+		_items[i]._position.y = 0;
+		_items[i]._sequenceId = 0;
+	}
+
+	_currItem0 = 0;
+	_prevItem0 = 0;
+	_someItem0 = 0;
+	_pt1.x = 0;
+	_pt1.y = 0;
+	_pt2.x = 0;
+	_pt2.y = 0;
+
+}
+
+void BbdouBubble::addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progResKeywordId,
+	uint32 namedPointId, int16 count, uint32 *namedPointIds) {
+	Item0 item0;
+	item0._sequenceId1 = sequenceId1;
+	item0._sequenceId2 = sequenceId2;
+	item0._progResKeywordId = progResKeywordId;
+	item0._baseNamedPointId = namedPointId;
+	item0._count = count;
+	for (int16 i = 0; i < count; ++i)
+		item0._namedPointIds[i] = FROM_LE_32(namedPointIds[i]);
+	item0._objectId = 0;
+	item0._pt.x = 0;
+	item0._pt.y = 0;
+	_item0s.push_back(item0);
+}
+
+void BbdouBubble::show() {
+	
+	if (_prevItem0) {
+		hide();
+	}
+
+	_prevItem0 = _currItem0;
+	_currItem0 = 0;
+
+	// TODO calcBubbles(_pt1, _pt2);
+	
+	Control *control = _vm->_dict->getObjectControl(_prevItem0->_objectId);
+	control->setActorPosition(_pt2);
+	control->startSequenceActor(0x60057, 2, 0);
+	control->startSequenceActor(_prevItem0->_sequenceId1, 2, 0);
+	control->appearActor();
+	control->deactivateObject();
+	
+	for (uint i = 0; i < 32; ++i) {
+		if (_items[i]._enabled == 1) {
+			Control *subControl = _vm->_dict->getObjectControl(_items[i]._objectId);
+			subControl->setActorPosition(_items[i]._position);
+			subControl->startSequenceActor(_items[i]._sequenceId, 2, 0);
+		}
+	}
+	
+}
+
+void BbdouBubble::hide() {
+	_someItem0 = _prevItem0;
+	_prevItem0 = 0;
+	if (_someItem0) {
+		Control *control = _vm->_dict->getObjectControl(_someItem0->_objectId);
+		control->startSequenceActor(_someItem0->_sequenceId2, 2, 0);
+		for (uint i = 0; i < 32; ++i) {
+			Control *subControl = _vm->_dict->getObjectControl(_objectIds[i]);
+			subControl->stopActor();
+			subControl->disappearActor();
+		}
+		for (uint i = 0; i < 32; ++i) {
+			Control *subControl = _vm->_dict->getObjectControl(_items[i]._objectId);
+			subControl->stopActor();
+			subControl->disappearActor();
+		}
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_bubble.h b/engines/illusions/bbdou/bbdou_bubble.h
new file mode 100644
index 0000000..9064acd
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_bubble.h
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_BUBBLE_H
+#define ILLUSIONS_BBDOU_BBDOU_BUBBLE_H
+
+#include "illusions/specialcode.h"
+#include "common/rect.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+class BbdouSpecialCode;
+class Control;
+
+struct Item0 {
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	int16 _count;
+	uint32 _progResKeywordId;
+	uint32 _baseNamedPointId;
+	uint32 _namedPointIds[32];
+	uint32 _objectId;
+	Common::Point _pt;
+	Item0() : _count(0) {}
+};
+
+struct Item141C {
+	uint32 _objectId;
+	int16 _enabled;
+	Common::Point _position;
+	int16 _fieldA;
+	uint32 _sequenceId;
+};
+
+class BbdouBubble {
+public:
+	BbdouBubble(IllusionsEngine *vm, BbdouSpecialCode *bbdou);
+	~BbdouBubble();
+	void init();
+	void addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progResKeywordId,
+		uint32 namedPointId, int16 count, uint32 *namedPointIds);
+	void show();
+	void hide();
+protected:
+	IllusionsEngine *_vm;
+	BbdouSpecialCode *_bbdou;
+	Common::Array<Item0> _item0s;
+	Item0 *_currItem0;
+	Item0 *_prevItem0;
+	Item0 *_someItem0;
+	uint32 _objectIds[32];
+	Common::Point _pt1;
+	Common::Point _pt2;
+	int _field1414;
+	int _field1418;
+	Item141C _items[32];
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_BUBBLE_H
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 9c0bcc8..16cd82a 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -23,17 +23,178 @@
 #include "illusions/illusions.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
 
 namespace Illusions {
 
-BbdouCursor::BbdouCursor(IllusionsEngine *vm)
-	: _vm(vm) {
+// NOTE It's assumed there's only one game cursor object
+// The original stores the _data inside the actor, here it's inside the Cursor class.
+
+BbdouCursor::BbdouCursor(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
+	: _vm(vm), _bbdou(bbdou) {
 }
 
 BbdouCursor::~BbdouCursor() {
 }
 
-void BbdouCursor::init() {
+void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
+
+	Common::Point pos = _vm->_camera->getCurrentPan();
+	_vm->_controls->placeActor(0x50001, pos, 0x6000C, objectId, 0);
+
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	//control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::actorControlRoutine1));
+	control->_flags |= 8;
+	
+	_data._mode = 1;
+	_data._mode2 = 0;
+	_data._verbId1 = 0x1B0000;
+	_data._progResKeywordId = progResKeywordId;
+	_data._currOverlappedObjectId = 0;
+	_data._overlappedObjectId = 0;
+	_data._sequenceId = 0x6000F;
+	_data._holdingObjectId = 0;
+	_data._holdingObjectId2 = 0;
+	_data._visibleCtr = 0;
+	_data._causeThreadId1 = 0;
+	_data._causeThreadId2 = 0;
+	_data._field90 = 0;
+	_data._flags = 0;
+	_data._item10._field58 = 1;
+	_data._sequenceId98 = 0;
+	_data._idleCtr = 0;
+	_data._item10._verbId = 0x1B0000;
+	_data._item10._field0 = 1;
+	_data._item10._playSound48 = 0;
+	_data._item10._objectIds[0] = 0;
+	_data._item10._objectIds[1] = 0;
+	_data._item10._index = 0;
+	_data._item10._flag56 = 0;
+
+	clearCursorDataField14();
+
+	control->setActorIndexTo1();
+
+}
+
+void BbdouCursor::enable(uint32 objectId) {
+	++_data._visibleCtr;
+	if (_data._visibleCtr == 1) {
+		Control *control = _vm->_dict->getObjectControl(objectId);
+		show(control);
+		_vm->_camera->pushCameraMode();
+		_vm->_camera->panEdgeFollow(objectId, 360);
+		_data._idleCtr = 0;
+	}
+	_vm->_input->discardButtons(0xFFFF);
+}
+
+void BbdouCursor::disable(uint32 objectId) {
+	hide(objectId);
+}
+
+void BbdouCursor::addCursorSequence(uint32 objectId, uint32 sequenceId) {
+	for (uint i = 0; i < kMaxCursorSequences; ++i)
+		if (_cursorSequences[i]._objectId == 0) {
+			_cursorSequences[i]._objectId = objectId;
+			_cursorSequences[i]._sequenceId = sequenceId;
+			break;
+		}
+}
+
+uint32 BbdouCursor::findCursorSequenceId(uint32 objectId) {
+	for (uint i = 0; i < kMaxCursorSequences; ++i)
+		if (_cursorSequences[i]._objectId == objectId)
+			return _cursorSequences[i]._sequenceId;
+	return 0;
+}
+
+int BbdouCursor::findStruct8bsValue(uint32 objectId) {
+	for (uint i = 0; i < kMaxCursorSequences; ++i)
+		if (_cursorStruct8bs[i]._objectId == objectId)
+			return _cursorStruct8bs[i]._value;
+	return 11;
+}
+
+void BbdouCursor::saveInfo() {
+	_data._mode2 = _data._mode;
+	_data._sequenceId2 = _data._sequenceId;
+	_data._holdingObjectId2 = _data._holdingObjectId;
+}
+
+void BbdouCursor::restoreInfo() {
+	_data._mode = _data._mode2;
+	_data._holdingObjectId = _data._holdingObjectId2;
+	_data._sequenceId = _data._sequenceId2;
+	_data._mode2 = 0;
+	_data._holdingObjectId2 = 0;
+	_data._sequenceId2 = 0;
+}
+
+void BbdouCursor::restoreAfterTrackingCursor() {
+	_data._holdingObjectId = _data._holdingObjectId2;
+	if (_data._holdingObjectId2) {
+		_data._mode = 2;
+		_data._sequenceId = findCursorSequenceId(_data._holdingObjectId2);
+	} else {
+		_data._mode = 1;
+		_data._sequenceId = 0x6000F;
+	}
+	_data._mode2 = 0;
+	_data._sequenceId2 = 0;
+	_data._holdingObjectId2 = 0;
+	_data._sequenceId98 = 0;
+}
+
+uint32 BbdouCursor::getSequenceId1(int sequenceIndex) {
+	switch (sequenceIndex) {
+	case 2:
+		return 0x60010;
+	case 3:
+		return 0x60011;
+	case 4:
+		return 0x60012;
+	case 5:
+		return 0x60013;
+	case 6:
+		return 0x60015;
+	case 7:
+		return 0x60014;
+	default:
+		return 0;
+	}
+}
+
+void BbdouCursor::clearCursorDataField14() {
+	for (uint i = 0; i < 32; ++i)
+		_data._item10._verbActive[i] = 0;
+	if (_data._item10._field0 == 1) {
+		_data._item10._verbActive[1] = 1;
+		_data._item10._verbActive[2] = 1;
+		_data._item10._verbActive[3] = 1;
+		_data._item10._verbActive[5] = 1;
+	} else if (_data._item10._field0 == 3) {
+		_data._item10._verbActive[1] = 1;
+		_data._item10._verbActive[2] = 1;
+	}
+}
+
+void BbdouCursor::show(Control *control) {
+	control->startSequenceActor(_data._sequenceId, 2, 0);
+	control->appearActor();
+}
+
+void BbdouCursor::hide(uint32 objectId) {
+	--_data._visibleCtr;
+	if (_data._visibleCtr == 0) {
+		Control *control = _vm->_dict->getObjectControl(objectId);
+		control->startSequenceActor(0x60029, 2, 0);
+		// TODO item10_sub_10005040(objectId, &cursorData->item10);
+		_vm->_camera->popCameraMode();
+	}
+	_vm->_input->discardButtons(0xFFFF);
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index df3ed82..8f02632 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -28,14 +28,85 @@
 namespace Illusions {
 
 class IllusionsEngine;
+class BbdouSpecialCode;
+class Control;
+struct Item10;
+
+struct Item10 {
+	int _field0;
+	int16 _verbActive[32];
+	uint32 _verbId;
+	int16 _playSound48;
+	//field_4A dw
+	uint32 _objectIds[2];
+	int16 _index;
+	int16 _flag56;
+	int _field58;
+};
+
+struct CursorData {
+	int _mode;
+	int _mode2;
+	uint32 _verbId1;
+	uint32 _progResKeywordId;
+	Item10 _item10;
+	uint32 _currOverlappedObjectId;
+	uint32 _overlappedObjectId;
+	uint32 _sequenceId;
+	uint32 _sequenceId2;
+	uint32 _holdingObjectId;
+	uint32 _holdingObjectId2;
+	int _visibleCtr;
+	//field_86 dw
+	uint32 _causeThreadId1;
+	uint32 _causeThreadId2;
+	int16 _field90;
+	//field_92 dw
+	uint _flags;
+	uint32 _sequenceId98;
+	int16 _idleCtr;
+	//field_9E db
+	//field_9F db
+};
+
+struct CursorSequence {
+	uint32 _objectId;
+	uint32 _sequenceId;
+	CursorSequence() : _objectId(0), _sequenceId(0) {}
+};
+
+struct Struct8b {
+	uint32 _objectId;
+	int _value;
+	Struct8b() : _objectId(0), _value(0) {}
+};
+
+const uint kMaxCursorSequences = 100;
 
 class BbdouCursor {
 public:
-	BbdouCursor(IllusionsEngine *vm);
+	BbdouCursor(IllusionsEngine *vm, BbdouSpecialCode *bbdou);
 	~BbdouCursor();
-	void init();
-protected:
+	void init(uint32 objectId, uint32 progResKeywordId);
+	void enable(uint32 objectId);
+	void disable(uint32 objectId);
+	void addCursorSequence(uint32 objectId, uint32 sequenceId);
+	uint32 findCursorSequenceId(uint32 objectId);
+	int findStruct8bsValue(uint32 objectId);
+	void saveInfo();
+	void restoreInfo();
+	void restoreAfterTrackingCursor();
+	uint32 getSequenceId1(int sequenceIndex);
+public:
 	IllusionsEngine *_vm;
+	BbdouSpecialCode *_bbdou;
+	Control *_control;
+	CursorData _data;
+	CursorSequence _cursorSequences[kMaxCursorSequences];
+	Struct8b _cursorStruct8bs[512];
+	void clearCursorDataField14();
+	void show(Control *control);
+	void hide(uint32 objectId);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 028fdb5..e0d29e7 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -22,7 +22,13 @@
 
 #include "illusions/illusions.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
+#include "illusions/bbdou/bbdou_bubble.h"
 #include "illusions/bbdou/bbdou_cursor.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 
 namespace Illusions {
@@ -31,15 +37,313 @@ namespace Illusions {
 
 BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine *vm)
 	: SpecialCode(vm) {
+	_bubble = new BbdouBubble(_vm, this);
+	_cursor = new BbdouCursor(_vm, this);
 }
 
 BbdouSpecialCode::~BbdouSpecialCode() {
+	delete _cursor;
+	delete _bubble;
 }
 
+typedef Common::Functor1Mem<OpCall&, void, BbdouSpecialCode> SpecialCodeFunctionI;
+#define SPECIAL(id, func) _map[id] = new SpecialCodeFunctionI(this, &BbdouSpecialCode::func);
+
 void BbdouSpecialCode::init() {
+	// TODO
+	SPECIAL(0x00160006, spcInitCursor);
+	SPECIAL(0x00160008, spcEnableCursor);
+	SPECIAL(0x00160009, spcDisableCursor);
+	SPECIAL(0x0016000A, spcAddCursorSequence);
+	SPECIAL(0x00160013, spcInitBubble);
+	SPECIAL(0x00160014, spcSetupBubble);
+	SPECIAL(0x00160015, spcSetObjectInteractMode);
 }
 
 void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
+	MapIterator it = _map.find(specialCodeId);
+	if (it != _map.end()) {
+		(*(*it)._value)(opCall);
+	} else {
+		debug("BbdouSpecialCode::run() Unimplemented special code %08X", specialCodeId);
+		_vm->notifyThreadId(opCall._callerThreadId);
+	}
+}
+
+// Special codes
+
+// Convenience macros
+#define	ARG_SKIP(x) opCall.skip(x); 
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %08X)", name);
+
+void BbdouSpecialCode::spcInitCursor(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	ARG_UINT32(progResKeywordId);
+	_cursor->init(objectId, progResKeywordId);
+	setCursorControlRoutine(objectId, 0);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcEnableCursor(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	_cursor->enable(objectId);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcDisableCursor(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	_cursor->disable(objectId);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcAddCursorSequence(OpCall &opCall) {
+	ARG_SKIP(4);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	_cursor->addCursorSequence(objectId, sequenceId);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcInitBubble(OpCall &opCall) {
+	_bubble->init();
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcSetupBubble(OpCall &opCall) {
+	ARG_UINT32(sequenceId1);
+	ARG_UINT32(sequenceId2);
+	ARG_UINT32(progResKeywordId);
+	ARG_UINT32(namedPointId);
+	ARG_INT16(count);
+	_bubble->addItem0(sequenceId1, sequenceId2, progResKeywordId, namedPointId,
+		count, (uint32*)opCall._code);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
+	ARG_SKIP(4);
+	ARG_UINT32(objectId);
+	ARG_INT16(value);
+	// TODO Cursor_updateStruct8bs(objectId, v3);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::playSoundEffect(int soundIndex) {
+	static const uint32 kSoundEffectIds[] = {
+		      0, 1,
+		0x900C1, 2,
+		      0, 3,
+		0x900C0, 4,
+		0x900C2, 5, 
+		      0, 6
+	};
+	uint32 soundEffectId = kSoundEffectIds[2 * soundIndex];
+	if (soundEffectId) {
+		// TODO _vm->startSound(soundEffectId, 255, 0);
+	}
+}
+
+void BbdouSpecialCode::resetItem10(uint32 objectId, Item10 *item10) {
+	if (item10->_playSound48 == 1) {
+		_bubble->hide();
+		item10->_verbId = 0x1B0000;
+		item10->_playSound48 = 0;
+		item10->_objectIds[0] = 0;
+		item10->_objectIds[1] = 0;
+	}
+	_vm->_input->discardButtons(0xFFFF);
+}
+
+bool BbdouSpecialCode::testValueRange(int value) {
+	return value >= 2 && value <= 7;
+}
+
+void BbdouSpecialCode::setCursorControlRoutine(uint32 objectId, int num) {
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (num == 0)
+		control->_actor->setControlRoutine(
+			new Common::Functor2Mem<Control*, uint32, void, BbdouSpecialCode>(this, &BbdouSpecialCode::cursorInteractControlRoutine));
+	else
+		control->_actor->setControlRoutine(
+			new Common::Functor2Mem<Control*, uint32, void, BbdouSpecialCode>(this, &BbdouSpecialCode::cursorControlRoutine2));
+}
+
+Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos) {
+	Common::Point pt = _vm->_camera->getScreenOffset();
+	pt.x += cursorPos.x;
+	pt.y += cursorPos.y;
+	return pt;
+}
+
+bool BbdouSpecialCode::runCause(Control *control, CursorData &cursorData,
+	uint32 verbId, uint32 objectId1, uint32 objectId2, int soundIndex) {
+	// TODO
+	return false;
+}
+
+void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
+	Item10 *item10, uint32 progResKeywordId) {
+	// TODO
+}
+
+bool BbdouSpecialCode::findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId) {
+	// TODO
+	return false;
+}
+
+void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime) {
+	Actor *actor = cursorControl->_actor;
+	CursorData &cursorData = _cursor->_data;
+	
+	if (cursorData._visibleCtr > 0) {
+
+		Common::Point cursorPos = _vm->_input->getCursorPosition();
+
+		if (cursorPos == actor->_position) {
+			cursorData._idleCtr += deltaTime;
+			if (cursorData._idleCtr > 3600)
+				cursorData._idleCtr = 0;
+		} else {
+			actor->_position.x = cursorPos.x;
+			actor->_position.y = cursorPos.y;
+			cursorData._idleCtr = 0;
+		}
+
+		if (updateTrackingCursor(cursorControl))
+			cursorData._flags |= 1;
+		else
+			cursorData._flags &= ~1;
+
+		cursorPos = getBackgroundCursorPos(cursorPos);
+		bool foundOverlapped = false;
+		Control *overlappedControl = 0;
+		
+		if (cursorData._flags & 1) {
+			foundOverlapped = 0;
+		} else if (_vm->_scriptMan->_activeScenes.getCurrentScene() == 0x1000D) {
+			/* TODO foundOverlapped = artcntrlGetOverlappedObjectAccurate(cursorControl, cursorPos,
+			&overlappedControl, cursorData._item10._field58);*/
+		} else {
+			foundOverlapped = _vm->_controls->getOverlappedObject(cursorControl, cursorPos,
+				&overlappedControl, cursorData._item10._field58);
+			debug("overlappedControl: %p", (void*)overlappedControl);
+		}
+		
+		if (foundOverlapped) {
+			if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
+				if (cursorData._item10._playSound48)
+					playSoundEffect(4);
+				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+				if (!testValueRange(value)) {
+					if (cursorData._mode == 3)
+						_cursor->restoreInfo();
+					_cursor->show(cursorControl);
+					cursorControl->setActorIndexTo2();
+					if (cursorData._overlappedObjectId != overlappedControl->_objectId) {
+						cursorData._overlappedObjectId = overlappedControl->_objectId;
+						runCause(cursorControl, cursorData, 0x1B0009, 0, overlappedControl->_objectId, 0);
+					}
+					if (value == 10) {
+						if (cursorData._holdingObjectId) {
+							cursorData._item10._verbId = 0x1B0003;
+							cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+						}
+						else {
+							cursorData._item10._verbId = 0x1B0002;
+							cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+						}
+					} else {
+						playSoundEffect(3);
+						showBubble(cursorControl->_objectId, overlappedControl->_objectId,
+							cursorData._holdingObjectId, &cursorData._item10,
+							cursorData._progResKeywordId);
+						cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+					}
+				} else {
+					if (cursorData._mode != 3) {
+						_cursor->saveInfo();
+						cursorData._mode = 3;
+						cursorData._item10._verbId = 0x1B0006;
+						cursorData._holdingObjectId = 0;
+					}
+					cursorData._sequenceId = _cursor->getSequenceId1(value);
+					_cursor->show(cursorControl);
+					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+				}
+			}
+		} else {
+			if (cursorData._overlappedObjectId) {
+				runCause(cursorControl, cursorData, 0x1B0009, 0, 0x40003, 0);
+				cursorData._overlappedObjectId = 0;
+			}
+			if (cursorData._currOverlappedObjectId || cursorData._mode == 3) {
+				if (cursorData._mode == 3)
+					_cursor->restoreInfo();
+				_cursor->show(cursorControl);
+				cursorControl->setActorIndexTo1();
+				if (cursorData._item10._playSound48)
+					playSoundEffect(4);
+				resetItem10(cursorControl->_objectId, &cursorData._item10);
+			}
+			cursorData._currOverlappedObjectId = 0;
+		}
+	}
+
+	actor->_seqCodeValue1 = 100 * deltaTime;
+
+	if (cursorData._visibleCtr <= 0) {
+		if (cursorData._currOverlappedObjectId || cursorData._mode == 3 || cursorData._mode == 4) {
+			if (cursorData._mode == 3) {
+				_cursor->restoreInfo();
+			} else if (cursorData._mode == 4) {
+				_cursor->restoreAfterTrackingCursor();
+			}
+			cursorControl->setActorIndexTo1();
+		}
+		cursorData._currOverlappedObjectId = 0;
+	} else if (cursorData._currOverlappedObjectId) {
+		if (_vm->_input->pollButton(1)) {
+			cursorData._idleCtr = 0;
+			if (runCause(cursorControl, cursorData, cursorData._item10._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId, 1)) {
+				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				cursorData._currOverlappedObjectId = 0;
+				cursorControl->setActorIndexTo1();
+			}
+		} else if (_vm->_input->pollButton(2)) {
+			uint32 verbId;
+			cursorData._idleCtr = 0;
+			if (cursorData._holdingObjectId) {
+				runCause(cursorControl, cursorData, 0x1B000B, 0, 0x40003, 0);
+				cursorData._currOverlappedObjectId = 0;
+			} else if (findVerbId(&cursorData._item10, cursorData._currOverlappedObjectId, 0, verbId) &&
+				runCause(cursorControl, cursorData, verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId, 1)) {
+				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				cursorData._currOverlappedObjectId = 0;
+				cursorControl->setActorIndexTo1();
+			}
+		}
+	} else {
+		if (_vm->_input->pollButton(1)) {
+			cursorData._idleCtr = 0;
+			runCause(cursorControl, cursorData, 0x1B0002, 0, 0x40003, 0);
+		} else if (_vm->_input->pollButton(4)) {
+			cursorData._idleCtr = 0;
+			if (cursorData._item10._field58 <= 1)
+				runCause(cursorControl, cursorData, cursorData._holdingObjectId != 0 ? 0x1B000B : 0x1B0004, 0, 0x40003, 0);
+		}
+	}
+
+}
+
+void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 deltaTime) {
+	// TODO
+}
+
+bool BbdouSpecialCode::updateTrackingCursor(Control *cursorControl) {
+	// TODO
+	return false;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 98aef58..1dda19f 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -24,11 +24,17 @@
 #define ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
 
 #include "illusions/specialcode.h"
+#include "common/hashmap.h"
 
 namespace Illusions {
 
 class IllusionsEngine;
+class BbdouBubble;
 class BbdouCursor;
+struct CursorData;
+struct Item10;
+
+typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
 class BbdouSpecialCode : public SpecialCode {
 public:
@@ -37,7 +43,34 @@ public:
 	virtual void init();
 	virtual void run(uint32 specialCodeId, OpCall &opCall);
 public:
+	typedef Common::HashMap<uint32, SpecialCodeFunction*> Map;
+	typedef Map::iterator MapIterator;
+	Map _map;
 	BbdouCursor *_cursor;
+	BbdouBubble *_bubble;
+	// Special code interface functions
+	void spcInitCursor(OpCall &opCall);
+	void spcEnableCursor(OpCall &opCall);
+	void spcDisableCursor(OpCall &opCall);
+	void spcAddCursorSequence(OpCall &opCall);
+	void spcInitBubble(OpCall &opCall);
+	void spcSetupBubble(OpCall &opCall);
+	void spcSetObjectInteractMode(OpCall &opCall);
+protected:
+	// Internal functions
+	void playSoundEffect(int soundIndex);
+	void resetItem10(uint32 objectId, Item10 *item10);
+	bool testValueRange(int value);
+	void setCursorControlRoutine(uint32 objectId, int num);
+	Common::Point getBackgroundCursorPos(Common::Point cursorPos);
+	bool runCause(Control *control, CursorData &cursorData,
+		uint32 verbId, uint32 objectId1, uint32 objectId2, int soundIndex);
+	void showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
+		Item10 *item10, uint32 progResKeywordId);
+	bool findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
+	void cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime);
+	void cursorControlRoutine2(Control *cursorControl, uint32 deltaTime);
+	bool updateTrackingCursor(Control *cursorControl);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index a59c568..cf40801 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -213,23 +213,9 @@ bool IllusionsEngine::hasFeature(EngineFeature f) const {
 
 void IllusionsEngine::updateEvents() {
 	Common::Event event;
-
 	while (_eventMan->pollEvent(event)) {
+		_input->processEvent(event);
 		switch (event.type) {
-		case Common::EVENT_KEYDOWN:
-			break;
-		case Common::EVENT_KEYUP:
-			break;
-		case Common::EVENT_MOUSEMOVE:
-  			break;
-		case Common::EVENT_LBUTTONDOWN:
-  			break;
-		case Common::EVENT_LBUTTONUP:
-  			break;
-		case Common::EVENT_RBUTTONDOWN:
-  			break;
-		case Common::EVENT_RBUTTONUP:
-  			break;
 		case Common::EVENT_QUIT:
 			quitGame();
 			break;
@@ -240,7 +226,9 @@ void IllusionsEngine::updateEvents() {
 }
 
 Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
-	// TODO Dummy, to be replaced later
+	Control *control = _dict->getObjectControl(objectId);
+	if (control && control->_actor)
+		return &control->_actor->_position;
 	return 0;
 }
 
@@ -296,6 +284,7 @@ int IllusionsEngine::updateGraphics() {
 	Common::Point panPoint(0, 0);
 
 	uint32 currTime = getCurrentTime();
+	
 	_camera->update(currTime);
 
 	// TODO Move to BackgroundItems class
@@ -312,7 +301,7 @@ int IllusionsEngine::updateGraphics() {
 				panPoint = backgroundItem->_panPoints[i];
 		}
 	}
-	
+
 	// TODO Move to Controls class
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index fd922ec..b9ae53a 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -27,18 +27,45 @@ namespace Illusions {
 Input::Input() {
 	_buttonStates = 0;
 	_newButtons = 0;
-	// TODO? _buttonsDown = 0;
-	// TODO? _unk6 = 0;
+	_buttonsDown = 0;
+	_newKeys = 0;
 	_enabledButtons = 0xFFFF;
 	_cursorPos.x = 0;
 	_cursorPos.y = 0;
 	_prevCursorPos.x = 0;
 	_prevCursorPos.y = 0;
 	// TODO Not sure if this is still needed newTimer(40, 0, 0, Input_onTimer);
+	initKeys();
 }
 
 void Input::processEvent(Common::Event event) {
 	// TODO
+	switch (event.type) {
+	case Common::EVENT_KEYDOWN:
+		handleKey(event.kbd.keycode, MOUSE_NONE, true);
+		break;
+	case Common::EVENT_KEYUP:
+		handleKey(event.kbd.keycode, MOUSE_NONE, false);
+		break;
+	case Common::EVENT_MOUSEMOVE:
+		_cursorPos.x = event.mouse.x;
+		_cursorPos.y = event.mouse.y;
+		break;
+	case Common::EVENT_LBUTTONDOWN:
+		handleMouseButton(MOUSE_BUTTON0, true);
+		break;
+	case Common::EVENT_LBUTTONUP:
+		handleMouseButton(MOUSE_BUTTON0, false);
+		break;
+	case Common::EVENT_RBUTTONDOWN:
+		handleMouseButton(MOUSE_BUTTON1, true);
+		break;
+	case Common::EVENT_RBUTTONUP:
+		handleMouseButton(MOUSE_BUTTON1, false);
+		break;
+	default:
+		break;
+	}
 }
 
 bool Input::pollButton(uint buttons) {
@@ -90,4 +117,59 @@ Common::Point Input::getCursorDelta() {
 	return deltaPos;
 }
 
+void Input::initKeys() {
+	// NOTE Skipped debugging keys of the original engine, not sure if used
+	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON0, 0x01);
+	addKeyMapping(Common::KEYCODE_RETURN, MOUSE_NONE, 0x01);
+	addKeyMapping(Common::KEYCODE_TAB, MOUSE_NONE, 0x04);
+	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x04);
+	addKeyMapping(Common::KEYCODE_ESCAPE, MOUSE_NONE, 0x08);
+	addKeyMapping(Common::KEYCODE_SPACE, MOUSE_NONE, 0x10);
+	addKeyMapping(Common::KEYCODE_F1, MOUSE_NONE, 0x20);
+	addKeyMapping(Common::KEYCODE_UP, MOUSE_NONE, 0x40);
+	addKeyMapping(Common::KEYCODE_DOWN, MOUSE_NONE, 0x80);
+	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x80);
+}
+
+void Input::addKeyMapping(Common::KeyCode key, int mouseButton, uint bitMask) {
+	KeyMapping keyMapping;
+	keyMapping._key = key;
+	keyMapping._mouseButton = mouseButton;
+	keyMapping._bitMask = bitMask;
+	keyMapping._down = false;
+	_keyMap.push_back(keyMapping);
+}
+
+void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
+	for (KeyMap::iterator it = _keyMap.begin(); it != _keyMap.end(); ++it) {
+		KeyMapping &keyMapping = *it;
+		if ((keyMapping._key != Common::KEYCODE_INVALID && keyMapping._key == key) ||
+			(keyMapping._mouseButton != MOUSE_NONE && keyMapping._mouseButton == mouseButton)) {
+			if (down && !keyMapping._down) {
+				_newKeys |= keyMapping._bitMask;
+				keyMapping._down = true;
+			} else if (!down)
+				keyMapping._down = false;
+		}
+	}
+	uint prevButtonStates = _buttonStates;
+
+	debug("_newKeys = %08X", _newKeys);
+
+	_buttonStates |= _newKeys;
+	_newKeys = 0;
+	_newButtons = ~prevButtonStates & _buttonStates;
+	
+	debug("_buttonStates = %08X", _buttonStates);
+	
+}
+
+void Input::handleMouseButton(int mouseButton, bool down) {
+	if (down)
+		_buttonsDown |= mouseButton;
+	else
+		_buttonsDown &= ~mouseButton;
+	handleKey(Common::KEYCODE_INVALID, mouseButton, down);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index 700d9ed..f3c9654 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -23,11 +23,26 @@
 #ifndef ILLUSIONS_INPUT_H
 #define ILLUSIONS_INPUT_H
 
+#include "common/array.h"
 #include "common/events.h"
+#include "common/keyboard.h"
 #include "common/rect.h"
 
 namespace Illusions {
 
+enum {
+	MOUSE_NONE    = 0,
+	MOUSE_BUTTON0 = 1,
+	MOUSE_BUTTON1 = 2
+};
+
+struct KeyMapping {
+	Common::KeyCode _key;
+	int _mouseButton;
+	uint _bitMask;
+	bool _down;
+};
+
 class Input {
 public:
 	Input();
@@ -43,9 +58,16 @@ public:
 	void setCursorPosition(Common::Point mousePos);
 	Common::Point getCursorDelta();
 protected:
-	uint _buttonStates, _newButtons;
+	typedef Common::Array<KeyMapping> KeyMap;
+	uint _buttonStates, _newButtons, _buttonsDown;
 	uint _enabledButtons;
+	uint _newKeys;
 	Common::Point _cursorPos, _prevCursorPos;
+	KeyMap _keyMap;
+	void initKeys();
+	void addKeyMapping(Common::KeyCode key, int mouseButton, uint bitMask);
+	void handleKey(Common::KeyCode key, int mouseButton, bool down);
+	void handleMouseButton(int mouseButton, bool down);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 77f089a..762bd4c 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	actor.o \
 	actorresource.o \
 	backgroundresource.o \
+	bbdou/bbdou_bubble.o \
 	bbdou/bbdou_cursor.o \
 	bbdou/bbdou_specialcode.o \
 	camera.o \
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index a191eff..8abfa8b 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -29,6 +29,7 @@
 #include "illusions/scriptman.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
+#include "illusions/specialcode.h"
 
 namespace Illusions {
 
@@ -559,11 +560,11 @@ void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall)
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeId);
 	_vm->_scriptMan->_callerThreadId = opCall._callerThreadId;
-	// TODO _vm->runSpecialCode(specialCodeId, opCall._code + 8, opCall._threadId);
+	_vm->_specialCode->run(specialCodeId, opCall);
 	_vm->_scriptMan->_callerThreadId = 0;
 
 	//DEBUG Resume calling thread, later done by the special code
-	_vm->notifyThreadId(opCall._callerThreadId);
+	//_vm->notifyThreadId(opCall._callerThreadId);
 
 }
 


Commit: 22e898f7eb1bddc363900f4146696bf6e9d41e2f
    https://github.com/scummvm/scummvm/commit/22e898f7eb1bddc363900f4146696bf6e9d41e2f
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Work on interaction; work on Cause related code

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index fbc5958..b2fccb1 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -861,15 +861,14 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 			(!testControl->_actor || (testControl->_actor->_flags & 1))) {
 			Common::Rect collisionRect;
 			testControl->getCollisionRect(collisionRect);
-			debug("collisionRect(%d, %d, %d, %d)", collisionRect.left, collisionRect.top, collisionRect.right, collisionRect.bottom);
-			debug("pt(%d, %d)", pt.x, pt.y);
+			//debug("collisionRect(%d, %d, %d, %d)", collisionRect.left, collisionRect.top, collisionRect.right, collisionRect.bottom);
+			//debug("pt(%d, %d)", pt.x, pt.y);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
 				int testPriority = testControl->getPriority();
-				debug("testPriority: %d; minPriority: %d", testPriority, minPriority);
+				//debug("testPriority: %d; minPriority: %d", testPriority, minPriority);
 				if ((!foundControl || foundPriority < testPriority) &&
 					testPriority >= minPriority) {
-		debug("overlapped() %08X; pauseCtr: %d; flags: %04X",
-			testControl->_objectId, testControl->_pauseCtr, testControl->_flags);
+					//debug("overlapped() %08X; pauseCtr: %d; flags: %04X", testControl->_objectId, testControl->_pauseCtr, testControl->_flags);
 					foundControl = testControl;
 					foundPriority = testPriority;
 				}
@@ -877,8 +876,6 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 		}
 	}
 
-	debug("OVERLAPPED DONE\n");
-
 	if (foundControl) {
 		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & 0x40)) {
 			uint32 parentObjectId = foundControl->getSubActorParent();
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 16cd82a..1c4af97 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -72,7 +72,7 @@ void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
 	_data._item10._objectIds[1] = 0;
 	_data._item10._index = 0;
 	_data._item10._flag56 = 0;
-
+	
 	clearCursorDataField14();
 
 	control->setActorIndexTo1();
@@ -111,8 +111,32 @@ uint32 BbdouCursor::findCursorSequenceId(uint32 objectId) {
 	return 0;
 }
 
+void BbdouCursor::setStruct8bsValue(uint32 objectId, int value) {
+	// TODO Clean this up, preliminary
+	Struct8b *struct8b = 0;
+	for (uint i = 0; i < 512; ++i)
+		if (_cursorStruct8bs[i]._objectId == objectId) {
+			struct8b = &_cursorStruct8bs[i];
+			break;
+		}
+	if (!struct8b) {
+		for (uint i = 0; i < 512; ++i)
+			if (_cursorStruct8bs[i]._objectId == 0) {
+				struct8b = &_cursorStruct8bs[i];
+				break;
+			}
+	}
+	if (value != 11) {
+		struct8b->_objectId = objectId;
+		struct8b->_value = value;
+	} else if (struct8b->_objectId == objectId) {
+		struct8b->_objectId = 0;
+		struct8b->_value = 0;
+	}
+}
+
 int BbdouCursor::findStruct8bsValue(uint32 objectId) {
-	for (uint i = 0; i < kMaxCursorSequences; ++i)
+	for (uint i = 0; i < 512; ++i)
 		if (_cursorStruct8bs[i]._objectId == objectId)
 			return _cursorStruct8bs[i]._value;
 	return 11;
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index 8f02632..bd3ba3a 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -92,6 +92,7 @@ public:
 	void disable(uint32 objectId);
 	void addCursorSequence(uint32 objectId, uint32 sequenceId);
 	uint32 findCursorSequenceId(uint32 objectId);
+	void setStruct8bsValue(uint32 objectId, int value);
 	int findStruct8bsValue(uint32 objectId);
 	void saveInfo();
 	void restoreInfo();
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index e0d29e7..62b0a7a 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -33,6 +33,24 @@
 
 namespace Illusions {
 
+CauseThread::CauseThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId,
+	BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId, uint32 verbId,
+	uint32 objectId2, uint32 objectId)
+	: Thread(vm, threadId, callingThreadId, 0), _bbdou(bbdou), _cursorObjectId(cursorObjectId),
+	_sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId) {
+	_type = kTTSpecialThread;
+}
+		
+void CauseThread::onNotify() {
+	_bbdou->_cursor->_data._causeThreadId1 = 0;
+	terminate();
+}
+
+void CauseThread::onTerminated() {
+	_bbdou->_cursor->_data._causeThreadId1 = 0;
+	_bbdou->_cursor->enable(_cursorObjectId);
+}
+
 // BbdouSpecialCode
 
 BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine *vm)
@@ -125,7 +143,7 @@ void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
 	ARG_SKIP(4);
 	ARG_UINT32(objectId);
 	ARG_INT16(value);
-	// TODO Cursor_updateStruct8bs(objectId, v3);
+	_cursor->setStruct8bsValue(objectId, value);
 	_vm->notifyThreadId(opCall._callerThreadId);
 }
 
@@ -176,19 +194,26 @@ Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos)
 	return pt;
 }
 
-bool BbdouSpecialCode::runCause(Control *control, CursorData &cursorData,
-	uint32 verbId, uint32 objectId1, uint32 objectId2, int soundIndex) {
-	// TODO
-	return false;
-}
-
 void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
 	Item10 *item10, uint32 progResKeywordId) {
 	// TODO
 }
 
 bool BbdouSpecialCode::findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId) {
-	// TODO
+	if (item10->_playSound48) {
+		int verbNum = item10->_verbId & 0xFFFF;
+		int verbNumI = verbNum + 1;
+		while (1) {
+			if (verbNumI >= 32)
+				verbNumI = 0;
+			if (verbNumI++ == verbNum)
+				break;
+			if (item10->_verbActive[verbNumI] && testVerbId(verbNumI | 0x1B0000, always0, currOverlappedObjectId)) {
+				outVerbId = verbNumI | 0x1B0000;
+				return true;
+			}
+		}
+	}
 	return false;
 }
 
@@ -221,21 +246,22 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 		
 		if (cursorData._flags & 1) {
 			foundOverlapped = 0;
-		} else if (_vm->_scriptMan->_activeScenes.getCurrentScene() == 0x1000D) {
+		} else if (_vm->getCurrentScene() == 0x1000D) {
 			/* TODO foundOverlapped = artcntrlGetOverlappedObjectAccurate(cursorControl, cursorPos,
 			&overlappedControl, cursorData._item10._field58);*/
 		} else {
 			foundOverlapped = _vm->_controls->getOverlappedObject(cursorControl, cursorPos,
 				&overlappedControl, cursorData._item10._field58);
-			debug("overlappedControl: %p", (void*)overlappedControl);
 		}
 		
 		if (foundOverlapped) {
+			debug("overlappedControl: %p", (void*)overlappedControl);
 			if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
 				if (cursorData._item10._playSound48)
 					playSoundEffect(4);
 				resetItem10(cursorControl->_objectId, &cursorData._item10);
 				int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+				debug("object value: %d", value);
 				if (!testValueRange(value)) {
 					if (cursorData._mode == 3)
 						_cursor->restoreInfo();
@@ -346,4 +372,119 @@ bool BbdouSpecialCode::updateTrackingCursor(Control *cursorControl) {
 	return false;
 }
 
+bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId) {
+	static const uint32 kVerbIdsEE[] = {0x001B0002, 0x001B0001, 0};
+	static const uint32 kVerbIdsE9[] = {0x001B0005, 0};
+	static const uint32 kVerbIdsE8[] = {0x001B0005, 0x001B0001, 0};
+	static const uint32 kVerbIdsHE[] = {0x001B0003, 0x001B0001, 0};
+	static const uint32 kVerbIdsH9[] = {0x001B0003, 0};
+	static const uint32 kVerbIdsH8[] = {0x001B0003, 0x001B0001, 0};
+	
+	const uint32 *verbIds;
+	int value = _cursor->findStruct8bsValue(overlappedObjectId);
+  
+	if (holdingObjectId) {
+		if (value == 9)
+			verbIds = kVerbIdsH9;
+		else if (value == 9)
+			verbIds = kVerbIdsH8;
+		else
+			verbIds = kVerbIdsHE;
+	} else {
+		if (value == 9)
+			verbIds = kVerbIdsE9;
+		else if (value == 8)
+			verbIds = kVerbIdsE8;
+		else
+			verbIds = kVerbIdsEE;
+	}
+	
+	for (; *verbIds; ++verbIds)
+		if (*verbIds == verbId)
+			return true;
+	return false;
+}
+
+bool BbdouSpecialCode::getCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
+	uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId) {
+	bool success = false;
+	objectId2 = verbId != 0x1B0003 ? 0 : objectId2;
+	if (_vm->causeIsDeclared(sceneId, verbId, objectId2, objectId)) {
+		outVerbId = verbId;
+		outObjectId2 = objectId2;
+		outObjectId = objectId;
+		success = true;
+	} else if (objectId2 != 0 && _vm->causeIsDeclared(sceneId, 0x1B0008, 0, objectId)) {
+		outVerbId = 0x1B0008;
+		outObjectId2 = 0;
+		outObjectId = objectId;
+		success = true;
+	} else if (_vm->causeIsDeclared(sceneId, verbId, objectId2, 0x40001)) {
+		outVerbId = verbId;
+		outObjectId2 = objectId2;
+		outObjectId = 0x40001;
+		success = true;
+	} else if (objectId2 != 0 && _vm->causeIsDeclared(sceneId, 0x1B0008, 0, 0x40001)) {
+		outVerbId = 0x1B0008;
+		outObjectId2 = 0;
+		outObjectId = 0x40001;
+		success = true;
+	}
+
+	if (success) {
+		debug("getCause() -> %08X %08X %08X", outVerbId, outObjectId2, outObjectId);
+	}
+
+	return success;
+}
+
+bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
+	uint32 verbId, uint32 objectId2, uint32 objectId, int soundIndex) {
+	debug("runCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
+	uint32 sceneId = _vm->getCurrentScene();
+	uint32 outVerbId, outObjectId2, outObjectId;
+	bool success = false;
+	
+	if (getCause(_vm->getCurrentScene(), verbId, objectId2, objectId, outVerbId, outObjectId2, outObjectId)) {
+		sceneId = _vm->getCurrentScene();
+		success = true;
+	} else if (getCause(0x10003, verbId, objectId2, objectId, outVerbId, outObjectId2, outObjectId)) {
+		sceneId = 0x10003;
+		success = true;
+	}
+	
+	debug("runCause() success: %d", success);
+	
+	if (!success)
+		return false;
+	
+
+	_cursor->hide(cursorControl->_objectId);
+
+	uint32 threadId = startCauseThread(cursorControl->_objectId, _vm->getCurrentScene(), outVerbId, outObjectId2, outObjectId);
+
+	if (cursorData._field90) {
+		_vm->_scriptMan->_threads->killThread(cursorData._causeThreadId2);
+		cursorData._field90 = 0;
+	}
+
+	if (soundIndex)
+		playSoundEffect(soundIndex);
+	
+	cursorData._causeThreadId1 = _vm->causeTrigger(sceneId, outVerbId, outObjectId2, outObjectId, threadId);
+	cursorData._causeThreadId2 = cursorData._causeThreadId1;
+
+	return true;
+}
+
+uint32 BbdouSpecialCode::startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	uint32 tempThreadId = _vm->_scriptMan->newTempThreadId();
+	debug("staring cause thread %08X...", tempThreadId);
+	CauseThread *causeThread = new CauseThread(_vm, tempThreadId, 0, this,
+		cursorObjectId, sceneId, verbId, objectId2, objectId);
+	_vm->_scriptMan->_threads->startThread(causeThread);
+	causeThread->suspend();
+	return tempThreadId;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 1dda19f..61f6b70 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -24,6 +24,7 @@
 #define ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
 
 #include "illusions/specialcode.h"
+#include "illusions/thread.h"
 #include "common/hashmap.h"
 
 namespace Illusions {
@@ -36,6 +37,24 @@ struct Item10;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
+class BbdouSpecialCode;
+
+class CauseThread : public Thread {
+public:
+	CauseThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId,
+		BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId,
+		uint32 verbId, uint32 objectId2, uint32 objectId);
+	virtual void onNotify();
+	virtual void onTerminated();
+public:
+	BbdouSpecialCode *_bbdou;
+	uint32 _cursorObjectId;
+	uint32 _sceneId;
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _objectId;
+};
+
 class BbdouSpecialCode : public SpecialCode {
 public:
 	BbdouSpecialCode(IllusionsEngine *vm);
@@ -63,14 +82,18 @@ protected:
 	bool testValueRange(int value);
 	void setCursorControlRoutine(uint32 objectId, int num);
 	Common::Point getBackgroundCursorPos(Common::Point cursorPos);
-	bool runCause(Control *control, CursorData &cursorData,
-		uint32 verbId, uint32 objectId1, uint32 objectId2, int soundIndex);
 	void showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
 		Item10 *item10, uint32 progResKeywordId);
 	bool findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
 	void cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime);
 	void cursorControlRoutine2(Control *cursorControl, uint32 deltaTime);
 	bool updateTrackingCursor(Control *cursorControl);
+	bool testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId);
+	bool getCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
+		uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId);
+	bool runCause(Control *cursorControl, CursorData &cursorData,
+		uint32 verbId, uint32 objectId2, uint32 objectId, int soundIndex);
+	uint32 startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index cf40801..5f7bb23 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -169,6 +169,7 @@ Common::Error IllusionsEngine::run() {
 #endif
 
 	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
+	_scriptMan->_doScriptThreadInit = true;
 
 	//_camera->panToPoint(Common::Point(800, 0), 500, 0);
 
@@ -347,6 +348,26 @@ int IllusionsEngine::getRandom(int max) {
 	return _random->getRandomNumber(max - 1);
 }
 
+bool IllusionsEngine::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	uint32 codeOffs;
+	// TODO Also search for native trigger functions later (findCauseFunc)
+	bool r = _scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
+	debug("causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
+		sceneId, verbId, objectId2, objectId, r);
+	return r;
+}
+
+uint32 IllusionsEngine::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
+	uint32 codeOffs;
+	uint32 causeThreadId = 0;
+	// TODO Also search for native trigger functions later and run it (findCauseFunc)
+	if (_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
+		causeThreadId = _scriptMan->startTempScriptThread(_scriptMan->_scriptResource->getCode(codeOffs),
+			callingThreadId, verbId, objectId2, objectId);
+	}
+	return causeThreadId;
+}
+
 int IllusionsEngine::convertPanXCoord(int16 x) {
 	// TODO
 	return 0;
@@ -389,4 +410,8 @@ bool IllusionsEngine::isVoicePlaying() {
 	return false;
 }
 
+uint32 IllusionsEngine::getCurrentScene() {
+	return _scriptMan->_activeScenes.getCurrentScene();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index e0f27b0..71f7a38 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -112,6 +112,11 @@ public:
 	int updateSequences();
 	int updateGraphics();
 	int getRandom(int max);
+
+	// TODO Move to ScriptMan?
+	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
+
 	int convertPanXCoord(int16 x);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
@@ -121,7 +126,9 @@ public:
 	bool isVoiceCued();
 	void startVoice(int volume, int panX);
 	void stopVoice();
-	bool isVoicePlaying();	
+	bool isVoicePlaying();
+	
+	uint32 getCurrentScene();	
 
 #if 0
 
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index de18dee..27e570a 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -198,6 +198,13 @@ uint32 ScriptMan::startTalkThread(int16 duration, uint32 objectId, uint32 talkId
 	return tempThreadId;
 }
 
+bool ScriptMan::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (progInfo)
+		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	return false;
+}
+
 void ScriptMan::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index bd9f2c7..adfc365 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -83,6 +83,7 @@ public:
 	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
 	uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
 		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
+	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
 	uint32 clipTextDuration(uint32 duration);
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 6aa8ec6..9c4f171 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -144,6 +144,15 @@ void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		_causes[i].load(stream);
 }
 
+bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
+	for (uint i = 0; i < _causesCount; ++i)
+		if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
+			codeOffs = _causes[i]._codeOffs;
+			return true;
+		}
+	return false;
+}
+
 // ProgInfo
 
 ProgInfo::ProgInfo()
@@ -186,6 +195,20 @@ void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	}
 }
 
+bool ProgInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	TriggerObject *triggerObject = findTriggerObject(objectId);
+	if (triggerObject)
+		return triggerObject->findTriggerCause(verbId, objectId2, codeOffs);
+	return false;
+}
+
+TriggerObject *ProgInfo::findTriggerObject(uint32 objectId) {
+	for (uint i = 0; i < _triggerObjectsCount; ++i)
+		if (_triggerObjects[i]._objectId == objectId)
+			return &_triggerObjects[i];
+	return 0;
+}
+
 // ScriptResource
 
 ScriptResource::ScriptResource()
@@ -241,6 +264,10 @@ byte *ScriptResource::getThreadCode(uint32 threadId) {
 	return _data + _codeOffsets[(threadId & 0xFFFF) - 1];
 }
 
+byte *ScriptResource::getCode(uint32 codeOffs) {
+	return _data + codeOffs;
+}
+
 ProgInfo *ScriptResource::getProgInfo(uint32 index) {
 	if (index > 0 && index <= _progInfosCount)
 		return &_progInfos[index - 1];
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index e2df45a..5e2da45 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -78,6 +78,7 @@ public:
 	TriggerObject();
 	~TriggerObject();
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs);
 public:
 	uint32 _objectId;
 	uint _causesCount;
@@ -89,12 +90,14 @@ public:
 	ProgInfo();
 	~ProgInfo();
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 protected:
 	uint16 _id;
 	uint16 _unk;
 	byte *_name;
 	uint _triggerObjectsCount;
 	TriggerObject *_triggerObjects;
+	TriggerObject *findTriggerObject(uint32 objectId);
 };
 
 class ScriptResource {
@@ -103,6 +106,7 @@ public:
 	~ScriptResource();
 	void load(byte *data, uint32 dataSize);
 	byte *getThreadCode(uint32 threadId);
+	byte *getCode(uint32 codeOffs);
 	ProgInfo *getProgInfo(uint32 index);
 public:
 	byte *_data;


Commit: 33d28deb690c79a6aca190c5b1bc998c39d95662
    https://github.com/scummvm/scummvm/commit/33d28deb690c79a6aca190c5b1bc998c39d95662
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Additions in various places

- Add NamedPoint and related code
- Remove some debug output
- Fix right mouse button input
- Add bubble code
- Add BBDOU inventory skeleton

Changed paths:
  A engines/illusions/bbdou/bbdou_inventory.cpp
  A engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_bubble.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/camera.cpp
    engines/illusions/cursor.cpp
    engines/illusions/graphics.cpp
    engines/illusions/graphics.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/input.cpp
    engines/illusions/module.mk
    engines/illusions/scriptman.cpp
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/spritedrawqueue.cpp
    engines/illusions/spritedrawqueue.h
    engines/illusions/thread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index b2fccb1..7f2a054 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -125,6 +125,10 @@ Actor::Actor(IllusionsEngine *vm)
 
 }
 
+Actor::~Actor() {
+	delete _controlRoutine;
+}
+
 void Actor::pause() {
 	++_pauseCtr;
 }
@@ -181,6 +185,15 @@ void Actor::runControlRoutine(Control *control, uint32 deltaTime) {
 		(*_controlRoutine)(control, deltaTime);
 }
 
+bool Actor::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	if (_namedPoints->findNamedPoint(namedPointId, pt)) {
+		pt.x += _position.x;
+		pt.y += _position.y;
+		return true;
+	}
+	return false;
+}
+
 // Control
 
 Control::Control(IllusionsEngine *vm)
@@ -199,7 +212,6 @@ Control::Control(IllusionsEngine *vm)
 	_position.y = 0;
 	_actorTypeId = 0;
 	_actor = 0;
-	// TODO _buf = 0;
 	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
 }
 
@@ -372,7 +384,7 @@ void Control::setPriority(int16 priority) {
 	_priority = priority;
 }
 
-int Control::getPriority() {
+uint32 Control::getPriority() {
 	uint32 objectId;
 	int16 positionY, priority, priority1;
 	if (_actor) {
@@ -397,13 +409,13 @@ int Control::getPriority() {
 	}
 
 	priority -= 1;
-	int p = 50 * priority1 / 100;
+	uint32 p = 50 * priority1 / 100;
 	if (p)
 		--p;
 
 	positionY = CLIP<int16>(positionY, -5000, 5000);
 
-	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));
+	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));;
 }
 
 Common::Point Control::calcPosition(Common::Point posDelta) {
@@ -644,18 +656,9 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_path40 = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
-	//debug("sequence: %p", (void*)sequence);
 
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
-	
-	/*
-	for (int i = 0; i < 64; ++i) {
-		debugN("%02X ", sequence->_sequenceCode[i]);
-	}
-	debug(".");
-	*/
-	
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
 	_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
@@ -683,6 +686,7 @@ void Control::execSequenceOpcode(OpCall &opCall) {
 Controls::Controls(IllusionsEngine *vm)
 	: _vm(vm) {
 	_sequenceOpcodes = new SequenceOpcodes(_vm);
+	_nextTempObjectId = 0;
 }
 
 Controls::~Controls() {
@@ -721,11 +725,10 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	actor->_position = placePt;
 	actor->_position2 = placePt;
 	Common::Point currPan = _vm->_camera->getCurrentPan();
-	// TODO if (!artcntrl_calcPointDirection(placePt, panPos, &actor->facing))
-	actor->_facing = 64;
+	if (!_vm->calcPointDirection(placePt, currPan, actor->_facing))
+		actor->_facing = 64;
 	actor->_scale = actorType->_scale;
-	// TODO actor->_namedPointsCount = actorType->_namedPointsCount;
-	// TODO actor->_namedPoints = actorType->_namedPoints;
+	actor->_namedPoints = &actorType->_namedPoints;
 	
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
 	if (actorType->_pathWalkPointsIndex) {
@@ -791,8 +794,7 @@ void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, Wi
 	actor->_position2 = placePt;
 	actor->_facing = 64;
 	actor->_scale = 100;
-	// TODO actor->_namedPointsCount = 0;
-	// TODO actor->_namedPoints = 0;
+	actor->_namedPoints = 0;
 	actor->_pathCtrY = 140;
 
 	_controls.push_back(control);
@@ -816,6 +818,17 @@ void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Commo
 	_vm->_dict->setObjectControl(objectId, control);
 }
 
+void Controls::placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId) {
+	Control *parentControl = _vm->_dict->getObjectControl(objectId);
+	uint32 tempObjectId = newTempObjectId();
+	placeActor(actorTypeId, Common::Point(0, 0), sequenceId, tempObjectId, 0);
+	parentControl->_actor->_subobjects[linkIndex - 1] = tempObjectId;
+	Actor *subActor = _vm->_dict->getObjectControl(tempObjectId)->_actor;
+	subActor->_flags |= 0x40;
+	subActor->_parentObjectId = parentControl->_objectId;
+	subActor->_linkIndex = linkIndex;
+}
+
 void Controls::destroyControlsByTag(uint32 tag) {
 	ItemsIterator it = _controls.begin();
 	while (it != _controls.end()) {
@@ -851,8 +864,8 @@ void Controls::unpauseControlsByTag(uint32 tag) {
 
 bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority) {
 	Control *foundControl = 0;
-	int foundPriority = 0;
-	// TODO minPriority = artcntrlGetPriorityFromBase(minPriority);
+	uint32 foundPriority = 0;
+	uint32 minPriorityExt = _vm->getPriorityFromBase(minPriority);
 
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *testControl = *it;
@@ -861,14 +874,10 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 			(!testControl->_actor || (testControl->_actor->_flags & 1))) {
 			Common::Rect collisionRect;
 			testControl->getCollisionRect(collisionRect);
-			//debug("collisionRect(%d, %d, %d, %d)", collisionRect.left, collisionRect.top, collisionRect.right, collisionRect.bottom);
-			//debug("pt(%d, %d)", pt.x, pt.y);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
-				int testPriority = testControl->getPriority();
-				//debug("testPriority: %d; minPriority: %d", testPriority, minPriority);
+				uint32 testPriority = testControl->getPriority();
 				if ((!foundControl || foundPriority < testPriority) &&
-					testPriority >= minPriority) {
-					//debug("overlapped() %08X; pauseCtr: %d; flags: %04X", testControl->_objectId, testControl->_pauseCtr, testControl->_flags);
+					testPriority >= minPriorityExt) {
 					foundControl = testControl;
 					foundPriority = testPriority;
 				}
@@ -887,6 +896,15 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 	return foundControl != 0;
 }
 
+bool Controls::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_pauseCtr == 0 && control->_actor && control->_actor->findNamedPoint(namedPointId, pt))
+			return true;
+	}
+	return false;
+}
+
 void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 
 	Actor *actor = control->_actor;
@@ -925,6 +943,17 @@ Control *Controls::newControl() {
 	return new Control(_vm);
 }
 
+uint32 Controls::newTempObjectId() {
+	uint32 nextTempObjectId1 = _nextTempObjectId;
+	uint32 nextTempObjectId2 = _nextTempObjectId + 0x1000;
+	if (nextTempObjectId2 > 0xFFFF) {
+		nextTempObjectId1 = 0;
+		nextTempObjectId2 = 0x1000;
+	}
+	_nextTempObjectId = nextTempObjectId1 + 1;
+	return nextTempObjectId2 | 0x40000;
+}
+
 void Controls::destroyControl(Control *control) {
 
 	if (control->_pauseCtr <= 0)
@@ -947,10 +976,6 @@ void Controls::destroyControl(Control *control) {
 		delete control->_actor;
 		control->_actor = 0;
 	}
-	/* TODO
-	if (control->_buf)
-		free(control->_buf);
-	*/
 	delete control;
 }
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 9ddfc9c..7e3bc7f 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -70,6 +70,7 @@ typedef Common::Functor2<Control*, uint32, void> ActorControlRoutine;
 class Actor {
 public:
 	Actor(IllusionsEngine *vm);
+	~Actor();
 	void pause();
 	void unpause();
 	void createSurface(SurfInfo &surfInfo);
@@ -79,6 +80,7 @@ public:
 	int16 popSequenceStack();
 	void setControlRoutine(ActorControlRoutine *controlRoutine);
 	void runControlRoutine(Control *control, uint32 deltaTime);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 	IllusionsEngine *_vm;
 	byte _drawFlags;
@@ -94,6 +96,7 @@ public:
 	Graphics::Surface *_surface;
 	
 	FramesList *_frames;
+	NamedPoints *_namedPoints;
 	
 	ScaleLayer *_scaleLayer;
 	PriorityLayer *_priorityLayer;
@@ -157,7 +160,7 @@ public:
 	void clearNotifyThreadId1();
 	void clearNotifyThreadId2();
 	void setPriority(int16 priority);
-	int getPriority();
+	uint32 getPriority();
 	Common::Point calcPosition(Common::Point posDelta);
 	uint32 getSubActorParent();
 	void getCollisionRectAccurate(Common::Rect &collisionRect);
@@ -200,10 +203,12 @@ public:
 	void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
+	void placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId);
 	void destroyControlsByTag(uint32 tag);
 	void pauseControlsByTag(uint32 tag);
 	void unpauseControlsByTag(uint32 tag);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 	void actorControlRoutine(Control *control, uint32 deltaTime);	
 public:
 	typedef Common::List<Control*> Items;
@@ -211,8 +216,10 @@ public:
 	IllusionsEngine *_vm;
 	Items _controls;
 	SequenceOpcodes *_sequenceOpcodes;
+	uint32 _nextTempObjectId;
 	Actor *newActor();
 	Control *newControl();
+	uint32 newTempObjectId();
 	void destroyControl(Control *control);
 };
 
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index bb3cd4d..affca78 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -108,16 +108,15 @@ void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
 		_sequenceId, _unk4, sequenceCodeOffs);
 
-	debug("_sequenceId: %08X", _sequenceId);
 }
 
 void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_actorTypeId = stream.readUint32LE();
 	_surfInfo.load(stream);
 	uint32 pointsConfigOffs = stream.readUint32LE();
-	stream.readUint16LE(); // TODO namedPointsCount dw
+	uint namedPointsCount = stream.readUint16LE();
 	stream.skip(2); // Skip padding
-	stream.readUint32LE(); // TODO namedPoints dd
+	uint32 namedPointsOffs = stream.readUint32LE();
 	_color.r = stream.readByte();
 	_color.g = stream.readByte();
 	_color.b = stream.readByte();
@@ -132,6 +131,8 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_regionLayerIndex = stream.readUint16LE();
 	_flags = stream.readUint16LE();
 	_pointsConfig = dataStart + pointsConfigOffs;
+	stream.seek(namedPointsOffs);
+	_namedPoints.load(namedPointsCount, stream);
 
 	debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
 		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
@@ -140,8 +141,6 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
 		_priorityLayerIndex, _regionLayerIndex,_flags);
 
-	debug("_actorTypeId: %08X; dimensions: (%d, %d)", _actorTypeId, _surfInfo._dimensions._width, _surfInfo._dimensions._height);
-
 }
 
 // ActorResource
@@ -162,10 +161,10 @@ void ActorResource::load(byte *data, uint32 dataSize) {
 	uint actorTypesCount = stream.readUint16LE();
 	stream.seek(0x10);
 	uint32 actorTypesOffs = stream.readUint32LE();
-	stream.seek(actorTypesOffs);
 	_actorTypes.reserve(actorTypesCount);
 	for (uint i = 0; i < actorTypesCount; ++i) {
 		ActorType actorType;
+		stream.seek(actorTypesOffs + i * 0x2C);
 		actorType.load(data, stream);
 		_actorTypes.push_back(actorType);
 	}
@@ -195,6 +194,12 @@ void ActorResource::load(byte *data, uint32 dataSize) {
 		frame.load(data, stream);
 		_frames.push_back(frame);
 	}
+	
+	// Load named points
+	// The count isn't stored explicitly so calculate it
+	uint namedPointsCount = (actorTypesOffs - 0x20) / 8;
+	stream.seek(0x20);
+	_namedPoints.load(namedPointsCount, stream);
 
 }
 
@@ -205,6 +210,10 @@ bool ActorResource::containsSequence(Sequence *sequence) {
 	return false;
 }
 
+bool ActorResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	return _namedPoints.findNamedPoint(namedPointId, pt);
+}
+
 // ActorItem
 
 ActorItem::ActorItem(IllusionsEngine *vm)
@@ -286,4 +295,13 @@ ActorItem *ActorItems::findActorByResource(ActorResource *actorResource) {
 	return 0;
 }
 
+bool ActorItems::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
+		ActorItem *actorItem = *it;
+		if (actorItem->_pauseCtr == 0 && actorItem->_actRes->findNamedPoint(namedPointId, pt))
+			return true;
+	}
+	return false;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index e22ed7e..78cfc19 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -62,8 +62,7 @@ struct ActorType {
 	uint32 _actorTypeId;
 	SurfInfo _surfInfo;
 	byte *_pointsConfig;
-	// TODO namedPointsCount dw
-	// TODO namedPoints dd
+	NamedPoints _namedPoints;
 	RGB _color;
 	byte _scale;
 	byte _priority;
@@ -86,11 +85,13 @@ public:
 	~ActorResource();
 	void load(byte *data, uint32 dataSize);
 	bool containsSequence(Sequence *sequence);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 	uint32 _totalSize;
 	Common::Array<ActorType> _actorTypes;
 	Common::Array<Sequence> _sequences;
 	FramesList _frames;
+	NamedPoints _namedPoints;
 };
 
 class ActorItem {
@@ -116,6 +117,7 @@ public:
 	void unpauseByTag(uint32 tag);
 	FramesList *findSequenceFrames(Sequence *sequence);
 	ActorItem *findActorByResource(ActorResource *actorResource);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 protected:
 	typedef Common::List<ActorItem*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index d80a9e2..d2b01fe 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -86,7 +86,7 @@ void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	uint32 mapOffs = stream.pos();
 	_map = dataStart + mapOffs;
 	
-	debug("TileMap::load() _width: %d; _height: %d",
+	debug(0, "TileMap::load() _width: %d; _height: %d",
 		_width, _height);
 }
 
@@ -104,7 +104,7 @@ void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_tileMap.load(dataStart, stream);
 	_tilePixels = dataStart + tilePixelsOffs;
 	
-	debug("BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
+	debug(0, "BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
 		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
 }
 
@@ -121,7 +121,7 @@ void PriorityLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_map += 8;
 	_values = dataStart + valuesOffs;
 	
-	debug("PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
+	debug(0, "PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
 		_width, _height, mapOffs, valuesOffs, _mapWidth, _mapHeight);
 }
 
@@ -140,7 +140,7 @@ void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	uint32 valuesOffs = stream.readUint32LE();
 	_values = dataStart + valuesOffs;
 	
-	debug("ScaleLayer::load() _height: %d; valuesOffs: %08X",
+	debug(0, "ScaleLayer::load() _height: %d; valuesOffs: %08X",
 		_height, valuesOffs);
 }
 
@@ -158,7 +158,7 @@ void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream)
 	uint32 pointsConfigOffs = stream.readUint32LE();
 	_pointsConfig = dataStart + pointsConfigOffs;
 	
-	debug("BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
+	debug(0, "BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
 		_objectId, _flags, _priority, pointsConfigOffs);
 }
 
@@ -192,7 +192,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_scaleLayers = new ScaleLayer[_scaleLayersCount];
 	stream.seek(0x2C);
 	uint32 scaleLayersOffs = stream.readUint32LE();
-	debug("_scaleLayersCount: %d", _scaleLayersCount);
+	debug(0, "_scaleLayersCount: %d", _scaleLayersCount);
 	for (uint i = 0; i < _scaleLayersCount; ++i) {
 		stream.seek(scaleLayersOffs + i * 8);
 		_scaleLayers[i].load(data, stream);
@@ -204,7 +204,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_priorityLayers = new PriorityLayer[_priorityLayersCount];
 	stream.seek(0x34);
 	uint32 priorityLayersOffs = stream.readUint32LE();
-	debug("_priorityLayersCount: %d", _priorityLayersCount);
+	debug(0, "_priorityLayersCount: %d", _priorityLayersCount);
 	for (uint i = 0; i < _priorityLayersCount; ++i) {
 		stream.seek(priorityLayersOffs + i * 12);
 		_priorityLayers[i].load(data, stream);
@@ -216,11 +216,19 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_backgroundObjects = new BackgroundObject[_backgroundObjectsCount];
 	stream.seek(0x44);
 	uint32 backgroundObjectsOffs = stream.readUint32LE();
-	debug("_backgroundObjectsCount: %d", _backgroundObjectsCount);
+	debug(0, "_backgroundObjectsCount: %d", _backgroundObjectsCount);
 	for (uint i = 0; i < _backgroundObjectsCount; ++i) {
 		stream.seek(backgroundObjectsOffs + i * 12);
 		_backgroundObjects[i].load(data, stream);
 	}
+	
+	// Load named points
+	stream.seek(0xC);
+	uint namedPointsCount = stream.readUint16LE();
+	stream.seek(0x24);
+	uint32 namedPointsOffs = stream.readUint32LE();
+	stream.seek(namedPointsOffs);
+	_namedPoints.load(namedPointsCount, stream);
 
 }
 
@@ -239,6 +247,10 @@ ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
 	return &_scaleLayers[index];
 }
 
+bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	return _namedPoints.findNamedPoint(namedPointId, pt);
+}
+
 // BackgroundItem
 
 BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
@@ -421,6 +433,11 @@ void BackgroundItems::refreshPan() {
 	}
 }
 
+bool BackgroundItems::findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	BackgroundResource *backgroundResource = getActiveBgResource();
+	return backgroundResource ? backgroundResource->findNamedPoint(namedPointId, pt) : false;
+}
+
 BackgroundItem *BackgroundItems::debugFirst() {
 	return *(_items.begin());
 }
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index cfb9067..daf2ffd 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -116,6 +116,7 @@ public:
 	int findMasterBgIndex();
 	PriorityLayer *getPriorityLayer(uint index);
 	ScaleLayer *getScaleLayer(uint index);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 
 	uint _bgInfosCount;
@@ -129,6 +130,8 @@ public:
 	
 	uint _backgroundObjectsCount;
 	BackgroundObject *_backgroundObjects;
+	
+	NamedPoints _namedPoints;
 
 };
 
@@ -168,6 +171,7 @@ public:
 	BackgroundResource *getActiveBgResource();
 	WidthHeight getMasterBgDimensions();
 	void refreshPan();
+	bool findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt);
 	BackgroundItem *debugFirst();
 //protected:
 public:
diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index 3256bc4..cc6019c 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -60,8 +60,8 @@ void BbdouBubble::init() {
 		0x00040037, 0x00040038, 0x00040039, 0x0004003A
 	};
 
-	_field1414 = 0x4005B;
-	_field1418 = 0x4005C;
+	_objectId1414 = 0x4005B;
+	_objectId1418 = 0x4005C;
 
 	for (uint i = 0; i < 32; ++i)
 		_objectIds[i] = kObjectIds3[i];
@@ -147,4 +147,40 @@ void BbdouBubble::hide() {
 	}
 }
 
+void BbdouBubble::setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId) {
+	for (uint i = 0; i < 32; ++i)
+		_items[i]._enabled = 0;
+	int16 maxCount = 32;
+	for (uint i = 0; i < _item0s.size(); ++i) {
+		Item0 *item0 = &_item0s[i];
+		if (item0->_count < maxCount && item0->_count >= minCount &&
+			(!progResKeywordId || item0->_progResKeywordId == progResKeywordId)) {
+			maxCount = item0->_count;
+			_currItem0 = item0; 
+		}
+	}
+	_pt1 = pt1;
+	_pt2 = pt2;
+	_currItem0->_pt = pt2;
+	_currItem0->_objectId = _objectId1414;
+	if (_prevItem0 && _prevItem0->_objectId == _currItem0->_objectId)
+		_currItem0->_objectId = _objectId1418;
+}
+
+uint32 BbdouBubble::addItem(uint positionIndex, uint32 sequenceId) {
+	for (uint i = 0; i < 32; ++i) {
+		Item141C *item = &_items[i];
+		if (!item->_enabled) {
+			Common::Point itemPos = _vm->getNamedPointPosition(_currItem0->_namedPointIds[positionIndex]);
+			Common::Point basePos = _vm->getNamedPointPosition(_currItem0->_baseNamedPointId);
+			item->_enabled = 1;
+			item->_sequenceId = sequenceId;
+			item->_position.x = itemPos.x + _currItem0->_pt.x - basePos.x;
+			item->_position.y = itemPos.y + _currItem0->_pt.y - basePos.y;
+			return item->_objectId;
+		}
+	}
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_bubble.h b/engines/illusions/bbdou/bbdou_bubble.h
index 9064acd..2426c9e 100644
--- a/engines/illusions/bbdou/bbdou_bubble.h
+++ b/engines/illusions/bbdou/bbdou_bubble.h
@@ -61,6 +61,8 @@ public:
 		uint32 namedPointId, int16 count, uint32 *namedPointIds);
 	void show();
 	void hide();
+	void setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId);
+	uint32 addItem(uint positionIndex, uint32 sequenceId);
 protected:
 	IllusionsEngine *_vm;
 	BbdouSpecialCode *_bbdou;
@@ -71,8 +73,8 @@ protected:
 	uint32 _objectIds[32];
 	Common::Point _pt1;
 	Common::Point _pt2;
-	int _field1414;
-	int _field1418;
+	int _objectId1414;
+	int _objectId1418;
 	Item141C _items[32];
 };
 
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
new file mode 100644
index 0000000..b1d4f92
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -0,0 +1,32 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/bbdou/bbdou_inventory.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+
+namespace Illusions {
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
new file mode 100644
index 0000000..31023c0
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
+#define ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
+
+#include "illusions/specialcode.h"
+#include "common/rect.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+class BbdouSpecialCode;
+class Control;
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 62b0a7a..96e4cb0 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -33,6 +33,22 @@
 
 namespace Illusions {
 
+static const Struct10 kStruct10s[] = {
+	{0x1B0000,       0,       0,       0},
+	{0x1B0001, 0x6001A, 0x6001B, 0x6001C},
+	{0x1B0002, 0x6001D, 0x6001E, 0x6001F},
+	{0x1B0003, 0x60020, 0x60021, 0x60022},
+	{0x1B0004, 0x60023, 0x60024, 0x60025},
+	{0x1B0005, 0x60026, 0x60027, 0x60028},
+	{0x1B0006,       0,       0,       0},
+	{0x1B0007,       0,       0,       0},
+	{0x1B0008,       0,       0,       0},
+	{0x1B0009,       0,       0,       0},
+	{0x1B000A,       0,       0,       0},
+	{0x1B000B,       0,       0,       0},
+	{0x1B000C,       0,       0,       0},
+};
+
 CauseThread::CauseThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId,
 	BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId, uint32 verbId,
 	uint32 objectId2, uint32 objectId)
@@ -92,8 +108,8 @@ void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
 
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %08X)", name);
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(1, "ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(1, "ARG_UINT32(" #name " = %08X)", name);
 
 void BbdouSpecialCode::spcInitCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
@@ -196,7 +212,58 @@ Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos)
 
 void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
 	Item10 *item10, uint32 progResKeywordId) {
-	// TODO
+	
+	Common::Rect collisionRect;
+	Control *overlappedControl, *control2, *control3;
+	Common::Point pt1(320, 240), pt2, currPan;
+	
+	overlappedControl = _vm->_dict->getObjectControl(overlappedObjectId);
+	overlappedControl->getCollisionRect(collisionRect);
+
+	currPan = _vm->_camera->getCurrentPan();
+	pt2.x = CLIP((collisionRect.right + collisionRect.left) / 2, currPan.x - 274, currPan.x + 274);
+	pt2.y = CLIP(collisionRect.top - (collisionRect.bottom - collisionRect.top) / 8, currPan.y - 204, currPan.y + 204);
+
+	control2 = _vm->_dict->getObjectControl(0x4000F);
+	if (!control2 || (control2->_actor && control2->_actor->_frameIndex == 0))
+		control2 = _vm->_dict->getObjectControl(0x4000E);
+
+	if (control2 && control2->_actor && control2->_actor->_frameIndex) {
+		pt1.x = control2->_actor->_surfInfo._dimensions._width / 2 + pt1.x - control2->_position.x;
+		pt1.y = control2->_actor->_position.y - control2->_position.y;
+		pt1.y = pt1.y >= 500 ? 500 : pt1.y + 32;
+		if (ABS(pt1.x - pt2.x) < ABS(pt1.y - pt2.y) / 2)
+			pt1.y += 80;
+	}
+
+	_bubble->setup(1, pt1, pt2, progResKeywordId);
+
+	item10->_objectIds[0] = _bubble->addItem(0, 0x6005A);
+	item10->_objectIds[1] = _bubble->addItem(0, 0x6005A);
+	item10->_index = 0;
+	
+	int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+	if (holdingObjectId) {
+		item10->_verbId = 0x1B0003;
+	} else if (value == 9) {
+		item10->_verbId = 0x1B0005;
+	} else if (value == 8) {
+		item10->_verbId = 0x1B0005;
+	} else {
+		item10->_verbId = 0x1B0002;
+	}
+	
+	uint32 sequenceId = kStruct10s[item10->_verbId & 0xFFFF]._sequenceId2;
+	_bubble->show();
+	
+	control3 = _vm->_dict->getObjectControl(item10->_objectIds[0]);
+	control3->startSequenceActor(sequenceId, 2, 0);
+	control3->appearActor();
+	control3->deactivateObject();
+	
+	item10->_playSound48 = 1;
+	_vm->_input->discardButtons(0xFFFF);
+
 }
 
 bool BbdouSpecialCode::findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId) {
@@ -255,13 +322,11 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 		}
 		
 		if (foundOverlapped) {
-			debug("overlappedControl: %p", (void*)overlappedControl);
 			if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
 				if (cursorData._item10._playSound48)
 					playSoundEffect(4);
 				resetItem10(cursorControl->_objectId, &cursorData._item10);
 				int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
-				debug("object value: %d", value);
 				if (!testValueRange(value)) {
 					if (cursorData._mode == 3)
 						_cursor->restoreInfo();
@@ -453,8 +518,6 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 		success = true;
 	}
 	
-	debug("runCause() success: %d", success);
-	
 	if (!success)
 		return false;
 	
@@ -479,7 +542,7 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 
 uint32 BbdouSpecialCode::startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
 	uint32 tempThreadId = _vm->_scriptMan->newTempThreadId();
-	debug("staring cause thread %08X...", tempThreadId);
+	debug(3, "Starting cause thread %08X...", tempThreadId);
 	CauseThread *causeThread = new CauseThread(_vm, tempThreadId, 0, this,
 		cursorObjectId, sceneId, verbId, objectId2, objectId);
 	_vm->_scriptMan->_threads->startThread(causeThread);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 61f6b70..9781c6d 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -39,6 +39,13 @@ typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
 class BbdouSpecialCode;
 
+struct Struct10 {
+	uint32 _verbId;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	uint32 _sequenceId3;
+};
+
 class CauseThread : public Thread {
 public:
 	CauseThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId,
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 0aff84f..300065a 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -134,7 +134,7 @@ void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
 	} else {
 		_activeState._currPan = _activeState._panTargetPoint;
 		stopPan();
-		_vm->notifyThreadId(_activeState._panNotifyId);
+		_vm->notifyThreadId(panNotifyId);
 	}
 }
 
@@ -396,23 +396,14 @@ void Camera::updateMode3(uint32 currTime) {
 
 bool Camera::updatePan(uint32 currTime) {
 	if (currTime - _activeState._time28 >= _activeState._time2E) {
-debug("#1 updatePan");
 		_activeState._panXShl = _activeState._panTargetPoint.x << 16;
 		_activeState._panYShl = _activeState._panTargetPoint.y << 16;
 	} else {
-debug("#2 updatePan");
-debug("_activeState._someX = %d", _activeState._someX);
-debug("1) _activeState._panXShl = %08X", _activeState._panXShl);
-debug("currTime - _activeState._panStartTime = %d", currTime - _activeState._panStartTime);
 		_activeState._panXShl += fixedMul(_activeState._someX, (currTime - _activeState._panStartTime) << 16);
 		_activeState._panYShl += fixedMul(_activeState._someY, (currTime - _activeState._panStartTime) << 16);
-debug("2) _activeState._panXShl = %08X", _activeState._panXShl);
 	}
 	_activeState._panStartTime = currTime;
 	Common::Point newPan(_activeState._panXShl >> 16, _activeState._panYShl >> 16);
-	
-	debug("newPan = %d, %d", newPan.x, newPan.y);
-	
 	if (_activeState._currPan.x != newPan.x || _activeState._currPan.y != newPan.y) {
 		_activeState._currPan = newPan;
 		return true;
@@ -442,24 +433,13 @@ void Camera::recalcPan(uint32 currTime) {
 		FP16 x2 = _activeState._panTargetPoint.x << 16;
 		FP16 y2 = _activeState._panTargetPoint.y << 16;
 		FP16 distance = fixedDistance(x1, y1, x2, y2);
-		
-		debug("(%08X, %08X), (%08X, %08X) %08X", x1, y1, x2, y2, distance);
-		
 		_activeState._time2E = 60 * fixedTrunc(distance) / _activeState._panSpeed;
-		
-		debug("_activeState._time2E = %d", _activeState._time2E);
-		
 	}
 
 	if (_activeState._time2E != 0) {
-debug("#1 recalcPan");
-debug("_activeState._panTargetPoint.x = %d; _activeState._currPan2.x = %d", _activeState._panTargetPoint.x, _activeState._currPan2.x);
-debug("_activeState._panTargetPoint.x - _activeState._currPan2.x = %d", _activeState._panTargetPoint.x - _activeState._currPan2.x);
-debug("_activeState._time2E = %d", _activeState._time2E);
 		_activeState._someX = fixedDiv((_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16, _activeState._time2E << 16);
 		_activeState._someY = fixedDiv((_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16, _activeState._time2E << 16);
 	} else {
-debug("#2 recalcPan");	
 		_activeState._someX = (_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16;
 		_activeState._someY = (_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16;
 	}
@@ -486,19 +466,10 @@ bool Camera::calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags) {
 }
 
 void Camera::clipPanTargetPoint() {
-
-	debug("clip in (%d, %d)", _activeState._panTargetPoint.x, _activeState._panTargetPoint.y);
-
 	_activeState._panTargetPoint.x = CLIP(_activeState._panTargetPoint.x,
 		_activeState._bounds._topLeft.x, _activeState._bounds._bottomRight.x);
 	_activeState._panTargetPoint.y = CLIP(_activeState._panTargetPoint.y,
 		_activeState._bounds._topLeft.y, _activeState._bounds._bottomRight.y);
-		
-	debug("clip rect (%d, %d, %d, %d)", _activeState._bounds._topLeft.x, _activeState._bounds._topLeft.y,
-		_activeState._bounds._bottomRight.x, _activeState._bounds._bottomRight.y);		
-
-	debug("clip out (%d, %d)", _activeState._panTargetPoint.x, _activeState._panTargetPoint.y);
-
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
index 36ab97a..ccbff95 100644
--- a/engines/illusions/cursor.cpp
+++ b/engines/illusions/cursor.cpp
@@ -73,7 +73,7 @@ void Cursor::show() {
 
 void Cursor::hide() {
 	--_visibleCtr;
-	if (_visibleCtr < 0) {
+	if (_visibleCtr <= 0) {
 		_control->_flags &= ~1;
 		_control->_actor->_flags &= ~1;
 	}
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index 6113098..37fe4d5 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -24,6 +24,8 @@
 
 namespace Illusions {
 
+// WidthHeight
+
 void WidthHeight::load(Common::SeekableReadStream &stream) {
 	_width = stream.readSint16LE();
 	_height = stream.readSint16LE();
@@ -32,6 +34,8 @@ void WidthHeight::load(Common::SeekableReadStream &stream) {
 		_width, _height);
 }
 
+// SurfInfo
+
 void SurfInfo::load(Common::SeekableReadStream &stream) {
 	_pixelSize = stream.readUint32LE();
 	_dimensions.load(stream);
@@ -40,6 +44,34 @@ void SurfInfo::load(Common::SeekableReadStream &stream) {
 		_pixelSize);
 }
 
+// NamedPoint
+
+void NamedPoint::load(Common::SeekableReadStream &stream) {
+	_namedPointId = stream.readUint32LE();
+	loadPoint(stream, _pt);
+}
+
+// NamedPoints
+
+bool NamedPoints::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	for (ItemsIterator it = _namedPoints.begin(); it != _namedPoints.end(); ++it)
+		if ((*it)._namedPointId == namedPointId) {
+			pt = (*it)._pt;
+			return true;
+		}
+	return false;
+}
+
+void NamedPoints::load(uint count, Common::SeekableReadStream &stream) {
+	_namedPoints.reserve(count);
+	for (uint i = 0; i < count; ++i) {
+		NamedPoint namedPoint;
+		namedPoint.load(stream);
+		_namedPoints.push_back(namedPoint);
+		debug(0, "namedPoint(%08X, %d, %d)", namedPoint._namedPointId, namedPoint._pt.x, namedPoint._pt.y);
+	}
+}
+
 void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
 	pt.x = stream.readSint16LE();
 	pt.y = stream.readSint16LE();
diff --git a/engines/illusions/graphics.h b/engines/illusions/graphics.h
index 4f50f15..a264368 100644
--- a/engines/illusions/graphics.h
+++ b/engines/illusions/graphics.h
@@ -23,6 +23,7 @@
 #ifndef ILLUSIONS_GRAPHICS_H
 #define ILLUSIONS_GRAPHICS_H
 
+#include "common/array.h"
 #include "common/rect.h"
 #include "common/stream.h"
 
@@ -48,6 +49,22 @@ struct RGB {
 	byte r, g, b;
 };
 
+struct NamedPoint {
+	uint32 _namedPointId;
+	Common::Point _pt;
+	void load(Common::SeekableReadStream &stream);
+};
+
+class NamedPoints {
+public:
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
+	void load(uint count, Common::SeekableReadStream &stream);
+protected:
+	typedef Common::Array<NamedPoint> Items;
+	typedef Items::iterator ItemsIterator;
+	Items _namedPoints;
+};
+
 void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt);
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 5f7bb23..bd302f8 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -113,41 +113,6 @@ Common::Error IllusionsEngine::run() {
 	_unpauseControlActorFlag = false;
 	_lastUpdateTime = 0;
 	
-#if 0
-	// ActorResource test
-	_resSys->loadResource(0x00100006, 0, 0);
-#endif
-
-#if 0
-	// BackgroundResource test
-	_resSys->loadResource(0x00110007, 0, 0);
-	BackgroundItem *backgroundItem = _backgroundItems->debugFirst();
-	_system->copyRectToScreen((byte*)backgroundItem->_surfaces[0]->getBasePtr(0, 0), backgroundItem->_surfaces[0]->pitch, 0, 0, 640, 480);
-	_system->updateScreen();
-	_camera->panToPoint(Common::Point(800, 0), 500, 0);
-	while (!shouldQuit()) {
-		//debug("panPoints[0] = %d, %d", backgroundItem->_panPoints[0].x, backgroundItem->_panPoints[0].y);
-		uint32 t = getCurrentTime();
-		//debug("t = %d", t);
-		_camera->update(t);
-		_system->delayMillis(10);
-		_system->copyRectToScreen((byte*)backgroundItem->_surfaces[0]->getBasePtr(backgroundItem->_panPoints[0].x, 0),
-			backgroundItem->_surfaces[0]->pitch, 0, 0, 640, 480);
-		_system->updateScreen();
-		updateEvents();
-	}
-#endif
-	
-#if 0
-	// ScriptResource test
-	_resSys->loadResource(0x000D0001, 0, 0);
-	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
-	while (!shouldQuit()) {
-		updateEvents();
-		_scriptMan->_threads->updateThreads();
-	}
-#endif
-
 #if 1
 	// Actor/graphics/script test
 
@@ -156,23 +121,9 @@ Common::Error IllusionsEngine::run() {
 	
 	_resSys->loadResource(0x000D0001, 0, 0);
 
-#if 0	
-	_resSys->loadResource(0x0011000B, 0, 0);
-	_resSys->loadResource(0x0010000B, 0, 0);
-
-#if 0
-	_controls->placeActor(0x00050009, Common::Point(0, 0), 0x00060573, 0x00040001, 0);
-	Control *control = *_controls->_controls.begin();
-	control->setActorFrameIndex(1);
-	control->appearActor();
-#endif
-#endif
-
 	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
 	_scriptMan->_doScriptThreadInit = true;
 
-	//_camera->panToPoint(Common::Point(800, 0), 500, 0);
-
 	while (!shouldQuit()) {
 		_scriptMan->_threads->updateThreads();
 		updateActors();
@@ -262,7 +213,6 @@ uint32 IllusionsEngine::getElapsedUpdateTime() {
 int IllusionsEngine::updateActors() {
 	// TODO Move to Controls class
 	uint32 deltaTime = getElapsedUpdateTime();
-	//debug("deltaTime: %d", deltaTime);
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
 		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_controlRoutine)
@@ -294,8 +244,7 @@ int IllusionsEngine::updateGraphics() {
 		BackgroundResource *bgRes = backgroundItem->_bgRes;
 		for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
 			BgInfo *bgInfo = &bgRes->_bgInfos[i];
-			// TODO int16 priority = artcntrlGetPriorityFromBase(bgInfos[v7].priorityBase);
-			int16 priority = -1;
+			uint32 priority = getPriorityFromBase(bgInfo->_priorityBase);
 			_screen->_drawQueue->insertSurface(backgroundItem->_surfaces[i],
 				bgInfo->_surfInfo._dimensions, backgroundItem->_panPoints[i], priority);
 			if (bgInfo->_flags & 1)
@@ -324,8 +273,7 @@ int IllusionsEngine::updateGraphics() {
 			}
 			*/
 			if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
-				// TODO int16 priority = control->getPriority();
-				int16 priority = 2;
+				uint32 priority = control->getPriority();
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
 					actor->_surfInfo._dimensions, drawPosition, control->_position,
 					priority, actor->_scale, actor->_spriteFlags);
@@ -352,7 +300,7 @@ bool IllusionsEngine::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 obje
 	uint32 codeOffs;
 	// TODO Also search for native trigger functions later (findCauseFunc)
 	bool r = _scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
-	debug("causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
+	debug(3, "causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
 		sceneId, verbId, objectId2, objectId, r);
 	return r;
 }
@@ -374,10 +322,26 @@ int IllusionsEngine::convertPanXCoord(int16 x) {
 }
 
 Common::Point IllusionsEngine::getNamedPointPosition(uint32 namedPointId) {
+	Common::Point pt;
+	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt) ||
+		_actorItems->findNamedPoint(namedPointId, pt) ||
+		_controls->findNamedPoint(namedPointId, pt))
+    	return pt;
 	// TODO
+	debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
 	return Common::Point(0, 0);
 }
 
+uint32 IllusionsEngine::getPriorityFromBase(int16 priority) {
+	return 32000000 * priority;
+}
+
+bool IllusionsEngine::calcPointDirection(Common::Point &pos1, Common::Point &pos2, uint &facing) {
+	// TODO
+	facing = 0;
+	return false;
+}
+
 void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
 	// TODO
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 71f7a38..58dc519 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -119,6 +119,9 @@ public:
 
 	int convertPanXCoord(int16 x);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
+	uint32 getPriorityFromBase(int16 priority);
+	bool calcPointDirection(Common::Point &pos1, Common::Point &pos2, uint &facing);
+
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
 	
 	bool isSoundActive();
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index b9ae53a..1a174b1 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -121,6 +121,7 @@ void Input::initKeys() {
 	// NOTE Skipped debugging keys of the original engine, not sure if used
 	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON0, 0x01);
 	addKeyMapping(Common::KEYCODE_RETURN, MOUSE_NONE, 0x01);
+	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x02);
 	addKeyMapping(Common::KEYCODE_TAB, MOUSE_NONE, 0x04);
 	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x04);
 	addKeyMapping(Common::KEYCODE_ESCAPE, MOUSE_NONE, 0x08);
@@ -153,15 +154,9 @@ void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
 		}
 	}
 	uint prevButtonStates = _buttonStates;
-
-	debug("_newKeys = %08X", _newKeys);
-
 	_buttonStates |= _newKeys;
 	_newKeys = 0;
 	_newButtons = ~prevButtonStates & _buttonStates;
-	
-	debug("_buttonStates = %08X", _buttonStates);
-	
 }
 
 void Input::handleMouseButton(int mouseButton, bool down) {
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 762bd4c..6a31657 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	backgroundresource.o \
 	bbdou/bbdou_bubble.o \
 	bbdou/bbdou_cursor.o \
+	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
 	camera.o \
 	cursor.o \
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 27e570a..b556c0c 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -147,14 +147,14 @@ void ScriptMan::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
 
 void ScriptMan::startScriptThread(uint32 threadId, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
-	debug("Starting script thread %08X", threadId);
+	debug(2, "Starting script thread %08X", threadId);
 	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
 	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
 }
 
 void ScriptMan::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
-	debug("Starting anonymous script thread %08X", threadId);
+	debug(2, "Starting anonymous script thread %08X", threadId);
 	uint32 tempThreadId = newTempThreadId();
 	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
 	scriptCodeIp = _scriptResource->getThreadCode(threadId);
@@ -164,7 +164,7 @@ void ScriptMan::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
 uint32 ScriptMan::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
 	uint32 tempThreadId = newTempThreadId();
-	debug("Starting temp script thread %08X", tempThreadId);
+	debug(2, "Starting temp script thread %08X", tempThreadId);
 	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
 	return tempThreadId;
 }
@@ -179,7 +179,7 @@ uint32 ScriptMan::startTimerThread(uint32 duration, uint32 threadId) {
 
 uint32 ScriptMan::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
 	uint32 tempThreadId = newTempThreadId();
-	debug("Starting abortable thread %08X", tempThreadId);
+	debug(2, "Starting abortable thread %08X", tempThreadId);
 	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
 	AbortableThread *abortableThread = new AbortableThread(_vm, tempThreadId, callingThreadId, 0,
 		scriptThreadId, scriptCodeIp2);
@@ -189,7 +189,7 @@ uint32 ScriptMan::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2,
 
 uint32 ScriptMan::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
 	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
-	debug("Starting talk thread");
+	debug(2, "Starting talk thread");
 	uint32 tempThreadId = newTempThreadId();
 	// TODO endTalkThreadsNoNotify();
 	TalkThread *talkThread = new TalkThread(_vm, tempThreadId, callingThreadId, 0,
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 8abfa8b..abfda67 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/actor.h"
+#include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
@@ -69,7 +70,7 @@ ScriptOpcodes::~ScriptOpcodes() {
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
+	debug(0, "\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
@@ -96,18 +97,27 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(17, opUnloadResource);
 	OPCODE(20, opEnterScene);
 	OPCODE(25, opChangeScene);
+	OPCODE(26, opStartModalScene);
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
+	OPCODE(32, opPanCenterObject);
+	OPCODE(35, opPanToNamedPoint);
+	OPCODE(37, opPanStop);
 	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
 	OPCODE(45, opSetProperty);
-	OPCODE(46, opPlaceActor);	
+	OPCODE(46, opPlaceActor);
+	OPCODE(47, opFaceActor);
+	OPCODE(48, opFaceActorToObject);	
 	OPCODE(49, opStartSequenceActor);
+	OPCODE(51, opStartMoveActor);
+	OPCODE(53, opSetActorToNamedPoint);
 	OPCODE(56, opStartTalkThread);
 	OPCODE(57, opAppearActor);
 	OPCODE(58, opDisappearActor);
 	OPCODE(60, opActivateObject);
 	OPCODE(61, opDeactivateObject);
+	OPCODE(62, opSetDefaultSequence);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
 	OPCODE(65, opSetDenySfx);
@@ -126,7 +136,9 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	OPCODE(103, opJumpIf);
-	OPCODE(107, opBoolNot);
+	OPCODE(107, opNot);
+	OPCODE(108, opAnd);
+	OPCODE(109, opOr);
 	OPCODE(110, opGetProperty);
 	OPCODE(111, opCompareBlockCounter);
 	OPCODE(126, opDebug126);
@@ -156,8 +168,8 @@ void ScriptOpcodes::freeOpcodes() {
 
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug("ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug("ARG_UINT32(" #name " = %08X)", name);
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(0, "ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(0, "ARG_UINT32(" #name " = %08X)", name);
 
 void ScriptOpcodes::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
 	opCall._result = kTSSuspend;
@@ -218,7 +230,7 @@ void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(resourceId);
 	// NOTE Skipped checking for stalled resources
-	uint32 sceneId = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	uint32 sceneId = _vm->getCurrentScene();
 	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
 }
 
@@ -248,7 +260,7 @@ void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_UINT32(threadId);
 	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardButtons(0xFFFF);
-	_vm->_scriptMan->_prevSceneId = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_vm->_scriptMan->_prevSceneId = _vm->getCurrentScene();
 	_vm->_scriptMan->exitScene(opCall._callerThreadId);
 	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
 	// TODO _vm->_gameStates->writeStates(_vm->_scriptMan->_prevSceneId, sceneId, threadId);
@@ -256,6 +268,20 @@ void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
 }
 
+void ScriptOpcodes::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_scriptMan->enterPause(opCall._callerThreadId);
+	// TODO _vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
+	_vm->_scriptMan->startScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+	opCall._result = kTSSuspend;
+}
+
 void ScriptOpcodes::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -271,6 +297,23 @@ void ScriptOpcodes::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCal
 	opCall._result = kTSYield;
 }
 
+void ScriptOpcodes::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	_vm->_camera->panCenterObject(objectId, speed);
+}
+
+void ScriptOpcodes::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_camera->panToPoint(pos, speed, opCall._callerThreadId);
+}
+
+void ScriptOpcodes::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_camera->stopPan();
+}
+
 void ScriptOpcodes::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(flag);
 	_vm->_screen->setDisplayOn(flag != 0);
@@ -299,16 +342,59 @@ void ScriptOpcodes::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
 }
 
+void ScriptOpcodes::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->faceActor(facing);
+}
+
+void ScriptOpcodes::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Control *control2 = _vm->_dict->getObjectControl(objectId2);
+	Common::Point pos1 = control1->getActorPosition();
+	Common::Point pos2 = control2->getActorPosition();
+	uint facing;
+	if (_vm->calcPointDirection(pos1, pos2, facing))
+		control1->faceActor(facing);
+}
+
 void ScriptOpcodes::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
 	ARG_UINT32(sequenceId);
 	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
-	debug("control: %p", (void*)control);
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void ScriptOpcodes::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	// TODO _control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+
+	//DEBUG Resume calling thread, later done by the walking
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void ScriptOpcodes::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->stopActor();
+	control->setActorPosition(pos);
+}
+
 void ScriptOpcodes::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(duration);	
 	ARG_UINT32(objectId);
@@ -358,6 +444,15 @@ void ScriptOpcodes::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCal
 	control->deactivateObject();
 }
 
+void ScriptOpcodes::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(defaultSequenceId);
+	ARG_UINT32(sequenceId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
+}
+
 void ScriptOpcodes::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(soundEffectId);
@@ -482,11 +577,23 @@ void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 		opCall._deltaOfs += jumpOffs;
 }
 
-void ScriptOpcodes::opBoolNot(ScriptThread *scriptThread, OpCall &opCall) {
+void ScriptOpcodes::opNot(ScriptThread *scriptThread, OpCall &opCall) {
 	int16 value = _vm->_scriptMan->_stack.pop();
 	_vm->_scriptMan->_stack.push(value != 0 ? 0 : 1);
 }
 
+void ScriptOpcodes::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_scriptMan->_stack.pop();
+	int16 value2 = _vm->_scriptMan->_stack.pop();
+	_vm->_scriptMan->_stack.push(value1 & value2);
+}
+
+void ScriptOpcodes::opOr(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_scriptMan->_stack.pop();
+	int16 value2 = _vm->_scriptMan->_stack.pop();
+	_vm->_scriptMan->_stack.push(value1 | value2);
+}
+
 void ScriptOpcodes::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(propertyId)
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 0e75f78..8516c85 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -72,18 +72,27 @@ protected:
 	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
@@ -102,7 +111,9 @@ protected:
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
-	void opBoolNot(ScriptThread *scriptThread, OpCall &opCall);
+	void opNot(ScriptThread *scriptThread, OpCall &opCall);
+	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
+	void opOr(ScriptThread *scriptThread, OpCall &opCall);
 	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 2495bca..409aa15 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -24,6 +24,7 @@
 #include "illusions/sequenceopcodes.h"
 #include "illusions/actor.h"
 #include "illusions/actorresource.h"
+#include "illusions/dictionary.h"
 #include "illusions/scriptopcodes.h"
 
 namespace Illusions {
@@ -59,7 +60,9 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(5, opSetRandomFrameDelay);
 	OPCODE(6, opSetFrameSpeed);
 	OPCODE(7, opJump);
+	OPCODE(8, opJumpRandom);
 	OPCODE(9, opGotoSequence);
+	OPCODE(10, opStartForeignSequence);
 	OPCODE(11, opBeginLoop);
 	OPCODE(12, opNextLoop);
 	OPCODE(14, opSwitchActorIndex);
@@ -74,6 +77,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(40, opSetPriorityLayer);
 	OPCODE(50, opPlaySound);
 	OPCODE(51, opStopSound);
+	OPCODE(53, opPlaceSubActor);
 }
 
 #undef OPCODE
@@ -142,6 +146,13 @@ void SequenceOpcodes::opJump(Control *control, OpCall &opCall) {
 	opCall._deltaOfs += jumpOffs;
 }
 
+void SequenceOpcodes::opJumpRandom(Control *control, OpCall &opCall) {
+	ARG_INT16(count);
+	ARG_SKIP(_vm->getRandom(count) * 2);
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
 void SequenceOpcodes::opGotoSequence(Control *control, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(nextSequenceId);
@@ -155,6 +166,13 @@ void SequenceOpcodes::opGotoSequence(Control *control, OpCall &opCall) {
 	opCall._deltaOfs = 0;
 }
 
+void SequenceOpcodes::opStartForeignSequence(Control *control, OpCall &opCall) {
+	ARG_INT16(foreignObjectNum);
+	ARG_UINT32(sequenceId);
+	Control *foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
+	foreignControl->startSequenceActor(sequenceId, 2, 0);
+}
+
 void SequenceOpcodes::opBeginLoop(Control *control, OpCall &opCall) {
 	ARG_INT16(loopCount);
 	control->_actor->pushSequenceStack(loopCount);
@@ -252,4 +270,11 @@ void SequenceOpcodes::opStopSound(Control *control, OpCall &opCall) {
 	// TODO _vm->stopSound(soundEffectId);
 }
 
+void SequenceOpcodes::opPlaceSubActor(Control *control, OpCall &opCall) {
+ 	ARG_INT16(linkIndex);
+ 	ARG_UINT32(actorTypeId);
+ 	ARG_UINT32(sequenceId);
+ 	_vm->_controls->placeSubActor(control->_objectId, linkIndex, actorTypeId, sequenceId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index dd1302f..849846f 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -50,7 +50,9 @@ protected:
 	void opSetRandomFrameDelay(Control *control, OpCall &opCall);
 	void opSetFrameSpeed(Control *control, OpCall &opCall);	
 	void opJump(Control *control, OpCall &opCall);
+	void opJumpRandom(Control *control, OpCall &opCall);
 	void opGotoSequence(Control *control, OpCall &opCall);
+	void opStartForeignSequence(Control *control, OpCall &opCall);
 	void opBeginLoop(Control *control, OpCall &opCall);
 	void opNextLoop(Control *control, OpCall &opCall);
 	void opSwitchActorIndex(Control *control, OpCall &opCall);
@@ -64,7 +66,8 @@ protected:
 	void opSetPriority(Control *control, OpCall &opCall);
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
 	void opPlaySound(Control *control, OpCall &opCall);
-	void opStopSound(Control *control, OpCall &opCall);	
+	void opStopSound(Control *control, OpCall &opCall);
+	void opPlaceSubActor(Control *control, OpCall &opCall);	
 	
 };
 
diff --git a/engines/illusions/spritedrawqueue.cpp b/engines/illusions/spritedrawqueue.cpp
index f39eb5f..d5a7394 100644
--- a/engines/illusions/spritedrawqueue.cpp
+++ b/engines/illusions/spritedrawqueue.cpp
@@ -82,7 +82,7 @@ void SpriteDrawQueue::drawAll() {
 }
 
 void SpriteDrawQueue::insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
-	Common::Point &drawPosition, Common::Point &controlPosition, int priority, int16 scale, uint16 flags) {
+	Common::Point &drawPosition, Common::Point &controlPosition, uint32 priority, int16 scale, uint16 flags) {
 	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
 	item->_drawFlags = drawFlags;
 	*item->_drawFlags &= 4;
@@ -98,7 +98,7 @@ void SpriteDrawQueue::insertSprite(byte *drawFlags, Graphics::Surface *surface,
 }
 
 void SpriteDrawQueue::insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-	Common::Point &drawPosition, int priority) {
+	Common::Point &drawPosition, uint32 priority) {
 	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
 	item->_surface = surface;
 	item->_dimensions = dimensions;
@@ -110,12 +110,12 @@ void SpriteDrawQueue::insertSurface(Graphics::Surface *surface, WidthHeight &dim
 	item->_controlPosition.y = 0;
 	item->_flags = 0;
 	item->_scale = 100;
-	item->_priority = priority << 16;
+	item->_priority = priority;// << 16;
 	insert(item, priority);
 }
 
 void SpriteDrawQueue::insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-	Common::Point &drawPosition, int priority) {
+	Common::Point &drawPosition, uint32 priority) {
 	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
 	item->_surface = surface;
 	item->_drawPosition = drawPosition;
@@ -130,7 +130,7 @@ void SpriteDrawQueue::insertTextSurface(Graphics::Surface *surface, WidthHeight
 	insert(item, priority);
 }
 
-void SpriteDrawQueue::insert(SpriteDrawQueueItem *item, int priority) {
+void SpriteDrawQueue::insert(SpriteDrawQueueItem *item, uint32 priority) {
 	SpriteDrawQueueListIterator insertionPos = Common::find_if(_queue.begin(), _queue.end(),
 		FindInsertionPosition(priority));
 	_queue.insert(insertionPos, item);
@@ -143,9 +143,6 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	srcRect.right = item->_dimensions._width;
 	srcRect.bottom = item->_dimensions._height;
 	
-	//debug("item->_drawPosition.x: %d; item->_drawPosition.y: %d", item->_drawPosition.x, item->_drawPosition.y);
-	//debug("item->_controlPosition.x: %d; item->_controlPosition.y: %d", item->_controlPosition.x, item->_controlPosition.y);
-
 	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
 	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
 	dstRect.right = item->_drawPosition.x + item->_scale * (item->_dimensions._width - item->_controlPosition.x) / 100;
diff --git a/engines/illusions/spritedrawqueue.h b/engines/illusions/spritedrawqueue.h
index 30b999a..baa4217 100644
--- a/engines/illusions/spritedrawqueue.h
+++ b/engines/illusions/spritedrawqueue.h
@@ -39,7 +39,7 @@ struct SpriteDrawQueueItem {
 	int16 _scale;
 	uint16 _flags;
 	//field_A dw
-	int _priority;
+	uint32 _priority;
 	Graphics::Surface *_surface;
 	WidthHeight _dimensions;
 	Common::Point _drawPosition;
@@ -53,18 +53,18 @@ public:
 	bool draw(SpriteDrawQueueItem *item);
 	void drawAll();
 	void insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
-		Common::Point &drawPosition, Common::Point &controlPosition, int priority, int16 scale, uint16 flags);
+		Common::Point &drawPosition, Common::Point &controlPosition, uint32 priority, int16 scale, uint16 flags);
 	void insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-		Common::Point &drawPosition, int priority);
+		Common::Point &drawPosition, uint32 priority);
 	void insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-		Common::Point &drawPosition, int priority);
+		Common::Point &drawPosition, uint32 priority);
 protected:
 	typedef Common::List<SpriteDrawQueueItem*> SpriteDrawQueueList;
 	typedef SpriteDrawQueueList::iterator SpriteDrawQueueListIterator;
 
 	struct FindInsertionPosition : public Common::UnaryFunction<const SpriteDrawQueueItem*, bool> {
-		int _priority;
-		FindInsertionPosition(int priority) : _priority(priority) {}
+		uint32 _priority;
+		FindInsertionPosition(uint32 priority) : _priority(priority) {}
 		bool operator()(const SpriteDrawQueueItem *item) const {
 			return item->_priority >= _priority;
 		}
@@ -72,7 +72,7 @@ protected:
 
 	Screen *_screen;
 	SpriteDrawQueueList _queue;	
-	void insert(SpriteDrawQueueItem *item, int priority);
+	void insert(SpriteDrawQueueItem *item, uint32 priority);
 	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
 };
 
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 0a9b0d9..83e0ce5 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -101,10 +101,8 @@ int Thread::update() {
 
 void Thread::terminate() {
 	if (!_terminated) {
-		if (!(_notifyFlags & 1)) {
-			debug("Thread::terminate() _callingThreadId: %08X", _callingThreadId);
+		if (!(_notifyFlags & 1))
 			_vm->notifyThreadId(_callingThreadId);
-		}
 		_callingThreadId = 0;
 		onTerminated();
 		// TODO _vm->removeThread(_threadId, this);


Commit: 3b3f84c764f8cab8f54b06bbafc607cfb6899fdd
    https://github.com/scummvm/scummvm/commit/3b3f84c764f8cab8f54b06bbafc607cfb6899fdd
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement inventory

- Implement cursor camera tracking
- Various bugfixes

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/illusions.cpp
    engines/illusions/scriptman.cpp
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/talkresource.cpp
    engines/illusions/talkthread.cpp
    engines/illusions/talkthread.h
    engines/illusions/thread.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 7f2a054..53190ef 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -433,13 +433,13 @@ Common::Point Control::calcPosition(Common::Point posDelta) {
 		pos.x += accuX * actor->_scale / 100;
 		pos.y += accuY * actor->_scale / 100;
 		_actor->_position = pos;
-		if (!(_actor->_flags & 8)) {
+		if (!(_flags & 8)) {
 			pos.x -= posDelta.x;
 			pos.y -= posDelta.y;
 		}
 	} else {
 		pos = _actor->_position;
-		if (!(_actor->_flags & 8)) {
+		if (!(_flags & 8)) {
 			pos.x -= posDelta.x;
 			pos.y -= posDelta.y;
 		}
@@ -574,7 +574,8 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 				subControl->_actor->notifyThreadId2 = threadId;
 				subControl->_actor->entryTblPtr = entryTblPtr;
 				subControl->_actor->flags |= 0x80;
-				script_TalkThreads_sub_417FA0(subControl->_actor->_notifyThreadId2, 0);
+				Thread *thread = _vm->_threads->findThread(threadId);
+				thread->sendMessage(kMsgClearSequenceId2, 0);
 			}
 			*/
 		}
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index d2b01fe..50a88d2 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -181,6 +181,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_bgInfos = new BgInfo[_bgInfosCount];
 	stream.seek(0x20);
 	uint32 bgInfosOffs = stream.readUint32LE();
+	debug("_bgInfosCount: %d", _bgInfosCount);
 	for (uint i = 0; i < _bgInfosCount; ++i) {
 		stream.seek(bgInfosOffs + i * 0x1C);
 		_bgInfos[i].load(data, stream);
@@ -314,7 +315,7 @@ void BackgroundItem::pause() {
 			krndictRemoveID(_bgRes->_item48s[i].id);
 		*/
 		// TODO _vm->setDefPointDimensions1();
-		// TODO memcpy(&_savedCamera, &_vm->camera, sizeof(backgroundItem->savedCamera));
+		_vm->_camera->getActiveState(_savedCameraState);
 		/* Unused
 		_savedPalette = malloc(1024);
 		savePalette(_savedPalette);
@@ -338,11 +339,7 @@ void BackgroundItem::unpause() {
 		_savedPalette = 0;
 		*/
 		// TODO _vm->_screen->_fadeClear();
-		// TODO memcpy(&_vm->camera, &_savedCamera, sizeof(SavedCamera));
-		/* TODO
-		currTime = krnxxxGetCurrentTime();
-		_vm->_camera.panStartTime = currTime;
-		*/
+		_vm->_camera->setActiveState(_savedCameraState);
 		_vm->_backgroundItems->refreshPan();
 	}
 }
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index daf2ffd..741047f 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -23,6 +23,7 @@
 #ifndef ILLUSIONS_BACKGROUNDRESOURCE_H
 #define ILLUSIONS_BACKGROUNDRESOURCE_H
 
+#include "illusions/camera.h"
 #include "illusions/graphics.h"
 #include "illusions/resourcesystem.h"
 #include "graphics/surface.h"
@@ -154,7 +155,7 @@ public:
 	BackgroundResource *_bgRes;
 	Common::Point _panPoints[kMaxBackgroundItemSurfaces];
 	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
-	// TODO SavedCamera savedCamera;
+	CameraState _savedCameraState;
 	// TODO? byte *savedPalette;
 };
 
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 1c4af97..ec87755 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/bbdou/bbdou_cursor.h"
+#include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
@@ -142,6 +143,25 @@ int BbdouCursor::findStruct8bsValue(uint32 objectId) {
 	return 11;
 }
 
+bool BbdouCursor::updateTrackingCursor(Control *control) {
+	uint32 sequenceId;
+	if (getTrackingCursorSequenceId(control, sequenceId)) {
+		if (_data._sequenceId98 != sequenceId) {
+			saveBeforeTrackingCursor(control, sequenceId);
+			show(control);
+			_data._sequenceId98 = sequenceId;
+		}
+		return true;
+	} else {
+		if (_data._sequenceId98) {
+			_data._sequenceId98 = 0;
+			restoreAfterTrackingCursor();
+			show(control);
+		}
+		return false;
+	}
+}
+
 void BbdouCursor::saveInfo() {
 	_data._mode2 = _data._mode;
 	_data._sequenceId2 = _data._sequenceId;
@@ -157,6 +177,24 @@ void BbdouCursor::restoreInfo() {
 	_data._sequenceId2 = 0;
 }
 
+void BbdouCursor::saveBeforeTrackingCursor(Control *control, uint32 sequenceId) {
+	if (_data._currOverlappedObjectId || _data._mode == 3) {
+		if (_data._mode == 3)
+			restoreInfo();
+		control->setActorIndexTo1();
+		if (_data._item10._playSound48)
+			_bbdou->playSoundEffect(4);
+		_bbdou->resetItem10(control->_objectId, &_data._item10);
+	}
+	_data._currOverlappedObjectId = 0;
+	if (_data._mode != 4) {
+		saveInfo();
+		_data._mode = 4;
+		_data._holdingObjectId = 0;
+	}
+	_data._sequenceId = sequenceId;
+}
+
 void BbdouCursor::restoreAfterTrackingCursor() {
 	_data._holdingObjectId = _data._holdingObjectId2;
 	if (_data._holdingObjectId2) {
@@ -191,6 +229,104 @@ uint32 BbdouCursor::getSequenceId1(int sequenceIndex) {
 	}
 }
 
+uint BbdouCursor::calcTrackingFlags(Common::Point actorPos, Common::Point trackingLimits) {
+	uint trackingFlags = 0;
+	int16 x = actorPos.x - 320;
+	int16 y = actorPos.y - 240;
+	if (x < -trackingLimits.x)
+		trackingFlags = 1;
+	else if (x > trackingLimits.x)
+		trackingFlags = 3;
+	else
+		trackingFlags = 2;
+	if (y < -trackingLimits.y)
+		trackingFlags += 0;
+	else if (y > trackingLimits.y)
+		trackingFlags += 6;
+	else
+		trackingFlags += 3;
+	return trackingFlags;
+}
+
+uint BbdouCursor::calcTrackingCursorIndex(uint trackingFlags) {
+	uint cursorIndex = 0;
+	switch (trackingFlags) {
+	case 1:
+		if (_vm->_camera->isAtPanLimit(1)) {
+			if (!_vm->_camera->isAtPanLimit(3))
+				cursorIndex = 4;
+		} else {
+			if (!_vm->_camera->isAtPanLimit(3))
+				cursorIndex = 1;
+			else
+				cursorIndex = 2;
+		}
+		break;
+	case 2:
+		if (!_vm->_camera->isAtPanLimit(1))
+			cursorIndex = 2;
+		break;
+	case 3:
+		if (_vm->_camera->isAtPanLimit(1)) {
+			if (!_vm->_camera->isAtPanLimit(4))
+				cursorIndex = 6;
+		} else {
+			if (!_vm->_camera->isAtPanLimit(4))
+				cursorIndex = 3;
+			else
+				cursorIndex = 2;
+		}
+		break;
+	case 4:
+		if (!_vm->_camera->isAtPanLimit(3))
+			cursorIndex = 4;
+		break;
+	case 6:
+		if (!_vm->_camera->isAtPanLimit(4))
+			cursorIndex = 6;
+		break;
+	case 7:
+		if (_vm->_camera->isAtPanLimit(2)) {
+			if (!_vm->_camera->isAtPanLimit(3))
+				cursorIndex = 4;
+		} else {
+			if (!_vm->_camera->isAtPanLimit(3))
+				cursorIndex = 8;
+			else
+				cursorIndex = 7;
+		}
+		break;
+	case 8:
+		if (!_vm->_camera->isAtPanLimit(2))
+			cursorIndex = 8;
+		break;
+	case 9:
+		if (_vm->_camera->isAtPanLimit(2)) {
+			if (!_vm->_camera->isAtPanLimit(4))
+				cursorIndex = 6;
+		} else {
+			if (!_vm->_camera->isAtPanLimit(4))
+				cursorIndex = 9;
+			else
+				cursorIndex = 8;
+		}
+		break;
+	}
+	return cursorIndex;
+}
+
+bool BbdouCursor::getTrackingCursorSequenceId(Control *control, uint32 &outSequenceId) {
+	static const uint32 kTrackingCursorSequenceIds[] = {
+		0, 0x000609BF, 0x00060018, 0x000609C0, 0x00060016,
+		0, 0x00060017, 0x000609C1, 0x00060019, 0x000609C2
+	};
+	Common::Point trackingLimits = _vm->_camera->getTrackingLimits();
+	uint trackingFlags = calcTrackingFlags(control->_actor->_position, trackingLimits);
+	uint cursorIndex = calcTrackingCursorIndex(trackingFlags);
+	outSequenceId = kTrackingCursorSequenceIds[cursorIndex];
+	return outSequenceId != 0;
+}
+
 void BbdouCursor::clearCursorDataField14() {
 	for (uint i = 0; i < 32; ++i)
 		_data._item10._verbActive[i] = 0;
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index bd3ba3a..43918e8 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -94,10 +94,15 @@ public:
 	uint32 findCursorSequenceId(uint32 objectId);
 	void setStruct8bsValue(uint32 objectId, int value);
 	int findStruct8bsValue(uint32 objectId);
+	bool updateTrackingCursor(Control *control);
 	void saveInfo();
 	void restoreInfo();
+	void saveBeforeTrackingCursor(Control *control, uint32 sequenceId);
 	void restoreAfterTrackingCursor();
 	uint32 getSequenceId1(int sequenceIndex);
+	uint calcTrackingFlags(Common::Point actorPos, Common::Point trackingLimits);
+	uint calcTrackingCursorIndex(uint trackingFlags);
+	bool getTrackingCursorSequenceId(Control *control, uint32 &outSequenceId);
 public:
 	IllusionsEngine *_vm;
 	BbdouSpecialCode *_bbdou;
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index b1d4f92..4c5646f 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -22,6 +22,8 @@
 
 #include "illusions/illusions.h"
 #include "illusions/bbdou/bbdou_inventory.h"
+#include "illusions/bbdou/bbdou_cursor.h"
+#include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
@@ -29,4 +31,198 @@
 
 namespace Illusions {
 
+// InventoryItem
+
+InventoryItem::InventoryItem(uint32 objectId, uint32 sequenceId)
+	: _objectId(objectId), _sequenceId(sequenceId),
+	_assigned(false), _flag(false), _timesPresent(0), _fieldE(0) {
+}
+
+// InventorySlot
+
+InventorySlot::InventorySlot(uint32 namedPointId)
+	: _namedPointId(namedPointId), _objectId(0), _inventoryItem(0) {
+}
+
+// InventoryBag
+
+InventoryBag::InventoryBag(IllusionsEngine *vm, uint32 sceneId)
+	: _vm(vm), _sceneId(sceneId), _isActive(false), _fieldA(0) {
+}
+
+void InventoryBag::registerInventorySlot(uint32 namedPointId) {
+	_inventorySlots.push_back(new InventorySlot(namedPointId));
+}
+
+bool InventoryBag::addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot) {
+	// NOTE Skipped support for multiple items per slot, not used in BBDOU
+	if (!inventorySlot) {
+		for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it)
+			if (!(*it)->_inventoryItem) {
+				inventorySlot = *it;
+				break;
+			}
+	}
+	if (inventorySlot) {
+		inventorySlot->_inventoryItem = inventoryItem;
+		return true;
+	}
+	return false;
+}
+
+void InventoryBag::removeInventoryItem(InventoryItem *inventoryItem) {
+	// TODO
+}
+
+void InventoryBag::buildItems() {
+	for (InventorySlotsIterator it = _inventorySlots.begin();
+		it != _inventorySlots.end(); ++it) {
+		InventorySlot *inventorySlot = *it;
+		InventoryItem *inventoryItem = inventorySlot->_inventoryItem;
+		if (inventoryItem) {
+			++inventoryItem->_timesPresent;
+			if (!inventoryItem->_assigned || inventoryItem->_flag || inventoryItem->_timesPresent > 1)
+				inventorySlot->_inventoryItem = 0;
+		}
+	}
+}
+
+// BbdouInventory
+
+BbdouInventory::BbdouInventory(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
+	: _vm(vm), _bbdou(bbdou) {
+}
+				                                             
+void BbdouInventory::registerInventoryBag(uint32 sceneId) {
+	_inventoryBags.push_back(new InventoryBag(_vm, sceneId));
+	_activeBagSceneId = sceneId;
+}
+
+void BbdouInventory::registerInventoryItem(uint32 objectId, uint32 sequenceId) {
+	_activeBagSceneId = 0;
+	_inventoryItems.push_back(new InventoryItem(objectId, sequenceId));
+}
+
+void BbdouInventory::registerInventorySlot(uint32 namedPointId) {
+	InventoryBag *inventoryBag = getInventoryBag(_activeBagSceneId);
+	inventoryBag->registerInventorySlot(namedPointId);
+}
+
+void BbdouInventory::addInventoryItem(uint32 objectId) {
+	_activeBagSceneId = 0;
+	InventoryItem *inventoryItem = getInventoryItem(objectId);
+	bool assigned = inventoryItem->_assigned;
+	inventoryItem->_assigned = true;
+	if (!assigned && !inventoryItem->_flag) {
+		for (uint i = 0; i < _inventoryBags.size(); ++i)
+			if (!_inventoryBags[i]->addInventoryItem(inventoryItem, 0))
+				inventoryItem->_assigned = false;
+	}
+	if (_activeInventorySceneId)
+		refresh();
+}
+
+void BbdouInventory::removeInventoryItem(uint32 objectId) {
+	InventoryItem *inventoryItem = getInventoryItem(objectId);
+	bool flag = inventoryItem->_flag;
+	inventoryItem->_flag = true;
+	if (!flag && inventoryItem->_assigned) {
+		if (_activeInventorySceneId) {
+			InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
+			inventoryBag->removeInventoryItem(inventoryItem);
+		}
+		refresh();
+	}
+}
+
+void BbdouInventory::open() {
+	_activeBagSceneId = 0;
+	InventoryBag *inventoryBag = getInventoryBag(_vm->getCurrentScene());
+	buildItems(inventoryBag);
+	if (_activeInventorySceneId) {
+		refresh();
+		refresh();
+	} else {
+		_activeInventorySceneId = _vm->getCurrentScene();
+		_index = 1;
+		inventoryBag->_isActive = true;
+		for (InventoryBag::InventorySlotsIterator it = inventoryBag->_inventorySlots.begin();
+			it != inventoryBag->_inventorySlots.end(); ++it) {
+			InventorySlot *inventorySlot = *it;
+			Common::Point slotPos = _vm->getNamedPointPosition(inventorySlot->_namedPointId);
+			Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
+			if (control) {
+				control->setActorPosition(slotPos);
+				control->startSequenceActor(0x0006005A, 2, 0);
+			} else {
+				inventorySlot->_objectId = _vm->_controls->newTempObjectId();
+				_vm->_controls->placeActor(0x00050012, slotPos, 0x0006005A, inventorySlot->_objectId, 0);
+			}
+			// TODO causeDeclare(0x1B0002, 0, inventorySlot->_objectId, Inventory_cause0x1B0002);
+			// TODO causeDeclare(0x1B0001, 0, inventorySlot->_objectId, Inventory_cause0x1B0001);
+			// TODO causeDeclare(0x1B0008, 0, inventorySlot->_objectId, Inventory_cause0x1B0001);
+		}
+		refresh();
+	}
+}
+
+void BbdouInventory::close() {
+	if (!_activeInventorySceneId)
+		return;
+	InventoryBag *inventoryBag = getInventoryBag(_vm->getCurrentScene());
+	for (InventoryBag::InventorySlotsIterator it = inventoryBag->_inventorySlots.begin();
+		it != inventoryBag->_inventorySlots.end(); ++it) {
+		InventorySlot *inventorySlot = *it;
+		Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
+		control->startSequenceActor(0x00060187, 2, 0);
+    }
+    inventoryBag->_isActive = false;
+    _activeInventorySceneId = 0;
+}
+
+InventoryBag *BbdouInventory::getInventoryBag(uint32 sceneId) {
+	for (uint i = 0; i < _inventoryBags.size(); ++i)
+		if (_inventoryBags[i]->_sceneId == sceneId)
+			return _inventoryBags[i];
+	return 0;
+}
+
+InventoryItem *BbdouInventory::getInventoryItem(uint32 objectId) {
+	for (uint i = 0; i < _inventoryItems.size(); ++i)
+		if (_inventoryItems[i]->_objectId == objectId)
+			return _inventoryItems[i];
+	return 0;
+}
+
+void BbdouInventory::refresh() {
+	if (!_activeInventorySceneId)
+		return;
+	InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
+	for (InventoryBag::InventorySlotsIterator it = inventoryBag->_inventorySlots.begin();
+		it != inventoryBag->_inventorySlots.end(); ++it) {
+		InventorySlot *inventorySlot = *it;
+		Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
+		InventoryItem *inventoryItem = inventorySlot->_inventoryItem;
+		if (inventoryItem) {
+			control->startSequenceActor(inventoryItem->_sequenceId, 2, 0);
+			control->appearActor();
+		} else {
+			control->startSequenceActor(0x00060187, 2, 0);
+		}
+	}
+}
+
+void BbdouInventory::buildItems(InventoryBag *inventoryBag) {
+	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it)
+		(*it)->_timesPresent = 0;
+	inventoryBag->buildItems();
+	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it) {
+		InventoryItem *inventoryItem = *it;
+	    if (inventoryItem->_assigned && !inventoryItem->_flag &&
+			inventoryItem->_timesPresent == 0 &&
+			inventoryItem->_objectId != _bbdou->_cursor->_data._holdingObjectId)
+			inventoryBag->addInventoryItem(inventoryItem, 0);
+	}
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index 31023c0..a983212 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -24,6 +24,7 @@
 #define ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
 
 #include "illusions/specialcode.h"
+#include "common/array.h"
 #include "common/rect.h"
 
 namespace Illusions {
@@ -32,6 +33,68 @@ class IllusionsEngine;
 class BbdouSpecialCode;
 class Control;
 
+struct InventoryItem {
+	uint32 _objectId;
+	uint32 _sequenceId;
+	bool _assigned;
+	bool _flag;
+	int _timesPresent;
+	int _fieldE;
+	InventoryItem(uint32 objectId, uint32 sequenceId);
+};
+
+struct InventorySlot {
+	uint32 _namedPointId;
+	uint32 _objectId;
+	InventoryItem *_inventoryItem;
+	InventorySlot(uint32 namedPointId);
+};
+
+class InventoryBag {
+public:
+	InventoryBag(IllusionsEngine *vm, uint32 sceneId);
+	void registerInventorySlot(uint32 namedPointId);
+	bool addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot);
+	void removeInventoryItem(InventoryItem *inventoryItem);
+	void buildItems();
+protected:
+public:
+	typedef Common::Array<InventorySlot*> InventorySlots;
+	typedef InventorySlots::iterator InventorySlotsIterator;
+	IllusionsEngine *_vm;
+	uint32 _sceneId;
+	InventorySlots _inventorySlots;
+	bool _isActive;
+	int _fieldA;
+};
+
+class BbdouInventory {
+public:
+	BbdouInventory(IllusionsEngine *vm, BbdouSpecialCode *bbdou);
+	void registerInventoryBag(uint32 sceneId);
+	void registerInventoryItem(uint32 objectId, uint32 sequenceId);
+	void registerInventorySlot(uint32 namedPointId);
+	void addInventoryItem(uint32 objectId);
+	void removeInventoryItem(uint32 objectId);
+	void open();
+	void close();
+	InventoryBag *getInventoryBag(uint32 sceneId);
+	InventoryItem *getInventoryItem(uint32 objectId);
+	void refresh();
+	void buildItems(InventoryBag *inventoryBag);
+protected:
+	typedef Common::Array<InventoryItem*> InventoryItems;
+	typedef InventoryItems::iterator InventoryItemsIterator;
+	IllusionsEngine *_vm;
+	BbdouSpecialCode *_bbdou;
+	Common::Array<InventoryBag*> _inventoryBags;
+	InventoryItems _inventoryItems;
+	uint32 _activeBagSceneId;
+	uint32 _activeInventorySceneId;
+	int _index;
+	//field_12 dw
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 96e4cb0..c74676d 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/bbdou/bbdou_bubble.h"
+#include "illusions/bbdou/bbdou_inventory.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -73,9 +74,11 @@ BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine *vm)
 	: SpecialCode(vm) {
 	_bubble = new BbdouBubble(_vm, this);
 	_cursor = new BbdouCursor(_vm, this);
+	_inventory = new BbdouInventory(_vm, this);
 }
 
 BbdouSpecialCode::~BbdouSpecialCode() {
+	delete _inventory;
 	delete _cursor;
 	delete _bubble;
 }
@@ -92,6 +95,12 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160013, spcInitBubble);
 	SPECIAL(0x00160014, spcSetupBubble);
 	SPECIAL(0x00160015, spcSetObjectInteractMode);
+	SPECIAL(0x00160019, spcRegisterInventoryBag);
+	SPECIAL(0x0016001A, spcRegisterInventorySlot);
+	SPECIAL(0x0016001B, spcRegisterInventoryItem);
+	SPECIAL(0x0016001C, spcOpenInventory);
+	SPECIAL(0x0016001D, spcAddInventoryItem);
+	SPECIAL(0x00160025, spcCloseInventory);
 }
 
 void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
@@ -163,6 +172,36 @@ void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._callerThreadId);
 }
 
+void BbdouSpecialCode::spcRegisterInventoryBag(OpCall &opCall) {
+	ARG_UINT32(sceneId);
+	_inventory->registerInventoryBag(sceneId);
+}
+
+void BbdouSpecialCode::spcRegisterInventorySlot(OpCall &opCall) {
+	ARG_UINT32(namedPointId);
+	_inventory->registerInventorySlot(namedPointId);
+}
+
+void BbdouSpecialCode::spcRegisterInventoryItem(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	_inventory->registerInventoryItem(objectId, sequenceId);
+}
+
+void BbdouSpecialCode::spcOpenInventory(OpCall &opCall) {
+	_inventory->open();
+}
+
+void BbdouSpecialCode::spcAddInventoryItem(OpCall &opCall) {
+	ARG_UINT32(objectId);
+debug("### spcAddInventoryItem %08X", objectId);
+	_inventory->addInventoryItem(objectId);
+}
+
+void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
+	_inventory->close();
+}
+
 void BbdouSpecialCode::playSoundEffect(int soundIndex) {
 	static const uint32 kSoundEffectIds[] = {
 		      0, 1,
@@ -302,7 +341,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 			cursorData._idleCtr = 0;
 		}
 
-		if (updateTrackingCursor(cursorControl))
+		if (_cursor->updateTrackingCursor(cursorControl))
 			cursorData._flags |= 1;
 		else
 			cursorData._flags &= ~1;
@@ -432,11 +471,6 @@ void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 delt
 	// TODO
 }
 
-bool BbdouSpecialCode::updateTrackingCursor(Control *cursorControl) {
-	// TODO
-	return false;
-}
-
 bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId) {
 	static const uint32 kVerbIdsEE[] = {0x001B0002, 0x001B0001, 0};
 	static const uint32 kVerbIdsE9[] = {0x001B0005, 0};
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 9781c6d..dcfb5ba 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -32,6 +32,7 @@ namespace Illusions {
 class IllusionsEngine;
 class BbdouBubble;
 class BbdouCursor;
+class BbdouInventory;
 struct CursorData;
 struct Item10;
 
@@ -74,6 +75,7 @@ public:
 	Map _map;
 	BbdouCursor *_cursor;
 	BbdouBubble *_bubble;
+	BbdouInventory *_inventory;
 	// Special code interface functions
 	void spcInitCursor(OpCall &opCall);
 	void spcEnableCursor(OpCall &opCall);
@@ -82,10 +84,18 @@ public:
 	void spcInitBubble(OpCall &opCall);
 	void spcSetupBubble(OpCall &opCall);
 	void spcSetObjectInteractMode(OpCall &opCall);
-protected:
-	// Internal functions
+	void spcRegisterInventoryBag(OpCall &opCall);
+	void spcRegisterInventorySlot(OpCall &opCall);
+	void spcRegisterInventoryItem(OpCall &opCall);
+	void spcOpenInventory(OpCall &opCall);
+	void spcAddInventoryItem(OpCall &opCall);
+	void spcCloseInventory(OpCall &opCall);
+
 	void playSoundEffect(int soundIndex);
 	void resetItem10(uint32 objectId, Item10 *item10);
+
+protected:
+	// Internal functions
 	bool testValueRange(int value);
 	void setCursorControlRoutine(uint32 objectId, int num);
 	Common::Point getBackgroundCursorPos(Common::Point cursorPos);
@@ -94,7 +104,6 @@ protected:
 	bool findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
 	void cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime);
 	void cursorControlRoutine2(Control *cursorControl, uint32 deltaTime);
-	bool updateTrackingCursor(Control *cursorControl);
 	bool testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId);
 	bool getCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
 		uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId);
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 300065a..57d6f6f 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -243,8 +243,6 @@ void Camera::update(uint32 currTime) {
 	if (_activeState._paused)
 		return;
 
-	//debug("_activeState._cameraMode = %d", _activeState._cameraMode);
-
 	switch (_activeState._cameraMode) {
 	case 1:
 		updateMode1(currTime);
@@ -306,6 +304,33 @@ Common::Point Camera::getScreenOffset() {
 	return screenOffs;
 }
 
+Common::Point Camera::getTrackingLimits() {
+	return _activeState._trackingLimits;
+}
+
+bool Camera::isAtPanLimit(int limitNum) {
+	switch (limitNum) {
+	case 1:
+		return _activeState._currPan.y <= _activeState._bounds._topLeft.y;
+	case 2:
+		return _activeState._currPan.y >= _activeState._bounds._bottomRight.y;
+	case 3:
+		return _activeState._currPan.x <= _activeState._bounds._topLeft.x;
+	case 4:
+		return _activeState._currPan.x >= _activeState._bounds._bottomRight.x;
+	}
+	return false;
+}
+
+void Camera::setActiveState(CameraState &state) {
+	_activeState = state;
+	_activeState._panStartTime = getCurrentTime();
+}
+
+void Camera::getActiveState(CameraState &state) {
+	state = _activeState;
+}
+
 void Camera::updateMode1(uint32 currTime) {
 	Common::Point ptOffs = getPtOffset(*_activeState._panToPositionPtr);
 	int deltaX = ptOffs.x - _activeState._currPan.x + 320 - _activeState._centerPt.x;
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index 6c9556c..a6f8a73 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -81,6 +81,10 @@ public:
 	void setBoundsToDimensions(WidthHeight &dimensions);
 	Common::Point getCurrentPan();
 	Common::Point getScreenOffset();
+	Common::Point getTrackingLimits();
+	bool isAtPanLimit(int limitNum);
+	void setActiveState(CameraState &state);
+	void getActiveState(CameraState &state);
 protected:
 	IllusionsEngine *_vm;
 	CameraState _activeState;
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index bd302f8..968dc0f 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -310,6 +310,7 @@ uint32 IllusionsEngine::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objec
 	uint32 causeThreadId = 0;
 	// TODO Also search for native trigger functions later and run it (findCauseFunc)
 	if (_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
+		debug("Run cause at %08X", codeOffs);
 		causeThreadId = _scriptMan->startTempScriptThread(_scriptMan->_scriptResource->getCode(codeOffs),
 			callingThreadId, verbId, objectId2, objectId);
 	}
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index b556c0c..961ffd4 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -210,8 +210,7 @@ void ScriptMan::setCurrFontId(uint32 fontId) {
 }
 
 bool ScriptMan::checkActiveTalkThreads() {
-	// TODO
-	return false;
+	return _threads->isActiveThread(kMsgQueryTalkThreadActive);
 }
 
 uint32 ScriptMan::clipTextDuration(uint32 duration) {
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index abfda67..fbb9448 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -98,6 +98,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(20, opEnterScene);
 	OPCODE(25, opChangeScene);
 	OPCODE(26, opStartModalScene);
+	OPCODE(27, opExitModalScene);
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
 	OPCODE(32, opPanCenterObject);
@@ -282,6 +283,14 @@ void ScriptOpcodes::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall
 	opCall._result = kTSSuspend;
 }
 
+void ScriptOpcodes::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_scriptMan->exitScene(opCall._callerThreadId);
+	_vm->_scriptMan->leavePause(opCall._callerThreadId);
+	// TODO _vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+}
+
 void ScriptOpcodes::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -609,7 +618,7 @@ void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &op
 	bool compareResult = false;
 	switch (compareOp) {
 	case 1:
-		compareResult = false;//lvalue == rvalue;
+		compareResult = lvalue == rvalue;
 		break;
 	case 2:
 		compareResult = lvalue != rvalue;
@@ -660,7 +669,6 @@ void ScriptOpcodes::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeModuleId);
 	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
-	// TODO _vm->loadSpecialCodeModule(specialCodeModuleId);
 }
 
 void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
@@ -669,10 +677,6 @@ void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall)
 	_vm->_scriptMan->_callerThreadId = opCall._callerThreadId;
 	_vm->_specialCode->run(specialCodeId, opCall);
 	_vm->_scriptMan->_callerThreadId = 0;
-
-	//DEBUG Resume calling thread, later done by the special code
-	//_vm->notifyThreadId(opCall._callerThreadId);
-
 }
 
 void ScriptOpcodes::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 8516c85..507d21b 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -73,6 +73,7 @@ protected:
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 409aa15..7df968d 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -57,6 +57,7 @@ void SequenceOpcodes::initOpcodes() {
 	// Register opcodes
 	OPCODE(2, opSetFrameIndex);
 	OPCODE(3, opEndSequence);
+	OPCODE(4, opIncFrameDelay);
 	OPCODE(5, opSetRandomFrameDelay);
 	OPCODE(6, opSetFrameSpeed);
 	OPCODE(7, opJump);
@@ -67,6 +68,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(12, opNextLoop);
 	OPCODE(14, opSwitchActorIndex);
 	OPCODE(15, opSwitchFacing);
+	OPCODE(16, opAppearActor);
 	OPCODE(17, opDisappearActor);
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
@@ -130,10 +132,17 @@ void SequenceOpcodes::opEndSequence(Control *control, OpCall &opCall) {
 	opCall._result = 1;
 }
 
+void SequenceOpcodes::opIncFrameDelay(Control *control, OpCall &opCall) {
+	ARG_INT16(frameDelayIncr);
+	control->_actor->_seqCodeValue3 += frameDelayIncr;
+	opCall._result = 2;
+}
+
 void SequenceOpcodes::opSetRandomFrameDelay(Control *control, OpCall &opCall) {
 	ARG_INT16(minFrameDelay);
 	ARG_INT16(maxFrameDelay);
 	control->_actor->_seqCodeValue3 += 0;//DEBUG minFrameDelay + _vm->getRandom(maxFrameDelay);
+	opCall._result = 2;
 }
 
 void SequenceOpcodes::opSetFrameSpeed(Control *control, OpCall &opCall) {
@@ -201,6 +210,10 @@ void SequenceOpcodes::opSwitchFacing(Control *control, OpCall &opCall) {
 		opCall._deltaOfs += jumpOffs;
 }
 
+void SequenceOpcodes::opAppearActor(Control *control, OpCall &opCall) {
+	control->appearActor();
+}
+
 void SequenceOpcodes::opDisappearActor(Control *control, OpCall &opCall) {
 	control->disappearActor();
 	control->_actor->_newFrameIndex = 0;
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 849846f..abf1987 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -47,6 +47,7 @@ protected:
 	// Opcodes
 	void opSetFrameIndex(Control *control, OpCall &opCall);
 	void opEndSequence(Control *control, OpCall &opCall);
+	void opIncFrameDelay(Control *control, OpCall &opCall);
 	void opSetRandomFrameDelay(Control *control, OpCall &opCall);
 	void opSetFrameSpeed(Control *control, OpCall &opCall);	
 	void opJump(Control *control, OpCall &opCall);
@@ -57,6 +58,7 @@ protected:
 	void opNextLoop(Control *control, OpCall &opCall);
 	void opSwitchActorIndex(Control *control, OpCall &opCall);
 	void opSwitchFacing(Control *control, OpCall &opCall);
+	void opAppearActor(Control *control, OpCall &opCall);
 	void opDisappearActor(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index fe14eff..1d17408 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -29,7 +29,7 @@ namespace Illusions {
 // TalkResourceLoader
 
 void TalkResourceLoader::load(Resource *resource) {
-	debug("TalkResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
+	debug("TalkResourceLoader::load() Loading text %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	TalkResource *talkResource = new TalkResource();
 	talkResource->load(resource->_data, resource->_dataSize);
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index fc777b7..c8bee71 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -72,11 +72,12 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 	_voiceStartTime = getCurrentTime();
 	_voiceEndTime = _voiceStartTime + duration;
 	_entryTblPtr = 0;
-	
-	/* TODO
-	if (callingThreadId)
-		thread->tag = *(_DWORD *)(krndictGetIDValue(callingThreadId) + 20);
-	*/
+
+	if (callingThreadId) {
+		Thread *callingThread = _vm->_scriptMan->_threads->findThread(callingThreadId);
+		if (callingThread)
+			_tag = callingThread->_tag;
+	}
 	
 }
 
@@ -251,6 +252,31 @@ void TalkThread::onResume() {
 void TalkThread::onTerminated() {
 }
 
+void TalkThread::onKill() {
+	_callingThreadId = 0;
+	sendMessage(kMsgClearSequenceId1, 0);
+	sendMessage(kMsgClearSequenceId2, 0);
+}
+
+uint32 TalkThread::sendMessage(int msgNum, uint32 msgValue) {
+	// TODO
+	switch (msgNum) {
+	case kMsgQueryTalkThreadActive:
+        if (_status != 1 && _status != 2)
+			return 1;
+		break;
+	case kMsgClearSequenceId1:
+		_sequenceId1 = 0;
+		_flags |= 3;
+		// TODO _field30 = 0;
+		break;
+	case kMsgClearSequenceId2:
+		_sequenceId2 = 0;
+		break;
+	}
+	return 0;
+}
+
 void TalkThread::refreshText() {
 	_currEntryText = _entryText;
 	int charCount = insertText();
diff --git a/engines/illusions/talkthread.h b/engines/illusions/talkthread.h
index fd0e3d0..ada593b 100644
--- a/engines/illusions/talkthread.h
+++ b/engines/illusions/talkthread.h
@@ -30,6 +30,12 @@ namespace Illusions {
 class IllusionsEngine;
 struct TalkEntry;
 
+enum {
+	kMsgQueryTalkThreadActive    = 0,
+	kMsgClearSequenceId1         = 1,
+	kMsgClearSequenceId2         = 2
+};
+
 class TalkThread : public Thread {
 public:
 	TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
@@ -41,6 +47,8 @@ public:
 	virtual void onPause();
 	virtual void onResume();
 	virtual void onTerminated();
+	virtual void onKill();
+	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
 public:
 	//field0 dw
 	int _status;
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 83e0ce5..38e6900 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -54,6 +54,15 @@ void Thread::onResume() {
 void Thread::onTerminated() {
 }
 
+void Thread::onKill() {
+	// TODO artmgrThreadIsDead(thread->threadId);
+	terminate();
+}
+
+uint32 Thread::sendMessage(int msgNum, uint32 msgValue) {
+	return 0;
+}
+
 void Thread::pause() {
 	if (!_terminated) {
 		++_pauseCtr;
@@ -252,14 +261,7 @@ void ThreadList::killThread(uint32 threadId) {
 			killThread(childThread->_threadId);
 	}
 	
-	if (thread->_type == kTTTalkThread) {
-		thread->_callingThreadId = 0;
-		// TODO script_TalkThreads_sub_417F60(thread->_threadId, 0);
-		// TODO script_TalkThreads_sub_417FA0(thread->_threadId, 0);
-	} else {
-		// TODO artmgrThreadIsDead(thread->threadId);
-		thread->terminate();
-	}
+	thread->onKill();
 
 }
 
@@ -274,4 +276,12 @@ uint32 ThreadList::getThreadSceneId(uint32 threadId) {
 	return thread ? thread->_tag : 0;
 }
 
+bool ThreadList::isActiveThread(int msgNum) {
+	// Check if at least one thread returns a non-null value for the message
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it)
+		if ((*it)->sendMessage(msgNum, 0) != 0)
+			return true;
+	return false;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index fc1a059..2cd3cc2 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -54,6 +54,8 @@ public:
 	virtual void onPause();
 	virtual void onResume();
 	virtual void onTerminated();
+	virtual void onKill();
+	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
 	void pause();
 	void resume();
 	void suspend();
@@ -93,6 +95,7 @@ public:
 	void killThread(uint32 threadId);
 	void setThreadSceneId(uint32 threadId, uint32 sceneId);
 	uint32 getThreadSceneId(uint32 threadId);
+	bool isActiveThread(int msgNum);
 protected:
 	typedef Common::List<Thread*> List;
 	typedef List::iterator Iterator;


Commit: babe997295dbadd95ab497bc6b88e7a0021567e5
    https://github.com/scummvm/scummvm/commit/babe997295dbadd95ab497bc6b88e7a0021567e5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add more script opcodes

- Add support for duplicate keys to the dictionary
- Add trigger functions support
- Improve inventory, now items can be clicked

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/backgroundresource.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/dictionary.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/talkresource.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 53190ef..d9eb949 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -30,6 +30,8 @@
 #include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/sequenceopcodes.h"
+#include "illusions/talkthread.h"
+#include "illusions/thread.h"
 
 namespace Illusions {
 
@@ -641,6 +643,51 @@ void Control::setActorIndexTo2() {
 	_actor->_actorIndex = 2;
 }
 
+void Control::startSubSequence(int linkIndex, uint32 sequenceId) {
+	Control *linkedControl = _vm->_dict->getObjectControl(_actor->_subobjects[linkIndex - 1]);
+	Actor *linkedActor = linkedControl->_actor;
+	if (!linkedActor->_entryTblPtr)
+		linkedActor->_flags &= ~0x80;
+	linkedActor->_flags &= ~0x400;
+	linkedActor->_flags |= 0x100;
+	linkedActor->_sequenceId = sequenceId;
+	linkedActor->_notifyThreadId1 = 0;
+	linkedActor->_notifyId3C = 0;
+	linkedActor->_path40 = 0;
+
+	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
+	linkedActor->_seqCodeIp = sequence->_sequenceCode;
+	linkedActor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
+	linkedActor->_seqCodeValue3 = 0;
+	linkedActor->_seqCodeValue1 = 0;
+	linkedActor->_seqCodeValue2 = 600;
+	linkedActor->initSequenceStack();
+	linkedControl->sequenceActor();
+	linkedControl->appearActor();
+}
+
+void Control::stopSubSequence(int linkIndex) {
+	Control *linkedControl = _vm->_dict->getObjectControl(_actor->_subobjects[linkIndex - 1]);
+	Actor *linkedActor = linkedControl->_actor;
+	uint32 notifySequenceId2 = _actor->_notifyThreadId2;
+	_actor->_linkIndex2 = linkIndex;
+	if (_actor->_entryTblPtr) {
+		linkedActor->_flags |= 0x80;
+		linkedActor->_entryTblPtr = _actor->_entryTblPtr;
+		linkedActor->_notifyThreadId2 = _actor->_notifyThreadId2;
+		linkedActor->_seqCodeValue1 = _actor->_seqCodeValue1;
+		linkedActor->_seqCodeValue3 = _actor->_seqCodeValue3;
+		_actor->_flags &= ~0x80;
+		_actor->_entryTblPtr = 0;
+		_actor->_notifyThreadId1 = 0;
+		_actor->_notifyThreadId2 = 0;
+	}
+	if (notifySequenceId2) {
+		Thread *talkThread = _vm->_scriptMan->_threads->findThread(notifySequenceId2);
+		talkThread->sendMessage(kMsgClearSequenceId2, 0);
+	}
+}
+
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 
 	stopActor();
@@ -710,6 +757,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	Actor *actor = newActor();
 
 	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
+	
 	control->_objectId = objectId;
 	control->_flags = actorType->_flags;
 	control->_priority = actorType->_priority;
@@ -718,11 +766,13 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	control->_actor = actor;
 	if (actorTypeId == 0x50001 && objectId == 0x40004)
 		actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Cursor>(_vm->_cursor, &Cursor::cursorControlRoutine));
+		
 	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
 		actor->createSurface(actorType->_surfInfo);
 	} else {
 		actor->_flags |= 0x0200;
 	}
+
 	actor->_position = placePt;
 	actor->_position2 = placePt;
 	Common::Point currPan = _vm->_camera->getCurrentPan();
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 7e3bc7f..50d3f7c 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -174,6 +174,8 @@ public:
 	void sequenceActor();
 	void setActorIndexTo1();
 	void setActorIndexTo2();
+	void startSubSequence(int linkIndex, uint32 sequenceId);
+	void stopSubSequence(int linkIndex);
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index affca78..058b093 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -140,7 +140,7 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
 	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
 		_priorityLayerIndex, _regionLayerIndex,_flags);
-
+		
 }
 
 // ActorResource
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 50a88d2..754b9d2 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -66,6 +66,7 @@ void BackgroundResourceLoader::unload(Resource *resource) {
 	delete backgroundItem->_bgRes;
 	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
 	// TODO _vm->setDefPointDimensions1();
+	debug("BackgroundResourceLoader::unload() Unloading background %08X OK", resource->_resId);
 }
 
 void BackgroundResourceLoader::buildFilename(Resource *resource) {
@@ -272,11 +273,12 @@ void BackgroundItem::initSurface() {
 }
 
 void BackgroundItem::freeSurface() {
-	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
-		_surfaces[i]->free();
-		delete _surfaces[i];
-		_surfaces[i] = 0;
-	}
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i)
+		if (_surfaces[i]) {
+			_surfaces[i]->free();
+			delete _surfaces[i];
+			_surfaces[i] = 0;
+		}
 }
 
 void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 4c5646f..5502616 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -28,9 +28,12 @@
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/scriptman.h"
 
 namespace Illusions {
 
+typedef Common::Functor2Mem<TriggerFunction*, uint32, void, BbdouInventory> InventoryTriggerFunctionCallback;
+
 // InventoryItem
 
 InventoryItem::InventoryItem(uint32 objectId, uint32 sequenceId)
@@ -71,7 +74,9 @@ bool InventoryBag::addInventoryItem(InventoryItem *inventoryItem, InventorySlot
 }
 
 void InventoryBag::removeInventoryItem(InventoryItem *inventoryItem) {
-	// TODO
+	for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it)
+		if ((*it)->_inventoryItem && (*it)->_inventoryItem->_objectId == inventoryItem->_objectId)
+			(*it)->_inventoryItem = 0;
 }
 
 void InventoryBag::buildItems() {
@@ -87,12 +92,34 @@ void InventoryBag::buildItems() {
 	}
 }
 
+InventorySlot *InventoryBag::getInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]->_objectId == objectId)
+			return _inventorySlots[i];
+	return 0;
+}
+
+InventorySlot *InventoryBag::findClosestSlot(Common::Point putPos, int index) {
+	uint minDistance = 0xFFFFFFFF;
+	InventorySlot *minDistanceSlot = 0;
+	for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it) {
+		InventorySlot *inventorySlot = *it;
+		Common::Point slotPos = _vm->getNamedPointPosition(inventorySlot->_namedPointId);
+		uint currDistance = (slotPos.y - putPos.y) * (slotPos.y - putPos.y) + (slotPos.x - putPos.x) * (slotPos.x - putPos.x);
+		if (currDistance < minDistance) {
+			minDistance = currDistance;
+			minDistanceSlot = inventorySlot;
+		}
+	}
+	return minDistanceSlot;
+}
+
 // BbdouInventory
 
 BbdouInventory::BbdouInventory(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
 	: _vm(vm), _bbdou(bbdou) {
 }
-				                                             
+
 void BbdouInventory::registerInventoryBag(uint32 sceneId) {
 	_inventoryBags.push_back(new InventoryBag(_vm, sceneId));
 	_activeBagSceneId = sceneId;
@@ -158,9 +185,9 @@ void BbdouInventory::open() {
 				inventorySlot->_objectId = _vm->_controls->newTempObjectId();
 				_vm->_controls->placeActor(0x00050012, slotPos, 0x0006005A, inventorySlot->_objectId, 0);
 			}
-			// TODO causeDeclare(0x1B0002, 0, inventorySlot->_objectId, Inventory_cause0x1B0002);
-			// TODO causeDeclare(0x1B0001, 0, inventorySlot->_objectId, Inventory_cause0x1B0001);
-			// TODO causeDeclare(0x1B0008, 0, inventorySlot->_objectId, Inventory_cause0x1B0001);
+			_vm->causeDeclare(0x1B0002, 0, inventorySlot->_objectId, new InventoryTriggerFunctionCallback(this, &BbdouInventory::cause0x1B0002));
+			_vm->causeDeclare(0x1B0001, 0, inventorySlot->_objectId, new InventoryTriggerFunctionCallback(this, &BbdouInventory::cause0x1B0001));
+			_vm->causeDeclare(0x1B0008, 0, inventorySlot->_objectId, new InventoryTriggerFunctionCallback(this, &BbdouInventory::cause0x1B0001));
 		}
 		refresh();
 	}
@@ -175,7 +202,7 @@ void BbdouInventory::close() {
 		InventorySlot *inventorySlot = *it;
 		Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
 		control->startSequenceActor(0x00060187, 2, 0);
-    }
+	}
     inventoryBag->_isActive = false;
     _activeInventorySceneId = 0;
 }
@@ -225,4 +252,88 @@ void BbdouInventory::buildItems(InventoryBag *inventoryBag) {
 	}
 }
 
+void BbdouInventory::cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId) {
+	// TODO
+	debug("cause0x1B0001");
+	uint32 foundSceneId, foundVerbId, foundObjectId2, foundObjectId;
+	bool found;
+	InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
+	InventorySlot *inventorySlot = inventoryBag->getInventorySlot(triggerFunction->_objectId);
+	uint32 objectId = inventorySlot->_inventoryItem->_objectId;
+
+	foundSceneId = _activeInventorySceneId;
+	foundVerbId = triggerFunction->_verbId;
+	foundObjectId2 = 0;
+
+	if (triggerFunction->_verbId == 0x1B0008) {
+		foundVerbId = 0x1B0003;
+		foundObjectId2 = _bbdou->_cursor->_data._holdingObjectId;
+	}
+
+	if (_vm->causeIsDeclared(_activeInventorySceneId, foundVerbId, foundObjectId2, objectId)) {
+		foundSceneId = _activeInventorySceneId;
+		foundObjectId = objectId;
+		found = true;
+	} else if (foundVerbId == 0x1B0003 && _vm->causeIsDeclared(_activeInventorySceneId, 0x1B0008, 0, objectId)) {
+		foundSceneId = _activeInventorySceneId;
+		foundVerbId = 0x1B0008;
+		foundObjectId2 = 0;
+		foundObjectId = objectId;
+		found = true;
+	} else if (_vm->causeIsDeclared(_activeInventorySceneId, foundVerbId, foundObjectId2, 0x40001)) {
+		foundSceneId = _activeInventorySceneId;
+		foundObjectId = 0x40001;
+		found = true;
+	} else if (_vm->causeIsDeclared(0x10003, foundVerbId, foundObjectId2, objectId)) {
+		foundSceneId = 0x10003;
+		foundObjectId = objectId;
+		found = true;
+	} else if (foundVerbId == 0x1B0003 && _vm->causeIsDeclared(0x10003, 0x1B0008, 0, objectId)) {
+		foundSceneId = 0x10003;
+		foundVerbId = 0x1B0008;
+		foundObjectId2 = 0;
+		foundObjectId = objectId;
+		found = true;
+	} else if (_vm->causeIsDeclared(0x10003, foundVerbId, foundObjectId2, 0x40001)) {
+		foundSceneId = 0x10003;
+		foundObjectId = 0x40001;
+		found = true;
+	}
+
+	if (found)
+		_vm->causeTrigger(foundSceneId, foundVerbId, foundObjectId2, foundObjectId, callingThreadId);
+	else
+		_vm->notifyThreadId(callingThreadId);
+
+}
+
+void BbdouInventory::cause0x1B0002(TriggerFunction *triggerFunction, uint32 callingThreadId) {
+	InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
+	InventorySlot *inventorySlot = inventoryBag->getInventorySlot(triggerFunction->_objectId);
+	uint32 objectId = inventorySlot->_inventoryItem->_objectId;
+	if (_vm->causeIsDeclared(_activeInventorySceneId, triggerFunction->_verbId, 0, objectId)) {
+		_vm->causeTrigger(_activeInventorySceneId, triggerFunction->_verbId, 0, objectId, callingThreadId);
+	} else {
+		_bbdou->startHoldingObjectId(0x4001A, objectId, 0);
+		_vm->notifyThreadId(callingThreadId);
+	}
+}
+
+void BbdouInventory::putBackInventoryItem(uint32 objectId, Common::Point cursorPosition) {
+	InventoryItem *inventoryItem = getInventoryItem(objectId);
+	bool flag = inventoryItem->_flag;
+	inventoryItem->_flag = false;
+	if (!flag && !inventoryItem->_assigned)
+		return;
+	for (uint i = 0; i < _inventoryBags.size(); ++i) {
+		if (_inventoryBags[i]->_sceneId == _activeInventorySceneId) {
+			InventorySlot *inventorySlot = _inventoryBags[i]->findClosestSlot(cursorPosition, _index);
+			_inventoryBags[i]->addInventoryItem(inventoryItem, inventorySlot);
+		} else {
+			debug("putBackInventoryItem OTHER STUFF TODO");
+		}
+	}
+	refresh();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index a983212..3c211e5 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -32,6 +32,7 @@ namespace Illusions {
 class IllusionsEngine;
 class BbdouSpecialCode;
 class Control;
+class TriggerFunction;
 
 struct InventoryItem {
 	uint32 _objectId;
@@ -57,6 +58,8 @@ public:
 	bool addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot);
 	void removeInventoryItem(InventoryItem *inventoryItem);
 	void buildItems();
+	InventorySlot *getInventorySlot(uint32 objectId);
+	InventorySlot *findClosestSlot(Common::Point putPos, int index);
 protected:
 public:
 	typedef Common::Array<InventorySlot*> InventorySlots;
@@ -82,6 +85,9 @@ public:
 	InventoryItem *getInventoryItem(uint32 objectId);
 	void refresh();
 	void buildItems(InventoryBag *inventoryBag);
+	void cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId);
+	void cause0x1B0002(TriggerFunction *triggerFunction, uint32 callingThreadId);
+	void putBackInventoryItem(uint32 objectId, Common::Point cursorPosition);
 protected:
 	typedef Common::Array<InventoryItem*> InventoryItems;
 	typedef InventoryItems::iterator InventoryItemsIterator;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index c74676d..1547aab 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -92,6 +92,8 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160008, spcEnableCursor);
 	SPECIAL(0x00160009, spcDisableCursor);
 	SPECIAL(0x0016000A, spcAddCursorSequence);
+	SPECIAL(0x0016000B, spcCursorStartHoldingObjectId);
+	SPECIAL(0x0016000C, spcCursorStopHoldingObjectId);
 	SPECIAL(0x00160013, spcInitBubble);
 	SPECIAL(0x00160014, spcSetupBubble);
 	SPECIAL(0x00160015, spcSetObjectInteractMode);
@@ -100,6 +102,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001B, spcRegisterInventoryItem);
 	SPECIAL(0x0016001C, spcOpenInventory);
 	SPECIAL(0x0016001D, spcAddInventoryItem);
+	SPECIAL(0x0016001E, spcRemoveInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
 }
 
@@ -148,6 +151,22 @@ void BbdouSpecialCode::spcAddCursorSequence(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._callerThreadId);
 }
 
+void BbdouSpecialCode::spcCursorStartHoldingObjectId(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	ARG_UINT32(holdingObjectId);
+	ARG_INT16(doPlaySound);
+	startHoldingObjectId(objectId, holdingObjectId, doPlaySound != 0);
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
+void BbdouSpecialCode::spcCursorStopHoldingObjectId(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	ARG_INT16(doPlaySound);
+	stopHoldingObjectId(objectId, doPlaySound != 0);
+	_cursor->_data._mode = 1;
+	_vm->notifyThreadId(opCall._callerThreadId);
+}
+
 void BbdouSpecialCode::spcInitBubble(OpCall &opCall) {
 	_bubble->init();
 	_vm->notifyThreadId(opCall._callerThreadId);
@@ -194,10 +213,14 @@ void BbdouSpecialCode::spcOpenInventory(OpCall &opCall) {
 
 void BbdouSpecialCode::spcAddInventoryItem(OpCall &opCall) {
 	ARG_UINT32(objectId);
-debug("### spcAddInventoryItem %08X", objectId);
 	_inventory->addInventoryItem(objectId);
 }
 
+void BbdouSpecialCode::spcRemoveInventoryItem(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	_inventory->removeInventoryItem(objectId);
+}
+
 void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 	_inventory->close();
 }
@@ -531,7 +554,7 @@ bool BbdouSpecialCode::getCause(uint32 sceneId, uint32 verbId, uint32 objectId2,
 	}
 
 	if (success) {
-		debug("getCause() -> %08X %08X %08X", outVerbId, outObjectId2, outObjectId);
+		//debug("getCause() -> %08X %08X %08X", outVerbId, outObjectId2, outObjectId);
 	}
 
 	return success;
@@ -539,7 +562,7 @@ bool BbdouSpecialCode::getCause(uint32 sceneId, uint32 verbId, uint32 objectId2,
 
 bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 	uint32 verbId, uint32 objectId2, uint32 objectId, int soundIndex) {
-	debug("runCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
+	//debug("runCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
 	uint32 sceneId = _vm->getCurrentScene();
 	uint32 outVerbId, outObjectId2, outObjectId;
 	bool success = false;
@@ -584,4 +607,35 @@ uint32 BbdouSpecialCode::startCauseThread(uint32 cursorObjectId, uint32 sceneId,
 	return tempThreadId;
 }
 
+void BbdouSpecialCode::startHoldingObjectId(uint32 objectId1, uint32 holdingObjectId, bool doPlaySound) {
+	Control *control = _vm->_dict->getObjectControl(objectId1);
+	if (_cursor->_data._holdingObjectId)
+		_inventory->putBackInventoryItem(_cursor->_data._holdingObjectId, control->_actor->_position);
+	_cursor->_data._holdingObjectId = holdingObjectId;
+	_cursor->_data._sequenceId = _cursor->findCursorSequenceId(holdingObjectId);
+	if (_cursor->_data._visibleCtr > 0)
+		_cursor->show(control);
+	_cursor->_data._mode = 2;
+	_cursor->_data._item10._verbId = 0x1B0003;
+	if (!doPlaySound)
+		playSoundEffect(5);
+	_inventory->removeInventoryItem(holdingObjectId);
+}
+
+void BbdouSpecialCode::stopHoldingObjectId(uint32 objectId1, bool doPlaySound) {
+	Control *control = _vm->_dict->getObjectControl(objectId1);
+	uint32 holdingObjectId = _cursor->_data._holdingObjectId;
+	_cursor->_data._holdingObjectId = 0;
+	_cursor->_data._sequenceId = 0x6000F;
+	if (!doPlaySound && holdingObjectId)
+		playSoundEffect(6);
+	if (_cursor->_data._visibleCtr > 0)
+		_cursor->show(control);
+	_cursor->_data._item10._verbId = 0x1B0001;
+	if (_cursor->_data._mode == 3)
+		holdingObjectId = _cursor->_data._holdingObjectId2;
+	if (holdingObjectId)
+		_inventory->putBackInventoryItem(holdingObjectId, control->_actor->_position);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index dcfb5ba..d018f38 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -81,6 +81,8 @@ public:
 	void spcEnableCursor(OpCall &opCall);
 	void spcDisableCursor(OpCall &opCall);
 	void spcAddCursorSequence(OpCall &opCall);
+	void spcCursorStartHoldingObjectId(OpCall &opCall);
+	void spcCursorStopHoldingObjectId(OpCall &opCall);
 	void spcInitBubble(OpCall &opCall);
 	void spcSetupBubble(OpCall &opCall);
 	void spcSetObjectInteractMode(OpCall &opCall);
@@ -89,10 +91,13 @@ public:
 	void spcRegisterInventoryItem(OpCall &opCall);
 	void spcOpenInventory(OpCall &opCall);
 	void spcAddInventoryItem(OpCall &opCall);
+	void spcRemoveInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
 
 	void playSoundEffect(int soundIndex);
 	void resetItem10(uint32 objectId, Item10 *item10);
+	void startHoldingObjectId(uint32 objectId1, uint32 holdingObjectId, bool doPlaySound);
+	void stopHoldingObjectId(uint32 objectId1, bool doPlaySound);
 
 protected:
 	// Internal functions
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index 6bdc539..11b05a6 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -34,25 +34,49 @@ class TalkEntry;
 
 template<class T>
 class DictionaryHashMap {
+protected:
+	typedef Common::List<T*> List;
+	typedef typename List::iterator ListIterator;
+	typedef Common::HashMap<uint32, List*> Map;
+	typedef typename Map::iterator MapIterator;
+	Map _map;
 public:
 
+	~DictionaryHashMap() {
+		for (MapIterator it = _map.begin(); it != _map.end(); ++it)
+			delete it->_value;
+	}
+
 	void add(uint32 id, T *value) {
-		_map[id] = value;
+		MapIterator it = _map.find(id);
+		List *list;
+		if (it != _map.end())
+			list = it->_value;
+		else {
+			 list = new List();
+			 _map[id] = list;
+		}
+		list->push_back(value);
 	}
 
 	void remove(uint32 id) {
-		_map.erase(id);
+		MapIterator it = _map.find(id);
+		List *list;
+		if (it != _map.end()) {
+			list = it->_value;
+			list->pop_back();
+			if (list->empty())
+				_map.erase(id);
+		}
 	}
 
 	T *find(uint32 id) {
-		typename Common::HashMap<uint32, T*>::iterator it = _map.find(id);
+		MapIterator it = _map.find(id);
 		if (it != _map.end())
-			return it->_value;
+			return it->_value->back();
 		return 0;
 	}
 
-protected:
-	Common::HashMap<uint32, T*> _map;
 };
 
 class Dictionary {
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 968dc0f..99b7b19 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -107,6 +107,7 @@ Common::Error IllusionsEngine::run() {
 	_controls = new Controls(this);
 	_cursor = new Cursor(this);
 	_talkItems = new TalkItems(this);
+	_triggerFunctions = new TriggerFunctions();
 	
 	// TODO Move to own class
 	_resGetCtr = 0;
@@ -117,7 +118,9 @@ Common::Error IllusionsEngine::run() {
 	// Actor/graphics/script test
 
 	/* TODO 0x0010000B LinkIndex 0x00060AAB 0x00060556
-	*/	
+	*/
+	
+	_scriptMan->_globalSceneId = 0x00010003;	
 	
 	_resSys->loadResource(0x000D0001, 0, 0);
 
@@ -136,6 +139,7 @@ Common::Error IllusionsEngine::run() {
 	}
 #endif
 
+	delete _triggerFunctions;
 	delete _talkItems;
 	delete _cursor;
 	delete _controls;
@@ -276,7 +280,7 @@ int IllusionsEngine::updateGraphics() {
 				uint32 priority = control->getPriority();
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
 					actor->_surfInfo._dimensions, drawPosition, control->_position,
-					priority, actor->_scale, actor->_spriteFlags);
+					priority, 100/*actor->_scale TODO DEBUG*/, actor->_spriteFlags);
 			}
 		}
 	}
@@ -298,19 +302,26 @@ int IllusionsEngine::getRandom(int max) {
 
 bool IllusionsEngine::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
 	uint32 codeOffs;
-	// TODO Also search for native trigger functions later (findCauseFunc)
-	bool r = _scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
+	bool r =
+		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
+		_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
 	debug(3, "causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
 		sceneId, verbId, objectId2, objectId, r);
 	return r;
 }
 
+void IllusionsEngine::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
+}
+
 uint32 IllusionsEngine::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
 	uint32 codeOffs;
 	uint32 causeThreadId = 0;
-	// TODO Also search for native trigger functions later and run it (findCauseFunc)
-	if (_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
-		debug("Run cause at %08X", codeOffs);
+	TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
+	if (triggerFunction) {
+		triggerFunction->run(callingThreadId);
+	} else if (_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
+		//debug("Run cause at %08X", codeOffs);
 		causeThreadId = _scriptMan->startTempScriptThread(_scriptMan->_scriptResource->getCode(codeOffs),
 			callingThreadId, verbId, objectId2, objectId);
 	}
@@ -329,7 +340,7 @@ Common::Point IllusionsEngine::getNamedPointPosition(uint32 namedPointId) {
 		_controls->findNamedPoint(namedPointId, pt))
     	return pt;
 	// TODO
-	debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+	//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
 	return Common::Point(0, 0);
 }
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 58dc519..cf3549e 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -59,6 +59,7 @@ class Control;
 class Controls;
 class Cursor;
 class Dictionary;
+class FramesList;
 class Input;
 class Screen;
 class ScriptResource;
@@ -66,7 +67,10 @@ class ScriptMan;
 class Sequence;
 class SpecialCode;
 class TalkItems;
-class FramesList;
+class TriggerFunctions;
+class TriggerFunction;
+
+typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
 
 class IllusionsEngine : public Engine {
 protected:
@@ -97,6 +101,7 @@ public:
 	Cursor *_cursor;
 	TalkItems *_talkItems;
 	SpecialCode *_specialCode;
+	TriggerFunctions *_triggerFunctions;
 	
 	int _resGetCtr;
 	uint32 _resGetTime;
@@ -115,6 +120,7 @@ public:
 
 	// TODO Move to ScriptMan?
 	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
 	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
 
 	int convertPanXCoord(int16 x);
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 961ffd4..2074943 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -61,15 +61,15 @@ void ActiveScenes::unpauseActiveScene() {
 	--_stack.top()._pauseCtr;
 }
 
-int ActiveScenes::getActiveScenesCount() {
+uint ActiveScenes::getActiveScenesCount() {
 	return _stack.size();
 }
 
 void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
 	if (sceneId)
-		*sceneId = _stack[index]._sceneId;
+		*sceneId = _stack[index - 1]._sceneId;
 	if (pauseCtr)
-		*pauseCtr = _stack[index]._pauseCtr;
+		*pauseCtr = _stack[index - 1]._pauseCtr;
 }
 
 uint32 ActiveScenes::getCurrentScene() {
@@ -85,6 +85,60 @@ bool ActiveScenes::isSceneActive(uint32 sceneId) {
 	return false;
 }
 
+// TriggerFunction
+
+TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
+	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
+}
+
+TriggerFunction::~TriggerFunction() {
+	delete _callback;
+}
+
+void TriggerFunction::run(uint32 callingThreadId) {
+	(*_callback)(this, callingThreadId);
+}
+
+// TriggerFunctions
+
+void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end()) {
+		delete *it;
+		_triggerFunctions.erase(it);
+	}
+	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
+}
+
+TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end())
+		return (*it);
+	return 0;
+}
+
+void TriggerFunctions::removeBySceneId(uint32 sceneId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	while (it != _triggerFunctions.end()) {
+		if ((*it)->_sceneId == sceneId) {
+			delete *it;
+			it = _triggerFunctions.erase(it);
+		} else
+			++it;
+	}
+}
+
+TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	for (; it != _triggerFunctions.end(); ++it) {
+		TriggerFunction *triggerFunction = *it;
+		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
+			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
+			break;
+	}
+	return it;		
+}
+
 // ScriptStack
 
 ScriptStack::ScriptStack() {
@@ -240,7 +294,7 @@ void ScriptMan::reset() {
 bool ScriptMan::enterScene(uint32 sceneId, uint32 threadId) {
 	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
 	if (!progInfo) {
-		// TODO dumpActiveScenes(_someSceneId2, threadId);
+		// TODO dumpActiveScenes(_globalSceneId, threadId);
 		sceneId = _theSceneId;
 	}
 	_activeScenes.push(sceneId);
@@ -253,7 +307,7 @@ void ScriptMan::exitScene(uint32 threadId) {
 	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
 	_threads->terminateThreadsByTag(sceneId, threadId);
 	_vm->_controls->destroyControlsByTag(sceneId);
-	// TODO causeFunc_removeBySceneId(sceneId);
+	_vm->_triggerFunctions->removeBySceneId(sceneId);
 	_vm->_resSys->unloadResourcesByTag(sceneId);
 	_activeScenes.pop();
 }
@@ -278,6 +332,19 @@ void ScriptMan::leavePause(uint32 threadId) {
 	_activeScenes.unpauseActiveScene();
 }
 
+void ScriptMan::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
+	uint activeScenesCount = _activeScenes.getActiveScenesCount();
+	while (activeScenesCount > 0) {
+		uint32 activeSceneId;
+		_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, 0);
+		if (activeSceneId == sceneId)
+			break;
+		exitScene(threadId);
+		--activeScenesCount;
+	}
+	_vm->_camera->clearCameraModeStack();
+}
+
 void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
 	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId, notifyFlags,
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index adfc365..6ea28c8 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -23,6 +23,7 @@
 #ifndef ILLUSIONS_SCRIPTMAN_H
 #define ILLUSIONS_SCRIPTMAN_H
 
+#include "illusions/illusions.h"
 #include "illusions/scriptresource.h"
 #include "illusions/thread.h"
 #include "common/algorithm.h"
@@ -46,7 +47,7 @@ public:
 	void pop();
 	void pauseActiveScene();
 	void unpauseActiveScene();
-	int getActiveScenesCount();
+	uint getActiveScenesCount();
 	void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
 	uint32 getCurrentScene();
 	bool isSceneActive(uint32 sceneId);
@@ -54,6 +55,31 @@ protected:
 	Common::FixedStack<ActiveScene, 16> _stack;
 };
 
+struct TriggerFunction;
+
+struct TriggerFunction {
+	uint32 _sceneId;
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _objectId;
+	TriggerFunctionCallback *_callback;
+	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	~TriggerFunction();
+	void run(uint32 callingThreadId);
+};
+
+class TriggerFunctions {
+public:
+	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void removeBySceneId(uint32 sceneId);
+public:
+	typedef Common::List<TriggerFunction*> Items;
+	typedef Items::iterator ItemsIterator;
+	Items _triggerFunctions;
+	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+};
+
 class ScriptStack {
 public:
 	ScriptStack();
@@ -92,6 +118,7 @@ public:
 	void exitScene(uint32 threadId);
 	void enterPause(uint32 threadId);
 	void leavePause(uint32 threadId);
+	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
 public:
 
 	IllusionsEngine *_vm;
@@ -104,6 +131,7 @@ public:
 	
 	uint32 _theSceneId;
 	uint32 _theThreadId;
+	uint32 _globalSceneId;
 	bool _doScriptThreadInit;
 	uint32 _nextTempThreadId;
 	
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index fbb9448..77a7d1f 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -102,7 +102,9 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
 	OPCODE(32, opPanCenterObject);
+	OPCODE(34, opPanToObject);
 	OPCODE(35, opPanToNamedPoint);
+	OPCODE(36, opPanToPoint);
 	OPCODE(37, opPanStop);
 	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
@@ -137,6 +139,9 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	OPCODE(103, opJumpIf);
+	OPCODE(104, opIsPrevSceneId);
+	OPCODE(105, opIsCurrentSceneId);
+	OPCODE(106, opIsActiveSceneId);
 	OPCODE(107, opNot);
 	OPCODE(108, opAnd);
 	OPCODE(109, opOr);
@@ -156,6 +161,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(176, opStackPush0);
 	OPCODE(177, opSetFontId);
 	OPCODE(178, opAddMenuKey);
+	OPCODE(179, opChangeSceneAll);
 }
 
 #undef OPCODE
@@ -248,17 +254,30 @@ void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 	uint scenesCount = _vm->_scriptMan->_activeScenes.getActiveScenesCount();
 	if (scenesCount > 0) {
 		uint32 currSceneId;
-		_vm->_scriptMan->_activeScenes.getActiveSceneInfo(scenesCount - 1, &currSceneId, 0);
+		_vm->_scriptMan->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
 		// TODO krnfileDump(currSceneId);
 	}
 	if (!_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId))
 		opCall._result = kTSTerminate;
 }
 
+//DEBUG Scenes
+uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
+//uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
+//uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
+//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+
 void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
+	
+	if (dsceneId) {
+		sceneId = dsceneId;
+		threadId = dthreadId;
+		dsceneId = 0;
+	}
+	
 	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardButtons(0xFFFF);
 	_vm->_scriptMan->_prevSceneId = _vm->getCurrentScene();
@@ -312,6 +331,14 @@ void ScriptOpcodes::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall
 	_vm->_camera->panCenterObject(objectId, speed);
 }
 
+void ScriptOpcodes::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	_vm->_camera->panToPoint(pos, speed, opCall._callerThreadId);
+}
+
 void ScriptOpcodes::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);	
 	ARG_UINT32(namedPointId);
@@ -319,6 +346,13 @@ void ScriptOpcodes::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall
 	_vm->_camera->panToPoint(pos, speed, opCall._callerThreadId);
 }
 
+void ScriptOpcodes::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_INT16(x);	
+	ARG_INT16(y);	
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._callerThreadId);
+}
+
 void ScriptOpcodes::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_camera->stopPan();
 }
@@ -586,6 +620,21 @@ void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 		opCall._deltaOfs += jumpOffs;
 }
 
+void ScriptOpcodes::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_UINT32(sceneId);
+	_vm->_scriptMan->_stack.push(_vm->_scriptMan->_prevSceneId == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_UINT32(sceneId);
+	_vm->_scriptMan->_stack.push(_vm->getCurrentScene() == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_UINT32(sceneId);
+	_vm->_scriptMan->_stack.push(_vm->_scriptMan->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
+}
+
 void ScriptOpcodes::opNot(ScriptThread *scriptThread, OpCall &opCall) {
 	int16 value = _vm->_scriptMan->_stack.pop();
 	_vm->_scriptMan->_stack.push(value != 0 ? 0 : 1);
@@ -731,4 +780,18 @@ void ScriptOpcodes::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
 	// TODO _vm->addMenuKey(key, threadId);
 }
 
+void ScriptOpcodes::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_scriptMan->_prevSceneId = _vm->getCurrentScene();
+	_vm->_scriptMan->dumpActiveScenes(_vm->_scriptMan->_globalSceneId, opCall._callerThreadId);
+	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_scriptMan->_prevSceneId, sceneId, threadId);
+	_vm->_scriptMan->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 507d21b..32f23fd 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -77,7 +77,9 @@ protected:
 	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
@@ -112,6 +114,9 @@ protected:
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opNot(ScriptThread *scriptThread, OpCall &opCall);
 	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
 	void opOr(ScriptThread *scriptThread, OpCall &opCall);
@@ -131,6 +136,7 @@ protected:
 	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
 	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
 	
 };
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 7df968d..23d1781 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -70,9 +70,12 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(15, opSwitchFacing);
 	OPCODE(16, opAppearActor);
 	OPCODE(17, opDisappearActor);
+	OPCODE(18, opAppearForeignActor);
+	OPCODE(19, opDisappearForeignActor);
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
 	OPCODE(33, opSetPathWalkPoints);
+	OPCODE(35, opSetScale);
 	OPCODE(36, opSetScaleLayer);
 	OPCODE(38, opSetPathWalkRects);
 	OPCODE(39, opSetPriority);
@@ -80,6 +83,8 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(50, opPlaySound);
 	OPCODE(51, opStopSound);
 	OPCODE(53, opPlaceSubActor);
+	OPCODE(54, opStartSubSequence);
+	OPCODE(55, opStopSubSequence);
 }
 
 #undef OPCODE
@@ -219,6 +224,23 @@ void SequenceOpcodes::opDisappearActor(Control *control, OpCall &opCall) {
 	control->_actor->_newFrameIndex = 0;
 }
 
+void SequenceOpcodes::opAppearForeignActor(Control *control, OpCall &opCall) {
+	ARG_INT16(foreignObjectNum);
+	Control *foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
+	if (!foreignControl) {
+		Common::Point pos = _vm->getNamedPointPosition(0x00070023);
+		_vm->_controls->placeActor(0x00050001, pos, 0x00060001, foreignObjectNum | 0x40000, 0);
+		foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
+	}
+	foreignControl->appearActor();
+}
+
+void SequenceOpcodes::opDisappearForeignActor(Control *control, OpCall &opCall) {
+	ARG_INT16(foreignObjectNum);
+	Control *foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
+	foreignControl->disappearActor();
+}
+
 void SequenceOpcodes::opNotifyThreadId1(Control *control, OpCall &opCall) {
 	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
 }
@@ -235,6 +257,12 @@ void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	// TODO control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
 }
 
+void SequenceOpcodes::opSetScale(Control *control, OpCall &opCall) {
+	ARG_INT16(scale);
+	control->_actor->_flags &= ~4;
+	control->setActorScale(scale);
+}
+
 void SequenceOpcodes::opSetScaleLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(scaleLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
@@ -290,4 +318,15 @@ void SequenceOpcodes::opPlaceSubActor(Control *control, OpCall &opCall) {
  	_vm->_controls->placeSubActor(control->_objectId, linkIndex, actorTypeId, sequenceId);
 }
 
+void SequenceOpcodes::opStartSubSequence(Control *control, OpCall &opCall) {
+ 	ARG_INT16(linkIndex);
+ 	ARG_UINT32(sequenceId);
+	control->startSubSequence(linkIndex, sequenceId);
+}
+
+void SequenceOpcodes::opStopSubSequence(Control *control, OpCall &opCall) {
+ 	ARG_INT16(linkIndex);
+	control->stopSubSequence(linkIndex);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index abf1987..028d567 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -60,16 +60,21 @@ protected:
 	void opSwitchFacing(Control *control, OpCall &opCall);
 	void opAppearActor(Control *control, OpCall &opCall);
 	void opDisappearActor(Control *control, OpCall &opCall);
+	void opAppearForeignActor(Control *control, OpCall &opCall);
+	void opDisappearForeignActor(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
 	void opSetPathWalkPoints(Control *control, OpCall &opCall);
+	void opSetScale(Control *control, OpCall &opCall);
 	void opSetScaleLayer(Control *control, OpCall &opCall);
 	void opSetPathWalkRects(Control *control, OpCall &opCall);
 	void opSetPriority(Control *control, OpCall &opCall);
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
 	void opPlaySound(Control *control, OpCall &opCall);
 	void opStopSound(Control *control, OpCall &opCall);
-	void opPlaceSubActor(Control *control, OpCall &opCall);	
+	void opPlaceSubActor(Control *control, OpCall &opCall);
+	void opStartSubSequence(Control *control, OpCall &opCall);
+	void opStopSubSequence(Control *control, OpCall &opCall);	
 	
 };
 
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index 1d17408..0c1108e 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -78,7 +78,7 @@ void TalkEntry::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_tblPtr = dataStart + tblOffs;
 	_voiceName = dataStart + voiceNameOffs;
 	
-	debug("TalkEntry::load() _talkId: %08X; textOffs: %08X; tblOffs: %08X; voiceNameOffs: %08X",
+	debug(0, "TalkEntry::load() _talkId: %08X; textOffs: %08X; tblOffs: %08X; voiceNameOffs: %08X",
 		_talkId, textOffs, tblOffs, voiceNameOffs);
 }
 


Commit: e0e4d2ffa92fccdef1ea3eae0228ec812c7dbac5
    https://github.com/scummvm/scummvm/commit/e0e4d2ffa92fccdef1ea3eae0228ec812c7dbac5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement sprite scaling

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/screen.cpp


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 99b7b19..4748634 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -280,7 +280,7 @@ int IllusionsEngine::updateGraphics() {
 				uint32 priority = control->getPriority();
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
 					actor->_surfInfo._dimensions, drawPosition, control->_position,
-					priority, 100/*actor->_scale TODO DEBUG*/, actor->_spriteFlags);
+					priority, actor->_scale, actor->_spriteFlags);
 			}
 		}
 	}
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 694382d..d8e6a12 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -111,10 +111,68 @@ void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	//debug("Screen::drawSurface20");
 }
 
+static uint16 average(const uint16 a, const uint16 b) {
+	byte r1, g1, b1, r2, g2, b2;
+	g_system->getScreenFormat().colorToRGB(a, r1, g1, b1);
+	g_system->getScreenFormat().colorToRGB(b, r2, g2, b2);
+	return g_system->getScreenFormat().RGBToColor((r1 + r1 + r2) / 3, (g1 + g1 + g2) / 3, (b1 + b1 + b2) / 3);
+}
+
 void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
 	// Scaled
-	// TODO
-	//debug("Screen::drawSurface21");
+	const int dstWidth = dstRect.width(), dstHeight = dstRect.height();
+	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
+	const int errYStart = srcHeight / dstHeight;
+	const int errYIncr = srcHeight % dstHeight;
+	const int midY = dstHeight / 2;
+	const int errXStart = srcWidth / dstWidth;
+	const int errXIncr = srcWidth % dstWidth;
+	const int midX = dstWidth / 2;
+	int h = dstHeight, errY = 0, skipY, srcY = srcRect.top;
+	byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top);
+	skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1;
+	h -= skipY;
+	while (h-- > 0) {
+		int w = dstWidth, errX = 0, skipX;
+		skipX = (dstWidth < srcWidth) ? 0 : dstWidth / (2*srcWidth) + 1;
+		w -= skipX;
+		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcY);
+		byte *dstRow = dst; 
+		while (w-- > 0) {
+			uint16 pixel = READ_LE_UINT16(src);
+			if (pixel != _colorKey1) {
+				if (errX >= midX) {
+					uint16 npixel = READ_LE_UINT16(src + 2);
+					if (npixel == _colorKey1)
+						npixel = READ_LE_UINT16(dstRow);
+					pixel = average(pixel, npixel);
+				}
+				WRITE_LE_UINT16(dstRow, pixel);
+			}
+			dstRow += 2;
+			src += 2 * errXStart;
+			errX += errXIncr;
+			if (errX >= dstWidth) {
+				errX -= dstWidth;
+				src += 2;
+			}
+		}
+		while (skipX-- > 0) {
+			uint16 pixel = READ_LE_UINT16(src);
+			if (pixel != _colorKey1)
+				WRITE_LE_UINT16(dstRow, pixel);
+			src += 2;
+			dstRow += 2;
+		}
+		dst += _backSurface->pitch;
+		srcY += errYStart;
+		errY += errYIncr;
+		if (errY >= dstHeight) {
+			errY -= dstHeight;
+			++srcY;
+		}
+	}
+
 }
 
 } // End of namespace Illusions


Commit: f2c48e3ae0e5775da6f3dcac841414cf8b5379ef
    https://github.com/scummvm/scummvm/commit/f2c48e3ae0e5775da6f3dcac841414cf8b5379ef
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement pathwalking (actual pathfinding todo)

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/fixedpoint.cpp
    engines/illusions/fixedpoint.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index d9eb949..2973a92 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -25,6 +25,7 @@
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
 #include "illusions/dictionary.h"
+#include "illusions/fixedpoint.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
 #include "illusions/scriptman.h"
@@ -94,29 +95,29 @@ Actor::Actor(IllusionsEngine *vm)
 	
 	_notifyId3C = 0;
 
-	_pathCtrY = 0;
-	
 	_controlRoutine = 0;
 	setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(_vm->_controls, &Controls::actorControlRoutine));
 
-#if 0 // TODO
-	_field2 = 0;
-	_path40 = 0;
-	_path4C = 0;
-	_pathFlag50 = 0;
+	_walkCallerThreadId1 = 0;
+	_pathAngle = 0;
+	_pathFlag50 = false;
 	_pathCtrX = 0;
-	_pathInitialPosFlag = 1;
+	_pathCtrY = 0;
+	_pathInitialPosFlag = true;
 	_pathInitialPos.x = 0;
 	_pathInitialPos.y = 0;
+	_pathPoints = 0;
+	_pathPointIndex = 0;
+	_pathPointsCount = 0;
+	_pathNode = 0;
+
+#if 0 // TODO
+	_field2 = 0;
 	_namedPointsCount = 0;
 	_namedPoints = 0;
 	_field164 = 0;
 	_pathWalkRects = 0;
 	_pathWalkPoints = 0;
-	_pathNode = 0;
-	_pathPoints = 0;
-	_pathPointIndex = 0;
-	_pathPointsCount = 0;
 	_regionLayer = 0;
 	_transitionRegionId = 0;
 	_field18C = 0;
@@ -342,7 +343,7 @@ void Control::setActorScale(int scale) {
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
-			subControl->activateObject();
+			subControl->setActorScale(scale);
 		}
 }
 
@@ -528,19 +529,17 @@ void Control::setActorFrameIndex(int16 frameIndex) {
 
 void Control::stopActor() {
 	_actor->_seqCodeIp = 0;
-	/* TODO
 	if (_actor->_pathNode) {
 		if (_actor->_flags & 0x0400) {
-			// TODO delete _actor->_pathNode;
+			delete _actor->_pathNode;
 			_actor->_flags &= ~0x0400;
 		}
 		_actor->_pathNode = 0;
 		_actor->_pathPoints = 0;
 		_actor->_pathPointsCount = 0;
 		_actor->_pathPointIndex = 0;
-		_actor->_path40 = 0;
+		_actor->_walkCallerThreadId1 = 0;
 	}
-	*/
 	_vm->notifyThreadId(_actor->_notifyThreadId1);
 	_vm->notifyThreadId(_actor->_notifyId3C);
 }
@@ -570,16 +569,14 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 	if (_actor->_linkIndex2) {
 		Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[_actor->_linkIndex2 - 1]);
 		if (subControl->_actor->_flags & 1) {
-			/* TODO
-			if (control->_actor->pathNode) {
+			if (subControl->_actor->_pathNode) {
 				doSeq = false;
-				subControl->_actor->notifyThreadId2 = threadId;
-				subControl->_actor->entryTblPtr = entryTblPtr;
-				subControl->_actor->flags |= 0x80;
-				Thread *thread = _vm->_threads->findThread(threadId);
+				subControl->_actor->_notifyThreadId2 = threadId;
+				subControl->_actor->_entryTblPtr = entryTblPtr;
+				subControl->_actor->_flags |= 0x80;
+				Thread *thread = _vm->_scriptMan->_threads->findThread(threadId);
 				thread->sendMessage(kMsgClearSequenceId2, 0);
 			}
-			*/
 		}
 	}
 	if (doSeq)
@@ -653,7 +650,7 @@ void Control::startSubSequence(int linkIndex, uint32 sequenceId) {
 	linkedActor->_sequenceId = sequenceId;
 	linkedActor->_notifyThreadId1 = 0;
 	linkedActor->_notifyId3C = 0;
-	linkedActor->_path40 = 0;
+	linkedActor->_walkCallerThreadId1 = 0;
 
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 	linkedActor->_seqCodeIp = sequence->_sequenceCode;
@@ -688,6 +685,213 @@ void Control::stopSubSequence(int linkIndex) {
 	}
 }
 
+void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 callerThreadId1, uint32 callerThreadId2) {
+	PointArray *pathNode;
+	ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
+
+	_actor->_pathAngle = 0;
+	_actor->_pathFlag50 = false;
+	_actor->_seqCodeValue3 = 0;
+	_actor->_seqCodeValue1 = 0;
+	// TODO _actor->_field_BC = _actor->_position.x;
+	// TODO _actor->_field_BE = _actor->_position.x;
+	_actor->_pathInitialPosFlag = true;
+	// TODO _actor->_field_C0 = destPt.x;
+	// TODO _actor->_field_C2 = destPt.y;
+
+	/* TODO	
+	uint newFacing;
+	if (calcPointDirection(_actor->_position, destPt, newFacing))
+		faceActor(newFacing);
+	*/
+
+	if (actorType->_value1E)
+		_actor->_pathCtrY = actorType->_value1E;
+	else
+		_actor->_pathCtrY = 140;
+
+	pathNode = createPath(destPt);
+
+	if (pathNode->size() == 1 &&
+		_actor->_position.x == (*pathNode)[0].x &&
+		_actor->_position.y == (*pathNode)[0].y) {
+		delete pathNode;
+		_vm->notifyThreadId(callerThreadId2);
+	} else {
+		_actor->_posXShl = _actor->_position.x << 16;
+		_actor->_posYShl = _actor->_position.y << 16;
+		startSequenceActor(sequenceId, 1, 0);
+		_actor->_pathNode = pathNode;
+		_actor->_pathPointsCount = pathNode->size();
+		_actor->_pathPoints = pathNode->size();
+		_actor->_flags |= 0x0400;
+		_actor->_walkCallerThreadId1 = callerThreadId1;
+		_vm->notifyThreadId(_actor->_notifyId3C);
+		_actor->_notifyId3C = callerThreadId2;
+		_actor->_pathPointIndex = 0;
+		_vm->_input->discardButtons(0x10);
+	}
+
+}
+
+PointArray *Control::createPath(Common::Point destPt) {
+	// TODO Implement actual pathfinding
+	PointArray *pathNode = new PointArray();
+	pathNode->push_back(destPt);
+	return pathNode;
+}
+
+void Control::updateActorMovement(uint32 deltaTime) {
+	// TODO This needs some cleanup
+
+	static const int16 kAngleTbl[] = {60, 0, 120, 0, 60, 0, 120, 0};
+
+	while (1) {
+
+	bool again = false;
+
+	/* TODO
+	if (controla->objectId == GameScript_getField0() && again == const0 && Input_pollButton__(0x10u)) {
+		again = 1;
+		Control_disappearActor__(controla);
+		HIBYTE(_actor->_flags) |= 0x80u;
+		_actor->_seqCodeIp = 0;
+		deltaTime = 2;
+	}
+	*/
+
+	Common::Point prevPt;
+	if (_actor->_pathPointIndex == 0) {
+		if (_actor->_pathInitialPosFlag) {
+			_actor->_pathCtrX = 0;
+			_actor->_pathInitialPos = _actor->_position;
+			_actor->_pathInitialPosFlag = false;
+		}
+		prevPt = _actor->_pathInitialPos;
+	} else {
+		prevPt = (*_actor->_pathNode)[_actor->_pathPointIndex - 1];
+	}
+
+	Common::Point currPt = (*_actor->_pathNode)[_actor->_pathPointIndex];
+
+	int16 deltaX = currPt.x - prevPt.x;
+	int16 deltaY = currPt.y - prevPt.y;
+
+	if (!_actor->_pathFlag50) {
+
+		// TODO Move to own function
+		FP16 angle;
+		if (currPt.x == prevPt.x) {
+			if (prevPt.y >= currPt.y)
+				angle = fixedMul(-0x5A0000, 0x478);
+			else
+				angle = fixedMul(0x5A0000, 0x478);
+		} else {
+			angle = fixedAtan(fixedDiv(deltaY << 16, deltaX << 16));
+		}
+		_actor->_pathAngle = angle;
+
+		// TODO Move to own function
+		int16 v13 = (fixedTrunc(fixedMul(angle, 0x394BB8)) + 360) % 360;
+		if (deltaX >= 0)
+			v13 += 180;
+		v13 = (v13 + 90) % 360;
+		int16 v15 = kAngleTbl[0] / -2;
+		uint newFacing = 1;
+		for (uint i = 0; i < 8; ++i) {
+			v15 += kAngleTbl[i];
+			if (v13 < v15) {
+				newFacing = 1 << i;
+				break;
+			}
+		}
+		if (newFacing != _actor->_facing) {
+			refreshSequenceCode();
+			faceActor(newFacing);
+		}
+
+		_actor->_pathFlag50 = true;
+
+	}
+
+	FP16 deltaX24, deltaY24;
+
+	if (_actor->_flags & 0x0400) {
+
+		FP16 v20 = fixedMul((deltaTime + _actor->_pathCtrX) << 16, _actor->_pathCtrY << 16);
+		FP16 v21 = fixedDiv(v20, 100 << 16);
+		FP16 v22 = fixedMul(v21, _actor->_scale << 16);
+		FP16 v23 = fixedDiv(v22, 100 << 16);
+		_actor->_seqCodeValue1 = 100 * _actor->_pathCtrY * deltaTime / 100;
+		if (v23) {
+			FP16 prevDistance = fixedDistance(prevPt.x << 16, prevPt.y << 16, _actor->_posXShl, _actor->_posYShl);
+			FP16 distance = prevDistance + v23;
+			if (prevPt.x > currPt.x)
+				distance = -distance;
+			deltaX24 = fixedMul(fixedCos(_actor->_pathAngle), distance);
+			deltaY24 = fixedMul(fixedSin(_actor->_pathAngle), distance);
+		} else {
+			deltaX24 = _actor->_posXShl - (prevPt.x << 16);
+			deltaY24 = _actor->_posYShl - (prevPt.y << 16);
+		}
+	} else {
+		if (100 * (int)deltaTime <= _actor->_seqCodeValue2)
+			break;
+		deltaX24 = deltaX << 16;
+		deltaY24 = deltaY << 16;
+	}
+
+	if (ABS(deltaX24) < ABS(deltaX << 16) ||
+		ABS(deltaY24) < ABS(deltaY << 16)) {
+		FP16 newX = (prevPt.x << 16) + deltaX24;
+		FP16 newY = (prevPt.y << 16) + deltaY24;
+		if (newX == _actor->_posXShl &&	newY == _actor->_posYShl) {
+			_actor->_pathCtrX += deltaTime;
+		} else {
+			_actor->_pathCtrX = 0;
+			_actor->_posXShl = newX;
+			_actor->_posYShl = newY;
+			_actor->_position.x = fixedTrunc(_actor->_posXShl);
+			_actor->_position.y = fixedTrunc(_actor->_posYShl);
+		}
+	} else {
+		_actor->_position = currPt;
+		_actor->_posXShl = _actor->_position.x << 16;
+		_actor->_posYShl = _actor->_position.y << 16;
+		--_actor->_pathPointsCount;
+		++_actor->_pathPointIndex;
+		++_actor->_pathPoints;
+		_actor->_pathInitialPosFlag = true;
+		if (_actor->_pathPointsCount == 0) {
+			if (_actor->_flags & 0x0400) {
+				delete _actor->_pathNode;
+				_actor->_flags &= ~0x0400;
+			}
+			_actor->_pathNode = 0;
+			_actor->_pathPoints = 0;
+			_actor->_pathPointsCount = 0;
+			_actor->_pathPointIndex = 0;
+			if (_actor->_notifyId3C) {
+				_vm->notifyThreadId(_actor->_notifyId3C);
+				_actor->_walkCallerThreadId1 = 0;
+			}
+			again = false;
+		}
+		_actor->_pathFlag50 = false;
+	}
+
+	if (!again)
+		break;
+
+	}
+
+}
+
+void Control::refreshSequenceCode() {
+	Sequence *sequence = _vm->_dict->findSequence(_actor->_sequenceId);
+	_actor->_seqCodeIp = sequence->_sequenceCode;
+}
+
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 
 	stopActor();
@@ -701,7 +905,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_sequenceId = sequenceId;
 	_actor->_notifyThreadId1 = notifyThreadId;
 	_actor->_notifyId3C = 0;
-	_actor->_path40 = 0;
+	_actor->_walkCallerThreadId1 = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 
@@ -963,8 +1167,8 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 	if (actor->_pauseCtr > 0)
 		return;
 
-	if (false/*actor->_pathNode*/) {
-		// TODO Update pathwalking
+	if (control->_actor->_pathNode) {
+		control->updateActorMovement(deltaTime);
 	} else {
 		actor->_seqCodeValue1 = 100 * deltaTime;
 	}
@@ -1014,10 +1218,8 @@ void Controls::destroyControl(Control *control) {
 		_vm->_cursor->setControl(0);
 	
 	if (control->_actor) {
-		/* TODO
-		if (actor->_pathNode && (actor->_flags & 0x400))
-			delete actor->_pathNode;
-		*/
+		if (control->_actor->_pathNode && (control->_actor->_flags & 0x400))
+			delete control->_actor->_pathNode;
 		if (!(control->_actor->_flags & 0x200))
 			control->_actor->destroySurface();
 		/* TODO
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 50d3f7c..4dd8ba5 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -66,6 +66,7 @@ protected:
 };
 
 typedef Common::Functor2<Control*, uint32, void> ActorControlRoutine;
+typedef Common::Array<Common::Point> PointArray;
 
 class Actor {
 public:
@@ -134,8 +135,17 @@ public:
 	int _seqCodeValue2;
 	int _seqCodeValue3;
 	
-	int _pathCtrY;
-	int _path40;
+	int _pathCtrX, _pathCtrY;
+	int _pathAngle;
+	int32 _posXShl, _posYShl;
+	uint _pathPointIndex;
+	uint _pathPointsCount;
+	Common::Point _pathInitialPos;
+	bool _pathInitialPosFlag;
+	bool _pathFlag50;
+	PointArray *_pathNode;
+	uint _pathPoints;
+	uint32 _walkCallerThreadId1;
 	
 };
 
@@ -176,6 +186,10 @@ public:
 	void setActorIndexTo2();
 	void startSubSequence(int linkIndex, uint32 sequenceId);
 	void stopSubSequence(int linkIndex);
+	void startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 callerThreadId1, uint32 callerThreadId2);
+	PointArray *createPath(Common::Point destPt);
+	void updateActorMovement(uint32 deltaTime);
+	void refreshSequenceCode();
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
@@ -211,7 +225,7 @@ public:
 	void unpauseControlsByTag(uint32 tag);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
-	void actorControlRoutine(Control *control, uint32 deltaTime);	
+	void actorControlRoutine(Control *control, uint32 deltaTime);
 public:
 	typedef Common::List<Control*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/fixedpoint.cpp b/engines/illusions/fixedpoint.cpp
index 7fc15e4..54dbb13 100644
--- a/engines/illusions/fixedpoint.cpp
+++ b/engines/illusions/fixedpoint.cpp
@@ -41,20 +41,33 @@ FP16 fixedDiv(FP16 a, FP16 b) {
 	return ((float)a / b) * 65536.0;
 }
 
-int fixedTrunc(FP16 value) {
+int16 fixedTrunc(FP16 value) {
 	// CHECKME Not sure if this correct
-	int result = value >> 16;
+	int16 result = (value >> 16) & 0xFFFF;
 	if ((value & 0xFFFF) >= 0x8000)
 		++result;
 	return result;
 }
 
 FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2) {
-	float xd = ABS(fixedToFloat(x1) - fixedToFloat(x2));
-	float yd = ABS(fixedToFloat(y1) - fixedToFloat(y2));
+	float xd = fixedToFloat(x1) - fixedToFloat(x2);
+	float yd = fixedToFloat(y1) - fixedToFloat(y2);
 	if (xd != 0.0 || yd != 0.0)
 		return floatToFixed(sqrt(xd * xd + yd * yd));
 	return 0;
 }
 
+FP16 fixedAtan(FP16 value) {
+	//return floatToFixed(atan2(1.0, fixedToFloat(value)));
+	return floatToFixed(atan(fixedToFloat(value)));
+}
+
+FP16 fixedCos(FP16 value) {
+	return floatToFixed(cos(fixedToFloat(value)));
+}
+
+FP16 fixedSin(FP16 value) {
+	return floatToFixed(sin(fixedToFloat(value)));
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/fixedpoint.h b/engines/illusions/fixedpoint.h
index a14eb8b..1320daa7 100644
--- a/engines/illusions/fixedpoint.h
+++ b/engines/illusions/fixedpoint.h
@@ -29,16 +29,15 @@ namespace Illusions {
 
 typedef int32 FP16;
 
-struct FPRect {
-	FP16 x1, y1, x2, y2;
-};
-
 FP16 floatToFixed(float value);
 float fixedToFloat(FP16 value);
 FP16 fixedMul(FP16 a, FP16 b);
 FP16 fixedDiv(FP16 a, FP16 b);
-int fixedTrunc(FP16 value);
+int16 fixedTrunc(FP16 value);
 FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2);
+FP16 fixedAtan(FP16 value);
+FP16 fixedCos(FP16 value);
+FP16 fixedSin(FP16 value);
 
 } // End of namespace Illusions
 
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 2074943..8a59d70 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -354,7 +354,7 @@ void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint no
 		scriptThread->pause();
 	if (_doScriptThreadInit) {
 		int updateResult = 4;
-		while (scriptThread->_pauseCtr <= 0 && updateResult != 1 && updateResult != 2)
+		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
 			updateResult = scriptThread->update();
 	}
 }
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 77a7d1f..00f5795 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -422,10 +422,7 @@ void ScriptOpcodes::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall)
 	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	// TODO _control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
-
-	//DEBUG Resume calling thread, later done by the walking
-	_vm->notifyThreadId(opCall._threadId);
+	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
 }
 
 void ScriptOpcodes::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {


Commit: d77d4ed4a61d79ffc75bb9dbdab01157da387a13
    https://github.com/scummvm/scummvm/commit/d77d4ed4a61d79ffc75bb9dbdab01157da387a13
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix bug which occured when trying to walk while talking

- Fix minor bugs
- Work on talk thread handling

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/illusions.cpp
    engines/illusions/scriptman.cpp
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptthread.cpp
    engines/illusions/scriptthread.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/specialcode.cpp
    engines/illusions/thread.cpp
    engines/illusions/thread.h
    engines/illusions/timerthread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 2973a92..42f16ca 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -540,8 +540,8 @@ void Control::stopActor() {
 		_actor->_pathPointIndex = 0;
 		_actor->_walkCallerThreadId1 = 0;
 	}
-	_vm->notifyThreadId(_actor->_notifyThreadId1);
 	_vm->notifyThreadId(_actor->_notifyId3C);
+	_vm->notifyThreadId(_actor->_notifyThreadId1);
 }
 
 void Control::startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId) {
@@ -569,7 +569,7 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 	if (_actor->_linkIndex2) {
 		Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[_actor->_linkIndex2 - 1]);
 		if (subControl->_actor->_flags & 1) {
-			if (subControl->_actor->_pathNode) {
+			if (_actor->_pathNode) {
 				doSeq = false;
 				subControl->_actor->_notifyThreadId2 = threadId;
 				subControl->_actor->_entryTblPtr = entryTblPtr;
@@ -597,7 +597,7 @@ void Control::sequenceActor() {
 	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
 		bool breakInner = false;
 		while (!breakInner) {
-			debug(1, "SEQ op: %08X", _actor->_seqCodeIp[0]);
+			debug(1, "SEQ[%08X] op: %08X", _actor->_sequenceId, _actor->_seqCodeIp[0]);
 			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
 			opCall._opSize = _actor->_seqCodeIp[1];
 			opCall._code = _actor->_seqCodeIp + 2;
@@ -666,8 +666,9 @@ void Control::startSubSequence(int linkIndex, uint32 sequenceId) {
 void Control::stopSubSequence(int linkIndex) {
 	Control *linkedControl = _vm->_dict->getObjectControl(_actor->_subobjects[linkIndex - 1]);
 	Actor *linkedActor = linkedControl->_actor;
-	uint32 notifySequenceId2 = _actor->_notifyThreadId2;
+	uint32 notifyThreadId2 = _actor->_notifyThreadId2;
 	_actor->_linkIndex2 = linkIndex;
+//TODO BUGGY!	
 	if (_actor->_entryTblPtr) {
 		linkedActor->_flags |= 0x80;
 		linkedActor->_entryTblPtr = _actor->_entryTblPtr;
@@ -679,13 +680,14 @@ void Control::stopSubSequence(int linkIndex) {
 		_actor->_notifyThreadId1 = 0;
 		_actor->_notifyThreadId2 = 0;
 	}
-	if (notifySequenceId2) {
-		Thread *talkThread = _vm->_scriptMan->_threads->findThread(notifySequenceId2);
+	if (notifyThreadId2) {
+		Thread *talkThread = _vm->_scriptMan->_threads->findThread(notifyThreadId2);
 		talkThread->sendMessage(kMsgClearSequenceId2, 0);
 	}
 }
 
 void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 callerThreadId1, uint32 callerThreadId2) {
+
 	PointArray *pathNode;
 	ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
 
@@ -895,7 +897,7 @@ void Control::refreshSequenceCode() {
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 
 	stopActor();
-	
+
 	_actor->_flags &= ~0x80;
 	_actor->_flags &= ~0x0400;
 	_actor->_flags |= 0x0100;
@@ -925,7 +927,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	}
 
 	sequenceActor();
-	
+
 }
 
 void Control::execSequenceOpcode(OpCall &opCall) {
@@ -1095,6 +1097,17 @@ void Controls::destroyControlsByTag(uint32 tag) {
 	}
 }
 
+void Controls::threadIsDead(uint32 threadId) {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_actor &&
+			(control->_actor->_notifyThreadId1 == threadId || control->_actor->_notifyId3C == threadId)) {
+			control->_actor->_notifyThreadId1 = 0;
+			control->_actor->_notifyId3C = 0;
+		}
+	}
+}
+
 void Controls::pauseControlsByTag(uint32 tag) {
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *control = *it;
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 4dd8ba5..4c937ef 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -221,6 +221,7 @@ public:
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
 	void placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId);
 	void destroyControlsByTag(uint32 tag);
+	void threadIsDead(uint32 threadId);
 	void pauseControlsByTag(uint32 tag);
 	void unpauseControlsByTag(uint32 tag);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 4748634..684314e 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -229,8 +229,9 @@ int IllusionsEngine::updateSequences() {
 	// TODO Move to Controls class
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
-		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_seqCodeIp)
+		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_seqCodeIp) {
 			control->sequenceActor();
+		}
 	}
 	return 1;
 }
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index 8a59d70..c438def 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -245,7 +245,7 @@ uint32 ScriptMan::startTalkThread(int16 duration, uint32 objectId, uint32 talkId
 	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
 	debug(2, "Starting talk thread");
 	uint32 tempThreadId = newTempThreadId();
-	// TODO endTalkThreadsNoNotify();
+	_threads->endTalkThreadsNoNotify();
 	TalkThread *talkThread = new TalkThread(_vm, tempThreadId, callingThreadId, 0,
 		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
 	_threads->startThread(talkThread);
@@ -353,7 +353,7 @@ void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint no
 	if (_pauseCtr > 0)
 		scriptThread->pause();
 	if (_doScriptThreadInit) {
-		int updateResult = 4;
+		int updateResult = kTSRun;
 		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
 			updateResult = scriptThread->update();
 	}
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 00f5795..0a96f9c 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -215,7 +215,7 @@ void ScriptOpcodes::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCal
 	if (maxDuration)
 		duration += _vm->getRandom(maxDuration);
 		
-duration = 5;//DEBUG Speeds up things		
+duration = 1;//DEBUG Speeds up things		
 		
 	if (isAbortable)
 		_vm->_scriptMan->startAbortableTimerThread(duration, opCall._threadId);
@@ -262,10 +262,11 @@ void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 }
 
 //DEBUG Scenes
-uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
+//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
 //uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
 //uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//
 
 void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -442,11 +443,7 @@ void ScriptOpcodes::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall
 	ARG_UINT32(sequenceId1);
 	ARG_UINT32(sequenceId2);
 	ARG_UINT32(namedPointId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
-	_vm->_scriptMan->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._callerThreadId);
-
-	//DEBUG Resume calling thread, later done after talking is finished
-	//_vm->notifyThreadId(opCall._threadId);
+	_vm->_scriptMan->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._threadId);
 }
 
 void ScriptOpcodes::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
@@ -618,16 +615,19 @@ void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 }
 
 void ScriptOpcodes::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	_vm->_scriptMan->_stack.push(_vm->_scriptMan->_prevSceneId == sceneId ? 1 : 0);
 }
 
 void ScriptOpcodes::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	_vm->_scriptMan->_stack.push(_vm->getCurrentScene() == sceneId ? 1 : 0);
 }
 
 void ScriptOpcodes::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	_vm->_scriptMan->_stack.push(_vm->_scriptMan->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
 }
@@ -698,7 +698,7 @@ void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
 	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
 	
 	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._threadId);
+	_vm->notifyThreadId(opCall._callerThreadId);
 	
 }
 
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 69835d9..6f6d3a0 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -55,31 +55,6 @@ int ScriptThread::onUpdate() {
 	return opCall._result;
 }
 
-void ScriptThread::onSuspend() {
-	// TODO
-	debug(1, "ScriptThread::onSuspend()");
-}
-
-void ScriptThread::onNotify() {
-	// TODO
-	debug(1, "ScriptThread::onNotify()");
-}
-
-void ScriptThread::onPause() {
-	// TODO
-	debug(1, "ScriptThread::onPause()");
-}
-
-void ScriptThread::onResume() {
-	// TODO
-	debug(1, "ScriptThread::onResume()");
-}
-
-void ScriptThread::onTerminated() {
-	// TODO
-	debug(1, "ScriptThread::onTerminated()");
-}
-
 void ScriptThread::execOpcode(OpCall &opCall) {
 	// TODO Clean this up
 	_vm->_scriptMan->_scriptOpcodes->execOpcode(this, opCall);
diff --git a/engines/illusions/scriptthread.h b/engines/illusions/scriptthread.h
index f0d122b..cb82b6c 100644
--- a/engines/illusions/scriptthread.h
+++ b/engines/illusions/scriptthread.h
@@ -35,11 +35,6 @@ public:
 	ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
 	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
 public:
 	int16 _sequenceStalled;
 	byte *_scriptCodeIp;
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 23d1781..cab0fbe 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -119,7 +119,7 @@ void SequenceOpcodes::opSetFrameIndex(Control *control, OpCall &opCall) {
 	control->_actor->_flags &= ~0x0100;
 	if (control->_actor->_flags & 0x8000) {
 		control->appearActor();
-		control->_actor->_flags &= ~0x800;
+		control->_actor->_flags &= ~0x8000;
 	}
 	control->_actor->_newFrameIndex = frameIndex;
 }
@@ -146,7 +146,7 @@ void SequenceOpcodes::opIncFrameDelay(Control *control, OpCall &opCall) {
 void SequenceOpcodes::opSetRandomFrameDelay(Control *control, OpCall &opCall) {
 	ARG_INT16(minFrameDelay);
 	ARG_INT16(maxFrameDelay);
-	control->_actor->_seqCodeValue3 += 0;//DEBUG minFrameDelay + _vm->getRandom(maxFrameDelay);
+	control->_actor->_seqCodeValue3 += minFrameDelay + _vm->getRandom(maxFrameDelay);
 	opCall._result = 2;
 }
 
@@ -172,7 +172,7 @@ void SequenceOpcodes::opGotoSequence(Control *control, OpCall &opCall) {
 	ARG_UINT32(nextSequenceId);
 	uint32 notifyThreadId1 = control->_actor->_notifyThreadId1;
 	control->clearNotifyThreadId1();
-	if (false/*TODO control->_actor->_pathNode*/) {
+	if (control->_actor->_pathNode) {
 		control->startSequenceActor(nextSequenceId, 1, notifyThreadId1);
 	} else {
 		control->startSequenceActor(nextSequenceId, 2, notifyThreadId1);
diff --git a/engines/illusions/specialcode.cpp b/engines/illusions/specialcode.cpp
index 7ac49a2..a412462 100644
--- a/engines/illusions/specialcode.cpp
+++ b/engines/illusions/specialcode.cpp
@@ -30,14 +30,12 @@ namespace Illusions {
 
 void SpecialCodeLoader::load(Resource *resource) {
 	debug("SpecialCodeLoader::load() Loading special code %08X...", resource->_resId);
-	// TODO
 	_vm->_specialCode = new BbdouSpecialCode(_vm);
 	_vm->_specialCode->init();
 }
 
 void SpecialCodeLoader::unload(Resource *resource) {
 	debug("SpecialCodeLoader::unload() Unloading special code %08X...", resource->_resId);
-	// TODO
 	delete _vm->_specialCode;
 	_vm->_specialCode = 0;
 }
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 38e6900..980a8af 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/thread.h"
+#include "illusions/actor.h"
 
 namespace Illusions {
 
@@ -55,7 +56,7 @@ void Thread::onTerminated() {
 }
 
 void Thread::onKill() {
-	// TODO artmgrThreadIsDead(thread->threadId);
+	_vm->_controls->threadIsDead(_threadId);
 	terminate();
 }
 
@@ -110,11 +111,11 @@ int Thread::update() {
 
 void Thread::terminate() {
 	if (!_terminated) {
-		if (!(_notifyFlags & 1))
+		if (!(_notifyFlags & 1)) {
 			_vm->notifyThreadId(_callingThreadId);
+		}
 		_callingThreadId = 0;
 		onTerminated();
-		// TODO _vm->removeThread(_threadId, this);
 		_terminated = true;
 	}
 }
@@ -128,7 +129,6 @@ ThreadList::ThreadList(IllusionsEngine *vm)
 void ThreadList::startThread(Thread *thread) {
 	// TODO tag has to be set by the Thread class scrmgrGetCurrentScene();
 	_threads.push_back(thread);
-	// TODO _vm->addThread(thread->_threadId, thread);
 }
 
 void ThreadList::updateThreads() {
@@ -246,6 +246,14 @@ void ThreadList::endTalkThreads() {
 	}
 }
 
+void ThreadList::endTalkThreadsNoNotify() {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_type == kTTTalkThread && thread->_callingThreadId == 0)
+			thread->terminate();
+	}
+}
+
 void ThreadList::killThread(uint32 threadId) {
 
 	if (!threadId)
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index 2cd3cc2..ea4868d 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -92,6 +92,7 @@ public:
 	void pauseThreads(uint32 threadId);
 	void resumeThreads(uint32 threadId);
 	void endTalkThreads();
+	void endTalkThreadsNoNotify();
 	void killThread(uint32 threadId);
 	void setThreadSceneId(uint32 threadId, uint32 sceneId);
 	uint32 getThreadSceneId(uint32 threadId);
diff --git a/engines/illusions/timerthread.cpp b/engines/illusions/timerthread.cpp
index 42dde6d..bf9c32f 100644
--- a/engines/illusions/timerthread.cpp
+++ b/engines/illusions/timerthread.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/timerthread.h"
 #include "illusions/input.h"
+#include "illusions/scriptman.h"
 #include "illusions/time.h"
 
 namespace Illusions {
@@ -35,7 +36,13 @@ TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThr
 	_type = kTTTimerThread;
 	_startTime = getCurrentTime();
 	_endTime = _startTime + _duration;
-	// TODO _tag = *(_DWORD *)(krndictGetIDValue(callingThreadId) + 20);
+
+	if (callingThreadId) {
+		Thread *callingThread = _vm->_scriptMan->_threads->findThread(callingThreadId);
+		if (callingThread)
+			_tag = callingThread->_tag;
+	}
+
 }
 
 int TimerThread::onUpdate() {


Commit: d67021b32cf96388291135b19060d6dba8b7fbd4
    https://github.com/scummvm/scummvm/commit/d67021b32cf96388291135b19060d6dba8b7fbd4
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement talkitem pausing/unpausing

- Fix isActiveThread to check only non-paused and non-terminated threads

Changed paths:
    engines/illusions/scriptopcodes.cpp
    engines/illusions/talkresource.cpp
    engines/illusions/talkresource.h
    engines/illusions/thread.cpp


diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 0a96f9c..aa63512 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -31,6 +31,7 @@
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
 #include "illusions/specialcode.h"
+#include "illusions/talkresource.h"
 
 namespace Illusions {
 
@@ -296,7 +297,7 @@ void ScriptOpcodes::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall
 	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardButtons(0xFFFF);
 	_vm->_scriptMan->enterPause(opCall._callerThreadId);
-	// TODO _vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
 	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
 	_vm->_scriptMan->startScriptThread(threadId, 0,
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
@@ -308,7 +309,7 @@ void ScriptOpcodes::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall)
 	_vm->_input->discardButtons(0xFFFF);
 	_vm->_scriptMan->exitScene(opCall._callerThreadId);
 	_vm->_scriptMan->leavePause(opCall._callerThreadId);
-	// TODO _vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
 }
 
 void ScriptOpcodes::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index 0c1108e..2487d61 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -35,26 +35,14 @@ void TalkResourceLoader::load(Resource *resource) {
 	talkResource->load(resource->_data, resource->_dataSize);
 	resource->_refId = talkResource;
 
-	_vm->_talkItems->newTalkItem(resource->_resId, talkResource);
-
-	for (uint i = 0; i < talkResource->_talkEntriesCount; ++i) {
-		TalkEntry *talkEntry = &talkResource->_talkEntries[i];
-		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
-	}
-	
+	TalkItem *talkItem = _vm->_talkItems->newTalkItem(resource->_resId, resource->_tag, talkResource);
+	talkItem->registerResources();
 }
 
 void TalkResourceLoader::unload(Resource *resource) {
 	TalkItem *talkItem = _vm->_talkItems->findTalkItem(resource->_resId);
-	TalkResource *talkResource = talkItem->_talkRes;
-
-	for (uint i = 0; i < talkResource->_talkEntriesCount; ++i) {
-		TalkEntry *talkEntry = &talkResource->_talkEntries[i];
-		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
-	}
-	
+	talkItem->unregisterResources();
 	_vm->_talkItems->freeTalkItem(talkItem);
-	
 }
 
 void TalkResourceLoader::buildFilename(Resource *resource) {
@@ -109,23 +97,50 @@ void TalkResource::load(byte *data, uint32 dataSize) {
 
 // TalkItem
 
-TalkItem::TalkItem(uint32 talkId, TalkResource *talkResource)
-	: _talkId(talkId), _talkRes(talkResource), _pauseCtr(0) {
+TalkItem::TalkItem(IllusionsEngine *vm, uint32 talkId, uint32 tag, TalkResource *talkResource)
+	: _vm(vm), _talkId(talkId), _tag(tag), _talkRes(talkResource), _pauseCtr(0) {
 }
 
 TalkItem::~TalkItem() {
 }
 
+void TalkItem::registerResources() {
+	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
+		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
+	}
+}
+
+void TalkItem::unregisterResources() {
+	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
+		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
+	}
+}
+
+void TalkItem::pause() {
+	++_pauseCtr;
+	if (_pauseCtr == 1)
+		unregisterResources();
+}
+
+void TalkItem::unpause() {
+	--_pauseCtr;
+	if (_pauseCtr == 0)
+		registerResources();
+}
+
 // TalkItems
 
-TalkItems::TalkItems(IllusionsEngine *vm) {
+TalkItems::TalkItems(IllusionsEngine *vm)
+	: _vm(vm) {
 }
 
 TalkItems::~TalkItems() {
 }
 
-TalkItem *TalkItems::newTalkItem(uint32 talkId, TalkResource *talkResource) {
-	TalkItem *talkItem = new TalkItem(talkId, talkResource);
+TalkItem *TalkItems::newTalkItem(uint32 talkId, uint32 tag, TalkResource *talkResource) {
+	TalkItem *talkItem = new TalkItem(_vm, talkId, tag, talkResource);
 	_items.push_back(talkItem);
 	return talkItem;
 }
@@ -142,4 +157,23 @@ TalkItem *TalkItems::findTalkItem(uint32 talkId) {
 	return 0;
 }
 
+TalkItem *TalkItems::findTalkItemByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			return (*it);
+	return 0;
+}
+
+void TalkItems::pauseByTag(uint32 tag) {
+	TalkItem *talkItem = findTalkItemByTag(tag);
+	if (talkItem)
+		talkItem->pause();
+}
+
+void TalkItems::unpauseByTag(uint32 tag) {
+	TalkItem *talkItem = findTalkItemByTag(tag);
+	if (talkItem)
+		talkItem->unpause();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/talkresource.h b/engines/illusions/talkresource.h
index beb859c..ff82645 100644
--- a/engines/illusions/talkresource.h
+++ b/engines/illusions/talkresource.h
@@ -63,10 +63,16 @@ public:
 
 class TalkItem {
 public:
-	TalkItem(uint32 talkId, TalkResource *talkResource);
+	TalkItem(IllusionsEngine *vm, uint32 talkId, uint32 tag, TalkResource *talkResource);
 	~TalkItem();
+	void registerResources();
+	void unregisterResources();
+	void pause();
+	void unpause();
 public:
+	IllusionsEngine *_vm;
 	uint32 _talkId;
+	uint32 _tag;
 	TalkResource *_talkRes;
 	int _pauseCtr;
 };
@@ -75,9 +81,12 @@ class TalkItems {
 public:
 	TalkItems(IllusionsEngine *vm);
 	~TalkItems();
-	TalkItem *newTalkItem(uint32 talkId, TalkResource *talkResource);
+	TalkItem *newTalkItem(uint32 talkId, uint32 tag, TalkResource *talkResource);
 	void freeTalkItem(TalkItem *talkItem);
 	TalkItem *findTalkItem(uint32 talkId);
+	TalkItem *findTalkItemByTag(uint32 tag);
+	void pauseByTag(uint32 tag);
+	void unpauseByTag(uint32 tag);
 //protected:
 public:
 	typedef Common::List<TalkItem*> Items;
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 980a8af..cac13d9 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -286,9 +286,12 @@ uint32 ThreadList::getThreadSceneId(uint32 threadId) {
 
 bool ThreadList::isActiveThread(int msgNum) {
 	// Check if at least one thread returns a non-null value for the message
-	for (Iterator it = _threads.begin(); it != _threads.end(); ++it)
-		if ((*it)->sendMessage(msgNum, 0) != 0)
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (!thread->_terminated && thread->_pauseCtr <= 0 &&
+			thread->sendMessage(msgNum, 0) != 0)
 			return true;
+	}
 	return false;
 }
 


Commit: 7dc8533f730645e7177bbdc4d214664d82856963
    https://github.com/scummvm/scummvm/commit/7dc8533f730645e7177bbdc4d214664d82856963
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix thread camera panning

- Minor fixes and corrections

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/talkthread.cpp
    engines/illusions/thread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 42f16ca..75b4900 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -668,7 +668,6 @@ void Control::stopSubSequence(int linkIndex) {
 	Actor *linkedActor = linkedControl->_actor;
 	uint32 notifyThreadId2 = _actor->_notifyThreadId2;
 	_actor->_linkIndex2 = linkIndex;
-//TODO BUGGY!	
 	if (_actor->_entryTblPtr) {
 		linkedActor->_flags |= 0x80;
 		linkedActor->_entryTblPtr = _actor->_entryTblPtr;
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index ec87755..4aca265 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -351,7 +351,7 @@ void BbdouCursor::hide(uint32 objectId) {
 	if (_data._visibleCtr == 0) {
 		Control *control = _vm->_dict->getObjectControl(objectId);
 		control->startSequenceActor(0x60029, 2, 0);
-		// TODO item10_sub_10005040(objectId, &cursorData->item10);
+		_bbdou->resetItem10(objectId, &_data._item10);
 		_vm->_camera->popCameraMode();
 	}
 	_vm->_input->discardButtons(0xFFFF);
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 5502616..26265a7 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -162,6 +162,14 @@ void BbdouInventory::removeInventoryItem(uint32 objectId) {
 	}
 }
 
+bool BbdouInventory::hasInventoryItem(uint32 objectId) {
+	for (uint i = 0; i < _inventoryItems.size(); ++i)
+		if (_inventoryItems[i]->_objectId == objectId &&
+			_inventoryItems[i]->_assigned)
+			return true;
+	return false;
+}
+
 void BbdouInventory::open() {
 	_activeBagSceneId = 0;
 	InventoryBag *inventoryBag = getInventoryBag(_vm->getCurrentScene());
@@ -203,8 +211,8 @@ void BbdouInventory::close() {
 		Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
 		control->startSequenceActor(0x00060187, 2, 0);
 	}
-    inventoryBag->_isActive = false;
-    _activeInventorySceneId = 0;
+	inventoryBag->_isActive = false;
+	_activeInventorySceneId = 0;
 }
 
 InventoryBag *BbdouInventory::getInventoryBag(uint32 sceneId) {
@@ -245,7 +253,7 @@ void BbdouInventory::buildItems(InventoryBag *inventoryBag) {
 	inventoryBag->buildItems();
 	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it) {
 		InventoryItem *inventoryItem = *it;
-	    if (inventoryItem->_assigned && !inventoryItem->_flag &&
+		if (inventoryItem->_assigned && !inventoryItem->_flag &&
 			inventoryItem->_timesPresent == 0 &&
 			inventoryItem->_objectId != _bbdou->_cursor->_data._holdingObjectId)
 			inventoryBag->addInventoryItem(inventoryItem, 0);
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index 3c211e5..e4ee28f 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -79,6 +79,7 @@ public:
 	void registerInventorySlot(uint32 namedPointId);
 	void addInventoryItem(uint32 objectId);
 	void removeInventoryItem(uint32 objectId);
+	bool hasInventoryItem(uint32 objectId);
 	void open();
 	void close();
 	InventoryBag *getInventoryBag(uint32 sceneId);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 1547aab..a3a3149 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -103,6 +103,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001C, spcOpenInventory);
 	SPECIAL(0x0016001D, spcAddInventoryItem);
 	SPECIAL(0x0016001E, spcRemoveInventoryItem);
+	SPECIAL(0x0016001F, spcHasInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
 }
 
@@ -221,6 +222,11 @@ void BbdouSpecialCode::spcRemoveInventoryItem(OpCall &opCall) {
 	_inventory->removeInventoryItem(objectId);
 }
 
+void BbdouSpecialCode::spcHasInventoryItem(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	_vm->_scriptMan->_stack.push(_inventory->hasInventoryItem(objectId) ? 1 : 0);
+}
+
 void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 	_inventory->close();
 }
@@ -349,7 +355,7 @@ bool BbdouSpecialCode::findVerbId(Item10 *item10, uint32 currOverlappedObjectId,
 void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime) {
 	Actor *actor = cursorControl->_actor;
 	CursorData &cursorData = _cursor->_data;
-	
+
 	if (cursorData._visibleCtr > 0) {
 
 		Common::Point cursorPos = _vm->_input->getCursorPosition();
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index d018f38..734e8e8 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -92,6 +92,7 @@ public:
 	void spcOpenInventory(OpCall &opCall);
 	void spcAddInventoryItem(OpCall &opCall);
 	void spcRemoveInventoryItem(OpCall &opCall);
+	void spcHasInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
 
 	void playSoundEffect(int soundIndex);
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index aa63512..d94b96d 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -263,17 +263,19 @@ void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 }
 
 //DEBUG Scenes
-//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
+uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
 //uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
 //uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
-uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//
+//uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
+//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
+//uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
 
 void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
-	
+
 	if (dsceneId) {
 		sceneId = dsceneId;
 		threadId = dthreadId;
@@ -338,21 +340,21 @@ void ScriptOpcodes::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_UINT32(objectId);
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	Common::Point pos = control->getActorPosition();
-	_vm->_camera->panToPoint(pos, speed, opCall._callerThreadId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
 void ScriptOpcodes::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);	
 	ARG_UINT32(namedPointId);
 	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_camera->panToPoint(pos, speed, opCall._callerThreadId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
 void ScriptOpcodes::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);	
 	ARG_INT16(x);	
 	ARG_INT16(y);	
-	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._callerThreadId);
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
 }
 
 void ScriptOpcodes::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
@@ -609,7 +611,7 @@ void ScriptOpcodes::opActivateButton(ScriptThread *scriptThread, OpCall &opCall)
 }
 
 void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs)
+	ARG_INT16(jumpOffs);
 	int16 value = _vm->_scriptMan->_stack.pop();
 	if (value == 0)
 		opCall._deltaOfs += jumpOffs;
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index c8bee71..be745f6 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -262,7 +262,7 @@ uint32 TalkThread::sendMessage(int msgNum, uint32 msgValue) {
 	// TODO
 	switch (msgNum) {
 	case kMsgQueryTalkThreadActive:
-        if (_status != 1 && _status != 2)
+		if (_status != 1 && _status != 2)
 			return 1;
 		break;
 	case kMsgClearSequenceId1:
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index cac13d9..3a5a4a9 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -111,9 +111,8 @@ int Thread::update() {
 
 void Thread::terminate() {
 	if (!_terminated) {
-		if (!(_notifyFlags & 1)) {
+		if (!(_notifyFlags & 1))
 			_vm->notifyThreadId(_callingThreadId);
-		}
 		_callingThreadId = 0;
 		onTerminated();
 		_terminated = true;
@@ -127,7 +126,7 @@ ThreadList::ThreadList(IllusionsEngine *vm)
 }
 
 void ThreadList::startThread(Thread *thread) {
-	// TODO tag has to be set by the Thread class scrmgrGetCurrentScene();
+	// TODO tag has to be set by the Thread class
 	_threads.push_back(thread);
 }
 


Commit: 8d7d6599b9bc29152cbccbcec6b2e25b66272715
    https://github.com/scummvm/scummvm/commit/8d7d6599b9bc29152cbccbcec6b2e25b66272715
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix special code functions thread notifying (used wrong thread value)

- Add more script and sequence opcodes

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/talkthread.cpp
    engines/illusions/thread.cpp


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index a3a3149..f4c23bb 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -105,6 +105,8 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001E, spcRemoveInventoryItem);
 	SPECIAL(0x0016001F, spcHasInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
+	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
+	SPECIAL(0x0016003A, spcSaladCtl);
 }
 
 void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
@@ -113,7 +115,7 @@ void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
 		(*(*it)._value)(opCall);
 	} else {
 		debug("BbdouSpecialCode::run() Unimplemented special code %08X", specialCodeId);
-		_vm->notifyThreadId(opCall._callerThreadId);
+		_vm->notifyThreadId(opCall._threadId);
 	}
 }
 
@@ -129,19 +131,19 @@ void BbdouSpecialCode::spcInitCursor(OpCall &opCall) {
 	ARG_UINT32(progResKeywordId);
 	_cursor->init(objectId, progResKeywordId);
 	setCursorControlRoutine(objectId, 0);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcEnableCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_cursor->enable(objectId);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcDisableCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_cursor->disable(objectId);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcAddCursorSequence(OpCall &opCall) {
@@ -149,7 +151,7 @@ void BbdouSpecialCode::spcAddCursorSequence(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	ARG_UINT32(sequenceId);
 	_cursor->addCursorSequence(objectId, sequenceId);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcCursorStartHoldingObjectId(OpCall &opCall) {
@@ -157,7 +159,7 @@ void BbdouSpecialCode::spcCursorStartHoldingObjectId(OpCall &opCall) {
 	ARG_UINT32(holdingObjectId);
 	ARG_INT16(doPlaySound);
 	startHoldingObjectId(objectId, holdingObjectId, doPlaySound != 0);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcCursorStopHoldingObjectId(OpCall &opCall) {
@@ -165,12 +167,12 @@ void BbdouSpecialCode::spcCursorStopHoldingObjectId(OpCall &opCall) {
 	ARG_INT16(doPlaySound);
 	stopHoldingObjectId(objectId, doPlaySound != 0);
 	_cursor->_data._mode = 1;
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcInitBubble(OpCall &opCall) {
 	_bubble->init();
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcSetupBubble(OpCall &opCall) {
@@ -181,7 +183,7 @@ void BbdouSpecialCode::spcSetupBubble(OpCall &opCall) {
 	ARG_INT16(count);
 	_bubble->addItem0(sequenceId1, sequenceId2, progResKeywordId, namedPointId,
 		count, (uint32*)opCall._code);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
@@ -189,7 +191,7 @@ void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	ARG_INT16(value);
 	_cursor->setStruct8bsValue(objectId, value);
-	_vm->notifyThreadId(opCall._callerThreadId);
+	_vm->notifyThreadId(opCall._threadId);
 }
 
 void BbdouSpecialCode::spcRegisterInventoryBag(OpCall &opCall) {
@@ -225,12 +227,33 @@ void BbdouSpecialCode::spcRemoveInventoryItem(OpCall &opCall) {
 void BbdouSpecialCode::spcHasInventoryItem(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_vm->_scriptMan->_stack.push(_inventory->hasInventoryItem(objectId) ? 1 : 0);
+debug("_inventory->hasInventoryItem(%08X) = %d", objectId, _inventory->hasInventoryItem(objectId));	
 }
 
 void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 	_inventory->close();
 }
 
+void BbdouSpecialCode::spcIsCursorHoldingObjectId(OpCall &opCall) {
+	ARG_UINT32(cursorObjectId);
+	ARG_UINT32(objectId);
+	_vm->_scriptMan->_stack.push(isHoldingObjectId(objectId) ? 1 : 0);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void BbdouSpecialCode::spcSaladCtl(OpCall &opCall) {
+	ARG_UINT32(cmd);
+	ARG_UINT32(sequenceId);
+	switch (cmd) {
+	case 1:
+		initSalad();
+		break;
+	case 2:
+		addSalad(sequenceId);
+		break;
+	}
+}
+
 void BbdouSpecialCode::playSoundEffect(int soundIndex) {
 	static const uint32 kSoundEffectIds[] = {
 		      0, 1,
@@ -644,4 +667,30 @@ void BbdouSpecialCode::stopHoldingObjectId(uint32 objectId1, bool doPlaySound) {
 		_inventory->putBackInventoryItem(holdingObjectId, control->_actor->_position);
 }
 
+bool BbdouSpecialCode::isHoldingObjectId(uint32 objectId) {
+	return _cursor->_data._holdingObjectId == objectId;
+}
+
+void BbdouSpecialCode::initSalad() {
+	for (uint i = 0; i < 12; ++i) {
+		_saladObjectIds[i] = _vm->_controls->newTempObjectId();
+		_vm->_controls->placeActor(0x00050192, Common::Point(0, 0), 0x00060C26, _saladObjectIds[i], 0);
+	}
+	_saladCount = 0;
+}
+
+void BbdouSpecialCode::addSalad(uint32 sequenceId) {
+	if (_saladCount >= 12) {
+		Control *control = _vm->_dict->getObjectControl(_saladObjectIds[_saladCount - 1]);
+		control->unlinkObject();
+	} else {
+		++_saladCount;
+	}
+	Control *control = _vm->_dict->getObjectControl(_saladObjectIds[_saladCount - 1]);
+	control->linkToObject(0x00040309, _saladCount);
+	control->startSequenceActor(sequenceId, 2, 0);
+	control->setPriority(_saladCount + 9);
+	control->deactivateObject();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 734e8e8..f272193 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -76,6 +76,11 @@ public:
 	BbdouCursor *_cursor;
 	BbdouBubble *_bubble;
 	BbdouInventory *_inventory;
+
+	// Salad
+	uint _saladCount;
+	uint32 _saladObjectIds[12];
+
 	// Special code interface functions
 	void spcInitCursor(OpCall &opCall);
 	void spcEnableCursor(OpCall &opCall);
@@ -94,11 +99,14 @@ public:
 	void spcRemoveInventoryItem(OpCall &opCall);
 	void spcHasInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
+	void spcIsCursorHoldingObjectId(OpCall &opCall);
+	void spcSaladCtl(OpCall &opCall);
 
 	void playSoundEffect(int soundIndex);
 	void resetItem10(uint32 objectId, Item10 *item10);
 	void startHoldingObjectId(uint32 objectId1, uint32 holdingObjectId, bool doPlaySound);
 	void stopHoldingObjectId(uint32 objectId1, bool doPlaySound);
+	bool isHoldingObjectId(uint32 objectId);
 
 protected:
 	// Internal functions
@@ -116,6 +124,9 @@ protected:
 	bool runCause(Control *cursorControl, CursorData &cursorData,
 		uint32 verbId, uint32 objectId2, uint32 objectId, int soundIndex);
 	uint32 startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	// Salad
+	void initSalad();
+	void addSalad(uint32 sequenceId);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index d94b96d..a551bdf 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -109,6 +109,7 @@ void ScriptOpcodes::initOpcodes() {
 	OPCODE(37, opPanStop);
 	OPCODE(39, opSetDisplay);
 	OPCODE(42, opIncBlockCounter);
+	OPCODE(43, opClearBlockCounter);
 	OPCODE(45, opSetProperty);
 	OPCODE(46, opPlaceActor);
 	OPCODE(47, opFaceActor);
@@ -263,13 +264,17 @@ void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 }
 
 //DEBUG Scenes
-uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
+//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
 //uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
 //uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 //uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
 //uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
 //uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
+//uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
+//uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
+//uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
+uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 
 void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -373,6 +378,11 @@ void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall
 		_vm->_scriptMan->_scriptResource->_blockCounters.set(index, value);
 }
 
+void ScriptOpcodes::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptMan->_scriptResource->_blockCounters.set(index, 0);
+}
+
 void ScriptOpcodes::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(value);	
 	ARG_UINT32(propertyId);	
@@ -690,7 +700,7 @@ void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &op
 
 void ScriptOpcodes::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Prints some debug text
-	debug("[DBG] %s", (char*)opCall._code);
+	debug(1, "[DBG] %s", (char*)opCall._code);
 }
 
 void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
@@ -747,7 +757,7 @@ void ScriptOpcodes::opStartAbortableThread(ScriptThread *scriptThread, OpCall &o
 	ARG_INT16(codeOffs);
 	ARG_INT16(skipOffs);
 	_vm->_scriptMan->startAbortableThread(opCall._code + codeOffs,
-		opCall._code + skipOffs, opCall._callerThreadId);
+		opCall._code + skipOffs, opCall._threadId);
 }
 
 void ScriptOpcodes::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 32f23fd..4fc6cd6 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -83,6 +83,7 @@ protected:
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index cab0fbe..e53a7ec 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -25,6 +25,7 @@
 #include "illusions/actor.h"
 #include "illusions/actorresource.h"
 #include "illusions/dictionary.h"
+#include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 
 namespace Illusions {
@@ -82,6 +83,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(40, opSetPriorityLayer);
 	OPCODE(50, opPlaySound);
 	OPCODE(51, opStopSound);
+	OPCODE(52, opStartScriptThread);
 	OPCODE(53, opPlaceSubActor);
 	OPCODE(54, opStartSubSequence);
 	OPCODE(55, opStopSubSequence);
@@ -311,6 +313,12 @@ void SequenceOpcodes::opStopSound(Control *control, OpCall &opCall) {
 	// TODO _vm->stopSound(soundEffectId);
 }
 
+void SequenceOpcodes::opStartScriptThread(Control *control, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_scriptMan->startScriptThread(threadId, 0, 0, 0, 0);
+}
+
 void SequenceOpcodes::opPlaceSubActor(Control *control, OpCall &opCall) {
  	ARG_INT16(linkIndex);
  	ARG_UINT32(actorTypeId);
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 028d567..747287e 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -72,6 +72,7 @@ protected:
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
 	void opPlaySound(Control *control, OpCall &opCall);
 	void opStopSound(Control *control, OpCall &opCall);
+	void opStartScriptThread(Control *control, OpCall &opCall);
 	void opPlaceSubActor(Control *control, OpCall &opCall);
 	void opStartSubSequence(Control *control, OpCall &opCall);
 	void opStopSubSequence(Control *control, OpCall &opCall);	
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index be745f6..10ff05d 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -78,7 +78,8 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 		if (callingThread)
 			_tag = callingThread->_tag;
 	}
-	
+
+	//debug("New talk thread: %08X %08X", _threadId, _talkId);
 }
 
 int TalkThread::onUpdate() {
@@ -307,7 +308,7 @@ static char *debugW2I(byte *wstr) {
 int TalkThread::insertText() {
 	int charCount = 100;
 	
-	debug("[%s]", debugW2I(_currEntryText));
+	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
 	_entryText = 0;
 	
 	// TODO _vm->getDimensions1(&dimensions);
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 3a5a4a9..d597d48 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -267,7 +267,7 @@ void ThreadList::killThread(uint32 threadId) {
 		if (childThread->_callingThreadId == threadId)
 			killThread(childThread->_threadId);
 	}
-	
+
 	thread->onKill();
 
 }


Commit: 08899f5e9b3d8af2b147610e164be7f561ca0bd9
    https://github.com/scummvm/scummvm/commit/08899f5e9b3d8af2b147610e164be7f561ca0bd9
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement calcPointDirection

- Implement sequence opcode 1

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 75b4900..97c8dc0 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -700,11 +700,9 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 	// TODO _actor->_field_C0 = destPt.x;
 	// TODO _actor->_field_C2 = destPt.y;
 
-	/* TODO	
 	uint newFacing;
-	if (calcPointDirection(_actor->_position, destPt, newFacing))
+	if (_vm->calcPointDirection(_actor->_position, destPt, newFacing))
 		faceActor(newFacing);
-	*/
 
 	if (actorType->_value1E)
 		_actor->_pathCtrY = actorType->_value1E;
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 684314e..a024b8d 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -349,10 +349,36 @@ uint32 IllusionsEngine::getPriorityFromBase(int16 priority) {
 	return 32000000 * priority;
 }
 
-bool IllusionsEngine::calcPointDirection(Common::Point &pos1, Common::Point &pos2, uint &facing) {
-	// TODO
+bool IllusionsEngine::calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing) {
 	facing = 0;
-	return false;
+	uint xd = 0, yd = 0;
+	if (srcPt.x < dstPt.x)
+		xd = 0x40;
+	else if (srcPt.x > dstPt.x)
+		xd = 0x04;
+	else
+		xd = 0x00;
+	if (srcPt.y < dstPt.y)
+		yd = 0x01;
+	else if (srcPt.y > dstPt.y)
+		yd = 0x10;
+	else
+		yd = 0x00;
+	if (!xd && !yd)
+		facing = 0;
+	else if (!yd && xd)
+		facing = xd;
+	else if (yd && !xd)
+		facing = yd;
+	else if (xd == 0x04 && yd == 0x01)
+		facing = 0x02;
+	else if (xd == 0x40 && yd == 0x01)
+		facing = 0x80;
+	else if (xd == 0x04 && yd == 0x10)
+		facing = 0x08;
+	else if (xd == 0x40 && yd == 0x10)
+		facing = 0x20;
+	return facing != 0;
 }
 
 void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index cf3549e..08a9f76 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -126,7 +126,7 @@ public:
 	int convertPanXCoord(int16 x);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	uint32 getPriorityFromBase(int16 priority);
-	bool calcPointDirection(Common::Point &pos1, Common::Point &pos2, uint &facing);
+	bool calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing);
 
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
 	
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index e53a7ec..818b681 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -56,6 +56,7 @@ void SequenceOpcodes::initOpcodes() {
 	for (uint i = 0; i < 256; ++i)
 		_opcodes[i] = 0;
 	// Register opcodes
+	OPCODE(1, opYield);
 	OPCODE(2, opSetFrameIndex);
 	OPCODE(3, opEndSequence);
 	OPCODE(4, opIncFrameDelay);
@@ -103,6 +104,10 @@ void SequenceOpcodes::freeOpcodes() {
 #define ARG_INT16(name) int16 name = opCall.readSint16(); debug(1, "ARG_INT16(" #name " = %d)", name);
 #define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(1, "ARG_UINT32(" #name " = %08X)", name);
 
+void SequenceOpcodes::opYield(Control *control, OpCall &opCall) {
+	opCall._result = 2;
+}
+
 void SequenceOpcodes::opSetFrameIndex(Control *control, OpCall &opCall) {
 	ARG_INT16(frameIndex);
 	if (control->_actor->_flags & 0x80) {
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 747287e..02561e3 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -45,6 +45,7 @@ protected:
 	void freeOpcodes();
 
 	// Opcodes
+	void opYield(Control *control, OpCall &opCall);
 	void opSetFrameIndex(Control *control, OpCall &opCall);
 	void opEndSequence(Control *control, OpCall &opCall);
 	void opIncFrameDelay(Control *control, OpCall &opCall);


Commit: 6d17b40796bf6ff91d4d036a33280e0ec27788f6
    https://github.com/scummvm/scummvm/commit/6d17b40796bf6ff91d4d036a33280e0ec27788f6
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Use screen pixel format when creating surfaces

Changed paths:
    engines/illusions/screen.cpp


diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index d8e6a12..f89e424 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -47,10 +47,8 @@ Screen::~Screen() {
 }
 
 Graphics::Surface *Screen::allocSurface(int16 width, int16 height) {
-	// TODO Use screen pixel format?
-	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
 	Graphics::Surface *surface = new Graphics::Surface();
-	surface->create(width, height, pixelFormat16);
+	surface->create(width, height, _vm->_system->getScreenFormat());
 	return surface; 
 }
 


Commit: 54bce3d37a852e0d3a674277ff09212a7adfc007
    https://github.com/scummvm/scummvm/commit/54bce3d37a852e0d3a674277ff09212a7adfc007
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move SpriteDecompressQueue to screen

Changed paths:
  R engines/illusions/spritedecompressqueue.cpp
  R engines/illusions/spritedecompressqueue.h
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h


diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 6a31657..6933553 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -27,7 +27,6 @@ MODULE_OBJS := \
 	sequenceopcodes.o \
 	soundresource.o \
 	specialcode.o \
-	spritedecompressqueue.o \
 	spritedrawqueue.o \
 	talkresource.o \
 	talkthread.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index f89e424..aa5b81c 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -24,10 +24,116 @@
 #include "illusions/screen.h"
 #include "illusions/graphics.h"
 #include "illusions/spritedrawqueue.h"
-#include "illusions/spritedecompressqueue.h"
 
 namespace Illusions {
 
+// SpriteDecompressQueue
+
+SpriteDecompressQueue::SpriteDecompressQueue() {
+}
+
+SpriteDecompressQueue::~SpriteDecompressQueue() {
+}
+
+void SpriteDecompressQueue::insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
+	byte *compressedPixels, Graphics::Surface *surface) {
+	SpriteDecompressQueueItem *item = new SpriteDecompressQueueItem();
+	item->_drawFlags = drawFlags;
+	*item->_drawFlags &= 1;
+	item->_flags = flags;
+	item->_dimensions = dimensions;
+	item->_compressedPixels = compressedPixels;
+	item->_field8 = field8;
+	item->_surface = surface;
+	_queue.push_back(item);
+}
+
+void SpriteDecompressQueue::decompressAll() {
+	SpriteDecompressQueueListIterator it = _queue.begin();
+	while (it != _queue.end()) {
+		decompress(*it);
+		delete *it;
+		it = _queue.erase(it);
+	}
+}
+
+void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
+	byte *src = item->_compressedPixels;
+	Graphics::Surface *dstSurface = item->_surface;
+	int dstSize = item->_dimensions._width * item->_dimensions._height;
+	int processedSize = 0;
+	int xincr, x, xstart;
+	int yincr, y;
+	
+	// Safeguard
+	if (item->_dimensions._width > item->_surface->w ||
+		item->_dimensions._height > item->_surface->h) {
+		debug("Incorrect frame dimensions (%d, %d <> %d, %d)",
+			item->_dimensions._width, item->_dimensions._height,
+			item->_surface->w, item->_surface->h);
+		return;
+	}
+	
+	if (item->_flags & 1) {
+		x = xstart = item->_dimensions._width - 1;
+		xincr = -1;
+	} else {
+		x = xstart = 0;
+		xincr = 1;
+	}
+
+	if (item->_flags & 2) {
+		y = item->_dimensions._height - 1;
+		yincr = -1;
+	} else {
+		y = 0;
+		yincr = 1;
+	}
+	
+	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
+	
+	while (processedSize < dstSize) {
+		int16 op = READ_LE_UINT16(src);
+		src += 2;
+		if (op & 0x8000) {
+			int runCount = (op & 0x7FFF) + 1;
+			processedSize += runCount;
+			uint16 runColor = READ_LE_UINT16(src);
+			src += 2;
+			while (runCount--) {
+				WRITE_LE_UINT16(dst, runColor);
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += 2 * xincr;
+				}
+			}
+		} else {
+			int copyCount = op + 1;
+			processedSize += copyCount;
+			while (copyCount--) {
+				uint16 color = READ_LE_UINT16(src);
+				src += 2;
+				WRITE_LE_UINT16(dst, color);
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += 2 * xincr;
+				}
+			}
+		}
+	}
+
+	*item->_drawFlags &= ~1;
+ 
+}
+
 // Screen
 
 Screen::Screen(IllusionsEngine *vm)
@@ -92,7 +198,6 @@ void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface,
 	for (int16 yc = 0; yc < h; ++yc) {
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcRect.top + yc);
 		byte *dst = (byte*)_backSurface->getBasePtr(destX, destY + yc);
-		//memcpy(dst, src, w * 2);
 		for (int16 xc = 0; xc < w; ++xc) {
 			uint16 pixel = READ_LE_UINT16(src);
 			if (pixel != _colorKey1)
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 8fa95ec..33a0de3 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -24,12 +24,36 @@
 #define ILLUSIONS_SCREEN_H
 
 #include "illusions/spritedrawqueue.h"
-#include "illusions/spritedecompressqueue.h"
+#include "common/list.h"
 #include "graphics/surface.h"
 
 namespace Illusions {
 
 class IllusionsEngine;
+class Screen;
+
+struct SpriteDecompressQueueItem {
+	byte *_drawFlags;
+	uint32 _flags;
+	uint32 _field8;
+	WidthHeight _dimensions;
+	byte *_compressedPixels;
+	Graphics::Surface *_surface;
+};
+
+class SpriteDecompressQueue {
+public:
+	SpriteDecompressQueue();
+	~SpriteDecompressQueue();
+	void insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
+		byte *compressedPixels, Graphics::Surface *surface);
+	void decompressAll();
+protected:
+	typedef Common::List<SpriteDecompressQueueItem*> SpriteDecompressQueueList;
+	typedef SpriteDecompressQueueList::iterator SpriteDecompressQueueListIterator;
+	SpriteDecompressQueueList _queue;
+	void decompress(SpriteDecompressQueueItem *item);
+};
 
 class Screen {
 public:
diff --git a/engines/illusions/spritedecompressqueue.cpp b/engines/illusions/spritedecompressqueue.cpp
deleted file mode 100644
index c20b0db..0000000
--- a/engines/illusions/spritedecompressqueue.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/spritedecompressqueue.h"
-
-namespace Illusions {
-
-SpriteDecompressQueue::SpriteDecompressQueue() {
-}
-
-SpriteDecompressQueue::~SpriteDecompressQueue() {
-}
-
-void SpriteDecompressQueue::insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
-	byte *compressedPixels, Graphics::Surface *surface) {
-	SpriteDecompressQueueItem *item = new SpriteDecompressQueueItem();
-	item->_drawFlags = drawFlags;
-	*item->_drawFlags &= 1;
-	item->_flags = flags;
-	item->_dimensions = dimensions;
-	item->_compressedPixels = compressedPixels;
-	item->_field8 = field8;
-	item->_surface = surface;
-	_queue.push_back(item);
-}
-
-void SpriteDecompressQueue::decompressAll() {
-	SpriteDecompressQueueListIterator it = _queue.begin();
-	while (it != _queue.end()) {
-		decompress(*it);
-		delete *it;
-		it = _queue.erase(it);
-	}
-}
-
-void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
-	byte *src = item->_compressedPixels;
-	Graphics::Surface *dstSurface = item->_surface;
-	int dstSize = item->_dimensions._width * item->_dimensions._height;
-	int processedSize = 0;
-	int xincr, x, xstart;
-	int yincr, y;
-	
-	// Safeguard
-	if (item->_dimensions._width > item->_surface->w ||
-		item->_dimensions._height > item->_surface->h) {
-		debug("Incorrect frame dimensions (%d, %d <> %d, %d)",
-			item->_dimensions._width, item->_dimensions._height,
-			item->_surface->w, item->_surface->h);
-		return;
-	}
-	
-	if (item->_flags & 1) {
-		x = xstart = item->_dimensions._width - 1;
-		xincr = -1;
-	} else {
-		x = xstart = 0;
-		xincr = 1;
-	}
-
-	if (item->_flags & 2) {
-		y = item->_dimensions._height - 1;
-		yincr = -1;
-	} else {
-		y = 0;
-		yincr = 1;
-	}
-	
-	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
-	
-	while (processedSize < dstSize) {
-		int16 op = READ_LE_UINT16(src);
-		src += 2;
-		if (op & 0x8000) {
-			int runCount = (op & 0x7FFF) + 1;
-			processedSize += runCount;
-			uint16 runColor = READ_LE_UINT16(src);
-			src += 2;
-			while (runCount--) {
-				WRITE_LE_UINT16(dst, runColor);
-				x += xincr;
-				if (x >= item->_dimensions._width || x < 0) {
-					x = xstart;
-					y += yincr;
-					dst = (byte*)dstSurface->getBasePtr(x, y);
-				} else {
-					dst += 2 * xincr;
-				}
-			}
-		} else {
-			int copyCount = op + 1;
-			processedSize += copyCount;
-			while (copyCount--) {
-				uint16 color = READ_LE_UINT16(src);
-				src += 2;
-				WRITE_LE_UINT16(dst, color);
-				x += xincr;
-				if (x >= item->_dimensions._width || x < 0) {
-					x = xstart;
-					y += yincr;
-					dst = (byte*)dstSurface->getBasePtr(x, y);
-				} else {
-					dst += 2 * xincr;
-				}
-			}
-		}
-	}
-
-	*item->_drawFlags &= ~1;
- 
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/spritedecompressqueue.h b/engines/illusions/spritedecompressqueue.h
deleted file mode 100644
index ede1cf8..0000000
--- a/engines/illusions/spritedecompressqueue.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SPRITEDECOMPRESSQUEUE_H
-#define ILLUSIONS_SPRITEDECOMPRESSQUEUE_H
-
-#include "illusions/illusions.h"
-#include "illusions/graphics.h"
-#include "common/list.h"
-#include "graphics/surface.h"
-
-namespace Illusions {
-
-struct SpriteDecompressQueueItem {
-	byte *_drawFlags;
-	uint32 _flags;
-	uint32 _field8;
-	WidthHeight _dimensions;
-	byte *_compressedPixels;
-	Graphics::Surface *_surface;
-};
-
-class SpriteDecompressQueue {
-public:
-	SpriteDecompressQueue();
-	~SpriteDecompressQueue();
-	void insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
-		byte *compressedPixels, Graphics::Surface *surface);
-	void decompressAll();
-protected:
-	typedef Common::List<SpriteDecompressQueueItem*> SpriteDecompressQueueList;
-	typedef SpriteDecompressQueueList::iterator SpriteDecompressQueueListIterator;
-	
-	SpriteDecompressQueueList _queue;
-	
-	void decompress(SpriteDecompressQueueItem *item);
-
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SPRITEDRAWQUEUE_H


Commit: 6ba5570de52344da602cf02fb83725b468e3d8c5
    https://github.com/scummvm/scummvm/commit/6ba5570de52344da602cf02fb83725b468e3d8c5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move SpriteDrawQueue to screen

Changed paths:
  R engines/illusions/spritedrawqueue.cpp
  R engines/illusions/spritedrawqueue.h
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h


diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 6933553..c4fe62b 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -27,7 +27,6 @@ MODULE_OBJS := \
 	sequenceopcodes.o \
 	soundresource.o \
 	specialcode.o \
-	spritedrawqueue.o \
 	talkresource.o \
 	talkthread.o \
 	thread.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index aa5b81c..de2efae 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -22,8 +22,6 @@
 
 #include "illusions/illusions.h"
 #include "illusions/screen.h"
-#include "illusions/graphics.h"
-#include "illusions/spritedrawqueue.h"
 
 namespace Illusions {
 
@@ -134,6 +132,170 @@ void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
  
 }
 
+// SpriteDrawQueue
+
+SpriteDrawQueue::SpriteDrawQueue(Screen *screen)
+	: _screen(screen) {
+}
+
+SpriteDrawQueue::~SpriteDrawQueue() {
+}
+
+bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
+
+	// Check if the sprite has finished decompressing
+	if (item->_kind != 0 && (*item->_drawFlags & 1)) {		
+		insert(item, item->_priority);
+		return false;
+	}
+
+	if (!_screen->isDisplayOn()) {
+		if (item->_drawFlags)
+			*item->_drawFlags &= ~4;
+		return true;			
+	}	
+
+	Common::Rect srcRect, dstRect;
+	
+	// Check if the sprite is on-screen
+	if (!calcItemRect(item, srcRect, dstRect))
+		return true;
+
+	if (item->_scale == 100) {
+		if (item->_flags & 1)
+			_screen->drawSurface10(dstRect.left, dstRect.top, item->_surface, srcRect, _screen->getColorKey2());
+		else
+			_screen->drawSurface11(dstRect.left, dstRect.top, item->_surface, srcRect);
+	} else {
+		if (item->_flags & 1)
+			_screen->drawSurface20(dstRect, item->_surface, srcRect, _screen->getColorKey2());
+		else
+			_screen->drawSurface21(dstRect, item->_surface, srcRect);
+	}
+	
+	if (item->_drawFlags)
+		*item->_drawFlags &= ~4;
+
+	return true;
+}
+
+void SpriteDrawQueue::drawAll() {
+	SpriteDrawQueueListIterator it = _queue.begin();
+	while (it != _queue.end()) {
+		if (draw(*it)) {
+			delete *it;
+			it = _queue.erase(it);
+		} else
+			++it;
+	}
+}
+
+void SpriteDrawQueue::insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
+	Common::Point &drawPosition, Common::Point &controlPosition, uint32 priority, int16 scale, uint16 flags) {
+	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
+	item->_drawFlags = drawFlags;
+	*item->_drawFlags &= 4;
+	item->_surface = surface;
+	item->_dimensions = dimensions;
+	item->_controlPosition = controlPosition;
+	item->_scale = scale;
+	item->_priority = priority;
+	item->_drawPosition = drawPosition;
+	item->_kind = 1;
+	item->_flags = flags;
+	insert(item, priority);
+}
+
+void SpriteDrawQueue::insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+	Common::Point &drawPosition, uint32 priority) {
+	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
+	item->_surface = surface;
+	item->_dimensions = dimensions;
+	item->_drawFlags = 0;
+	item->_kind = 0;
+	item->_drawPosition.x = -drawPosition.x;
+	item->_drawPosition.y = -drawPosition.y;
+	item->_controlPosition.x = 0;
+	item->_controlPosition.y = 0;
+	item->_flags = 0;
+	item->_scale = 100;
+	item->_priority = priority;// << 16;
+	insert(item, priority);
+}
+
+void SpriteDrawQueue::insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+	Common::Point &drawPosition, uint32 priority) {
+	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
+	item->_surface = surface;
+	item->_drawPosition = drawPosition;
+	item->_dimensions = dimensions;
+	item->_drawFlags = 0;
+	item->_kind = 0;
+	item->_controlPosition.x = 0;
+	item->_controlPosition.y = 0;
+	item->_flags = 0;
+	item->_priority = priority;
+	item->_scale = 100;
+	insert(item, priority);
+}
+
+void SpriteDrawQueue::insert(SpriteDrawQueueItem *item, uint32 priority) {
+	SpriteDrawQueueListIterator insertionPos = Common::find_if(_queue.begin(), _queue.end(),
+		FindInsertionPosition(priority));
+	_queue.insert(insertionPos, item);
+}
+
+bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect) {
+
+	srcRect.left = 0;
+	srcRect.top = 0;
+	srcRect.right = item->_dimensions._width;
+	srcRect.bottom = item->_dimensions._height;
+	
+	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
+	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
+	dstRect.right = item->_drawPosition.x + item->_scale * (item->_dimensions._width - item->_controlPosition.x) / 100;
+	dstRect.bottom = item->_drawPosition.y + item->_scale * (item->_dimensions._height - item->_controlPosition.y) / 100;
+	
+	/* CHECKME This seems to be unused basically and only called from debug code
+		Left here just in case...
+	if (gfx_seemsAlways0) {
+		dstRect.left += screenOffsetPt.x;
+		dstRect.right = screenOffsetPt.x + dstRect.right;
+		dstRect.top = screenOffsetPt.y + dstRect.top;
+		dstRect.bottom = screenOffsetPt.y + dstRect.bottom;
+	}
+	*/
+
+	// Check if the sprite is on-screen
+	if (dstRect.left >= 640 || dstRect.right <= 0 || dstRect.top >= 480 || dstRect.bottom <= 0)
+		return false;
+
+	// Clip the sprite rect if neccessary
+
+	if (dstRect.left < 0) {
+		srcRect.left += -100 * dstRect.left / item->_scale;
+		dstRect.left = 0;
+	}
+
+	if (dstRect.top < 0) {
+		srcRect.top += -100 * dstRect.top / item->_scale;
+		dstRect.top = 0;
+	}
+
+	if (dstRect.right > 640) {
+		srcRect.right += 100 * (640 - dstRect.right) / item->_scale;
+		dstRect.right = 640;
+	}
+
+	if (dstRect.bottom > 480) {
+		srcRect.bottom += 100 * (480 - dstRect.bottom) / item->_scale;
+		dstRect.bottom = 480;
+	}
+
+	return true;
+}
+
 // Screen
 
 Screen::Screen(IllusionsEngine *vm)
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 33a0de3..6332f78 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -23,8 +23,9 @@
 #ifndef ILLUSIONS_SCREEN_H
 #define ILLUSIONS_SCREEN_H
 
-#include "illusions/spritedrawqueue.h"
+#include "illusions/graphics.h"
 #include "common/list.h"
+#include "common/rect.h"
 #include "graphics/surface.h"
 
 namespace Illusions {
@@ -55,6 +56,46 @@ protected:
 	void decompress(SpriteDecompressQueueItem *item);
 };
 
+struct SpriteDrawQueueItem {
+	byte *_drawFlags;
+	int16 _kind;
+	int16 _scale;
+	uint16 _flags;
+	uint32 _priority;
+	Graphics::Surface *_surface;
+	WidthHeight _dimensions;
+	Common::Point _drawPosition;
+	Common::Point _controlPosition;
+};
+
+class SpriteDrawQueue {
+public:
+	SpriteDrawQueue(Screen *screen);
+	~SpriteDrawQueue();
+	bool draw(SpriteDrawQueueItem *item);
+	void drawAll();
+	void insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
+		Common::Point &drawPosition, Common::Point &controlPosition, uint32 priority, int16 scale, uint16 flags);
+	void insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+		Common::Point &drawPosition, uint32 priority);
+	void insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
+		Common::Point &drawPosition, uint32 priority);
+protected:
+	typedef Common::List<SpriteDrawQueueItem*> SpriteDrawQueueList;
+	typedef SpriteDrawQueueList::iterator SpriteDrawQueueListIterator;
+	struct FindInsertionPosition : public Common::UnaryFunction<const SpriteDrawQueueItem*, bool> {
+		uint32 _priority;
+		FindInsertionPosition(uint32 priority) : _priority(priority) {}
+		bool operator()(const SpriteDrawQueueItem *item) const {
+			return item->_priority >= _priority;
+		}
+	};
+	Screen *_screen;
+	SpriteDrawQueueList _queue;	
+	void insert(SpriteDrawQueueItem *item, uint32 priority);
+	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
+};
+
 class Screen {
 public:
 	Screen(IllusionsEngine *vm);
diff --git a/engines/illusions/spritedrawqueue.cpp b/engines/illusions/spritedrawqueue.cpp
deleted file mode 100644
index d5a7394..0000000
--- a/engines/illusions/spritedrawqueue.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/spritedrawqueue.h"
-#include "illusions/screen.h"
-
-namespace Illusions {
-
-SpriteDrawQueue::SpriteDrawQueue(Screen *screen)
-	: _screen(screen) {
-}
-
-SpriteDrawQueue::~SpriteDrawQueue() {
-}
-
-bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
-
-	// Check if the sprite has finished decompressing
-	if (item->_kind != 0 && (*item->_drawFlags & 1)) {		
-		insert(item, item->_priority);
-		return false;
-	}
-
-	if (!_screen->isDisplayOn()) {
-		if (item->_drawFlags)
-			*item->_drawFlags &= ~4;
-		return true;			
-	}	
-
-	Common::Rect srcRect, dstRect;
-	
-	// Check if the sprite is on-screen
-	if (!calcItemRect(item, srcRect, dstRect))
-		return true;
-
-	if (item->_scale == 100) {
-		if (item->_flags & 1)
-			_screen->drawSurface10(dstRect.left, dstRect.top, item->_surface, srcRect, _screen->getColorKey2());
-		else
-			_screen->drawSurface11(dstRect.left, dstRect.top, item->_surface, srcRect);
-	} else {
-		if (item->_flags & 1)
-			_screen->drawSurface20(dstRect, item->_surface, srcRect, _screen->getColorKey2());
-		else
-			_screen->drawSurface21(dstRect, item->_surface, srcRect);
-	}
-	
-	if (item->_drawFlags)
-		*item->_drawFlags &= ~4;
-
-	return true;
-}
-
-void SpriteDrawQueue::drawAll() {
-	SpriteDrawQueueListIterator it = _queue.begin();
-	while (it != _queue.end()) {
-		if (draw(*it)) {
-			delete *it;
-			it = _queue.erase(it);
-		} else
-			++it;
-	}
-}
-
-void SpriteDrawQueue::insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
-	Common::Point &drawPosition, Common::Point &controlPosition, uint32 priority, int16 scale, uint16 flags) {
-	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
-	item->_drawFlags = drawFlags;
-	*item->_drawFlags &= 4;
-	item->_surface = surface;
-	item->_dimensions = dimensions;
-	item->_controlPosition = controlPosition;
-	item->_scale = scale;
-	item->_priority = priority;
-	item->_drawPosition = drawPosition;
-	item->_kind = 1;
-	item->_flags = flags;
-	insert(item, priority);
-}
-
-void SpriteDrawQueue::insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-	Common::Point &drawPosition, uint32 priority) {
-	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
-	item->_surface = surface;
-	item->_dimensions = dimensions;
-	item->_drawFlags = 0;
-	item->_kind = 0;
-	item->_drawPosition.x = -drawPosition.x;
-	item->_drawPosition.y = -drawPosition.y;
-	item->_controlPosition.x = 0;
-	item->_controlPosition.y = 0;
-	item->_flags = 0;
-	item->_scale = 100;
-	item->_priority = priority;// << 16;
-	insert(item, priority);
-}
-
-void SpriteDrawQueue::insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-	Common::Point &drawPosition, uint32 priority) {
-	SpriteDrawQueueItem *item = new SpriteDrawQueueItem();
-	item->_surface = surface;
-	item->_drawPosition = drawPosition;
-	item->_dimensions = dimensions;
-	item->_drawFlags = 0;
-	item->_kind = 0;
-	item->_controlPosition.x = 0;
-	item->_controlPosition.y = 0;
-	item->_flags = 0;
-	item->_priority = priority;
-	item->_scale = 100;
-	insert(item, priority);
-}
-
-void SpriteDrawQueue::insert(SpriteDrawQueueItem *item, uint32 priority) {
-	SpriteDrawQueueListIterator insertionPos = Common::find_if(_queue.begin(), _queue.end(),
-		FindInsertionPosition(priority));
-	_queue.insert(insertionPos, item);
-}
-
-bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect) {
-
-	srcRect.left = 0;
-	srcRect.top = 0;
-	srcRect.right = item->_dimensions._width;
-	srcRect.bottom = item->_dimensions._height;
-	
-	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
-	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
-	dstRect.right = item->_drawPosition.x + item->_scale * (item->_dimensions._width - item->_controlPosition.x) / 100;
-	dstRect.bottom = item->_drawPosition.y + item->_scale * (item->_dimensions._height - item->_controlPosition.y) / 100;
-	
-	/* CHECKME This seems to be unused basically and only called from debug code
-		Left here just in case...
-	if (gfx_seemsAlways0) {
-		dstRect.left += screenOffsetPt.x;
-		dstRect.right = screenOffsetPt.x + dstRect.right;
-		dstRect.top = screenOffsetPt.y + dstRect.top;
-		dstRect.bottom = screenOffsetPt.y + dstRect.bottom;
-	}
-	*/
-
-	// Check if the sprite is on-screen
-	if (dstRect.left >= 640 || dstRect.right <= 0 || dstRect.top >= 480 || dstRect.bottom <= 0)
-		return false;
-
-	// Clip the sprite rect if neccessary
-
-	if (dstRect.left < 0) {
-		srcRect.left += -100 * dstRect.left / item->_scale;
-		dstRect.left = 0;
-	}
-
-	if (dstRect.top < 0) {
-		srcRect.top += -100 * dstRect.top / item->_scale;
-		dstRect.top = 0;
-	}
-
-	if (dstRect.right > 640) {
-		srcRect.right += 100 * (640 - dstRect.right) / item->_scale;
-		dstRect.right = 640;
-	}
-
-	if (dstRect.bottom > 480) {
-		srcRect.bottom += 100 * (480 - dstRect.bottom) / item->_scale;
-		dstRect.bottom = 480;
-	}
-
-	return true;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/spritedrawqueue.h b/engines/illusions/spritedrawqueue.h
deleted file mode 100644
index baa4217..0000000
--- a/engines/illusions/spritedrawqueue.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SPRITEDRAWQUEUE_H
-#define ILLUSIONS_SPRITEDRAWQUEUE_H
-
-#include "illusions/illusions.h"
-#include "illusions/graphics.h"
-#include "common/list.h"
-#include "common/rect.h"
-#include "graphics/surface.h"
-
-namespace Illusions {
-
-class Screen;
-
-struct SpriteDrawQueueItem {
-	byte *_drawFlags;
-	int16 _kind;
-	int16 _scale;
-	uint16 _flags;
-	//field_A dw
-	uint32 _priority;
-	Graphics::Surface *_surface;
-	WidthHeight _dimensions;
-	Common::Point _drawPosition;
-	Common::Point _controlPosition;
-};
-
-class SpriteDrawQueue {
-public:
-	SpriteDrawQueue(Screen *screen);
-	~SpriteDrawQueue();
-	bool draw(SpriteDrawQueueItem *item);
-	void drawAll();
-	void insertSprite(byte *drawFlags, Graphics::Surface *surface, WidthHeight &dimensions,
-		Common::Point &drawPosition, Common::Point &controlPosition, uint32 priority, int16 scale, uint16 flags);
-	void insertSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-		Common::Point &drawPosition, uint32 priority);
-	void insertTextSurface(Graphics::Surface *surface, WidthHeight &dimensions,
-		Common::Point &drawPosition, uint32 priority);
-protected:
-	typedef Common::List<SpriteDrawQueueItem*> SpriteDrawQueueList;
-	typedef SpriteDrawQueueList::iterator SpriteDrawQueueListIterator;
-
-	struct FindInsertionPosition : public Common::UnaryFunction<const SpriteDrawQueueItem*, bool> {
-		uint32 _priority;
-		FindInsertionPosition(uint32 priority) : _priority(priority) {}
-		bool operator()(const SpriteDrawQueueItem *item) const {
-			return item->_priority >= _priority;
-		}
-	};
-
-	Screen *_screen;
-	SpriteDrawQueueList _queue;	
-	void insert(SpriteDrawQueueItem *item, uint32 priority);
-	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SPRITEDRAWQUEUE_H


Commit: 3f15233f78a264e45ce74ecd2db9ed3119b1b654
    https://github.com/scummvm/scummvm/commit/3f15233f78a264e45ce74ecd2db9ed3119b1b654
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Refactor code in preparation for the Duckman engine

Changed paths:
  A engines/illusions/illusions_bbdou.cpp
  A engines/illusions/illusions_bbdou.h
  A engines/illusions/scriptopcodes_bbdou.cpp
  A engines/illusions/scriptopcodes_bbdou.h
    engines/illusions/abortablethread.cpp
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_bubble.h
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/detection.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptman.cpp
    engines/illusions/scriptman.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptthread.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/specialcode.cpp
    engines/illusions/specialcode.h
    engines/illusions/talkthread.cpp
    engines/illusions/timerthread.cpp


diff --git a/engines/illusions/abortablethread.cpp b/engines/illusions/abortablethread.cpp
index 493b81e..5f46ca8 100644
--- a/engines/illusions/abortablethread.cpp
+++ b/engines/illusions/abortablethread.cpp
@@ -35,7 +35,7 @@ AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 ca
 	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId), 
 	_scriptCodeIp(scriptCodeIp), _status(1) {
 	_type = kTTAbortableThread;
-	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_tag = _vm->getCurrentScene();
 	_vm->_input->discardButtons(8);
 }
 
@@ -43,9 +43,9 @@ int AbortableThread::onUpdate() {
 	if (_status != 1 || _pauseCtr < 0)
 		return kTSTerminate;
 	if (_vm->_input->pollButton(8)) {
-		_vm->_scriptMan->_threads->killThread(_scriptThreadId);
+		_vm->_threads->killThread(_scriptThreadId);
 		++_pauseCtr;
-		_vm->_scriptMan->startTempScriptThread(_scriptCodeIp, _threadId, 0, 0, 0);
+		_vm->startTempScriptThread(_scriptCodeIp, _threadId, 0, 0, 0);
 		_status = 2;
 		return kTSSuspend;
 	}
diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 97c8dc0..1d9aae0 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -215,7 +215,7 @@ Control::Control(IllusionsEngine *vm)
 	_position.y = 0;
 	_actorTypeId = 0;
 	_actor = 0;
-	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_tag = _vm->getCurrentScene();
 }
 
 Control::~Control() {
@@ -574,7 +574,7 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 				subControl->_actor->_notifyThreadId2 = threadId;
 				subControl->_actor->_entryTblPtr = entryTblPtr;
 				subControl->_actor->_flags |= 0x80;
-				Thread *thread = _vm->_scriptMan->_threads->findThread(threadId);
+				Thread *thread = _vm->_threads->findThread(threadId);
 				thread->sendMessage(kMsgClearSequenceId2, 0);
 			}
 		}
@@ -680,7 +680,7 @@ void Control::stopSubSequence(int linkIndex) {
 		_actor->_notifyThreadId2 = 0;
 	}
 	if (notifyThreadId2) {
-		Thread *talkThread = _vm->_scriptMan->_threads->findThread(notifyThreadId2);
+		Thread *talkThread = _vm->_threads->findThread(notifyThreadId2);
 		talkThread->sendMessage(kMsgClearSequenceId2, 0);
 	}
 }
diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index cc6019c..7ccd0ea 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_bubble.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -29,7 +29,7 @@
 
 namespace Illusions {
 
-BbdouBubble::BbdouBubble(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
+BbdouBubble::BbdouBubble(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
 	: _vm(vm), _bbdou(bbdou) {
 }
 
diff --git a/engines/illusions/bbdou/bbdou_bubble.h b/engines/illusions/bbdou/bbdou_bubble.h
index 2426c9e..fcf5f51 100644
--- a/engines/illusions/bbdou/bbdou_bubble.h
+++ b/engines/illusions/bbdou/bbdou_bubble.h
@@ -28,7 +28,7 @@
 
 namespace Illusions {
 
-class IllusionsEngine;
+class IllusionsEngine_BBDOU;
 class BbdouSpecialCode;
 class Control;
 
@@ -54,7 +54,7 @@ struct Item141C {
 
 class BbdouBubble {
 public:
-	BbdouBubble(IllusionsEngine *vm, BbdouSpecialCode *bbdou);
+	BbdouBubble(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
 	~BbdouBubble();
 	void init();
 	void addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progResKeywordId,
@@ -64,7 +64,7 @@ public:
 	void setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId);
 	uint32 addItem(uint positionIndex, uint32 sequenceId);
 protected:
-	IllusionsEngine *_vm;
+	IllusionsEngine_BBDOU *_vm;
 	BbdouSpecialCode *_bbdou;
 	Common::Array<Item0> _item0s;
 	Item0 *_currItem0;
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 4aca265..c158472 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/actor.h"
@@ -33,7 +33,7 @@ namespace Illusions {
 // NOTE It's assumed there's only one game cursor object
 // The original stores the _data inside the actor, here it's inside the Cursor class.
 
-BbdouCursor::BbdouCursor(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
+BbdouCursor::BbdouCursor(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
 	: _vm(vm), _bbdou(bbdou) {
 }
 
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index 43918e8..7b42e4f 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -27,7 +27,7 @@
 
 namespace Illusions {
 
-class IllusionsEngine;
+class IllusionsEngine_BBDOU;
 class BbdouSpecialCode;
 class Control;
 struct Item10;
@@ -85,7 +85,7 @@ const uint kMaxCursorSequences = 100;
 
 class BbdouCursor {
 public:
-	BbdouCursor(IllusionsEngine *vm, BbdouSpecialCode *bbdou);
+	BbdouCursor(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
 	~BbdouCursor();
 	void init(uint32 objectId, uint32 progResKeywordId);
 	void enable(uint32 objectId);
@@ -104,7 +104,7 @@ public:
 	uint calcTrackingCursorIndex(uint trackingFlags);
 	bool getTrackingCursorSequenceId(Control *control, uint32 &outSequenceId);
 public:
-	IllusionsEngine *_vm;
+	IllusionsEngine_BBDOU *_vm;
 	BbdouSpecialCode *_bbdou;
 	Control *_control;
 	CursorData _data;
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 26265a7..c2ffd99 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_inventory.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
@@ -49,7 +49,7 @@ InventorySlot::InventorySlot(uint32 namedPointId)
 
 // InventoryBag
 
-InventoryBag::InventoryBag(IllusionsEngine *vm, uint32 sceneId)
+InventoryBag::InventoryBag(IllusionsEngine_BBDOU *vm, uint32 sceneId)
 	: _vm(vm), _sceneId(sceneId), _isActive(false), _fieldA(0) {
 }
 
@@ -116,7 +116,7 @@ InventorySlot *InventoryBag::findClosestSlot(Common::Point putPos, int index) {
 
 // BbdouInventory
 
-BbdouInventory::BbdouInventory(IllusionsEngine *vm, BbdouSpecialCode *bbdou)
+BbdouInventory::BbdouInventory(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
 	: _vm(vm), _bbdou(bbdou) {
 }
 
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index e4ee28f..2784347 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -29,7 +29,7 @@
 
 namespace Illusions {
 
-class IllusionsEngine;
+class IllusionsEngine_BBDOU;
 class BbdouSpecialCode;
 class Control;
 class TriggerFunction;
@@ -53,7 +53,7 @@ struct InventorySlot {
 
 class InventoryBag {
 public:
-	InventoryBag(IllusionsEngine *vm, uint32 sceneId);
+	InventoryBag(IllusionsEngine_BBDOU *vm, uint32 sceneId);
 	void registerInventorySlot(uint32 namedPointId);
 	bool addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot);
 	void removeInventoryItem(InventoryItem *inventoryItem);
@@ -64,7 +64,7 @@ protected:
 public:
 	typedef Common::Array<InventorySlot*> InventorySlots;
 	typedef InventorySlots::iterator InventorySlotsIterator;
-	IllusionsEngine *_vm;
+	IllusionsEngine_BBDOU *_vm;
 	uint32 _sceneId;
 	InventorySlots _inventorySlots;
 	bool _isActive;
@@ -73,7 +73,7 @@ public:
 
 class BbdouInventory {
 public:
-	BbdouInventory(IllusionsEngine *vm, BbdouSpecialCode *bbdou);
+	BbdouInventory(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
 	void registerInventoryBag(uint32 sceneId);
 	void registerInventoryItem(uint32 objectId, uint32 sequenceId);
 	void registerInventorySlot(uint32 namedPointId);
@@ -92,7 +92,7 @@ public:
 protected:
 	typedef Common::Array<InventoryItem*> InventoryItems;
 	typedef InventoryItems::iterator InventoryItemsIterator;
-	IllusionsEngine *_vm;
+	IllusionsEngine_BBDOU *_vm;
 	BbdouSpecialCode *_bbdou;
 	Common::Array<InventoryBag*> _inventoryBags;
 	InventoryItems _inventoryItems;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index f4c23bb..8011b3e 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/bbdou/bbdou_bubble.h"
 #include "illusions/bbdou/bbdou_inventory.h"
@@ -50,7 +50,7 @@ static const Struct10 kStruct10s[] = {
 	{0x1B000C,       0,       0,       0},
 };
 
-CauseThread::CauseThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId,
+CauseThread::CauseThread(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
 	BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId, uint32 verbId,
 	uint32 objectId2, uint32 objectId)
 	: Thread(vm, threadId, callingThreadId, 0), _bbdou(bbdou), _cursorObjectId(cursorObjectId),
@@ -70,8 +70,8 @@ void CauseThread::onTerminated() {
 
 // BbdouSpecialCode
 
-BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine *vm)
-	: SpecialCode(vm) {
+BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine_BBDOU *vm)
+	: _vm(vm) {
 	_bubble = new BbdouBubble(_vm, this);
 	_cursor = new BbdouCursor(_vm, this);
 	_inventory = new BbdouInventory(_vm, this);
@@ -121,11 +121,6 @@ void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
 
 // Special codes
 
-// Convenience macros
-#define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(1, "ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(1, "ARG_UINT32(" #name " = %08X)", name);
-
 void BbdouSpecialCode::spcInitCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	ARG_UINT32(progResKeywordId);
@@ -226,7 +221,7 @@ void BbdouSpecialCode::spcRemoveInventoryItem(OpCall &opCall) {
 
 void BbdouSpecialCode::spcHasInventoryItem(OpCall &opCall) {
 	ARG_UINT32(objectId);
-	_vm->_scriptMan->_stack.push(_inventory->hasInventoryItem(objectId) ? 1 : 0);
+	_vm->_stack->push(_inventory->hasInventoryItem(objectId) ? 1 : 0);
 debug("_inventory->hasInventoryItem(%08X) = %d", objectId, _inventory->hasInventoryItem(objectId));	
 }
 
@@ -237,7 +232,7 @@ void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 void BbdouSpecialCode::spcIsCursorHoldingObjectId(OpCall &opCall) {
 	ARG_UINT32(cursorObjectId);
 	ARG_UINT32(objectId);
-	_vm->_scriptMan->_stack.push(isHoldingObjectId(objectId) ? 1 : 0);
+	_vm->_stack->push(isHoldingObjectId(objectId) ? 1 : 0);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
@@ -613,7 +608,7 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 	uint32 threadId = startCauseThread(cursorControl->_objectId, _vm->getCurrentScene(), outVerbId, outObjectId2, outObjectId);
 
 	if (cursorData._field90) {
-		_vm->_scriptMan->_threads->killThread(cursorData._causeThreadId2);
+		_vm->_threads->killThread(cursorData._causeThreadId2);
 		cursorData._field90 = 0;
 	}
 
@@ -627,11 +622,11 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 }
 
 uint32 BbdouSpecialCode::startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	uint32 tempThreadId = _vm->_scriptMan->newTempThreadId();
+	uint32 tempThreadId = _vm->newTempThreadId();
 	debug(3, "Starting cause thread %08X...", tempThreadId);
 	CauseThread *causeThread = new CauseThread(_vm, tempThreadId, 0, this,
 		cursorObjectId, sceneId, verbId, objectId2, objectId);
-	_vm->_scriptMan->_threads->startThread(causeThread);
+	_vm->_threads->startThread(causeThread);
 	causeThread->suspend();
 	return tempThreadId;
 }
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index f272193..b9080c9 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -29,7 +29,7 @@
 
 namespace Illusions {
 
-class IllusionsEngine;
+class IllusionsEngine_BBDOU;
 class BbdouBubble;
 class BbdouCursor;
 class BbdouInventory;
@@ -49,7 +49,7 @@ struct Struct10 {
 
 class CauseThread : public Thread {
 public:
-	CauseThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId,
+	CauseThread(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
 		BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId,
 		uint32 verbId, uint32 objectId2, uint32 objectId);
 	virtual void onNotify();
@@ -65,13 +65,14 @@ public:
 
 class BbdouSpecialCode : public SpecialCode {
 public:
-	BbdouSpecialCode(IllusionsEngine *vm);
+	BbdouSpecialCode(IllusionsEngine_BBDOU *vm);
 	virtual ~BbdouSpecialCode();
 	virtual void init();
 	virtual void run(uint32 specialCodeId, OpCall &opCall);
 public:
 	typedef Common::HashMap<uint32, SpecialCodeFunction*> Map;
 	typedef Map::iterator MapIterator;
+	IllusionsEngine_BBDOU *_vm;
 	Map _map;
 	BbdouCursor *_cursor;
 	BbdouBubble *_bubble;
diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index 88d63b4..c42909c 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 
 #include "common/config-manager.h"
 #include "engines/advancedDetector.h"
@@ -37,18 +38,26 @@ static const PlainGameDescriptor illusionsGames[] = {
 
 namespace Illusions {
 
-static const ADGameDescription gameDescriptions[] = {
+struct IllusionsGameDescription {
+	ADGameDescription desc;
+	int gameId;
+};
+
+static const IllusionsGameDescription gameDescriptions[] = {
 	{
-		"bbdou",
-		0,
-		AD_ENTRY1s("000D0001.scr", "d0c846d5dccc5607a482c7dcbdf06973", 601980),
-		Common::EN_ANY,
-		Common::kPlatformWindows,
-		ADGF_NO_FLAGS,
-		GUIO0()
+		{
+			"bbdou",
+			0,
+			AD_ENTRY1s("000D0001.scr", "d0c846d5dccc5607a482c7dcbdf06973", 601980),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameIdBBDOU
 	},
 
-	AD_TABLE_END_MARKER
+	{AD_TABLE_END_MARKER, 0}
 };
 
 } // End of namespace Illusions
@@ -60,7 +69,7 @@ static const char * const directoryGlobs[] = {
 
 class IllusionsMetaEngine : public AdvancedMetaEngine {
 public:
-	IllusionsMetaEngine() : AdvancedMetaEngine(Illusions::gameDescriptions, sizeof(ADGameDescription), illusionsGames) {
+	IllusionsMetaEngine() : AdvancedMetaEngine(Illusions::gameDescriptions, sizeof(Illusions::IllusionsGameDescription), illusionsGames) {
 		_singleid = "illusions";
 		_maxScanDepth = 2;
 		_directoryGlobs = directoryGlobs;
@@ -88,13 +97,13 @@ bool IllusionsMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 		false;
 		/*
-	    (f == kSupportsListSaves) ||
-	    (f == kSupportsDeleteSave) ||
-	    (f == kSupportsLoadingDuringStartup) ||
-	    (f == kSavesSupportMetaInfo) ||
-	    (f == kSavesSupportThumbnail) ||
-	    (f == kSavesSupportCreationDate);
-	    */
+		(f == kSupportsListSaves) ||
+		(f == kSupportsDeleteSave) ||
+		(f == kSupportsLoadingDuringStartup) ||
+		(f == kSavesSupportMetaInfo) ||
+		(f == kSavesSupportThumbnail) ||
+		(f == kSavesSupportCreationDate);
+		*/
 }
 
 #if 0
@@ -159,8 +168,16 @@ SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target,
 #endif
 
 bool IllusionsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	if (desc) {
-		*engine = new Illusions::IllusionsEngine(syst, desc);
+	const Illusions::IllusionsGameDescription *gd = (const Illusions::IllusionsGameDescription *)desc;
+	if (gd) {
+		switch (gd->gameId) {
+		case Illusions::kGameIdBBDOU:
+			*engine = new Illusions::IllusionsEngine_BBDOU(syst, desc);
+			break;
+		default:
+			error("Unknown game id");
+			break;
+		}
 	}
 	return desc != 0;
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index a024b8d..60d9b5b 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -41,6 +41,8 @@
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
 
+#include "illusions/talkthread.h"
+
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
@@ -71,102 +73,6 @@ IllusionsEngine::~IllusionsEngine() {
 
 }
 
-Common::Error IllusionsEngine::run() {
-
-	// Init search paths
-	const Common::FSNode gameDataDir(ConfMan.get("path"));
-	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
-
-	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
-	initGraphics(640, 480, true, &pixelFormat16);
-	
-	_dict = new Dictionary();
-
-	_resSys = new ResourceSystem();
-	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
-	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
-	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
-	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
-	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
-	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
-	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
-	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
-
-	_screen = new Screen(this);
-	_input = new Input();	
-	_scriptMan = new ScriptMan(this);
-	_actorItems = new ActorItems(this);
-	_backgroundItems = new BackgroundItems(this);
-	_camera = new Camera(this);
-	_controls = new Controls(this);
-	_cursor = new Cursor(this);
-	_talkItems = new TalkItems(this);
-	_triggerFunctions = new TriggerFunctions();
-	
-	// TODO Move to own class
-	_resGetCtr = 0;
-	_unpauseControlActorFlag = false;
-	_lastUpdateTime = 0;
-	
-#if 1
-	// Actor/graphics/script test
-
-	/* TODO 0x0010000B LinkIndex 0x00060AAB 0x00060556
-	*/
-	
-	_scriptMan->_globalSceneId = 0x00010003;	
-	
-	_resSys->loadResource(0x000D0001, 0, 0);
-
-	_scriptMan->startScriptThread(0x00020004, 0, 0, 0, 0);
-	_scriptMan->_doScriptThreadInit = true;
-
-	while (!shouldQuit()) {
-		_scriptMan->_threads->updateThreads();
-		updateActors();
-		updateSequences();
-		updateGraphics();
-		_screen->updateSprites();
-		_system->updateScreen();
-		updateEvents();
-		_system->delayMillis(10);
-	}
-#endif
-
-	delete _triggerFunctions;
-	delete _talkItems;
-	delete _cursor;
-	delete _controls;
-	delete _camera;
-	delete _backgroundItems;
-	delete _actorItems;
-	delete _scriptMan;
-	delete _input;
-	delete _screen;
-	delete _resSys;
-	delete _dict;
-	
-	debug("Ok");
-	
-	return Common::kNoError;
-}
-
-bool IllusionsEngine::hasFeature(EngineFeature f) const {
-	return
-		false;
-		/*
-		(f == kSupportsRTL) ||
-		(f == kSupportsLoadingDuringRuntime) ||
-		(f == kSupportsSavingDuringRuntime);
-		*/
-}
-
 void IllusionsEngine::updateEvents() {
 	Common::Event event;
 	while (_eventMan->pollEvent(event)) {
@@ -182,20 +88,12 @@ void IllusionsEngine::updateEvents() {
 }
 
 Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
-	Control *control = _dict->getObjectControl(objectId);
+	Control *control = getObjectControl(objectId);
 	if (control && control->_actor)
 		return &control->_actor->_position;
 	return 0;
 }
 
-void IllusionsEngine::notifyThreadId(uint32 &threadId) {
-	if (threadId) {
-		uint32 tempThreadId = threadId;
-		threadId = 0;
-		_scriptMan->_threads->notifyId(tempThreadId);
-	}
-}
-
 uint32 IllusionsEngine::getElapsedUpdateTime() {
 	uint32 result = 0;
 	uint32 currTime = getCurrentTime();
@@ -301,54 +199,11 @@ int IllusionsEngine::getRandom(int max) {
 	return _random->getRandomNumber(max - 1);
 }
 
-bool IllusionsEngine::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	uint32 codeOffs;
-	bool r =
-		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
-		_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
-	debug(3, "causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
-		sceneId, verbId, objectId2, objectId, r);
-	return r;
-}
-
-void IllusionsEngine::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
-	_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
-}
-
-uint32 IllusionsEngine::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
-	uint32 codeOffs;
-	uint32 causeThreadId = 0;
-	TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
-	if (triggerFunction) {
-		triggerFunction->run(callingThreadId);
-	} else if (_scriptMan->findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
-		//debug("Run cause at %08X", codeOffs);
-		causeThreadId = _scriptMan->startTempScriptThread(_scriptMan->_scriptResource->getCode(codeOffs),
-			callingThreadId, verbId, objectId2, objectId);
-	}
-	return causeThreadId;
-}
-
 int IllusionsEngine::convertPanXCoord(int16 x) {
 	// TODO
 	return 0;
 }
 
-Common::Point IllusionsEngine::getNamedPointPosition(uint32 namedPointId) {
-	Common::Point pt;
-	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt) ||
-		_actorItems->findNamedPoint(namedPointId, pt) ||
-		_controls->findNamedPoint(namedPointId, pt))
-    	return pt;
-	// TODO
-	//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
-	return Common::Point(0, 0);
-}
-
-uint32 IllusionsEngine::getPriorityFromBase(int16 priority) {
-	return 32000000 * priority;
-}
-
 bool IllusionsEngine::calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing) {
 	facing = 0;
 	uint xd = 0, yd = 0;
@@ -413,8 +268,30 @@ bool IllusionsEngine::isVoicePlaying() {
 	return false;
 }
 
-uint32 IllusionsEngine::getCurrentScene() {
-	return _scriptMan->_activeScenes.getCurrentScene();
+void IllusionsEngine::setCurrFontId(uint32 fontId) {
+	_fontId = fontId;
+}
+
+bool IllusionsEngine::checkActiveTalkThreads() {
+	return _threads->isActiveThread(kMsgQueryTalkThreadActive);
+}
+
+uint32 IllusionsEngine::clipTextDuration(uint32 duration) {
+	switch (_field8) {
+	case 2:
+		if (duration == 0)
+			duration = 240;
+		break;
+	case 3:
+		if (duration < _fieldA)
+			duration = _fieldA;
+		break;
+	case 4:
+		if (duration > _fieldA)
+			duration = _fieldA;
+		break;
+	}
+	return duration;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 08a9f76..a9b8555 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -62,25 +62,23 @@ class Dictionary;
 class FramesList;
 class Input;
 class Screen;
+class ScriptOpcodes;
 class ScriptResource;
-class ScriptMan;
 class Sequence;
 class SpecialCode;
 class TalkItems;
-class TriggerFunctions;
-class TriggerFunction;
+class ThreadList;
 
-typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
+enum {
+	kGameIdBBDOU   = 1,
+	kGameIdDuckman = 2
+};
 
 class IllusionsEngine : public Engine {
-protected:
-	Common::Error run();
-	virtual bool hasFeature(EngineFeature f) const;
 public:
 	IllusionsEngine(OSystem *syst, const ADGameDescription *gd);
 	~IllusionsEngine();
 	const Common::String getTargetName() { return _targetName; }
-	
 private:
 	const ADGameDescription *_gameDescription;
 	Graphics::PixelFormat _pixelFormat;
@@ -93,51 +91,61 @@ public:
 
 	Screen *_screen;
 	Input *_input;
-	ScriptMan *_scriptMan;
 	ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
 	Camera *_camera;
 	Controls *_controls;
-	Cursor *_cursor;
 	TalkItems *_talkItems;
+	ScriptOpcodes *_scriptOpcodes;
 	SpecialCode *_specialCode;
-	TriggerFunctions *_triggerFunctions;
+	ThreadList *_threads;
+	Cursor *_cursor;
+	
+	ScriptResource *_scriptResource;
 	
 	int _resGetCtr;
 	uint32 _resGetTime;
 	bool _unpauseControlActorFlag;
 	uint32 _lastUpdateTime;
 
+	uint32 _fontId;
+	int _field8;
+	uint32 _fieldA, _fieldE;
+
+	int16 _menuChoiceOfs;
+
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
-	
-	void notifyThreadId(uint32 &threadId);
-	
 	uint32 getElapsedUpdateTime();
 	int updateActors();
 	int updateSequences();
 	int updateGraphics();
 	int getRandom(int max);
-
-	// TODO Move to ScriptMan?
-	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
-
 	int convertPanXCoord(int16 x);
-	Common::Point getNamedPointPosition(uint32 namedPointId);
-	uint32 getPriorityFromBase(int16 priority);
 	bool calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing);
-
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
-	
 	bool isSoundActive();
 	bool cueVoice(byte *voiceName);
 	bool isVoiceCued();
 	void startVoice(int volume, int panX);
 	void stopVoice();
 	bool isVoicePlaying();
+
+	void setCurrFontId(uint32 fontId);
+	bool checkActiveTalkThreads();
+	uint32 clipTextDuration(uint32 duration);
 	
-	uint32 getCurrentScene();	
+	virtual void loadSpecialCode(uint32 resId) = 0;
+	virtual void unloadSpecialCode(uint32 resId) = 0;
+	virtual void notifyThreadId(uint32 &threadId) = 0;
+	virtual Control *getObjectControl(uint32 objectId) = 0;
+	virtual Common::Point getNamedPointPosition(uint32 namedPointId) = 0;
+	virtual uint32 getPriorityFromBase(int16 priority) = 0;
+	virtual uint32 getPrevScene() = 0;	
+	virtual uint32 getCurrentScene() = 0;
+
+	virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
+	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10) = 0;
 
 #if 0
 
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
new file mode 100644
index 0000000..76d476a
--- /dev/null
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -0,0 +1,516 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_bbdou.h"
+#include "illusions/actor.h"
+#include "illusions/actorresource.h"
+#include "illusions/backgroundresource.h"
+#include "illusions/camera.h"
+#include "illusions/cursor.h"
+#include "illusions/dictionary.h"
+#include "illusions/fontresource.h"
+#include "illusions/graphics.h"
+#include "illusions/input.h"
+#include "illusions/resourcesystem.h"
+#include "illusions/screen.h"
+#include "illusions/scriptopcodes_bbdou.h"
+#include "illusions/scriptresource.h"
+#include "illusions/scriptman.h"
+#include "illusions/soundresource.h"
+#include "illusions/specialcode.h"
+#include "illusions/bbdou/bbdou_specialcode.h"
+#include "illusions/talkresource.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+
+#include "illusions/abortablethread.h"
+#include "illusions/scriptthread.h"
+#include "illusions/talkthread.h"
+#include "illusions/timerthread.h"
+
+#include "audio/audiostream.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/timer.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/font.h"
+#include "graphics/fontman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+// TriggerFunction
+
+TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
+	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
+}
+
+TriggerFunction::~TriggerFunction() {
+	delete _callback;
+}
+
+void TriggerFunction::run(uint32 callingThreadId) {
+	(*_callback)(this, callingThreadId);
+}
+
+// TriggerFunctions
+
+void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end()) {
+		delete *it;
+		_triggerFunctions.erase(it);
+	}
+	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
+}
+
+TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end())
+		return (*it);
+	return 0;
+}
+
+void TriggerFunctions::removeBySceneId(uint32 sceneId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	while (it != _triggerFunctions.end()) {
+		if ((*it)->_sceneId == sceneId) {
+			delete *it;
+			it = _triggerFunctions.erase(it);
+		} else
+			++it;
+	}
+}
+
+TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	for (; it != _triggerFunctions.end(); ++it) {
+		TriggerFunction *triggerFunction = *it;
+		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
+			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
+			break;
+	}
+	return it;		
+}
+
+// ActiveScenes
+
+ActiveScenes::ActiveScenes() {
+	clear();
+}
+
+void ActiveScenes::clear() {
+	_stack.clear();
+}
+
+void ActiveScenes::push(uint32 sceneId) {
+	ActiveScene activeScene;
+	activeScene._sceneId = sceneId;
+	activeScene._pauseCtr = 0;
+	_stack.push(activeScene);
+}
+
+void ActiveScenes::pop() {
+	_stack.pop();
+}
+
+void ActiveScenes::pauseActiveScene() {
+	++_stack.top()._pauseCtr;
+}
+
+void ActiveScenes::unpauseActiveScene() {
+	--_stack.top()._pauseCtr;
+}
+
+uint ActiveScenes::getActiveScenesCount() {
+	return _stack.size();
+}
+
+void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
+	if (sceneId)
+		*sceneId = _stack[index - 1]._sceneId;
+	if (pauseCtr)
+		*pauseCtr = _stack[index - 1]._pauseCtr;
+}
+
+uint32 ActiveScenes::getCurrentScene() {
+	if (_stack.size() > 0)
+		return _stack.top()._sceneId;
+	return 0;
+}
+
+bool ActiveScenes::isSceneActive(uint32 sceneId) {
+	for (uint i = 0; i < _stack.size(); ++i)
+		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
+			return true;
+	return false;
+}
+
+// IllusionsEngine_BBDOU
+
+IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const ADGameDescription *gd)
+	: IllusionsEngine(syst, gd) {
+}
+
+Common::Error IllusionsEngine_BBDOU::run() {
+
+	// Init search paths
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
+
+	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
+	initGraphics(640, 480, true, &pixelFormat16);
+	
+	_dict = new Dictionary();
+
+	_resSys = new ResourceSystem();
+	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
+	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
+	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
+	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
+	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
+
+	_screen = new Screen(this);
+	_input = new Input();	
+	_scriptMan = new ScriptMan(this);
+	_actorItems = new ActorItems(this);
+	_backgroundItems = new BackgroundItems(this);
+	_camera = new Camera(this);
+	_controls = new Controls(this);
+	_cursor = new Cursor(this);
+	_talkItems = new TalkItems(this);
+	_triggerFunctions = new TriggerFunctions();
+	_threads = new ThreadList(this);
+
+	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
+	_stack = new ScriptStack();
+	
+	// TODO Move to own class
+	_resGetCtr = 0;
+	_unpauseControlActorFlag = false;
+	_lastUpdateTime = 0;
+
+	_pauseCtr = 0;
+	_doScriptThreadInit = false;
+	_field8 = 1;
+	_fieldA = 0;
+	_fieldE = 240;
+	
+	_globalSceneId = 0x00010003;	
+	
+	_resSys->loadResource(0x000D0001, 0, 0);
+
+	startScriptThread(0x00020004, 0, 0, 0, 0);
+	_doScriptThreadInit = true;
+
+	while (!shouldQuit()) {
+		_threads->updateThreads();
+		updateActors();
+		updateSequences();
+		updateGraphics();
+		_screen->updateSprites();
+		_system->updateScreen();
+		updateEvents();
+		_system->delayMillis(10);
+	}
+
+	delete _stack;
+	delete _scriptOpcodes;
+
+	delete _threads;
+	delete _triggerFunctions;
+	delete _talkItems;
+	delete _cursor;
+	delete _controls;
+	delete _camera;
+	delete _backgroundItems;
+	delete _actorItems;
+	delete _scriptMan;
+	delete _input;
+	delete _screen;
+	delete _resSys;
+	delete _dict;
+	
+	debug("Ok");
+	
+	return Common::kNoError;
+}
+
+bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
+	return
+		false;
+		/*
+		(f == kSupportsRTL) ||
+		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsSavingDuringRuntime);
+		*/
+}
+
+bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	uint32 codeOffs;
+	bool r =
+		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
+		findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
+	debug(3, "causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
+		sceneId, verbId, objectId2, objectId, r);
+	return r;
+}
+
+void IllusionsEngine_BBDOU::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
+}
+
+uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
+	uint32 codeOffs;
+	uint32 causeThreadId = 0;
+	TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
+	if (triggerFunction) {
+		triggerFunction->run(callingThreadId);
+	} else if (findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
+		//debug("Run cause at %08X", codeOffs);
+		causeThreadId = startTempScriptThread(_scriptResource->getCode(codeOffs),
+			callingThreadId, verbId, objectId2, objectId);
+	}
+	return causeThreadId;
+}
+
+void IllusionsEngine_BBDOU::loadSpecialCode(uint32 resId) {
+	_specialCode = new BbdouSpecialCode(this);
+	_specialCode->init();
+}
+
+void IllusionsEngine_BBDOU::unloadSpecialCode(uint32 resId) {
+	delete _specialCode;
+	_specialCode = 0;
+}
+
+void IllusionsEngine_BBDOU::notifyThreadId(uint32 &threadId) {
+	if (threadId) {
+		uint32 tempThreadId = threadId;
+		threadId = 0;
+		_threads->notifyId(tempThreadId);
+	}
+}
+
+Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
+	return _dict->getObjectControl(objectId);
+}
+
+Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
+	Common::Point pt;
+	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt) ||
+		_actorItems->findNamedPoint(namedPointId, pt) ||
+		_controls->findNamedPoint(namedPointId, pt))
+		return pt;
+	// TODO
+	//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+	return Common::Point(0, 0);
+}
+
+uint32 IllusionsEngine_BBDOU::getPriorityFromBase(int16 priority) {
+	return 32000000 * priority;
+}
+
+uint32 IllusionsEngine_BBDOU::getCurrentScene() {
+	return _activeScenes.getCurrentScene();
+}
+
+uint32 IllusionsEngine_BBDOU::getPrevScene() {
+	return _prevSceneId;
+}
+
+void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
+	startScriptThread(threadId, callingThreadId, 0, 0, 0);
+}
+
+void IllusionsEngine_BBDOU::startScriptThread(uint32 threadId, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug(2, "Starting script thread %08X", threadId);
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+}
+
+void IllusionsEngine_BBDOU::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug(2, "Starting anonymous script thread %08X", threadId);
+	uint32 tempThreadId = newTempThreadId();
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+}
+
+uint32 IllusionsEngine_BBDOU::startAbortableTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, true);
+}
+
+uint32 IllusionsEngine_BBDOU::startTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, false);
+}
+
+uint32 IllusionsEngine_BBDOU::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting abortable thread %08X", tempThreadId);
+	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
+	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
+		scriptThreadId, scriptCodeIp2);
+	_threads->startThread(abortableThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
+	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
+	debug(2, "Starting talk thread");
+	uint32 tempThreadId = newTempThreadId();
+	_threads->endTalkThreadsNoNotify();
+	TalkThread *talkThread = new TalkThread(this, tempThreadId, callingThreadId, 0,
+		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
+	_threads->startThread(talkThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting temp script thread %08X", tempThreadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+	return tempThreadId;
+}
+
+void IllusionsEngine_BBDOU::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
+	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
+		scriptCodeIp, value8, valueC, value10);
+	_threads->startThread(scriptThread);
+	if (_pauseCtr > 0)
+		scriptThread->pause();
+	if (_doScriptThreadInit) {
+		int updateResult = kTSRun;
+		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
+			updateResult = scriptThread->update();
+	}
+}
+
+uint32 IllusionsEngine_BBDOU::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
+	uint32 tempThreadId = newTempThreadId();
+	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
+		duration, isAbortable);
+	_threads->startThread(timerThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_BBDOU::newTempThreadId() {
+	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
+	if (threadId > 65535) {
+		_nextTempThreadId = 0;
+		threadId = 2 * _scriptResource->_codeCount;
+	}
+	++_nextTempThreadId;
+	return 0x00020000 | threadId;
+}
+
+bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (!progInfo) {
+		dumpActiveScenes(_globalSceneId, threadId);
+		sceneId = _theSceneId;
+	}
+	_activeScenes.push(sceneId);
+	return progInfo != 0;
+}
+
+void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	// TODO krnfileDump(sceneId);
+	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
+	_threads->terminateThreadsByTag(sceneId, threadId);
+	_controls->destroyControlsByTag(sceneId);
+	_triggerFunctions->removeBySceneId(sceneId);
+	_resSys->unloadResourcesByTag(sceneId);
+	_activeScenes.pop();
+}
+
+void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	_camera->pushCameraMode();
+	_threads->suspendThreadsByTag(sceneId, threadId);
+	_controls->pauseControlsByTag(sceneId);
+	_actorItems->pauseByTag(sceneId);
+	_backgroundItems->pauseByTag(sceneId);
+	_activeScenes.pauseActiveScene();
+}
+
+void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	_backgroundItems->unpauseByTag(sceneId);
+	_actorItems->unpauseByTag(sceneId);
+	_controls->unpauseControlsByTag(sceneId);
+	_threads->notifyThreadsByTag(sceneId, threadId);
+	_camera->popCameraMode();
+	_activeScenes.unpauseActiveScene();
+}
+
+void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
+	uint activeScenesCount = _activeScenes.getActiveScenesCount();
+	while (activeScenesCount > 0) {
+		uint32 activeSceneId;
+		_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, 0);
+		if (activeSceneId == sceneId)
+			break;
+		exitScene(threadId);
+		--activeScenesCount;
+	}
+	_camera->clearCameraModeStack();
+}
+
+void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
+	_theSceneId = theSceneId;
+	_theThreadId = theThreadId;
+}
+
+bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (progInfo)
+		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	return false;
+}
+
+void IllusionsEngine_BBDOU::reset() {
+	_scriptResource->_blockCounters.clear();
+	_scriptResource->_properties.clear();
+	// TODO script_sub_417FF0(1, 0);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
new file mode 100644
index 0000000..cd6d8d7
--- /dev/null
+++ b/engines/illusions/illusions_bbdou.h
@@ -0,0 +1,150 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ILLUSIONS_BBDOU_H
+#define ILLUSIONS_ILLUSIONS_BBDOU_H
+
+#include "illusions/illusions.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+class Dictionary;
+class ScriptMan;
+class ScriptStack;
+class TriggerFunctions;
+class TriggerFunction;
+
+typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
+
+struct TriggerFunction {
+	uint32 _sceneId;
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _objectId;
+	TriggerFunctionCallback *_callback;
+	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	~TriggerFunction();
+	void run(uint32 callingThreadId);
+};
+
+class TriggerFunctions {
+public:
+	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void removeBySceneId(uint32 sceneId);
+public:
+	typedef Common::List<TriggerFunction*> Items;
+	typedef Items::iterator ItemsIterator;
+	Items _triggerFunctions;
+	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+};
+
+struct ActiveScene {
+	uint32 _sceneId;
+	int _pauseCtr;
+};
+
+class ActiveScenes {
+public:
+	ActiveScenes();
+	void clear();
+	void push(uint32 sceneId);
+	void pop();
+	void pauseActiveScene();
+	void unpauseActiveScene();
+	uint getActiveScenesCount();
+	void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
+	uint32 getCurrentScene();
+	bool isSceneActive(uint32 sceneId);
+protected:
+	Common::FixedStack<ActiveScene, 16> _stack;
+};
+
+class IllusionsEngine_BBDOU : public IllusionsEngine {
+public:
+	IllusionsEngine_BBDOU(OSystem *syst, const ADGameDescription *gd);
+protected:
+	virtual Common::Error run();
+	virtual bool hasFeature(EngineFeature f) const;
+public:	
+	ScriptMan *_scriptMan;
+	TriggerFunctions *_triggerFunctions;
+
+	ActiveScenes _activeScenes;
+	uint32 _prevSceneId;
+	uint32 _theSceneId;
+	uint32 _theThreadId;
+	uint32 _globalSceneId;
+
+	int _pauseCtr;
+	ScriptStack *_stack;
+	bool _doScriptThreadInit;
+
+	uint32 _nextTempThreadId;
+
+	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
+
+	void loadSpecialCode(uint32 resId);
+	void unloadSpecialCode(uint32 resId);
+	void notifyThreadId(uint32 &threadId);
+	Control *getObjectControl(uint32 objectId);
+	Common::Point getNamedPointPosition(uint32 namedPointId);
+	uint32 getPriorityFromBase(int16 priority);
+	uint32 getPrevScene();	
+	uint32 getCurrentScene();
+
+	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
+	void startScriptThread(uint32 threadId, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void startAnonScriptThread(int32 threadId, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
+	uint32 startTimerThread(uint32 duration, uint32 threadId);
+	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
+	uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
+		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
+	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
+	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
+	uint32 newTempThreadId();
+
+	bool enterScene(uint32 sceneId, uint32 threadId);
+	void exitScene(uint32 threadId);
+	void enterPause(uint32 threadId);
+	void leavePause(uint32 threadId);
+	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
+
+	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
+	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
+	void reset();
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index c4fe62b..63bf826 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -1,7 +1,7 @@
 MODULE := engines/illusions
 
 MODULE_OBJS := \
-    abortablethread.o \
+	abortablethread.o \
 	actor.o \
 	actorresource.o \
 	backgroundresource.o \
@@ -17,11 +17,13 @@ MODULE_OBJS := \
 	fontresource.o \
 	graphics.o \
 	illusions.o \
+	illusions_bbdou.o \
 	input.o \
 	resourcesystem.o \
 	screen.o \
 	scriptman.o \
 	scriptopcodes.o \
+	scriptopcodes_bbdou.o \
 	scriptresource.o \
 	scriptthread.o \
 	sequenceopcodes.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index de2efae..9e03a40 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -161,17 +161,7 @@ bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 	if (!calcItemRect(item, srcRect, dstRect))
 		return true;
 
-	if (item->_scale == 100) {
-		if (item->_flags & 1)
-			_screen->drawSurface10(dstRect.left, dstRect.top, item->_surface, srcRect, _screen->getColorKey2());
-		else
-			_screen->drawSurface11(dstRect.left, dstRect.top, item->_surface, srcRect);
-	} else {
-		if (item->_flags & 1)
-			_screen->drawSurface20(dstRect, item->_surface, srcRect, _screen->getColorKey2());
-		else
-			_screen->drawSurface21(dstRect, item->_surface, srcRect);
-	}
+	_screen->drawSurface(dstRect, item->_surface, srcRect, item->_scale, item->_flags);
 	
 	if (item->_drawFlags)
 		*item->_drawFlags &= ~4;
@@ -346,6 +336,20 @@ void Screen::updateSprites() {
 	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
 }
 
+void Screen::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
+	if (scale == 100) {
+		if (flags & 1)
+			drawSurface10(dstRect.left, dstRect.top, surface, srcRect, _colorKey2);
+		else
+			drawSurface11(dstRect.left, dstRect.top, surface, srcRect);
+	} else {
+		if (flags & 1)
+			drawSurface20(dstRect, surface, srcRect, _colorKey2);
+		else
+			drawSurface21(dstRect, surface, srcRect);
+	}
+}
+
 void Screen::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
 	// Unscaled
 	// TODO
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 6332f78..cfeaba6 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -106,6 +106,7 @@ public:
 	void setDisplayOn(bool isOn);
 	uint16 getColorKey2();
 	void updateSprites();
+	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
 	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
 	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
 	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
index c438def..ef4f462 100644
--- a/engines/illusions/scriptman.cpp
+++ b/engines/illusions/scriptman.cpp
@@ -22,123 +22,12 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptman.h"
-#include "illusions/abortablethread.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
-#include "illusions/scriptthread.h"
 #include "illusions/scriptopcodes.h"
-#include "illusions/talkthread.h"
-#include "illusions/timerthread.h"
 
 namespace Illusions {
 
-// ActiveScenes
-
-ActiveScenes::ActiveScenes() {
-	clear();
-}
-
-void ActiveScenes::clear() {
-	_stack.clear();
-}
-
-void ActiveScenes::push(uint32 sceneId) {
-	ActiveScene activeScene;
-	activeScene._sceneId = sceneId;
-	activeScene._pauseCtr = 0;
-	_stack.push(activeScene);
-}
-
-void ActiveScenes::pop() {
-	_stack.pop();
-}
-
-void ActiveScenes::pauseActiveScene() {
-	++_stack.top()._pauseCtr;
-}
-
-void ActiveScenes::unpauseActiveScene() {
-	--_stack.top()._pauseCtr;
-}
-
-uint ActiveScenes::getActiveScenesCount() {
-	return _stack.size();
-}
-
-void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
-	if (sceneId)
-		*sceneId = _stack[index - 1]._sceneId;
-	if (pauseCtr)
-		*pauseCtr = _stack[index - 1]._pauseCtr;
-}
-
-uint32 ActiveScenes::getCurrentScene() {
-	if (_stack.size() > 0)
-		return _stack.top()._sceneId;
-	return 0;
-}
-
-bool ActiveScenes::isSceneActive(uint32 sceneId) {
-	for (uint i = 0; i < _stack.size(); ++i)
-		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
-			return true;
-	return false;
-}
-
-// TriggerFunction
-
-TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
-	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
-}
-
-TriggerFunction::~TriggerFunction() {
-	delete _callback;
-}
-
-void TriggerFunction::run(uint32 callingThreadId) {
-	(*_callback)(this, callingThreadId);
-}
-
-// TriggerFunctions
-
-void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
-	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
-	if (it != _triggerFunctions.end()) {
-		delete *it;
-		_triggerFunctions.erase(it);
-	}
-	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
-}
-
-TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
-	if (it != _triggerFunctions.end())
-		return (*it);
-	return 0;
-}
-
-void TriggerFunctions::removeBySceneId(uint32 sceneId) {
-	ItemsIterator it = _triggerFunctions.begin();
-	while (it != _triggerFunctions.end()) {
-		if ((*it)->_sceneId == sceneId) {
-			delete *it;
-			it = _triggerFunctions.erase(it);
-		} else
-			++it;
-	}
-}
-
-TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	ItemsIterator it = _triggerFunctions.begin();
-	for (; it != _triggerFunctions.end(); ++it) {
-		TriggerFunction *triggerFunction = *it;
-		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
-			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
-			break;
-	}
-	return it;		
-}
-
 // ScriptStack
 
 ScriptStack::ScriptStack() {
@@ -180,201 +69,11 @@ int16 *ScriptStack::topPtr() {
 
 // ScriptMan
 
-ScriptMan::ScriptMan(IllusionsEngine *vm)
-	: _vm(vm), _pauseCtr(0), _doScriptThreadInit(false) {
-	_threads = new ThreadList(vm);
-	_scriptOpcodes = new ScriptOpcodes(vm);
-	_field8 = 1;
-	_fieldA = 0;
-	_fieldE = 240;
+ScriptMan::ScriptMan(IllusionsEngine_BBDOU *vm)
+	: _vm(vm) {
 }
 
 ScriptMan::~ScriptMan() {
-	delete _threads;
-	delete _scriptOpcodes;
-}
-
-void ScriptMan::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
-	_theSceneId = theSceneId;
-	_theThreadId = theThreadId;
-}
-
-void ScriptMan::startScriptThread(uint32 threadId, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	debug(2, "Starting script thread %08X", threadId);
-	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
-}
-
-void ScriptMan::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	debug(2, "Starting anonymous script thread %08X", threadId);
-	uint32 tempThreadId = newTempThreadId();
-	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
-}
-
-uint32 ScriptMan::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting temp script thread %08X", tempThreadId);
-	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
-	return tempThreadId;
-}
-
-uint32 ScriptMan::startAbortableTimerThread(uint32 duration, uint32 threadId) {
-	return newTimerThread(duration, threadId, true);
-}
-
-uint32 ScriptMan::startTimerThread(uint32 duration, uint32 threadId) {
-	return newTimerThread(duration, threadId, false);
-}
-
-uint32 ScriptMan::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
-	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting abortable thread %08X", tempThreadId);
-	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
-	AbortableThread *abortableThread = new AbortableThread(_vm, tempThreadId, callingThreadId, 0,
-		scriptThreadId, scriptCodeIp2);
-	_threads->startThread(abortableThread);
-	return tempThreadId;
-}
-
-uint32 ScriptMan::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
-	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
-	debug(2, "Starting talk thread");
-	uint32 tempThreadId = newTempThreadId();
-	_threads->endTalkThreadsNoNotify();
-	TalkThread *talkThread = new TalkThread(_vm, tempThreadId, callingThreadId, 0,
-		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
-	_threads->startThread(talkThread);
-	return tempThreadId;
-}
-
-bool ScriptMan::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (progInfo)
-		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
-	return false;
-}
-
-void ScriptMan::setCurrFontId(uint32 fontId) {
-	_fontId = fontId;
-}
-
-bool ScriptMan::checkActiveTalkThreads() {
-	return _threads->isActiveThread(kMsgQueryTalkThreadActive);
-}
-
-uint32 ScriptMan::clipTextDuration(uint32 duration) {
-	switch (_field8) {
-	case 2:
-		if (duration == 0)
-			duration = 240;
-		break;
-	case 3:
-		if (duration < _fieldA)
-			duration = _fieldA;
-		break;
-	case 4:
-		if (duration > _fieldA)
-			duration = _fieldA;
-		break;
-	}
-	return duration;
-}
-
-void ScriptMan::reset() {
-	_scriptResource->_blockCounters.clear();
-	_scriptResource->_properties.clear();
-	// TODO script_sub_417FF0(1, 0);
-}
-
-bool ScriptMan::enterScene(uint32 sceneId, uint32 threadId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (!progInfo) {
-		// TODO dumpActiveScenes(_globalSceneId, threadId);
-		sceneId = _theSceneId;
-	}
-	_activeScenes.push(sceneId);
-	return progInfo != 0;
-}
-
-void ScriptMan::exitScene(uint32 threadId) {
-	uint32 sceneId = _activeScenes.getCurrentScene();
-	// TODO krnfileDump(sceneId);
-	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
-	_threads->terminateThreadsByTag(sceneId, threadId);
-	_vm->_controls->destroyControlsByTag(sceneId);
-	_vm->_triggerFunctions->removeBySceneId(sceneId);
-	_vm->_resSys->unloadResourcesByTag(sceneId);
-	_activeScenes.pop();
-}
-
-void ScriptMan::enterPause(uint32 threadId) {
-	uint32 sceneId = _activeScenes.getCurrentScene();
-	_vm->_camera->pushCameraMode();
-	_threads->suspendThreadsByTag(sceneId, threadId);
-	_vm->_controls->pauseControlsByTag(sceneId);
-	_vm->_actorItems->pauseByTag(sceneId);
-	_vm->_backgroundItems->pauseByTag(sceneId);
-	_activeScenes.pauseActiveScene();
-}
-
-void ScriptMan::leavePause(uint32 threadId) {
-	uint32 sceneId = _activeScenes.getCurrentScene();
-	_vm->_backgroundItems->unpauseByTag(sceneId);
-	_vm->_actorItems->unpauseByTag(sceneId);
-	_vm->_controls->unpauseControlsByTag(sceneId);
-	_threads->notifyThreadsByTag(sceneId, threadId);
-	_vm->_camera->popCameraMode();
-	_activeScenes.unpauseActiveScene();
-}
-
-void ScriptMan::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
-	uint activeScenesCount = _activeScenes.getActiveScenesCount();
-	while (activeScenesCount > 0) {
-		uint32 activeSceneId;
-		_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, 0);
-		if (activeSceneId == sceneId)
-			break;
-		exitScene(threadId);
-		--activeScenesCount;
-	}
-	_vm->_camera->clearCameraModeStack();
-}
-
-void ScriptMan::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
-	ScriptThread *scriptThread = new ScriptThread(_vm, threadId, callingThreadId, notifyFlags,
-		scriptCodeIp, value8, valueC, value10);
-	_threads->startThread(scriptThread);
-	if (_pauseCtr > 0)
-		scriptThread->pause();
-	if (_doScriptThreadInit) {
-		int updateResult = kTSRun;
-		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
-			updateResult = scriptThread->update();
-	}
-}
-
-uint32 ScriptMan::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
-	uint32 tempThreadId = newTempThreadId();
-	TimerThread *timerThread = new TimerThread(_vm, tempThreadId, callingThreadId, 0,
-		duration, isAbortable);
-	_threads->startThread(timerThread);
-	return tempThreadId;
-}
-
-uint32 ScriptMan::newTempThreadId() {
-	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
-	if (threadId > 65535) {
-		_nextTempThreadId = 0;
-		threadId = 2 * _scriptResource->_codeCount;
-	}
-	++_nextTempThreadId;
-	return 0x00020000 | threadId;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
index 6ea28c8..d296d80 100644
--- a/engines/illusions/scriptman.h
+++ b/engines/illusions/scriptman.h
@@ -23,7 +23,7 @@
 #ifndef ILLUSIONS_SCRIPTMAN_H
 #define ILLUSIONS_SCRIPTMAN_H
 
-#include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 #include "illusions/scriptresource.h"
 #include "illusions/thread.h"
 #include "common/algorithm.h"
@@ -31,54 +31,7 @@
 
 namespace Illusions {
 
-class IllusionsEngine;
-class ScriptOpcodes;
-
-struct ActiveScene {
-	uint32 _sceneId;
-	int _pauseCtr;
-};
-
-class ActiveScenes {
-public:
-	ActiveScenes();
-	void clear();
-	void push(uint32 sceneId);
-	void pop();
-	void pauseActiveScene();
-	void unpauseActiveScene();
-	uint getActiveScenesCount();
-	void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
-	uint32 getCurrentScene();
-	bool isSceneActive(uint32 sceneId);
-protected:
-	Common::FixedStack<ActiveScene, 16> _stack;
-};
-
-struct TriggerFunction;
-
-struct TriggerFunction {
-	uint32 _sceneId;
-	uint32 _verbId;
-	uint32 _objectId2;
-	uint32 _objectId;
-	TriggerFunctionCallback *_callback;
-	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	~TriggerFunction();
-	void run(uint32 callingThreadId);
-};
-
-class TriggerFunctions {
-public:
-	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-	void removeBySceneId(uint32 sceneId);
-public:
-	typedef Common::List<TriggerFunction*> Items;
-	typedef Items::iterator ItemsIterator;
-	Items _triggerFunctions;
-	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-};
+class IllusionsEngine_BBDOU;
 
 class ScriptStack {
 public:
@@ -95,63 +48,12 @@ protected:
 
 class ScriptMan {
 public:
-	ScriptMan(IllusionsEngine *vm);
+	ScriptMan(IllusionsEngine_BBDOU *vm);
 	~ScriptMan();
-	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
-	void startScriptThread(uint32 threadId, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	void startAnonScriptThread(int32 threadId, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
-	uint32 startTimerThread(uint32 duration, uint32 threadId);
-	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
-	uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
-		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
-	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
-	void setCurrFontId(uint32 fontId);
-	bool checkActiveTalkThreads();
-	uint32 clipTextDuration(uint32 duration);
-	void reset();
-	bool enterScene(uint32 sceneId, uint32 threadId);
-	void exitScene(uint32 threadId);
-	void enterPause(uint32 threadId);
-	void leavePause(uint32 threadId);
-	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
 public:
 
-	IllusionsEngine *_vm;
-	ScriptResource *_scriptResource;
-
-	ActiveScenes _activeScenes;
-	ScriptStack _stack;
-	
-	int _pauseCtr;
-	
-	uint32 _theSceneId;
-	uint32 _theThreadId;
-	uint32 _globalSceneId;
-	bool _doScriptThreadInit;
-	uint32 _nextTempThreadId;
+	IllusionsEngine_BBDOU *_vm;
 	
-	uint32 _fontId;
-	int _field8;
-	uint32 _fieldA, _fieldE;
-	
-	uint32 _prevSceneId;
-	
-	ThreadList *_threads;
-	ScriptOpcodes *_scriptOpcodes;
-	
-	uint32 _callerThreadId;
-	int16 _menuChoiceOfs;
-	
-	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
-	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
-	uint32 newTempThreadId();
-
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index a551bdf..c8817f6 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -22,16 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptopcodes.h"
-#include "illusions/actor.h"
-#include "illusions/camera.h"
-#include "illusions/dictionary.h"
-#include "illusions/input.h"
-#include "illusions/screen.h"
-#include "illusions/scriptman.h"
-#include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
-#include "illusions/specialcode.h"
-#include "illusions/talkresource.h"
 
 namespace Illusions {
 
@@ -75,733 +66,4 @@ void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
-typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes> ScriptOpcodeI;
-#define OPCODE(op, func) \
-	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes::func); \
-	_opcodeNames[op] = #func;
-
-void ScriptOpcodes::initOpcodes() {
-	// First clear everything
-	for (uint i = 0; i < 256; ++i)
-		_opcodes[i] = 0;
-	// Register opcodes
-	OPCODE(2, opSuspend);
-	OPCODE(3, opYield);
-	OPCODE(4, opTerminate);
-	OPCODE(5, opJump);
-	OPCODE(6, opStartScriptThread);
-	OPCODE(8, opStartTempScriptThread);
-	OPCODE(9, opStartTimerThread);
-	OPCODE(14, opSetThreadSceneId);
-	OPCODE(15, opEndTalkThreads);
-	OPCODE(16, opLoadResource);
-	OPCODE(17, opUnloadResource);
-	OPCODE(20, opEnterScene);
-	OPCODE(25, opChangeScene);
-	OPCODE(26, opStartModalScene);
-	OPCODE(27, opExitModalScene);
-	OPCODE(30, opEnterCloseUpScene);
-	OPCODE(31, opExitCloseUpScene);
-	OPCODE(32, opPanCenterObject);
-	OPCODE(34, opPanToObject);
-	OPCODE(35, opPanToNamedPoint);
-	OPCODE(36, opPanToPoint);
-	OPCODE(37, opPanStop);
-	OPCODE(39, opSetDisplay);
-	OPCODE(42, opIncBlockCounter);
-	OPCODE(43, opClearBlockCounter);
-	OPCODE(45, opSetProperty);
-	OPCODE(46, opPlaceActor);
-	OPCODE(47, opFaceActor);
-	OPCODE(48, opFaceActorToObject);	
-	OPCODE(49, opStartSequenceActor);
-	OPCODE(51, opStartMoveActor);
-	OPCODE(53, opSetActorToNamedPoint);
-	OPCODE(56, opStartTalkThread);
-	OPCODE(57, opAppearActor);
-	OPCODE(58, opDisappearActor);
-	OPCODE(60, opActivateObject);
-	OPCODE(61, opDeactivateObject);
-	OPCODE(62, opSetDefaultSequence);
-	OPCODE(63, opSetSelectSfx);
-	OPCODE(64, opSetMoveSfx);
-	OPCODE(65, opSetDenySfx);
-	OPCODE(66, opSetAdjustUpSfx);
-	OPCODE(67, opSetAdjustDnSfx);
-	OPCODE(71, opStartSound);
-	OPCODE(74, opStopSound);
-	OPCODE(75, opStartMusic);
-	OPCODE(76, opStopMusic);
-	OPCODE(78, opStackPushRandom);
-	OPCODE(79, opIfLte);
-	OPCODE(80, opAddMenuChoice);
-	OPCODE(81, opDisplayMenu);
-	OPCODE(82, opSwitchMenuChoice);
-	OPCODE(84, opResetGame);
-	OPCODE(87, opDeactivateButton);
-	OPCODE(88, opActivateButton);
-	OPCODE(103, opJumpIf);
-	OPCODE(104, opIsPrevSceneId);
-	OPCODE(105, opIsCurrentSceneId);
-	OPCODE(106, opIsActiveSceneId);
-	OPCODE(107, opNot);
-	OPCODE(108, opAnd);
-	OPCODE(109, opOr);
-	OPCODE(110, opGetProperty);
-	OPCODE(111, opCompareBlockCounter);
-	OPCODE(126, opDebug126);
-	OPCODE(144, opPlayVideo);
-	OPCODE(146, opStackPop);
-	OPCODE(147, opStackDup);
-	OPCODE(148, opLoadSpecialCodeModule);
-	OPCODE(150, opRunSpecialCode);
-	OPCODE(160, opStopActor);
-	OPCODE(161, opSetActorUsePan);
-	OPCODE(168, opStartAbortableThread);
-	OPCODE(169, opKillThread);
-	OPCODE(175, opSetSceneIdThreadId);
-	OPCODE(176, opStackPush0);
-	OPCODE(177, opSetFontId);
-	OPCODE(178, opAddMenuKey);
-	OPCODE(179, opChangeSceneAll);
-}
-
-#undef OPCODE
-
-void ScriptOpcodes::freeOpcodes() {
-	for (uint i = 0; i < 256; ++i)
-		delete _opcodes[i];
-}
-
-// Opcodes
-
-// Convenience macros
-#define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(0, "ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(0, "ARG_UINT32(" #name " = %08X)", name);
-
-void ScriptOpcodes::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes::opYield(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSTerminate;
-}
-
-void ScriptOpcodes::opJump(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs);
-	opCall._deltaOfs += jumpOffs;
-}
-
-void ScriptOpcodes::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_scriptMan->startScriptThread(threadId, opCall._threadId,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(codeOffs);
-	_vm->_scriptMan->startTempScriptThread(opCall._code + codeOffs,
-		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(isAbortable);
-	ARG_INT16(duration);
-	ARG_INT16(maxDuration);
-	if (maxDuration)
-		duration += _vm->getRandom(maxDuration);
-		
-duration = 1;//DEBUG Speeds up things		
-		
-	if (isAbortable)
-		_vm->_scriptMan->startAbortableTimerThread(duration, opCall._threadId);
-	else
-		_vm->_scriptMan->startTimerThread(duration, opCall._threadId);
-}
-
-void ScriptOpcodes::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_scriptMan->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
-}
-
-void ScriptOpcodes::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_scriptMan->_threads->endTalkThreads();
-}
-
-void ScriptOpcodes::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
-	uint32 sceneId = _vm->getCurrentScene();
-	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
-}
-
-void ScriptOpcodes::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_resSys->unloadResourceById(resourceId);
-}
-
-void ScriptOpcodes::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	uint scenesCount = _vm->_scriptMan->_activeScenes.getActiveScenesCount();
-	if (scenesCount > 0) {
-		uint32 currSceneId;
-		_vm->_scriptMan->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
-		// TODO krnfileDump(currSceneId);
-	}
-	if (!_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId))
-		opCall._result = kTSTerminate;
-}
-
-//DEBUG Scenes
-//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
-//uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
-//uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
-//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
-//uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
-//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
-//uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
-//uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
-//uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
-//uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
-uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
-
-void ScriptOpcodes::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-
-	if (dsceneId) {
-		sceneId = dsceneId;
-		threadId = dthreadId;
-		dsceneId = 0;
-	}
-	
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_scriptMan->_prevSceneId = _vm->getCurrentScene();
-	_vm->_scriptMan->exitScene(opCall._callerThreadId);
-	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_scriptMan->_prevSceneId, sceneId, threadId);
-	_vm->_scriptMan->startAnonScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_scriptMan->enterPause(opCall._callerThreadId);
-	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
-	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
-	_vm->_scriptMan->startScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_scriptMan->exitScene(opCall._callerThreadId);
-	_vm->_scriptMan->leavePause(opCall._callerThreadId);
-	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
-}
-
-void ScriptOpcodes::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_scriptMan->enterPause(opCall._callerThreadId);
-	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
-}
-
-void ScriptOpcodes::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_scriptMan->exitScene(opCall._callerThreadId);
-	_vm->_scriptMan->leavePause(opCall._callerThreadId);
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(objectId);
-	_vm->_camera->panCenterObject(objectId, speed);
-}
-
-void ScriptOpcodes::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = control->getActorPosition();
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
-void ScriptOpcodes::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
-void ScriptOpcodes::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_INT16(x);	
-	ARG_INT16(y);	
-	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
-}
-
-void ScriptOpcodes::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_camera->stopPan();
-}
-
-void ScriptOpcodes::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(flag);
-	_vm->_screen->setDisplayOn(flag != 0);
-}
-
-void ScriptOpcodes::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	byte value = _vm->_scriptMan->_scriptResource->_blockCounters.get(index) + 1;
-	if (value <= 63)
-		_vm->_scriptMan->_scriptResource->_blockCounters.set(index, value);
-}
-
-void ScriptOpcodes::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	_vm->_scriptMan->_scriptResource->_blockCounters.set(index, 0);
-}
-
-void ScriptOpcodes::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value);	
-	ARG_UINT32(propertyId);	
-	_vm->_scriptMan->_scriptResource->_properties.set(propertyId, value != 0);
-}
-
-void ScriptOpcodes::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(actorTypeId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
-}
-
-void ScriptOpcodes::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(facing);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->faceActor(facing);
-}
-
-void ScriptOpcodes::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId1);
-	ARG_UINT32(objectId2);
-	Control *control1 = _vm->_dict->getObjectControl(objectId1);
-	Control *control2 = _vm->_dict->getObjectControl(objectId2);
-	Common::Point pos1 = control1->getActorPosition();
-	Common::Point pos2 = control2->getActorPosition();
-	uint facing;
-	if (_vm->calcPointDirection(pos1, pos2, facing))
-		control1->faceActor(facing);
-}
-
-void ScriptOpcodes::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->startSequenceActor(sequenceId, 2, opCall._threadId);
-}
-
-void ScriptOpcodes::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
-}
-
-void ScriptOpcodes::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(namedPointId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->stopActor();
-	control->setActorPosition(pos);
-}
-
-void ScriptOpcodes::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(duration);	
-	ARG_UINT32(objectId);
-	ARG_UINT32(talkId);
-	ARG_UINT32(sequenceId1);
-	ARG_UINT32(sequenceId2);
-	ARG_UINT32(namedPointId);
-	_vm->_scriptMan->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._threadId);
-}
-
-void ScriptOpcodes::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	if (!control) {
-		Common::Point pos = _vm->getNamedPointPosition(0x70023);
-		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
-		control = _vm->_dict->getObjectControl(objectId);
-		control->startSequenceActor(0x60001, 2, 0);
-	}
-	control->appearActor();
-}
-
-void ScriptOpcodes::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->disappearActor();
-}
-
-void ScriptOpcodes::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	if (control)
-		control->activateObject();
-}
-
-void ScriptOpcodes::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->deactivateObject();
-}
-
-void ScriptOpcodes::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(defaultSequenceId);
-	ARG_UINT32(sequenceId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
-}
-
-void ScriptOpcodes::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setSelectSfx(soundEffectId);
-}
-
-void ScriptOpcodes::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setMoveSfx(soundEffectId);
-}
-
-void ScriptOpcodes::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setDenySfx(soundEffectId);
-}
-
-void ScriptOpcodes::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustUpSfx(soundEffectId);
-}
-
-void ScriptOpcodes::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustDnSfx(soundEffectId);
-}
-
-void ScriptOpcodes::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(volume);
-	ARG_INT16(pan);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->startSound(soundEffectId, volume, pan);
-}
-void ScriptOpcodes::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->stopSound(soundEffectId);
-}
-
-void ScriptOpcodes::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(volume);
-	ARG_INT16(pan);
-	ARG_UINT32(musicId);
-	ARG_UINT32(type);
-	// TODO _vm->playMusic(musicId, type, volume, pan);
-}
-
-void ScriptOpcodes::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	// TODO _vm->stopMusic();
-}
-
-void ScriptOpcodes::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(maxValue);
-	_vm->_scriptMan->_stack.push(_vm->getRandom(maxValue) + 1);
-}
-
-void ScriptOpcodes::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(rvalue);
-	ARG_INT16(elseJumpOffs);
-	int16 lvalue = _vm->_scriptMan->_stack.pop();
-	if (!(lvalue <= rvalue))
-		opCall._deltaOfs += elseJumpOffs;
-}
-
-void ScriptOpcodes::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(jumpOffs);
-	ARG_INT16(endMarker);
-	_vm->_scriptMan->_stack.push(endMarker);
-	_vm->_scriptMan->_stack.push(jumpOffs);
-}
-
-void ScriptOpcodes::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(unk1);
-	ARG_UINT32(menuId);
-	ARG_UINT32(unk2);
-	// TODO _vm->_shellMgr->displayMenu(_vm->_scriptMan->_stack.topPtr(), &_vm->_scriptMan->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
-	// Remove menu choices from the stack
-	do {
-		_vm->_scriptMan->_stack.pop();
-	} while (_vm->_scriptMan->_stack.pop() == 0);
-
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
-
-}
-
-void ScriptOpcodes::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-_vm->_scriptMan->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
-
-	opCall._deltaOfs += _vm->_scriptMan->_menuChoiceOfs;
-}
-
-void ScriptOpcodes::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_scriptMan->_threads->terminateThreads(opCall._callerThreadId);
-	_vm->_scriptMan->reset();
-	_vm->_input->activateButton(0xFFFF);
-	// TODO _vm->stopMusic();
-	// TODO _vm->_gameStates->clear();
-}
-
-void ScriptOpcodes::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(button)
-	_vm->_input->deactivateButton(button);
-}
-
-void ScriptOpcodes::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(button)
-	_vm->_input->activateButton(button);
-}
-
-void ScriptOpcodes::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs);
-	int16 value = _vm->_scriptMan->_stack.pop();
-	if (value == 0)
-		opCall._deltaOfs += jumpOffs;
-}
-
-void ScriptOpcodes::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_scriptMan->_stack.push(_vm->_scriptMan->_prevSceneId == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_scriptMan->_stack.push(_vm->getCurrentScene() == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_scriptMan->_stack.push(_vm->_scriptMan->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
-}
-
-void ScriptOpcodes::opNot(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_scriptMan->_stack.pop();
-	_vm->_scriptMan->_stack.push(value != 0 ? 0 : 1);
-}
-
-void ScriptOpcodes::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value1 = _vm->_scriptMan->_stack.pop();
-	int16 value2 = _vm->_scriptMan->_stack.pop();
-	_vm->_scriptMan->_stack.push(value1 & value2);
-}
-
-void ScriptOpcodes::opOr(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value1 = _vm->_scriptMan->_stack.pop();
-	int16 value2 = _vm->_scriptMan->_stack.pop();
-	_vm->_scriptMan->_stack.push(value1 | value2);
-}
-
-void ScriptOpcodes::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(propertyId)
-	bool value = _vm->_scriptMan->_scriptResource->_properties.get(propertyId);
-	_vm->_scriptMan->_stack.push(value ? 1 : 0);
-}
-
-void ScriptOpcodes::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	ARG_INT16(compareOp);	
-	ARG_INT16(rvalue);
-	int16 lvalue = _vm->_scriptMan->_scriptResource->_blockCounters.get(index);
-	bool compareResult = false;
-	switch (compareOp) {
-	case 1:
-		compareResult = lvalue == rvalue;
-		break;
-	case 2:
-		compareResult = lvalue != rvalue;
-		break;
-	case 3:
-		compareResult = lvalue < rvalue;
-		break;
-	case 4:
-		compareResult = lvalue > rvalue;
-		break;
-	case 5:
-		compareResult = lvalue >= rvalue;
-		break;
-	case 6:
-		compareResult = lvalue <= rvalue;
-		break;
-	}
-	_vm->_scriptMan->_stack.push(compareResult ? 1 : 0);
-}
-
-void ScriptOpcodes::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Prints some debug text
-	debug(1, "[DBG] %s", (char*)opCall._code);
-}
-
-void ScriptOpcodes::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(videoId);
-	ARG_UINT32(priority);
-	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
-	
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
-	
-}
-
-void ScriptOpcodes::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_scriptMan->_stack.pop(); 
-}
-
-void ScriptOpcodes::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_scriptMan->_stack.peek();
-	_vm->_scriptMan->_stack.push(value); 
-}
-
-void ScriptOpcodes::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeModuleId);
-	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
-}
-
-void ScriptOpcodes::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeId);
-	_vm->_scriptMan->_callerThreadId = opCall._callerThreadId;
-	_vm->_specialCode->run(specialCodeId, opCall);
-	_vm->_scriptMan->_callerThreadId = 0;
-}
-
-void ScriptOpcodes::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->stopActor();
-}
-
-void ScriptOpcodes::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(usePan)
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->setActorUsePan(usePan);
-}
-
-void ScriptOpcodes::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(codeOffs);
-	ARG_INT16(skipOffs);
-	_vm->_scriptMan->startAbortableThread(opCall._code + codeOffs,
-		opCall._code + skipOffs, opCall._threadId);
-}
-
-void ScriptOpcodes::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_scriptMan->_threads->killThread(threadId);
-}
-
-void ScriptOpcodes::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->_scriptMan->setSceneIdThreadId(sceneId, threadId);
-}
-
-void ScriptOpcodes::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_scriptMan->_stack.push(0);
-}
-
-void ScriptOpcodes::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(fontId);
-	_vm->_scriptMan->setCurrFontId(fontId);
-}
-
-void ScriptOpcodes::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(key);
-	ARG_UINT32(threadId);
-	// TODO _vm->addMenuKey(key, threadId);
-}
-
-void ScriptOpcodes::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_scriptMan->_prevSceneId = _vm->getCurrentScene();
-	_vm->_scriptMan->dumpActiveScenes(_vm->_scriptMan->_globalSceneId, opCall._callerThreadId);
-	_vm->_scriptMan->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_scriptMan->_prevSceneId, sceneId, threadId);
-	_vm->_scriptMan->startAnonScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 4fc6cd6..1020e61 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -49,98 +49,21 @@ typedef Common::Functor2<ScriptThread*, OpCall&, void> ScriptOpcode;
 class ScriptOpcodes {
 public:
 	ScriptOpcodes(IllusionsEngine *vm);
-	~ScriptOpcodes();
+	virtual ~ScriptOpcodes();
 	void execOpcode(ScriptThread *scriptThread, OpCall &opCall);
 protected:
 	IllusionsEngine *_vm;
 	ScriptOpcode *_opcodes[256];
 	Common::String _opcodeNames[256];
-	void initOpcodes();
-	void freeOpcodes();
-
-	// Opcodes
-	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
-	void opYield(ScriptThread *scriptThread, OpCall &opCall);
-	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
-	void opJump(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
-	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
-	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
-	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
-	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
-	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
-	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opNot(ScriptThread *scriptThread, OpCall &opCall);
-	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
-	void opOr(ScriptThread *scriptThread, OpCall &opCall);
-	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
-	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
-	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
-	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
-	
+	virtual void initOpcodes() {}
+	virtual void freeOpcodes() {}
 };
 
+// Convenience macros
+#define	ARG_SKIP(x) opCall.skip(x); 
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(0, "ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(0, "ARG_UINT32(" #name " = %08X)", name);
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_SCRIPTOPCODES_H
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
new file mode 100644
index 0000000..f63a221
--- /dev/null
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -0,0 +1,771 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/scriptopcodes_bbdou.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+#include "illusions/scriptman.h"
+#include "illusions/scriptresource.h"
+#include "illusions/scriptthread.h"
+#include "illusions/specialcode.h"
+#include "illusions/talkresource.h"
+
+namespace Illusions {
+
+// ScriptOpcodes_BBDOU
+
+ScriptOpcodes_BBDOU::ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm)
+	: ScriptOpcodes(vm), _vm(vm) {
+	initOpcodes();
+}
+
+ScriptOpcodes_BBDOU::~ScriptOpcodes_BBDOU() {
+	freeOpcodes();
+}
+
+typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> ScriptOpcodeI;
+#define OPCODE(op, func) \
+	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_BBDOU::func); \
+	_opcodeNames[op] = #func;
+
+void ScriptOpcodes_BBDOU::initOpcodes() {
+	// First clear everything
+	for (uint i = 0; i < 256; ++i)
+		_opcodes[i] = 0;
+	// Register opcodes
+	OPCODE(2, opSuspend);
+	OPCODE(3, opYield);
+	OPCODE(4, opTerminate);
+	OPCODE(5, opJump);
+	OPCODE(6, opStartScriptThread);
+	OPCODE(8, opStartTempScriptThread);
+	OPCODE(9, opStartTimerThread);
+	OPCODE(14, opSetThreadSceneId);
+	OPCODE(15, opEndTalkThreads);
+	OPCODE(16, opLoadResource);
+	OPCODE(17, opUnloadResource);
+	OPCODE(20, opEnterScene);
+	OPCODE(25, opChangeScene);
+	OPCODE(26, opStartModalScene);
+	OPCODE(27, opExitModalScene);
+	OPCODE(30, opEnterCloseUpScene);
+	OPCODE(31, opExitCloseUpScene);
+	OPCODE(32, opPanCenterObject);
+	OPCODE(34, opPanToObject);
+	OPCODE(35, opPanToNamedPoint);
+	OPCODE(36, opPanToPoint);
+	OPCODE(37, opPanStop);
+	OPCODE(39, opSetDisplay);
+	OPCODE(42, opIncBlockCounter);
+	OPCODE(43, opClearBlockCounter);
+	OPCODE(45, opSetProperty);
+	OPCODE(46, opPlaceActor);
+	OPCODE(47, opFaceActor);
+	OPCODE(48, opFaceActorToObject);	
+	OPCODE(49, opStartSequenceActor);
+	OPCODE(51, opStartMoveActor);
+	OPCODE(53, opSetActorToNamedPoint);
+	OPCODE(56, opStartTalkThread);
+	OPCODE(57, opAppearActor);
+	OPCODE(58, opDisappearActor);
+	OPCODE(60, opActivateObject);
+	OPCODE(61, opDeactivateObject);
+	OPCODE(62, opSetDefaultSequence);
+	OPCODE(63, opSetSelectSfx);
+	OPCODE(64, opSetMoveSfx);
+	OPCODE(65, opSetDenySfx);
+	OPCODE(66, opSetAdjustUpSfx);
+	OPCODE(67, opSetAdjustDnSfx);
+	OPCODE(71, opStartSound);
+	OPCODE(74, opStopSound);
+	OPCODE(75, opStartMusic);
+	OPCODE(76, opStopMusic);
+	OPCODE(78, opStackPushRandom);
+	OPCODE(79, opIfLte);
+	OPCODE(80, opAddMenuChoice);
+	OPCODE(81, opDisplayMenu);
+	OPCODE(82, opSwitchMenuChoice);
+	OPCODE(84, opResetGame);
+	OPCODE(87, opDeactivateButton);
+	OPCODE(88, opActivateButton);
+	OPCODE(103, opJumpIf);
+	OPCODE(104, opIsPrevSceneId);
+	OPCODE(105, opIsCurrentSceneId);
+	OPCODE(106, opIsActiveSceneId);
+	OPCODE(107, opNot);
+	OPCODE(108, opAnd);
+	OPCODE(109, opOr);
+	OPCODE(110, opGetProperty);
+	OPCODE(111, opCompareBlockCounter);
+	OPCODE(126, opDebug126);
+	OPCODE(144, opPlayVideo);
+	OPCODE(146, opStackPop);
+	OPCODE(147, opStackDup);
+	OPCODE(148, opLoadSpecialCodeModule);
+	OPCODE(150, opRunSpecialCode);
+	OPCODE(160, opStopActor);
+	OPCODE(161, opSetActorUsePan);
+	OPCODE(168, opStartAbortableThread);
+	OPCODE(169, opKillThread);
+	OPCODE(175, opSetSceneIdThreadId);
+	OPCODE(176, opStackPush0);
+	OPCODE(177, opSetFontId);
+	OPCODE(178, opAddMenuKey);
+	OPCODE(179, opChangeSceneAll);
+}
+
+#undef OPCODE
+
+void ScriptOpcodes_BBDOU::freeOpcodes() {
+	for (uint i = 0; i < 256; ++i)
+		delete _opcodes[i];
+}
+
+// Opcodes
+
+void ScriptOpcodes_BBDOU::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_BBDOU::opYield(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_BBDOU::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes_BBDOU::opJump(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_BBDOU::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->startScriptThread(threadId, opCall._threadId,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_BBDOU::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(codeOffs);
+	_vm->startTempScriptThread(opCall._code + codeOffs,
+		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_BBDOU::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(isAbortable);
+	ARG_INT16(duration);
+	ARG_INT16(maxDuration);
+	if (maxDuration)
+		duration += _vm->getRandom(maxDuration);
+		
+duration = 1;//DEBUG Speeds up things		
+		
+	if (isAbortable)
+		_vm->startAbortableTimerThread(duration, opCall._threadId);
+	else
+		_vm->startTimerThread(duration, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
+}
+
+void ScriptOpcodes_BBDOU::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_threads->endTalkThreads();
+}
+
+void ScriptOpcodes_BBDOU::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	uint32 sceneId = _vm->getCurrentScene();
+	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_resSys->unloadResourceById(resourceId);
+}
+
+void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
+	if (scenesCount > 0) {
+		uint32 currSceneId;
+		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
+		// TODO krnfileDump(currSceneId);
+	}
+	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
+		opCall._result = kTSTerminate;
+}
+
+//DEBUG Scenes
+//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
+//uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
+//uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
+//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+//uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
+//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
+//uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
+//uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
+//uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
+//uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
+uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+
+void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+
+	if (dsceneId) {
+		sceneId = dsceneId;
+		threadId = dthreadId;
+		dsceneId = 0;
+	}
+	
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_prevSceneId = _vm->getCurrentScene();
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	_vm->startScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_BBDOU::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+}
+
+void ScriptOpcodes_BBDOU::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_BBDOU::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	_vm->_camera->panCenterObject(objectId, speed);
+}
+
+void ScriptOpcodes_BBDOU::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_INT16(x);	
+	ARG_INT16(y);	
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_camera->stopPan();
+}
+
+void ScriptOpcodes_BBDOU::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flag);
+	_vm->_screen->setDisplayOn(flag != 0);
+}
+
+void ScriptOpcodes_BBDOU::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
+	if (value <= 63)
+		_vm->_scriptResource->_blockCounters.set(index, value);
+}
+
+void ScriptOpcodes_BBDOU::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.set(index, 0);
+}
+
+void ScriptOpcodes_BBDOU::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value);	
+	ARG_UINT32(propertyId);	
+	_vm->_scriptResource->_properties.set(propertyId, value != 0);
+}
+
+void ScriptOpcodes_BBDOU::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(actorTypeId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->faceActor(facing);
+}
+
+void ScriptOpcodes_BBDOU::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Control *control2 = _vm->_dict->getObjectControl(objectId2);
+	Common::Point pos1 = control1->getActorPosition();
+	Common::Point pos2 = control2->getActorPosition();
+	uint facing;
+	if (_vm->calcPointDirection(pos1, pos2, facing))
+		control1->faceActor(facing);
+}
+
+void ScriptOpcodes_BBDOU::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->stopActor();
+	control->setActorPosition(pos);
+}
+
+void ScriptOpcodes_BBDOU::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(duration);	
+	ARG_UINT32(objectId);
+	ARG_UINT32(talkId);
+	ARG_UINT32(sequenceId1);
+	ARG_UINT32(sequenceId2);
+	ARG_UINT32(namedPointId);
+	_vm->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (!control) {
+		Common::Point pos = _vm->getNamedPointPosition(0x70023);
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
+		control = _vm->_dict->getObjectControl(objectId);
+		control->startSequenceActor(0x60001, 2, 0);
+	}
+	control->appearActor();
+}
+
+void ScriptOpcodes_BBDOU::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->disappearActor();
+}
+
+void ScriptOpcodes_BBDOU::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (control)
+		control->activateObject();
+}
+
+void ScriptOpcodes_BBDOU::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->deactivateObject();
+}
+
+void ScriptOpcodes_BBDOU::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(defaultSequenceId);
+	ARG_UINT32(sequenceId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
+}
+
+void ScriptOpcodes_BBDOU::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setSelectSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setMoveSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setDenySfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustUpSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustDnSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(volume);
+	ARG_INT16(pan);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->startSound(soundEffectId, volume, pan);
+}
+void ScriptOpcodes_BBDOU::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->stopSound(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(volume);
+	ARG_INT16(pan);
+	ARG_UINT32(musicId);
+	ARG_UINT32(type);
+	// TODO _vm->playMusic(musicId, type, volume, pan);
+}
+
+void ScriptOpcodes_BBDOU::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	// TODO _vm->stopMusic();
+}
+
+void ScriptOpcodes_BBDOU::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(maxValue);
+	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
+}
+
+void ScriptOpcodes_BBDOU::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(rvalue);
+	ARG_INT16(elseJumpOffs);
+	int16 lvalue = _vm->_stack->pop();
+	if (!(lvalue <= rvalue))
+		opCall._deltaOfs += elseJumpOffs;
+}
+
+void ScriptOpcodes_BBDOU::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(jumpOffs);
+	ARG_INT16(endMarker);
+	_vm->_stack->push(endMarker);
+	_vm->_stack->push(jumpOffs);
+}
+
+void ScriptOpcodes_BBDOU::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(unk1);
+	ARG_UINT32(menuId);
+	ARG_UINT32(unk2);
+	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
+	// Remove menu choices from the stack
+	do {
+		_vm->_stack->pop();
+	} while (_vm->_stack->pop() == 0);
+
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+
+}
+
+void ScriptOpcodes_BBDOU::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+_vm->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
+
+	opCall._deltaOfs += _vm->_menuChoiceOfs;
+}
+
+void ScriptOpcodes_BBDOU::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_threads->terminateThreads(opCall._callerThreadId);
+	_vm->reset();
+	_vm->_input->activateButton(0xFFFF);
+	// TODO _vm->stopMusic();
+	// TODO _vm->_gameStates->clear();
+}
+
+void ScriptOpcodes_BBDOU::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->deactivateButton(button);
+}
+
+void ScriptOpcodes_BBDOU::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->activateButton(button);
+}
+
+void ScriptOpcodes_BBDOU::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	int16 value = _vm->_stack->pop();
+	if (value == 0)
+		opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_BBDOU::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opNot(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->pop();
+	_vm->_stack->push(value != 0 ? 0 : 1);
+}
+
+void ScriptOpcodes_BBDOU::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 & value2);
+}
+
+void ScriptOpcodes_BBDOU::opOr(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 | value2);
+}
+
+void ScriptOpcodes_BBDOU::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(propertyId)
+	bool value = _vm->_scriptResource->_properties.get(propertyId);
+	_vm->_stack->push(value ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	ARG_INT16(compareOp);	
+	ARG_INT16(rvalue);
+	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
+	bool compareResult = false;
+	switch (compareOp) {
+	case 1:
+		compareResult = lvalue == rvalue;
+		break;
+	case 2:
+		compareResult = lvalue != rvalue;
+		break;
+	case 3:
+		compareResult = lvalue < rvalue;
+		break;
+	case 4:
+		compareResult = lvalue > rvalue;
+		break;
+	case 5:
+		compareResult = lvalue >= rvalue;
+		break;
+	case 6:
+		compareResult = lvalue <= rvalue;
+		break;
+	}
+	_vm->_stack->push(compareResult ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG] %s", (char*)opCall._code);
+}
+
+void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(videoId);
+	ARG_UINT32(priority);
+	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
+	
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+	
+}
+
+void ScriptOpcodes_BBDOU::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->pop(); 
+}
+
+void ScriptOpcodes_BBDOU::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->peek();
+	_vm->_stack->push(value); 
+}
+
+void ScriptOpcodes_BBDOU::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeModuleId);
+	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
+}
+
+void ScriptOpcodes_BBDOU::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeId);
+	_vm->_specialCode->run(specialCodeId, opCall);
+}
+
+void ScriptOpcodes_BBDOU::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->stopActor();
+}
+
+void ScriptOpcodes_BBDOU::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(usePan)
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorUsePan(usePan);
+}
+
+void ScriptOpcodes_BBDOU::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(codeOffs);
+	ARG_INT16(skipOffs);
+	_vm->startAbortableThread(opCall._code + codeOffs,
+		opCall._code + skipOffs, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->killThread(threadId);
+}
+
+void ScriptOpcodes_BBDOU::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->setSceneIdThreadId(sceneId, threadId);
+}
+
+void ScriptOpcodes_BBDOU::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->push(0);
+}
+
+void ScriptOpcodes_BBDOU::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(fontId);
+	_vm->setCurrFontId(fontId);
+}
+
+void ScriptOpcodes_BBDOU::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(key);
+	ARG_UINT32(threadId);
+	// TODO _vm->addMenuKey(key, threadId);
+}
+
+void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_prevSceneId = _vm->getCurrentScene();
+	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes_bbdou.h b/engines/illusions/scriptopcodes_bbdou.h
new file mode 100644
index 0000000..28c7987
--- /dev/null
+++ b/engines/illusions/scriptopcodes_bbdou.h
@@ -0,0 +1,128 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTOPCODES_BBDOU_H
+#define ILLUSIONS_SCRIPTOPCODES_BBDOU_H
+
+#include "illusions/scriptopcodes.h"
+#include "common/func.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+class ScriptThread;
+
+class ScriptOpcodes_BBDOU : public ScriptOpcodes {
+public:
+	ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm);
+	~ScriptOpcodes_BBDOU();
+	void initOpcodes();
+	void freeOpcodes();
+protected:
+	IllusionsEngine_BBDOU *_vm;
+
+	// Opcodes
+	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
+	void opYield(ScriptThread *scriptThread, OpCall &opCall);
+	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
+	void opJump(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
+	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
+	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
+	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opNot(ScriptThread *scriptThread, OpCall &opCall);
+	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
+	void opOr(ScriptThread *scriptThread, OpCall &opCall);
+	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
+	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTOPCODES_H
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 9c4f171..715039e 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -34,7 +34,7 @@ void ScriptResourceLoader::load(Resource *resource) {
 	ScriptResource *scriptResource = new ScriptResource();
 	scriptResource->load(resource->_data, resource->_dataSize);
 	
-	_vm->_scriptMan->_scriptResource = scriptResource;
+	_vm->_scriptResource = scriptResource;
 	
 }
 
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 6f6d3a0..78fb8b8 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -34,7 +34,7 @@ ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingT
 	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptCodeIp(scriptCodeIp), _value8(value8),
 	_valueC(valueC), _value10(value10), _sequenceStalled(0) {
 	_type = kTTScriptThread;
-	_tag = _vm->_scriptMan->_activeScenes.getCurrentScene();
+	_tag = _vm->getCurrentScene();
 }
 
 int ScriptThread::onUpdate() {
@@ -56,8 +56,7 @@ int ScriptThread::onUpdate() {
 }
 
 void ScriptThread::execOpcode(OpCall &opCall) {
-	// TODO Clean this up
-	_vm->_scriptMan->_scriptOpcodes->execOpcode(this, opCall);
+	_vm->_scriptOpcodes->execOpcode(this, opCall);
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 818b681..f020423 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -99,11 +99,6 @@ void SequenceOpcodes::freeOpcodes() {
 
 // Opcodes
 
-// Convenience macros
-#define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(1, "ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(1, "ARG_UINT32(" #name " = %08X)", name);
-
 void SequenceOpcodes::opYield(Control *control, OpCall &opCall) {
 	opCall._result = 2;
 }
@@ -138,7 +133,7 @@ void SequenceOpcodes::opEndSequence(Control *control, OpCall &opCall) {
 		control->_actor->_frames = 0;
 		control->_actor->_frameIndex = 0;
 		control->_actor->_newFrameIndex = 0;
-		// TODO _vm->_resSys->unloadResourceById(control->_actor->_sequenceId);
+		_vm->_resSys->unloadResourceById(control->_actor->_sequenceId);
 	}
 	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
 	opCall._result = 1;
@@ -321,7 +316,7 @@ void SequenceOpcodes::opStopSound(Control *control, OpCall &opCall) {
 void SequenceOpcodes::opStartScriptThread(Control *control, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(threadId);
-	_vm->_scriptMan->startScriptThread(threadId, 0, 0, 0, 0);
+	_vm->startScriptThreadSimple(threadId, 0);
 }
 
 void SequenceOpcodes::opPlaceSubActor(Control *control, OpCall &opCall) {
diff --git a/engines/illusions/specialcode.cpp b/engines/illusions/specialcode.cpp
index a412462..3728657 100644
--- a/engines/illusions/specialcode.cpp
+++ b/engines/illusions/specialcode.cpp
@@ -30,14 +30,12 @@ namespace Illusions {
 
 void SpecialCodeLoader::load(Resource *resource) {
 	debug("SpecialCodeLoader::load() Loading special code %08X...", resource->_resId);
-	_vm->_specialCode = new BbdouSpecialCode(_vm);
-	_vm->_specialCode->init();
+	_vm->loadSpecialCode(resource->_resId);
 }
 
 void SpecialCodeLoader::unload(Resource *resource) {
 	debug("SpecialCodeLoader::unload() Unloading special code %08X...", resource->_resId);
-	delete _vm->_specialCode;
-	_vm->_specialCode = 0;
+	_vm->unloadSpecialCode(resource->_resId);
 }
 
 void SpecialCodeLoader::buildFilename(Resource *resource) {
diff --git a/engines/illusions/specialcode.h b/engines/illusions/specialcode.h
index e3ecc8d..9a69b38 100644
--- a/engines/illusions/specialcode.h
+++ b/engines/illusions/specialcode.h
@@ -44,12 +44,9 @@ protected:
 
 class SpecialCode {
 public:
-	SpecialCode(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~SpecialCode() {}
 	virtual void init() = 0;
 	virtual void run(uint32 specialCodeId, OpCall &opCall) = 0;
-protected:
-	IllusionsEngine *_vm;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index 10ff05d..d74ddde 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -52,16 +52,16 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 
 	if (duration)
 		_status = 1;
-	else if (_vm->_scriptMan->checkActiveTalkThreads())
+	else if (_vm->checkActiveTalkThreads())
 		_status = 2;
 	else
 		_status = 3;
 	
 	_flags = 0x0E;
 	
-	_durationMult = _vm->_scriptMan->clipTextDuration(_vm->_scriptMan->_fieldE);
+	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
 	_textDuration = _durationMult;
-	_defDurationMult = _vm->_scriptMan->clipTextDuration(240);
+	_defDurationMult = _vm->clipTextDuration(240);
 	_textStartTime = 0;
 	_textEndTime = 0;
 	_textDurationElapsed = 0;
@@ -74,7 +74,7 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 	_entryTblPtr = 0;
 
 	if (callingThreadId) {
-		Thread *callingThread = _vm->_scriptMan->_threads->findThread(callingThreadId);
+		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
 		if (callingThread)
 			_tag = callingThread->_tag;
 	}
@@ -90,7 +90,7 @@ int TalkThread::onUpdate() {
 
 	case 1:
 		if (isTimerExpired(_voiceStartTime, _voiceEndTime)) {
-			if (_vm->_scriptMan->checkActiveTalkThreads())
+			if (_vm->checkActiveTalkThreads())
 				_status = 2;
 			else
 				_status = 3;
@@ -98,7 +98,7 @@ int TalkThread::onUpdate() {
 		return kTSYield;
 
 	case 2:
-		if (_vm->_scriptMan->checkActiveTalkThreads())
+		if (_vm->checkActiveTalkThreads())
 			return kTSYield;
 		_status = 3;
 		// Fallthrough to status 3
@@ -312,7 +312,7 @@ int TalkThread::insertText() {
 	_entryText = 0;
 	
 	// TODO _vm->getDimensions1(&dimensions);
-	// TODO _vm->insertText(_currEntryText, _vm->_scriptMan->currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
+	// TODO _vm->insertText(_currEntryText, _vm->_currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
 	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
 	// TODO _entryText = outTextPtr;
 	// TODO _vm->getPoint1(&pt);
diff --git a/engines/illusions/timerthread.cpp b/engines/illusions/timerthread.cpp
index bf9c32f..8cd5ea2 100644
--- a/engines/illusions/timerthread.cpp
+++ b/engines/illusions/timerthread.cpp
@@ -38,7 +38,7 @@ TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThr
 	_endTime = _startTime + _duration;
 
 	if (callingThreadId) {
-		Thread *callingThread = _vm->_scriptMan->_threads->findThread(callingThreadId);
+		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
 		if (callingThread)
 			_tag = callingThread->_tag;
 	}


Commit: 67366aa04b723fadec300cc6ce1d5c6ee9241af7
    https://github.com/scummvm/scummvm/commit/67366aa04b723fadec300cc6ce1d5c6ee9241af7
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on Duckman

Changed paths:
  A engines/illusions/illusions_duckman.cpp
  A engines/illusions/illusions_duckman.h
  A engines/illusions/midiresource.cpp
  A engines/illusions/midiresource.h
  A engines/illusions/scriptopcodes_duckman.cpp
  A engines/illusions/scriptopcodes_duckman.h
  A engines/illusions/talkthread_duckman.cpp
  A engines/illusions/talkthread_duckman.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/cursor.cpp
    engines/illusions/cursor.h
    engines/illusions/detection.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_bbdou.h
    engines/illusions/module.mk
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_bbdou.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/scriptthread.cpp
    engines/illusions/scriptthread.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/talkthread.cpp
    engines/illusions/thread.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 1d9aae0..bfb6fe2 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -226,7 +226,7 @@ void Control::pause() {
 	_vm->_dict->setObjectControl(_objectId, 0);
 
 	if (_objectId == 0x40004)
-		_vm->_cursor->setControl(0);
+		_vm->setCursorControl(0);
 
 	if (_actor && !(_actor->_flags & 0x0200))
 		_actor->destroySurface();
@@ -238,7 +238,7 @@ void Control::unpause() {
 	_vm->_dict->setObjectControl(_objectId, this);
 
 	if (_objectId == 0x40004)
-		_vm->_cursor->setControl(this);
+		_vm->setCursorControl(this);
   
 	if (_actor && !(_actor->_flags & 0x0200)) {
 		SurfInfo surfInfo;
@@ -254,7 +254,7 @@ void Control::unpause() {
 
 void Control::appearActor() {
 	if (_objectId == 0x40004) {
-		_vm->_cursor->show();
+		_vm->showCursor();
 	} else {
 		if (_actor->_frameIndex || _actorTypeId == 0x50004)
 			_actor->_flags |= 1;
@@ -270,7 +270,7 @@ void Control::appearActor() {
 
 void Control::disappearActor() {
 	if (_objectId == 0x40004) {
-		_vm->_cursor->hide();
+		_vm->hideCursor();
 	} else {
 		_actor->_flags &= ~1;
 		_actor->_flags &= ~0x1000;
@@ -540,8 +540,10 @@ void Control::stopActor() {
 		_actor->_pathPointIndex = 0;
 		_actor->_walkCallerThreadId1 = 0;
 	}
-	_vm->notifyThreadId(_actor->_notifyId3C);
-	_vm->notifyThreadId(_actor->_notifyThreadId1);
+	if (_vm->getGameId() == kGameIdBBDOU) {
+		_vm->notifyThreadId(_actor->_notifyId3C);
+		_vm->notifyThreadId(_actor->_notifyThreadId1);
+	}
 }
 
 void Control::startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId) {
@@ -623,6 +625,7 @@ void Control::sequenceActor() {
 			appearActor();
 			_actor->_flags &= ~0x1000;
 		}
+		debug(1, "New frame OK");
 	}
 	
 	if (sequenceFinished) {
@@ -905,22 +908,43 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_notifyThreadId1 = notifyThreadId;
 	_actor->_notifyId3C = 0;
 	_actor->_walkCallerThreadId1 = 0;
+	_actor->_entryTblPtr = 0;
 	
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 
+	if (!sequence && _vm->getGameId() == kGameIdDuckman) {
+		debug("Load external sequence...");
+		_vm->_resSys->loadResource(0x00060000 | (sequenceId & 0xFFFF), _vm->getCurrentScene(), 0);
+		sequence = _vm->_dict->findSequence(sequenceId);
+		_actor->_flags |= 0x800;
+	}
+
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
+	
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
-	_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
+
+    if (_vm->getGameId() == kGameIdBBDOU) {
+		_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
+	} else if (_vm->getGameId() == kGameIdDuckman) {
+		_actor->_seqCodeValue2 = value == 1 ? 350 : 750;
+	}
+
 	_actor->initSequenceStack();
-	stopSequenceActor();
+	
+	if (_vm->getGameId() == kGameIdBBDOU)
+		stopSequenceActor();
+	
 	_actor->_linkIndex2 = 0;
+	
 	if (entryTblPtr) {
 		_actor->_flags |= 0x80;
 		_actor->_entryTblPtr = entryTblPtr;
-		_actor->_notifyThreadId1 = 0;
-		_actor->_notifyThreadId2 = notifyThreadId;
+		if (_vm->getGameId() == kGameIdBBDOU) {
+			_actor->_notifyThreadId1 = 0;
+			_actor->_notifyThreadId2 = notifyThreadId;
+		}
 	}
 
 	sequenceActor();
@@ -958,7 +982,6 @@ void Controls::placeBackgroundObject(BackgroundObject *backgroundObject) {
 void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId) {
 	Control *control = newControl();
 	Actor *actor = newActor();
-
 	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
 	
 	control->_objectId = objectId;
@@ -967,8 +990,9 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	control->readPointsConfig(actorType->_pointsConfig);
 	control->_actorTypeId = actorTypeId;
 	control->_actor = actor;
-	if (actorTypeId == 0x50001 && objectId == 0x40004)
-		actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Cursor>(_vm->_cursor, &Cursor::cursorControlRoutine));
+
+	if (_vm->isCursorObject(actorTypeId, objectId))
+		_vm->setCursorControlRoutine(control);
 		
 	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
 		actor->createSurface(actorType->_surfInfo);
@@ -1015,12 +1039,11 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	_controls.push_back(control);
 	_vm->_dict->setObjectControl(objectId, control);
 
-	if (actorTypeId == 0x50001 && objectId == 0x40004)
-		_vm->_cursor->place(control, sequenceId);
+	if (_vm->isCursorObject(actorTypeId, objectId))
+		_vm->placeCursorControl(control, sequenceId);
 
 	control->_flags |= 0x01;
 	actor->_flags |= 0x1000;
-
 	control->startSequenceActor(sequenceId, 2, notifyThreadId);
 }
 
@@ -1083,6 +1106,25 @@ void Controls::placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId,
 	subActor->_linkIndex = linkIndex;
 }
 
+void Controls::destroyControls() {
+	ItemsIterator it = _controls.begin();
+	while (it != _controls.end()) {
+		destroyControl(*it);
+		it = _controls.erase(it);
+	}
+}
+
+void Controls::destroyActiveControls() {
+	ItemsIterator it = _controls.begin();
+	while (it != _controls.end()) {
+		if ((*it)->_pauseCtr <= 0) {
+			destroyControl(*it);
+			it = _controls.erase(it);
+		} else
+			++it;			
+	}
+}
+
 void Controls::destroyControlsByTag(uint32 tag) {
 	ItemsIterator it = _controls.begin();
 	while (it != _controls.end()) {
@@ -1105,6 +1147,24 @@ void Controls::threadIsDead(uint32 threadId) {
 	}
 }
 
+void Controls::pauseControls() {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		++control->_pauseCtr;
+		if (control->_pauseCtr == 1)
+			control->pause();
+	}
+}
+
+void Controls::unpauseControls() {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		--control->_pauseCtr;
+		if (control->_pauseCtr == 0)
+			control->unpause();
+	}
+}
+
 void Controls::pauseControlsByTag(uint32 tag) {
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *control = *it;
@@ -1225,7 +1285,7 @@ void Controls::destroyControl(Control *control) {
 		_vm->_dict->setObjectControl(control->_objectId, 0);
 
 	if (control->_objectId == 0x40004 && control->_pauseCtr <= 0)
-		_vm->_cursor->setControl(0);
+		_vm->setCursorControl(0);
 	
 	if (control->_actor) {
 		if (control->_actor->_pathNode && (control->_actor->_flags & 0x400))
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 4c937ef..d1740eb 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -220,8 +220,12 @@ public:
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
 	void placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId);
+	void destroyControls();
+	void destroyActiveControls();
 	void destroyControlsByTag(uint32 tag);
 	void threadIsDead(uint32 threadId);
+	void pauseControls();
+	void unpauseControls();
 	void pauseControlsByTag(uint32 tag);
 	void unpauseControlsByTag(uint32 tag);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index 058b093..d3762f9 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -32,7 +32,7 @@ void ActorResourceLoader::load(Resource *resource) {
 	debug("ActorResourceLoader::load() Loading actor %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	ActorResource *actorResource = new ActorResource();
-	actorResource->load(resource->_data, resource->_dataSize);
+	actorResource->load(resource);
 	resource->_refId = actorResource;
 	
 	ActorItem *actorItem = _vm->_actorItems->allocActorItem();
@@ -151,7 +151,10 @@ ActorResource::ActorResource() {
 ActorResource::~ActorResource() {
 }
 
-void ActorResource::load(byte *data, uint32 dataSize) {
+void ActorResource::load(Resource *resource) {
+	byte *data = resource->_data;
+	uint32 dataSize = resource->_dataSize;
+	
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
 
 	_totalSize = stream.readUint32LE();
@@ -196,10 +199,14 @@ void ActorResource::load(byte *data, uint32 dataSize) {
 	}
 	
 	// Load named points
-	// The count isn't stored explicitly so calculate it
-	uint namedPointsCount = (actorTypesOffs - 0x20) / 8;
-	stream.seek(0x20);
-	_namedPoints.load(namedPointsCount, stream);
+	if (resource->_gameId == kGameIdBBDOU) {
+		// The count isn't stored explicitly so calculate it
+		uint namedPointsCount = (actorTypesOffs - 0x20) / 8;
+		stream.seek(0x20);
+		_namedPoints.load(namedPointsCount, stream);
+	}
+	
+	debug("ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
 
 }
 
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index 78cfc19..3a4c462 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -83,7 +83,7 @@ class ActorResource {
 public:
 	ActorResource();
 	~ActorResource();
-	void load(byte *data, uint32 dataSize);
+	void load(Resource *resource);
 	bool containsSequence(Sequence *sequence);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 754b9d2..a4111c7 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -53,8 +53,12 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	// TODO camera_fadeClear();
 	int index = backgroundItem->_bgRes->findMasterBgIndex();
 	_vm->_camera->set(backgroundItem->_bgRes->_bgInfos[index - 1]._panPoint, backgroundItem->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
+
+	if (backgroundItem->_bgRes->_palettesCount > 0) {
+		Palette *palette = &backgroundItem->_bgRes->_palettes[backgroundItem->_bgRes->_paletteIndex - 1];
+		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
+	}
 	
-	// NOTE Skipped palette loading (not used in BBDOU)
 }
 
 void BackgroundResourceLoader::unload(Resource *resource) {
@@ -150,6 +154,15 @@ int ScaleLayer::getScale(Common::Point pos) {
 	return _values[pos.y];
 }
 
+// Palette
+
+void Palette::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_count = stream.readUint16LE();
+	_unk = stream.readUint16LE();
+	uint32 paletteOffs = stream.readUint32LE();
+	_palette = dataStart + paletteOffs;
+}
+
 // BackgroundObject
 
 void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
@@ -176,13 +189,15 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
 	// TODO A lot
 	
+	stream.seek(8);
+	_paletteIndex = stream.readUint16LE();
+	
 	// Load background pixels
 	stream.seek(0x0A);
 	_bgInfosCount = stream.readUint16LE();
 	_bgInfos = new BgInfo[_bgInfosCount];
 	stream.seek(0x20);
 	uint32 bgInfosOffs = stream.readUint32LE();
-	debug("_bgInfosCount: %d", _bgInfosCount);
 	for (uint i = 0; i < _bgInfosCount; ++i) {
 		stream.seek(bgInfosOffs + i * 0x1C);
 		_bgInfos[i].load(data, stream);
@@ -232,6 +247,18 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	stream.seek(namedPointsOffs);
 	_namedPoints.load(namedPointsCount, stream);
 
+	// Load palettes
+	stream.seek(0x18);
+	_palettesCount = stream.readUint16LE();
+	_palettes = new Palette[_palettesCount];
+	stream.seek(0x3C);
+	uint32 palettesOffs = stream.readUint32LE();
+	debug(0, "_palettesCount: %d", _palettesCount);
+	for (uint i = 0; i < _palettesCount; ++i) {
+		stream.seek(palettesOffs + i * 8);
+		_palettes[i].load(data, stream);
+	}
+
 }
 
 int BackgroundResource::findMasterBgIndex() {
@@ -255,7 +282,7 @@ bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt)
 
 // BackgroundItem
 
-BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0) {
+BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
 }
 
 BackgroundItem::~BackgroundItem() {
@@ -282,6 +309,41 @@ void BackgroundItem::freeSurface() {
 }
 
 void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	switch (_vm->getGameId()) {
+	case kGameIdDuckman:
+		drawTiles8(surface, tileMap, tilePixels);
+		break;
+	case kGameIdBBDOU:
+		drawTiles16(surface, tileMap, tilePixels);
+		break;
+	}
+}
+
+void BackgroundItem::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	const int kTileWidth = 32;
+	const int kTileHeight = 8;
+	const int kTileSize = kTileWidth * kTileHeight;
+	uint tileMapIndex = 0;
+	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
+		int tileDestY = tileY * kTileHeight;
+		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
+		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
+			int tileDestX = tileX * kTileWidth;
+			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
+			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
+			++tileMapIndex;
+			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
+			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
+			for (int h = 0; h < tileDestH; ++h) {
+				memcpy(dst, src, tileDestW);
+				dst += surface->pitch;
+				src += kTileWidth;
+			}
+		}
+	}
+}
+
+void BackgroundItem::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
 	const int kTileWidth = 32;
 	const int kTileHeight = 8;
 	const int kTileSize = kTileWidth * kTileHeight * 2;
@@ -318,10 +380,8 @@ void BackgroundItem::pause() {
 		*/
 		// TODO _vm->setDefPointDimensions1();
 		_vm->_camera->getActiveState(_savedCameraState);
-		/* Unused
-		_savedPalette = malloc(1024);
-		savePalette(_savedPalette);
-		*/
+		_savedPalette = new byte[1024];
+		_vm->_screen->getPalette(_savedPalette);
 		freeSurface();
 	}
 }
@@ -335,11 +395,9 @@ void BackgroundItem::unpause() {
 			krndictAddID(_bgRes->_item48s[i].id, _bgRes->_item48s[i]);
 		*/
 		initSurface();
-		/* Unused
-		restorePalette(_savedPalette, 1, 256);
-		free(_savedPalette);
+		_vm->_screen->setPalette(_savedPalette, 1, 256);
+		delete[] _savedPalette;
 		_savedPalette = 0;
-		*/
 		// TODO _vm->_screen->_fadeClear();
 		_vm->_camera->setActiveState(_savedCameraState);
 		_vm->_backgroundItems->refreshPan();
@@ -437,8 +495,4 @@ bool BackgroundItems::findActiveBackgroundNamedPoint(uint32 namedPointId, Common
 	return backgroundResource ? backgroundResource->findNamedPoint(namedPointId, pt) : false;
 }
 
-BackgroundItem *BackgroundItems::debugFirst() {
-	return *(_items.begin());
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 741047f..75c867a 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -101,6 +101,13 @@ points dd ?
 BgResource_PathWalkPoints ends
 #endif
 
+struct Palette {
+	uint16 _count;
+	uint16 _unk;
+	byte *_palette;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
 struct BackgroundObject {
 	uint32 _objectId;
 	uint16 _flags;
@@ -120,6 +127,8 @@ public:
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 
+	uint _paletteIndex;
+
 	uint _bgInfosCount;
 	BgInfo *_bgInfos;
 	
@@ -133,6 +142,9 @@ public:
 	BackgroundObject *_backgroundObjects;
 	
 	NamedPoints _namedPoints;
+	
+	uint _palettesCount;
+	Palette *_palettes;
 
 };
 
@@ -156,7 +168,9 @@ public:
 	Common::Point _panPoints[kMaxBackgroundItemSurfaces];
 	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
 	CameraState _savedCameraState;
-	// TODO? byte *savedPalette;
+	byte *_savedPalette;
+	void drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
+	void drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 };
 
 class BackgroundItems {
@@ -173,7 +187,6 @@ public:
 	WidthHeight getMasterBgDimensions();
 	void refreshPan();
 	bool findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt);
-	BackgroundItem *debugFirst();
 //protected:
 public:
 	typedef Common::List<BackgroundItem*> Items;
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
index ccbff95..b591ae6 100644
--- a/engines/illusions/cursor.cpp
+++ b/engines/illusions/cursor.cpp
@@ -79,15 +79,4 @@ void Cursor::hide() {
 	}
 }
 
-void Cursor::cursorControlRoutine(Control *control, uint32 deltaTime) {
-	_control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (_control->_actor->_flags & 1) {
-		if (_status == 2) {
-			// Unused nullsub_1(control);
-		} else if (_status == 3) {
-			// TODO _vm->_shellMgr->handleMouse(_control);
-		}
-	}
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/cursor.h b/engines/illusions/cursor.h
index 3179b71..e528f09 100644
--- a/engines/illusions/cursor.h
+++ b/engines/illusions/cursor.h
@@ -37,7 +37,8 @@ public:
 	void show();
 	void hide();
 	void cursorControlRoutine(Control *control, uint32 deltaTime);
-protected:
+//protected:
+public:
 	IllusionsEngine *_vm;
 	Control *_control;
 	uint32 _sequenceId;
diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index c42909c..5c1e53a 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/illusions_bbdou.h"
+#include "illusions/illusions_duckman.h"
 
 #include "common/config-manager.h"
 #include "engines/advancedDetector.h"
@@ -33,16 +34,12 @@
 static const PlainGameDescriptor illusionsGames[] = {
 	{ "illusions", "Illusions engine game" },
 	{ "bbdou", "Beavis and Butthead Do U" },
+	{ "duckman", "Duckman" },
 	{ 0, 0 }
 };
 
 namespace Illusions {
 
-struct IllusionsGameDescription {
-	ADGameDescription desc;
-	int gameId;
-};
-
 static const IllusionsGameDescription gameDescriptions[] = {
 	{
 		{
@@ -57,6 +54,19 @@ static const IllusionsGameDescription gameDescriptions[] = {
 		kGameIdBBDOU
 	},
 
+	{
+		{
+			"duckman",
+			0,
+			AD_ENTRY1s("duckman.gam", "172c0514f3793041718159cf9cf9935f", 29560832),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameIdDuckman
+	},
+
 	{AD_TABLE_END_MARKER, 0}
 };
 
@@ -172,7 +182,10 @@ bool IllusionsMetaEngine::createInstance(OSystem *syst, Engine **engine, const A
 	if (gd) {
 		switch (gd->gameId) {
 		case Illusions::kGameIdBBDOU:
-			*engine = new Illusions::IllusionsEngine_BBDOU(syst, desc);
+			*engine = new Illusions::IllusionsEngine_BBDOU(syst, gd);
+			break;
+		case Illusions::kGameIdDuckman:
+			*engine = new Illusions::IllusionsEngine_Duckman(syst, gd);
 			break;
 		default:
 			error("Unknown game id");
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 60d9b5b..4d9f4a0 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -58,7 +58,7 @@
 
 namespace Illusions {
 
-IllusionsEngine::IllusionsEngine(OSystem *syst, const ADGameDescription *gd) :
+IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *gd) :
 	Engine(syst), _gameDescription(gd) {
 	
 	_random = new Common::RandomSource("illusions");
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index a9b8555..ff7d219 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -35,6 +35,7 @@
 #include "common/system.h"
 #include "common/winexe.h"
 #include "common/winexe_pe.h"
+#include "engines/advancedDetector.h"
 #include "engines/engine.h"
 #include "graphics/surface.h"
 
@@ -74,15 +75,21 @@ enum {
 	kGameIdDuckman = 2
 };
 
+struct IllusionsGameDescription {
+	ADGameDescription desc;
+	int gameId;
+};
+
 class IllusionsEngine : public Engine {
 public:
-	IllusionsEngine(OSystem *syst, const ADGameDescription *gd);
+	IllusionsEngine(OSystem *syst, const IllusionsGameDescription *gd);
 	~IllusionsEngine();
 	const Common::String getTargetName() { return _targetName; }
 private:
-	const ADGameDescription *_gameDescription;
+	const IllusionsGameDescription *_gameDescription;
 	Graphics::PixelFormat _pixelFormat;
-public:	
+public:
+	
 	Common::RandomSource *_random;
 	Dictionary *_dict;
 	ResourceSystem *_resSys;
@@ -99,7 +106,6 @@ public:
 	ScriptOpcodes *_scriptOpcodes;
 	SpecialCode *_specialCode;
 	ThreadList *_threads;
-	Cursor *_cursor;
 	
 	ScriptResource *_scriptResource;
 	
@@ -114,6 +120,10 @@ public:
 
 	int16 _menuChoiceOfs;
 
+	int getGameId() const {
+		return _gameDescription->gameId;
+	}
+
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 	uint32 getElapsedUpdateTime();
 	int updateActors();
@@ -133,7 +143,7 @@ public:
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
 	uint32 clipTextDuration(uint32 duration);
-	
+
 	virtual void loadSpecialCode(uint32 resId) = 0;
 	virtual void unloadSpecialCode(uint32 resId) = 0;
 	virtual void notifyThreadId(uint32 &threadId) = 0;
@@ -143,6 +153,13 @@ public:
 	virtual uint32 getPrevScene() = 0;	
 	virtual uint32 getCurrentScene() = 0;
 
+	virtual bool isCursorObject(uint32 actorTypeId, uint32 objectId) = 0;
+	virtual void setCursorControlRoutine(Control *control) = 0;
+	virtual void placeCursorControl(Control *control, uint32 sequenceId) = 0;
+	virtual void setCursorControl(Control *control) = 0;
+	virtual void showCursor() = 0;
+	virtual void hideCursor() = 0;
+
 	virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
 	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 76d476a..84b3876 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -172,7 +172,7 @@ bool ActiveScenes::isSceneActive(uint32 sceneId) {
 
 // IllusionsEngine_BBDOU
 
-IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const ADGameDescription *gd)
+IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd)
 	: IllusionsEngine(syst, gd) {
 }
 
@@ -188,12 +188,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
 
-	Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
-	initGraphics(640, 480, true, &pixelFormat16);
-	
 	_dict = new Dictionary();
 
-	_resSys = new ResourceSystem();
+	_resSys = new ResourceSystem(this);
 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
 	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
@@ -203,7 +200,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
 	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
 
-	_screen = new Screen(this);
+	_screen = new Screen(this, 640, 480, 16);
 	_input = new Input();	
 	_scriptMan = new ScriptMan(this);
 	_actorItems = new ActorItems(this);
@@ -281,12 +278,9 @@ bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
 
 bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
 	uint32 codeOffs;
-	bool r =
+	return 
 		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
 		findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
-	debug(3, "causeIsDeclared() sceneId: %08X; verbId: %08X; objectId2: %08X; objectId: %08X -> %d",
-		sceneId, verbId, objectId2, objectId, r);
-	return r;
 }
 
 void IllusionsEngine_BBDOU::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
@@ -352,6 +346,45 @@ uint32 IllusionsEngine_BBDOU::getPrevScene() {
 	return _prevSceneId;
 }
 
+bool IllusionsEngine_BBDOU::isCursorObject(uint32 actorTypeId, uint32 objectId) {
+	return actorTypeId == 0x50001 && objectId == 0x40004;
+}
+
+void IllusionsEngine_BBDOU::setCursorControlRoutine(Control *control) {
+	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_BBDOU>
+		(this, &IllusionsEngine_BBDOU::cursorControlRoutine));
+}
+
+void IllusionsEngine_BBDOU::placeCursorControl(Control *control, uint32 sequenceId) {
+	_cursor->place(control, sequenceId);
+}
+
+void IllusionsEngine_BBDOU::setCursorControl(Control *control) {
+	_cursor->setControl(control);
+}
+
+void IllusionsEngine_BBDOU::showCursor() {
+	_cursor->show();
+}
+
+void IllusionsEngine_BBDOU::hideCursor() {
+	_cursor->hide();
+}
+
+void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
+	control->_actor->_seqCodeValue1 = 100 * deltaTime;
+	if (control->_actor->_flags & 1) {
+		switch (_cursor->_status) {
+		case 2:
+			// Unused nullsub_1(control);
+			break;
+		case 3:
+			// TODO _vm->_shellMgr->handleMouse(control);
+			break;
+		}
+	}
+}
+
 void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
 	startScriptThread(threadId, callingThreadId, 0, 0, 0);
 }
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
index cd6d8d7..d50fe4c 100644
--- a/engines/illusions/illusions_bbdou.h
+++ b/engines/illusions/illusions_bbdou.h
@@ -83,13 +83,14 @@ protected:
 
 class IllusionsEngine_BBDOU : public IllusionsEngine {
 public:
-	IllusionsEngine_BBDOU(OSystem *syst, const ADGameDescription *gd);
+	IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd);
 protected:
 	virtual Common::Error run();
 	virtual bool hasFeature(EngineFeature f) const;
 public:	
 	ScriptMan *_scriptMan;
 	TriggerFunctions *_triggerFunctions;
+	Cursor *_cursor;
 
 	ActiveScenes _activeScenes;
 	uint32 _prevSceneId;
@@ -113,8 +114,16 @@ public:
 	Control *getObjectControl(uint32 objectId);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	uint32 getPriorityFromBase(int16 priority);
-	uint32 getPrevScene();	
 	uint32 getCurrentScene();
+	uint32 getPrevScene();	
+	
+	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
+	void setCursorControlRoutine(Control *control);
+	void placeCursorControl(Control *control, uint32 sequenceId);
+	void setCursorControl(Control *control);
+	void showCursor();
+	void hideCursor();
+	void cursorControlRoutine(Control *control, uint32 deltaTime);
 
 	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
 	void startScriptThread(uint32 threadId, uint32 callingThreadId,
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
new file mode 100644
index 0000000..74c2788
--- /dev/null
+++ b/engines/illusions/illusions_duckman.cpp
@@ -0,0 +1,440 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/actorresource.h"
+#include "illusions/backgroundresource.h"
+#include "illusions/camera.h"
+#include "illusions/cursor.h"
+#include "illusions/dictionary.h"
+#include "illusions/fontresource.h"
+#include "illusions/graphics.h"
+#include "illusions/input.h"
+#include "illusions/midiresource.h"
+#include "illusions/resourcesystem.h"
+#include "illusions/screen.h"
+#include "illusions/scriptopcodes_duckman.h"
+#include "illusions/scriptresource.h"
+#include "illusions/scriptman.h"
+#include "illusions/soundresource.h"
+#include "illusions/specialcode.h"
+//TODO#include "illusions/bbdou/bbdou_specialcode.h"
+#include "illusions/talkresource.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+
+#include "illusions/abortablethread.h"
+#include "illusions/scriptthread.h"
+#include "illusions/talkthread_duckman.h"
+#include "illusions/timerthread.h"
+
+#include "audio/audiostream.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/timer.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/font.h"
+#include "graphics/fontman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+// IllusionsEngine_Duckman
+
+IllusionsEngine_Duckman::IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd)
+	: IllusionsEngine(syst, gd) {
+}
+
+Common::Error IllusionsEngine_Duckman::run() {
+
+	// Init search paths
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "x");// DEBUG until gam reader is done
+
+	_dict = new Dictionary();
+
+	_resSys = new ResourceSystem(this);
+	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
+	_resSys->addResourceLoader(0x000A0000, new MidiGroupResourceLoader(this));
+	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
+	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
+	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
+
+	_screen = new Screen(this, 320, 200, 8);
+	_input = new Input();	
+	_actorItems = new ActorItems(this);
+	_backgroundItems = new BackgroundItems(this);
+	_camera = new Camera(this);
+	_controls = new Controls(this);
+	_talkItems = new TalkItems(this);
+	_threads = new ThreadList(this);
+
+	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
+	_stack = new ScriptStack();
+	
+	// TODO Move to own class
+	_resGetCtr = 0;
+	_unpauseControlActorFlag = false;
+	_lastUpdateTime = 0;
+
+	_pauseCtr = 0;
+	_doScriptThreadInit = false;
+	_field8 = 1;
+	_fieldA = 0;
+	_fieldE = 240;
+	
+	_globalSceneId = 0x00010003;	
+	
+	_resSys->loadResource(0x000D0001, 0x00010001, 0);
+
+    initActiveScenes();
+	startScriptThread(0x00020004, 0);
+	_doScriptThreadInit = true;
+
+	while (!shouldQuit()) {
+		_threads->updateThreads();
+		updateActors();
+		updateSequences();
+		updateGraphics();
+		_screen->updateSprites();
+		_screen->updatePalette();
+		_system->updateScreen();
+		updateEvents();
+		_system->delayMillis(10);
+	}
+
+	delete _stack;
+	delete _scriptOpcodes;
+
+	delete _threads;
+	delete _talkItems;
+	delete _controls;
+	delete _camera;
+	delete _backgroundItems;
+	delete _actorItems;
+	delete _input;
+	delete _screen;
+	delete _resSys;
+	delete _dict;
+	
+	debug("Ok");
+	
+	return Common::kNoError;
+}
+
+bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
+	return
+		false;
+		/*
+		(f == kSupportsRTL) ||
+		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsSavingDuringRuntime);
+		*/
+}
+
+void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
+//TODO	_specialCode = new BbdouSpecialCode(this);
+//TODO	_specialCode->init();
+}
+
+void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
+//TODO	delete _specialCode;
+//TODO	_specialCode = 0;
+}
+
+void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
+	if (threadId) {
+		uint32 tempThreadId = threadId;
+		threadId = 0;
+		_threads->notifyId(tempThreadId);
+	}
+}
+
+Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
+	return _dict->getObjectControl(objectId);
+}
+
+Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
+	Common::Point pt;
+	Common::Point currPan = _camera->getCurrentPan();
+	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt)) {
+		return pt;
+	} else if (namedPointId - 0x00070001 > 209) {
+      if (_controls->findNamedPoint(namedPointId, pt)) {
+      	return pt;
+	  } else {
+	  	return currPan;
+	  }
+	} else {
+		// TODO
+		//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+		return Common::Point(0, 0);
+	}
+}
+
+uint32 IllusionsEngine_Duckman::getPriorityFromBase(int16 priority) {
+	return 32000000 * priority;
+}
+
+uint32 IllusionsEngine_Duckman::getCurrentScene() {
+	return _activeScenes[_activeScenesCount];
+}
+
+uint32 IllusionsEngine_Duckman::getPrevScene() {
+	uint index = _activeScenesCount - 1;
+	if (_activeScenesCount == 1)
+		index = 5;
+	return _activeScenes[index];
+}
+
+bool IllusionsEngine_Duckman::isCursorObject(uint32 actorTypeId, uint32 objectId) {
+	return actorTypeId == 0x50001;
+}
+
+void IllusionsEngine_Duckman::setCursorControlRoutine(Control *control) {
+	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_Duckman>
+		(this, &IllusionsEngine_Duckman::cursorControlRoutine));
+}
+
+void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequenceId) {
+	// TODO
+	control->_actor->_actorIndex = 2;
+	// TODO _cursor->place(control, sequenceId);
+}
+
+void IllusionsEngine_Duckman::setCursorControl(Control *control) {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::showCursor() {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::hideCursor() {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
+	control->_actor->_seqCodeValue1 = 100 * deltaTime;
+	// TODO
+}
+
+void IllusionsEngine_Duckman::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
+	startScriptThread(threadId, callingThreadId);
+}
+
+void IllusionsEngine_Duckman::startScriptThread(uint32 threadId, uint32 callingThreadId) {
+	debug(2, "Starting script thread %08X", threadId);
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp);
+}
+
+uint32 IllusionsEngine_Duckman::startAbortableTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, true);
+}
+
+uint32 IllusionsEngine_Duckman::startTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, false);
+}
+
+uint32 IllusionsEngine_Duckman::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting abortable thread %08X", tempThreadId);
+	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
+	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
+		scriptThreadId, scriptCodeIp2);
+	_threads->startThread(abortableThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_Duckman::startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
+	uint32 sequenceId2, uint32 callingThreadId) {
+	debug(2, "Starting talk thread");
+	uint32 tempThreadId = newTempThreadId();
+	TalkThread_Duckman *talkThread = new TalkThread_Duckman(this, tempThreadId, callingThreadId, 0,
+		objectId, talkId, sequenceId1, sequenceId2);
+	_threads->startThread(talkThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_Duckman::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting temp script thread %08X", tempThreadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp);
+	return tempThreadId;
+}
+
+void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp) {
+	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
+		scriptCodeIp, 0, 0, 0);
+	_threads->startThread(scriptThread);
+	if (_pauseCtr > 0)
+		scriptThread->pause();
+	if (_doScriptThreadInit) {
+		int updateResult = kTSRun;
+		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
+			updateResult = scriptThread->update();
+	}
+}
+
+uint32 IllusionsEngine_Duckman::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
+	uint32 tempThreadId = newTempThreadId();
+	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
+		duration, isAbortable);
+	_threads->startThread(timerThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_Duckman::newTempThreadId() {
+	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
+	if (threadId > 65535) {
+		_nextTempThreadId = 0;
+		threadId = 2 * _scriptResource->_codeCount;
+	}
+	++_nextTempThreadId;
+	return 0x00020000 | threadId;
+}
+
+void IllusionsEngine_Duckman::initActiveScenes() {
+	_activeScenesCount = 0;
+	_activeScenes[0] = 0xEFEF;
+	pushActiveScene(0x10000);
+}
+
+void IllusionsEngine_Duckman::pushActiveScene(uint32 sceneId) {
+	++_activeScenesCount;
+	if (_activeScenesCount >= 6)
+		_activeScenesCount = 1;
+	_activeScenes[_activeScenesCount] = sceneId;
+}
+
+void IllusionsEngine_Duckman::popActiveScene() {
+	--_activeScenesCount;
+	if (_activeScenesCount == 0)
+		_activeScenesCount = 5;
+}
+
+bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (!progInfo)
+		return false;
+	pushActiveScene(sceneId);
+	uint resourcesCount;
+	uint32 *resources;
+	progInfo->getResources(resourcesCount, resources);
+	for (uint i = 0; i < resourcesCount; ++i)
+		_resSys->loadResource(resources[i], sceneId, 0);
+	return true;
+}
+
+bool IllusionsEngine_Duckman::enterScene(uint32 sceneId, uint32 threadId) {
+	if (loadScene(sceneId)) {
+		if (threadId)
+			startScriptThread(threadId, 0);
+		return true;
+	}
+	// TODO startScriptThread2(0x10002, 0x20001, 0);
+	return false;
+}
+
+void IllusionsEngine_Duckman::exitScene() {
+	popActiveScene();
+}
+
+bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId) {
+	uint32 currSceneId = getCurrentScene();
+	if (currSceneId != 0x10003)
+		dumpCurrSceneFiles(currSceneId, callerThreadId);
+	_threads->terminateThreads(callerThreadId);
+	_controls->destroyControls();
+	_resSys->unloadSceneResources(0x10003, 0x10001);
+	if (enterScene(sceneId, threadId)) {
+		// TODO GameStates_writeStates(sceneId, threadId);
+		return true;
+	}
+	return false;
+}
+
+void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
+	_threads->suspendThreads(threadId);
+	_controls->pauseControls();
+	_actorItems->pauseByTag(sceneId);
+	_backgroundItems->pauseByTag(sceneId);
+}
+
+void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
+	_backgroundItems->unpauseByTag(sceneId);
+	_actorItems->unpauseByTag(sceneId);
+	_controls->unpauseControls();
+	_threads->notifyThreads(threadId);
+}
+
+void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
+	// TODO UpdateFunctions_disableByTag(sceneId);
+	_threads->terminateActiveThreads(threadId);
+	_threads->terminateThreadsByTag(sceneId, threadId);
+	_controls->destroyActiveControls();
+	_resSys->unloadResourcesByTag(sceneId);
+}
+
+void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
+	_theSceneId = theSceneId;
+	_theThreadId = theThreadId;
+}
+
+bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (progInfo)
+		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	return false;
+}
+
+void IllusionsEngine_Duckman::reset() {
+	_scriptResource->_blockCounters.clear();
+	_scriptResource->_properties.clear();
+	// TODO script_sub_417FF0(1, 0);
+}
+
+uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
+	return _scriptResource->getObjectActorTypeId(objectId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
new file mode 100644
index 0000000..f70156a
--- /dev/null
+++ b/engines/illusions/illusions_duckman.h
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ILLUSIONS_DUCKMAN_H
+#define ILLUSIONS_ILLUSIONS_DUCKMAN_H
+
+#include "illusions/illusions.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+class Dictionary;
+class ScriptStack;
+
+class IllusionsEngine_Duckman : public IllusionsEngine {
+public:
+	IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd);
+protected:
+	virtual Common::Error run();
+	virtual bool hasFeature(EngineFeature f) const;
+public:	
+
+	// TODO ActiveScenes _activeScenes;
+	uint32 _prevSceneId;
+	uint32 _theSceneId;
+	uint32 _theThreadId;
+	uint32 _globalSceneId;
+
+	int _pauseCtr;
+	ScriptStack *_stack;
+	bool _doScriptThreadInit;
+
+	uint32 _nextTempThreadId;
+	
+	uint _activeScenesCount;
+	uint32 _activeScenes[6];
+
+	void loadSpecialCode(uint32 resId);
+	void unloadSpecialCode(uint32 resId);
+	void notifyThreadId(uint32 &threadId);
+	Control *getObjectControl(uint32 objectId);
+	Common::Point getNamedPointPosition(uint32 namedPointId);
+	uint32 getPriorityFromBase(int16 priority);
+	uint32 getCurrentScene();
+	uint32 getPrevScene();	
+
+	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
+	void setCursorControlRoutine(Control *control);
+	void placeCursorControl(Control *control, uint32 sequenceId);
+	void setCursorControl(Control *control);
+	void showCursor();
+	void hideCursor();
+	void cursorControlRoutine(Control *control, uint32 deltaTime);
+
+	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
+	void startScriptThread(uint32 threadId, uint32 callingThreadId);
+	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
+	uint32 startTimerThread(uint32 duration, uint32 threadId);
+	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
+	uint32 startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
+		uint32 sequenceId2, uint32 callingThreadId);
+	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp);
+	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
+	uint32 newTempThreadId();
+
+    void initActiveScenes();
+	void pushActiveScene(uint32 sceneId);
+    void popActiveScene();
+	bool loadScene(uint32 sceneId);
+	bool enterScene(uint32 sceneId, uint32 threadId);
+	void exitScene();
+	bool changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId);
+	void enterPause(uint32 sceneId, uint32 threadId);
+	void leavePause(uint32 sceneId, uint32 threadId);
+	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
+	void dumpCurrSceneFiles(uint32 sceneId, uint32 threadId);
+
+	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
+	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
+	void reset();
+	
+	uint32 getObjectActorTypeId(uint32 objectId);
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/midiresource.cpp b/engines/illusions/midiresource.cpp
new file mode 100644
index 0000000..b6e181e
--- /dev/null
+++ b/engines/illusions/midiresource.cpp
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/midiresource.h"
+
+namespace Illusions {
+
+// MidiGroupResourceLoader
+
+void MidiGroupResourceLoader::load(Resource *resource) {
+	debug("MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
+
+    // TODO
+	
+}
+
+void MidiGroupResourceLoader::unload(Resource *resource) {
+}
+
+void MidiGroupResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
+}
+
+bool MidiGroupResourceLoader::isFlag(int flag) {
+	return false;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/midiresource.h b/engines/illusions/midiresource.h
new file mode 100644
index 0000000..54ad876
--- /dev/null
+++ b/engines/illusions/midiresource.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_MIDIRESOURCE_H
+#define ILLUSIONS_MIDIRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class MidiGroupResourceLoader : public BaseResourceLoader {
+public:
+	MidiGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~MidiGroupResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void unload(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 63bf826..4341732 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -18,12 +18,15 @@ MODULE_OBJS := \
 	graphics.o \
 	illusions.o \
 	illusions_bbdou.o \
+	illusions_duckman.o \
 	input.o \
+	midiresource.o \
 	resourcesystem.o \
 	screen.o \
 	scriptman.o \
 	scriptopcodes.o \
 	scriptopcodes_bbdou.o \
+	scriptopcodes_duckman.o \
 	scriptresource.o \
 	scriptthread.o \
 	sequenceopcodes.o \
@@ -31,6 +34,7 @@ MODULE_OBJS := \
 	specialcode.o \
 	talkresource.o \
 	talkthread.o \
+	talkthread_duckman.o \
 	thread.o \
 	time.o \
 	timerthread.o \
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index 01a076b..3d99541 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/resourcesystem.h"
+#include "illusions/illusions.h"
 
 #include "common/algorithm.h"
 #include "common/debug.h"
@@ -38,6 +39,7 @@ void Resource::loadData() {
 	_dataSize = fd.size();
 	_data = (byte*)malloc(_dataSize);
 	fd.read(_data, _dataSize);
+	debug("Resource::loadData() OK");
 }
 
 void Resource::unloadData() {
@@ -50,7 +52,8 @@ void Resource::unloadData() {
 
 // ResourceSystem
 
-ResourceSystem::ResourceSystem() {
+ResourceSystem::ResourceSystem(IllusionsEngine *vm)
+	: _vm(vm) {
 }
 
 ResourceSystem::~ResourceSystem() {
@@ -64,6 +67,7 @@ void ResourceSystem::addResourceLoader(uint32 resTypeId, BaseResourceLoader *res
 }
 
 void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
+	debug("ResourceSystem::loadResource(%08X, %08X, %08X)", resId, tag, threadId);
 	BaseResourceLoader *resourceLoader = getResourceLoader(resId);
 
 	Resource *resource = new Resource();
@@ -72,6 +76,7 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 	resource->_tag = tag;
 	resource->_threadId = threadId;
 	resource->_resourceLoader = resourceLoader;
+	resource->_gameId = _vm->getGameId();
 
 	resourceLoader->buildFilename(resource);
 
@@ -108,6 +113,14 @@ void ResourceSystem::unloadResourcesByTag(uint32 tag) {
 	}
 }
 
+void ResourceSystem::unloadSceneResources(uint32 sceneId1, uint32 sceneId2) {
+	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceNotEqualByScenes(sceneId1, sceneId2));
+	while (it != _resources.end()) {
+		unloadResource(*it);
+		it = Common::find_if(it, _resources.end(), ResourceNotEqualByScenes(sceneId1, sceneId2));
+	}
+}
+
 BaseResourceLoader *ResourceSystem::getResourceLoader(uint32 resId) {
 	ResourceLoadersMapIterator it = _resourceLoaders.find(ResourceTypeId(resId));
 	if (it != _resourceLoaders.end())
@@ -121,6 +134,7 @@ Resource *ResourceSystem::getResource(uint32 resId) {
 }
 
 void ResourceSystem::unloadResource(Resource *resource) {
+	debug("Unloading %08X... (tag: %08X)", resource->_resId, resource->_tag);
 	resource->_resourceLoader->unload(resource);
 	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByValue(resource));
 	if (it != _resources.end())
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index 833a7db..0a214c5 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -36,6 +36,7 @@ namespace Illusions {
 #define ResourceTypeId(x) ((x) & 0xFFFF0000)
 
 class BaseResourceLoader;
+class IllusionsEngine;
 
 struct Resource {
 	bool _loaded;
@@ -46,7 +47,8 @@ struct Resource {
 	uint32 _dataSize;
 	BaseResourceLoader *_resourceLoader;
 	void *_refId;
-	Common::String _filename; // TODO Check if this is needed
+	int _gameId;
+	Common::String _filename;
 	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0),
 	_resourceLoader(0), _refId(0) {}
 	~Resource() {
@@ -80,7 +82,7 @@ public:
 
 class ResourceSystem {
 public:
-	ResourceSystem();
+	ResourceSystem(IllusionsEngine *vm);
 	~ResourceSystem();
 
 	void addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader);
@@ -89,10 +91,12 @@ public:
 	void loadResource(uint32 resId, uint32 tag, uint32 threadId);
 	void unloadResourceById(uint32 resId);
 	void unloadResourcesByTag(uint32 tag);
-
+	void unloadSceneResources(uint32 sceneId1, uint32 sceneId2);
+	
 protected:
 	typedef Common::HashMap<uint32, BaseResourceLoader*> ResourceLoadersMap;
 	typedef ResourceLoadersMap::iterator ResourceLoadersMapIterator;
+	IllusionsEngine *_vm;
 	ResourceLoadersMap _resourceLoaders;
 	BaseResourceLoader *getResourceLoader(uint32 resId);
 
@@ -124,6 +128,14 @@ protected:
 		}
 	};
 
+	struct ResourceNotEqualByScenes : public Common::UnaryFunction<const Resource*, bool> {
+		uint32 _sceneId1, _sceneId2;
+		ResourceNotEqualByScenes(uint32 sceneId1, uint32 sceneId2) : _sceneId1(sceneId1), _sceneId2(sceneId2) {}
+		bool operator()(const Resource *resource) const {
+			return resource->_tag != _sceneId1 && resource->_tag != _sceneId2;
+		}
+	};
+
 	Resource *getResource(uint32 resId);
 	void unloadResource(Resource *resource);
 	
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 9e03a40..0eb7832 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -22,12 +22,15 @@
 
 #include "illusions/illusions.h"
 #include "illusions/screen.h"
+#include "engines/util.h"
+#include "graphics/palette.h"
 
 namespace Illusions {
 
 // SpriteDecompressQueue
 
-SpriteDecompressQueue::SpriteDecompressQueue() {
+SpriteDecompressQueue::SpriteDecompressQueue(Screen *screen)
+	: _screen(screen) {
 }
 
 SpriteDecompressQueue::~SpriteDecompressQueue() {
@@ -56,80 +59,7 @@ void SpriteDecompressQueue::decompressAll() {
 }
 
 void SpriteDecompressQueue::decompress(SpriteDecompressQueueItem *item) {
-	byte *src = item->_compressedPixels;
-	Graphics::Surface *dstSurface = item->_surface;
-	int dstSize = item->_dimensions._width * item->_dimensions._height;
-	int processedSize = 0;
-	int xincr, x, xstart;
-	int yincr, y;
-	
-	// Safeguard
-	if (item->_dimensions._width > item->_surface->w ||
-		item->_dimensions._height > item->_surface->h) {
-		debug("Incorrect frame dimensions (%d, %d <> %d, %d)",
-			item->_dimensions._width, item->_dimensions._height,
-			item->_surface->w, item->_surface->h);
-		return;
-	}
-	
-	if (item->_flags & 1) {
-		x = xstart = item->_dimensions._width - 1;
-		xincr = -1;
-	} else {
-		x = xstart = 0;
-		xincr = 1;
-	}
-
-	if (item->_flags & 2) {
-		y = item->_dimensions._height - 1;
-		yincr = -1;
-	} else {
-		y = 0;
-		yincr = 1;
-	}
-	
-	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
-	
-	while (processedSize < dstSize) {
-		int16 op = READ_LE_UINT16(src);
-		src += 2;
-		if (op & 0x8000) {
-			int runCount = (op & 0x7FFF) + 1;
-			processedSize += runCount;
-			uint16 runColor = READ_LE_UINT16(src);
-			src += 2;
-			while (runCount--) {
-				WRITE_LE_UINT16(dst, runColor);
-				x += xincr;
-				if (x >= item->_dimensions._width || x < 0) {
-					x = xstart;
-					y += yincr;
-					dst = (byte*)dstSurface->getBasePtr(x, y);
-				} else {
-					dst += 2 * xincr;
-				}
-			}
-		} else {
-			int copyCount = op + 1;
-			processedSize += copyCount;
-			while (copyCount--) {
-				uint16 color = READ_LE_UINT16(src);
-				src += 2;
-				WRITE_LE_UINT16(dst, color);
-				x += xincr;
-				if (x >= item->_dimensions._width || x < 0) {
-					x = xstart;
-					y += yincr;
-					dst = (byte*)dstSurface->getBasePtr(x, y);
-				} else {
-					dst += 2 * xincr;
-				}
-			}
-		}
-	}
-
-	*item->_drawFlags &= ~1;
- 
+	_screen->decompressSprite(item);
 }
 
 // SpriteDrawQueue
@@ -258,7 +188,7 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	*/
 
 	// Check if the sprite is on-screen
-	if (dstRect.left >= 640 || dstRect.right <= 0 || dstRect.top >= 480 || dstRect.bottom <= 0)
+	if (dstRect.left >= _screen->getScreenWidth() || dstRect.right <= 0 || dstRect.top >= _screen->getScreenHeight() || dstRect.bottom <= 0)
 		return false;
 
 	// Clip the sprite rect if neccessary
@@ -273,14 +203,14 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 		dstRect.top = 0;
 	}
 
-	if (dstRect.right > 640) {
-		srcRect.right += 100 * (640 - dstRect.right) / item->_scale;
-		dstRect.right = 640;
+	if (dstRect.right > _screen->getScreenWidth()) {
+		srcRect.right += 100 * (_screen->getScreenWidth() - dstRect.right) / item->_scale;
+		dstRect.right = _screen->getScreenWidth();
 	}
 
-	if (dstRect.bottom > 480) {
-		srcRect.bottom += 100 * (480 - dstRect.bottom) / item->_scale;
-		dstRect.bottom = 480;
+	if (dstRect.bottom > _screen->getScreenHeight()) {
+		srcRect.bottom += 100 * (_screen->getScreenHeight() - dstRect.bottom) / item->_scale;
+		dstRect.bottom = _screen->getScreenHeight();
 	}
 
 	return true;
@@ -288,13 +218,24 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 
 // Screen
 
-Screen::Screen(IllusionsEngine *vm)
+Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
 	: _vm(vm), _colorKey2(0) {
 	_displayOn = true;
-	_backSurface = allocSurface(640, 480);
-	_decompressQueue = new SpriteDecompressQueue();
+	_decompressQueue = new SpriteDecompressQueue(this);
 	_drawQueue = new SpriteDrawQueue(this);
 	_colorKey1 = 0xF800 | 0x1F;
+	if (bpp == 8) {
+		initGraphics(width, height, false);
+	} else {
+		Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
+		initGraphics(width, height, true, &pixelFormat16);
+	}
+
+	_backSurface = allocSurface(width, height);
+
+	_needRefreshPalette = false;
+	memset(_mainPalette, 0, sizeof(_mainPalette));
+
 }
 
 Screen::~Screen() {
@@ -336,7 +277,295 @@ void Screen::updateSprites() {
 	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
 }
 
+void Screen::decompressSprite(SpriteDecompressQueueItem *item) {
+	switch (_backSurface->format.bytesPerPixel) {
+	case 1:
+		decompressSprite8(item);
+		break;
+	case 2:
+		decompressSprite16(item);
+		break;
+	default:
+		break;
+	}
+}
+
 void Screen::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
+	switch (_backSurface->format.bytesPerPixel) {
+	case 1:
+		drawSurface8(dstRect, surface, srcRect, scale, flags);
+		break;
+	case 2:
+		drawSurface16(dstRect, surface, srcRect, scale, flags);
+		break;
+	default:
+		break;
+	}
+}
+
+void Screen::setPalette(byte *colors, uint start, uint count) {
+	byte *dstPal = &_mainPalette[3 * (start - 1)];
+	for (uint i = 0; i < count; ++i) {
+		*dstPal++ = *colors++;
+		*dstPal++ = *colors++;
+		*dstPal++ = *colors++;
+		++colors;
+	}
+	// TODO Build colorTransTbl
+	_needRefreshPalette = true;
+}
+
+void Screen::getPalette(byte *colors) {
+	byte *srcPal = _mainPalette;
+	for (uint i = 0; i < 256; ++i) {
+		*colors++ = *srcPal++;
+		*colors++ = *srcPal++;
+		*colors++ = *srcPal++;
+		++colors;
+	}
+}
+
+void Screen::updatePalette() {
+	if (_needRefreshPalette) {
+		// TODO Update fader palette
+		setSystemPalette(_mainPalette);
+		_needRefreshPalette = false;
+	}
+}
+
+void Screen::setSystemPalette(byte *palette) {
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+}
+
+void Screen::decompressSprite8(SpriteDecompressQueueItem *item) {
+	byte *src = item->_compressedPixels;
+	Graphics::Surface *dstSurface = item->_surface;
+	int dstSize = item->_dimensions._width * item->_dimensions._height;
+	int processedSize = 0;
+	int xincr, x, xstart;
+	int yincr, y;
+	
+	*item->_drawFlags &= ~1;
+
+	// Safeguard
+	if (item->_dimensions._width > item->_surface->w ||
+		item->_dimensions._height > item->_surface->h) {
+		debug("Incorrect frame dimensions (%d, %d <> %d, %d)",
+			item->_dimensions._width, item->_dimensions._height,
+			item->_surface->w, item->_surface->h);
+		return;
+	}
+	
+	if (item->_flags & 1) {
+		x = xstart = item->_dimensions._width - 1;
+		xincr = -1;
+	} else {
+		x = xstart = 0;
+		xincr = 1;
+	}
+
+	if (item->_flags & 2) {
+		y = item->_dimensions._height - 1;
+		yincr = -1;
+	} else {
+		y = 0;
+		yincr = 1;
+	}
+	
+	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
+	
+	while (processedSize < dstSize) {
+		byte op = *src++;
+		if (op & 0x80) {
+			int runCount = (op & 0x7F) + 1;
+			processedSize += runCount;
+			byte runColor = *src++;
+			while (runCount--) {
+				*dst = runColor;
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += xincr;
+				}
+			}
+		} else {
+			int copyCount = op + 1;
+			processedSize += copyCount;
+			while (copyCount--) {
+				byte color = *src++;
+				*dst = color;
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += xincr;
+				}
+			}
+		}
+	}
+
+}
+
+void Screen::drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
+	drawSurface81(dstRect.left, dstRect.top, surface, srcRect);
+	/*
+	if (scale == 100) {
+		drawSurface81(dstRect.left, dstRect.top, surface, srcRect);
+	} else {
+		drawSurface82(dstRect, surface, srcRect);
+	}
+	*/
+}
+
+void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// Unscaled
+	const int16 w = srcRect.width();
+	const int16 h = srcRect.height();
+	for (int16 yc = 0; yc < h; ++yc) {
+		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcRect.top + yc);
+		byte *dst = (byte*)_backSurface->getBasePtr(destX, destY + yc);
+		for (int16 xc = 0; xc < w; ++xc) {
+			byte pixel = *src++;
+			if (pixel != 0)
+				*dst = pixel;
+			++dst;				
+		}
+	}
+}
+
+void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
+	// Scaled
+	const int dstWidth = dstRect.width(), dstHeight = dstRect.height();
+	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
+	const int errYStart = srcHeight / dstHeight;
+	const int errYIncr = srcHeight % dstHeight;
+	const int midY = dstHeight / 2;
+	const int errXStart = srcWidth / dstWidth;
+	const int errXIncr = srcWidth % dstWidth;
+	const int midX = dstWidth / 2;
+	int h = dstHeight, errY = 0, skipY, srcY = srcRect.top;
+	byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top);
+	skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1;
+	h -= skipY;
+	while (h-- > 0) {
+		int w = dstWidth, errX = 0, skipX;
+		skipX = (dstWidth < srcWidth) ? 0 : dstWidth / (2*srcWidth) + 1;
+		w -= skipX;
+		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcY);
+		byte *dstRow = dst; 
+		while (w-- > 0) {
+			byte pixel = *src;
+			if (pixel != 0) {
+				*dstRow = pixel;
+			}
+			++dstRow;
+			src += errXStart;
+			errX += errXIncr;
+			if (errX >= dstWidth) {
+				errX -= dstWidth;
+				++src;
+			}
+		}
+		while (skipX-- > 0) {
+			byte pixel = *src;
+			if (pixel != 0)
+				*dstRow = pixel;
+			++src;
+			++dstRow;
+		}
+		dst += _backSurface->pitch;
+		srcY += errYStart;
+		errY += errYIncr;
+		if (errY >= dstHeight) {
+			errY -= dstHeight;
+			++srcY;
+		}
+	}
+}
+
+void Screen::decompressSprite16(SpriteDecompressQueueItem *item) {
+	byte *src = item->_compressedPixels;
+	Graphics::Surface *dstSurface = item->_surface;
+	int dstSize = item->_dimensions._width * item->_dimensions._height;
+	int processedSize = 0;
+	int xincr, x, xstart;
+	int yincr, y;
+	
+	*item->_drawFlags &= ~1;
+
+	// Safeguard
+	if (item->_dimensions._width > item->_surface->w ||
+		item->_dimensions._height > item->_surface->h) {
+		debug("Incorrect frame dimensions (%d, %d <> %d, %d)",
+			item->_dimensions._width, item->_dimensions._height,
+			item->_surface->w, item->_surface->h);
+		return;
+	}
+	
+	if (item->_flags & 1) {
+		x = xstart = item->_dimensions._width - 1;
+		xincr = -1;
+	} else {
+		x = xstart = 0;
+		xincr = 1;
+	}
+
+	if (item->_flags & 2) {
+		y = item->_dimensions._height - 1;
+		yincr = -1;
+	} else {
+		y = 0;
+		yincr = 1;
+	}
+	
+	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
+	
+	while (processedSize < dstSize) {
+		int16 op = READ_LE_UINT16(src);
+		src += 2;
+		if (op & 0x8000) {
+			int runCount = (op & 0x7FFF) + 1;
+			processedSize += runCount;
+			uint16 runColor = READ_LE_UINT16(src);
+			src += 2;
+			while (runCount--) {
+				WRITE_LE_UINT16(dst, runColor);
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += 2 * xincr;
+				}
+			}
+		} else {
+			int copyCount = op + 1;
+			processedSize += copyCount;
+			while (copyCount--) {
+				uint16 color = READ_LE_UINT16(src);
+				src += 2;
+				WRITE_LE_UINT16(dst, color);
+				x += xincr;
+				if (x >= item->_dimensions._width || x < 0) {
+					x = xstart;
+					y += yincr;
+					dst = (byte*)dstSurface->getBasePtr(x, y);
+				} else {
+					dst += 2 * xincr;
+				}
+			}
+		}
+	}
+
+}
+
+void Screen::drawSurface16(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
 	if (scale == 100) {
 		if (flags & 1)
 			drawSurface10(dstRect.left, dstRect.top, surface, srcRect, _colorKey2);
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index cfeaba6..5bd5eb7 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -44,7 +44,7 @@ struct SpriteDecompressQueueItem {
 
 class SpriteDecompressQueue {
 public:
-	SpriteDecompressQueue();
+	SpriteDecompressQueue(Screen *screen);
 	~SpriteDecompressQueue();
 	void insert(byte *drawFlags, uint32 flags, uint32 field8, WidthHeight &dimensions,
 		byte *compressedPixels, Graphics::Surface *surface);
@@ -52,6 +52,7 @@ public:
 protected:
 	typedef Common::List<SpriteDecompressQueueItem*> SpriteDecompressQueueList;
 	typedef SpriteDecompressQueueList::iterator SpriteDecompressQueueListIterator;
+	Screen *_screen;
 	SpriteDecompressQueueList _queue;
 	void decompress(SpriteDecompressQueueItem *item);
 };
@@ -96,9 +97,11 @@ protected:
 	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
 };
 
+// TODO Split into two classes (8bit and 16bit)?
+
 class Screen {
 public:
-	Screen(IllusionsEngine *vm);
+	Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp);
 	~Screen();
 	Graphics::Surface *allocSurface(int16 width, int16 height);
 	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
@@ -106,11 +109,13 @@ public:
 	void setDisplayOn(bool isOn);
 	uint16 getColorKey2();
 	void updateSprites();
+	void decompressSprite(SpriteDecompressQueueItem *item);
 	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
-	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
-	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
-	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
-	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+	void setPalette(byte *colors, uint start, uint count);
+	void getPalette(byte *colors);
+	void updatePalette();
+	int16 getScreenWidth() const { return _backSurface->w; }
+	int16 getScreenHeight() const { return _backSurface->h; }
 public:
 	IllusionsEngine *_vm;
 	bool _displayOn;
@@ -119,6 +124,23 @@ public:
 	SpriteDecompressQueue *_decompressQueue;
 	SpriteDrawQueue *_drawQueue;
 	Graphics::Surface *_backSurface;
+	
+	bool _needRefreshPalette;
+	byte _mainPalette[768];
+	
+	void setSystemPalette(byte *palette);
+
+	void decompressSprite8(SpriteDecompressQueueItem *item);
+	void drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
+	void drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
+	void drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+
+	void decompressSprite16(SpriteDecompressQueueItem *item);
+	void drawSurface16(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
+	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
+	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
+	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
+	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index f63a221..b566f52 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions.h"
+#include "illusions/illusions_bbdou.h"
 #include "illusions/scriptopcodes_bbdou.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
diff --git a/engines/illusions/scriptopcodes_bbdou.h b/engines/illusions/scriptopcodes_bbdou.h
index 28c7987..dbbc325 100644
--- a/engines/illusions/scriptopcodes_bbdou.h
+++ b/engines/illusions/scriptopcodes_bbdou.h
@@ -125,4 +125,4 @@ protected:
 
 } // End of namespace Illusions
 
-#endif // ILLUSIONS_SCRIPTOPCODES_H
+#endif // ILLUSIONS_SCRIPTOPCODES_BBDOU_H
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
new file mode 100644
index 0000000..ffe4ac0
--- /dev/null
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -0,0 +1,811 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/scriptopcodes_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+#include "illusions/scriptman.h"
+#include "illusions/scriptresource.h"
+#include "illusions/scriptthread.h"
+#include "illusions/specialcode.h"
+#include "illusions/talkresource.h"
+
+namespace Illusions {
+
+// ScriptOpcodes_Duckman
+
+ScriptOpcodes_Duckman::ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm)
+	: ScriptOpcodes(vm), _vm(vm) {
+	initOpcodes();
+}
+
+ScriptOpcodes_Duckman::~ScriptOpcodes_Duckman() {
+	freeOpcodes();
+}
+
+typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_Duckman> ScriptOpcodeI;
+#define OPCODE(op, func) \
+	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_Duckman::func); \
+	_opcodeNames[op] = #func;
+
+void ScriptOpcodes_Duckman::initOpcodes() {
+	// First clear everything
+	for (uint i = 0; i < 256; ++i)
+		_opcodes[i] = 0;
+	OPCODE(2, opSuspend);
+	OPCODE(3, opYield);
+	OPCODE(4, opTerminate);
+	OPCODE(5, opJump);
+	OPCODE(6, opStartScriptThread);
+	OPCODE(7, opStartTimerThread);
+	OPCODE(9, opNotifyThread);
+	OPCODE(10, opSuspendThread);
+	OPCODE(18, opEnterScene18);
+	OPCODE(20, opChangeScene);
+	OPCODE(24, opEnterScene24);
+	OPCODE(25, opLeaveScene24);
+	OPCODE(38, opStartFade);
+	OPCODE(39, opSetDisplay);
+	OPCODE(49, opPlaceActor);
+	OPCODE(52, opStartSequenceActor);
+	OPCODE(56, opStartTalkThread);
+	OPCODE(57, opAppearActor);
+	OPCODE(58, opDisappearActor);
+	OPCODE(59, opActivateObject);
+	OPCODE(60, opDeactivateObject);
+	OPCODE(61, opSetDefaultSequence);
+	OPCODE(66, opPlayVideo);
+	OPCODE(69, opRunSpecialCode);
+	OPCODE(72, opStartSound);
+	OPCODE(75, opStopSound);
+	OPCODE(76, opStartMidiMusic);
+	OPCODE(77, opStopMidiMusic);
+	OPCODE(80, opAddMenuChoice);
+	OPCODE(81, opDisplayMenu);
+	OPCODE(82, opSwitchMenuChoice);
+	OPCODE(84, opResetGame);
+	OPCODE(87, opDeactivateButton);
+	OPCODE(88, opActivateButton);
+	OPCODE(96, opIncBlockCounter);
+	OPCODE(104, opJumpIf);
+	OPCODE(106, opNot);
+	OPCODE(107, opAnd);
+	OPCODE(108, opOr);
+	OPCODE(109, opGetProperty);
+	OPCODE(110, opCompareBlockCounter);
+	OPCODE(126, opDebug126);
+#if 0		
+	// Register opcodes
+	OPCODE(8, opStartTempScriptThread);
+	OPCODE(14, opSetThreadSceneId);
+	OPCODE(15, opEndTalkThreads);
+	OPCODE(17, opUnloadResource);
+	OPCODE(20, opEnterScene);
+	OPCODE(26, opStartModalScene);
+	OPCODE(27, opExitModalScene);
+	OPCODE(30, opEnterCloseUpScene);
+	OPCODE(31, opExitCloseUpScene);
+	OPCODE(32, opPanCenterObject);
+	OPCODE(34, opPanToObject);
+	OPCODE(35, opPanToNamedPoint);
+	OPCODE(36, opPanToPoint);
+	OPCODE(37, opPanStop);
+	OPCODE(43, opClearBlockCounter);
+	OPCODE(45, opSetProperty);
+	OPCODE(47, opFaceActor);
+	OPCODE(48, opFaceActorToObject);	
+	OPCODE(51, opStartMoveActor);
+	OPCODE(53, opSetActorToNamedPoint);
+	OPCODE(63, opSetSelectSfx);
+	OPCODE(64, opSetMoveSfx);
+	OPCODE(65, opSetDenySfx);
+	OPCODE(66, opSetAdjustUpSfx);
+	OPCODE(67, opSetAdjustDnSfx);
+	OPCODE(78, opStackPushRandom);
+	OPCODE(79, opIfLte);
+	OPCODE(104, opIsPrevSceneId);
+	OPCODE(105, opIsCurrentSceneId);
+	OPCODE(106, opIsActiveSceneId);
+	OPCODE(146, opStackPop);
+	OPCODE(147, opStackDup);
+	OPCODE(148, opLoadSpecialCodeModule);
+	OPCODE(160, opStopActor);
+	OPCODE(161, opSetActorUsePan);
+	OPCODE(168, opStartAbortableThread);
+	OPCODE(169, opKillThread);
+	OPCODE(175, opSetSceneIdThreadId);
+	OPCODE(176, opStackPush0);
+	OPCODE(177, opSetFontId);
+	OPCODE(178, opAddMenuKey);
+	OPCODE(179, opChangeSceneAll);
+#endif	
+}
+
+#undef OPCODE
+
+void ScriptOpcodes_Duckman::freeOpcodes() {
+	for (uint i = 0; i < 256; ++i)
+		delete _opcodes[i];
+}
+
+// Opcodes
+
+void ScriptOpcodes_Duckman::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_Duckman::opYield(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_Duckman::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes_Duckman::opJump(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_Duckman::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->startScriptThread(threadId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(isAbortable);
+	ARG_INT16(duration);
+	ARG_INT16(maxDuration);
+	if (maxDuration)
+		duration += _vm->getRandom(maxDuration);
+		
+//duration = 1;//DEBUG Speeds up things
+duration = 5;		
+		
+	if (isAbortable)
+		_vm->startAbortableTimerThread(duration, opCall._threadId);
+	else
+		_vm->startTimerThread(duration, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opNotifyThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->notifyId(threadId);
+	_vm->_threads->notifyTimerThreads(threadId);
+}
+
+void ScriptOpcodes_Duckman::opSuspendThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->suspendId(threadId);
+	_vm->_threads->suspendTimerThreads(threadId);
+}
+
+void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->enterScene(sceneId, 0);
+}
+
+static uint dsceneId = 0x00010008, dthreadId = 0x00020029;
+
+void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->_input->discardButtons(0xFFFF);
+	
+	//DEBUG
+	if (dsceneId) {
+		sceneId = dsceneId;
+		threadId = dthreadId;
+		dsceneId = 0;
+	}
+	
+	if (_vm->_scriptResource->_properties.get(31)) {
+		_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
+	} else {
+		_vm->changeScene(sceneId, threadId, opCall._callerThreadId);
+	}
+}
+
+void ScriptOpcodes_Duckman::opEnterScene24(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
+	_vm->enterScene(sceneId, 0);
+}
+
+void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
+	_vm->exitScene();
+	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
+}
+
+void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(arg1);
+	ARG_INT16(arg2);
+	ARG_INT16(arg3);
+	ARG_INT16(arg4);
+	ARG_INT16(arg5);
+	// TODO
+
+	//DEBUG Resume calling thread, later done when the fading is finished
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flag);
+	_vm->_screen->setDisplayOn(flag != 0);
+}
+
+void ScriptOpcodes_Duckman::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	uint32 actorTypeId = _vm->getObjectActorTypeId(objectId);
+	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(talkId);
+	ARG_UINT32(sequenceId1);
+	ARG_UINT32(sequenceId2);
+	_vm->startTalkThread(objectId, talkId, sequenceId1, sequenceId2, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (!control) {
+		Common::Point pos = _vm->getNamedPointPosition(0x70001);		
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
+		control = _vm->_dict->getObjectControl(objectId);
+	}
+	control->appearActor();
+}
+
+void ScriptOpcodes_Duckman::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->disappearActor();
+}
+
+void ScriptOpcodes_Duckman::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (control)
+		control->activateObject();
+}
+
+void ScriptOpcodes_Duckman::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->deactivateObject();
+}
+
+void ScriptOpcodes_Duckman::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(defaultSequenceId);
+	ARG_UINT32(sequenceId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
+}
+
+void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	// NOTE This has no attached objectId or priority
+	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
+	
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._threadId);
+	
+}
+
+void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeId);
+//TODO	_vm->_specialCode->run(specialCodeId, opCall);
+	//DEBUG Resume calling thread, later done by the special code
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(volume);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->startSound(soundEffectId, volume, pan);
+}
+
+void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->stopSound(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(musicId);
+	// TODO _vm->playMidiMusic(musicId);
+}
+
+void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	// TODO _vm->stopMidiMusic();
+}
+
+void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(jumpOffs);
+	ARG_INT16(endMarker);
+	_vm->_stack->push(endMarker);
+	_vm->_stack->push(jumpOffs);
+}
+
+void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(unk1);
+	ARG_UINT32(menuId);
+	ARG_UINT32(unk2);
+	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
+	// Remove menu choices from the stack
+	do {
+		_vm->_stack->pop();
+	} while (_vm->_stack->pop() == 0);
+
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+
+}
+
+void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game"
+
+	opCall._deltaOfs += _vm->_menuChoiceOfs;
+}
+
+void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->reset();
+	_vm->_input->activateButton(0xFFFF);
+	// TODO _vm->stopMusic();
+	// TODO _vm->_gameStates->clear();
+}
+
+void ScriptOpcodes_Duckman::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->deactivateButton(button);
+}
+
+void ScriptOpcodes_Duckman::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->activateButton(button);
+}
+
+void ScriptOpcodes_Duckman::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
+	if (value <= 63)
+		_vm->_scriptResource->_blockCounters.set(index, value);
+}
+
+void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	int16 value = _vm->_stack->pop();
+	if (value == 0)
+		opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_Duckman::opNot(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->pop();
+	_vm->_stack->push(value != 0 ? 0 : 1);
+}
+
+void ScriptOpcodes_Duckman::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 & value2);
+}
+
+void ScriptOpcodes_Duckman::opOr(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 | value2);
+}
+
+void ScriptOpcodes_Duckman::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(propertyId)
+	bool value = _vm->_scriptResource->_properties.get(propertyId);
+	_vm->_stack->push(value ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	ARG_INT16(compareOp);	
+	ARG_INT16(rvalue);
+	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
+	bool compareResult = false;
+	switch (compareOp) {
+	case 1:
+		compareResult = lvalue == rvalue;
+		break;
+	case 2:
+		compareResult = lvalue != rvalue;
+		break;
+	case 3:
+		compareResult = lvalue < rvalue;
+		break;
+	case 4:
+		compareResult = lvalue > rvalue;
+		break;
+	case 5:
+		compareResult = lvalue >= rvalue;
+		break;
+	case 6:
+		compareResult = lvalue <= rvalue;
+		break;
+	}
+	_vm->_stack->push(compareResult ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG] %s", (char*)opCall._code);
+}
+
+#if 0
+
+void ScriptOpcodes_Duckman::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(codeOffs);
+	_vm->startTempScriptThread(opCall._code + codeOffs,
+		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_Duckman::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
+}
+
+void ScriptOpcodes_Duckman::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_threads->endTalkThreads();
+}
+
+void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	uint32 sceneId = _vm->getCurrentScene();
+	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_resSys->unloadResourceById(resourceId);
+}
+
+void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
+	if (scenesCount > 0) {
+		uint32 currSceneId;
+		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
+		// TODO krnfileDump(currSceneId);
+	}
+	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
+		opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	_vm->startScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+}
+
+void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_Duckman::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	_vm->_camera->panCenterObject(objectId, speed);
+}
+
+void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_INT16(x);	
+	ARG_INT16(y);	
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_camera->stopPan();
+}
+
+void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.set(index, 0);
+}
+
+void ScriptOpcodes_Duckman::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value);	
+	ARG_UINT32(propertyId);	
+	_vm->_scriptResource->_properties.set(propertyId, value != 0);
+}
+
+void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->faceActor(facing);
+}
+
+void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Control *control2 = _vm->_dict->getObjectControl(objectId2);
+	Common::Point pos1 = control1->getActorPosition();
+	Common::Point pos2 = control2->getActorPosition();
+	uint facing;
+	if (_vm->calcPointDirection(pos1, pos2, facing))
+		control1->faceActor(facing);
+}
+
+void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->stopActor();
+	control->setActorPosition(pos);
+}
+
+void ScriptOpcodes_Duckman::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setSelectSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setMoveSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setDenySfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustUpSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustDnSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(maxValue);
+	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
+}
+
+void ScriptOpcodes_Duckman::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(rvalue);
+	ARG_INT16(elseJumpOffs);
+	int16 lvalue = _vm->_stack->pop();
+	if (!(lvalue <= rvalue))
+		opCall._deltaOfs += elseJumpOffs;
+}
+
+void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->pop(); 
+}
+
+void ScriptOpcodes_Duckman::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->peek();
+	_vm->_stack->push(value); 
+}
+
+void ScriptOpcodes_Duckman::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeModuleId);
+	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
+}
+
+void ScriptOpcodes_Duckman::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->stopActor();
+}
+
+void ScriptOpcodes_Duckman::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(usePan)
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorUsePan(usePan);
+}
+
+void ScriptOpcodes_Duckman::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(codeOffs);
+	ARG_INT16(skipOffs);
+	_vm->startAbortableThread(opCall._code + codeOffs,
+		opCall._code + skipOffs, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->killThread(threadId);
+}
+
+void ScriptOpcodes_Duckman::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->setSceneIdThreadId(sceneId, threadId);
+}
+
+void ScriptOpcodes_Duckman::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->push(0);
+}
+
+void ScriptOpcodes_Duckman::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(fontId);
+	_vm->setCurrFontId(fontId);
+}
+
+void ScriptOpcodes_Duckman::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(key);
+	ARG_UINT32(threadId);
+	// TODO _vm->addMenuKey(key, threadId);
+}
+
+void ScriptOpcodes_Duckman::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_prevSceneId = _vm->getCurrentScene();
+	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+#endif
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
new file mode 100644
index 0000000..a533d7d
--- /dev/null
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -0,0 +1,137 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTOPCODES_DUCKMAN_H
+#define ILLUSIONS_SCRIPTOPCODES_DUCKMAN_H
+
+#include "illusions/scriptopcodes.h"
+#include "common/func.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+class ScriptThread;
+
+class ScriptOpcodes_Duckman : public ScriptOpcodes {
+public:
+	ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm);
+	~ScriptOpcodes_Duckman();
+	void initOpcodes();
+	void freeOpcodes();
+protected:
+	IllusionsEngine_Duckman *_vm;
+
+	// Opcodes
+	
+	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
+	void opYield(ScriptThread *scriptThread, OpCall &opCall);
+	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
+	void opJump(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opNotifyThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSuspendThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
+	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
+	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opNot(ScriptThread *scriptThread, OpCall &opCall);
+	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
+	void opOr(ScriptThread *scriptThread, OpCall &opCall);
+	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	
+#if 0	
+	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
+	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
+	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
+#endif	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTOPCODES_DUCKMAN_H
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 715039e..fb8260f 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -32,13 +32,14 @@ void ScriptResourceLoader::load(Resource *resource) {
 	debug(2, "ScriptResourceLoader::load() Loading script %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	ScriptResource *scriptResource = new ScriptResource();
-	scriptResource->load(resource->_data, resource->_dataSize);
+	scriptResource->load(resource);
 	
 	_vm->_scriptResource = scriptResource;
 	
 }
 
 void ScriptResourceLoader::unload(Resource *resource) {
+	delete _vm->_scriptResource;
 }
 
 void ScriptResourceLoader::buildFilename(Resource *resource) {
@@ -153,14 +154,21 @@ bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &co
 	return false;
 }
 
+void TriggerObject::fixupProgInfosDuckman() {
+	for (uint i = 0; i < _causesCount; ++i)
+		_causes[i]._verbId &= 0xFFFF;
+}
+
 // ProgInfo
 
 ProgInfo::ProgInfo()
-	: _triggerObjectsCount(0), _triggerObjects(0) {
+	: _triggerObjectsCount(0), _triggerObjects(0),
+	_resourcesCount(0), _resources(0) {
 }
 
 ProgInfo::~ProgInfo() {
 	delete[] _triggerObjects;
+	delete[] _resources;
 }
 
 char *debugW2I(byte *wstr) {
@@ -180,10 +188,15 @@ void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_name = dataStart + stream.pos();
 	stream.skip(128);
 	_triggerObjectsCount = stream.readUint16LE();
-	stream.skip(2); // Skip padding
+	_resourcesCount = stream.readUint16LE();
 	debug(2, "\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
 		_id, _unk, debugW2I(_name));
 	uint32 triggerObjectsListOffs = stream.readUint32LE();
+	if (_resourcesCount > 0) {
+		_resources = new uint32[_resourcesCount];
+		for (uint i = 0; i < _resourcesCount; ++i)
+			_resources[i] = stream.readUint32LE();
+	}
 	if (_triggerObjectsCount > 0) {
 		_triggerObjects = new TriggerObject[_triggerObjectsCount];
 		for (uint i = 0; i < _triggerObjectsCount; ++i) {
@@ -202,6 +215,11 @@ bool ProgInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId
 	return false;
 }
 
+void ProgInfo::getResources(uint &resourcesCount, uint32 *&resources) {
+	resourcesCount = _resourcesCount;
+	resources = _resources;
+}
+
 TriggerObject *ProgInfo::findTriggerObject(uint32 objectId) {
 	for (uint i = 0; i < _triggerObjectsCount; ++i)
 		if (_triggerObjects[i]._objectId == objectId)
@@ -209,36 +227,67 @@ TriggerObject *ProgInfo::findTriggerObject(uint32 objectId) {
 	return 0;
 }
 
+void ProgInfo::fixupProgInfosDuckman() {
+	for (uint i = 0; i < _triggerObjectsCount; ++i)
+		_triggerObjects[i].fixupProgInfosDuckman();
+}
+
 // ScriptResource
 
 ScriptResource::ScriptResource()
-	: _codeOffsets(0) {
+	: _codeOffsets(0), _objectMap(0) {
 }
 
 ScriptResource::~ScriptResource() {
 	delete[] _codeOffsets;
+	delete[] _objectMap;
 }
 
-void ScriptResource::load(byte *data, uint32 dataSize) {
-	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+void ScriptResource::load(Resource *resource) {
+	_data = resource->_data;
+	_dataSize = resource->_dataSize;
+
+	Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
+	
+	uint32 objectMapOffs, progInfosOffs;
+	_objectMapCount = 0;
 	
-	_data = data;
-	_dataSize = dataSize;
+	if (resource->_gameId == kGameIdBBDOU) {
+		progInfosOffs = 0x18;
+	} else if (resource->_gameId == kGameIdDuckman) {
+		for (uint i = 0; i < 27; ++i)
+			_soundIds[i] = stream.readUint32LE();
+		progInfosOffs = 0x8C;
+	}
 	
 	stream.skip(4); // Skip unused
+
+	// Read item counts
 	uint propertiesCount = stream.readUint16LE();
 	uint blockCountersCount = stream.readUint16LE();
+	if (resource->_gameId == kGameIdDuckman)
+		_objectMapCount = stream.readUint16LE();
 	_codeCount = stream.readUint16LE();
 	_progInfosCount = stream.readUint16LE();
+	if (resource->_gameId == kGameIdDuckman)
+		stream.readUint16LE();//Unused?
+
+	// Read item offsets
 	uint32 propertiesOffs = stream.readUint32LE();
 	uint32 blockCountersOffs = stream.readUint32LE();
+	if (resource->_gameId == kGameIdDuckman)
+		objectMapOffs = stream.readUint32LE();
 	uint32 codeTblOffs = stream.readUint32LE();
 	
+	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _progInfosCount: %d; _objectMapCount: %d",
+		propertiesCount, blockCountersCount, _codeCount, _progInfosCount, _objectMapCount);
+	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X; objectMapOffs: %08X",
+		propertiesOffs, blockCountersOffs, codeTblOffs, objectMapOffs);
 	// Init properties
-	_properties.init(propertiesCount, data + propertiesOffs);
+	_properties.init(propertiesCount, _data + propertiesOffs);
 	
 	// Init blockcounters
-	_blockCounters.init(blockCountersCount, data + blockCountersOffs);
+	_blockCounters.init(blockCountersCount, _data + blockCountersOffs);
 	
 	_codeOffsets = new uint32[_codeCount];
 	stream.seek(codeTblOffs);
@@ -247,16 +296,23 @@ void ScriptResource::load(byte *data, uint32 dataSize) {
 
 	_progInfos = new ProgInfo[_progInfosCount];
 	for (uint i = 0; i < _progInfosCount; ++i) {
-		stream.seek(0x18 + i * 4);
+		stream.seek(progInfosOffs + i * 4);
 		uint32 progInfoOffs = stream.readUint32LE();
 		stream.seek(progInfoOffs);
-		_progInfos[i].load(data, stream);
+		_progInfos[i].load(_data, stream);
 	}
-
-	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _progInfosCount: %d",
-		propertiesCount, blockCountersCount, _codeCount, _progInfosCount);
-	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X",
-		propertiesOffs, blockCountersOffs, codeTblOffs);
+	
+	if (_objectMapCount > 0) {
+		_objectMap = new uint32[_objectMapCount];
+		stream.seek(objectMapOffs);
+		for (uint i = 0; i < _objectMapCount; ++i) {
+			_objectMap[i] = stream.readUint32LE();
+			stream.skip(4);
+		}
+	}
+	
+	if (resource->_gameId == kGameIdDuckman)
+		fixupProgInfosDuckman();
 
 }
 
@@ -274,4 +330,13 @@ ProgInfo *ScriptResource::getProgInfo(uint32 index) {
 	return 0;
 }
 
+uint32 ScriptResource::getObjectActorTypeId(uint32 objectId) {
+	return _objectMap[(objectId & 0xFFFF) - 1];
+}
+
+void ScriptResource::fixupProgInfosDuckman() {
+	for (uint i = 0; i < _progInfosCount; ++i)
+		_progInfos[i].fixupProgInfosDuckman();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 5e2da45..f33ac10 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -79,6 +79,7 @@ public:
 	~TriggerObject();
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs);
+	void fixupProgInfosDuckman();
 public:
 	uint32 _objectId;
 	uint _causesCount;
@@ -91,12 +92,16 @@ public:
 	~ProgInfo();
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
+	void getResources(uint &resourcesCount, uint32 *&resources);
+	void fixupProgInfosDuckman();
 protected:
 	uint16 _id;
 	uint16 _unk;
 	byte *_name;
 	uint _triggerObjectsCount;
 	TriggerObject *_triggerObjects;
+	uint _resourcesCount;
+	uint32 *_resources;
 	TriggerObject *findTriggerObject(uint32 objectId);
 };
 
@@ -104,10 +109,11 @@ class ScriptResource {
 public:
 	ScriptResource();
 	~ScriptResource();
-	void load(byte *data, uint32 dataSize);
+	void load(Resource *resource);
 	byte *getThreadCode(uint32 threadId);
 	byte *getCode(uint32 codeOffs);
 	ProgInfo *getProgInfo(uint32 index);
+	uint32 getObjectActorTypeId(uint32 objectId);
 public:
 	byte *_data;
 	uint32 _dataSize;
@@ -117,6 +123,11 @@ public:
 	uint32 *_codeOffsets;
 	uint _progInfosCount;
 	ProgInfo *_progInfos;
+	// Duckman specific
+	uint32 _soundIds[27];
+	uint _objectMapCount;
+	uint32 *_objectMap;
+	void fixupProgInfosDuckman();
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 78fb8b8..73ef8ec 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -42,11 +42,7 @@ int ScriptThread::onUpdate() {
 	opCall._result = kTSRun;
 	opCall._callerThreadId = _threadId;
 	while (!_terminated && opCall._result == kTSRun) {
-		opCall._op = _scriptCodeIp[0];
-		opCall._opSize = _scriptCodeIp[1] >> 1;
-		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
-		opCall._code = _scriptCodeIp + 2;
-		opCall._deltaOfs = opCall._opSize;
+		loadOpcode(opCall);
 		execOpcode(opCall);
 		_scriptCodeIp += opCall._deltaOfs;
 	}
@@ -55,6 +51,25 @@ int ScriptThread::onUpdate() {
 	return opCall._result;
 }
 
+void ScriptThread::loadOpcode(OpCall &opCall) {
+#if 0
+	for (uint i = 0; i < 16; ++i)
+		debugN("%02X ", _scriptCodeIp[i]);
+	debug(".");
+#endif
+	if (_vm->getGameId() == kGameIdDuckman) {
+		opCall._op = _scriptCodeIp[0] & 0x7F;
+		opCall._opSize = _scriptCodeIp[1];
+		opCall._threadId = _scriptCodeIp[0] & 0x80 ? _threadId : 0;
+	} else {
+		opCall._op = _scriptCodeIp[0];
+		opCall._opSize = _scriptCodeIp[1] >> 1;
+		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
+	}
+	opCall._code = _scriptCodeIp + 2;
+	opCall._deltaOfs = opCall._opSize;
+}
+
 void ScriptThread::execOpcode(OpCall &opCall) {
 	_vm->_scriptOpcodes->execOpcode(this, opCall);
 }
diff --git a/engines/illusions/scriptthread.h b/engines/illusions/scriptthread.h
index cb82b6c..0306c4f 100644
--- a/engines/illusions/scriptthread.h
+++ b/engines/illusions/scriptthread.h
@@ -41,6 +41,7 @@ public:
 	uint32 _value8;
 	uint32 _valueC;
 	uint32 _value10;
+	void loadOpcode(OpCall &opCall);
 	void execOpcode(OpCall &opCall);
 };
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index f020423..e00bb23 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -74,11 +74,13 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(17, opDisappearActor);
 	OPCODE(18, opAppearForeignActor);
 	OPCODE(19, opDisappearForeignActor);
+	OPCODE(21, opMoveDelta);
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
 	OPCODE(33, opSetPathWalkPoints);
 	OPCODE(35, opSetScale);
 	OPCODE(36, opSetScaleLayer);
+	OPCODE(37, opDeactivatePathWalkRects);
 	OPCODE(38, opSetPathWalkRects);
 	OPCODE(39, opSetPriority);
 	OPCODE(40, opSetPriorityLayer);
@@ -243,6 +245,14 @@ void SequenceOpcodes::opDisappearForeignActor(Control *control, OpCall &opCall)
 	foreignControl->disappearActor();
 }
 
+void SequenceOpcodes::opMoveDelta(Control *control, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(deltaX);
+	ARG_INT16(deltaY);
+	control->_actor->_position.x += deltaX;
+	control->_actor->_position.y += deltaY;
+}
+
 void SequenceOpcodes::opNotifyThreadId1(Control *control, OpCall &opCall) {
 	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
 }
@@ -274,10 +284,14 @@ void SequenceOpcodes::opSetScaleLayer(Control *control, OpCall &opCall) {
 	control->setActorScale(scale);
 }
 
+void SequenceOpcodes::opDeactivatePathWalkRects(Control *control, OpCall &opCall) {
+	control->_actor->_flags &= ~0x0010;
+}
+
 void SequenceOpcodes::opSetPathWalkRects(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkRectsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
-	control->_actor->_flags |= 0x10;
+	control->_actor->_flags |= 0x0010;
 	// TODO control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
 }
 
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 02561e3..3aab3a8 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -63,11 +63,13 @@ protected:
 	void opDisappearActor(Control *control, OpCall &opCall);
 	void opAppearForeignActor(Control *control, OpCall &opCall);
 	void opDisappearForeignActor(Control *control, OpCall &opCall);
+	void opMoveDelta(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
 	void opSetPathWalkPoints(Control *control, OpCall &opCall);
 	void opSetScale(Control *control, OpCall &opCall);
 	void opSetScaleLayer(Control *control, OpCall &opCall);
+	void opDeactivatePathWalkRects(Control *control, OpCall &opCall);
 	void opSetPathWalkRects(Control *control, OpCall &opCall);
 	void opSetPriority(Control *control, OpCall &opCall);
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index d74ddde..5ad6f72 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -110,11 +110,12 @@ int TalkThread::onUpdate() {
 		_entryText = talkEntry->_text;
 		_entryTblPtr = talkEntry->_tblPtr;
 		if (_sequenceId1) {
-			_pauseCtr = 0;
 			// TODO _field30 = v6;
+			_pauseCtr = 0;
 		} else {
-			_flags = 3;
 			// TODO _field30 = 0;
+			_flags |= 2;
+			_flags |= 1;
 		}
 		if (_vm->isSoundActive()) {
 			if (!_vm->cueVoice(talkEntry->_voiceName) && !_durationMult)
@@ -150,7 +151,7 @@ int TalkThread::onUpdate() {
 			}
 			_vm->startVoice(255, panX);
 		}
-		_vm->_input->discardButtons(16);
+		_vm->_input->discardButtons(0x10);
 		_status = 6;
 		return kTSYield;
 
@@ -161,7 +162,7 @@ int TalkThread::onUpdate() {
 			// TODO _vm->removeText();
 			if (_entryText && *_entryText) {
 				refreshText();
-				_vm->_input->discardButtons(16);
+				_vm->_input->discardButtons(0x10);
 			} else {
 				_flags |= 8;
 			}
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
new file mode 100644
index 0000000..46c11c8
--- /dev/null
+++ b/engines/illusions/talkthread_duckman.cpp
@@ -0,0 +1,311 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/talkthread_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/scriptman.h"
+#include "illusions/talkresource.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// TalkThread_Duckman
+
+TalkThread_Duckman::TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _objectId(objectId), _talkId(talkId) {
+	_type = kTTTalkThread;
+
+	if ((sequenceId1 & 0xFFFF0000) == 0x60000) {
+		_sequenceId1 = sequenceId1;
+		_sequenceId2 = sequenceId2;
+		_namedPointId1 = 0;
+		_namedPointId2 = 0;
+	} else {
+		_sequenceId1 = 0;
+		_sequenceId2 = 0;
+		_namedPointId1 = sequenceId1;
+		_namedPointId2 = sequenceId2;
+	}
+
+	if (_vm->checkActiveTalkThreads())
+		_status = 1;
+	else
+		_status = 2;
+		
+	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
+	_textDuration = _durationMult;
+	_defDurationMult = _vm->clipTextDuration(240);
+	
+	_tag = _vm->getCurrentScene();
+
+}
+
+int TalkThread_Duckman::onUpdate() {
+
+	TalkEntry *talkEntry;
+
+	switch (_status) {
+
+	case 1:
+		if (_vm->checkActiveTalkThreads())
+			return kTSYield;
+		_status = 3;
+		// Fallthrough to status 2
+
+	case 2:
+		talkEntry = getTalkResourceEntry(_talkId);
+		_flags = 0;
+		_entryText = talkEntry->_text;
+		_currEntryText = 0;
+		_entryTblPtr = talkEntry->_tblPtr;
+		_flags = 0;
+		if (_sequenceId1) {
+			_pauseCtr = 0;
+			_pauseCtrPtr = &_pauseCtr;
+		} else {
+			_pauseCtrPtr = 0;
+			_flags |= 2;
+			_flags |= 1;
+		}
+		if (_vm->isSoundActive()) {
+			if (!_vm->cueVoice(talkEntry->_voiceName) && !_durationMult)
+				_durationMult = _defDurationMult;
+		} else {
+			_flags |= 4;
+			if (!_durationMult)
+				_durationMult = _defDurationMult;
+		}
+		if (_objectId == 0 || _durationMult == 0)
+			_flags |= 8;
+		_status = 3;
+		// Fallthrough to status 3 
+
+	case 3:
+		if (!(_flags & 4) && !_vm->isVoiceCued())
+			return kTSYield;
+		_status = 4;
+		// Fallthrough to status 4
+		
+	case 4: 
+		if (!(_flags & 8) ) {
+			uint32 actorTypeId = _vm->getObjectActorTypeId(_objectId);
+			// TODO getActorTypeColor(actorTypeId, &_colorR, &_colorG, &_colorB);
+			refreshText();
+		}
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			control->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
+		}
+		if (!(_flags & 4)) {
+			int16 panX = 0;
+			if (_flags & 1) {
+				if (_namedPointId2) {
+					panX = _vm->getNamedPointPosition(_namedPointId2).x;
+					panX = _vm->convertPanXCoord(panX);
+				}
+			} else {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				panX = control->getActorPosition().x;
+				panX = _vm->convertPanXCoord(panX);
+			}
+			_vm->startVoice(255, panX);
+		}
+		_vm->_input->discardButtons(0x20);
+		_status = 5;
+		return kTSYield;
+
+	case 5:
+		if (!(_flags & 4) && !_vm->isVoicePlaying())
+			_flags |= 4;
+		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
+			// TODO _vm->removeText();
+			if (_entryText && *_entryText) {
+				refreshText();
+				_vm->_input->discardButtons(0x20);
+			} else {
+				_flags |= 8;
+			}
+		}
+		if (!(_flags & 2)) {
+			if (*_pauseCtrPtr < 0) {
+				++(*_pauseCtrPtr);
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+				_flags |= 2;
+			}
+		}
+		if (_objectId && _vm->_input->pollButton(0x20)) {
+			if (!(_flags & 8)) {
+				// TODO largeObj_removeText();
+				if (_entryText && *_entryText)
+					refreshText();
+				else
+					_flags |= 8;
+			}
+			if (_flags & 8) {
+				if (!(_flags & 4)) {
+					_vm->stopVoice();
+					_flags |= 4;
+				}
+				if (!(_flags & 2)) {
+					Control *control = _vm->_dict->getObjectControl(_objectId);
+					control->clearNotifyThreadId1();
+					control->startSequenceActor(_sequenceId2, 2, 0);
+					_flags |= 2;
+				}
+			}
+		}
+		/*
+		debug("8: %d", (_flags & 8) != 0);
+		debug("4: %d", (_flags & 4) != 0);
+		debug("2: %d", (_flags & 2) != 0);
+		*/
+		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
+debug("TALK DONE");		
+			_vm->_input->discardButtons(0x20);
+			return kTSTerminate;
+		}
+		return kTSYield;
+
+	case 6:
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			if (*_pauseCtrPtr >= 0) {
+				control->clearNotifyThreadId1();
+			} else {
+				++(*_pauseCtrPtr);
+			}
+			control->startSequenceActor(_sequenceId2, 2, 0);
+			_flags |= 2;
+		}
+		return kTSTerminate;
+		
+	}
+
+	return kTSTerminate;
+
+}
+
+void TalkThread_Duckman::onSuspend() {
+}
+
+void TalkThread_Duckman::onNotify() {
+}
+
+void TalkThread_Duckman::onPause() {
+}
+
+void TalkThread_Duckman::onResume() {
+}
+
+void TalkThread_Duckman::onTerminated() {
+	if (_status == 5) {
+		if (!(_flags & 4))
+			_vm->stopVoice();
+		if (!(_flags & 8)) {
+			// TODO largeObj_removeText();
+		}
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			if (control) {
+				control->clearNotifyThreadId1();
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+		}
+	}
+}
+
+void TalkThread_Duckman::onKill() {
+	_callingThreadId = 0;
+	sendMessage(kMsgClearSequenceId1, 0);
+	sendMessage(kMsgClearSequenceId2, 0);
+}
+
+uint32 TalkThread_Duckman::sendMessage(int msgNum, uint32 msgValue) {
+	// TODO
+	switch (msgNum) {
+	case kMsgQueryTalkThreadActive:
+		if (_status != 1 && _status != 2)
+			return 1;
+		break;
+	case kMsgClearSequenceId1:
+		_sequenceId1 = 0;
+		_flags |= 3;
+		// TODO _pauseCtrPtr = 0;
+		break;
+	case kMsgClearSequenceId2:
+		_sequenceId2 = 0;
+		break;
+	}
+	return 0;
+}
+
+void TalkThread_Duckman::refreshText() {
+	_currEntryText = _entryText;
+	int charCount = insertText();
+	uint32 duration = _durationMult;
+	if (charCount < 80) {
+		duration = _durationMult * charCount / 80;
+		if (duration < 25 * _durationMult / 100)
+			duration = 25 * _durationMult / 100;
+		if (duration < 60)
+			duration = 60;
+	}
+	_textDuration = duration;
+	_textStartTime = getCurrentTime();
+	_textEndTime = _textStartTime + _textDuration;
+}
+
+static char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	while (*wstr != 0) {
+		*p++ = *wstr;
+		wstr += 2;
+	}
+	*p = 0;
+	return buf;
+}
+
+int TalkThread_Duckman::insertText() {
+	int charCount = 100;
+	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
+	_entryText = 0;
+	// TODO _vm->getDimensions1(&dimensions);
+	// TODO _vm->insertText(_currEntryText, 0x00120001, dimensions, 0, 2, 0, 0, _colorR, _colorG, _colorB, 0, &outTextPtr);
+	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
+	// TODO _entryText = outTextPtr;
+	// TODO _vm->getPoint1(&pt);
+	// TODO _vm->updateTextInfoPosition(pt);
+	return charCount >> 1;
+}
+
+TalkEntry *TalkThread_Duckman::getTalkResourceEntry(uint32 talkId) {
+	TalkEntry *talkEntry = _vm->_dict->findTalkEntry(talkId);
+	return talkEntry;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/talkthread_duckman.h b/engines/illusions/talkthread_duckman.h
new file mode 100644
index 0000000..656dab7
--- /dev/null
+++ b/engines/illusions/talkthread_duckman.h
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TALKTHREAD_DUCKMAN_H
+#define ILLUSIONS_TALKTHREAD_DUCKMAN_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+struct TalkEntry;
+
+enum {
+	kMsgQueryTalkThreadActive    = 0,
+	kMsgClearSequenceId1         = 1,
+	kMsgClearSequenceId2         = 2
+};
+
+class TalkThread_Duckman : public Thread {
+public:
+	TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+	virtual void onKill();
+	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
+public:
+	IllusionsEngine_Duckman *_vm;
+	//field0 dw
+	int _status;
+	uint _flags;
+	uint32 _textStartTime;
+	uint32 _textEndTime;
+	uint32 _textDuration;
+	uint32 _defDurationMult;
+	uint32 _textDurationElapsed;
+	uint32 _durationMult;
+	//field12 dw
+	uint32 _objectId;
+	uint32 _talkId;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	uint32 _namedPointId1;
+	uint32 _namedPointId2;
+	byte *_entryTblPtr;
+	byte *_entryText;
+	byte *_currEntryText;
+	//field30 dd
+	uint32 _voiceStartTime;
+	uint32 _voiceEndTime;
+	uint32 _voiceDuration;
+	uint32 _voiceDurationElapsed;
+	int *_pauseCtrPtr;
+	byte _colorR, _colorG, _colorB;
+	void refreshText();
+	int insertText();
+	TalkEntry *getTalkResourceEntry(uint32 talkId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKTHREAD_H
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index d597d48..7ecc3d8 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -197,6 +197,14 @@ void ThreadList::terminateThreads(uint32 threadId) {
 	}
 }
 
+void ThreadList::terminateActiveThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_pauseCtr <= 0 && thread->_threadId != threadId)
+			thread->terminate();
+	}
+}
+
 void ThreadList::terminateThreadsByTag(uint32 tag, uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
@@ -213,6 +221,14 @@ void ThreadList::suspendThreadsByTag(uint32 tag, uint32 threadId) {
 	}
 }
 
+void ThreadList::notifyThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_threadId != threadId)
+			thread->notify();
+	}
+}
+
 void ThreadList::notifyThreadsByTag(uint32 tag, uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
@@ -229,6 +245,14 @@ void ThreadList::pauseThreads(uint32 threadId) {
 	}
 }
 
+void ThreadList::suspendThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_threadId != threadId)
+			thread->suspend();
+	}
+}
+
 void ThreadList::resumeThreads(uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index ea4868d..e9a9ec3 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -86,10 +86,13 @@ public:
 	void notifyTimerThreads(uint32 callingThreadId);
 	void suspendTimerThreads(uint32 callingThreadId);
 	void terminateThreads(uint32 threadId);
+	void terminateActiveThreads(uint32 threadId);
 	void terminateThreadsByTag(uint32 tag, uint32 threadId);
 	void suspendThreadsByTag(uint32 tag, uint32 threadId);
+	void notifyThreads(uint32 threadId);
 	void notifyThreadsByTag(uint32 tag, uint32 threadId);
 	void pauseThreads(uint32 threadId);
+	void suspendThreads(uint32 threadId);
 	void resumeThreads(uint32 threadId);
 	void endTalkThreads();
 	void endTalkThreadsNoNotify();


Commit: 2e149cf651ee9344ee96ea904c5dce5a444aeaff
    https://github.com/scummvm/scummvm/commit/2e149cf651ee9344ee96ea904c5dce5a444aeaff
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on Duckman

- Implement Duckman cursor and interaction handling
- Add more script opcodes
- Add TextDrawer and ScreenText (needs minor refactoring for BBDOU)

Changed paths:
  A engines/illusions/causethread_duckman.cpp
  A engines/illusions/causethread_duckman.h
  A engines/illusions/cursor_duckman.cpp
  A engines/illusions/cursor_duckman.h
  A engines/illusions/screentext.cpp
  A engines/illusions/screentext.h
  A engines/illusions/textdrawer.cpp
  A engines/illusions/textdrawer.h
    engines/illusions/actor.cpp
    engines/illusions/backgroundresource.cpp
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/dictionary.cpp
    engines/illusions/dictionary.h
    engines/illusions/fontresource.cpp
    engines/illusions/fontresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_bbdou.h
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptopcodes.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptopcodes_duckman.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/talkthread_duckman.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index bfb6fe2..f3677e4 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -253,32 +253,49 @@ void Control::unpause() {
 }
 
 void Control::appearActor() {
-	if (_objectId == 0x40004) {
-		_vm->showCursor();
-	} else {
-		if (_actor->_frameIndex || _actorTypeId == 0x50004)
-			_actor->_flags |= 1;
-		else
-			_actor->_flags |= 0x1000;
-		for (uint i = 0; i < kSubObjectsCount; ++i)
-			if (_actor->_subobjects[i]) {
-				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
-				subControl->appearActor();
+	if (_vm->getGameId() == kGameIdDuckman) {
+		_flags |= 1;
+		_actor->_flags |= 1;
+		if (_objectId == 0x40004) {
+			if (_actor->_frameIndex) {
+				_actor->_flags |= 0x2000;
+				_actor->_flags |= 0x4000;
 			}
+			_vm->_input->discardButtons(0xFFFF);
+		}
+	} else {
+		if (_objectId == 0x40004) {
+			_vm->showCursor();
+		} else {
+			if (_actor->_frameIndex || _actorTypeId == 0x50004)
+				_actor->_flags |= 1;
+			else
+				_actor->_flags |= 0x1000;
+			for (uint i = 0; i < kSubObjectsCount; ++i)
+				if (_actor->_subobjects[i]) {
+					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
+					subControl->appearActor();
+				}
+		}
 	}
 }
 
 void Control::disappearActor() {
-	if (_objectId == 0x40004) {
-		_vm->hideCursor();
-	} else {
+	if (_vm->getGameId() == kGameIdDuckman) {
+		_flags &= ~1;
 		_actor->_flags &= ~1;
-		_actor->_flags &= ~0x1000;
-		for (uint i = 0; i < kSubObjectsCount; ++i)
-			if (_actor->_subobjects[i]) {
-				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
-				subControl->disappearActor();
-			}
+	} else {
+		if (_objectId == 0x40004) {
+			_vm->hideCursor();
+		} else {
+			_actor->_flags &= ~1;
+			_actor->_flags &= ~0x1000;
+			for (uint i = 0; i < kSubObjectsCount; ++i)
+				if (_actor->_subobjects[i]) {
+					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
+					subControl->disappearActor();
+				}
+		}
 	}
 }
 
@@ -925,7 +942,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
 
-    if (_vm->getGameId() == kGameIdBBDOU) {
+	if (_vm->getGameId() == kGameIdBBDOU) {
 		_actor->_seqCodeValue2 = value == 1 ? 350 : 600;
 	} else if (_vm->getGameId() == kGameIdDuckman) {
 		_actor->_seqCodeValue2 = value == 1 ? 350 : 750;
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index a4111c7..2841971 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -69,7 +69,7 @@ void BackgroundResourceLoader::unload(Resource *resource) {
 	// TODO Remove IDs from item48s
 	delete backgroundItem->_bgRes;
 	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
-	// TODO _vm->setDefPointDimensions1();
+	_vm->setDefaultTextCoords();
 	debug("BackgroundResourceLoader::unload() Unloading background %08X OK", resource->_resId);
 }
 
@@ -378,7 +378,7 @@ void BackgroundItem::pause() {
 		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
 			krndictRemoveID(_bgRes->_item48s[i].id);
 		*/
-		// TODO _vm->setDefPointDimensions1();
+		_vm->setDefaultTextCoords();
 		_vm->_camera->getActiveState(_savedCameraState);
 		_savedPalette = new byte[1024];
 		_vm->_screen->getPalette(_savedPalette);
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 57d6f6f..b9d9342 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -279,7 +279,7 @@ void Camera::update(uint32 currTime) {
 
 }
 
-void Camera::setBounds(Common::Point &minPt, Common::Point &maxPt) {
+void Camera::setBounds(Common::Point minPt, Common::Point maxPt) {
 	_activeState._bounds._topLeft = minPt;
 	_activeState._bounds._bottomRight = maxPt;
 }
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index a6f8a73..9fa12c4 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -77,7 +77,7 @@ public:
 	void popCameraMode();
 	void clearCameraModeStack();
 	void update(uint32 currTime);
-	void setBounds(Common::Point &minPt, Common::Point &maxPt);
+	void setBounds(Common::Point minPt, Common::Point maxPt);
 	void setBoundsToDimensions(WidthHeight &dimensions);
 	Common::Point getCurrentPan();
 	Common::Point getScreenOffset();
diff --git a/engines/illusions/causethread_duckman.cpp b/engines/illusions/causethread_duckman.cpp
new file mode 100644
index 0000000..a10b03f
--- /dev/null
+++ b/engines/illusions/causethread_duckman.cpp
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/causethread_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/input.h"
+
+namespace Illusions {
+
+// TalkThread
+
+CauseThread_Duckman::CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 triggerThreadId)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _triggerThreadId(triggerThreadId), _flag(false) {
+	_type = kTTCauseThread;
+	_tag = _vm->getCurrentScene();
+}
+
+int CauseThread_Duckman::onUpdate() {
+	if (_flag) {
+		if (_vm->getCurrentScene() == _tag) {
+			Control *cursorCursor = _vm->getObjectControl(0x40004);
+			cursorCursor->appearActor();
+			_vm->_input->discardButtons(1);
+		}
+		return kTSTerminate;
+	} else {
+		_tag = _vm->getCurrentScene();
+		Control *cursorCursor = _vm->getObjectControl(0x40004);
+		cursorCursor->disappearActor();
+		_vm->_input->discardButtons(1);
+		_vm->startScriptThread(_triggerThreadId, _threadId);
+		_flag = true;
+		return kTSSuspend;
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/causethread_duckman.h b/engines/illusions/causethread_duckman.h
new file mode 100644
index 0000000..97bf00e
--- /dev/null
+++ b/engines/illusions/causethread_duckman.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_CAUSETHREAD_DUCKMAN_H
+#define ILLUSIONS_CAUSETHREAD_DUCKMAN_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+class CauseThread_Duckman : public Thread {
+public:
+	CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 triggerThreadId);
+	virtual int onUpdate();
+public:
+	IllusionsEngine_Duckman *_vm;
+	bool _flag;
+	uint32 _triggerThreadId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_CAUSETHREAD_DUCKMAN_H
diff --git a/engines/illusions/cursor_duckman.cpp b/engines/illusions/cursor_duckman.cpp
new file mode 100644
index 0000000..d23c9ee
--- /dev/null
+++ b/engines/illusions/cursor_duckman.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/cursor_duckman.h"
+#include "illusions/input.h"
+
+namespace Illusions {
+
+Cursor_Duckman::Cursor_Duckman(IllusionsEngine_Duckman *vm)
+	: _vm(vm) {
+	_status = 1;
+	_control = 0;
+	_x = 320;
+	_y = 240;
+	_cursorNum = 1;
+	_field_10 = 0;
+	_sequenceId = 0;
+}
+
+void Cursor_Duckman::place(Control *control, uint32 sequenceId) {
+	_status = 2;
+	_control = control;
+	_cursorNum = 1;
+	_field_10 = 0;
+	_sequenceId = sequenceId;
+	_visibleCtr = 0;
+	_control->_flags |= 8;
+	setActorIndex(_cursorNum, 1, 0);
+	_vm->_input->setCursorPosition(_control->_actor->_position);
+}
+
+void Cursor_Duckman::setActorIndex(int a2, int a3, int a4) {
+	_control->_actor->_actorIndex = 1;// TODO?!? *((_BYTE *)&stru_42C040[30].y + 2 * ((always0 != 0) + 2 * a2) + a3 + 1);
+}
+
+void Cursor_Duckman::setControl(Control *control) {
+	_control = control;
+}
+
+void Cursor_Duckman::show() {
+	++_visibleCtr;
+	if (_visibleCtr > 0) {
+		_control->_flags |= 1;
+		_control->_actor->_flags |= 1;
+		if (_control->_actor->_frameIndex) {
+			_control->_actor->_flags |= 0x2000;
+			_control->_actor->_flags |= 0x4000;
+		}
+		_vm->_input->discardButtons(0xFFFF);
+	}
+}
+
+void Cursor_Duckman::hide() {
+	--_visibleCtr;
+	if (_visibleCtr <= 0) {
+		_control->_flags &= ~1;
+		_control->_actor->_flags &= ~1;
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/cursor_duckman.h b/engines/illusions/cursor_duckman.h
new file mode 100644
index 0000000..1525857
--- /dev/null
+++ b/engines/illusions/cursor_duckman.h
@@ -0,0 +1,30 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_CURSOR_H
+#define ILLUSIONS_CURSOR_H
+
+namespace Illusions {
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_CURSOR_H
diff --git a/engines/illusions/dictionary.cpp b/engines/illusions/dictionary.cpp
index 2d9d4b4..5cf4cef 100644
--- a/engines/illusions/dictionary.cpp
+++ b/engines/illusions/dictionary.cpp
@@ -24,6 +24,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/actorresource.h"
 #include "illusions/backgroundresource.h"
+#include "illusions/fontresource.h"
 #include "illusions/talkresource.h"
 
 namespace Illusions {
@@ -40,6 +41,18 @@ ActorType *Dictionary::findActorType(uint32 id) {
 	return _actorTypes.find(id);
 }
 
+void Dictionary::addFont(uint32 id, FontResource *fontResource) {
+	_fontResources.add(id, fontResource);
+}
+
+void Dictionary::removeFont(uint32 id) {
+	_fontResources.remove(id);
+}
+
+FontResource *Dictionary::findFont(uint32 id) {
+	return _fontResources.find(id);
+}
+
 void Dictionary::addSequence(uint32 id, Sequence *sequence) {
 	_sequences.add(id, sequence);
 }
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index 11b05a6..63cb9b1 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -29,6 +29,7 @@ namespace Illusions {
 
 class ActorType;
 class Control;
+class FontResource;
 class Sequence;
 class TalkEntry;
 
@@ -86,6 +87,10 @@ public:
 	void removeActorType(uint32 id);
 	ActorType *findActorType(uint32 id);
 
+    void addFont(uint32 id, FontResource *fontResource);
+	void removeFont(uint32 id);
+	FontResource *findFont(uint32 id);
+
     void addSequence(uint32 id, Sequence *sequence);
 	void removeSequence(uint32 id);
 	Sequence *findSequence(uint32 id);
@@ -99,9 +104,10 @@ public:
 
 protected:
 	DictionaryHashMap<ActorType> _actorTypes;
+	DictionaryHashMap<Control> _controls;
+	DictionaryHashMap<FontResource> _fontResources;
 	DictionaryHashMap<Sequence> _sequences;
 	DictionaryHashMap<TalkEntry> _talkEntries;
-	DictionaryHashMap<Control> _controls;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
index e0f48d0..adfb135 100644
--- a/engines/illusions/fontresource.cpp
+++ b/engines/illusions/fontresource.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/fontresource.h"
+#include "illusions/dictionary.h"
 
 namespace Illusions {
 
@@ -30,11 +31,19 @@ namespace Illusions {
 void FontResourceLoader::load(Resource *resource) {
 	debug("FontResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
 
-    // TODO
+	// TODO
+	FontResource *fontResource = new FontResource();
+	fontResource->load(resource);
+	resource->_refId = fontResource;
+
+	_vm->_dict->addFont(resource->_resId, fontResource);
 	
 }
 
 void FontResourceLoader::unload(Resource *resource) {
+	FontResource *fontResource = _vm->_dict->findFont(resource->_resId);
+	delete fontResource;
+	_vm->_dict->removeFont(resource->_resId);
 }
 
 void FontResourceLoader::buildFilename(Resource *resource) {
@@ -46,4 +55,76 @@ bool FontResourceLoader::isFlag(int flag) {
 		flag == kRlfLoadFile;
 }
 
+// CharInfo
+
+void CharInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_width = stream.readUint16LE();
+	_field_2 = stream.readUint16LE();
+	uint32 pixelsOffs = stream.readUint32LE();
+	_pixels = dataStart + pixelsOffs;
+	debug("CharInfo::load() _width: %d; _field_2: %d; pixelsOffs: %08X",
+		_width, _field_2, pixelsOffs);
+}
+
+// CharRange
+
+void CharRange::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_firstChar = stream.readUint16LE();
+	_lastChar = stream.readUint16LE();
+	uint count = _lastChar - _firstChar + 1;
+	uint32 charInfosOffs = stream.readUint32LE();
+	_charInfos = new CharInfo[count];
+	for (uint i = 0; i < count; ++i) {
+		stream.seek(charInfosOffs + i * 8);
+		_charInfos[i].load(dataStart, stream);
+	}
+	debug("CharRange::load() _firstChar: %d; _lastChar: %d; charInfosOffs: %08X",
+		_firstChar, _lastChar, charInfosOffs);
+}
+
+CharInfo *CharRange::getCharInfo(uint16 c) {
+	return &_charInfos[c - _firstChar];
+}
+
+bool CharRange::containsChar(uint16 c) {
+	return c >= _firstChar && c <= _lastChar;
+}
+
+// FontResource
+
+FontResource::FontResource() {
+}
+
+FontResource::~FontResource() {
+}
+
+void FontResource::load(Resource *resource) {
+	byte *data = resource->_data;
+	uint32 dataSize = resource->_dataSize;
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	_totalSize = stream.readUint32LE();
+	_charHeight = stream.readUint16LE();
+	_field_6 = stream.readUint16LE();
+	_colorIndex = stream.readUint16LE();
+	_lineIncr = stream.readUint16LE();
+	_widthC = stream.readUint16LE();
+	_charRangesCount = stream.readUint16LE();
+	uint32 charRangesOffs = stream.pos();
+	_charRanges = new CharRange[_charRangesCount];
+	for (uint i = 0; i < _charRangesCount; ++i) {
+		stream.seek(charRangesOffs + i * 8);
+		_charRanges[i].load(data, stream);
+	}
+	debug("FontResource::load() _charHeight: %d; _field_6: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
+		_charHeight, _field_6, _colorIndex, _lineIncr, _widthC, _charRangesCount);
+}
+
+CharInfo *FontResource::getCharInfo(uint16 c) {
+	for (uint i = 0; i < _charRangesCount; ++i)
+		if (_charRanges[i].containsChar(c))
+			return _charRanges[i].getCharInfo(c);
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/fontresource.h b/engines/illusions/fontresource.h
index 66860bb..33360d7 100644
--- a/engines/illusions/fontresource.h
+++ b/engines/illusions/fontresource.h
@@ -42,6 +42,43 @@ protected:
 	IllusionsEngine *_vm;
 };
 
+// TODO
+
+struct CharInfo {
+	int16 _width;
+	int16 _field_2;
+	byte *_pixels;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct CharRange {
+	uint16 _firstChar;
+	uint16 _lastChar;
+	CharInfo *_charInfos;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	CharInfo *getCharInfo(uint16 c);
+	bool containsChar(uint16 c);
+};
+
+class FontResource {
+public:
+	FontResource();
+	~FontResource();
+	void load(Resource *resource);
+	CharInfo *getCharInfo(uint16 c);
+	int16 getColorIndex() const { return _colorIndex; }
+public:
+	uint32 _totalSize;
+	int16 _charHeight;
+	int16 _field_6;
+	int16 _colorIndex;
+	int16 _lineIncr;
+	int16 _widthC;
+	uint _charRangesCount;
+	CharRange *_charRanges;
+	CharRange *getCharRange(uint16 c);
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_FONTRESOURCE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 4d9f4a0..f4ba0cd 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -32,6 +32,7 @@
 #include "illusions/input.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
+#include "illusions/screentext.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
 #include "illusions/soundresource.h"
@@ -159,7 +160,6 @@ int IllusionsEngine::updateGraphics() {
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
 		Actor *actor = control->_actor;
-		
 		if (control->_pauseCtr == 0 && actor && (actor->_flags & 1) && !(actor->_flags & 0x0200)) {
 			Common::Point drawPosition = control->calcPosition(panPoint);
 			if (actor->_flags & 0x2000) {
@@ -184,13 +184,11 @@ int IllusionsEngine::updateGraphics() {
 		}
 	}
 
-#if 0 // TODO
-	if (_textInfo._surface) {
+	if (_screenText->_surface) {
 		int16 priority = getPriorityFromBase(99);
-		_screen->_drawQueue->insertTextSurface(_textInfo._surface, _textInfo._dimensions,
-			_textInfo._position, priority);
+		_screen->_drawQueue->insertTextSurface(_screenText->_surface, _screenText->_dimensions,
+			_screenText->_position, priority);
 	}
-#endif
 
 	return 1;
 }
@@ -294,4 +292,20 @@ uint32 IllusionsEngine::clipTextDuration(uint32 duration) {
 	return duration;
 }
 
+void IllusionsEngine::getDefaultTextDimensions(WidthHeight &dimensions) {
+	dimensions = _defaultTextDimensions;
+}
+
+void IllusionsEngine::setDefaultTextDimensions(WidthHeight &dimensions) {
+	_defaultTextDimensions = dimensions;
+}
+
+void IllusionsEngine::getDefaultTextPosition(Common::Point &position) {
+	position = _defaultTextPosition;
+}
+
+void IllusionsEngine::setDefaultTextPosition(Common::Point &position) {
+	_defaultTextPosition = position;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index ff7d219..3bfa876 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -23,6 +23,7 @@
 #ifndef ILLUSIONS_ILLUSIONS_H
 #define ILLUSIONS_ILLUSIONS_H
 
+#include "illusions/graphics.h"
 #include "audio/mixer.h"
 #include "audio/decoders/aiff.h"
 #include "common/array.h"
@@ -63,6 +64,7 @@ class Dictionary;
 class FramesList;
 class Input;
 class Screen;
+class ScreenText;
 class ScriptOpcodes;
 class ScriptResource;
 class Sequence;
@@ -97,6 +99,7 @@ public:
 	void updateEvents();
 
 	Screen *_screen;
+	ScreenText *_screenText;
 	Input *_input;
 	ActorItems *_actorItems;
 	BackgroundItems *_backgroundItems;
@@ -118,6 +121,9 @@ public:
 	int _field8;
 	uint32 _fieldA, _fieldE;
 
+	WidthHeight _defaultTextDimensions;
+	Common::Point _defaultTextPosition;
+
 	int16 _menuChoiceOfs;
 
 	int getGameId() const {
@@ -143,7 +149,12 @@ public:
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
 	uint32 clipTextDuration(uint32 duration);
+	void getDefaultTextDimensions(WidthHeight &dimensions);
+	void setDefaultTextDimensions(WidthHeight &dimensions);
+	void getDefaultTextPosition(Common::Point &position);
+	void setDefaultTextPosition(Common::Point &position);
 
+	virtual void setDefaultTextCoords() = 0;
 	virtual void loadSpecialCode(uint32 resId) = 0;
 	virtual void unloadSpecialCode(uint32 resId) = 0;
 	virtual void notifyThreadId(uint32 &threadId) = 0;
@@ -152,14 +163,12 @@ public:
 	virtual uint32 getPriorityFromBase(int16 priority) = 0;
 	virtual uint32 getPrevScene() = 0;	
 	virtual uint32 getCurrentScene() = 0;
-
 	virtual bool isCursorObject(uint32 actorTypeId, uint32 objectId) = 0;
 	virtual void setCursorControlRoutine(Control *control) = 0;
 	virtual void placeCursorControl(Control *control, uint32 sequenceId) = 0;
 	virtual void setCursorControl(Control *control) = 0;
 	virtual void showCursor() = 0;
 	virtual void hideCursor() = 0;
-
 	virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
 	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 84b3876..d5e9713 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -228,8 +228,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	
 	_globalSceneId = 0x00010003;	
 	
+    setDefaultTextCoords();
+	
 	_resSys->loadResource(0x000D0001, 0, 0);
-
 	startScriptThread(0x00020004, 0, 0, 0, 0);
 	_doScriptThreadInit = true;
 
@@ -301,6 +302,15 @@ uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32
 	return causeThreadId;
 }
 
+void IllusionsEngine_BBDOU::setDefaultTextCoords() {
+	WidthHeight dimensions;
+	dimensions._width = 480;
+	dimensions._height = 48;
+	Common::Point pt(320, 448);
+	setDefaultTextDimensions(dimensions);
+	setDefaultTextPosition(pt);
+}
+
 void IllusionsEngine_BBDOU::loadSpecialCode(uint32 resId) {
 	_specialCode = new BbdouSpecialCode(this);
 	_specialCode->init();
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
index d50fe4c..05aea8a 100644
--- a/engines/illusions/illusions_bbdou.h
+++ b/engines/illusions/illusions_bbdou.h
@@ -108,6 +108,8 @@ public:
 	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
 	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
 
+    void setDefaultTextCoords();
+
 	void loadSpecialCode(uint32 resId);
 	void unloadSpecialCode(uint32 resId);
 	void notifyThreadId(uint32 &threadId);
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 74c2788..0cd27e8 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -33,6 +33,7 @@
 #include "illusions/midiresource.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
+#include "illusions/screentext.h"
 #include "illusions/scriptopcodes_duckman.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
@@ -40,11 +41,13 @@
 #include "illusions/specialcode.h"
 //TODO#include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/talkresource.h"
+#include "illusions/textdrawer.h"
 #include "illusions/thread.h"
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
 
 #include "illusions/abortablethread.h"
+#include "illusions/causethread_duckman.h"
 #include "illusions/scriptthread.h"
 #include "illusions/talkthread_duckman.h"
 #include "illusions/timerthread.h"
@@ -93,6 +96,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
 
 	_screen = new Screen(this, 320, 200, 8);
+	_screenText = new ScreenText(this);
 	_input = new Input();	
 	_actorItems = new ActorItems(this);
 	_backgroundItems = new BackgroundItems(this);
@@ -115,11 +119,18 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_fieldA = 0;
 	_fieldE = 240;
 	
-	_globalSceneId = 0x00010003;	
-	
-	_resSys->loadResource(0x000D0001, 0x00010001, 0);
+	_globalSceneId = 0x00010003;
 
+    initSpecialCode();
+    setDefaultTextCoords();
+	initCursor();
     initActiveScenes();
+
+	_resSys->loadResource(0x120001, 0x00010001, 0);
+	_resSys->loadResource(0x120002, 0x00010001, 0);
+	_resSys->loadResource(0x120003, 0x00010001, 0);
+
+	_resSys->loadResource(0x000D0001, 0x00010001, 0);
 	startScriptThread(0x00020004, 0);
 	_doScriptThreadInit = true;
 
@@ -145,6 +156,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _backgroundItems;
 	delete _actorItems;
 	delete _input;
+	delete _screenText;
 	delete _screen;
 	delete _resSys;
 	delete _dict;
@@ -164,6 +176,15 @@ bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
 		*/
 }
 
+void IllusionsEngine_Duckman::setDefaultTextCoords() {
+	WidthHeight dimensions;
+	dimensions._width = 300;
+	dimensions._height = 32;
+	Common::Point pt(160, 176);
+	setDefaultTextDimensions(dimensions);
+	setDefaultTextPosition(pt);
+}
+
 void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
 //TODO	_specialCode = new BbdouSpecialCode(this);
 //TODO	_specialCode->init();
@@ -229,13 +250,40 @@ void IllusionsEngine_Duckman::setCursorControlRoutine(Control *control) {
 }
 
 void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequenceId) {
+	_cursor._gameState = 2;
+	_cursor._control = control;
+	_cursor._actorIndex = 1;
+	_cursor._savedActorIndex = 1;
+	_cursor._currOverlappedControl = 0;
+	_cursor._sequenceId1 = sequenceId;
+	_cursor._field14[0] = true;
+	_cursor._field14[1] = true;
+	_cursor._field14[2] = false;
+	_cursor._field14[3] = false;
+	_cursor._field14[4] = false;
+	_cursor._field14[5] = false;
+	_cursor._field14[9] = false;
+	_cursor._field14[10] = false;
+	_cursor._field14[11] = false;
+	_cursor._field14[12] = false;
+	_cursor._field14[6] = _cursor._sequenceId2 != 0 && _cursor._objectId != 0;
+	_cursor._field14[7] = false;
+	_cursor._field14[8] = false;
+	_cursor._op113_choiceOfsPtr = 0;
+	_cursor._notifyThreadId30 = 0;
+	_cursor._op113_objectNumCtr = 0;
+	_cursor._overlappedObjectNum = 0;
+	_cursor._field40 = 0;
+	control->_flags |= 8;
+	setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	// TODO Input_setMousePos(cursorControl->actor->position);
 	// TODO
-	control->_actor->_actorIndex = 2;
+	//control->_actor->_actorIndex = 2;
 	// TODO _cursor->place(control, sequenceId);
 }
 
 void IllusionsEngine_Duckman::setCursorControl(Control *control) {
-	// TODO
+	_cursor._control = control;
 }
 
 void IllusionsEngine_Duckman::showCursor() {
@@ -246,9 +294,108 @@ void IllusionsEngine_Duckman::hideCursor() {
 	// TODO
 }
 
+void IllusionsEngine_Duckman::initCursor() {
+	_cursor._gameState = 1;
+	_cursor._control = 0;
+	_cursor._position.x = 160;
+	_cursor._position.y = 100;
+	_cursor._objectId = 0;
+	_cursor._actorIndex = 1;
+	_cursor._savedActorIndex = 1;
+	_cursor._currOverlappedControl = 0;
+	_cursor._sequenceId1 = 0;
+	_cursor._sequenceId2 = 0;
+	_cursor._field14[0] = true;
+	_cursor._field14[1] = true;
+	_cursor._field14[2] = false;
+	_cursor._field14[3] = false;
+	_cursor._field14[4] = false;
+	_cursor._field14[5] = false;
+	_cursor._field14[6] = false;
+	_cursor._field14[7] = false;
+	_cursor._field14[8] = false;
+	_cursor._field14[9] = false;
+	_cursor._field14[10] = false;
+	_cursor._field14[11] = false;
+	_cursor._field14[12] = false;
+	_cursor._op113_choiceOfsPtr = 0;
+	_cursor._notifyThreadId30 = 0;
+	_cursor._op113_objectNumCtr = 0;
+	_cursor._overlappedObjectNum = 0;
+	_cursor._field40 = 0;
+}
+
+void IllusionsEngine_Duckman::setCursorActorIndex(int actorIndex, int a, int b) {
+	static int kCursorMap[13][2][2] = {
+		{{ 1,  2}, { 0,  0}},
+		{{ 3,  4}, { 0,  0}},
+		{{ 5,  6}, {13, 14}},
+		{{ 7,  8}, { 0,  0}},
+		{{ 9, 10}, { 0,  0}},
+		{{11, 12}, { 0,  0}},
+		{{ 1,  2}, { 0,  0}},
+		{{ 0,  0}, { 0,  0}},
+		{{ 0,  0}, { 0,  0}},
+		{{15, 16}, { 0,  0}},
+		{{17, 18}, { 0,  0}},
+		{{19, 20}, { 0,  0}},
+		{{21, 22}, { 0,  0}}
+	};
+	_cursor._control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
+	debug("_cursor._control->_actor->_actorIndex: %d", _cursor._control->_actor->_actorIndex);
+}
+
+void IllusionsEngine_Duckman::enableCursorVerb(int verbNum) {
+	if (verbNum != 7 || _cursor._sequenceId2)
+		_cursor._field14[verbNum - 1] = true;
+}
+
+void IllusionsEngine_Duckman::disableCursorVerb(int verbNum) {
+	_cursor._field14[verbNum - 1] = false;
+	if (_cursor._actorIndex == verbNum) {
+		_cursor._actorIndex = getCursorActorIndex();
+		setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		startCursorSequence();
+		_cursor._currOverlappedControl = 0;
+	}
+}
+
+void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
+	if (mode == 1) {
+		enableCursorVerb(4);
+		disableCursorVerb(1);
+		disableCursorVerb(2);
+		disableCursorVerb(7);
+		_cursor._actorIndex = 4;
+	} else {
+		enableCursorVerb(1);
+		enableCursorVerb(2);
+		enableCursorVerb(7);
+		disableCursorVerb(4);
+		_cursor._actorIndex = 1;
+	}
+	_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+	if (_cursor._currOverlappedControl)
+		setCursorActorIndex(_cursor._actorIndex, 2, 0);
+	else
+		setCursorActorIndex(_cursor._actorIndex, 1, 0);
+}
+
 void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	// TODO
+	if (control->_actor->_flags & 1) {
+		switch (_cursor._gameState) {
+		case 2:
+			updateGameState2();
+			break;
+		case 3:
+			// TODO updateGameState3(cursorControl);
+			break;
+		case 4:
+			// TODO ShellMgr_update(cursorControl);
+			break;
+		}
+	}
 }
 
 void IllusionsEngine_Duckman::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
@@ -437,4 +584,225 @@ uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
 	return _scriptResource->getObjectActorTypeId(objectId);
 }
 
+Common::Point IllusionsEngine_Duckman::convertMousePos(Common::Point mousePos) {
+	Common::Point screenOffsPt = _camera->getScreenOffset();
+	mousePos.x += screenOffsPt.x;
+	mousePos.y += screenOffsPt.y;
+	return mousePos;
+}
+
+void IllusionsEngine_Duckman::startCursorSequence() {
+	// NOTE Calls to startCursorSequence were put after calls to setCursorActorIndex
+	// to make the cursor switch more immediate. In the original these calls are swapped.
+	if (_cursor._actorIndex == 7)
+		_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
+	else
+		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+}
+
+int IllusionsEngine_Duckman::getCursorActorIndex() {
+	int result = _cursor._actorIndex;
+	do {
+		++result;
+		if (result > 13)
+			result = 1;
+	} while (!_cursor._field14[result - 1]);
+	return result;
+}
+
+void IllusionsEngine_Duckman::updateGameState2() {
+	Common::Point cursorPos = _input->getCursorPosition();
+	Common::Point convMousePos = convertMousePos(cursorPos);
+	int trackCursorIndex = -1;
+	bool foundOverlapped;
+	Control *overlappedControl;
+
+	_cursor._control->_actor->_position = cursorPos;
+
+	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
+
+	if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
+		trackCursorIndex = 10;
+	} else if (cursorPos.y >= 192 && !_camera->isAtPanLimit(2)) {
+		trackCursorIndex = 11;
+	} else if (cursorPos.x < 8 && !_camera->isAtPanLimit(3)) {
+		trackCursorIndex = 12;
+	} else if (cursorPos.x >= 312 && !_camera->isAtPanLimit(4)) {
+		trackCursorIndex = 13;
+	} else if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
+		_cursor._actorIndex = _cursor._savedActorIndex;
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		startCursorSequence();
+	}
+
+	if (trackCursorIndex >= 0) {
+		if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
+			_cursor._savedActorIndex = _cursor._actorIndex;
+		if (_cursor._actorIndex != trackCursorIndex) {
+			_cursor._actorIndex = trackCursorIndex;
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+			startCursorSequence();
+		}
+		_cursor._currOverlappedControl = 0;
+		foundOverlapped = false;
+	}
+
+	if (foundOverlapped) {
+		if (_cursor._currOverlappedControl != overlappedControl) {
+			int cursorValue2 = 0;
+			if (overlappedControl->_flags & 2) {
+				if (_cursor._actorIndex != 3) {
+					_cursor._savedActorIndex = _cursor._actorIndex;
+					_cursor._actorIndex = 3;
+				}
+				if (overlappedControl->_flags & 0x40)
+					cursorValue2 = 1;
+			} else if (_cursor._actorIndex == 3) {
+				_cursor._actorIndex = _cursor._savedActorIndex;
+			}
+			setCursorActorIndex(_cursor._actorIndex, 2, cursorValue2);
+			startCursorSequence();
+			_cursor._currOverlappedControl = overlappedControl;
+		}
+	} else if (_cursor._currOverlappedControl) {
+		if (_cursor._actorIndex == 3)
+			_cursor._actorIndex = _cursor._savedActorIndex;
+		setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		startCursorSequence();
+		_cursor._currOverlappedControl = 0;
+	}
+
+	if (_input->pollButton(1)) {
+		if (_cursor._currOverlappedControl) {
+			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
+		} else {
+			_cursor._position = convertMousePos(_cursor._control->_actor->_position);
+			// TODO clipMousePos(&_cursor._position);
+			if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13)
+				runTriggerCause(1, _cursor._objectId, 0x40003);
+			else
+				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
+		}
+	} else if (_input->pollButton(2)) {
+		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
+			int newActorIndex = getCursorActorIndex();
+			debug("newActorIndex = %d", newActorIndex);
+			if (newActorIndex != _cursor._actorIndex) {
+				_cursor._actorIndex = newActorIndex;
+				if (_cursor._currOverlappedControl)
+					setCursorActorIndex(_cursor._actorIndex, 2, 0);
+				else
+					setCursorActorIndex(_cursor._actorIndex, 1, 0);
+				startCursorSequence();
+			}
+		}
+	} else if (_input->pollButton(8)) {
+		if (_cursor._field14[0] == 1) {
+			runTriggerCause(1, 0, _scriptResource->getField6C());
+		} else if (_cursor._field14[1] == 1) {
+			runTriggerCause(2, 0, _scriptResource->getField6C());
+		}
+	}
+
+}
+
+void IllusionsEngine_Duckman::playSoundEffect(int index) {
+	// TODO
+}
+
+bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(getCurrentScene() & 0xFFFF);
+	bool found =
+		progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
+		progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
+	if (!found) {
+		progInfo = _scriptResource->getProgInfo(3);
+		found =
+			progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
+			progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
+	}
+	return found;
+}
+
+uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
+	// TODO
+	debug("runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
+	uint32 triggerThreadId;
+
+	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
+		return 0;
+
+	bool flag = false;
+	if (_scriptResource->_properties.get(0x000E003C)) {
+		if (verbId == 7 && objectId == 0x40003 ) {
+			playSoundEffect(7);
+			flag = true;
+		} else if (objectId == 0x40003 ) {
+			playSoundEffect(14);
+			flag = true;
+		} else if (verbId == 3 ) {
+			playSoundEffect(16);
+			flag = true;
+		} else if (verbId == 2 ) {
+			flag = true;
+		}
+	}
+
+	if (!flag) {
+		if (objectId == 0x40003) {
+			playSoundEffect(14);
+		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getField6C() == objectId) {
+			playSoundEffect(15);
+		} else if (verbId == 7 && _scriptResource->getField6C() == objectId) {
+			playSoundEffect(15);
+		} else if (verbId == 1) {
+			playSoundEffect(1);
+		} else if (verbId == 2) {
+			playSoundEffect(2);
+		} else if (verbId == 3) {
+			playSoundEffect(3);
+		} else if (verbId == 4 || verbId == 7) {
+			playSoundEffect(4);
+		} else if (verbId == 9) {
+			playSoundEffect(5);
+		}
+	}
+
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting cause thread %08X", tempThreadId);
+	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
+		triggerThreadId);
+	_threads->startThread(causeThread);
+
+	return tempThreadId;
+}
+
+// Special code
+
+typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
+#define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &IllusionsEngine_Duckman::func);
+
+void IllusionsEngine_Duckman::initSpecialCode() {
+	SPECIAL(0x00160002, spcSetCursorHandMode);
+}
+
+void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCall) {
+	SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
+	if (it != _specialCodeMap.end()) {
+		(*(*it)._value)(opCall);
+	} else {
+		debug("IllusionsEngine_Duckman::runSpecialCode() Unimplemented special code %08X", specialCodeId);
+		notifyThreadId(opCall._threadId);
+	}
+}
+
+void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	setCursorHandMode(mode);
+	notifyThreadId(opCall._threadId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index f70156a..375407f 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -32,6 +32,31 @@ namespace Illusions {
 class Dictionary;
 class ScriptStack;
 
+struct Cursor_Duckman {
+	int _gameState;
+	Control *_control;
+	Common::Point _position;
+	uint32 _objectId;
+	int _actorIndex;
+	int _savedActorIndex;
+	bool _field14[14];
+	Control *_currOverlappedControl;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	uint32 _notifyThreadId30;
+	int16 *_op113_choiceOfsPtr;
+	int _op113_objectNumCtr;
+	uint _overlappedObjectNum;
+	uint32 _field3C;
+	uint32 _field40;
+};
+
+struct OpCall;
+
+typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
+typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
+typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
+
 class IllusionsEngine_Duckman : public IllusionsEngine {
 public:
 	IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd);
@@ -55,6 +80,12 @@ public:
 	uint _activeScenesCount;
 	uint32 _activeScenes[6];
 
+	Cursor_Duckman _cursor;
+
+    SpecialCodeMap _specialCodeMap;
+
+	void setDefaultTextCoords();
+
 	void loadSpecialCode(uint32 resId);
 	void unloadSpecialCode(uint32 resId);
 	void notifyThreadId(uint32 &threadId);
@@ -70,6 +101,11 @@ public:
 	void setCursorControl(Control *control);
 	void showCursor();
 	void hideCursor();
+	void initCursor();
+	void setCursorActorIndex(int actorIndex, int a, int b);
+	void enableCursorVerb(int verbNum);
+	void disableCursorVerb(int verbNum);
+	void setCursorHandMode(int mode);
 	void cursorControlRoutine(Control *control, uint32 deltaTime);
 
 	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
@@ -104,6 +140,19 @@ public:
 	
 	uint32 getObjectActorTypeId(uint32 objectId);
 	
+	Common::Point convertMousePos(Common::Point mousePos);
+	void startCursorSequence();
+	int getCursorActorIndex();
+	void updateGameState2();
+	void playSoundEffect(int index);
+	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
+	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
+
+	// Special code
+	void initSpecialCode();
+	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
+	void spcSetCursorHandMode(OpCall &opCall);
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 4341732..f0688d3 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -10,6 +10,7 @@ MODULE_OBJS := \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
 	camera.o \
+	causethread_duckman.o \
 	cursor.o \
 	detection.o \
 	dictionary.o \
@@ -23,6 +24,7 @@ MODULE_OBJS := \
 	midiresource.o \
 	resourcesystem.o \
 	screen.o \
+	screentext.o \
 	scriptman.o \
 	scriptopcodes.o \
 	scriptopcodes_bbdou.o \
@@ -35,6 +37,7 @@ MODULE_OBJS := \
 	talkresource.o \
 	talkthread.o \
 	talkthread_duckman.o \
+	textdrawer.o \
 	thread.o \
 	time.o \
 	timerthread.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 0eb7832..f4b6982 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/screen.h"
+#include "illusions/fontresource.h"
 #include "engines/util.h"
 #include "graphics/palette.h"
 
@@ -315,6 +316,14 @@ void Screen::setPalette(byte *colors, uint start, uint count) {
 	_needRefreshPalette = true;
 }
 
+void Screen::setPaletteEntry(int16 index, byte r, byte g, byte b) {
+	byte colors[4];
+	colors[0] = r;
+	colors[1] = g;
+	colors[2] = b;
+	setPalette(colors, index, 1);
+}
+
 void Screen::getPalette(byte *colors) {
 	byte *srcPal = _mainPalette;
 	for (uint i = 0; i < 256; ++i) {
@@ -333,6 +342,26 @@ void Screen::updatePalette() {
 	}
 }
 
+void Screen::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
+	for (uint i = 0; i < count; ++i)
+		x += font->_widthC + drawChar(font, surface, x, y, *text++);
+}
+
+int16 Screen::drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+	const CharInfo *charInfo = font->getCharInfo(c);
+	const int16 charWidth = charInfo->_width;
+	byte *dst = (byte*)surface->getBasePtr(x, y);
+	byte *pixels = charInfo->_pixels;
+	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
+		for (int16 xc = 0; xc < charWidth; ++xc)
+			if (pixels[xc])
+				dst[xc] = pixels[xc];
+		dst += surface->pitch;
+		pixels += charWidth;
+	}
+	return charWidth;
+}
+
 void Screen::setSystemPalette(byte *palette) {
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 }
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 5bd5eb7..91750cf 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -31,7 +31,7 @@
 namespace Illusions {
 
 class IllusionsEngine;
-class Screen;
+class FontResource;
 
 struct SpriteDecompressQueueItem {
 	byte *_drawFlags;
@@ -112,8 +112,11 @@ public:
 	void decompressSprite(SpriteDecompressQueueItem *item);
 	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
 	void setPalette(byte *colors, uint start, uint count);
+	void setPaletteEntry(int16 index, byte r, byte g, byte b);
 	void getPalette(byte *colors);
 	void updatePalette();
+	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
+	int16 drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
 	int16 getScreenWidth() const { return _backSurface->w; }
 	int16 getScreenHeight() const { return _backSurface->h; }
 public:
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
new file mode 100644
index 0000000..684ab5a
--- /dev/null
+++ b/engines/illusions/screentext.cpp
@@ -0,0 +1,171 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/screentext.h"
+#include "illusions/dictionary.h"
+#include "illusions/fontresource.h"
+#include "illusions/screen.h"
+#include "illusions/textdrawer.h"
+#include "engines/util.h"
+#include "graphics/palette.h"
+
+namespace Illusions {
+
+ScreenText::ScreenText(IllusionsEngine *vm)
+	: _vm(vm), _surface(0) {
+}
+
+ScreenText::~ScreenText() {
+}
+
+void ScreenText::getTextInfoDimensions(WidthHeight &textInfoDimensions) {
+	textInfoDimensions = _dimensions;
+}
+
+void ScreenText::setTextInfoPosition(Common::Point position) {
+	_position = position;
+	clipTextInfoPosition(_position);
+}
+
+void ScreenText::updateTextInfoPosition(Common::Point position) {
+	WidthHeight dimensions;
+	getTextInfoDimensions(dimensions);
+	position.x = position.x - dimensions._width / 2;
+	position.y = position.y - dimensions._height / 2;
+	setTextInfoPosition(position);
+}
+
+void ScreenText::clipTextInfoPosition(Common::Point &position) {
+	// TODO Set min/max for BBDOU
+	if (position.x < 2)
+		position.x = 2;
+	else if (position.x + _dimensions._width > 318)
+		position.x = 318 - _dimensions._width;
+	if (position.y < 2)
+		position.y = 2;
+	else if (position.y + _dimensions._height > 198)
+		position.y = 198 - _dimensions._height;
+}
+
+bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, Common::Point offsPt,
+	uint16 *text, uint textFlags, uint16 color2, uint16 color1, uint16 *&outTextPtr) {
+	TextDrawer textDrawer;
+	bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr);
+	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
+	_dimensions = dimensions;
+	textDrawer.drawText(_vm->_screen, _surface, color2, color1);
+	return done;
+}
+
+bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions, Common::Point offsPt, uint flags,
+	uint16 color2, uint16 color1, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr) {
+	
+	if (!_screenTexts.empty()) {
+		ScreenTextEntry *screenText = _screenTexts.back();
+		screenText->_info._position = _position;
+		freeTextSurface();
+	}
+	
+	ScreenTextEntry *screenText = new ScreenTextEntry();
+	screenText->_info._fontId = fontId;
+	screenText->_info._dimensions = dimensions;
+	screenText->_info._offsPt = offsPt;
+	screenText->_info._flags = 0;
+	if (flags & 8)
+		screenText->_info._flags |= 8;
+	if (flags & 0x10)
+		screenText->_info._flags |= 0x10;
+	if (flags & 1)
+		screenText->_info._flags |= 1;
+	else
+		screenText->_info._flags |= 2;
+	screenText->_info._color2 = color2;
+	screenText->_info._color1 = color1;
+	screenText->_info._colorR = colorR;
+	screenText->_info._colorG = colorG;
+	screenText->_info._colorB = colorB;
+	_screenTexts.push_back(screenText);
+
+	FontResource *font = _vm->_dict->findFont(screenText->_info._fontId);
+	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
+		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
+		outTextPtr);
+	_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
+
+	uint16 *textPart = screenText->_text;
+	while (text != outTextPtr)
+		*textPart++ = *text++;
+	*textPart = 0;
+
+	updateTextInfoPosition(Common::Point(160, 100));
+
+	return done;
+}
+
+void ScreenText::removeText() {
+	freeTextSurface();
+
+	if (!_screenTexts.empty()) {
+		ScreenTextEntry *screenText = _screenTexts.back();
+		delete screenText;
+		_screenTexts.pop_back();
+	}
+
+	if (!_screenTexts.empty()) {
+		ScreenTextEntry *screenText = _screenTexts.back();
+		if (screenText->_info._fontId) {
+			uint16 *outTextPtr;
+			FontResource *font = _vm->_dict->findFont(screenText->_info._fontId);
+			refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
+				screenText->_text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
+				outTextPtr);
+			_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
+			setTextInfoPosition(screenText->_info._position);
+		}
+	}
+
+}
+
+void ScreenText::clearText() {
+	
+	if (!_screenTexts.empty()) {
+		ScreenTextEntry *screenText = _screenTexts.back();
+		screenText->_info._position = _position;
+		freeTextSurface();
+	}
+	
+	ScreenTextEntry *screenText = new ScreenTextEntry();
+	screenText->_info._fontId = 0;
+	_screenTexts.push_back(screenText);
+
+}
+
+void ScreenText::freeTextSurface() {
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+		_surface = 0;
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/screentext.h b/engines/illusions/screentext.h
new file mode 100644
index 0000000..4340224
--- /dev/null
+++ b/engines/illusions/screentext.h
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCREENTEXT_H
+#define ILLUSIONS_SCREENTEXT_H
+
+#include "illusions/graphics.h"
+#include "common/list.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+class FontResource;
+
+struct ScreenTextInfo {
+	Common::Point _position;
+	WidthHeight _dimensions;
+	Common::Point _offsPt;
+	uint32 _fontId;
+	uint16 _color2;
+	uint16 _color1;
+	byte _colorR, _colorG, _colorB;
+	uint _flags;
+};
+
+struct ScreenTextEntry {
+	ScreenTextInfo _info;
+	uint16 _text[1024];
+};
+
+class ScreenText {
+public:
+	ScreenText(IllusionsEngine *vm);
+	~ScreenText();
+	void getTextInfoDimensions(WidthHeight &textInfoDimensions);
+	void setTextInfoPosition(Common::Point position);
+	void updateTextInfoPosition(Common::Point position);
+	void clipTextInfoPosition(Common::Point &position);
+	bool refreshScreenText(FontResource *font, WidthHeight dimensions, Common::Point offsPt,
+		uint16 *text, uint textFlags, uint16 color2, uint16 color1, uint16 *&outTextPtr);
+	bool insertText(uint16 *text, uint32 fontId, WidthHeight dimensions, Common::Point offsPt, uint flags,
+		uint16 color2, uint16 color1, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr);
+	void removeText();
+	void clearText();
+public:
+	IllusionsEngine *_vm;
+	Common::Point _position;
+	WidthHeight _dimensions;
+	Graphics::Surface *_surface;
+	Common::List<ScreenTextEntry*> _screenTexts;
+	void freeTextSurface();
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCREENTEXT_H
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 1020e61..cf20380 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -61,6 +61,7 @@ protected:
 
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
+#define ARG_BYTE(name) byte name = opCall.readByte(); debug(0, "ARG_BYTE(" #name " = %d)", name);
 #define ARG_INT16(name) int16 name = opCall.readSint16(); debug(0, "ARG_INT16(" #name " = %d)", name);
 #define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(0, "ARG_UINT32(" #name " = %08X)", name);
 
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index ffe4ac0..f7e9676 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -63,14 +63,25 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(7, opStartTimerThread);
 	OPCODE(9, opNotifyThread);
 	OPCODE(10, opSuspendThread);
+	OPCODE(16, opLoadResource);
+	OPCODE(17, opUnloadResource);
 	OPCODE(18, opEnterScene18);
 	OPCODE(20, opChangeScene);
+	OPCODE(22, opStartModalScene);
+	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
 	OPCODE(25, opLeaveScene24);
+	OPCODE(34, opPanToObject);
 	OPCODE(38, opStartFade);
 	OPCODE(39, opSetDisplay);
+	OPCODE(40, opSetCameraBounds);
+	OPCODE(48, opSetProperty);
 	OPCODE(49, opPlaceActor);
+	OPCODE(50, opFaceActor);
+	OPCODE(51, opFaceActorToObject);
 	OPCODE(52, opStartSequenceActor);
+	OPCODE(54, opStartMoveActor);
+	OPCODE(55, opStartMoveActorToObject);
 	OPCODE(56, opStartTalkThread);
 	OPCODE(57, opAppearActor);
 	OPCODE(58, opDisappearActor);
@@ -102,22 +113,14 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(8, opStartTempScriptThread);
 	OPCODE(14, opSetThreadSceneId);
 	OPCODE(15, opEndTalkThreads);
-	OPCODE(17, opUnloadResource);
 	OPCODE(20, opEnterScene);
-	OPCODE(26, opStartModalScene);
-	OPCODE(27, opExitModalScene);
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
 	OPCODE(32, opPanCenterObject);
-	OPCODE(34, opPanToObject);
 	OPCODE(35, opPanToNamedPoint);
 	OPCODE(36, opPanToPoint);
 	OPCODE(37, opPanStop);
 	OPCODE(43, opClearBlockCounter);
-	OPCODE(45, opSetProperty);
-	OPCODE(47, opFaceActor);
-	OPCODE(48, opFaceActorToObject);	
-	OPCODE(51, opStartMoveActor);
 	OPCODE(53, opSetActorToNamedPoint);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
@@ -206,13 +209,32 @@ void ScriptOpcodes_Duckman::opSuspendThread(ScriptThread *scriptThread, OpCall &
 	_vm->_threads->suspendTimerThreads(threadId);
 }
 
+void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	uint32 sceneId = _vm->getCurrentScene();
+	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_resSys->unloadResourceById(resourceId);
+}
+
 void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	_vm->enterScene(sceneId, 0);
 }
 
-static uint dsceneId = 0x00010008, dthreadId = 0x00020029;
+//static uint dsceneId = 0, dthreadId = 0;
+static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
+//static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
+//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -234,6 +256,30 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	}
 }
 
+void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
+	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->enterScene(sceneId, threadId);
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_input->discardButtons(0xFFFF);
+	if (_vm->_scriptResource->_properties.get(0x000E0027)) {
+		// TODO _vm->startScriptThread2(0x10002, 0x20001, 0);
+		opCall._result = kTSTerminate;
+	} else {
+          _vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
+          _vm->exitScene();
+          _vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
+          _vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+	}
+}
+
 void ScriptOpcodes_Duckman::opEnterScene24(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -249,6 +295,14 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o
 	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
 }
 
+void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(arg1);
 	ARG_INT16(arg2);
@@ -266,6 +320,20 @@ void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opC
 	_vm->_screen->setDisplayOn(flag != 0);
 }
 
+void ScriptOpcodes_Duckman::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(x1);
+	ARG_INT16(y1);
+	ARG_INT16(x2);
+	ARG_INT16(y2);
+	_vm->_camera->setBounds(Common::Point(x1, y1), Common::Point(x2, y2));
+}
+
+void ScriptOpcodes_Duckman::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value);
+	ARG_UINT32(propertyId);
+	_vm->_scriptResource->_properties.set(propertyId, value != 0);
+}
+
 void ScriptOpcodes_Duckman::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -276,6 +344,26 @@ void ScriptOpcodes_Duckman::opPlaceActor(ScriptThread *scriptThread, OpCall &opC
 	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->faceActor(facing);
+}
+
+void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Control *control2 = _vm->_dict->getObjectControl(objectId2);
+	Common::Point pos1 = control1->getActorPosition();
+	Common::Point pos2 = control2->getActorPosition();
+	uint facing;
+	if (_vm->calcPointDirection(pos1, pos2, facing))
+		control1->faceActor(facing);
+}
+
 void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -285,6 +373,37 @@ void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpC
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	ARG_UINT32(sequenceId);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Common::Point pos;
+	if (objectId2 == 0x40003) {
+		pos = _vm->_cursor._position;
+	} else {
+		Control *control2 = _vm->_dict->getObjectControl(objectId2);
+		pos = control2->_feetPt;
+		if (control2->_actor) {
+			pos.x += control2->_actor->_position.x;
+			pos.y += control2->_actor->_position.y;
+		}
+	}
+	control1->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -351,7 +470,7 @@ void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCa
 void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeId);
-//TODO	_vm->_specialCode->run(specialCodeId, opCall);
+	_vm->runSpecialCode(specialCodeId, opCall);
 	//DEBUG Resume calling thread, later done by the special code
 	_vm->notifyThreadId(opCall._threadId);
 }
@@ -514,21 +633,6 @@ void ScriptOpcodes_Duckman::opEndTalkThreads(ScriptThread *scriptThread, OpCall
 	_vm->_threads->endTalkThreads();
 }
 
-void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
-	uint32 sceneId = _vm->getCurrentScene();
-	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_resSys->unloadResourceById(resourceId);
-}
-
 void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -542,28 +646,6 @@ void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opC
 		opCall._result = kTSTerminate;
 }
 
-void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->enterPause(opCall._callerThreadId);
-	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-	_vm->startScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->exitScene(opCall._callerThreadId);
-	_vm->leavePause(opCall._callerThreadId);
-	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
-}
-
 void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -585,14 +667,6 @@ void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall
 	_vm->_camera->panCenterObject(objectId, speed);
 }
 
-void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = control->getActorPosition();
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
 void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);	
 	ARG_UINT32(namedPointId);
@@ -616,43 +690,6 @@ void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCa
 	_vm->_scriptResource->_blockCounters.set(index, 0);
 }
 
-void ScriptOpcodes_Duckman::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value);	
-	ARG_UINT32(propertyId);	
-	_vm->_scriptResource->_properties.set(propertyId, value != 0);
-}
-
-void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(facing);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->faceActor(facing);
-}
-
-void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId1);
-	ARG_UINT32(objectId2);
-	Control *control1 = _vm->_dict->getObjectControl(objectId1);
-	Control *control2 = _vm->_dict->getObjectControl(objectId2);
-	Common::Point pos1 = control1->getActorPosition();
-	Common::Point pos2 = control2->getActorPosition();
-	uint facing;
-	if (_vm->calcPointDirection(pos1, pos2, facing))
-		control1->faceActor(facing);
-}
-
-void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
-}
-
 void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
index a533d7d..2a30c58 100644
--- a/engines/illusions/scriptopcodes_duckman.h
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -49,15 +49,26 @@ protected:
 	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opNotifyThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opSuspendThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
@@ -89,23 +100,14 @@ protected:
 	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index fb8260f..90658ca 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -33,7 +33,7 @@ void ScriptResourceLoader::load(Resource *resource) {
 
 	ScriptResource *scriptResource = new ScriptResource();
 	scriptResource->load(resource);
-	
+
 	_vm->_scriptResource = scriptResource;
 	
 }
@@ -311,6 +311,9 @@ void ScriptResource::load(Resource *resource) {
 		}
 	}
 	
+	stream.seek(0x6C);
+	_field6C = stream.readUint32LE();
+	
 	if (resource->_gameId == kGameIdDuckman)
 		fixupProgInfosDuckman();
 
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index f33ac10..6452c7c 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -114,6 +114,7 @@ public:
 	byte *getCode(uint32 codeOffs);
 	ProgInfo *getProgInfo(uint32 index);
 	uint32 getObjectActorTypeId(uint32 objectId);
+	uint32 getField6C() const { return _field6C; }
 public:
 	byte *_data;
 	uint32 _dataSize;
@@ -127,6 +128,7 @@ public:
 	uint32 _soundIds[27];
 	uint _objectMapCount;
 	uint32 *_objectMap;
+	uint32 _field6C;
 	void fixupProgInfosDuckman();
 };
 
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
index 46c11c8..fe666a3 100644
--- a/engines/illusions/talkthread_duckman.cpp
+++ b/engines/illusions/talkthread_duckman.cpp
@@ -25,6 +25,7 @@
 #include "illusions/actor.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/screentext.h"
 #include "illusions/scriptman.h"
 #include "illusions/talkresource.h"
 #include "illusions/time.h"
@@ -109,7 +110,7 @@ int TalkThread_Duckman::onUpdate() {
 		_status = 4;
 		// Fallthrough to status 4
 		
-	case 4: 
+	case 4:
 		if (!(_flags & 8) ) {
 			uint32 actorTypeId = _vm->getObjectActorTypeId(_objectId);
 			// TODO getActorTypeColor(actorTypeId, &_colorR, &_colorG, &_colorB);
@@ -141,7 +142,7 @@ int TalkThread_Duckman::onUpdate() {
 		if (!(_flags & 4) && !_vm->isVoicePlaying())
 			_flags |= 4;
 		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
-			// TODO _vm->removeText();
+			_vm->_screenText->removeText();
 			if (_entryText && *_entryText) {
 				refreshText();
 				_vm->_input->discardButtons(0x20);
@@ -159,7 +160,7 @@ int TalkThread_Duckman::onUpdate() {
 		}
 		if (_objectId && _vm->_input->pollButton(0x20)) {
 			if (!(_flags & 8)) {
-				// TODO largeObj_removeText();
+				_vm->_screenText->removeText();
 				if (_entryText && *_entryText)
 					refreshText();
 				else
@@ -178,13 +179,7 @@ int TalkThread_Duckman::onUpdate() {
 				}
 			}
 		}
-		/*
-		debug("8: %d", (_flags & 8) != 0);
-		debug("4: %d", (_flags & 4) != 0);
-		debug("2: %d", (_flags & 2) != 0);
-		*/
 		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
-debug("TALK DONE");		
 			_vm->_input->discardButtons(0x20);
 			return kTSTerminate;
 		}
@@ -226,7 +221,7 @@ void TalkThread_Duckman::onTerminated() {
 		if (!(_flags & 4))
 			_vm->stopVoice();
 		if (!(_flags & 8)) {
-			// TODO largeObj_removeText();
+			_vm->_screenText->removeText();
 		}
 		if (!(_flags & 2)) {
 			Control *control = _vm->_dict->getObjectControl(_objectId);
@@ -291,16 +286,17 @@ static char *debugW2I(byte *wstr) {
 }
 
 int TalkThread_Duckman::insertText() {
-	int charCount = 100;
 	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
-	_entryText = 0;
-	// TODO _vm->getDimensions1(&dimensions);
-	// TODO _vm->insertText(_currEntryText, 0x00120001, dimensions, 0, 2, 0, 0, _colorR, _colorG, _colorB, 0, &outTextPtr);
-	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
-	// TODO _entryText = outTextPtr;
-	// TODO _vm->getPoint1(&pt);
-	// TODO _vm->updateTextInfoPosition(pt);
-	return charCount >> 1;
+	WidthHeight dimensions;
+	_vm->getDefaultTextDimensions(dimensions);
+	uint16 *outTextPtr;
+	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions, Common::Point(0, 0), 2, 0, 0, _colorR, _colorG, _colorB, outTextPtr);
+	_entryText = (byte*)outTextPtr;
+	Common::Point pt;
+	_vm->getDefaultTextPosition(pt);
+	_vm->_screenText->updateTextInfoPosition(pt);
+	int charCount = (_entryText - _currEntryText) / 2;
+	return charCount;
 }
 
 TalkEntry *TalkThread_Duckman::getTalkResourceEntry(uint32 talkId) {
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
new file mode 100644
index 0000000..d3f9379
--- /dev/null
+++ b/engines/illusions/textdrawer.cpp
@@ -0,0 +1,195 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/textdrawer.h"
+#include "illusions/screen.h"
+
+namespace Illusions {
+
+bool TextDrawer::wrapText(FontResource *font, uint16 *text, WidthHeight *dimensions, Common::Point offsPt,
+	uint textFlags, uint16 *&outTextPtr) {
+	_font = font;	
+	_text = text;
+	_dimensions = dimensions;
+	_offsPt = offsPt;
+	_textFlags = textFlags;
+	// TODO Calc max width/height using textFlags (border, shadow)
+	_textLines.clear();
+	bool done = wrapTextIntern(0, 0, dimensions->_width, dimensions->_height, outTextPtr);
+	// TODO Adjust text dimensions
+	return done;
+}
+
+void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 color2, uint16 color1) {
+	// TODO Fill box, draw borders and shadow if flags are set
+	for (Common::Array<TextLine>::iterator it = _textLines.begin(); it != _textLines.end(); ++it) {
+		const TextLine &textLine = *it;
+		if (textLine._text)
+			screen->drawText(_font, surface, textLine._x, textLine._y, textLine._text, textLine._length);
+#if 0
+		for (int16 linePos = 0; linePos < textLine._length; ++linePos) {
+			const uint16 c = textLine._text[linePos];
+			debugN("%c", c);
+		}
+		debug(" ");
+#endif		
+	}
+}
+	
+bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeight, uint16 *&outTextPtr) {
+
+	bool lineBreak = false;
+	bool done = false;
+	bool hasChar13 = textHasChar(13);
+
+	uint16 *lineStartText = _text;
+	uint16 *currText = _text;
+	outTextPtr = _text;
+
+	int16 textPosY = y;
+	int16 currLineWidth = 0, currLineLen = 0;
+	int16 currWordWidth = 0, currWordLen = 0;
+	int16 maxLineWidth = 0;
+	int16 spaceWidth = getSpaceWidth();
+
+	while (*currText && !done) {
+
+		currWordWidth = 0;
+		currWordLen = 0;
+		do {
+			currWordWidth += getCharWidth(*currText);
+			++currText;
+			++currWordLen;
+		} while (*currText != 32 && *(currText - 1) != 32 && !hasChar13 && *currText != 13 && *currText);
+
+		if (currWordWidth - _font->_widthC > maxWidth) {
+			while (currWordWidth + currLineWidth - _font->_widthC > maxWidth) {
+				--currText;
+				--currWordLen;
+				currWordWidth -= getCharWidth(*currText);
+			}
+			lineBreak = true;
+		}
+
+		if (!lineBreak && currWordWidth + currLineWidth - _font->_widthC > maxWidth) {
+			lineBreak = true;
+		} else {
+			currLineWidth += currWordWidth;
+			currLineLen += currWordLen;
+			if (*currText == 0 || *currText == 13)
+				lineBreak = true;
+			if (lineBreak) {
+				currWordLen = 0;
+				currWordWidth = 0;
+			}
+		}
+		
+		while (lineBreak) {
+
+			currLineWidth -= _font->_widthC;
+
+			if (textPosY + _font->_charHeight <= maxHeight) {
+				int16 textPosX;
+
+				if (_textFlags & 2) {
+					textPosX = (_dimensions->_width - currLineWidth) / 2;
+					maxLineWidth = _dimensions->_width;
+				} else {
+					textPosX = x;
+				}
+				
+				_textLines.push_back(TextLine(lineStartText, currLineLen, textPosX, textPosY));
+
+				if (*currText == 13) {
+					++currText;
+					if (*currText == 10)
+						++currText;
+					while (*currText == 13) {
+						++currText;
+						if (*currText == 10)
+							++currText;
+						_textLines.push_back(TextLine());
+						textPosY += _font->_lineIncr + _font->_charHeight;
+					}
+					lineStartText = currText;
+				} else {
+					lineStartText = currText - currWordLen;
+					if (*lineStartText == 32) {
+						++lineStartText;
+						--currWordLen;
+						currWordWidth -= spaceWidth;
+					}
+				}
+
+				outTextPtr = lineStartText;
+
+				if (maxLineWidth < currLineWidth)
+					maxLineWidth = currLineWidth;
+
+				currLineWidth = currWordWidth;
+				currLineLen = currWordLen;
+				currWordWidth = 0;
+				currWordLen = 0;
+				textPosY += _font->_charHeight + _font->_lineIncr;
+				if (*currText || !currLineLen)
+					lineBreak = false;
+
+			} else {
+				lineBreak = false;
+				done = true;
+			}
+		}
+
+	}
+
+	_dimensions->_width = maxLineWidth;
+	_dimensions->_height = textPosY - _font->_lineIncr;
+
+	return !done;
+}
+
+bool TextDrawer::textHasChar(uint16 c) {
+	// TODO
+	return false;
+}
+
+int16 TextDrawer::getSpaceWidth() {
+	return getCharWidth(32);
+}
+
+int16 TextDrawer::getCharWidth(uint16 c) {
+	return _font->getCharInfo(c)->_width + _font->_widthC;
+}
+
+// TODO
+/*
+int16 offsX = (int16)(offsPt.x * 0.75);
+int16 offsY = (int16)(offsPt.y * 1.5);
+*/
+/*
+if (_surface)
+	drawChar(textPosX, textPosY, c);
+textPosX += getCharWidth(c);
+*/
+
+} // End of namespace Illusions
diff --git a/engines/illusions/textdrawer.h b/engines/illusions/textdrawer.h
new file mode 100644
index 0000000..a5f6102
--- /dev/null
+++ b/engines/illusions/textdrawer.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TEXTDRAWER_H
+#define ILLUSIONS_TEXTDRAWER_H
+
+#include "illusions/graphics.h"
+#include "illusions/fontresource.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+struct TextLine {
+	uint16 *_text;
+	int16 _length;
+	int16 _x, _y;
+	TextLine() : _text(0) {}
+	TextLine(uint16 *text, int16 length, int16 x, int16 y)
+		: _text(text), _length(length), _x(x), _y(y) {}
+};
+
+class TextDrawer {
+public:
+	bool wrapText(FontResource *font, uint16 *text, WidthHeight *dimensions, Common::Point offsPt,
+		uint textFlags, uint16 *&outTextPtr);
+	void drawText(Screen *screen, Graphics::Surface *surface, uint16 color2, uint16 color1);
+protected:
+	FontResource *_font;
+	uint16 *_text;
+	WidthHeight *_dimensions;
+	Common::Point _offsPt;
+	uint _textFlags;
+	Graphics::Surface *_surface;
+	
+	Common::Array<TextLine> _textLines;
+	
+	bool textHasChar(uint16 c);
+	int16 getSpaceWidth();
+	int16 getCharWidth(uint16 c);
+	bool wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeight, uint16 *&outTextPtr);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKRESOURCE_H
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index e9a9ec3..3a335a2 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -34,7 +34,8 @@ enum ThreadType {
 	kTTTimerThread       = 2,
 	kTTTalkThread        = 3,
 	kTTAbortableThread   = 4,
-	kTTSpecialThread     = 5
+	kTTSpecialThread     = 5,
+	kTTCauseThread       = 6
 };
 
 enum ThreadStatus {


Commit: 36c7ec4d344e13223ba630bd66baaf492870ce68
    https://github.com/scummvm/scummvm/commit/36c7ec4d344e13223ba630bd66baaf492870ce68
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Delete obsolete files

Changed paths:
  R engines/illusions/cursor_duckman.cpp
  R engines/illusions/cursor_duckman.h


diff --git a/engines/illusions/cursor_duckman.cpp b/engines/illusions/cursor_duckman.cpp
deleted file mode 100644
index d23c9ee..0000000
--- a/engines/illusions/cursor_duckman.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions_duckman.h"
-#include "illusions/actor.h"
-#include "illusions/cursor_duckman.h"
-#include "illusions/input.h"
-
-namespace Illusions {
-
-Cursor_Duckman::Cursor_Duckman(IllusionsEngine_Duckman *vm)
-	: _vm(vm) {
-	_status = 1;
-	_control = 0;
-	_x = 320;
-	_y = 240;
-	_cursorNum = 1;
-	_field_10 = 0;
-	_sequenceId = 0;
-}
-
-void Cursor_Duckman::place(Control *control, uint32 sequenceId) {
-	_status = 2;
-	_control = control;
-	_cursorNum = 1;
-	_field_10 = 0;
-	_sequenceId = sequenceId;
-	_visibleCtr = 0;
-	_control->_flags |= 8;
-	setActorIndex(_cursorNum, 1, 0);
-	_vm->_input->setCursorPosition(_control->_actor->_position);
-}
-
-void Cursor_Duckman::setActorIndex(int a2, int a3, int a4) {
-	_control->_actor->_actorIndex = 1;// TODO?!? *((_BYTE *)&stru_42C040[30].y + 2 * ((always0 != 0) + 2 * a2) + a3 + 1);
-}
-
-void Cursor_Duckman::setControl(Control *control) {
-	_control = control;
-}
-
-void Cursor_Duckman::show() {
-	++_visibleCtr;
-	if (_visibleCtr > 0) {
-		_control->_flags |= 1;
-		_control->_actor->_flags |= 1;
-		if (_control->_actor->_frameIndex) {
-			_control->_actor->_flags |= 0x2000;
-			_control->_actor->_flags |= 0x4000;
-		}
-		_vm->_input->discardButtons(0xFFFF);
-	}
-}
-
-void Cursor_Duckman::hide() {
-	--_visibleCtr;
-	if (_visibleCtr <= 0) {
-		_control->_flags &= ~1;
-		_control->_actor->_flags &= ~1;
-	}
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/cursor_duckman.h b/engines/illusions/cursor_duckman.h
deleted file mode 100644
index 1525857..0000000
--- a/engines/illusions/cursor_duckman.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_CURSOR_H
-#define ILLUSIONS_CURSOR_H
-
-namespace Illusions {
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_CURSOR_H


Commit: 998744608f27802526b085ebe94215719ef9f28d
    https://github.com/scummvm/scummvm/commit/998744608f27802526b085ebe94215719ef9f28d
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on Duckman

- Implement dialogs
- Change Camera class for Duckman

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/fontresource.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_bbdou.h
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/input.cpp
    engines/illusions/screen.cpp
    engines/illusions/screentext.cpp
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptopcodes_duckman.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/talkthread_duckman.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index f3677e4..a67f5b9 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -616,7 +616,7 @@ void Control::sequenceActor() {
 	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
 		bool breakInner = false;
 		while (!breakInner) {
-			debug(1, "SEQ[%08X] op: %08X", _actor->_sequenceId, _actor->_seqCodeIp[0]);
+			debug(1, "[%08X] SEQ[%08X] op: %08X", _objectId, _actor->_sequenceId, _actor->_seqCodeIp[0]);
 			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
 			opCall._opSize = _actor->_seqCodeIp[1];
 			opCall._code = _actor->_seqCodeIp + 2;
@@ -652,6 +652,10 @@ void Control::sequenceActor() {
 	
 }
 
+void Control::setActorIndex(int actorIndex) {
+	_actor->_actorIndex = actorIndex;
+}
+
 void Control::setActorIndexTo1() {
 	_actor->_actorIndex = 1;
 }
@@ -762,12 +766,23 @@ PointArray *Control::createPath(Common::Point destPt) {
 
 void Control::updateActorMovement(uint32 deltaTime) {
 	// TODO This needs some cleanup
+	// TODO Move while loop to caller
 
 	static const int16 kAngleTbl[] = {60, 0, 120, 0, 60, 0, 120, 0};
+	bool again = false;
 
 	while (1) {
 
-	bool again = false;
+	if (!again && _vm->testMainActorFastWalk(this)) {
+		again = true;
+		disappearActor();
+		_actor->_flags |= 0x8000;
+		_actor->_seqCodeIp = 0;
+		deltaTime = 2;
+	}
+
+	if (_vm->testMainActorCollision(this))
+		break;
 
 	/* TODO
 	if (controla->objectId == GameScript_getField0() && again == const0 && Input_pollButton__(0x10u)) {
@@ -912,7 +927,6 @@ void Control::refreshSequenceCode() {
 }
 
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
-
 	stopActor();
 
 	_actor->_flags &= ~0x80;
@@ -964,7 +978,8 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 		}
 	}
 
-	sequenceActor();
+	if (_vm->getGameId() == kGameIdBBDOU)
+		sequenceActor();
 
 }
 
@@ -1123,10 +1138,33 @@ void Controls::placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId,
 	subActor->_linkIndex = linkIndex;
 }
 
+void Controls::placeDialogItem(uint16 objectNum, uint32 actorTypeId, uint32 sequenceId, Common::Point placePt, int16 choiceJumpOffs) {
+	Control *control = newControl();
+	Actor *actor = newActor();
+	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
+	control->_flags = 0xC;
+	control->_priority = actorType->_priority;
+	control->_objectId = objectNum | 0x40000;
+	control->readPointsConfig(actorType->_pointsConfig);
+	control->_actorTypeId = actorTypeId;
+	control->_actor = actor;
+	actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, Controls>(this, &Controls::dialogItemControlRoutine));
+	actor->_choiceJumpOffs = choiceJumpOffs;
+	actor->createSurface(actorType->_surfInfo);
+	actor->_position = placePt;
+	actor->_position2 = placePt;
+	actor->_scale = actorType->_scale;
+	actor->_color = actorType->_color;
+	_controls.push_back(control);
+	control->appearActor();
+	control->startSequenceActor(sequenceId, 2, 0);
+	control->setActorIndex(1);
+}
+
 void Controls::destroyControls() {
 	ItemsIterator it = _controls.begin();
 	while (it != _controls.end()) {
-		destroyControl(*it);
+		destroyControlInternal(*it);
 		it = _controls.erase(it);
 	}
 }
@@ -1135,7 +1173,7 @@ void Controls::destroyActiveControls() {
 	ItemsIterator it = _controls.begin();
 	while (it != _controls.end()) {
 		if ((*it)->_pauseCtr <= 0) {
-			destroyControl(*it);
+			destroyControlInternal(*it);
 			it = _controls.erase(it);
 		} else
 			++it;			
@@ -1146,13 +1184,24 @@ void Controls::destroyControlsByTag(uint32 tag) {
 	ItemsIterator it = _controls.begin();
 	while (it != _controls.end()) {
 		if ((*it)->_tag == tag) {
-			destroyControl(*it);
+			destroyControlInternal(*it);
 			it = _controls.erase(it);
 		} else
 			++it;			
 	}
 }
 
+void Controls::destroyDialogItems() {
+	ItemsIterator it = _controls.begin();
+	while (it != _controls.end()) {
+		if (((*it)->_pauseCtr == 0) && ((*it)->_flags & 4)) {
+			destroyControlInternal(*it);
+			it = _controls.erase(it);
+		} else
+			++it;
+	}
+}
+
 void Controls::threadIsDead(uint32 threadId) {
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *control = *it;
@@ -1238,6 +1287,28 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 	return foundControl != 0;
 }
 
+bool Controls::getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl) {
+	Control *foundControl = 0;
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *testControl = *it;
+		if (testControl != control && testControl->_pauseCtr == 0 &&
+			(testControl->_flags & 1) && (testControl->_flags & 4)) {
+			Common::Rect collisionRect;
+			testControl->getCollisionRect(collisionRect);
+			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
+				(!foundControl || foundControl->_priority < testControl->_priority))
+				foundControl = testControl;
+		}
+	}
+	*outOverlappedControl = foundControl;
+	return foundControl != 0;
+}
+
+void Controls::destroyControl(Control *control) {
+	_controls.remove(control);
+	destroyControlInternal(control);
+}
+
 bool Controls::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *control = *it;
@@ -1277,6 +1348,12 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 
 }
 
+void Controls::dialogItemControlRoutine(Control *control, uint32 deltaTime) {
+	Actor *actor = control->_actor;
+	if (actor->_pauseCtr <= 0)
+		actor->_seqCodeValue1 = 100 * deltaTime;
+}
+
 Actor *Controls::newActor() {
 	return new Actor(_vm);
 }
@@ -1296,14 +1373,14 @@ uint32 Controls::newTempObjectId() {
 	return nextTempObjectId2 | 0x40000;
 }
 
-void Controls::destroyControl(Control *control) {
+void Controls::destroyControlInternal(Control *control) {
 
-	if (control->_pauseCtr <= 0)
+	if (!(control->_flags & 4) && control->_pauseCtr <= 0)
 		_vm->_dict->setObjectControl(control->_objectId, 0);
 
-	if (control->_objectId == 0x40004 && control->_pauseCtr <= 0)
+	if (!(control->_flags & 4) && control->_objectId == 0x40004 && control->_pauseCtr <= 0)
 		_vm->setCursorControl(0);
-	
+
 	if (control->_actor) {
 		if (control->_actor->_pathNode && (control->_actor->_flags & 0x400))
 			delete control->_actor->_pathNode;
@@ -1316,6 +1393,7 @@ void Controls::destroyControl(Control *control) {
 		delete control->_actor;
 		control->_actor = 0;
 	}
+
 	delete control;
 }
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index d1740eb..cce5a41 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -146,7 +146,10 @@ public:
 	PointArray *_pathNode;
 	uint _pathPoints;
 	uint32 _walkCallerThreadId1;
-	
+
+	RGB _color;
+	int16 _choiceJumpOffs;
+
 };
 
 class Control {
@@ -182,6 +185,7 @@ public:
 	void stopSequenceActor();
 	void startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId);
 	void sequenceActor();
+	void setActorIndex(int actorIndex);
 	void setActorIndexTo1();
 	void setActorIndexTo2();
 	void startSubSequence(int linkIndex, uint32 sequenceId);
@@ -220,17 +224,22 @@ public:
 	void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
 	void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
 	void placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId);
+	void placeDialogItem(uint16 objectNum, uint32 actorTypeId, uint32 sequenceId, Common::Point placePt, int16 choiceJumpOffs);
 	void destroyControls();
 	void destroyActiveControls();
 	void destroyControlsByTag(uint32 tag);
+	void destroyDialogItems();
 	void threadIsDead(uint32 threadId);
 	void pauseControls();
 	void unpauseControls();
 	void pauseControlsByTag(uint32 tag);
 	void unpauseControlsByTag(uint32 tag);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
+	bool getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl);
+	void destroyControl(Control *control);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 	void actorControlRoutine(Control *control, uint32 deltaTime);
+	void dialogItemControlRoutine(Control *control, uint32 deltaTime);
 public:
 	typedef Common::List<Control*> Items;
 	typedef Items::iterator ItemsIterator;
@@ -241,7 +250,7 @@ public:
 	Actor *newActor();
 	Control *newControl();
 	uint32 newTempObjectId();
-	void destroyControl(Control *control);
+	void destroyControlInternal(Control *control);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 2841971..3016943 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -404,25 +404,6 @@ void BackgroundItem::unpause() {
 	}
 }
 
-void BackgroundItem::refreshPan(WidthHeight &dimensions) {
-	Common::Point screenOffs = _vm->_camera->getScreenOffset();
-	int x = dimensions._width - 640;
-	int y = dimensions._height - 480;
-	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
-		const BgInfo &bgInfo = _bgRes->_bgInfos[i];
-		if (bgInfo._flags & 1) {
-			_panPoints[i] = screenOffs;
-		} else {
-			Common::Point newOffs(0, 0);
-			if (x > 0 && bgInfo._surfInfo._dimensions._width - 640 > 0)
-				newOffs.x = screenOffs.x * (bgInfo._surfInfo._dimensions._width - 640) / x;
-			if (y > 0 && bgInfo._surfInfo._dimensions._height - 480 > 0)
-				newOffs.y = screenOffs.y * (bgInfo._surfInfo._dimensions._height - 480) / y;
-			_panPoints[i] = newOffs;
-		}
-	}
-}
-
 // BackgroundItems
 
 BackgroundItems::BackgroundItems(IllusionsEngine *vm)
@@ -486,7 +467,7 @@ void BackgroundItems::refreshPan() {
 	BackgroundItem *backgroundItem = findActiveBackground();
 	if (backgroundItem) {
 		WidthHeight dimensions = getMasterBgDimensions();
-		backgroundItem->refreshPan(dimensions);
+		_vm->_camera->refreshPan(backgroundItem, dimensions);
 	}
 }
 
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 75c867a..ca6a372 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -159,7 +159,6 @@ public:
 	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 	void pause();
 	void unpause();
-	void refreshPan(WidthHeight &dimensions);
 public:
 	IllusionsEngine *_vm;
 	uint32 _tag;
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index b9d9342..92783a1 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -30,26 +30,27 @@ namespace Illusions {
 
 Camera::Camera(IllusionsEngine *vm)
 	: _vm(vm) {
+	init();
 	_activeState._cameraMode = 6;
 	_activeState._paused = false;
 	_activeState._panStartTime = getCurrentTime();
 	_activeState._panSpeed = 1;
-	_activeState._bounds._topLeft.x = 320;
-	_activeState._bounds._topLeft.y = 240;
-	_activeState._bounds._bottomRight.x = 320;
-	_activeState._bounds._bottomRight.y = 240;
-	_activeState._currPan.x = 320;
-	_activeState._currPan.y = 240;
-	_activeState._panXShl = 320 << 16;
-	_activeState._panYShl = 240 << 16;
-	_activeState._panTargetPoint.x = 320;
+	_activeState._bounds._topLeft.x = _screenMidX;
+	_activeState._bounds._topLeft.y = _screenMidY;
+	_activeState._bounds._bottomRight.x = _screenMidX;
+	_activeState._bounds._bottomRight.y = _screenMidY;
+	_activeState._currPan.x = _screenMidX;
+	_activeState._currPan.y = _screenMidY;
+	_activeState._panXShl = _screenMidX << 16;
+	_activeState._panYShl = _screenMidY << 16;
+	_activeState._panTargetPoint.x = _screenMidX;
 	_activeState._panTargetPoint.y = 240;
 	_activeState._panToPositionPtr = 0;
 	_activeState._panNotifyId = 0;
 	_activeState._trackingLimits.x = 0;
 	_activeState._trackingLimits.y = 0;
-	_activeState._centerPt.x = 320;
-	_activeState._centerPt.y = 240;
+	_activeState._centerPt.x = _screenMidX;
+	_activeState._centerPt.y = _screenMidY;
 	_activeState._pointFlags = 0;
 }
 
@@ -62,10 +63,10 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._paused = false;
 	_activeState._panStartTime = getCurrentTime();
 	_activeState._panSpeed = 1;
-	_activeState._bounds._topLeft.x = 320;
-	_activeState._bounds._topLeft.y = 240;
-	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - 640) + 320;
-	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - 480) + 240;
+	_activeState._bounds._topLeft.x = _screenMidX;
+	_activeState._bounds._topLeft.y = _screenMidY;
+	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - _screenWidth) + _screenMidX;
+	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - _screenHeight) + _screenMidY;
 	_activeState._panTargetPoint = panPoint;
 	clipPanTargetPoint();
 	_activeState._currPan = _activeState._panTargetPoint;
@@ -78,21 +79,26 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._trackingLimits.x = 0;
 	_activeState._trackingLimits.y = 0;
 	_activeState._pointFlags = 0;
-	_activeState._centerPt.x = 320;
-	_activeState._centerPt.y = 240;
+	_activeState._centerPt.x = _screenMidX;
+	_activeState._centerPt.y = _screenMidY;
 }
 
 void Camera::panCenterObject(uint32 objectId, int16 panSpeed) {
 	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
-	_activeState._cameraMode = 1;
+	if (_vm->getGameId() == kGameIdDuckman && objectId == 0x40004) {
+		_activeState._cameraMode = 2;
+		_activeState._trackingLimits.x = 156;
+		_activeState._trackingLimits.y = 96;
+	} else if (_vm->getGameId() == kGameIdBBDOU) {
+		_activeState._cameraMode = 1;
+		_activeState._trackingLimits = _centerObjectTrackingLimits;
+	}
 	_activeState._panSpeed = panSpeed;
-	_activeState._trackingLimits.x = 8;
-	_activeState._trackingLimits.y = 8;
 	_activeState._pointFlags = 0;
-	_activeState._panToPositionPtr = actorPosition;
 	_activeState._panObjectId = objectId;
-	_activeState._panTargetPoint = *actorPosition;
 	_activeState._panNotifyId = 0;
+	_activeState._panToPositionPtr = actorPosition;
+	_activeState._panTargetPoint = *actorPosition;
 	clipPanTargetPoint();
 	_activeState._panStartTime = getCurrentTime();
 	recalcPan(_activeState._panStartTime);
@@ -102,10 +108,9 @@ void Camera::panTrackObject(uint32 objectId) {
 	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
 	_activeState._cameraMode = 3;
 	_activeState._panObjectId = objectId;
-	_activeState._trackingLimits.x = 160;
-	_activeState._trackingLimits.y = 120;
+	_activeState._trackingLimits = _trackObjectTrackingLimits;
+	_activeState._panSpeed = _trackObjectTrackingLimitsPanSpeed;
 	_activeState._pointFlags = 0;
-	_activeState._panSpeed = 710;
 	_activeState._panToPositionPtr = actorPosition;
 	_activeState._panNotifyId = 0;
 	_activeState._panTargetPoint = *actorPosition;
@@ -286,10 +291,10 @@ void Camera::setBounds(Common::Point minPt, Common::Point maxPt) {
 
 void Camera::setBoundsToDimensions(WidthHeight &dimensions) {
 	// NOTE For the caller dimensions = artdispGetMasterBGDimensions();
-	_activeState._bounds._topLeft.x = 320;
-	_activeState._bounds._topLeft.y = 240;
-	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - 640) + 320;
-	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - 480) + 240;
+	_activeState._bounds._topLeft.x = _screenMidX;
+	_activeState._bounds._topLeft.y = _screenMidY;
+	_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - _screenWidth) + _screenMidX;
+	_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - _screenHeight) + _screenMidY;
 	clipPanTargetPoint();
 }
 
@@ -299,8 +304,8 @@ Common::Point Camera::getCurrentPan() {
 
 Common::Point Camera::getScreenOffset() {
 	Common::Point screenOffs = getCurrentPan();
-	screenOffs.x -= 320;
-	screenOffs.y -= 240;
+	screenOffs.x -= _screenMidX;
+	screenOffs.y -= _screenMidY;
 	return screenOffs;
 }
 
@@ -331,10 +336,29 @@ void Camera::getActiveState(CameraState &state) {
 	state = _activeState;
 }
 
+void Camera::refreshPan(BackgroundItem *backgroundItem, WidthHeight &dimensions) {
+	Common::Point screenOffs = getScreenOffset();
+	int x = dimensions._width - _screenWidth;
+	int y = dimensions._height - _screenHeight;
+	for (uint i = 0; i < backgroundItem->_bgRes->_bgInfosCount; ++i) {
+		const BgInfo &bgInfo = backgroundItem->_bgRes->_bgInfos[i];
+		if (bgInfo._flags & 1) {
+			backgroundItem->_panPoints[i] = screenOffs;
+		} else {
+			Common::Point newOffs(0, 0);
+			if (x > 0 && bgInfo._surfInfo._dimensions._width - _screenWidth > 0)
+				newOffs.x = screenOffs.x * (bgInfo._surfInfo._dimensions._width - _screenWidth) / x;
+			if (y > 0 && bgInfo._surfInfo._dimensions._height - _screenHeight > 0)
+				newOffs.y = screenOffs.y * (bgInfo._surfInfo._dimensions._height - _screenHeight) / y;
+			backgroundItem->_panPoints[i] = newOffs;
+		}
+	}
+}
+
 void Camera::updateMode1(uint32 currTime) {
 	Common::Point ptOffs = getPtOffset(*_activeState._panToPositionPtr);
-	int deltaX = ptOffs.x - _activeState._currPan.x + 320 - _activeState._centerPt.x;
-	int deltaY = ptOffs.y - _activeState._currPan.y + 240 - _activeState._centerPt.y;
+	int deltaX = ptOffs.x - _activeState._currPan.x + _screenMidX - _activeState._centerPt.x;
+	int deltaY = ptOffs.y - _activeState._currPan.y + _screenMidY - _activeState._centerPt.y;
 	int deltaXAbs = ABS(deltaX);
 	int deltaYAbs = ABS(deltaY);
 
@@ -359,14 +383,15 @@ void Camera::updateMode1(uint32 currTime) {
 }
 
 void Camera::updateMode2(uint32 currTime) {
+	// TOOD CHECKME Bigger differences in Duckman
 	Common::Point panToPosition = *_activeState._panToPositionPtr;
 	uint pointFlags = 0;
 	WRect rect;
 
-	rect._topLeft.x = 320 - _activeState._trackingLimits.x;
-	rect._topLeft.y = 240 - _activeState._trackingLimits.y;
-	rect._bottomRight.x = 320 + _activeState._trackingLimits.x;
-	rect._bottomRight.y = 240 + _activeState._trackingLimits.y;
+	rect._topLeft.x = _screenMidX - _activeState._trackingLimits.x;
+	rect._topLeft.y = _screenMidY - _activeState._trackingLimits.y;
+	rect._bottomRight.x = _screenMidX + _activeState._trackingLimits.x;
+	rect._bottomRight.y = _screenMidY + _activeState._trackingLimits.y;
 
 	if (calcPointFlags(panToPosition, rect, pointFlags)) {
 		if (pointFlags != _activeState._pointFlags) {
@@ -441,8 +466,8 @@ bool Camera::isPanFinished() {
 }
 
 Common::Point Camera::getPtOffset(Common::Point pt) {
-	pt.x = pt.x - _activeState._centerPt.x + 320;
-	pt.y = pt.y - _activeState._centerPt.y + 240;
+	pt.x = pt.x - _activeState._centerPt.x + _screenMidX;
+	pt.y = pt.y - _activeState._centerPt.y + _screenMidY;
 	return pt;
 }
 
@@ -497,4 +522,39 @@ void Camera::clipPanTargetPoint() {
 		_activeState._bounds._topLeft.y, _activeState._bounds._bottomRight.y);
 }
 
+void Camera::init() {
+	switch (_vm->getGameId()) {
+	case kGameIdDuckman:
+		initDuckman();
+		break;
+	case kGameIdBBDOU:
+		initBBDOU();
+		break;
+	}
+}
+
+void Camera::initDuckman() {
+	_centerObjectTrackingLimits.x = 4;
+	_centerObjectTrackingLimits.y = 4;
+	_screenWidth = 320;
+	_screenHeight = 200;
+	_screenMidX = 160;
+	_screenMidY = 100;
+	_trackObjectTrackingLimits.x = 80;
+	_trackObjectTrackingLimits.y = 50;
+	_trackObjectTrackingLimitsPanSpeed = 353;
+}
+
+void Camera::initBBDOU() {
+	_centerObjectTrackingLimits.x = 8;
+	_centerObjectTrackingLimits.y = 8;
+	_screenWidth = 640;
+	_screenHeight = 480;
+	_screenMidX = 320;
+	_screenMidY = 240;
+	_trackObjectTrackingLimits.x = 160;
+	_trackObjectTrackingLimits.y = 120;
+	_trackObjectTrackingLimitsPanSpeed = 710;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index 9fa12c4..85d308d 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -29,6 +29,8 @@
 
 namespace Illusions {
 
+class BackgroundItem;
+
 struct CameraState {
 	int _cameraMode;
 	//field_2 dw
@@ -85,10 +87,18 @@ public:
 	bool isAtPanLimit(int limitNum);
 	void setActiveState(CameraState &state);
 	void getActiveState(CameraState &state);
+	void refreshPan(BackgroundItem *backgroundItem, WidthHeight &dimensions);
 protected:
 	IllusionsEngine *_vm;
 	CameraState _activeState;
 	Common::FixedStack<CameraModeStackItem, 8> _stack;
+
+	int16 _screenWidth, _screenHeight;
+	int16 _screenMidX, _screenMidY;
+	Common::Point _centerObjectTrackingLimits;
+	Common::Point _trackObjectTrackingLimits;
+	int16 _trackObjectTrackingLimitsPanSpeed;
+
 	void updateMode1(uint32 currTime);
 	void updateMode2(uint32 currTime);
 	void updateMode3(uint32 currTime);
@@ -98,6 +108,9 @@ protected:
 	void recalcPan(uint32 currTime);
 	bool calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags);
 	void clipPanTargetPoint();
+	void init();
+	void initDuckman();
+	void initBBDOU();
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
index adfb135..9f7a075 100644
--- a/engines/illusions/fontresource.cpp
+++ b/engines/illusions/fontresource.cpp
@@ -62,7 +62,7 @@ void CharInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_field_2 = stream.readUint16LE();
 	uint32 pixelsOffs = stream.readUint32LE();
 	_pixels = dataStart + pixelsOffs;
-	debug("CharInfo::load() _width: %d; _field_2: %d; pixelsOffs: %08X",
+	debug(2, "CharInfo::load() _width: %d; _field_2: %d; pixelsOffs: %08X",
 		_width, _field_2, pixelsOffs);
 }
 
@@ -78,7 +78,7 @@ void CharRange::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		stream.seek(charInfosOffs + i * 8);
 		_charInfos[i].load(dataStart, stream);
 	}
-	debug("CharRange::load() _firstChar: %d; _lastChar: %d; charInfosOffs: %08X",
+	debug(2, "CharRange::load() _firstChar: %d; _lastChar: %d; charInfosOffs: %08X",
 		_firstChar, _lastChar, charInfosOffs);
 }
 
@@ -116,7 +116,7 @@ void FontResource::load(Resource *resource) {
 		stream.seek(charRangesOffs + i * 8);
 		_charRanges[i].load(data, stream);
 	}
-	debug("FontResource::load() _charHeight: %d; _field_6: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
+	debug(2, "FontResource::load() _charHeight: %d; _field_6: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
 		_charHeight, _field_6, _colorIndex, _lineIncr, _widthC, _charRangesCount);
 }
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 3bfa876..63fb3f6 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -158,6 +158,8 @@ public:
 	virtual void loadSpecialCode(uint32 resId) = 0;
 	virtual void unloadSpecialCode(uint32 resId) = 0;
 	virtual void notifyThreadId(uint32 &threadId) = 0;
+	virtual bool testMainActorFastWalk(Control *control) = 0;
+	virtual bool testMainActorCollision(Control *control) = 0;
 	virtual Control *getObjectControl(uint32 objectId) = 0;
 	virtual Common::Point getNamedPointPosition(uint32 namedPointId) = 0;
 	virtual uint32 getPriorityFromBase(int16 priority) = 0;
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index d5e9713..931db5c 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -32,6 +32,7 @@
 #include "illusions/input.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
+#include "illusions/screentext.h"
 #include "illusions/scriptopcodes_bbdou.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
@@ -201,6 +202,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
 
 	_screen = new Screen(this, 640, 480, 16);
+	_screenText = new ScreenText(this);
 	_input = new Input();	
 	_scriptMan = new ScriptMan(this);
 	_actorItems = new ActorItems(this);
@@ -221,7 +223,6 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_lastUpdateTime = 0;
 
 	_pauseCtr = 0;
-	_doScriptThreadInit = false;
 	_field8 = 1;
 	_fieldA = 0;
 	_fieldE = 240;
@@ -231,6 +232,8 @@ Common::Error IllusionsEngine_BBDOU::run() {
     setDefaultTextCoords();
 	
 	_resSys->loadResource(0x000D0001, 0, 0);
+
+	_doScriptThreadInit = false;
 	startScriptThread(0x00020004, 0, 0, 0, 0);
 	_doScriptThreadInit = true;
 
@@ -258,6 +261,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _actorItems;
 	delete _scriptMan;
 	delete _input;
+	delete _screenText;
 	delete _screen;
 	delete _resSys;
 	delete _dict;
@@ -329,6 +333,15 @@ void IllusionsEngine_BBDOU::notifyThreadId(uint32 &threadId) {
 	}
 }
 
+bool IllusionsEngine_BBDOU::testMainActorFastWalk(Control *control) {
+	return false;
+}
+
+bool IllusionsEngine_BBDOU::testMainActorCollision(Control *control) {
+	// Not used in BBDOU
+	return false;
+}
+
 Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
 	return _dict->getObjectControl(objectId);
 }
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
index 05aea8a..addd06c 100644
--- a/engines/illusions/illusions_bbdou.h
+++ b/engines/illusions/illusions_bbdou.h
@@ -113,6 +113,8 @@ public:
 	void loadSpecialCode(uint32 resId);
 	void unloadSpecialCode(uint32 resId);
 	void notifyThreadId(uint32 &threadId);
+	bool testMainActorFastWalk(Control *control);
+	bool testMainActorCollision(Control *control);
 	Control *getObjectControl(uint32 objectId);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	uint32 getPriorityFromBase(int16 priority);
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 0cd27e8..252483f 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -121,10 +121,10 @@ Common::Error IllusionsEngine_Duckman::run() {
 	
 	_globalSceneId = 0x00010003;
 
-    initSpecialCode();
-    setDefaultTextCoords();
+	initSpecialCode();
+	setDefaultTextCoords();
 	initCursor();
-    initActiveScenes();
+	initActiveScenes();
 
 	_resSys->loadResource(0x120001, 0x00010001, 0);
 	_resSys->loadResource(0x120002, 0x00010001, 0);
@@ -203,6 +203,17 @@ void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
 	}
 }
 
+bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
+	return
+		control->_objectId == _scriptResource->getMainActorObjectId() &&
+		_input->pollButton(0x20);
+}
+
+bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
+	// TODO
+	return false;
+}
+
 Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
 	return _dict->getObjectControl(objectId);
 }
@@ -213,11 +224,11 @@ Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId
 	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt)) {
 		return pt;
 	} else if (namedPointId - 0x00070001 > 209) {
-      if (_controls->findNamedPoint(namedPointId, pt)) {
-      	return pt;
-	  } else {
-	  	return currPan;
-	  }
+		if (_controls->findNamedPoint(namedPointId, pt)) {
+			return pt;
+		} else {
+			return currPan;
+		}
 	} else {
 		// TODO
 		//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
@@ -271,8 +282,8 @@ void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequen
 	_cursor._field14[8] = false;
 	_cursor._op113_choiceOfsPtr = 0;
 	_cursor._notifyThreadId30 = 0;
-	_cursor._op113_objectNumCtr = 0;
-	_cursor._overlappedObjectNum = 0;
+	_cursor._dialogItemsCount = 0;
+	_cursor._overlappedObjectId = 0;
 	_cursor._field40 = 0;
 	control->_flags |= 8;
 	setCursorActorIndex(_cursor._actorIndex, 1, 0);
@@ -320,8 +331,8 @@ void IllusionsEngine_Duckman::initCursor() {
 	_cursor._field14[12] = false;
 	_cursor._op113_choiceOfsPtr = 0;
 	_cursor._notifyThreadId30 = 0;
-	_cursor._op113_objectNumCtr = 0;
-	_cursor._overlappedObjectNum = 0;
+	_cursor._dialogItemsCount = 0;
+	_cursor._overlappedObjectId = 0;
 	_cursor._field40 = 0;
 }
 
@@ -342,7 +353,6 @@ void IllusionsEngine_Duckman::setCursorActorIndex(int actorIndex, int a, int b)
 		{{21, 22}, { 0,  0}}
 	};
 	_cursor._control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
-	debug("_cursor._control->_actor->_actorIndex: %d", _cursor._control->_actor->_actorIndex);
 }
 
 void IllusionsEngine_Duckman::enableCursorVerb(int verbNum) {
@@ -381,6 +391,31 @@ void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
 		setCursorActorIndex(_cursor._actorIndex, 1, 0);
 }
 
+void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 sequenceId) {
+	_cursor._objectId = objectId;
+	_cursor._sequenceId2 = sequenceId;
+	_cursor._actorIndex = 7;
+	_cursor._savedActorIndex = 7;
+	_cursor._field14[_cursor._actorIndex - 1] = true;
+	_cursor._control->startSequenceActor(sequenceId, 2, 0);
+	setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	_cursor._currOverlappedControl = 0;
+}
+
+void IllusionsEngine_Duckman::stopCursorHoldingObject() {
+	_cursor._field14[6] = false;
+	_cursor._objectId = 0;
+	_cursor._sequenceId2 = 0;
+	if (_cursor._actorIndex == 7) {
+		_cursor._actorIndex = getCursorActorIndex();
+		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	}
+}
+
 void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
 	if (control->_actor->_flags & 1) {
@@ -389,7 +424,7 @@ void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 delt
 			updateGameState2();
 			break;
 		case 3:
-			// TODO updateGameState3(cursorControl);
+			updateDialogState();
 			break;
 		case 4:
 			// TODO ShellMgr_update(cursorControl);
@@ -689,7 +724,6 @@ void IllusionsEngine_Duckman::updateGameState2() {
 	} else if (_input->pollButton(2)) {
 		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
 			int newActorIndex = getCursorActorIndex();
-			debug("newActorIndex = %d", newActorIndex);
 			if (newActorIndex != _cursor._actorIndex) {
 				_cursor._actorIndex = newActorIndex;
 				if (_cursor._currOverlappedControl)
@@ -701,9 +735,9 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		}
 	} else if (_input->pollButton(8)) {
 		if (_cursor._field14[0] == 1) {
-			runTriggerCause(1, 0, _scriptResource->getField6C());
+			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
 		} else if (_cursor._field14[1] == 1) {
-			runTriggerCause(2, 0, _scriptResource->getField6C());
+			runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
 		}
 	}
 
@@ -737,16 +771,16 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 
 	bool flag = false;
 	if (_scriptResource->_properties.get(0x000E003C)) {
-		if (verbId == 7 && objectId == 0x40003 ) {
+		if (verbId == 7 && objectId == 0x40003) {
 			playSoundEffect(7);
 			flag = true;
-		} else if (objectId == 0x40003 ) {
+		} else if (objectId == 0x40003) {
 			playSoundEffect(14);
 			flag = true;
-		} else if (verbId == 3 ) {
+		} else if (verbId == 3) {
 			playSoundEffect(16);
 			flag = true;
-		} else if (verbId == 2 ) {
+		} else if (verbId == 2) {
 			flag = true;
 		}
 	}
@@ -754,9 +788,9 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	if (!flag) {
 		if (objectId == 0x40003) {
 			playSoundEffect(14);
-		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getField6C() == objectId) {
+		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) {
 			playSoundEffect(15);
-		} else if (verbId == 7 && _scriptResource->getField6C() == objectId) {
+		} else if (verbId == 7 && _scriptResource->getMainActorObjectId() == objectId) {
 			playSoundEffect(15);
 		} else if (verbId == 1) {
 			playSoundEffect(1);
@@ -780,6 +814,116 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	return tempThreadId;
 }
 
+void IllusionsEngine_Duckman::addDialogItem(int16 choiceJumpOffs, uint32 sequenceId) {
+	DialogItem dialogItem;
+	dialogItem._choiceJumpOffs = choiceJumpOffs;
+	dialogItem._sequenceId = sequenceId;
+	_dialogItems.push_back(dialogItem);
+}
+
+void IllusionsEngine_Duckman::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId) {
+	static const uint32 kDialogSequenceIds[] = {
+		0,
+		0x6049C, 0x6049C, 0x6047A, 0x6049D,
+		0x60479, 0x6049E, 0x6049F, 0x60468
+	};
+	if (_dialogItems.size() == 1) {
+		*choiceOfsPtr = _dialogItems[0]._choiceJumpOffs;
+		notifyThreadId(callerThreadId);
+	} else {
+		if (!_cursor._control) {
+			Common::Point pos = getNamedPointPosition(0x70001);
+			_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
+			_cursor._control = _dict->getObjectControl(0x40004);
+		}
+		_cursor._control->appearActor();
+		setCursorActorIndex(6, 1, 0);
+
+		_cursor._gameState = 3;
+		_cursor._notifyThreadId30 = callerThreadId;
+		_cursor._dialogItemsCount = 0;
+		_cursor._overlappedObjectId = 0;
+		_cursor._op113_choiceOfsPtr = choiceOfsPtr;
+		_cursor._currOverlappedControl = 0;
+
+		/* TODO?
+		if (!_input->getCursorMouseMode())
+			_input->setMousePos((Point)0xBC0014);
+		*/
+
+		_cursor._dialogItemsCount = _dialogItems.size();
+		Common::Point placePt(20, 188);
+
+		for (uint i = 1; i <= _dialogItems.size(); ++i) {
+			DialogItem &dialogItem = _dialogItems[_dialogItems.size() - i];
+			_controls->placeDialogItem(i + 1, actorTypeId, dialogItem._sequenceId, placePt, dialogItem._choiceJumpOffs);
+			placePt.x += 40;
+		}
+
+		Common::Point placePt2 = getNamedPointPosition(0x700C3);
+		_controls->placeActor(0x5006E, placePt2, kDialogSequenceIds[_dialogItems.size()], 0x40148, 0);
+		Control *control = _dict->getObjectControl(0x40148);
+		control->_flags |= 8;
+		playSoundEffect(8);
+	}
+
+	_dialogItems.clear();
+
+}
+
+void IllusionsEngine_Duckman::updateDialogState() {
+	Common::Point mousePos = _input->getCursorPosition();
+	// TODO Handle keyboard input
+	_cursor._control->_actor->_position = mousePos;
+	mousePos = convertMousePos(mousePos);
+
+	Control *currOverlappedControl = _cursor._currOverlappedControl;
+	Control *newOverlappedControl;
+	
+	if (_controls->getDialogItemAtPos(_cursor._control, mousePos, &newOverlappedControl)) {
+		if (currOverlappedControl != newOverlappedControl) {
+			newOverlappedControl->setActorIndex(2);
+			newOverlappedControl->startSequenceActor(newOverlappedControl->_actor->_sequenceId, 2, 0);
+			if (currOverlappedControl) {
+				currOverlappedControl->setActorIndex(1);
+				currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
+			}
+			playSoundEffect(10);
+			startCursorSequence();
+			setCursorActorIndex(6, 2, 0);
+			_cursor._currOverlappedControl = newOverlappedControl;
+			_cursor._overlappedObjectId = newOverlappedControl->_objectId;
+		}
+	} else if (currOverlappedControl) {
+		currOverlappedControl->setActorIndex(1);
+		currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
+		playSoundEffect(10);
+		_cursor._currOverlappedControl = 0;
+		_cursor._overlappedObjectId = 0;
+		startCursorSequence();
+		setCursorActorIndex(6, 1, 0);
+	}
+
+	if (_input->pollButton(1)) {
+		if (_cursor._currOverlappedControl) {
+			playSoundEffect(9);
+			*_cursor._op113_choiceOfsPtr = _cursor._currOverlappedControl->_actor->_choiceJumpOffs;
+			_controls->destroyDialogItems();
+			Control *control = _dict->getObjectControl(0x40148);
+			_controls->destroyControl(control);
+			debug("_cursor._notifyThreadId30: %08X", _cursor._notifyThreadId30);
+			notifyThreadId(_cursor._notifyThreadId30);
+			_cursor._notifyThreadId30 = 0;
+			_cursor._gameState = 2;
+			_cursor._dialogItemsCount = 0;
+			_cursor._overlappedObjectId = 0;
+			_cursor._op113_choiceOfsPtr = 0;
+			_cursor._control->disappearActor();
+		}
+	}
+
+}
+
 // Special code
 
 typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 375407f..7213e21 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -45,12 +45,17 @@ struct Cursor_Duckman {
 	uint32 _sequenceId2;
 	uint32 _notifyThreadId30;
 	int16 *_op113_choiceOfsPtr;
-	int _op113_objectNumCtr;
-	uint _overlappedObjectNum;
+	int _dialogItemsCount;
+	uint32 _overlappedObjectId;
 	uint32 _field3C;
 	uint32 _field40;
 };
 
+struct DialogItem {
+	int16 _choiceJumpOffs;
+	uint32 _sequenceId;
+};
+
 struct OpCall;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
@@ -81,14 +86,17 @@ public:
 	uint32 _activeScenes[6];
 
 	Cursor_Duckman _cursor;
+	Common::Array<DialogItem> _dialogItems;
 
-    SpecialCodeMap _specialCodeMap;
+	SpecialCodeMap _specialCodeMap;
 
 	void setDefaultTextCoords();
 
 	void loadSpecialCode(uint32 resId);
 	void unloadSpecialCode(uint32 resId);
 	void notifyThreadId(uint32 &threadId);
+	bool testMainActorFastWalk(Control *control);
+	bool testMainActorCollision(Control *control);
 	Control *getObjectControl(uint32 objectId);
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	uint32 getPriorityFromBase(int16 priority);
@@ -106,6 +114,8 @@ public:
 	void enableCursorVerb(int verbNum);
 	void disableCursorVerb(int verbNum);
 	void setCursorHandMode(int mode);
+	void startCursorHoldingObject(uint32 objectId, uint32 sequenceId);
+	void stopCursorHoldingObject();
 	void cursorControlRoutine(Control *control, uint32 deltaTime);
 
 	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
@@ -122,9 +132,9 @@ public:
 	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
 	uint32 newTempThreadId();
 
-    void initActiveScenes();
+	void initActiveScenes();
 	void pushActiveScene(uint32 sceneId);
-    void popActiveScene();
+	void popActiveScene();
 	bool loadScene(uint32 sceneId);
 	bool enterScene(uint32 sceneId, uint32 threadId);
 	void exitScene();
@@ -148,6 +158,10 @@ public:
 	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
 	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
 
+	void addDialogItem(int16 choiceJumpOffs, uint32 sequenceId);
+	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
+	void updateDialogState();
+
 	// Special code
 	void initSpecialCode();
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 1a174b1..53b3291 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -119,6 +119,7 @@ Common::Point Input::getCursorDelta() {
 
 void Input::initKeys() {
 	// NOTE Skipped debugging keys of the original engine, not sure if used
+	// TODO Move this to the engine class and tidy up methods (one for mouse buttons, one for keys)
 	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON0, 0x01);
 	addKeyMapping(Common::KEYCODE_RETURN, MOUSE_NONE, 0x01);
 	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x02);
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index f4b6982..57989e7 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -93,7 +93,7 @@ bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 		return true;
 
 	_screen->drawSurface(dstRect, item->_surface, srcRect, item->_scale, item->_flags);
-	
+
 	if (item->_drawFlags)
 		*item->_drawFlags &= ~4;
 
@@ -441,14 +441,11 @@ void Screen::decompressSprite8(SpriteDecompressQueueItem *item) {
 }
 
 void Screen::drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
-	drawSurface81(dstRect.left, dstRect.top, surface, srcRect);
-	/*
 	if (scale == 100) {
 		drawSurface81(dstRect.left, dstRect.top, surface, srcRect);
 	} else {
 		drawSurface82(dstRect, surface, srcRect);
 	}
-	*/
 }
 
 void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 684ab5a..60fc901 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -110,7 +110,7 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
 		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
 		outTextPtr);
-	_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
+	//_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 
 	uint16 *textPart = screenText->_text;
 	while (text != outTextPtr)
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index f7e9676..63cdf65 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -71,7 +71,9 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
 	OPCODE(25, opLeaveScene24);
+	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
+	OPCODE(36, opPanToPoint);
 	OPCODE(38, opStartFade);
 	OPCODE(39, opSetDisplay);
 	OPCODE(40, opSetCameraBounds);
@@ -80,6 +82,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(50, opFaceActor);
 	OPCODE(51, opFaceActorToObject);
 	OPCODE(52, opStartSequenceActor);
+	OPCODE(53, opStartSequenceActorAtPosition);
 	OPCODE(54, opStartMoveActor);
 	OPCODE(55, opStartMoveActorToObject);
 	OPCODE(56, opStartTalkThread);
@@ -88,6 +91,8 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(59, opActivateObject);
 	OPCODE(60, opDeactivateObject);
 	OPCODE(61, opSetDefaultSequence);
+	OPCODE(64, opStopCursorHoldingObject);
+	OPCODE(65, opStartCursorHoldingObject);
 	OPCODE(66, opPlayVideo);
 	OPCODE(69, opRunSpecialCode);
 	OPCODE(72, opStartSound);
@@ -101,13 +106,23 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	OPCODE(96, opIncBlockCounter);
+	OPCODE(97, opClearBlockCounter);
 	OPCODE(104, opJumpIf);
+	OPCODE(105, opIsPrevSceneId);
 	OPCODE(106, opNot);
 	OPCODE(107, opAnd);
 	OPCODE(108, opOr);
 	OPCODE(109, opGetProperty);
 	OPCODE(110, opCompareBlockCounter);
+	OPCODE(112, opAddDialogItem);
+	OPCODE(113, opStartDialog);
+	OPCODE(114, opJumpToDialogChoice);
+	OPCODE(115, opSetBlockCounter115);
+	OPCODE(116, opSetBlockCounter116);
+	OPCODE(117, opSetBlockCounter117);
+	OPCODE(118, opSetBlockCounter118);
 	OPCODE(126, opDebug126);
+	OPCODE(127, opDebug127);
 #if 0		
 	// Register opcodes
 	OPCODE(8, opStartTempScriptThread);
@@ -118,9 +133,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(31, opExitCloseUpScene);
 	OPCODE(32, opPanCenterObject);
 	OPCODE(35, opPanToNamedPoint);
-	OPCODE(36, opPanToPoint);
 	OPCODE(37, opPanStop);
-	OPCODE(43, opClearBlockCounter);
 	OPCODE(53, opSetActorToNamedPoint);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
@@ -129,7 +142,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(67, opSetAdjustDnSfx);
 	OPCODE(78, opStackPushRandom);
 	OPCODE(79, opIfLte);
-	OPCODE(104, opIsPrevSceneId);
 	OPCODE(105, opIsCurrentSceneId);
 	OPCODE(106, opIsActiveSceneId);
 	OPCODE(146, opStackPop);
@@ -232,9 +244,14 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 }
 
 //static uint dsceneId = 0, dthreadId = 0;
-static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
+//static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
+//static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
+//static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
+//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Pizza
+//static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
+static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -242,6 +259,8 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	ARG_UINT32(threadId);
 	_vm->_input->discardButtons(0xFFFF);
 	
+	debug("changeScene(%08X, %08X)", sceneId, threadId);
+	
 	//DEBUG
 	if (dsceneId) {
 		sceneId = dsceneId;
@@ -273,10 +292,10 @@ void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall
 		// TODO _vm->startScriptThread2(0x10002, 0x20001, 0);
 		opCall._result = kTSTerminate;
 	} else {
-          _vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
-          _vm->exitScene();
-          _vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
-          _vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+		_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
+		_vm->exitScene();
+		_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
+		_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
 	}
 }
 
@@ -295,6 +314,12 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o
 	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
 }
 
+void ScriptOpcodes_Duckman::opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	_vm->_camera->panTrackObject(objectId);
+}
+
 void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);
 	ARG_UINT32(objectId);
@@ -303,6 +328,13 @@ void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &op
 	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_INT16(x);
+	ARG_INT16(y);
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(arg1);
 	ARG_INT16(arg2);
@@ -321,6 +353,7 @@ void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opC
 }
 
 void ScriptOpcodes_Duckman::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
 	ARG_INT16(x1);
 	ARG_INT16(y1);
 	ARG_INT16(x2);
@@ -353,8 +386,8 @@ void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCa
 
 void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
-	ARG_UINT32(objectId1);
 	ARG_UINT32(objectId2);
+	ARG_UINT32(objectId1);
 	Control *control1 = _vm->_dict->getObjectControl(objectId1);
 	Control *control2 = _vm->_dict->getObjectControl(objectId2);
 	Common::Point pos1 = control1->getActorPosition();
@@ -373,6 +406,17 @@ void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpC
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorPosition(pos);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -456,6 +500,22 @@ void ScriptOpcodes_Duckman::opSetDefaultSequence(ScriptThread *scriptThread, OpC
 	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
 }
 
+void ScriptOpcodes_Duckman::opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flags);
+	_vm->stopCursorHoldingObject();
+	if (!(flags & 1))
+		_vm->playSoundEffect(7);
+}
+
+void ScriptOpcodes_Duckman::opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flags);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	_vm->startCursorHoldingObject(objectId, sequenceId);
+	if (!(flags & 1))
+		_vm->playSoundEffect(6);
+}
+
 void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -550,6 +610,11 @@ void ScriptOpcodes_Duckman::opIncBlockCounter(ScriptThread *scriptThread, OpCall
 		_vm->_scriptResource->_blockCounters.set(index, value);
 }
 
+void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.set(index, 0);
+}
+
 void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(jumpOffs);
 	int16 value = _vm->_stack->pop();
@@ -557,6 +622,12 @@ void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall)
 		opCall._deltaOfs += jumpOffs;
 }
 
+void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
+}
+
 void ScriptOpcodes_Duckman::opNot(ScriptThread *scriptThread, OpCall &opCall) {
 	int16 value = _vm->_stack->pop();
 	_vm->_stack->push(value != 0 ? 0 : 1);
@@ -610,9 +681,56 @@ void ScriptOpcodes_Duckman::opCompareBlockCounter(ScriptThread *scriptThread, Op
 	_vm->_stack->push(compareResult ? 1 : 0);
 }
 
+void ScriptOpcodes_Duckman::opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(index);
+	ARG_INT16(choiceJumpOffs);
+	ARG_UINT32(sequenceId);
+	if (index && (_vm->_scriptResource->_blockCounters.getC0(index) & 0x40))
+		_vm->addDialogItem(choiceJumpOffs, sequenceId);
+}
+
+void ScriptOpcodes_Duckman::opStartDialog(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(actorTypeId);
+	_vm->startDialog(&_vm->_menuChoiceOfs, actorTypeId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_Duckman::opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._deltaOfs += _vm->_menuChoiceOfs;
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	if (_vm->_scriptResource->_blockCounters.getC0(index) & 0x80)
+		_vm->_scriptResource->_blockCounters.set(index, 0);
+	_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	if (!(_vm->_scriptResource->_blockCounters.getC0(index) & 0x80))
+		_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.setC0(index, 0);
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
+}
+
 void ScriptOpcodes_Duckman::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Prints some debug text
-	debug(1, "[DBG] %s", (char*)opCall._code);
+	debug(1, "[DBG126] %s", (char*)opCall._code);
+}
+
+void ScriptOpcodes_Duckman::opDebug127(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG127] %s", (char*)opCall._code);
 }
 
 #if 0
@@ -674,22 +792,10 @@ void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall
 	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
-void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_INT16(x);	
-	ARG_INT16(y);	
-	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
-}
-
 void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_camera->stopPan();
 }
 
-void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	_vm->_scriptResource->_blockCounters.set(index, 0);
-}
-
 void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -744,12 +850,6 @@ void ScriptOpcodes_Duckman::opIfLte(ScriptThread *scriptThread, OpCall &opCall)
 		opCall._deltaOfs += elseJumpOffs;
 }
 
-void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
-}
-
 void ScriptOpcodes_Duckman::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
index 2a30c58..124564e 100644
--- a/engines/illusions/scriptopcodes_duckman.h
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -58,7 +58,9 @@ protected:
 	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
@@ -67,6 +69,7 @@ protected:
 	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
@@ -75,6 +78,8 @@ protected:
 	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
@@ -88,13 +93,23 @@ protected:
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opNot(ScriptThread *scriptThread, OpCall &opCall);
 	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
 	void opOr(ScriptThread *scriptThread, OpCall &opCall);
 	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartDialog(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
 	
 #if 0	
 	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
@@ -105,9 +120,7 @@ protected:
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
-	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
@@ -116,7 +129,6 @@ protected:
 	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
 	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 90658ca..2054d4d 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -113,6 +113,17 @@ void BlockCounters::set(uint index, byte value) {
 	_blockCounters[index - 1] ^= (_blockCounters[index - 1] ^ value) & 0x3F;
 }
 
+byte BlockCounters::getC0(uint index) {
+	return _blockCounters[index - 1] & 0xC0;
+}
+
+void BlockCounters::setC0(uint index, byte value) {
+	byte oldValue = _blockCounters[index - 1] & 0x3F;
+	if (value & 0x80)
+		value = value & 0xBF;
+	_blockCounters[index - 1] = oldValue | (value & 0xC0);
+}
+
 // TriggerCause
 
 void TriggerCause::load(Common::SeekableReadStream &stream) {
@@ -146,11 +157,20 @@ void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
 }
 
 bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
-	for (uint i = 0; i < _causesCount; ++i)
-		if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
-			codeOffs = _causes[i]._codeOffs;
-			return true;
-		}
+	if ((verbId & 0xFFFF0000) == 0) {
+		for (uint i = 0; i < _causesCount; ++i)
+			if ((verbId == 7 && ((_causes[i]._verbId == 7 && _causes[i]._objectId2 == objectId2) || _causes[i]._verbId == 8)) ||
+				verbId == _causes[i]._verbId) {
+				codeOffs = _causes[i]._codeOffs;
+				return true;
+			}
+	} else {
+		for (uint i = 0; i < _causesCount; ++i)
+			if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
+				codeOffs = _causes[i]._codeOffs;
+				return true;
+			}
+	}
 	return false;
 }
 
@@ -311,8 +331,13 @@ void ScriptResource::load(Resource *resource) {
 		}
 	}
 	
-	stream.seek(0x6C);
-	_field6C = stream.readUint32LE();
+	if (resource->_gameId == kGameIdDuckman) {
+		stream.seek(0x6C);
+		_mainActorObjectId = stream.readUint32LE();
+	} else if (resource->_gameId == kGameIdBBDOU) {
+		stream.seek(0);
+		_mainActorObjectId = stream.readUint32LE();
+	}
 	
 	if (resource->_gameId == kGameIdDuckman)
 		fixupProgInfosDuckman();
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 6452c7c..35c3ed0 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -61,6 +61,8 @@ public:
 	void clear();
 	byte get(uint index);
 	void set(uint index, byte value);
+	byte getC0(uint index);
+	void setC0(uint index, byte value);
 public:
 	uint _count;
 	byte *_blockCounters;
@@ -114,7 +116,7 @@ public:
 	byte *getCode(uint32 codeOffs);
 	ProgInfo *getProgInfo(uint32 index);
 	uint32 getObjectActorTypeId(uint32 objectId);
-	uint32 getField6C() const { return _field6C; }
+	uint32 getMainActorObjectId() const { return _mainActorObjectId; }
 public:
 	byte *_data;
 	uint32 _dataSize;
@@ -128,7 +130,7 @@ public:
 	uint32 _soundIds[27];
 	uint _objectMapCount;
 	uint32 *_objectMap;
-	uint32 _field6C;
+	uint32 _mainActorObjectId;
 	void fixupProgInfosDuckman();
 };
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index e00bb23..83476ff 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -75,9 +75,11 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(18, opAppearForeignActor);
 	OPCODE(19, opDisappearForeignActor);
 	OPCODE(21, opMoveDelta);
+	OPCODE(25, opFaceActor);
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
 	OPCODE(33, opSetPathWalkPoints);
+	OPCODE(34, opDisableAutoScale);
 	OPCODE(35, opSetScale);
 	OPCODE(36, opSetScaleLayer);
 	OPCODE(37, opDeactivatePathWalkRects);
@@ -253,6 +255,11 @@ void SequenceOpcodes::opMoveDelta(Control *control, OpCall &opCall) {
 	control->_actor->_position.y += deltaY;
 }
 
+void SequenceOpcodes::opFaceActor(Control *control, OpCall &opCall) {
+	ARG_INT16(facing);
+	control->_actor->_facing = facing;
+}
+
 void SequenceOpcodes::opNotifyThreadId1(Control *control, OpCall &opCall) {
 	_vm->notifyThreadId(control->_actor->_notifyThreadId1);
 }
@@ -269,6 +276,11 @@ void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	// TODO control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
 }
 
+void SequenceOpcodes::opDisableAutoScale(Control *control, OpCall &opCall) {
+	// Keep current scale but don't autoscale
+	control->_actor->_flags &= ~4;
+}
+
 void SequenceOpcodes::opSetScale(Control *control, OpCall &opCall) {
 	ARG_INT16(scale);
 	control->_actor->_flags &= ~4;
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 3aab3a8..db15658 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -64,9 +64,11 @@ protected:
 	void opAppearForeignActor(Control *control, OpCall &opCall);
 	void opDisappearForeignActor(Control *control, OpCall &opCall);
 	void opMoveDelta(Control *control, OpCall &opCall);
+	void opFaceActor(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
 	void opSetPathWalkPoints(Control *control, OpCall &opCall);
+	void opDisableAutoScale(Control *control, OpCall &opCall);
 	void opSetScale(Control *control, OpCall &opCall);
 	void opSetScaleLayer(Control *control, OpCall &opCall);
 	void opDeactivatePathWalkRects(Control *control, OpCall &opCall);
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
index fe666a3..8c29071 100644
--- a/engines/illusions/talkthread_duckman.cpp
+++ b/engines/illusions/talkthread_duckman.cpp
@@ -240,10 +240,9 @@ void TalkThread_Duckman::onKill() {
 }
 
 uint32 TalkThread_Duckman::sendMessage(int msgNum, uint32 msgValue) {
-	// TODO
 	switch (msgNum) {
 	case kMsgQueryTalkThreadActive:
-		if (_status != 1 && _status != 2)
+		if (_status != 1)
 			return 1;
 		break;
 	case kMsgClearSequenceId1:


Commit: e131362590a768240454cc2b27f4548ba481ebfb
    https://github.com/scummvm/scummvm/commit/e131362590a768240454cc2b27f4548ba481ebfb
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on Duckman

- Implement inventory
- Ad more script and sequence opcodes
- Adjust existing code

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptopcodes_duckman.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index a67f5b9..83fd748 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -638,7 +638,8 @@ void Control::sequenceActor() {
 	if (_actor->_newFrameIndex != 0) {
 		debug(1, "New frame %d", _actor->_newFrameIndex);
 		setActorFrameIndex(_actor->_newFrameIndex);
-		if (!(_actor->_flags & 1) && (_actor->_flags & 0x1000) && (_objectId != 0x40004)) {
+		if (_vm->getGameId() == kGameIdBBDOU &&
+			!(_actor->_flags & 1) && (_actor->_flags & 0x1000) && (_objectId != 0x40004)) {
 			appearActor();
 			_actor->_flags &= ~0x1000;
 		}
@@ -752,7 +753,13 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 		_vm->notifyThreadId(_actor->_notifyId3C);
 		_actor->_notifyId3C = callerThreadId2;
 		_actor->_pathPointIndex = 0;
-		_vm->_input->discardButtons(0x10);
+
+		if (_vm->getGameId() == kGameIdBBDOU) {
+			_vm->_input->discardButtons(0x10);
+		} else if (_vm->getGameId() == kGameIdDuckman) {
+			_vm->_input->discardButtons(0x20);
+		}
+
 	}
 
 }
@@ -1071,11 +1078,16 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	_controls.push_back(control);
 	_vm->_dict->setObjectControl(objectId, control);
 
+    if (_vm->getGameId() == kGameIdDuckman) {
+		control->appearActor();
+    } else if (_vm->getGameId() == kGameIdBBDOU) {
+		control->_flags |= 0x01;
+		actor->_flags |= 0x1000;
+    }
+
 	if (_vm->isCursorObject(actorTypeId, objectId))
 		_vm->placeCursorControl(control, sequenceId);
 
-	control->_flags |= 0x01;
-	actor->_flags |= 0x1000;
 	control->startSequenceActor(sequenceId, 2, notifyThreadId);
 }
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index f4ba0cd..8a35096 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -177,6 +177,8 @@ int IllusionsEngine::updateGraphics() {
 			*/
 			if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
 				uint32 priority = control->getPriority();
+//if (control->_objectId == 0x0004001B) continue;
+//debug("objectId: %08X; priority: %d (%d)", control->_objectId, priority, control->_priority);				
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
 					actor->_surfInfo._dimensions, drawPosition, control->_position,
 					priority, actor->_scale, actor->_spriteFlags);
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 252483f..e36cd35 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -121,6 +121,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	
 	_globalSceneId = 0x00010003;
 
+	initInventory();
 	initSpecialCode();
 	setDefaultTextCoords();
 	initCursor();
@@ -391,6 +392,39 @@ void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
 		setCursorActorIndex(_cursor._actorIndex, 1, 0);
 }
 
+void IllusionsEngine_Duckman::setCursorInventoryMode(int mode, int value) {
+	_cursor._control = _cursor._control;
+	if (mode == 1) {
+		_savedInventoryActorIndex = _cursor._actorIndex;
+		if (_cursor._actorIndex == 3 || _cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
+			_cursor._savedActorIndex = _cursor._savedActorIndex;
+			if (_cursor._savedActorIndex == 1 || _cursor._savedActorIndex == 2 || _cursor._savedActorIndex == 7)
+				_savedInventoryActorIndex = _cursor._savedActorIndex;
+			else
+				_savedInventoryActorIndex = 0;
+		}
+		if (value == 1 && _cursor._objectId && _savedInventoryActorIndex != 7) {
+			_cursor._actorIndex = 7;
+			stopCursorHoldingObject();
+			_cursor._actorIndex = _savedInventoryActorIndex;
+		}
+	} else if (mode == 2) {
+		if (_savedInventoryActorIndex)
+			_cursor._actorIndex = _savedInventoryActorIndex;
+		else
+			_cursor._actorIndex = 1;
+		if (_cursor._actorIndex == 7)
+			_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
+		else
+			_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		_savedInventoryActorIndex = 0;
+	}
+}
+
 void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 sequenceId) {
 	_cursor._objectId = objectId;
 	_cursor._sequenceId2 = sequenceId;
@@ -427,7 +461,7 @@ void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 delt
 			updateDialogState();
 			break;
 		case 4:
-			// TODO ShellMgr_update(cursorControl);
+			// TODO ShellMgr_update(_cursor._control);
 			break;
 		}
 	}
@@ -924,6 +958,100 @@ void IllusionsEngine_Duckman::updateDialogState() {
 
 }
 
+void IllusionsEngine_Duckman::initInventory() {
+	_inventorySlots.push_back(DMInventorySlot( 64,  52));
+	_inventorySlots.push_back(DMInventorySlot(112,  52));
+	_inventorySlots.push_back(DMInventorySlot(160,  52));
+	_inventorySlots.push_back(DMInventorySlot(208,  52));
+	_inventorySlots.push_back(DMInventorySlot(255,  52));
+	_inventorySlots.push_back(DMInventorySlot( 64,  84));
+	_inventorySlots.push_back(DMInventorySlot(112,  84));
+	_inventorySlots.push_back(DMInventorySlot(160,  84));
+	_inventorySlots.push_back(DMInventorySlot(208,  84));
+	_inventorySlots.push_back(DMInventorySlot(255,  84));
+	_inventorySlots.push_back(DMInventorySlot( 64, 116));
+	_inventorySlots.push_back(DMInventorySlot(112, 116));
+	_inventorySlots.push_back(DMInventorySlot(160, 116));
+	_inventorySlots.push_back(DMInventorySlot(208, 116));
+	_inventorySlots.push_back(DMInventorySlot(255, 116));
+	_inventorySlots.push_back(DMInventorySlot( 64, 148));
+	_inventorySlots.push_back(DMInventorySlot(112, 148));
+	_inventorySlots.push_back(DMInventorySlot(160, 148));
+	_inventorySlots.push_back(DMInventorySlot(208, 148));
+	_inventorySlots.push_back(DMInventorySlot(255, 148));
+	_inventoyItems.push_back(DMInventoryItem(0x40011, 0xE005B));
+	_inventoyItems.push_back(DMInventoryItem(0x40099, 0xE001B));
+	_inventoyItems.push_back(DMInventoryItem(0x4000F, 0xE000C));
+	_inventoyItems.push_back(DMInventoryItem(0x40042, 0xE0012));
+	_inventoyItems.push_back(DMInventoryItem(0x40044, 0xE000F));
+	_inventoyItems.push_back(DMInventoryItem(0x40029, 0xE000D));
+	_inventoyItems.push_back(DMInventoryItem(0x400A7, 0xE005D));
+	_inventoyItems.push_back(DMInventoryItem(0x40096, 0xE001C));
+	_inventoyItems.push_back(DMInventoryItem(0x40077, 0xE0010));
+	_inventoyItems.push_back(DMInventoryItem(0x4008A, 0xE0033));
+	_inventoyItems.push_back(DMInventoryItem(0x4004B, 0xE0045));
+	_inventoyItems.push_back(DMInventoryItem(0x40054, 0xE0021));
+	_inventoyItems.push_back(DMInventoryItem(0x400C6, 0xE005A));
+	_inventoyItems.push_back(DMInventoryItem(0x4000B, 0xE005E));
+	_inventoyItems.push_back(DMInventoryItem(0x4005F, 0xE0016));
+	_inventoyItems.push_back(DMInventoryItem(0x40072, 0xE0017));
+	_inventoyItems.push_back(DMInventoryItem(0x400AA, 0xE005F));
+	_inventoyItems.push_back(DMInventoryItem(0x400B8, 0xE0050));
+	_inventoyItems.push_back(DMInventoryItem(0x4001F, 0xE001A));
+	_inventoyItems.push_back(DMInventoryItem(0x40095, 0xE0060));
+	_inventoyItems.push_back(DMInventoryItem(0x40041, 0xE0053));
+	_savedInventoryActorIndex = 0;
+}
+
+void IllusionsEngine_Duckman::openInventory() {
+
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
+		DMInventorySlot *inventorySlot = &_inventorySlots[i];
+		if (inventorySlot->_objectId) {
+			DMInventoryItem *inventoryItem = findInventoryItem(inventorySlot->_objectId);
+			if (!_scriptResource->_properties.get(inventoryItem->_propertyId))
+				inventorySlot->_objectId = 0;
+		}
+	}
+
+	for (uint i = 0; i < _inventoyItems.size(); ++i) {
+		DMInventoryItem *inventoryItem = &_inventoyItems[i];
+		if (_scriptResource->_properties.get(inventoryItem->_propertyId)) {
+			DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
+			if (inventorySlot) {
+				Control *control = getObjectControl(inventoryItem->_objectId);
+				control->setActorPosition(inventorySlot->_position);
+				control->appearActor();
+			} else {
+				addInventoryItem(inventoryItem->_objectId);
+			}
+		}
+	}
+
+}
+
+void IllusionsEngine_Duckman::addInventoryItem(uint32 objectId) {
+	DMInventorySlot *DMInventorySlot = findInventorySlot(0);
+	DMInventorySlot->_objectId = objectId;
+	Control *control = getObjectControl(objectId);
+	control->setActorPosition(DMInventorySlot->_position);
+	control->appearActor();
+}
+
+DMInventorySlot *IllusionsEngine_Duckman::findInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]._objectId == objectId)
+			return &_inventorySlots[i];
+	return 0;
+}
+
+DMInventoryItem *IllusionsEngine_Duckman::findInventoryItem(uint32 objectId) {
+	for (uint i = 0; i < _inventoyItems.size(); ++i)
+		if (_inventoyItems[i]._objectId == objectId)
+			return &_inventoyItems[i];
+	return 0;
+}
+
 // Special code
 
 typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
@@ -931,6 +1059,9 @@ typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeF
 
 void IllusionsEngine_Duckman::initSpecialCode() {
 	SPECIAL(0x00160002, spcSetCursorHandMode);
+	SPECIAL(0x00160005, spcOpenInventory);
+	SPECIAL(0x00160014, spcUpdateObject272Sequence);
+	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 }
 
 void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCall) {
@@ -949,4 +1080,58 @@ void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
 	notifyThreadId(opCall._threadId);
 }
 
+void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
+	openInventory();
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcSetCursorInventoryMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	ARG_BYTE(value);
+	setCursorInventoryMode(mode, value);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcUpdateObject272Sequence(OpCall &opCall) {
+	byte flags = 0;
+	uint32 sequenceId;
+	if (_scriptResource->_properties.get(0x000E0085))
+		flags |= 1;
+	if (_scriptResource->_properties.get(0x000E0083))
+		flags |= 2;
+	if (_scriptResource->_properties.get(0x000E0084))
+		flags |= 4;
+	switch (flags) {
+	case 0:
+		sequenceId = 0x603C1;
+		break;
+	case 1:
+		sequenceId = 0x603BF;
+		break;
+	case 2:
+		sequenceId = 0x603C2;
+		break;
+	case 3:
+		sequenceId = 0x603C0;
+		break;
+	case 4:
+		sequenceId = 0x603C3;
+		break;
+	case 5:
+		sequenceId = 0x603C5;
+		break;
+	case 6:
+		sequenceId = 0x603C4;
+		break;
+	case 7:
+		sequenceId = 0x603C6;
+		break;
+	default:
+		sequenceId = 0x603C1;
+		break;
+	}
+	Control *control = getObjectControl(0x40110);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 7213e21..56e6b47 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -56,6 +56,21 @@ struct DialogItem {
 	uint32 _sequenceId;
 };
 
+struct DMInventorySlot {
+	Common::Point _position;
+	uint32 _objectId;
+	DMInventorySlot() : _objectId(0) {}
+	DMInventorySlot(int16 x, int16 y) : _objectId(0), _position(x, y) {}
+};
+
+struct DMInventoryItem {
+	uint32 _objectId;
+	uint32 _propertyId;
+	DMInventoryItem() : _objectId(0) {}
+	DMInventoryItem(uint32 objectId, uint32 propertyId)
+		: _objectId(objectId), _propertyId(propertyId) {}
+};
+
 struct OpCall;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
@@ -86,8 +101,13 @@ public:
 	uint32 _activeScenes[6];
 
 	Cursor_Duckman _cursor;
+
 	Common::Array<DialogItem> _dialogItems;
 
+	int _savedInventoryActorIndex;
+	Common::Array<DMInventorySlot> _inventorySlots;
+	Common::Array<DMInventoryItem> _inventoyItems;
+
 	SpecialCodeMap _specialCodeMap;
 
 	void setDefaultTextCoords();
@@ -114,6 +134,7 @@ public:
 	void enableCursorVerb(int verbNum);
 	void disableCursorVerb(int verbNum);
 	void setCursorHandMode(int mode);
+	void setCursorInventoryMode(int mode, int value);
 	void startCursorHoldingObject(uint32 objectId, uint32 sequenceId);
 	void stopCursorHoldingObject();
 	void cursorControlRoutine(Control *control, uint32 deltaTime);
@@ -162,10 +183,19 @@ public:
 	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
 	void updateDialogState();
 
+	void initInventory();
+	void openInventory();
+	DMInventorySlot *findInventorySlot(uint32 objectId);
+	DMInventoryItem *findInventoryItem(uint32 objectId);
+	void addInventoryItem(uint32 objectId);
+
 	// Special code
 	void initSpecialCode();
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
 	void spcSetCursorHandMode(OpCall &opCall);
+	void spcOpenInventory(OpCall &opCall);
+	void spcSetCursorInventoryMode(OpCall &opCall);
+	void spcUpdateObject272Sequence(OpCall &opCall);
 
 };
 
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 63cdf65..31da692 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -74,6 +74,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
 	OPCODE(36, opPanToPoint);
+	OPCODE(37, opPanStop);
 	OPCODE(38, opStartFade);
 	OPCODE(39, opSetDisplay);
 	OPCODE(40, opSetCameraBounds);
@@ -99,6 +100,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(75, opStopSound);
 	OPCODE(76, opStartMidiMusic);
 	OPCODE(77, opStopMidiMusic);
+	OPCODE(78, opFadeMidiMusic);
 	OPCODE(80, opAddMenuChoice);
 	OPCODE(81, opDisplayMenu);
 	OPCODE(82, opSwitchMenuChoice);
@@ -133,7 +135,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(31, opExitCloseUpScene);
 	OPCODE(32, opPanCenterObject);
 	OPCODE(35, opPanToNamedPoint);
-	OPCODE(37, opPanStop);
 	OPCODE(53, opSetActorToNamedPoint);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
@@ -224,7 +225,6 @@ void ScriptOpcodes_Duckman::opSuspendThread(ScriptThread *scriptThread, OpCall &
 void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
 	uint32 sceneId = _vm->getCurrentScene();
 	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
 	_vm->notifyThreadId(opCall._threadId);
@@ -233,7 +233,6 @@ void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &o
 void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
 	_vm->_resSys->unloadResourceById(resourceId);
 }
 
@@ -243,7 +242,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 	_vm->enterScene(sceneId, 0);
 }
 
-//static uint dsceneId = 0, dthreadId = 0;
+static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
@@ -251,7 +250,8 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Pizza
 //static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
-static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
+//static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
+//static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -335,6 +335,10 @@ void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opC
 	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_camera->stopPan();
+}
+
 void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(arg1);
 	ARG_INT16(arg2);
@@ -401,7 +405,6 @@ void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpC
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
 	ARG_UINT32(sequenceId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
@@ -422,7 +425,6 @@ void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall
 	ARG_UINT32(objectId);
 	ARG_UINT32(sequenceId);
 	ARG_UINT32(namedPointId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
 	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
@@ -557,6 +559,12 @@ void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &
 	// TODO _vm->stopMidiMusic();
 }
 
+void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(duration);
+	ARG_INT16(finalVolume);
+	// TODO _vm->fadeMidiMusic(2, finalVolume, duration, opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_INT16(jumpOffs);
@@ -767,7 +775,6 @@ void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opC
 void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
-	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardButtons(0xFFFF);
 	_vm->enterPause(opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
@@ -792,10 +799,6 @@ void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall
 	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
-void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_camera->stopPan();
-}
-
 void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -933,7 +936,6 @@ void ScriptOpcodes_Duckman::opChangeSceneAll(ScriptThread *scriptThread, OpCall
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
-	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardButtons(0xFFFF);
 	_vm->_prevSceneId = _vm->getCurrentScene();
 	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
index 124564e..be093c4 100644
--- a/engines/illusions/scriptopcodes_duckman.h
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -61,6 +61,7 @@ protected:
 	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
@@ -86,6 +87,7 @@ protected:
 	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
 	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
 	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
@@ -120,7 +122,6 @@ protected:
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 83476ff..7266f8b 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -86,6 +86,8 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(38, opSetPathWalkRects);
 	OPCODE(39, opSetPriority);
 	OPCODE(40, opSetPriorityLayer);
+	OPCODE(41, opDisableAutoRegionLayer);
+	OPCODE(42, opSetRegionLayer);
 	OPCODE(50, opPlaySound);
 	OPCODE(51, opStopSound);
 	OPCODE(52, opStartScriptThread);
@@ -322,6 +324,17 @@ void SequenceOpcodes::opSetPriorityLayer(Control *control, OpCall &opCall) {
 	control->setPriority(priority);
 }
 
+void SequenceOpcodes::opDisableAutoRegionLayer(Control *control, OpCall &opCall) {
+	control->_actor->_flags &= ~0x20;
+}
+
+void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
+	ARG_INT16(regionLayerIndex);
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	control->_actor->_flags |= 0x20;
+//TODO	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
+}
+
 void SequenceOpcodes::opPlaySound(Control *control, OpCall &opCall) {
 	ARG_INT16(flags);
 	ARG_INT16(volume);
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index db15658..f423d07 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -75,6 +75,8 @@ protected:
 	void opSetPathWalkRects(Control *control, OpCall &opCall);
 	void opSetPriority(Control *control, OpCall &opCall);
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
+	void opDisableAutoRegionLayer(Control *control, OpCall &opCall);
+	void opSetRegionLayer(Control *control, OpCall &opCall);
 	void opPlaySound(Control *control, OpCall &opCall);
 	void opStopSound(Control *control, OpCall &opCall);
 	void opStartScriptThread(Control *control, OpCall &opCall);


Commit: 097d130e7dd5de95050e800419f4d07aabb7562b
    https://github.com/scummvm/scummvm/commit/097d130e7dd5de95050e800419f4d07aabb7562b
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement RegionLayer and related code

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 83fd748..030097b 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -71,11 +71,13 @@ Actor::Actor(IllusionsEngine *vm)
 	_frames = 0;
 	_scaleLayer = 0;
 	_priorityLayer = 0;
+	_regionLayer = 0;
 	_position.x = 0;
 	_position.y = 0;
 	_position2.x = 0;
 	_position2.y = 0;
 	_facing = 64;
+	_regionIndex = 0;
 	_fontId = 0;
 	_actorIndex = 0;
 	_parentObjectId = 0;
@@ -1069,7 +1071,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	}
 	
 	if (actorType->_regionLayerIndex) {
-		// TODO actor->_regionLayer = bgRes->getPriorityLayer(actorType->_regionLayerIndex - 1);
+		actor->_regionLayer = bgRes->getRegionLayer(actorType->_regionLayerIndex - 1);
 		actor->_flags |= 0x20;
 	}
 	
@@ -1355,7 +1357,28 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 	}
 
 	if (actor->_flags & 0x20) {
-		// TODO Update transition sequence (seems to be unused in BBDOU?)
+		// Update transition sequence
+		int regionIndex = actor->_regionLayer->getRegionIndex(actor->_position);
+		if (actor->_regionIndex != regionIndex) {
+			if (regionIndex) {
+				uint32 savedSequenceId = actor->_sequenceId;
+				byte *savedSeqCodeIp = actor->_seqCodeIp;
+				int savedSeqCodeValue1 = actor->_seqCodeValue1;
+				int savedSeqCodeValue3 = actor->_seqCodeValue3;
+				uint32 regionSequenceId = actor->_regionLayer->getRegionSequenceId(regionIndex);
+				debug("Running transition sequence %08X", regionSequenceId);
+				Sequence *sequence = _vm->_dict->findSequence(regionSequenceId);
+				actor->_sequenceId = regionSequenceId;
+				actor->_seqCodeIp = sequence->_sequenceCode;
+				actor->_seqCodeValue3 = 0;
+				control->sequenceActor();
+				actor->_sequenceId = savedSequenceId;
+				actor->_seqCodeIp = savedSeqCodeIp;
+				actor->_seqCodeValue3 = savedSeqCodeValue3;
+				actor->_seqCodeValue1 = savedSeqCodeValue1;
+			}
+			actor->_regionIndex = regionIndex;
+		}
 	}
 
 }
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index cce5a41..e71eebc 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -101,6 +101,7 @@ public:
 	
 	ScaleLayer *_scaleLayer;
 	PriorityLayer *_priorityLayer;
+	RegionLayer *_regionLayer;
 	
 	uint _seqStackCount;
 	int16 _seqStack[5];
@@ -108,6 +109,7 @@ public:
 	Common::Point _position;
 	Common::Point _position2;
 	uint _facing;
+	int _regionIndex;
 	
 	uint32 _fontId;
 	int16 _actorIndex;
@@ -129,10 +131,10 @@ public:
 	
 	ActorControlRoutine *_controlRoutine;
 	
-	byte *_seqCodeIp;
 	uint32 _sequenceId;
-	int _seqCodeValue1;
 	int _seqCodeValue2;
+	byte *_seqCodeIp;
+	int _seqCodeValue1;
 	int _seqCodeValue3;
 	
 	int _pathCtrX, _pathCtrY;
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 3016943..f760005 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -23,7 +23,9 @@
 #include "illusions/illusions.h"
 #include "illusions/backgroundresource.h"
 #include "illusions/actor.h"
+#include "illusions/actorresource.h"
 #include "illusions/camera.h"
+#include "illusions/dictionary.h"
 #include "illusions/screen.h"
 #include "common/str.h"
 
@@ -44,11 +46,15 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	backgroundItem->_tag = resource->_tag;
 	backgroundItem->initSurface();
 	
-	// TODO Insert background objects
+	// Insert background objects
 	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
 		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
 
-	// TODO Insert IDs from item48s
+	// Insert region sequences
+	for (uint i = 0; i < backgroundResource->_regionSequencesCount; ++i) {
+		Sequence *sequence = &backgroundResource->_regionSequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	}
 
 	// TODO camera_fadeClear();
 	int index = backgroundItem->_bgRes->findMasterBgIndex();
@@ -66,7 +72,10 @@ void BackgroundResourceLoader::unload(Resource *resource) {
 	// TODO Move to BackgroundItems
 	BackgroundItem *backgroundItem = _vm->_backgroundItems->findBackgroundByResource((BackgroundResource*)resource->_refId);
 	backgroundItem->freeSurface();
-	// TODO Remove IDs from item48s
+	for (uint i = 0; i < backgroundItem->_bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &backgroundItem->_bgRes->_regionSequences[i];
+		_vm->_dict->removeSequence(sequence->_sequenceId);
+	}
 	delete backgroundItem->_bgRes;
 	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
 	_vm->setDefaultTextCoords();
@@ -154,6 +163,39 @@ int ScaleLayer::getScale(Common::Point pos) {
 	return _values[pos.y];
 }
 
+// RegionLayer
+
+void RegionLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_unk = stream.readUint32LE();
+	uint32 regionSequenceIdsOffs = stream.readUint32LE();
+	_width = stream.readUint16LE();
+	_height = stream.readUint16LE();
+	uint32 mapOffs = stream.readUint32LE();
+	uint32 valuesOffs = stream.readUint32LE();
+	_regionSequenceIds = dataStart + regionSequenceIdsOffs;
+	_map = dataStart + mapOffs;
+	_values = dataStart + valuesOffs;
+	_mapWidth = READ_LE_UINT16(_map + 0);
+	_mapHeight = READ_LE_UINT16(_map + 2);
+	_map += 8;
+
+	debug("RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
+		_unk, regionSequenceIdsOffs, _width, _height, mapOffs, valuesOffs);
+}
+
+int RegionLayer::getRegionIndex(Common::Point pos) {
+	pos.x = CLIP<int16>(pos.x, 0, _width - 1);
+	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
+	const int16 tx = pos.x / 32, sx = pos.x % 32;
+	const int16 ty = pos.y / 8, sy = pos.y % 8;
+	uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
+	return _values[mapIndex * 32 * 8 + sx + sy * 32];
+}
+
+uint32 RegionLayer::getRegionSequenceId(int regionIndex) {
+	return READ_LE_UINT32(_regionSequenceIds + 4 * regionIndex);
+}
+
 // Palette
 
 void Palette::load(byte *dataStart, Common::SeekableReadStream &stream) {
@@ -227,6 +269,28 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 		_priorityLayers[i].load(data, stream);
 	}
 
+	// Load region layers
+	stream.seek(0x16);
+	_regionLayersCount = stream.readUint16LE();
+	_regionLayers = new RegionLayer[_regionLayersCount];
+	stream.seek(0x38);
+	uint32 regionLayersOffs = stream.readUint32LE();
+	debug("_regionLayersCount: %d", _regionLayersCount);
+	for (uint i = 0; i < _regionLayersCount; ++i) {
+		stream.seek(regionLayersOffs + i * 20);
+		_regionLayers[i].load(data, stream);
+	}
+
+	// Load region sequences
+	stream.seek(0x1E);
+	_regionSequencesCount = stream.readUint16LE();
+	_regionSequences = new Sequence[_regionSequencesCount];
+	stream.seek(0x48);
+	uint32 regionSequencesOffs = stream.readUint32LE();
+	stream.seek(regionSequencesOffs);
+	for (uint i = 0; i < _regionSequencesCount; ++i)
+		_regionSequences[i].load(data, stream);
+
 	// Load background objects
 	stream.seek(0x1C);
 	_backgroundObjectsCount = stream.readUint16LE();
@@ -276,6 +340,10 @@ ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
 	return &_scaleLayers[index];
 }
 
+RegionLayer *BackgroundResource::getRegionLayer(uint index) {
+	return &_regionLayers[index];
+}
+
 bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	return _namedPoints.findNamedPoint(namedPointId, pt);
 }
@@ -374,10 +442,10 @@ void BackgroundItem::pause() {
 	// TODO
 	++_pauseCtr;
 	if (_pauseCtr <= 1) {
-		/* TODO
-		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
-			krndictRemoveID(_bgRes->_item48s[i].id);
-		*/
+		for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+			Sequence *sequence = &_bgRes->_regionSequences[i];
+			_vm->_dict->removeSequence(sequence->_sequenceId);
+		}
 		_vm->setDefaultTextCoords();
 		_vm->_camera->getActiveState(_savedCameraState);
 		_savedPalette = new byte[1024];
@@ -390,10 +458,10 @@ void BackgroundItem::unpause() {
 	// TODO
 	--_pauseCtr;
 	if (_pauseCtr <= 0) {
-		/* TODO
-		for (uint i = 0; i < _bgRes->_item48sCount; ++i)
-			krndictAddID(_bgRes->_item48s[i].id, _bgRes->_item48s[i]);
-		*/
+		for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+			Sequence *sequence = &_bgRes->_regionSequences[i];
+			_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+		}
 		initSurface();
 		_vm->_screen->setPalette(_savedPalette, 1, 256);
 		delete[] _savedPalette;
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index ca6a372..3a7e1eb 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -39,6 +39,7 @@
 namespace Illusions {
 
 class IllusionsEngine;
+struct Sequence;
 
 class BackgroundResourceLoader : public BaseResourceLoader {
 public:
@@ -89,6 +90,20 @@ protected:
 	byte *_values;
 };
 
+class RegionLayer {
+public:
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	int getRegionIndex(Common::Point pos);
+	uint32 getRegionSequenceId(int regionIndex);
+protected:
+	uint32 _unk;
+	byte *_regionSequenceIds;
+	int16 _width, _height;
+	int16 _mapWidth, _mapHeight;
+	byte *_map, *_values;
+};
+
+
 #if 0
 BgResource_PathWalkRects struc ; (sizeof=0x8)
 count dd ?
@@ -124,6 +139,7 @@ public:
 	int findMasterBgIndex();
 	PriorityLayer *getPriorityLayer(uint index);
 	ScaleLayer *getScaleLayer(uint index);
+	RegionLayer *getRegionLayer(uint index);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 
@@ -137,7 +153,13 @@ public:
 
 	uint _scaleLayersCount;
 	ScaleLayer *_scaleLayers;
-	
+
+	uint _regionLayersCount;
+	RegionLayer *_regionLayers;
+
+	uint _regionSequencesCount;
+	Sequence *_regionSequences;
+
 	uint _backgroundObjectsCount;
 	BackgroundObject *_backgroundObjects;
 	
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 31da692..0312172 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -242,7 +242,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 	_vm->enterScene(sceneId, 0);
 }
 
-static uint dsceneId = 0, dthreadId = 0;
+//static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
@@ -252,6 +252,7 @@ static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
 //static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
+static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 7266f8b..868a5651 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -332,7 +332,7 @@ void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(regionLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
 	control->_actor->_flags |= 0x20;
-//TODO	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
+	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
 }
 
 void SequenceOpcodes::opPlaySound(Control *control, OpCall &opCall) {


Commit: e9a443fcd94bdfa10149596d668dafb660ed60e0
    https://github.com/scummvm/scummvm/commit/e9a443fcd94bdfa10149596d668dafb660ed60e0
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement palette shifting and color shadow table

Changed paths:
    engines/illusions/illusions_duckman.cpp
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptopcodes_duckman.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h


diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index e36cd35..135bd0b 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -232,7 +232,7 @@ Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId
 		}
 	} else {
 		// TODO
-		//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+		debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
 		return Common::Point(0, 0);
 	}
 }
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 57989e7..35e8b59 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -312,7 +312,7 @@ void Screen::setPalette(byte *colors, uint start, uint count) {
 		*dstPal++ = *colors++;
 		++colors;
 	}
-	// TODO Build colorTransTbl
+	buildColorTransTbl();
 	_needRefreshPalette = true;
 }
 
@@ -334,6 +334,42 @@ void Screen::getPalette(byte *colors) {
 	}
 }
 
+void Screen::shiftPalette(int16 fromIndex, int16 toIndex) {
+	//debug("shiftPalette(%d, %d)", fromIndex, toIndex);
+	byte r, g, b;
+	if (toIndex > fromIndex) {
+		r = _mainPalette[3 * toIndex + 0];
+		g = _mainPalette[3 * toIndex + 1];
+		b = _mainPalette[3 * toIndex + 2];
+		for (int16 i = toIndex; i > fromIndex; --i) {
+			byte *dst = &_mainPalette[3 * i];
+			byte *src = &_mainPalette[3 * (i - 1)];
+			dst[0] = src[0];
+			dst[1] = src[1];
+			dst[2] = src[2];
+		}
+		_mainPalette[3 * fromIndex + 0] = r;
+		_mainPalette[3 * fromIndex + 1] = g;
+		_mainPalette[3 * fromIndex + 2] = b;
+	} else {
+		r = _mainPalette[3 * toIndex + 0];
+		g = _mainPalette[3 * toIndex + 1];
+		b = _mainPalette[3 * toIndex + 2];
+		for (int16 i = toIndex + 1; i < fromIndex; +i) {
+			byte *dst = &_mainPalette[3 * i];
+			byte *src = &_mainPalette[3 * (i + 1)];
+			dst[0] = src[0];
+			dst[1] = src[1];
+			dst[2] = src[2];
+		}
+		_mainPalette[3 * fromIndex + 0] = r;
+		_mainPalette[3 * fromIndex + 1] = g;
+		_mainPalette[3 * fromIndex + 2] = b;
+	}
+	// TODO Refresh colorTransTbl
+	_needRefreshPalette = true;
+}
+
 void Screen::updatePalette() {
 	if (_needRefreshPalette) {
 		// TODO Update fader palette
@@ -342,6 +378,30 @@ void Screen::updatePalette() {
 	}
 }
 
+void Screen::buildColorTransTbl() {
+	const int cr = _mainPalette[3 * 1 + 0];
+	const int cg = _mainPalette[3 * 1 + 1];
+	const int cb = _mainPalette[3 * 1 + 2];
+	for (int index1 = 0; index1 < 256; ++index1) {
+		const int dr = (cr + _mainPalette[3 * index1 + 0]) / 2;
+		const int dg = (cg + _mainPalette[3 * index1 + 1]) / 2;
+		const int db = (cb + _mainPalette[3 * index1 + 2]) / 2;
+		int minDistance = 766;
+		int minIndex2 = 2;
+		for (int index2 = 2; index2 < 256; ++index2) {
+			int distance =
+				ABS(dr - _mainPalette[3 * index2 + 0]) +
+				ABS(dg - _mainPalette[3 * index2 + 1]) +
+				ABS(db - _mainPalette[3 * index2 + 2]);
+			if (distance < minDistance) {
+				minDistance = distance;
+				minIndex2 = index2;
+			}
+		}
+		_colorTransTbl[index1] = minIndex2;
+	}
+}
+
 void Screen::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
 	for (uint i = 0; i < count; ++i)
 		x += font->_widthC + drawChar(font, surface, x, y, *text++);
@@ -456,9 +516,13 @@ void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface,
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcRect.top + yc);
 		byte *dst = (byte*)_backSurface->getBasePtr(destX, destY + yc);
 		for (int16 xc = 0; xc < w; ++xc) {
-			byte pixel = *src++;
-			if (pixel != 0)
-				*dst = pixel;
+			const byte pixel = *src++;
+			if (pixel != 0) {
+				if (pixel == 1)
+					*dst = _colorTransTbl[*dst];
+				else
+					*dst = pixel;
+			}
 			++dst;				
 		}
 	}
@@ -485,9 +549,12 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcY);
 		byte *dstRow = dst; 
 		while (w-- > 0) {
-			byte pixel = *src;
+			const byte pixel = *src;
 			if (pixel != 0) {
-				*dstRow = pixel;
+				if (pixel == 1)
+					*dstRow = _colorTransTbl[*dstRow];
+				else
+					*dstRow = pixel;
 			}
 			++dstRow;
 			src += errXStart;
@@ -498,9 +565,13 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 			}
 		}
 		while (skipX-- > 0) {
-			byte pixel = *src;
-			if (pixel != 0)
-				*dstRow = pixel;
+			const byte pixel = *src;
+			if (pixel != 0) {
+				if (pixel == 1)
+					*dstRow = _colorTransTbl[*dstRow];
+				else
+					*dstRow = pixel;
+			}
 			++src;
 			++dstRow;
 		}
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 91750cf..bdecda4 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -114,6 +114,7 @@ public:
 	void setPalette(byte *colors, uint start, uint count);
 	void setPaletteEntry(int16 index, byte r, byte g, byte b);
 	void getPalette(byte *colors);
+	void shiftPalette(int16 fromIndex, int16 toIndex);
 	void updatePalette();
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
 	int16 drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
@@ -130,8 +131,10 @@ public:
 	
 	bool _needRefreshPalette;
 	byte _mainPalette[768];
+	byte _colorTransTbl[256];
 	
 	void setSystemPalette(byte *palette);
+	void buildColorTransTbl();
 
 	void decompressSprite8(SpriteDecompressQueueItem *item);
 	void drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 0312172..b932037 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -71,6 +71,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
 	OPCODE(25, opLeaveScene24);
+	OPCODE(32, opPanCenterObject);
 	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
 	OPCODE(36, opPanToPoint);
@@ -133,7 +134,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(20, opEnterScene);
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
-	OPCODE(32, opPanCenterObject);
 	OPCODE(35, opPanToNamedPoint);
 	OPCODE(53, opSetActorToNamedPoint);
 	OPCODE(63, opSetSelectSfx);
@@ -252,7 +252,8 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 //static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
 //static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
-static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;
+//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -315,6 +316,12 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o
 	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
 }
 
+void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_UINT32(objectId);
+	_vm->_camera->panCenterObject(objectId, speed);
+}
+
 void ScriptOpcodes_Duckman::opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -787,12 +794,6 @@ void ScriptOpcodes_Duckman::opExitCloseUpScene(ScriptThread *scriptThread, OpCal
 	opCall._result = kTSYield;
 }
 
-void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(objectId);
-	_vm->_camera->panCenterObject(objectId, speed);
-}
-
 void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);	
 	ARG_UINT32(namedPointId);
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
index be093c4..4c66d34 100644
--- a/engines/illusions/scriptopcodes_duckman.h
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -58,6 +58,7 @@ protected:
 	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
@@ -120,7 +121,6 @@ protected:
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 868a5651..92b4e12 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -25,6 +25,7 @@
 #include "illusions/actor.h"
 #include "illusions/actorresource.h"
 #include "illusions/dictionary.h"
+#include "illusions/screen.h"
 #include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 
@@ -88,6 +89,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(40, opSetPriorityLayer);
 	OPCODE(41, opDisableAutoRegionLayer);
 	OPCODE(42, opSetRegionLayer);
+	OPCODE(49, opShiftPalette);
 	OPCODE(50, opPlaySound);
 	OPCODE(51, opStopSound);
 	OPCODE(52, opStartScriptThread);
@@ -335,6 +337,12 @@ void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
 }
 
+void SequenceOpcodes::opShiftPalette(Control *control, OpCall &opCall) {
+	ARG_INT16(fromIndex);
+	ARG_INT16(toIndex);
+	_vm->_screen->shiftPalette(fromIndex, toIndex);
+}
+
 void SequenceOpcodes::opPlaySound(Control *control, OpCall &opCall) {
 	ARG_INT16(flags);
 	ARG_INT16(volume);
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index f423d07..384507e 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -77,6 +77,7 @@ protected:
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
 	void opDisableAutoRegionLayer(Control *control, OpCall &opCall);
 	void opSetRegionLayer(Control *control, OpCall &opCall);
+	void opShiftPalette(Control *control, OpCall &opCall);
 	void opPlaySound(Control *control, OpCall &opCall);
 	void opStopSound(Control *control, OpCall &opCall);
 	void opStartScriptThread(Control *control, OpCall &opCall);


Commit: b1927ca30458226ad7ed52cc85d37416261d444d
    https://github.com/scummvm/scummvm/commit/b1927ca30458226ad7ed52cc85d37416261d444d
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add more script and sequence for Duckman

Changed paths:
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h


diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 135bd0b..9f67d6b 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -1060,6 +1060,7 @@ typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeF
 void IllusionsEngine_Duckman::initSpecialCode() {
 	SPECIAL(0x00160002, spcSetCursorHandMode);
 	SPECIAL(0x00160005, spcOpenInventory);
+	SPECIAL(0x00160010, spcCenterNewspaper);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 }
@@ -1085,6 +1086,14 @@ void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
 	notifyThreadId(opCall._threadId);
 }
 
+void IllusionsEngine_Duckman::spcCenterNewspaper(OpCall &opCall) {
+	Control *control = getObjectControl(0x40017);
+	control->_flags |= 8;
+	control->_actor->_position.x = 160;
+	control->_actor->_position.y = 100;
+	notifyThreadId(opCall._threadId);
+}
+
 void IllusionsEngine_Duckman::spcSetCursorInventoryMode(OpCall &opCall) {
 	ARG_BYTE(mode);
 	ARG_BYTE(value);
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 56e6b47..3b369b9 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -194,6 +194,7 @@ public:
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
 	void spcSetCursorHandMode(OpCall &opCall);
 	void spcOpenInventory(OpCall &opCall);
+	void spcCenterNewspaper(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 92b4e12..d508eea 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -75,6 +75,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(17, opDisappearActor);
 	OPCODE(18, opAppearForeignActor);
 	OPCODE(19, opDisappearForeignActor);
+	OPCODE(20, opSetNamedPointPosition);
 	OPCODE(21, opMoveDelta);
 	OPCODE(25, opFaceActor);
 	OPCODE(28, opNotifyThreadId1);
@@ -251,6 +252,12 @@ void SequenceOpcodes::opDisappearForeignActor(Control *control, OpCall &opCall)
 	foreignControl->disappearActor();
 }
 
+void SequenceOpcodes::opSetNamedPointPosition(Control *control, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(namedPointId);
+	control->_actor->_position = _vm->getNamedPointPosition(namedPointId);
+}
+
 void SequenceOpcodes::opMoveDelta(Control *control, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_INT16(deltaX);
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 384507e..48241e1 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -63,6 +63,7 @@ protected:
 	void opDisappearActor(Control *control, OpCall &opCall);
 	void opAppearForeignActor(Control *control, OpCall &opCall);
 	void opDisappearForeignActor(Control *control, OpCall &opCall);
+	void opSetNamedPointPosition(Control *control, OpCall &opCall);
 	void opMoveDelta(Control *control, OpCall &opCall);
 	void opFaceActor(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);


Commit: 1f74de6a4679cecba4fd77e9c79cfaee9c3dd0f6
    https://github.com/scummvm/scummvm/commit/1f74de6a4679cecba4fd77e9c79cfaee9c3dd0f6
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: More work on Duckman

- Implement palette fader
- Add more inventory functions
- Add more script opcodes

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptopcodes_duckman.h


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 8a35096..afaaa00 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -141,6 +141,7 @@ int IllusionsEngine::updateGraphics() {
 	uint32 currTime = getCurrentTime();
 	
 	_camera->update(currTime);
+	updateFader();
 
 	// TODO Move to BackgroundItems class
 	BackgroundItem *backgroundItem = _backgroundItems->findActiveBackground();
@@ -177,8 +178,6 @@ int IllusionsEngine::updateGraphics() {
 			*/
 			if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
 				uint32 priority = control->getPriority();
-//if (control->_objectId == 0x0004001B) continue;
-//debug("objectId: %08X; priority: %d (%d)", control->_objectId, priority, control->_priority);				
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
 					actor->_surfInfo._dimensions, drawPosition, control->_position,
 					priority, actor->_scale, actor->_spriteFlags);
@@ -268,6 +267,29 @@ bool IllusionsEngine::isVoicePlaying() {
 	return false;
 }
 
+void IllusionsEngine::updateFader() {
+	if (_fader && !_fader->_paused && _fader->_active) {
+		int32 currTime = getCurrentTime();
+		int32 currDuration = currTime - _fader->_startTime;
+		if (currDuration) {
+			int newValue;
+			if (currDuration >= _fader->_duration) {
+				newValue = _fader->_maxValue;
+			} else {
+				newValue = (currDuration * (_fader->_maxValue - _fader->_minValue) / _fader->_duration) + _fader->_minValue;
+			}
+			if (_fader->_currValue != newValue) {
+				_fader->_currValue = newValue;
+				_screen->setFader(newValue, _fader->_firstIndex, _fader->_lastIndex);
+			}
+			if (_fader->_currValue == _fader->_maxValue) {
+				_fader->_active = false;
+				notifyThreadId(_fader->_notifyThreadId);
+			}
+		}
+	}
+}
+
 void IllusionsEngine::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 63fb3f6..4ef1931 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -61,6 +61,7 @@ class Control;
 class Controls;
 class Cursor;
 class Dictionary;
+struct Fader;
 class FramesList;
 class Input;
 class Screen;
@@ -111,7 +112,9 @@ public:
 	ThreadList *_threads;
 	
 	ScriptResource *_scriptResource;
-	
+
+	Fader *_fader;
+
 	int _resGetCtr;
 	uint32 _resGetTime;
 	bool _unpauseControlActorFlag;
@@ -146,6 +149,8 @@ public:
 	void stopVoice();
 	bool isVoicePlaying();
 
+	void updateFader();
+
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
 	uint32 clipTextDuration(uint32 duration);
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 931db5c..3b78e1a 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -214,6 +214,8 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_triggerFunctions = new TriggerFunctions();
 	_threads = new ThreadList(this);
 
+	_fader = 0;
+
 	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
 	_stack = new ScriptStack();
 	
@@ -229,7 +231,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	
 	_globalSceneId = 0x00010003;	
 	
-    setDefaultTextCoords();
+	setDefaultTextCoords();
 	
 	_resSys->loadResource(0x000D0001, 0, 0);
 
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 9f67d6b..1c9d30a 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -105,6 +105,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_talkItems = new TalkItems(this);
 	_threads = new ThreadList(this);
 
+	_fader = new Fader();
+
 	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
 	_stack = new ScriptStack();
 	
@@ -150,6 +152,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
+	delete _fader;
+
 	delete _threads;
 	delete _talkItems;
 	delete _controls;
@@ -177,6 +181,19 @@ bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
 		*/
 }
 
+
+void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
+	_fader->_active = true;
+	_fader->_currValue = minValue;
+	_fader->_minValue = minValue;
+	_fader->_maxValue = maxValue;
+	_fader->_firstIndex = firstIndex;
+	_fader->_lastIndex = lastIndex;
+	_fader->_startTime = getCurrentTime();
+	_fader->_duration = duration;
+	_fader->_notifyThreadId = threadId;
+}
+
 void IllusionsEngine_Duckman::setDefaultTextCoords() {
 	WidthHeight dimensions;
 	dimensions._width = 300;
@@ -1038,6 +1055,34 @@ void IllusionsEngine_Duckman::addInventoryItem(uint32 objectId) {
 	control->appearActor();
 }
 
+void IllusionsEngine_Duckman::clearInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]._objectId == objectId)
+			_inventorySlots[i]._objectId = 0;
+}
+
+void IllusionsEngine_Duckman::putBackInventoryItem() {
+	Common::Point mousePos = _input->getCursorPosition();
+	if (_cursor._objectId) {
+		DMInventorySlot *inventorySlot = findInventorySlot(_cursor._objectId);
+		if (inventorySlot)
+			inventorySlot->_objectId = 0;
+		inventorySlot = findClosestInventorySlot(mousePos);
+		inventorySlot->_objectId = _cursor._objectId;
+		Control *control = getObjectControl(_cursor._objectId);
+		control->setActorPosition(inventorySlot->_position);
+		control->appearActor();
+		_cursor._actorIndex = 7;
+		stopCursorHoldingObject();
+		_cursor._actorIndex = 2;
+		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	}
+}
+
 DMInventorySlot *IllusionsEngine_Duckman::findInventorySlot(uint32 objectId) {
 	for (uint i = 0; i < _inventorySlots.size(); ++i)
 		if (_inventorySlots[i]._objectId == objectId)
@@ -1052,6 +1097,24 @@ DMInventoryItem *IllusionsEngine_Duckman::findInventoryItem(uint32 objectId) {
 	return 0;
 }
 
+DMInventorySlot *IllusionsEngine_Duckman::findClosestInventorySlot(Common::Point pos) {
+	int minDistance = 0xFFFFFF;
+	DMInventorySlot *minInventorySlot = 0;
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
+		DMInventorySlot *inventorySlot = &_inventorySlots[i];
+		if (inventorySlot->_objectId == 0) {
+			int16 deltaX = ABS(inventorySlot->_position.x - pos.x);
+			int16 deltaY = ABS(inventorySlot->_position.y - pos.y);
+			int distance = deltaX * deltaX + deltaY * deltaY;
+			if (inventorySlot->_objectId == 0 && distance < minDistance) {
+				minDistance = distance;
+				minInventorySlot = inventorySlot;
+			}
+		}
+	}
+	return minInventorySlot;
+}
+
 // Special code
 
 typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
@@ -1060,6 +1123,8 @@ typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeF
 void IllusionsEngine_Duckman::initSpecialCode() {
 	SPECIAL(0x00160002, spcSetCursorHandMode);
 	SPECIAL(0x00160005, spcOpenInventory);
+	SPECIAL(0x00160007, spcPutBackInventoryItem);
+	SPECIAL(0x00160008, spcClearInventorySlot);
 	SPECIAL(0x00160010, spcCenterNewspaper);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
@@ -1086,6 +1151,17 @@ void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
 	notifyThreadId(opCall._threadId);
 }
 
+void IllusionsEngine_Duckman::spcPutBackInventoryItem(OpCall &opCall) {
+	putBackInventoryItem();
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcClearInventorySlot(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	clearInventorySlot(objectId);
+	notifyThreadId(opCall._threadId);
+}
+
 void IllusionsEngine_Duckman::spcCenterNewspaper(OpCall &opCall) {
 	Control *control = getObjectControl(0x40017);
 	control->_flags |= 8;
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 3b369b9..0502089 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -109,6 +109,8 @@ public:
 	Common::Array<DMInventoryItem> _inventoyItems;
 
 	SpecialCodeMap _specialCodeMap;
+	
+	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
 
 	void setDefaultTextCoords();
 
@@ -185,15 +187,20 @@ public:
 
 	void initInventory();
 	void openInventory();
+	void addInventoryItem(uint32 objectId);
+	void clearInventorySlot(uint32 objectId);
+	void putBackInventoryItem();
 	DMInventorySlot *findInventorySlot(uint32 objectId);
 	DMInventoryItem *findInventoryItem(uint32 objectId);
-	void addInventoryItem(uint32 objectId);
+	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
 
 	// Special code
 	void initSpecialCode();
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
 	void spcSetCursorHandMode(OpCall &opCall);
 	void spcOpenInventory(OpCall &opCall);
+	void spcPutBackInventoryItem(OpCall &opCall);
+	void spcClearInventorySlot(OpCall &opCall);
 	void spcCenterNewspaper(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 35e8b59..2ac1a44 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -237,6 +237,8 @@ Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
 	_needRefreshPalette = false;
 	memset(_mainPalette, 0, sizeof(_mainPalette));
 
+	_isFaderActive = false;
+
 }
 
 Screen::~Screen() {
@@ -372,12 +374,52 @@ void Screen::shiftPalette(int16 fromIndex, int16 toIndex) {
 
 void Screen::updatePalette() {
 	if (_needRefreshPalette) {
-		// TODO Update fader palette
-		setSystemPalette(_mainPalette);
+		if (_isFaderActive) {
+			updateFaderPalette();
+			setSystemPalette(_faderPalette);
+		} else {
+			setSystemPalette(_mainPalette);
+		}
 		_needRefreshPalette = false;
 	}
 }
 
+void Screen::updateFaderPalette() {
+	if (_newFaderValue >= 255) {
+		_newFaderValue -= 256;
+		for (int i = _firstFaderIndex; i <= _lastFaderIndex; ++i) {
+			byte r = _mainPalette[i * 3 + 0];
+			byte g = _mainPalette[i * 3 + 1];
+			byte b = _mainPalette[i * 3 + 2];
+			_faderPalette[i * 3 + 0] = r - (((_newFaderValue * (255 - r)) >> 8) & 0xFF);
+			_faderPalette[i * 3 + 1] = g - (((_newFaderValue * (255 - g)) >> 8) & 0xFF);
+			_faderPalette[i * 3 + 2] = b - (((_newFaderValue * (255 - b)) >> 8) & 0xFF);
+		}
+	} else {
+		for (int i = _firstFaderIndex; i <= _lastFaderIndex; ++i) {
+			byte r = _mainPalette[i * 3 + 0];
+			byte g = _mainPalette[i * 3 + 1];
+			byte b = _mainPalette[i * 3 + 2];
+			_faderPalette[i * 3 + 0] = _newFaderValue * r / 255;
+			_faderPalette[i * 3 + 1] = _newFaderValue * g / 255;
+			_faderPalette[i * 3 + 2] = _newFaderValue * b / 255;
+		}
+	}
+}
+
+void Screen::setFader(int newValue, int firstIndex, int lastIndex) {
+	if (newValue == 255) {
+		_isFaderActive = false;
+		_needRefreshPalette = true;
+	} else {
+		_isFaderActive = true;
+		_needRefreshPalette = true;
+		_newFaderValue = newValue;
+		_firstFaderIndex = firstIndex - 1;
+		_lastFaderIndex = lastIndex;
+	}
+}
+
 void Screen::buildColorTransTbl() {
 	const int cr = _mainPalette[3 * 1 + 0];
 	const int cg = _mainPalette[3 * 1 + 1];
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index bdecda4..15a703c 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -97,6 +97,20 @@ protected:
 	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
 };
 
+struct Fader {
+	bool _active;
+	int _currValue;
+	bool _paused;
+	int _minValue;
+	int _maxValue;
+	int _firstIndex;
+	int _lastIndex;
+	uint32 _startTime;
+	int _duration;
+	uint32 _notifyThreadId;
+	Fader() : _active(false), _paused(false) {}
+};
+
 // TODO Split into two classes (8bit and 16bit)?
 
 class Screen {
@@ -116,6 +130,8 @@ public:
 	void getPalette(byte *colors);
 	void shiftPalette(int16 fromIndex, int16 toIndex);
 	void updatePalette();
+	void updateFaderPalette();
+	void setFader(int newValue, int firstIndex, int lastIndex);
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
 	int16 drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
 	int16 getScreenWidth() const { return _backSurface->w; }
@@ -133,6 +149,10 @@ public:
 	byte _mainPalette[768];
 	byte _colorTransTbl[256];
 	
+	bool _isFaderActive;
+	byte _faderPalette[768];
+	int _newFaderValue, _firstFaderIndex, _lastFaderIndex;
+
 	void setSystemPalette(byte *palette);
 	void buildColorTransTbl();
 
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index b932037..3b90e0c 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -74,6 +74,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(32, opPanCenterObject);
 	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
+	OPCODE(35, opPanToNamedPoint);
 	OPCODE(36, opPanToPoint);
 	OPCODE(37, opPanStop);
 	OPCODE(38, opStartFade);
@@ -134,7 +135,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(20, opEnterScene);
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
-	OPCODE(35, opPanToNamedPoint);
 	OPCODE(53, opSetActorToNamedPoint);
 	OPCODE(63, opSetSelectSfx);
 	OPCODE(64, opSetMoveSfx);
@@ -336,6 +336,13 @@ void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &op
 	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);
 	ARG_INT16(x);
@@ -348,15 +355,14 @@ void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall
 }
 
 void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(arg1);
-	ARG_INT16(arg2);
-	ARG_INT16(arg3);
-	ARG_INT16(arg4);
-	ARG_INT16(arg5);
-	// TODO
-
+	ARG_INT16(duration);
+	ARG_INT16(minValue);
+	ARG_INT16(maxValue);
+	ARG_INT16(firstIndex);
+	ARG_INT16(lastIndex);
+	_vm->startFader(duration, minValue, maxValue, firstIndex, lastIndex, opCall._threadId);
 	//DEBUG Resume calling thread, later done when the fading is finished
-	_vm->notifyThreadId(opCall._threadId);
+	//_vm->notifyThreadId(opCall._threadId);
 }
 
 void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
@@ -794,13 +800,6 @@ void ScriptOpcodes_Duckman::opExitCloseUpScene(ScriptThread *scriptThread, OpCal
 	opCall._result = kTSYield;
 }
 
-void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
 void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
index 4c66d34..77050de 100644
--- a/engines/illusions/scriptopcodes_duckman.h
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -61,6 +61,7 @@ protected:
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
@@ -121,7 +122,6 @@ protected:
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);


Commit: 60600191a07fe8e7f4945b5dab63b5b374111ed4
    https://github.com/scummvm/scummvm/commit/60600191a07fe8e7f4945b5dab63b5b374111ed4
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Use the UpdateFunctions class for update routines

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_bbdou.h
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/updatefunctions.cpp
    engines/illusions/updatefunctions.h


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index afaaa00..5031f5e 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -88,6 +88,10 @@ void IllusionsEngine::updateEvents() {
 	}
 }
 
+void IllusionsEngine::runUpdateFunctions() {
+	_updateFunctions->update();
+}
+
 Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
 	Control *control = getObjectControl(objectId);
 	if (control && control->_actor)
@@ -113,7 +117,7 @@ uint32 IllusionsEngine::getElapsedUpdateTime() {
 	return result;
 }
 
-int IllusionsEngine::updateActors() {
+int IllusionsEngine::updateActors(uint flags) {
 	// TODO Move to Controls class
 	uint32 deltaTime = getElapsedUpdateTime();
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
@@ -124,7 +128,7 @@ int IllusionsEngine::updateActors() {
 	return 1;
 }
 
-int IllusionsEngine::updateSequences() {
+int IllusionsEngine::updateSequences(uint flags) {
 	// TODO Move to Controls class
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
@@ -135,7 +139,7 @@ int IllusionsEngine::updateSequences() {
 	return 1;
 }
 
-int IllusionsEngine::updateGraphics() {
+int IllusionsEngine::updateGraphics(uint flags) {
 	Common::Point panPoint(0, 0);
 
 	uint32 currTime = getCurrentTime();
@@ -194,6 +198,12 @@ int IllusionsEngine::updateGraphics() {
 	return 1;
 }
 
+int IllusionsEngine::updateSprites(uint flags) {
+	_screen->updateSprites();
+	_screen->updatePalette();
+	return 1;
+}
+
 int IllusionsEngine::getRandom(int max) {
 	return _random->getRandomNumber(max - 1);
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 4ef1931..ae7b4af 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -72,6 +72,7 @@ class Sequence;
 class SpecialCode;
 class TalkItems;
 class ThreadList;
+class UpdateFunctions;
 
 enum {
 	kGameIdBBDOU   = 1,
@@ -96,6 +97,7 @@ public:
 	Common::RandomSource *_random;
 	Dictionary *_dict;
 	ResourceSystem *_resSys;
+	UpdateFunctions *_updateFunctions;
 	
 	void updateEvents();
 
@@ -133,11 +135,14 @@ public:
 		return _gameDescription->gameId;
 	}
 
-	Common::Point *getObjectActorPositionPtr(uint32 objectId);
+	void runUpdateFunctions();
+	int updateActors(uint flags);
+	int updateSequences(uint flags);
+	int updateGraphics(uint flags);
+	int updateSprites(uint flags);
+
 	uint32 getElapsedUpdateTime();
-	int updateActors();
-	int updateSequences();
-	int updateGraphics();
+	Common::Point *getObjectActorPositionPtr(uint32 objectId);
 	int getRandom(int max);
 	int convertPanXCoord(int16 x);
 	bool calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing);
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 3b78e1a..f2cb4fd 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -64,6 +64,8 @@
 
 namespace Illusions {
 
+//typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> UpdateFunctionI;
+
 // TriggerFunction
 
 TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
@@ -213,6 +215,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_talkItems = new TalkItems(this);
 	_triggerFunctions = new TriggerFunctions();
 	_threads = new ThreadList(this);
+	_updateFunctions = new UpdateFunctions();
+
+	initUpdateFunctions();
 
 	_fader = 0;
 
@@ -240,11 +245,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_doScriptThreadInit = true;
 
 	while (!shouldQuit()) {
-		_threads->updateThreads();
-		updateActors();
-		updateSequences();
-		updateGraphics();
-		_screen->updateSprites();
+		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
 		_system->delayMillis(10);
@@ -253,6 +254,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
+	delete _updateFunctions;
 	delete _threads;
 	delete _triggerFunctions;
 	delete _talkItems;
@@ -283,6 +285,25 @@ bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
 		*/
 }
 
+#define UPDATEFUNCTION(priority, tag, callback) \
+	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
+		(this, &IllusionsEngine_BBDOU::callback));
+
+void IllusionsEngine_BBDOU::initUpdateFunctions() {
+	UPDATEFUNCTION(30, 0, updateScript);
+	UPDATEFUNCTION(50, 0, updateActors);
+	UPDATEFUNCTION(60, 0, updateSequences);
+	UPDATEFUNCTION(70, 0, updateGraphics);
+	UPDATEFUNCTION(90, 0, updateSprites);
+}
+
+#undef UPDATEFUNCTION
+
+int IllusionsEngine_BBDOU::updateScript(uint flags) {
+	_threads->updateThreads();
+	return 1;
+}
+
 bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
 	uint32 codeOffs;
 	return 
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
index addd06c..ea46fb9 100644
--- a/engines/illusions/illusions_bbdou.h
+++ b/engines/illusions/illusions_bbdou.h
@@ -104,6 +104,9 @@ public:
 
 	uint32 _nextTempThreadId;
 
+	void initUpdateFunctions();
+	int updateScript(uint flags);
+
 	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
 	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
 	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 1c9d30a..f9d0360 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -104,9 +104,12 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_controls = new Controls(this);
 	_talkItems = new TalkItems(this);
 	_threads = new ThreadList(this);
+	_updateFunctions = new UpdateFunctions();
 
 	_fader = new Fader();
 
+	initUpdateFunctions();
+
 	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
 	_stack = new ScriptStack();
 	
@@ -138,12 +141,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_doScriptThreadInit = true;
 
 	while (!shouldQuit()) {
-		_threads->updateThreads();
-		updateActors();
-		updateSequences();
-		updateGraphics();
-		_screen->updateSprites();
-		_screen->updatePalette();
+		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
 		_system->delayMillis(10);
@@ -154,6 +152,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _fader;
 
+	delete _updateFunctions;
 	delete _threads;
 	delete _talkItems;
 	delete _controls;
@@ -181,6 +180,25 @@ bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
 		*/
 }
 
+#define UPDATEFUNCTION(priority, tag, callback) \
+	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
+		(this, &IllusionsEngine_Duckman::callback));
+
+void IllusionsEngine_Duckman::initUpdateFunctions() {
+	UPDATEFUNCTION(30, 0, updateScript);
+	UPDATEFUNCTION(50, 0, updateActors);
+	UPDATEFUNCTION(60, 0, updateSequences);
+	UPDATEFUNCTION(70, 0, updateGraphics);
+	UPDATEFUNCTION(90, 0, updateSprites);
+}
+
+#undef UPDATEFUNCTION
+
+int IllusionsEngine_Duckman::updateScript(uint flags) {
+	// TODO Some more stuff
+	_threads->updateThreads();
+	return 1;
+}
 
 void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
 	_fader->_active = true;
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 0502089..ca48bc6 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -109,6 +109,9 @@ public:
 	Common::Array<DMInventoryItem> _inventoyItems;
 
 	SpecialCodeMap _specialCodeMap;
+
+	void initUpdateFunctions();
+	int updateScript(uint flags);
 	
 	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
 
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index 3a3aadf..e0cc775 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -73,4 +73,11 @@ void UpdateFunctions::update() {
 
 }
 
+void UpdateFunctions::terminateByScene(uint32 sceneId) {
+	UpdateFunctionListIterator it = _updateFunctions.begin();
+	while (it != _updateFunctions.end())
+		if ((*it)->_tag == sceneId)
+			(*it)->terminate();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/updatefunctions.h b/engines/illusions/updatefunctions.h
index bd8aef3..877bbc1 100644
--- a/engines/illusions/updatefunctions.h
+++ b/engines/illusions/updatefunctions.h
@@ -33,16 +33,18 @@ enum {
 	kUFTerminate    = 2   // Terminate update function
 };
 
-typedef Common::Functor0<int> UpdateFunctionCallback;
+typedef Common::Functor1<uint, int> UpdateFunctionCallback;
 
 class UpdateFunction {
 public:
 	int _priority;
 	uint32 _tag;
+	uint _flags;
 	UpdateFunctionCallback *_callback;
-	UpdateFunction() : _priority(0), _tag(0), _callback(0) {}
+	UpdateFunction() : _priority(0), _tag(0), _flags(0), _callback(0) {}
 	~UpdateFunction() { delete _callback; }
-	int run() { return (*_callback)(); }
+	void terminate() { _flags |= 1; }
+	int run() { return (*_callback)(_flags); }
 };
 
 class UpdateFunctions {
@@ -51,6 +53,7 @@ public:
 	~UpdateFunctions();
 	void add(int priority, uint32 tag, UpdateFunctionCallback *callback);
 	void update();
+	void terminateByScene(uint32 sceneId);
 protected:
 	typedef Common::List<UpdateFunction*> UpdateFunctionList;
 	typedef UpdateFunctionList::iterator UpdateFunctionListIterator;


Commit: 44c566b51e661ef5751b947aed071660cc628547
    https://github.com/scummvm/scummvm/commit/44c566b51e661ef5751b947aed071660cc628547
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add screen shaking effect

Changed paths:
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.h
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/updatefunctions.cpp


diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index ae7b4af..7ea49fd 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -68,6 +68,7 @@ class Screen;
 class ScreenText;
 class ScriptOpcodes;
 class ScriptResource;
+class ScriptStack;
 class Sequence;
 class SpecialCode;
 class TalkItems;
@@ -113,10 +114,15 @@ public:
 	SpecialCode *_specialCode;
 	ThreadList *_threads;
 	
+	uint32 _nextTempThreadId;
+	bool _doScriptThreadInit;
+	ScriptStack *_stack;
 	ScriptResource *_scriptResource;
 
 	Fader *_fader;
 
+	int _pauseCtr;
+
 	int _resGetCtr;
 	uint32 _resGetTime;
 	bool _unpauseControlActorFlag;
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
index ea46fb9..fccfb59 100644
--- a/engines/illusions/illusions_bbdou.h
+++ b/engines/illusions/illusions_bbdou.h
@@ -98,12 +98,6 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
-	int _pauseCtr;
-	ScriptStack *_stack;
-	bool _doScriptThreadInit;
-
-	uint32 _nextTempThreadId;
-
 	void initUpdateFunctions();
 	int updateScript(uint flags);
 
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index f9d0360..48db89d 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -200,6 +200,56 @@ int IllusionsEngine_Duckman::updateScript(uint flags) {
 	return 1;
 }
 
+void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId) {
+	_screenShaker = new ScreenShaker();
+	_screenShaker->_pointsIndex = 0;
+	_screenShaker->_pointsCount = pointsCount;
+	_screenShaker->_finished = false;
+	_screenShaker->_duration = duration;
+	_screenShaker->_nextTime = duration + getCurrentTime();
+	_screenShaker->_points = points;
+	_screenShaker->_notifyThreadId = threadId;
+	_updateFunctions->add(71, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
+		(this, &IllusionsEngine_Duckman::updateScreenShaker));
+}
+
+int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
+	if (_pauseCtr > 0 || getCurrentScene() == 0x10038) {
+		_screenShaker->_nextTime = getCurrentTime();
+		return 1;
+	}
+
+	if (flags & 1)
+		_screenShaker->_finished = true;
+
+	if (!_screenShaker->_finished) {
+		if (getCurrentTime() >= _screenShaker->_nextTime) {
+			++_screenShaker->_pointsIndex;
+			if (_screenShaker->_pointsIndex <= _screenShaker->_pointsCount) {
+				ScreenShakerPoint shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
+				if (shakePt.x == (int16)0x8000) {
+					// Loop
+					_screenShaker->_pointsIndex = 1;
+					shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
+				}
+				_screenShaker->_nextTime = getCurrentTime() + _screenShaker->_duration;
+				_screen->setScreenOffset(Common::Point(shakePt.x, shakePt.y));
+			} else
+				_screenShaker->_finished = true;
+		}
+	}
+
+    if (_screenShaker->_finished) {
+		notifyThreadId(_screenShaker->_notifyThreadId);
+		delete _screenShaker;
+		_screenShaker = 0;
+		_screen->setScreenOffset(Common::Point(0, 0));
+		return 2;
+	}
+
+	return 1;
+}
+
 void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
 	_fader->_active = true;
 	_fader->_currValue = minValue;
@@ -553,13 +603,6 @@ void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThr
 	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
 		scriptCodeIp, 0, 0, 0);
 	_threads->startThread(scriptThread);
-	if (_pauseCtr > 0)
-		scriptThread->pause();
-	if (_doScriptThreadInit) {
-		int updateResult = kTSRun;
-		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
-			updateResult = scriptThread->update();
-	}
 }
 
 uint32 IllusionsEngine_Duckman::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
@@ -980,7 +1023,6 @@ void IllusionsEngine_Duckman::updateDialogState() {
 			_controls->destroyDialogItems();
 			Control *control = _dict->getObjectControl(0x40148);
 			_controls->destroyControl(control);
-			debug("_cursor._notifyThreadId30: %08X", _cursor._notifyThreadId30);
 			notifyThreadId(_cursor._notifyThreadId30);
 			_cursor._notifyThreadId30 = 0;
 			_cursor._gameState = 2;
@@ -1139,6 +1181,7 @@ typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeF
 #define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &IllusionsEngine_Duckman::func);
 
 void IllusionsEngine_Duckman::initSpecialCode() {
+	SPECIAL(0x00160001, spcStartScreenShaker);
 	SPECIAL(0x00160002, spcSetCursorHandMode);
 	SPECIAL(0x00160005, spcOpenInventory);
 	SPECIAL(0x00160007, spcPutBackInventoryItem);
@@ -1148,6 +1191,8 @@ void IllusionsEngine_Duckman::initSpecialCode() {
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 }
 
+#undef SPECIAL
+
 void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCall) {
 	SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
 	if (it != _specialCodeMap.end()) {
@@ -1158,6 +1203,26 @@ void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCal
 	}
 }
 
+static const ScreenShakerPoint kShakerPoints0[] = {
+	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
+};
+
+static const ScreenShakeEffect kShakerEffect0 = {
+	6, 5, kShakerPoints0
+};
+
+static const ScreenShakeEffect *kShakerEffects = {
+	&kShakerEffect0
+};
+
+void IllusionsEngine_Duckman::spcStartScreenShaker(OpCall &opCall) {
+	// TODO Add more effects
+	ARG_BYTE(effect);
+	debug("### effect: %d", effect);
+	const ScreenShakeEffect *shakerEffect = &kShakerEffects[effect];
+	startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
+}
+
 void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
 	ARG_BYTE(mode);
 	setCursorHandMode(mode);
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index ca48bc6..da3036f 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -71,6 +71,26 @@ struct DMInventoryItem {
 		: _objectId(objectId), _propertyId(propertyId) {}
 };
 
+struct ScreenShakerPoint {
+	int16 x, y;
+};
+
+struct ScreenShakeEffect {
+	uint32 _duration;
+	uint _pointsCount;
+	const ScreenShakerPoint *_points;
+};
+
+struct ScreenShaker {
+	uint _pointsIndex;
+	uint _pointsCount;
+	bool _finished;
+	uint32 _duration;
+	uint32 _nextTime;
+	uint32 _notifyThreadId;
+	const ScreenShakerPoint *_points;
+};
+
 struct OpCall;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
@@ -91,12 +111,6 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
-	int _pauseCtr;
-	ScriptStack *_stack;
-	bool _doScriptThreadInit;
-
-	uint32 _nextTempThreadId;
-	
 	uint _activeScenesCount;
 	uint32 _activeScenes[6];
 
@@ -108,11 +122,16 @@ public:
 	Common::Array<DMInventorySlot> _inventorySlots;
 	Common::Array<DMInventoryItem> _inventoyItems;
 
+	ScreenShaker *_screenShaker;
+
 	SpecialCodeMap _specialCodeMap;
 
 	void initUpdateFunctions();
 	int updateScript(uint flags);
-	
+
+	void startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId);
+	int updateScreenShaker(uint flags);
+
 	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
 
 	void setDefaultTextCoords();
@@ -200,6 +219,7 @@ public:
 	// Special code
 	void initSpecialCode();
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
+	void spcStartScreenShaker(OpCall &opCall);
 	void spcSetCursorHandMode(OpCall &opCall);
 	void spcOpenInventory(OpCall &opCall);
 	void spcPutBackInventoryItem(OpCall &opCall);
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 2ac1a44..cd5ca68 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -178,15 +178,12 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	dstRect.right = item->_drawPosition.x + item->_scale * (item->_dimensions._width - item->_controlPosition.x) / 100;
 	dstRect.bottom = item->_drawPosition.y + item->_scale * (item->_dimensions._height - item->_controlPosition.y) / 100;
 	
-	/* CHECKME This seems to be unused basically and only called from debug code
-		Left here just in case...
-	if (gfx_seemsAlways0) {
-		dstRect.left += screenOffsetPt.x;
-		dstRect.right = screenOffsetPt.x + dstRect.right;
-		dstRect.top = screenOffsetPt.y + dstRect.top;
-		dstRect.bottom = screenOffsetPt.y + dstRect.bottom;
+	if (_screen->_isScreenOffsetActive) {
+		dstRect.left += _screen->_screenOffsetPt.x;
+		dstRect.right += _screen->_screenOffsetPt.x;
+		dstRect.top += _screen->_screenOffsetPt.y;
+		dstRect.bottom += _screen->_screenOffsetPt.y;
 	}
-	*/
 
 	// Check if the sprite is on-screen
 	if (dstRect.left >= _screen->getScreenWidth() || dstRect.right <= 0 || dstRect.top >= _screen->getScreenHeight() || dstRect.bottom <= 0)
@@ -238,6 +235,7 @@ Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
 	memset(_mainPalette, 0, sizeof(_mainPalette));
 
 	_isFaderActive = false;
+	_isScreenOffsetActive = false;
 
 }
 
@@ -267,6 +265,15 @@ void Screen::setDisplayOn(bool isOn) {
 	// TODO Clear screen when off
 }
 
+void Screen::setScreenOffset(Common::Point offsPt) {
+	if (offsPt.x != 0 || offsPt.y != 0) {
+		_isScreenOffsetActive = true;
+		_screenOffsetPt = offsPt;
+	} else {
+		_isScreenOffsetActive = false;
+	}
+}
+
 uint16 Screen::getColorKey2() {
 	return _colorKey2;
 }
@@ -275,11 +282,33 @@ void Screen::updateSprites() {
 	_decompressQueue->decompressAll();
 	// NOTE Skipped doShiftBrightness and related as it seems to be unused
 	_drawQueue->drawAll();
+	if (_isScreenOffsetActive)
+		clearScreenOffsetAreas();
 	if (!_displayOn) // TODO Check if a video is playing then don't do it
 		_backSurface->fillRect(Common::Rect(_backSurface->w, _backSurface->h), 0);
 	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
 }
 
+void Screen::clearScreenOffsetAreas() {
+	int16 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+	if (_screenOffsetPt.x < 0) {
+		x1 = _backSurface->w + _screenOffsetPt.x;
+		x2 = _backSurface->w;
+	} else if (_screenOffsetPt.x > 0) {
+		x1 = 0;
+		x2 = _screenOffsetPt.x;
+	}
+	if (_screenOffsetPt.y < 0) {
+		y1 = _backSurface->h + _screenOffsetPt.y;
+		y2 = _backSurface->h;
+	} else if (_screenOffsetPt.y > 0) {
+		y1 = 0;
+		y2 = _screenOffsetPt.y;
+	}
+	_backSurface->fillRect(Common::Rect(0, y1, _backSurface->w, y2), 0);
+	_backSurface->fillRect(Common::Rect(x1, 0, x2, _backSurface->h), 0);
+}
+
 void Screen::decompressSprite(SpriteDecompressQueueItem *item) {
 	switch (_backSurface->format.bytesPerPixel) {
 	case 1:
@@ -337,7 +366,6 @@ void Screen::getPalette(byte *colors) {
 }
 
 void Screen::shiftPalette(int16 fromIndex, int16 toIndex) {
-	//debug("shiftPalette(%d, %d)", fromIndex, toIndex);
 	byte r, g, b;
 	if (toIndex > fromIndex) {
 		r = _mainPalette[3 * toIndex + 0];
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 15a703c..1eef207 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -121,8 +121,10 @@ public:
 	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
 	bool isDisplayOn();
 	void setDisplayOn(bool isOn);
+	void setScreenOffset(Common::Point offsPt);
 	uint16 getColorKey2();
 	void updateSprites();
+	void clearScreenOffsetAreas();
 	void decompressSprite(SpriteDecompressQueueItem *item);
 	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
 	void setPalette(byte *colors, uint start, uint count);
@@ -153,6 +155,9 @@ public:
 	byte _faderPalette[768];
 	int _newFaderValue, _firstFaderIndex, _lastFaderIndex;
 
+	bool _isScreenOffsetActive;
+	Common::Point _screenOffsetPt;
+
 	void setSystemPalette(byte *palette);
 	void buildColorTransTbl();
 
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 3b90e0c..1caf3b8 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -244,7 +244,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-//static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
+static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 //static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
@@ -253,7 +253,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 //static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
 //static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
-static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
+//static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -547,8 +547,6 @@ void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeId);
 	_vm->runSpecialCode(specialCodeId, opCall);
-	//DEBUG Resume calling thread, later done by the special code
-	_vm->notifyThreadId(opCall._threadId);
 }
 
 void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index e0cc775..6e6cf52 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -49,12 +49,10 @@ void UpdateFunctions::add(int priority, uint32 tag, UpdateFunctionCallback *call
 }
 
 void UpdateFunctions::update() {
-
 	// Avoid running updates multiple times in the current time slice
 	while (_lastTimerUpdateTime == getCurrentTime())
 		g_system->delayMillis(10); // CHECKME Timer resolution
 	_lastTimerUpdateTime = getCurrentTime();
-
 	UpdateFunctionListIterator it = _updateFunctions.begin();
 	while (it != _updateFunctions.end()) {
 		int r = (*it)->run();
@@ -70,7 +68,6 @@ void UpdateFunctions::update() {
 			break;
 		}
 	}
-
 }
 
 void UpdateFunctions::terminateByScene(uint32 sceneId) {


Commit: b94b4c28ba33b9c5edbd9ba8143284786a8dfeac
    https://github.com/scummvm/scummvm/commit/b94b4c28ba33b9c5edbd9ba8143284786a8dfeac
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement pathfinding

Changed paths:
  A engines/illusions/pathfinder.cpp
  A engines/illusions/pathfinder.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/graphics.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/thread.cpp
    engines/illusions/thread.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 030097b..31f3a28 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -72,6 +72,8 @@ Actor::Actor(IllusionsEngine *vm)
 	_scaleLayer = 0;
 	_priorityLayer = 0;
 	_regionLayer = 0;
+	_pathWalkPoints = 0;
+	_pathWalkRects = 0;
 	_position.x = 0;
 	_position.y = 0;
 	_position2.x = 0;
@@ -115,13 +117,7 @@ Actor::Actor(IllusionsEngine *vm)
 
 #if 0 // TODO
 	_field2 = 0;
-	_namedPointsCount = 0;
-	_namedPoints = 0;
 	_field164 = 0;
-	_pathWalkRects = 0;
-	_pathWalkPoints = 0;
-	_regionLayer = 0;
-	_transitionRegionId = 0;
 	_field18C = 0;
 	_field190 = 0;
 	_field192 = 0;
@@ -768,9 +764,15 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 
 PointArray *Control::createPath(Common::Point destPt) {
 	// TODO Implement actual pathfinding
-	PointArray *pathNode = new PointArray();
-	pathNode->push_back(destPt);
-	return pathNode;
+	PointArray *walkPoints = (_actor->_flags & 2) ? _actor->_pathWalkPoints->_points : 0;
+	PathLines *walkRects = (_actor->_flags & 0x10) ? _actor->_pathWalkRects->_rects : 0;
+	PathFinder pathFinder;
+	WidthHeight bgDimensions = _vm->_backgroundItems->getMasterBgDimensions();
+	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
+	for (uint i = 0; i < path->size(); ++i) {
+		debug("Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
+	}
+	return path;
 }
 
 void Control::updateActorMovement(uint32 deltaTime) {
@@ -793,16 +795,6 @@ void Control::updateActorMovement(uint32 deltaTime) {
 	if (_vm->testMainActorCollision(this))
 		break;
 
-	/* TODO
-	if (controla->objectId == GameScript_getField0() && again == const0 && Input_pollButton__(0x10u)) {
-		again = 1;
-		Control_disappearActor__(controla);
-		HIBYTE(_actor->_flags) |= 0x80u;
-		_actor->_seqCodeIp = 0;
-		deltaTime = 2;
-	}
-	*/
-
 	Common::Point prevPt;
 	if (_actor->_pathPointIndex == 0) {
 		if (_actor->_pathInitialPosFlag) {
@@ -1051,7 +1043,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
 	if (actorType->_pathWalkPointsIndex) {
-		// TODO actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
+		actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
 		actor->_flags |= 0x02;
 	}
 
@@ -1061,7 +1053,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	}
 
 	if (actorType->_pathWalkRectIndex) {
-		// TODO actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
+		actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
 		actor->_flags |= 0x10;
 	}
 	
@@ -1080,12 +1072,12 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	_controls.push_back(control);
 	_vm->_dict->setObjectControl(objectId, control);
 
-    if (_vm->getGameId() == kGameIdDuckman) {
+	if (_vm->getGameId() == kGameIdDuckman) {
 		control->appearActor();
-    } else if (_vm->getGameId() == kGameIdBBDOU) {
+	} else if (_vm->getGameId() == kGameIdBBDOU) {
 		control->_flags |= 0x01;
 		actor->_flags |= 0x1000;
-    }
+	}
 
 	if (_vm->isCursorObject(actorTypeId, objectId))
 		_vm->placeCursorControl(control, sequenceId);
@@ -1318,6 +1310,24 @@ bool Controls::getDialogItemAtPos(Control *control, Common::Point pt, Control **
 	return foundControl != 0;
 }
 
+bool Controls::getOverlappedWalkObject(Control *control, Common::Point pt, Control **outOverlappedControl) {
+	Control *foundControl = 0;
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *testControl = *it;
+		if (testControl != control && testControl->_pauseCtr == 0 &&
+			(testControl->_flags & 1)) {
+			Common::Rect collisionRect;
+			testControl->getCollisionRect(collisionRect);
+			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
+				(!foundControl || foundControl->_priority < testControl->_priority))
+				foundControl = testControl;
+		}
+	}
+	if (foundControl)
+		*outOverlappedControl = foundControl;
+	return foundControl != 0;
+}
+
 void Controls::destroyControl(Control *control) {
 	_controls.remove(control);
 	destroyControlInternal(control);
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index e71eebc..7e69ee6 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -26,6 +26,7 @@
 #include "illusions/actorresource.h"
 #include "illusions/backgroundresource.h"
 #include "illusions/graphics.h"
+#include "illusions/pathfinder.h"
 #include "common/algorithm.h"
 #include "common/func.h"
 #include "common/list.h"
@@ -66,7 +67,6 @@ protected:
 };
 
 typedef Common::Functor2<Control*, uint32, void> ActorControlRoutine;
-typedef Common::Array<Common::Point> PointArray;
 
 class Actor {
 public:
@@ -102,6 +102,8 @@ public:
 	ScaleLayer *_scaleLayer;
 	PriorityLayer *_priorityLayer;
 	RegionLayer *_regionLayer;
+	PathWalkPoints *_pathWalkPoints;
+	PathWalkRects *_pathWalkRects;
 	
 	uint _seqStackCount;
 	int16 _seqStack[5];
@@ -238,6 +240,7 @@ public:
 	void unpauseControlsByTag(uint32 tag);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
 	bool getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl);
+	bool getOverlappedWalkObject(Control *control, Common::Point pt, Control **outOverlappedControl);
 	void destroyControl(Control *control);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 	void actorControlRoutine(Control *control, uint32 deltaTime);
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index f760005..07a07c2 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -218,6 +218,41 @@ void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream)
 		_objectId, _flags, _priority, pointsConfigOffs);
 }
 
+// PathWalkPoints
+
+void PathWalkPoints::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_points = new PointArray();
+	uint count = stream.readUint32LE();
+	uint32 pointsOffs = stream.readUint32LE();
+	_points->reserve(count);
+	stream.seek(pointsOffs);
+	for (uint i = 0; i < count; ++i) {
+		Common::Point pt;
+		loadPoint(stream, pt);
+		_points->push_back(pt);
+	}
+	debug(0, "PathWalkPoints::load() count: %d; pointsOffs: %08X",
+		count, pointsOffs);
+}
+
+// PathWalkRects
+
+void PathWalkRects::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_rects = new PathLines();
+	uint count = stream.readUint32LE();
+	uint32 rectsOffs = stream.readUint32LE();
+	_rects->reserve(count);
+	stream.seek(rectsOffs);
+	for (uint i = 0; i < count; ++i) {
+		PathLine rect;
+		loadPoint(stream, rect.p0);
+		loadPoint(stream, rect.p1);
+		_rects->push_back(rect);
+	}
+	debug(0, "PathWalkRects::load() count: %d; rectsOffs: %08X",
+		count, rectsOffs);
+}
+
 // BackgroundResource
 
 BackgroundResource::BackgroundResource() {
@@ -303,6 +338,30 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 		_backgroundObjects[i].load(data, stream);
 	}
 	
+	// Load path walk points
+	stream.seek(0x0E);
+	_pathWalkPointsCount = stream.readUint16LE();
+	debug("_pathWalkPointsCount: %d", _pathWalkPointsCount);
+	_pathWalkPoints = new PathWalkPoints[_pathWalkPointsCount];
+	stream.seek(0x28);
+	uint32 pathWalkPointsOffs = stream.readUint32LE();
+	for (uint i = 0; i < _pathWalkPointsCount; ++i) {
+		stream.seek(pathWalkPointsOffs + i * 8);
+		_pathWalkPoints[i].load(data, stream);
+	}
+
+	// Load path walk rects
+	stream.seek(0x12);
+	_pathWalkRectsCount = stream.readUint16LE();
+	debug("_pathWalkRectsCount: %d", _pathWalkRectsCount);
+	_pathWalkRects = new PathWalkRects[_pathWalkRectsCount];
+	stream.seek(0x30);
+	uint32 pathWalkRectsOffs = stream.readUint32LE();
+	for (uint i = 0; i < _pathWalkRectsCount; ++i) {
+		stream.seek(pathWalkRectsOffs + i * 8);
+		_pathWalkRects[i].load(data, stream);
+	}
+
 	// Load named points
 	stream.seek(0xC);
 	uint namedPointsCount = stream.readUint16LE();
@@ -344,6 +403,14 @@ RegionLayer *BackgroundResource::getRegionLayer(uint index) {
 	return &_regionLayers[index];
 }
 
+PathWalkPoints *BackgroundResource::getPathWalkPoints(uint index) {
+	return &_pathWalkPoints[index];
+}
+
+PathWalkRects *BackgroundResource::getPathWalkRects(uint index) {
+	return &_pathWalkRects[index];
+}
+
 bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	return _namedPoints.findNamedPoint(namedPointId, pt);
 }
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 3a7e1eb..b09c2d5 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -25,6 +25,7 @@
 
 #include "illusions/camera.h"
 #include "illusions/graphics.h"
+#include "illusions/pathfinder.h"
 #include "illusions/resourcesystem.h"
 #include "graphics/surface.h"
 
@@ -103,19 +104,6 @@ protected:
 	byte *_map, *_values;
 };
 
-
-#if 0
-BgResource_PathWalkRects struc ; (sizeof=0x8)
-count dd ?
-rects dd ?
-BgResource_PathWalkRects ends
-
-BgResource_PathWalkPoints struc ; (sizeof=0x8)
-count dd ?
-points dd ?
-BgResource_PathWalkPoints ends
-#endif
-
 struct Palette {
 	uint16 _count;
 	uint16 _unk;
@@ -131,6 +119,20 @@ struct BackgroundObject {
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 };
 
+struct PathWalkPoints {
+	PointArray *_points;
+	PathWalkPoints() : _points(0) {}
+	~PathWalkPoints() { delete _points; }
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct PathWalkRects {
+	PathLines *_rects;
+	PathWalkRects() : _rects(0) {}
+	~PathWalkRects() { delete _rects; }
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
 class BackgroundResource {
 public:
 	BackgroundResource();
@@ -140,6 +142,8 @@ public:
 	PriorityLayer *getPriorityLayer(uint index);
 	ScaleLayer *getScaleLayer(uint index);
 	RegionLayer *getRegionLayer(uint index);
+	PathWalkPoints *getPathWalkPoints(uint index);
+	PathWalkRects *getPathWalkRects(uint index);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 
@@ -162,7 +166,13 @@ public:
 
 	uint _backgroundObjectsCount;
 	BackgroundObject *_backgroundObjects;
-	
+
+	uint _pathWalkPointsCount;
+	PathWalkPoints *_pathWalkPoints;
+
+	uint _pathWalkRectsCount;
+	PathWalkRects *_pathWalkRects;
+
 	NamedPoints _namedPoints;
 	
 	uint _palettesCount;
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index 37fe4d5..a4fa0e6 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -76,7 +76,7 @@ void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
 	pt.x = stream.readSint16LE();
 	pt.y = stream.readSint16LE();
 	
-	debug(5, "loadPoint() x: %d; y: %d",
+	debug(0, "loadPoint() x: %d; y: %d",
 		pt.x, pt.y);
 }
 
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 48db89d..3f32ad4 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -118,6 +118,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_unpauseControlActorFlag = false;
 	_lastUpdateTime = 0;
 
+	_currWalkOverlappedControl = 0;
+
 	_pauseCtr = 0;
 	_doScriptThreadInit = false;
 	_field8 = 1;
@@ -239,7 +241,7 @@ int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
 		}
 	}
 
-    if (_screenShaker->_finished) {
+	if (_screenShaker->_finished) {
 		notifyThreadId(_screenShaker->_notifyThreadId);
 		delete _screenShaker;
 		_screenShaker = 0;
@@ -296,8 +298,29 @@ bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
 }
 
 bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
-	// TODO
-	return false;
+	bool result = false;
+	Control *overlappedControl;
+	if (_controls->getOverlappedWalkObject(control, control->_actor->_position, &overlappedControl)) {
+		if (_currWalkOverlappedControl != overlappedControl) {
+			_currWalkOverlappedControl = overlappedControl;
+			if (runTriggerCause(9, 0, overlappedControl->_objectId)) {
+				delete control->_actor->_pathNode;
+				control->_actor->_flags &= ~0x0400;
+				control->_actor->_pathNode = 0;
+				control->_actor->_pathPoints = 0;
+				control->_actor->_pathPointsCount = 0;
+				_threads->terminateThreadChain(control->_actor->_walkCallerThreadId1);
+				if (control->_actor->_notifyId3C) {
+					notifyThreadId(control->_actor->_notifyId3C);
+					control->_actor->_walkCallerThreadId1 = 0;
+				}
+				result = true;
+			}
+		}
+	} else {
+		_currWalkOverlappedControl = 0;
+	}
+	return result;
 }
 
 Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index da3036f..305a531 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -115,6 +115,7 @@ public:
 	uint32 _activeScenes[6];
 
 	Cursor_Duckman _cursor;
+	Control *_currWalkOverlappedControl;
 
 	Common::Array<DialogItem> _dialogItems;
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index f0688d3..4f2b58a 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS := \
 	illusions_duckman.o \
 	input.o \
 	midiresource.o \
+	pathfinder.o \
 	resourcesystem.o \
 	screen.o \
 	screentext.o \
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
new file mode 100644
index 0000000..a9a76e3
--- /dev/null
+++ b/engines/illusions/pathfinder.cpp
@@ -0,0 +1,338 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/pathfinder.h"
+
+namespace Illusions {
+
+PointArray *PathFinder::findPath(Common::Point sourcePt, Common::Point destPt,
+	PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions) {
+	_walkPoints = walkPoints;
+	_walkRects = walkRects;
+	_bgDimensions = bgDimensions;
+	return findPathInternal(sourcePt, destPt);
+}
+
+PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point destPt) {
+	PathLine line;
+	PointArray *foundPath = new PointArray();
+	line.p0 = sourcePt;
+	line.p1 = destPt;
+	
+	if (_walkRects && _walkPoints && isLineBlocked(line)) {
+		Common::Point nextStartPt = sourcePt, outPt;
+
+		if (!findValidDestLine(destPt)) {
+			findValidDestPt(destPt);
+			line.p1 = destPt;
+		}
+
+		_pathBytes = (byte*)calloc(1, _walkPoints->size());
+
+		bool done = false;
+		while (!done) {
+			line.p0 = nextStartPt;
+			if (!isLineBlocked(line)) {
+				foundPath->push_back(destPt);
+				done = true;
+			} else {
+				if (foundPath->size() < _walkPoints->size() + 2 && findClosestPt(nextStartPt, outPt, destPt)) {
+					foundPath->push_back(outPt);
+					nextStartPt = outPt;
+				} else {
+					if (foundPath->size() == 0)
+						foundPath->push_back(sourcePt);
+					done = true;
+				}
+			}
+		}
+
+		free(_pathBytes);
+		// TODO postProcess(sourcePt, foundPath);
+
+	} else {
+		foundPath->push_back(destPt);
+	}
+	return foundPath;
+}
+
+bool PathFinder::isLineBlocked(PathLine &line) {
+	for (uint i = 0; i < _walkRects->size(); ++i)
+		if (calcLineStatus(line, (*_walkRects)[i], 0) != 3)
+			return true;
+	return false;
+}
+
+int PathFinder::calcLineDistance(PathLine &line) {
+	int16 deltaX = line.p0.x - line.p1.x;
+	int16 deltaY = line.p0.y - line.p1.y;
+	if (deltaX != 0 || deltaY != 0)
+		return sqrt(deltaX * deltaX + deltaY * deltaY);
+	return 0;
+}
+
+bool PathFinder::findClosestPt(Common::Point &sourcePt, Common::Point &closestPt, Common::Point &destPt) {
+	PathLine sourceLine, destLine;
+	uint minIndex = 0;
+	int minDistance = 0xFFFF;
+	sourceLine.p0 = sourcePt;
+	destLine.p1 = destPt;
+	for (uint i = 0; i < _walkPoints->size(); ++i) {
+		sourceLine.p1 = (*_walkPoints)[i];
+		destLine.p0 = (*_walkPoints)[i];
+		if (!_pathBytes[i] && !isLineBlocked(sourceLine)) {
+			int currDistance = calcLineDistance(destLine);
+			if (currDistance <= minDistance) {
+				minDistance = currDistance;
+				minIndex = i + 1;
+			}
+		}
+	}
+	if (minIndex) {
+		closestPt = (*_walkPoints)[minIndex - 1];
+		_pathBytes[minIndex - 1] = 1;
+		return true;
+	}
+	return false;
+}
+
+bool PathFinder::findValidDestLine(Common::Point &destPt) {
+	PathLine destLine;
+	destLine.p0 = destPt;
+	for (uint i = 0; i < _walkPoints->size(); ++i) {
+		destLine.p1 = (*_walkPoints)[i];
+		if (!isLineBlocked(destLine))
+			return true;
+	}
+	return false;
+}
+
+void PathFinder::findValidDestPt(Common::Point &destPt) {
+	Common::Point minPt, outPt, deltaPt;
+	int minDistance = 0xFFFF, currDistance;
+	PathLine destLine;
+	for (uint i = 0; i < _walkRects->size(); ++i) {
+		PathLine &currRect = (*_walkRects)[i];
+		WidthHeight rectDimensions = calcRectDimensions(currRect);
+		adjustRectDimensions(rectDimensions);
+		clipLineToBg(destPt, rectDimensions, destLine);
+		if (calcLineStatus(destLine, currRect, &outPt) == 3) {
+			destLine.p0 = destPt;
+			destLine.p1 = currRect.p0;
+			currDistance = calcLineDistance(destLine);
+			if (currDistance < minDistance) {
+				minDistance = currDistance;
+				minPt = currRect.p0;
+			}
+			destLine.p0 = destPt;
+			destLine.p1 = currRect.p1;
+			currDistance = calcLineDistance(destLine);
+			if (currDistance < minDistance) {
+				minDistance = currDistance;
+				minPt = currRect.p1;
+			}
+		} else {
+			destLine.p0 = destPt;
+			destLine.p1 = outPt;
+			currDistance = calcLineDistance(destLine);
+			if (currDistance < minDistance) {
+				minDistance = currDistance;
+				minPt = outPt;
+			}
+		}
+	}
+	findDeltaPt(minPt, deltaPt);
+	destPt.x = deltaPt.x + minPt.x;
+	destPt.y = deltaPt.y + minPt.y;
+}
+
+WidthHeight PathFinder::calcRectDimensions(PathLine &rect) {
+	WidthHeight dimensions;
+	dimensions._width = rect.p1.x - rect.p0.x;
+	dimensions._height = rect.p1.y - rect.p0.y;
+	swapDimensions(dimensions);
+	return dimensions;
+}
+
+void PathFinder::adjustRectDimensions(WidthHeight &dimensions) {
+	dimensions._width = ABS(dimensions._height) * (dimensions._width < 0 ? -1 : 1);
+	dimensions._height = ABS(dimensions._width) * (dimensions._height < 0 ? -1 : 1);
+	if (dimensions._width)
+		dimensions._width = -dimensions._width;
+	else
+		dimensions._height = -dimensions._height;
+	swapDimensions(dimensions);
+}
+
+void PathFinder::swapDimensions(WidthHeight &dimensions) {
+	if (dimensions._width < 0) {
+		dimensions._width = -dimensions._width;
+		dimensions._height = -dimensions._height;
+	} else if (dimensions._width == 0)
+		dimensions._height = abs(dimensions._height);
+	else if (dimensions._height == 0)
+		dimensions._width = abs(dimensions._width);
+}
+
+void PathFinder::clipLineToBg(Common::Point &destPt, WidthHeight &rectDimensions, PathLine &outDestLine) {
+	if (rectDimensions._height == 0) {
+		outDestLine.p0.x = 0;
+		outDestLine.p0.y = destPt.y;
+		outDestLine.p1.x = _bgDimensions._width;
+		outDestLine.p1.y = destPt.y;
+	} else if (rectDimensions._width == 0) {
+		outDestLine.p0.y = 0;
+		outDestLine.p0.x = destPt.x;
+		outDestLine.p1.x = destPt.x;
+		outDestLine.p1.y = _bgDimensions._height;
+	} else {
+		outDestLine.p0 = destPt;
+		outDestLine.p1.x = destPt.x + rectDimensions._width;
+		outDestLine.p1.y = destPt.y + rectDimensions._height;
+		int16 y1 = destPt.y + (rectDimensions._height * -destPt.x / rectDimensions._width);
+		int16 y2 = destPt.y + (rectDimensions._height * (_bgDimensions._width - destPt.x) / rectDimensions._width);
+		int16 x1 = destPt.x + (rectDimensions._width * -destPt.y / rectDimensions._height);
+		int16 x2 = destPt.x + (rectDimensions._width * (_bgDimensions._height - destPt.y) / rectDimensions._height);
+		if (ABS(rectDimensions._height) <= ABS(rectDimensions._width)) {
+			outDestLine.p0.y = 0;
+			outDestLine.p0.x = _bgDimensions._width;
+			if (x1 < 0 || _bgDimensions._width < x1)
+				outDestLine.p0.y = y2;
+			else
+				outDestLine.p0.x = x1;
+			outDestLine.p1.x = 0;
+			outDestLine.p1.y = _bgDimensions._height;
+			if (x2 < 0 || _bgDimensions._width < x2)
+				outDestLine.p1.y = y1;
+			else
+				outDestLine.p1.x = x2;
+		} else {
+			outDestLine.p0.y = 0;
+			outDestLine.p0.x = 0;
+			if (x1 < 0 || _bgDimensions._width < x1)
+				outDestLine.p0.y = y1;
+			else
+				outDestLine.p0.x = x1;
+			outDestLine.p1.x = _bgDimensions._width;
+			outDestLine.p1.y = _bgDimensions._height;
+			if (x2 < 0 || _bgDimensions._width < x2)
+				outDestLine.p1.y = y2;
+			else
+				outDestLine.p1.x = x2;
+		}
+	}
+}
+
+void PathFinder::findDeltaPt(Common::Point pt, Common::Point &outDeltaPt) {
+	static const struct { int16 x, y; } kDeltaPoints[] = {
+		{ 0, -4}, {0, 4}, {-4, 0}, { 4,  0}, {-3, -3}, {3, 3}, {-3, 3}, { 3, -3},
+		{-2, -4}, {2, 4}, {-2, 4}, { 2, -4}, {-4, -2}, {4, 2}, {-4, 2}, { 4, -2},
+		{-1, -4}, {1, 4}, {-1, 4}, { 1, -4}, {-4, -1}, {4, 1}, {-4, 1}, { 4, -1},
+		{-2, -3}, {2, 3}, {-2, 3}, { 2, -3}, {-3, -2}, {3, 2}, {-3, 2}, { 3, -2}
+	};
+	Common::Point testPt;
+	for (uint i = 0; i < 32; ++i) {
+		testPt.x = pt.x + kDeltaPoints[i].x;
+		testPt.y = pt.y + kDeltaPoints[i].y;
+		if (findValidDestLine(testPt)) {
+			outDeltaPt.x = kDeltaPoints[i].x;
+			outDeltaPt.y = kDeltaPoints[i].y;
+			break;
+		}
+	}
+}
+
+bool PathFinder::testRect(PathLine &line, PathLine &rect) {
+	return line.p0.x <= rect.p1.x && line.p1.x >= rect.p0.x &&
+		line.p0.y <= rect.p1.y && line.p1.y >= rect.p0.y;
+}
+
+void PathFinder::swapLine(PathLine &line, PathLine &outLine) {
+	if (line.p1.x <= line.p0.x) {
+		outLine.p1.x = line.p0.x;
+		outLine.p0.x = line.p1.x;
+	} else {
+		outLine.p0.x = line.p0.x;
+		outLine.p1.x = line.p1.x;
+	}
+	if (line.p1.y <= line.p0.y) {
+		outLine.p1.y = line.p0.y;
+		outLine.p0.y = line.p1.y;
+	} else {
+		outLine.p0.y = line.p0.y;
+		outLine.p1.y = line.p1.y;
+	}
+}
+
+int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common::Point *outPoint) {
+	PathLine sourceLine1, destRect1;
+	swapLine(sourceLine, sourceLine1);
+	swapLine(destRect, destRect1);
+
+	if (!testRect(sourceLine1, destRect1))
+		return 3;
+
+	int sourceDeltaX = sourceLine.p1.x - sourceLine.p0.x;
+	int sourceDeltaY = sourceLine.p1.y - sourceLine.p0.y;
+	int destDeltaX = destRect.p0.x - destRect.p1.x;
+	int destDeltaY = destRect.p0.y - destRect.p1.y;
+	int sdDeltaX = sourceLine.p0.x - destRect.p0.x;
+	int sdDeltaY = sourceLine.p0.y - destRect.p0.y;
+	int delta1 = destDeltaY * sdDeltaX - destDeltaX * sdDeltaY;
+	int delta2 = sourceDeltaY * destDeltaX - sourceDeltaX * destDeltaY;
+	int delta3 = sourceDeltaX * sdDeltaY - sourceDeltaY * sdDeltaX;
+
+	if ((delta2 <= 0 && (delta1 > 0 || delta2 > delta1)) ||
+		(delta2 > 0 && (delta1 < 0 || delta2 < delta1)) ||
+		(delta2 <= 0 && (delta3 > 0 || delta2 > delta3)) ||
+		(delta2 > 0 && (delta3 < 0 || delta2 < delta3)))
+		return 3;
+	
+	if (!outPoint)
+		return 1;
+
+	if (delta2 == 0)
+		return 2;
+
+	int v15 = sourceDeltaX * delta1, v18 = sourceDeltaY * delta1;
+	int v16, v17;
+	
+	if ((v15 >= 0 && delta2 >= 0) || (v15 < 0 && delta2 < 0)) {
+		v16 = delta2 / 2;
+		v17 = delta2 / 2;
+	} else if ((v15 < 0 && delta2 >= 0) || (v15 >= 0 && delta2 < 0)) {
+		v17 = delta2 / 2;
+		v16 = delta2 / -2;
+	}
+
+	outPoint->x = sourceLine.p0.x + (v15 + v16) / delta2;
+	
+	if ((v18 >= 0 && delta2 < 0) || (v18 < 0 && delta2 >= 0))
+		v17 = -v17;
+
+	outPoint->y = sourceLine.p0.y + (v18 + v17) / delta2;
+	
+	return 1;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/pathfinder.h b/engines/illusions/pathfinder.h
new file mode 100644
index 0000000..ca402bb
--- /dev/null
+++ b/engines/illusions/pathfinder.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_PATHFINDER_H
+#define ILLUSIONS_PATHFINDER_H
+
+#include "illusions/graphics.h"
+#include "common/array.h"
+#include "common/list.h"
+#include "common/rect.h"
+
+namespace Illusions {
+
+struct PathLine {
+	Common::Point p0, p1;
+};
+
+typedef Common::Array<PathLine> PathLines;
+typedef Common::Array<Common::Point> PointArray;
+
+class PathFinder {
+public:
+	PointArray *findPath(Common::Point sourcePt, Common::Point destPt,
+		PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions);
+protected:
+	PointArray *_walkPoints;
+	PathLines *_walkRects;
+	WidthHeight _bgDimensions;
+	byte *_pathBytes;
+	PointArray *findPathInternal(Common::Point sourcePt, Common::Point destPt);
+	bool isLineBlocked(PathLine &line);
+	int calcLineDistance(PathLine &line);
+	bool findClosestPt(Common::Point &sourcePt, Common::Point &closestPt, Common::Point &destPt);
+	bool findValidDestLine(Common::Point &destPt);
+	void findValidDestPt(Common::Point &destPt);
+	WidthHeight calcRectDimensions(PathLine &rect);
+	void adjustRectDimensions(WidthHeight &dimensions);	
+	void swapDimensions(WidthHeight &dimensions);
+	void clipLineToBg(Common::Point &destPt, WidthHeight &rectDimensions, PathLine &outDestLine);
+	void findDeltaPt(Common::Point pt, Common::Point &outDeltaPt);
+	bool testRect(PathLine &line, PathLine &rect);
+	void swapLine(PathLine &line, PathLine &outLine);
+	int calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common::Point *outPoint);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_PATHFINDER_H
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 1caf3b8..971a8ee 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -200,7 +200,7 @@ void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCal
 		duration += _vm->getRandom(maxDuration);
 		
 //duration = 1;//DEBUG Speeds up things
-duration = 5;		
+duration = 5;
 		
 	if (isAbortable)
 		_vm->startAbortableTimerThread(duration, opCall._threadId);
@@ -244,7 +244,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
+//static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 //static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
@@ -252,8 +252,10 @@ static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
 //static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
-//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
+//static uint dsceneId = 0x00010022, dthreadId = 0x00020114;
+//static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -645,7 +647,7 @@ void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall)
 void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
+	_vm->_stack->push(_vm->getPrevScene() == sceneId ? 1 : 0);
 }
 
 void ScriptOpcodes_Duckman::opNot(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index d508eea..7783972 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -284,7 +284,7 @@ void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkPointsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
 	control->_actor->_flags |= 2;
-	// TODO control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
+	control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
 }
 
 void SequenceOpcodes::opDisableAutoScale(Control *control, OpCall &opCall) {
@@ -315,7 +315,7 @@ void SequenceOpcodes::opSetPathWalkRects(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkRectsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
 	control->_actor->_flags |= 0x0010;
-	// TODO control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
+	control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
 }
 
 void SequenceOpcodes::opSetPriority(Control *control, OpCall &opCall) {
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 7ecc3d8..6c6a7d6 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -277,6 +277,14 @@ void ThreadList::endTalkThreadsNoNotify() {
 	}
 }
 
+void ThreadList::terminateThreadChain(uint32 threadId) {
+	while (threadId) {
+		Thread *thread = findThread(threadId);
+		thread->terminate();
+		threadId = thread->_callingThreadId;
+	}
+}
+
 void ThreadList::killThread(uint32 threadId) {
 
 	if (!threadId)
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index 3a335a2..996226d 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -97,6 +97,7 @@ public:
 	void resumeThreads(uint32 threadId);
 	void endTalkThreads();
 	void endTalkThreadsNoNotify();
+	void terminateThreadChain(uint32 threadId);
 	void killThread(uint32 threadId);
 	void setThreadSceneId(uint32 threadId, uint32 sceneId);
 	uint32 getThreadSceneId(uint32 threadId);


Commit: d431d3521480d43fa646a5cd84c6a10d0d24843a
    https://github.com/scummvm/scummvm/commit/d431d3521480d43fa646a5cd84c6a10d0d24843a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Add microphone radar

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/screen.cpp
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_bbdou.h


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 8011b3e..cb4f32a 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -68,6 +68,41 @@ void CauseThread::onTerminated() {
 	_bbdou->_cursor->enable(_cursorObjectId);
 }
 
+// RadarMicrophoneThread
+RadarMicrophoneThread::RadarMicrophoneThread(IllusionsEngine_BBDOU *vm, uint32 threadId,
+	uint32 callingThreadId, uint32 cursorObjectId)
+	: Thread(vm, threadId, callingThreadId, 0), _cursorObjectId(cursorObjectId), _zonesCount(0) {
+	_tag = _vm->getCurrentScene();
+}
+
+int RadarMicrophoneThread::onUpdate() {
+	Control *control = _vm->getObjectControl(_cursorObjectId);
+	int16 cursorX = control->getActorPosition().x;
+	if (_currZoneIndex == 0 ||
+		cursorX >= _zones[_currZoneIndex - 1]._x ||
+		(_currZoneIndex >= 2 && cursorX < _zones[_currZoneIndex - 2]._x)) {//CHECKME
+		for (uint i = 0; i < _zonesCount; ++i) {
+			if (cursorX < _zones[i]._x) {
+				_currZoneIndex = i + 1;
+				_vm->startScriptThreadSimple(_zones[i]._threadId, 0);
+				break;
+			}
+		}
+	}
+	return kTSYield;
+}
+
+void RadarMicrophoneThread::addZone(uint32 threadId) {
+	_zones[_zonesCount++]._threadId = threadId;
+}
+
+void RadarMicrophoneThread::initZones() {
+	for (uint i = 0; i < _zonesCount; ++i)
+		_zones[i]._x = (i + 1) * 640 / _zonesCount;
+	_zones[_zonesCount]._x = 640;
+	_currZoneIndex = 0;
+}
+
 // BbdouSpecialCode
 
 BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine_BBDOU *vm)
@@ -105,7 +140,9 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001E, spcRemoveInventoryItem);
 	SPECIAL(0x0016001F, spcHasInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
+	SPECIAL(0x00160032, spcSetCursorField90);
 	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
+	SPECIAL(0x00160038, spcInitRadarMicrophone);
 	SPECIAL(0x0016003A, spcSaladCtl);
 }
 
@@ -229,6 +266,12 @@ void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 	_inventory->close();
 }
 
+void BbdouSpecialCode::spcSetCursorField90(OpCall &opCall) {
+	ARG_SKIP(4); // objectId unused
+	_cursor->_data._field90 = 1;
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void BbdouSpecialCode::spcIsCursorHoldingObjectId(OpCall &opCall) {
 	ARG_UINT32(cursorObjectId);
 	ARG_UINT32(objectId);
@@ -236,6 +279,21 @@ void BbdouSpecialCode::spcIsCursorHoldingObjectId(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void BbdouSpecialCode::spcInitRadarMicrophone(OpCall &opCall) {
+	ARG_UINT32(cursorObjectId);
+	uint32 tempThreadId = _vm->newTempThreadId();
+	RadarMicrophoneThread *radarMicrophoneThread = new RadarMicrophoneThread(_vm,
+		tempThreadId, opCall._callerThreadId, cursorObjectId);
+	for (uint i = 0; i < 7; ++i) {
+		ARG_UINT32(zoneThreadId);
+		if (zoneThreadId == 0)
+			break;
+		radarMicrophoneThread->addZone(zoneThreadId);
+	}
+	radarMicrophoneThread->initZones();
+	_vm->_threads->startThread(radarMicrophoneThread);
+}
+
 void BbdouSpecialCode::spcSaladCtl(OpCall &opCall) {
 	ARG_UINT32(cmd);
 	ARG_UINT32(sequenceId);
@@ -280,7 +338,7 @@ bool BbdouSpecialCode::testValueRange(int value) {
 }
 
 void BbdouSpecialCode::setCursorControlRoutine(uint32 objectId, int num) {
-	Control *control = _vm->_dict->getObjectControl(objectId);
+	Control *control = _vm->getObjectControl(objectId);
 	if (num == 0)
 		control->_actor->setControlRoutine(
 			new Common::Functor2Mem<Control*, uint32, void, BbdouSpecialCode>(this, &BbdouSpecialCode::cursorInteractControlRoutine));
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index b9080c9..037d475 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -63,6 +63,25 @@ public:
 	uint32 _objectId;
 };
 
+struct RadarMicrophoneZone {
+	int16 _x;
+	uint32 _threadId;
+};
+
+class RadarMicrophoneThread : public Thread {
+public:
+	RadarMicrophoneThread(IllusionsEngine_BBDOU *vm, uint32 threadId,
+		uint32 callingThreadId, uint32 cursorObjectId);
+	virtual int onUpdate();
+	void addZone(uint32 threadId);
+	void initZones();
+public:
+	uint32 _cursorObjectId;
+	uint _zonesCount;
+	uint _currZoneIndex;
+	RadarMicrophoneZone _zones[8];
+};
+
 class BbdouSpecialCode : public SpecialCode {
 public:
 	BbdouSpecialCode(IllusionsEngine_BBDOU *vm);
@@ -100,7 +119,9 @@ public:
 	void spcRemoveInventoryItem(OpCall &opCall);
 	void spcHasInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
+	void spcSetCursorField90(OpCall &opCall);
 	void spcIsCursorHoldingObjectId(OpCall &opCall);
+	void spcInitRadarMicrophone(OpCall &opCall);
 	void spcSaladCtl(OpCall &opCall);
 
 	void playSoundEffect(int soundIndex);
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index cd5ca68..076906e 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -336,15 +336,17 @@ void Screen::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Comm
 }
 
 void Screen::setPalette(byte *colors, uint start, uint count) {
-	byte *dstPal = &_mainPalette[3 * (start - 1)];
-	for (uint i = 0; i < count; ++i) {
-		*dstPal++ = *colors++;
-		*dstPal++ = *colors++;
-		*dstPal++ = *colors++;
-		++colors;
+	if (_backSurface->format.bytesPerPixel == 1) {
+		byte *dstPal = &_mainPalette[3 * (start - 1)];
+		for (uint i = 0; i < count; ++i) {
+			*dstPal++ = *colors++;
+			*dstPal++ = *colors++;
+			*dstPal++ = *colors++;
+			++colors;
+		}
+		buildColorTransTbl();
+		_needRefreshPalette = true;
 	}
-	buildColorTransTbl();
-	_needRefreshPalette = true;
 }
 
 void Screen::setPaletteEntry(int16 index, byte r, byte g, byte b) {
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index b566f52..ff5c98f 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -63,6 +63,7 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(6, opStartScriptThread);
 	OPCODE(8, opStartTempScriptThread);
 	OPCODE(9, opStartTimerThread);
+	OPCODE(12, opNotifyThreadId);
 	OPCODE(14, opSetThreadSceneId);
 	OPCODE(15, opEndTalkThreads);
 	OPCODE(16, opLoadResource);
@@ -191,6 +192,12 @@ duration = 1;//DEBUG Speeds up things
 		_vm->startTimerThread(duration, opCall._threadId);
 }
 
+void ScriptOpcodes_BBDOU::opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	Thread *thread = _vm->_threads->findThread(opCall._callerThreadId);
+	if (!(thread->_notifyFlags & 1))
+		_vm->notifyThreadId(thread->_callingThreadId);
+}
+
 void ScriptOpcodes_BBDOU::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -240,7 +247,8 @@ void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCal
 //uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
 //uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
 //uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
-uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
 
 void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/scriptopcodes_bbdou.h b/engines/illusions/scriptopcodes_bbdou.h
index dbbc325..b132011 100644
--- a/engines/illusions/scriptopcodes_bbdou.h
+++ b/engines/illusions/scriptopcodes_bbdou.h
@@ -48,6 +48,7 @@ protected:
 	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);


Commit: d57ae261791f17c8c83f51c8327d908b32afad3b
    https://github.com/scummvm/scummvm/commit/d57ae261791f17c8c83f51c8327d908b32afad3b
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add SoundMan and MusicPlayer

- Add remaining screen shake effects
- Implement Chinese dialog puzzle in Duckman

Changed paths:
  A engines/illusions/sound.cpp
  A engines/illusions/sound.h
    engines/illusions/actor.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/module.mk
    engines/illusions/screentext.cpp
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/talkthread_duckman.cpp
    engines/illusions/talkthread_duckman.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 31f3a28..7890d02 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -770,7 +770,7 @@ PointArray *Control::createPath(Common::Point destPt) {
 	WidthHeight bgDimensions = _vm->_backgroundItems->getMasterBgDimensions();
 	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
 	for (uint i = 0; i < path->size(); ++i) {
-		debug("Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
+		debug(0, "Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
 	}
 	return path;
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 5031f5e..1260714 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -35,6 +35,7 @@
 #include "illusions/screentext.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
+#include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
 #include "illusions/talkresource.h"
@@ -204,6 +205,11 @@ int IllusionsEngine::updateSprites(uint flags) {
 	return 1;
 }
 
+int IllusionsEngine::updateSoundMan(uint flags) {
+	_soundMan->update();
+	return 1;
+}
+
 int IllusionsEngine::getRandom(int max) {
 	return _random->getRandomNumber(max - 1);
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 7ea49fd..1a1f542 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -70,6 +70,7 @@ class ScriptOpcodes;
 class ScriptResource;
 class ScriptStack;
 class Sequence;
+class SoundMan;
 class SpecialCode;
 class TalkItems;
 class ThreadList;
@@ -113,6 +114,7 @@ public:
 	ScriptOpcodes *_scriptOpcodes;
 	SpecialCode *_specialCode;
 	ThreadList *_threads;
+	SoundMan *_soundMan;
 	
 	uint32 _nextTempThreadId;
 	bool _doScriptThreadInit;
@@ -146,6 +148,7 @@ public:
 	int updateSequences(uint flags);
 	int updateGraphics(uint flags);
 	int updateSprites(uint flags);
+	int updateSoundMan(uint flags);
 
 	uint32 getElapsedUpdateTime();
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index f2cb4fd..658732d 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -36,6 +36,7 @@
 #include "illusions/scriptopcodes_bbdou.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
+#include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
@@ -216,6 +217,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_triggerFunctions = new TriggerFunctions();
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
+	_soundMan = new SoundMan(this);
 
 	initUpdateFunctions();
 
@@ -254,6 +256,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
+    delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
 	delete _triggerFunctions;
@@ -295,6 +298,7 @@ void IllusionsEngine_BBDOU::initUpdateFunctions() {
 	UPDATEFUNCTION(60, 0, updateSequences);
 	UPDATEFUNCTION(70, 0, updateGraphics);
 	UPDATEFUNCTION(90, 0, updateSprites);
+	UPDATEFUNCTION(120, 0, updateSoundMan);
 }
 
 #undef UPDATEFUNCTION
@@ -376,7 +380,15 @@ Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId)
 		_controls->findNamedPoint(namedPointId, pt))
 		return pt;
 	// TODO
-	//debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+	switch (namedPointId) {
+	case 0x70001:
+		return Common::Point(0, 0);
+	case 0x70002:
+		return Common::Point(640, 0);
+	case 0x70023:
+		return Common::Point(320, 240);
+	}
+	debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
 	return Common::Point(0, 0);
 }
 
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 3f32ad4..3a87b09 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -37,6 +37,7 @@
 #include "illusions/scriptopcodes_duckman.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptman.h"
+#include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
 //TODO#include "illusions/bbdou/bbdou_specialcode.h"
@@ -105,6 +106,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_talkItems = new TalkItems(this);
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
+	_soundMan = new SoundMan(this);
 
 	_fader = new Fader();
 
@@ -142,6 +144,15 @@ Common::Error IllusionsEngine_Duckman::run() {
 	startScriptThread(0x00020004, 0);
 	_doScriptThreadInit = true;
 
+	//DEBUG
+	_scriptResource->_properties.set(0x000E003A, true);
+	_scriptResource->_properties.set(0x000E0020, true);
+	_scriptResource->_properties.set(0x000E003A, true);
+	_scriptResource->_properties.set(0x000E003B, true);
+	_scriptResource->_properties.set(0x000E0009, true);
+	_scriptResource->_properties.set(0x000E003D, true);
+	_scriptResource->_properties.set(0x000E0024, true);
+
 	while (!shouldQuit()) {
 		runUpdateFunctions();
 		_system->updateScreen();
@@ -154,6 +165,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _fader;
 
+    delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
 	delete _talkItems;
@@ -192,6 +204,7 @@ void IllusionsEngine_Duckman::initUpdateFunctions() {
 	UPDATEFUNCTION(60, 0, updateSequences);
 	UPDATEFUNCTION(70, 0, updateGraphics);
 	UPDATEFUNCTION(90, 0, updateSprites);
+	UPDATEFUNCTION(120, 0, updateSoundMan);
 }
 
 #undef UPDATEFUNCTION
@@ -1206,6 +1219,8 @@ typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeF
 void IllusionsEngine_Duckman::initSpecialCode() {
 	SPECIAL(0x00160001, spcStartScreenShaker);
 	SPECIAL(0x00160002, spcSetCursorHandMode);
+	SPECIAL(0x00160003, spcResetChinesePuzzle);
+	SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
 	SPECIAL(0x00160005, spcOpenInventory);
 	SPECIAL(0x00160007, spcPutBackInventoryItem);
 	SPECIAL(0x00160008, spcClearInventorySlot);
@@ -1226,6 +1241,8 @@ void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCal
 	}
 }
 
+// TODO Move to separate file
+
 static const ScreenShakerPoint kShakerPoints0[] = {
 	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
 };
@@ -1234,15 +1251,64 @@ static const ScreenShakeEffect kShakerEffect0 = {
 	6, 5, kShakerPoints0
 };
 
-static const ScreenShakeEffect *kShakerEffects = {
-	&kShakerEffect0
+static const ScreenShakerPoint kShakerPoints1[] = {
+	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
+	{ 1,  2}, {0, -1} 
+};
+
+static const ScreenShakeEffect kShakerEffect1 = {
+	9, 2, kShakerPoints1
+};
+
+static const ScreenShakerPoint kShakerPoints2[] = {
+	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
+	{0,  1}, {0, -1},
+};
+
+static const ScreenShakeEffect kShakerEffect2 = {
+	9, 2, kShakerPoints2
+};
+
+static const ScreenShakerPoint kShakerPoints3[] = {
+	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect3 = {
+	5, 2, kShakerPoints3
+};
+
+static const ScreenShakerPoint kShakerPoints4[] = {
+	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
+};
+
+static const ScreenShakeEffect kShakerEffect4 = {
+	8, 5, kShakerPoints4
+};
+
+static const ScreenShakerPoint kShakerPoints5[] = {
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect5 = {
+	31, 2, kShakerPoints5
+};
+
+static const ScreenShakeEffect *kShakerEffects[] = {
+	&kShakerEffect0,
+	&kShakerEffect1,
+	&kShakerEffect2,
+	&kShakerEffect3,
+	&kShakerEffect4,
+	&kShakerEffect5
 };
 
 void IllusionsEngine_Duckman::spcStartScreenShaker(OpCall &opCall) {
-	// TODO Add more effects
 	ARG_BYTE(effect);
 	debug("### effect: %d", effect);
-	const ScreenShakeEffect *shakerEffect = &kShakerEffects[effect];
+	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
 	startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
 }
 
@@ -1252,6 +1318,28 @@ void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
 	notifyThreadId(opCall._threadId);
 }
 
+void IllusionsEngine_Duckman::spcResetChinesePuzzle(OpCall &opCall) {
+	_scriptResource->_properties.set(0x000E0018, false);
+	_scriptResource->_properties.set(0x000E0019, false);
+	_chinesePuzzleIndex = 0;
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcAddChinesePuzzleAnswer(OpCall &opCall) {
+	ARG_BYTE(answer);
+	_chinesePuzzleAnswers[_chinesePuzzleIndex++] = answer;
+	if (_chinesePuzzleIndex == 3) {
+		_scriptResource->_properties.set(0x000E0018, true);
+		if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 5) ||
+			(_chinesePuzzleAnswers[0] == 5 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
+			_scriptResource->_properties.set(0x000E0019, true);
+		else if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 1) ||
+			(_chinesePuzzleAnswers[0] == 1 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
+			_scriptResource->_properties.set(0x000E00A0, true);
+	}
+	notifyThreadId(opCall._threadId);
+}
+
 void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
 	openInventory();
 	notifyThreadId(opCall._threadId);
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 305a531..48d8ef2 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -125,6 +125,9 @@ public:
 
 	ScreenShaker *_screenShaker;
 
+	uint _chinesePuzzleIndex;
+	byte _chinesePuzzleAnswers[3];
+
 	SpecialCodeMap _specialCodeMap;
 
 	void initUpdateFunctions();
@@ -146,7 +149,7 @@ public:
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	uint32 getPriorityFromBase(int16 priority);
 	uint32 getCurrentScene();
-	uint32 getPrevScene();	
+	uint32 getPrevScene();
 
 	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
 	void setCursorControlRoutine(Control *control);
@@ -222,6 +225,8 @@ public:
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
 	void spcStartScreenShaker(OpCall &opCall);
 	void spcSetCursorHandMode(OpCall &opCall);
+	void spcResetChinesePuzzle(OpCall &opCall);
+	void spcAddChinesePuzzleAnswer(OpCall &opCall);
 	void spcOpenInventory(OpCall &opCall);
 	void spcPutBackInventoryItem(OpCall &opCall);
 	void spcClearInventorySlot(OpCall &opCall);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 4f2b58a..1bdec55 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -33,6 +33,7 @@ MODULE_OBJS := \
 	scriptresource.o \
 	scriptthread.o \
 	sequenceopcodes.o \
+	sound.o \
 	soundresource.o \
 	specialcode.o \
 	talkresource.o \
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 60fc901..37c52c0 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -110,7 +110,8 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
 		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
 		outTextPtr);
-	//_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
+	debug("font->getColorIndex(): %d", font->getColorIndex());
+	_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 
 	uint16 *textPart = screenText->_text;
 	while (text != outTextPtr)
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index ff5c98f..30cd023 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -30,6 +30,7 @@
 #include "illusions/scriptman.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
+#include "illusions/sound.h"
 #include "illusions/specialcode.h"
 #include "illusions/talkresource.h"
 
@@ -526,11 +527,11 @@ void ScriptOpcodes_BBDOU::opStartMusic(ScriptThread *scriptThread, OpCall &opCal
 	ARG_INT16(pan);
 	ARG_UINT32(musicId);
 	ARG_UINT32(type);
-	// TODO _vm->playMusic(musicId, type, volume, pan);
+	_vm->_soundMan->playMusic(musicId, type, volume, pan, opCall._threadId);
 }
 
 void ScriptOpcodes_BBDOU::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	// TODO _vm->stopMusic();
+	_vm->_soundMan->stopMusic();
 }
 
 void ScriptOpcodes_BBDOU::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 971a8ee..a1e5e1c 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -244,18 +244,20 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
+//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+//static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
-//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
-//static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
-//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Pizza
-//static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
-//static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
-static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
-//static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 //static uint dsceneId = 0x00010022, dthreadId = 0x00020114;
+//static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
+static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
+//static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
+//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 //static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
+//static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
+//static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
+
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
new file mode 100644
index 0000000..632b842
--- /dev/null
+++ b/engines/illusions/sound.cpp
@@ -0,0 +1,101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/sound.h"
+
+namespace Illusions {
+
+// MusicPlayer
+
+MusicPlayer::MusicPlayer(Audio::Mixer *mixer)
+	: _mixer(mixer), _musicId(0), _flags(0) {
+	_flags = 1; // TODO?
+}
+
+MusicPlayer::~MusicPlayer() {
+}
+
+void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
+	debug("MusicPlayer::play(%08X)", musicId);
+	if (_flags & 1) {
+		stop();
+		_musicId = musicId;
+		Common::String filename = Common::String::format("%08x.wav", _musicId);
+		_flags |= 2;
+		_flags &= ~4;
+		if (looping) {
+			_flags |= 8;
+		} else {
+			_flags &= ~8;
+		}
+		Common::File *fd = new Common::File();
+		fd->open(filename);
+		Audio::AudioStream *audioStream = Audio::makeLoopingAudioStream(Audio::makeWAVStream(fd, DisposeAfterUse::YES), looping ? 0 : 1);
+		_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, audioStream, -1, volume, pan);
+	}
+}
+
+void MusicPlayer::stop() {
+	debug("MusicPlayer::stop()");
+	if ((_flags & 1) && (_flags & 2)) {
+		if (_mixer->isSoundHandleActive(_soundHandle))
+			_mixer->stopHandle(_soundHandle);
+		_flags &= ~2;
+		_flags &= ~4;
+		_flags &= ~8;
+		_musicId = 0;
+	}
+}
+
+bool MusicPlayer::isPlaying() {
+	return (_flags & 1) && (_flags & 2) && _mixer->isSoundHandleActive(_soundHandle);
+}
+
+// SoundMan
+
+SoundMan::SoundMan(IllusionsEngine *vm)
+	: _vm(vm), _musicNotifyThreadId(0) {
+	_musicPlayer = new MusicPlayer(_vm->_mixer);
+}
+
+SoundMan::~SoundMan() {
+	delete _musicPlayer;
+}
+
+void SoundMan::update() {
+	// TODO voc_testCued();
+	if (_musicNotifyThreadId && !_musicPlayer->isPlaying())
+		_vm->notifyThreadId(_musicNotifyThreadId);
+}
+
+void SoundMan::playMusic(uint32 musicId, int16 type, int16 volume, int16 pan, uint32 notifyThreadId) {
+	_vm->notifyThreadId(_musicNotifyThreadId);
+	_musicPlayer->play(musicId, type == 2, volume, pan);
+	_musicNotifyThreadId = notifyThreadId;
+}
+
+void SoundMan::stopMusic() {
+	_musicPlayer->stop();
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
new file mode 100644
index 0000000..e5139d3
--- /dev/null
+++ b/engines/illusions/sound.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SOUND_H
+#define ILLUSIONS_SOUND_H
+
+#include "illusions/graphics.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "audio/decoders/wave.h"
+#include "common/list.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class MusicPlayer {
+public:
+	MusicPlayer(Audio::Mixer *mixer);
+	~MusicPlayer();
+	void play(uint32 musicId, bool looping, int16 volume, int16 pan);
+	void stop();
+	bool isPlaying();
+protected:
+	Audio::Mixer *_mixer;
+	Audio::SoundHandle _soundHandle;
+	uint32 _musicId;
+	uint _flags;
+};
+
+class SoundMan {
+public:
+	SoundMan(IllusionsEngine *vm);
+	~SoundMan();
+	void update();
+	void playMusic(uint32 musicId, int16 type, int16 volume, int16 pan, uint32 notifyThreadId);
+	void stopMusic();
+protected:
+	IllusionsEngine *_vm;
+	uint32 _musicNotifyThreadId;
+	MusicPlayer *_musicPlayer;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SOUND_H
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
index 8c29071..990d1be 100644
--- a/engines/illusions/talkthread_duckman.cpp
+++ b/engines/illusions/talkthread_duckman.cpp
@@ -113,7 +113,7 @@ int TalkThread_Duckman::onUpdate() {
 	case 4:
 		if (!(_flags & 8) ) {
 			uint32 actorTypeId = _vm->getObjectActorTypeId(_objectId);
-			// TODO getActorTypeColor(actorTypeId, &_colorR, &_colorG, &_colorB);
+			getActorTypeColor(actorTypeId, _color);
 			refreshText();
 		}
 		if (!(_flags & 2)) {
@@ -289,7 +289,8 @@ int TalkThread_Duckman::insertText() {
 	WidthHeight dimensions;
 	_vm->getDefaultTextDimensions(dimensions);
 	uint16 *outTextPtr;
-	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions, Common::Point(0, 0), 2, 0, 0, _colorR, _colorG, _colorB, outTextPtr);
+	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
+		Common::Point(0, 0), 2, 0, 0, _color.r, _color.r, _color.r, outTextPtr);
 	_entryText = (byte*)outTextPtr;
 	Common::Point pt;
 	_vm->getDefaultTextPosition(pt);
@@ -303,4 +304,9 @@ TalkEntry *TalkThread_Duckman::getTalkResourceEntry(uint32 talkId) {
 	return talkEntry;
 }
 
+void TalkThread_Duckman::getActorTypeColor(uint32 actorTypeId, RGB &color) {
+	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
+	color = actorType->_color;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/talkthread_duckman.h b/engines/illusions/talkthread_duckman.h
index 656dab7..b729ad2 100644
--- a/engines/illusions/talkthread_duckman.h
+++ b/engines/illusions/talkthread_duckman.h
@@ -75,10 +75,11 @@ public:
 	uint32 _voiceDuration;
 	uint32 _voiceDurationElapsed;
 	int *_pauseCtrPtr;
-	byte _colorR, _colorG, _colorB;
+	RGB _color;
 	void refreshText();
 	int insertText();
 	TalkEntry *getTalkResourceEntry(uint32 talkId);
+	void getActorTypeColor(uint32 actorTypeId, RGB &color);
 };
 
 } // End of namespace Illusions


Commit: 9885a050f2e9b718e3fb7bab224dcfdc18292231
    https://github.com/scummvm/scummvm/commit/9885a050f2e9b718e3fb7bab224dcfdc18292231
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement VoicePlayer

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/sound.cpp
    engines/illusions/sound.h
    engines/illusions/talkthread.cpp
    engines/illusions/talkthread_duckman.cpp


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 1260714..c58eb85 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -257,32 +257,9 @@ void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority
 
 bool IllusionsEngine::isSoundActive() {
 	// TODO
-	return false;
-}
-
-bool IllusionsEngine::cueVoice(byte *voiceName) {
-	// TODO
 	return true;
 }
 
-bool IllusionsEngine::isVoiceCued() {
-	// TODO
-	return false;
-}
-
-void IllusionsEngine::startVoice(int volume, int panX) {
-	// TODO
-}
-
-void IllusionsEngine::stopVoice() {
-	// TODO
-}
-
-bool IllusionsEngine::isVoicePlaying() {
-	// TODO
-	return false;
-}
-
 void IllusionsEngine::updateFader() {
 	if (_fader && !_fader->_paused && _fader->_active) {
 		int32 currTime = getCurrentTime();
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 1a1f542..522d1dc 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -157,11 +157,6 @@ public:
 	bool calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing);
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
 	bool isSoundActive();
-	bool cueVoice(byte *voiceName);
-	bool isVoiceCued();
-	void startVoice(int volume, int panX);
-	void stopVoice();
-	bool isVoicePlaying();
 
 	void updateFader();
 
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 632b842..0f60e5d 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -33,6 +33,7 @@ MusicPlayer::MusicPlayer(Audio::Mixer *mixer)
 }
 
 MusicPlayer::~MusicPlayer() {
+	stop();
 }
 
 void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
@@ -40,7 +41,6 @@ void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
 	if (_flags & 1) {
 		stop();
 		_musicId = musicId;
-		Common::String filename = Common::String::format("%08x.wav", _musicId);
 		_flags |= 2;
 		_flags &= ~4;
 		if (looping) {
@@ -48,6 +48,7 @@ void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
 		} else {
 			_flags &= ~8;
 		}
+		Common::String filename = Common::String::format("%08x.wav", _musicId);
 		Common::File *fd = new Common::File();
 		fd->open(filename);
 		Audio::AudioStream *audioStream = Audio::makeLoopingAudioStream(Audio::makeWAVStream(fd, DisposeAfterUse::YES), looping ? 0 : 1);
@@ -71,15 +72,71 @@ bool MusicPlayer::isPlaying() {
 	return (_flags & 1) && (_flags & 2) && _mixer->isSoundHandleActive(_soundHandle);
 }
 
+// VoicePlayer
+
+VoicePlayer::VoicePlayer(Audio::Mixer *mixer)
+	: _mixer(mixer) {
+}
+
+VoicePlayer::~VoicePlayer() {
+	stop();
+}
+
+bool VoicePlayer::cue(const char *voiceName) {
+debug("VoicePlayer::cue(%s)", voiceName);
+	_voiceName = voiceName;
+	_voiceStatus = 2;
+	if (!isEnabled()) {
+		_voiceStatus = 3;
+		return false;
+	}
+	return true;
+}
+
+void VoicePlayer::stopCueing() {
+	_voiceStatus = 3;
+}
+
+void VoicePlayer::start(int16 volume, int16 pan) {
+	Common::String filename = Common::String::format("%s.wav", _voiceName.c_str());
+	Common::File *fd = new Common::File();
+	fd->open(filename);
+	Audio::AudioStream *audioStream = Audio::makeWAVStream(fd, DisposeAfterUse::YES);
+	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, audioStream, -1, volume, pan);
+	_voiceStatus = 4;
+}
+
+void VoicePlayer::stop() {
+	if (_mixer->isSoundHandleActive(_soundHandle))
+		_mixer->stopHandle(_soundHandle);
+	_voiceStatus = 1;
+	_voiceName.clear();
+}
+
+bool VoicePlayer::isPlaying() {
+	return _mixer->isSoundHandleActive(_soundHandle);
+}
+
+bool VoicePlayer::isEnabled() {
+	// TODO
+	return true;
+}
+
+bool VoicePlayer::isCued() {
+	return _voiceStatus == 2;
+}
+
 // SoundMan
 
 SoundMan::SoundMan(IllusionsEngine *vm)
 	: _vm(vm), _musicNotifyThreadId(0) {
 	_musicPlayer = new MusicPlayer(_vm->_mixer);
+	_voicePlayer = new VoicePlayer(_vm->_mixer);
 }
 
 SoundMan::~SoundMan() {
 	delete _musicPlayer;
+	delete _voicePlayer;
 }
 
 void SoundMan::update() {
@@ -98,4 +155,32 @@ void SoundMan::stopMusic() {
 	_musicPlayer->stop();
 }
 
+bool SoundMan::cueVoice(const char *voiceName) {
+	return _voicePlayer->cue(voiceName);
+}
+
+void SoundMan::stopCueingVoice() {
+	_voicePlayer->stopCueing();
+}
+
+void SoundMan::startVoice(int16 volume, int16 pan) {
+	_voicePlayer->start(volume, pan);
+}
+
+void SoundMan::stopVoice() {
+	_voicePlayer->stop();
+}
+
+bool SoundMan::isVoicePlaying() {
+	return _voicePlayer->isPlaying();
+}
+
+bool SoundMan::isVoiceEnabled() {
+	return _voicePlayer->isEnabled();
+}
+
+bool SoundMan::isVoiceCued() {
+	return _voicePlayer->isCued();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index e5139d3..9e2cd43 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -47,17 +47,46 @@ protected:
 	uint _flags;
 };
 
+class VoicePlayer {
+public:
+	VoicePlayer(Audio::Mixer *mixer);
+	~VoicePlayer();
+	bool cue(const char *voiceName);
+	void stopCueing();
+	void start(int16 volume, int16 pan);
+	void stop();
+	bool isPlaying();
+	bool isEnabled();
+	bool isCued();
+protected:
+	Audio::Mixer *_mixer;
+	Audio::SoundHandle _soundHandle;
+	Common::String _voiceName;
+	uint _voiceStatus;
+};
+
 class SoundMan {
 public:
 	SoundMan(IllusionsEngine *vm);
 	~SoundMan();
 	void update();
+
 	void playMusic(uint32 musicId, int16 type, int16 volume, int16 pan, uint32 notifyThreadId);
 	void stopMusic();
+
+	bool cueVoice(const char *voiceName);
+	void stopCueingVoice();
+	void startVoice(int16 volume, int16 pan);
+	void stopVoice();
+	bool isVoicePlaying();
+	bool isVoiceEnabled();
+	bool isVoiceCued();
+
 protected:
 	IllusionsEngine *_vm;
 	uint32 _musicNotifyThreadId;
 	MusicPlayer *_musicPlayer;
+	VoicePlayer *_voicePlayer;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index 5ad6f72..783e9d2 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -26,6 +26,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/scriptman.h"
+#include "illusions/sound.h"
 #include "illusions/talkresource.h"
 #include "illusions/time.h"
 
@@ -118,7 +119,7 @@ int TalkThread::onUpdate() {
 			_flags |= 1;
 		}
 		if (_vm->isSoundActive()) {
-			if (!_vm->cueVoice(talkEntry->_voiceName) && !_durationMult)
+			if (!_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName) && !_durationMult)
 				_durationMult = _defDurationMult;
 		} else {
 			_flags |= 4;
@@ -131,7 +132,7 @@ int TalkThread::onUpdate() {
 		// Fallthrough to status 4
 
 	case 4:
-		if (!(_flags & 4) && !_vm->isVoiceCued())
+		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
 			return kTSYield;
 		_status = 5;
 		// Fallthrough to status 5
@@ -149,14 +150,14 @@ int TalkThread::onUpdate() {
 				// TODO pt.x = (unsigned int)artcntrlGetNamedPointPosition((Point)_namedPointId);
 				// TODO panX = convertPanXCoord(pt.x);
 			}
-			_vm->startVoice(255, panX);
+			_vm->_soundMan->startVoice(255, panX);
 		}
 		_vm->_input->discardButtons(0x10);
 		_status = 6;
 		return kTSYield;
 
 	case 6:
-		if (!(_flags & 4) && !_vm->isVoicePlaying())
+		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
 			_flags |= 4;
 		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
 			// TODO _vm->removeText();
@@ -188,7 +189,7 @@ int TalkThread::onUpdate() {
 			}
 			if (_flags & 8) {
 				if (!(_flags & 4)) {
-					_vm->stopVoice();
+					_vm->_soundMan->stopVoice();
 					_flags |= 4;
 				}
 				if (!(_flags & 2)) {
@@ -228,7 +229,7 @@ int TalkThread::onUpdate() {
 			_flags |= 8;
 		}
 		if (!(_flags & 4)) {
-			_vm->stopVoice();
+			_vm->_soundMan->stopVoice();
 			_flags |= 4;
 		}
 		return kTSTerminate;
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
index 990d1be..7881268 100644
--- a/engines/illusions/talkthread_duckman.cpp
+++ b/engines/illusions/talkthread_duckman.cpp
@@ -27,6 +27,7 @@
 #include "illusions/input.h"
 #include "illusions/screentext.h"
 #include "illusions/scriptman.h"
+#include "illusions/sound.h"
 #include "illusions/talkresource.h"
 #include "illusions/time.h"
 
@@ -79,24 +80,23 @@ int TalkThread_Duckman::onUpdate() {
 	case 2:
 		talkEntry = getTalkResourceEntry(_talkId);
 		_flags = 0;
-		_entryText = talkEntry->_text;
 		_currEntryText = 0;
+		_entryText = talkEntry->_text;
 		_entryTblPtr = talkEntry->_tblPtr;
-		_flags = 0;
 		if (_sequenceId1) {
-			_pauseCtr = 0;
 			_pauseCtrPtr = &_pauseCtr;
+			_pauseCtr = 0;
 		} else {
 			_pauseCtrPtr = 0;
 			_flags |= 2;
 			_flags |= 1;
 		}
 		if (_vm->isSoundActive()) {
-			if (!_vm->cueVoice(talkEntry->_voiceName) && !_durationMult)
+			if (!_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName) && !_durationMult)
 				_durationMult = _defDurationMult;
 		} else {
 			_flags |= 4;
-			if (!_durationMult)
+			if (_durationMult == 0)
 				_durationMult = _defDurationMult;
 		}
 		if (_objectId == 0 || _durationMult == 0)
@@ -105,7 +105,7 @@ int TalkThread_Duckman::onUpdate() {
 		// Fallthrough to status 3 
 
 	case 3:
-		if (!(_flags & 4) && !_vm->isVoiceCued())
+		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
 			return kTSYield;
 		_status = 4;
 		// Fallthrough to status 4
@@ -132,14 +132,14 @@ int TalkThread_Duckman::onUpdate() {
 				panX = control->getActorPosition().x;
 				panX = _vm->convertPanXCoord(panX);
 			}
-			_vm->startVoice(255, panX);
+			_vm->_soundMan->startVoice(255, panX);
 		}
 		_vm->_input->discardButtons(0x20);
 		_status = 5;
 		return kTSYield;
 
 	case 5:
-		if (!(_flags & 4) && !_vm->isVoicePlaying())
+		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
 			_flags |= 4;
 		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
 			_vm->_screenText->removeText();
@@ -168,7 +168,7 @@ int TalkThread_Duckman::onUpdate() {
 			}
 			if (_flags & 8) {
 				if (!(_flags & 4)) {
-					_vm->stopVoice();
+					_vm->_soundMan->stopVoice();
 					_flags |= 4;
 				}
 				if (!(_flags & 2)) {
@@ -219,7 +219,7 @@ void TalkThread_Duckman::onResume() {
 void TalkThread_Duckman::onTerminated() {
 	if (_status == 5) {
 		if (!(_flags & 4))
-			_vm->stopVoice();
+			_vm->_soundMan->stopVoice();
 		if (!(_flags & 8)) {
 			_vm->_screenText->removeText();
 		}


Commit: ad2c0aaf3d81688f9c575eef64b571cb73249997
    https://github.com/scummvm/scummvm/commit/ad2c0aaf3d81688f9c575eef64b571cb73249997
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add sound effects

- Fix priority bugs in Duckman
- Add more script and sequence opcodes

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/illusions_duckman.h
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptopcodes_duckman.h
    engines/illusions/scriptresource.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/sound.cpp
    engines/illusions/sound.h
    engines/illusions/soundresource.cpp
    engines/illusions/soundresource.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 7890d02..e2b5e91 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -433,7 +433,19 @@ uint32 Control::getPriority() {
 
 	positionY = CLIP<int16>(positionY, -5000, 5000);
 
-	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));;
+	return p + 50 * ((objectId & 0x3F) + ((10000 * priority + positionY + 5000) << 6));
+}
+
+uint32 Control::getOverlapPriority() {
+	if (_vm->getGameId() == kGameIdBBDOU)
+		return getPriority();
+	return _priority;
+}
+
+uint32 Control::getDrawPriority() {
+	if (_vm->getGameId() == kGameIdBBDOU)
+		return getPriority();
+	return (_actor->_position.y + 32768) | (_priority << 16);
 }
 
 Common::Point Control::calcPosition(Common::Point posDelta) {
@@ -1008,7 +1020,7 @@ void Controls::placeBackgroundObject(BackgroundObject *backgroundObject) {
 	control->_priority = backgroundObject->_priority;
 	control->readPointsConfig(backgroundObject->_pointsConfig);
 	control->activateObject();
-	_controls.push_back(control);
+	_controls.push_front(control);
 	_vm->_dict->setObjectControl(control->_objectId, control);
 }
 
@@ -1069,7 +1081,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	
 	actor->_pathCtrY = 140;
 	
-	_controls.push_back(control);
+	_controls.push_front(control);
 	_vm->_dict->setObjectControl(objectId, control);
 
 	if (_vm->getGameId() == kGameIdDuckman) {
@@ -1112,7 +1124,7 @@ void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, Wi
 	actor->_namedPoints = 0;
 	actor->_pathCtrY = 140;
 
-	_controls.push_back(control);
+	_controls.push_front(control);
 	_vm->_dict->setObjectControl(objectId, control);
 	control->appearActor();
 }
@@ -1129,7 +1141,7 @@ void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Commo
 	control->_position.y = 0;
 	control->_actorTypeId = 0;
 	control->_actor = 0;
-	_controls.push_back(control);
+	_controls.push_front(control);
 	_vm->_dict->setObjectControl(objectId, control);
 }
 
@@ -1161,7 +1173,7 @@ void Controls::placeDialogItem(uint16 objectNum, uint32 actorTypeId, uint32 sequ
 	actor->_position2 = placePt;
 	actor->_scale = actorType->_scale;
 	actor->_color = actorType->_color;
-	_controls.push_back(control);
+	_controls.push_front(control);
 	control->appearActor();
 	control->startSequenceActor(sequenceId, 2, 0);
 	control->setActorIndex(1);
@@ -1272,7 +1284,7 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 			Common::Rect collisionRect;
 			testControl->getCollisionRect(collisionRect);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
-				uint32 testPriority = testControl->getPriority();
+				uint32 testPriority = testControl->getOverlapPriority();
 				if ((!foundControl || foundPriority < testPriority) &&
 					testPriority >= minPriorityExt) {
 					foundControl = testControl;
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 7e69ee6..5f95559 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -177,6 +177,8 @@ public:
 	void clearNotifyThreadId1();
 	void clearNotifyThreadId2();
 	void setPriority(int16 priority);
+	uint32 getOverlapPriority();
+	uint32 getDrawPriority();
 	uint32 getPriority();
 	Common::Point calcPosition(Common::Point posDelta);
 	uint32 getSubActorParent();
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 07a07c2..a39412c 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -61,7 +61,7 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	_vm->_camera->set(backgroundItem->_bgRes->_bgInfos[index - 1]._panPoint, backgroundItem->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
 
 	if (backgroundItem->_bgRes->_palettesCount > 0) {
-		Palette *palette = &backgroundItem->_bgRes->_palettes[backgroundItem->_bgRes->_paletteIndex - 1];
+		Palette *palette = backgroundItem->_bgRes->getPalette(backgroundItem->_bgRes->_paletteIndex - 1);
 		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
 	}
 	
@@ -298,7 +298,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_priorityLayers = new PriorityLayer[_priorityLayersCount];
 	stream.seek(0x34);
 	uint32 priorityLayersOffs = stream.readUint32LE();
-	debug(0, "_priorityLayersCount: %d", _priorityLayersCount);
+	debug("_priorityLayersCount: %d", _priorityLayersCount);
 	for (uint i = 0; i < _priorityLayersCount; ++i) {
 		stream.seek(priorityLayersOffs + i * 12);
 		_priorityLayers[i].load(data, stream);
@@ -411,6 +411,10 @@ PathWalkRects *BackgroundResource::getPathWalkRects(uint index) {
 	return &_pathWalkRects[index];
 }
 
+Palette *BackgroundResource::getPalette(uint index) {
+	return &_palettes[index];
+}
+
 bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	return _namedPoints.findNamedPoint(namedPointId, pt);
 }
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index b09c2d5..8d31deb 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -144,6 +144,7 @@ public:
 	RegionLayer *getRegionLayer(uint index);
 	PathWalkPoints *getPathWalkPoints(uint index);
 	PathWalkRects *getPathWalkRects(uint index);
+	Palette *getPalette(uint index);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 public:
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index c58eb85..993da5b 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -182,7 +182,7 @@ int IllusionsEngine::updateGraphics(uint flags) {
 			}
 			*/
 			if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
-				uint32 priority = control->getPriority();
+				uint32 priority = control->getDrawPriority();
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
 					actor->_surfInfo._dimensions, drawPosition, control->_position,
 					priority, actor->_scale, actor->_spriteFlags);
@@ -191,7 +191,8 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	}
 
 	if (_screenText->_surface) {
-		int16 priority = getPriorityFromBase(99);
+		// TODO Make nicer
+		uint32 priority = getGameId() == kGameIdDuckman ? getPriorityFromBase(19) : getPriorityFromBase(99);
 		_screen->_drawQueue->insertTextSurface(_screenText->_surface, _screenText->_dimensions,
 			_screenText->_position, priority);
 	}
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 3a87b09..1bbaf06 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -79,7 +79,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	// Init search paths
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "x");// DEBUG until gam reader is done
@@ -128,6 +128,9 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_fieldA = 0;
 	_fieldE = 240;
 	
+	_propertyTimersActive = false;
+	_propertyTimersPaused = false;
+
 	_globalSceneId = 0x00010003;
 
 	initInventory();
@@ -144,6 +147,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	startScriptThread(0x00020004, 0);
 	_doScriptThreadInit = true;
 
+#if 0
 	//DEBUG
 	_scriptResource->_properties.set(0x000E003A, true);
 	_scriptResource->_properties.set(0x000E0020, true);
@@ -152,6 +156,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_scriptResource->_properties.set(0x000E0009, true);
 	_scriptResource->_properties.set(0x000E003D, true);
 	_scriptResource->_properties.set(0x000E0024, true);
+#endif
 
 	while (!shouldQuit()) {
 		runUpdateFunctions();
@@ -165,7 +170,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _fader;
 
-    delete _soundMan;
+	delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
 	delete _talkItems;
@@ -359,7 +364,7 @@ Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId
 }
 
 uint32 IllusionsEngine_Duckman::getPriorityFromBase(int16 priority) {
-	return 32000000 * priority;
+	return priority << 16;
 }
 
 uint32 IllusionsEngine_Duckman::getCurrentScene() {
@@ -954,7 +959,7 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	}
 
 	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting cause thread %08X", tempThreadId);
+	debug("Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
 	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
 		triggerThreadId);
 	_threads->startThread(causeThread);
@@ -1211,6 +1216,93 @@ DMInventorySlot *IllusionsEngine_Duckman::findClosestInventorySlot(Common::Point
 	return minInventorySlot;
 }
 
+void IllusionsEngine_Duckman::addPropertyTimer(uint32 propertyId) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer) || findPropertyTimer(0, propertyTimer)) {
+		propertyTimer->_propertyId = propertyId;
+		propertyTimer->_startTime = 0;
+		propertyTimer->_duration = 0;
+		propertyTimer->_endTime = 0;
+	}
+}
+
+void IllusionsEngine_Duckman::setPropertyTimer(uint32 propertyId, uint32 duration) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer)) {
+		propertyTimer->_startTime = getCurrentTime();
+		propertyTimer->_duration = duration;
+		propertyTimer->_endTime = duration + propertyTimer->_startTime;
+	}
+	_scriptResource->_properties.set(propertyId, false);
+	if (!_propertyTimersActive) {
+		_updateFunctions->add(29, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
+			(this, &IllusionsEngine_Duckman::updatePropertyTimers));
+		_propertyTimersActive = true;
+	}
+}
+
+void IllusionsEngine_Duckman::removePropertyTimer(uint32 propertyId) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer))
+		propertyTimer->_propertyId = 0;
+	_scriptResource->_properties.set(propertyId, true);
+}
+
+bool IllusionsEngine_Duckman::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
+	for (uint i = 0; i < kPropertyTimersCount; ++i)
+		if (_propertyTimers[i]._propertyId == propertyId) {
+			propertyTimer = &_propertyTimers[i];
+			return true;
+		}
+	return false;
+}
+
+int IllusionsEngine_Duckman::updatePropertyTimers(uint flags) {
+	int result = 1;
+	uint32 currTime = getCurrentTime();
+	if (_pauseCtr <= 0) {
+		if (_propertyTimersPaused) {
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				propertyTimer._startTime = currTime;
+				propertyTimer._endTime = currTime + propertyTimer._duration;
+			}
+			_propertyTimersPaused = false;
+		}
+		if (flags & 1) {
+			_propertyTimersActive = false;
+			_propertyTimersPaused = false;
+			result = 2;
+		} else {
+			bool timersActive = false;
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				if (propertyTimer._propertyId) {
+					timersActive = true;
+					if (!_scriptResource->_properties.get(propertyTimer._propertyId) &&
+						isTimerExpired(propertyTimer._startTime, propertyTimer._endTime))
+						_scriptResource->_properties.set(propertyTimer._propertyId, true);
+				}
+			}
+			if (!timersActive) {
+				_propertyTimersActive = false;
+				_propertyTimersPaused = false;
+				result = 2;
+			}
+		}
+	} else {
+		if (!_propertyTimersPaused) {
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				propertyTimer._duration -= getDurationElapsed(propertyTimer._startTime, propertyTimer._endTime);
+			}
+			_propertyTimersPaused = true;
+		}
+		result = 1;
+	}
+	return result;
+}
+
 // Special code
 
 typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
@@ -1224,6 +1316,9 @@ void IllusionsEngine_Duckman::initSpecialCode() {
 	SPECIAL(0x00160005, spcOpenInventory);
 	SPECIAL(0x00160007, spcPutBackInventoryItem);
 	SPECIAL(0x00160008, spcClearInventorySlot);
+	SPECIAL(0x0016000A, spcAddPropertyTimer);
+	SPECIAL(0x0016000B, spcSetPropertyTimer);
+	SPECIAL(0x0016000C, spcRemovePropertyTimer);
 	SPECIAL(0x00160010, spcCenterNewspaper);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
@@ -1307,7 +1402,6 @@ static const ScreenShakeEffect *kShakerEffects[] = {
 
 void IllusionsEngine_Duckman::spcStartScreenShaker(OpCall &opCall) {
 	ARG_BYTE(effect);
-	debug("### effect: %d", effect);
 	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
 	startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
 }
@@ -1356,6 +1450,25 @@ void IllusionsEngine_Duckman::spcClearInventorySlot(OpCall &opCall) {
 	notifyThreadId(opCall._threadId);
 }
 
+void IllusionsEngine_Duckman::spcAddPropertyTimer(OpCall &opCall) {
+	ARG_UINT32(propertyId);
+	addPropertyTimer(propertyId);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcSetPropertyTimer(OpCall &opCall) {
+	ARG_INT16(propertyNum);
+	ARG_INT16(duration);
+	setPropertyTimer(propertyNum | 0xE0000, duration);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcRemovePropertyTimer(OpCall &opCall) {
+	ARG_UINT32(propertyId);
+	removePropertyTimer(propertyId);
+	notifyThreadId(opCall._threadId);
+}
+
 void IllusionsEngine_Duckman::spcCenterNewspaper(OpCall &opCall) {
 	Control *control = getObjectControl(0x40017);
 	control->_flags |= 8;
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
index 48d8ef2..7f6c427 100644
--- a/engines/illusions/illusions_duckman.h
+++ b/engines/illusions/illusions_duckman.h
@@ -91,6 +91,16 @@ struct ScreenShaker {
 	const ScreenShakerPoint *_points;
 };
 
+struct PropertyTimer {
+	uint32 _propertyId;
+	uint32 _startTime;
+	uint32 _duration;
+	uint32 _endTime;
+	PropertyTimer() : _propertyId(0) {}
+};
+
+const uint kPropertyTimersCount = 6;
+
 struct OpCall;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
@@ -125,6 +135,10 @@ public:
 
 	ScreenShaker *_screenShaker;
 
+	PropertyTimer _propertyTimers[kPropertyTimersCount];
+	bool _propertyTimersActive;
+	bool _propertyTimersPaused;
+
 	uint _chinesePuzzleIndex;
 	byte _chinesePuzzleAnswers[3];
 
@@ -220,6 +234,12 @@ public:
 	DMInventoryItem *findInventoryItem(uint32 objectId);
 	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
 
+	void addPropertyTimer(uint32 propertyId);
+	void setPropertyTimer(uint32 propertyId, uint32 duration);
+	void removePropertyTimer(uint32 propertyId);
+	bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
+	int updatePropertyTimers(uint flags);
+
 	// Special code
 	void initSpecialCode();
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
@@ -230,6 +250,9 @@ public:
 	void spcOpenInventory(OpCall &opCall);
 	void spcPutBackInventoryItem(OpCall &opCall);
 	void spcClearInventorySlot(OpCall &opCall);
+	void spcAddPropertyTimer(OpCall &opCall);
+	void spcSetPropertyTimer(OpCall &opCall);
+	void spcRemovePropertyTimer(OpCall &opCall);
 	void spcCenterNewspaper(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index a1e5e1c..e8daf6a 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -55,6 +55,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	// First clear everything
 	for (uint i = 0; i < 256; ++i)
 		_opcodes[i] = 0;
+	OPCODE(1, opNop);
 	OPCODE(2, opSuspend);
 	OPCODE(3, opYield);
 	OPCODE(4, opTerminate);
@@ -169,6 +170,9 @@ void ScriptOpcodes_Duckman::freeOpcodes() {
 
 // Opcodes
 
+void ScriptOpcodes_Duckman::opNop(ScriptThread *scriptThread, OpCall &opCall) {
+}
+
 void ScriptOpcodes_Duckman::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
 	opCall._result = kTSSuspend;
 }
@@ -242,7 +246,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 	_vm->enterScene(sceneId, 0);
 }
 
-//static uint dsceneId = 0, dthreadId = 0;
+static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
 //static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
@@ -251,13 +255,13 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 //static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
 //static uint dsceneId = 0x00010022, dthreadId = 0x00020114;
 //static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
-static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
+//static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
 //static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 //static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
 //static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
-
+//static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
index 77050de..f9f536e 100644
--- a/engines/illusions/scriptopcodes_duckman.h
+++ b/engines/illusions/scriptopcodes_duckman.h
@@ -42,6 +42,7 @@ protected:
 
 	// Opcodes
 	
+	void opNop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
 	void opYield(ScriptThread *scriptThread, OpCall &opCall);
 	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 2054d4d..18a258a 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -158,12 +158,13 @@ void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
 
 bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
 	if ((verbId & 0xFFFF0000) == 0) {
-		for (uint i = 0; i < _causesCount; ++i)
+		for (uint i = 0; i < _causesCount; ++i) {
 			if ((verbId == 7 && ((_causes[i]._verbId == 7 && _causes[i]._objectId2 == objectId2) || _causes[i]._verbId == 8)) ||
-				verbId == _causes[i]._verbId) {
+				(verbId != 7 && verbId == _causes[i]._verbId)) {
 				codeOffs = _causes[i]._codeOffs;
 				return true;
 			}
+		}
 	} else {
 		for (uint i = 0; i < _causesCount; ++i)
 			if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 7783972..5ef5158 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -28,6 +28,7 @@
 #include "illusions/screen.h"
 #include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
+#include "illusions/sound.h"
 
 namespace Illusions {
 
@@ -90,6 +91,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(40, opSetPriorityLayer);
 	OPCODE(41, opDisableAutoRegionLayer);
 	OPCODE(42, opSetRegionLayer);
+	OPCODE(48, opSetPalette);
 	OPCODE(49, opShiftPalette);
 	OPCODE(50, opPlaySound);
 	OPCODE(51, opStopSound);
@@ -344,6 +346,14 @@ void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
 }
 
+void SequenceOpcodes::opSetPalette(Control *control, OpCall &opCall) {
+	ARG_INT16(paletteIndex);
+	ARG_BYTE(fromIndex);
+	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	Palette *palette = bgRes->getPalette(paletteIndex - 1);
+	_vm->_screen->setPalette(palette->_palette, fromIndex, palette->_count);
+}
+
 void SequenceOpcodes::opShiftPalette(Control *control, OpCall &opCall) {
 	ARG_INT16(fromIndex);
 	ARG_INT16(toIndex);
@@ -359,12 +369,12 @@ void SequenceOpcodes::opPlaySound(Control *control, OpCall &opCall) {
 		volume = 255;
 	if (!(flags & 2))
 		pan = _vm->convertPanXCoord(control->_actor->_position.x);
-	// TODO _vm->startSound(soundEffectId, volume, pan);
+	_vm->_soundMan->playSound(soundEffectId, volume, pan);
 }
 
 void SequenceOpcodes::opStopSound(Control *control, OpCall &opCall) {
 	ARG_UINT32(soundEffectId);
-	// TODO _vm->stopSound(soundEffectId);
+	_vm->_soundMan->stopSound(soundEffectId);
 }
 
 void SequenceOpcodes::opStartScriptThread(Control *control, OpCall &opCall) {
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 48241e1..4d5cb7a 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -78,6 +78,7 @@ protected:
 	void opSetPriorityLayer(Control *control, OpCall &opCall);
 	void opDisableAutoRegionLayer(Control *control, OpCall &opCall);
 	void opSetRegionLayer(Control *control, OpCall &opCall);
+	void opSetPalette(Control *control, OpCall &opCall);
 	void opShiftPalette(Control *control, OpCall &opCall);
 	void opPlaySound(Control *control, OpCall &opCall);
 	void opStopSound(Control *control, OpCall &opCall);
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 0f60e5d..23caf7e 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -27,8 +27,8 @@ namespace Illusions {
 
 // MusicPlayer
 
-MusicPlayer::MusicPlayer(Audio::Mixer *mixer)
-	: _mixer(mixer), _musicId(0), _flags(0) {
+MusicPlayer::MusicPlayer()
+	: _musicId(0), _flags(0) {
 	_flags = 1; // TODO?
 }
 
@@ -52,15 +52,15 @@ void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
 		Common::File *fd = new Common::File();
 		fd->open(filename);
 		Audio::AudioStream *audioStream = Audio::makeLoopingAudioStream(Audio::makeWAVStream(fd, DisposeAfterUse::YES), looping ? 0 : 1);
-		_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, audioStream, -1, volume, pan);
+		g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, audioStream, -1, volume, pan);
 	}
 }
 
 void MusicPlayer::stop() {
 	debug("MusicPlayer::stop()");
 	if ((_flags & 1) && (_flags & 2)) {
-		if (_mixer->isSoundHandleActive(_soundHandle))
-			_mixer->stopHandle(_soundHandle);
+		if (g_system->getMixer()->isSoundHandleActive(_soundHandle))
+			g_system->getMixer()->stopHandle(_soundHandle);
 		_flags &= ~2;
 		_flags &= ~4;
 		_flags &= ~8;
@@ -69,13 +69,12 @@ void MusicPlayer::stop() {
 }
 
 bool MusicPlayer::isPlaying() {
-	return (_flags & 1) && (_flags & 2) && _mixer->isSoundHandleActive(_soundHandle);
+	return (_flags & 1) && (_flags & 2) && g_system->getMixer()->isSoundHandleActive(_soundHandle);
 }
 
 // VoicePlayer
 
-VoicePlayer::VoicePlayer(Audio::Mixer *mixer)
-	: _mixer(mixer) {
+VoicePlayer::VoicePlayer() {
 }
 
 VoicePlayer::~VoicePlayer() {
@@ -102,19 +101,19 @@ void VoicePlayer::start(int16 volume, int16 pan) {
 	Common::File *fd = new Common::File();
 	fd->open(filename);
 	Audio::AudioStream *audioStream = Audio::makeWAVStream(fd, DisposeAfterUse::YES);
-	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, audioStream, -1, volume, pan);
+	g_system->getMixer()->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, audioStream, -1, volume, pan);
 	_voiceStatus = 4;
 }
 
 void VoicePlayer::stop() {
-	if (_mixer->isSoundHandleActive(_soundHandle))
-		_mixer->stopHandle(_soundHandle);
+	if (g_system->getMixer()->isSoundHandleActive(_soundHandle))
+		g_system->getMixer()->stopHandle(_soundHandle);
 	_voiceStatus = 1;
 	_voiceName.clear();
 }
 
 bool VoicePlayer::isPlaying() {
-	return _mixer->isSoundHandleActive(_soundHandle);
+	return g_system->getMixer()->isSoundHandleActive(_soundHandle);
 }
 
 bool VoicePlayer::isEnabled() {
@@ -126,17 +125,62 @@ bool VoicePlayer::isCued() {
 	return _voiceStatus == 2;
 }
 
+// Sound
+
+Sound::Sound(uint32 soundEffectId, uint32 soundGroupId, bool looping)
+	: _stream(0), _soundEffectId(soundEffectId), _soundGroupId(soundGroupId), _looping(looping) {
+	load();
+}
+
+Sound::~Sound() {
+	unload();
+}
+
+void Sound::load() {
+	Common::String filename = Common::String::format("%08x/%08x.wav", _soundGroupId, _soundEffectId);
+	Common::File *fd = new Common::File();
+	if (!fd->open(filename)) {
+		delete fd;
+		error("SoundMan::loadSound() Could not load %s", filename.c_str());
+	}
+	_stream = Audio::makeWAVStream(fd, DisposeAfterUse::YES);
+}
+
+void Sound::unload() {
+	stop();
+	delete _stream;
+	_stream = 0;
+}
+
+void Sound::play(int16 volume, int16 pan) {
+	stop();
+	_stream->rewind();
+	Audio::AudioStream *audioStream = new Audio::LoopingAudioStream(_stream, _looping ? 0 : 1, DisposeAfterUse::NO);
+	g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, audioStream,
+		-1, volume, pan, DisposeAfterUse::YES);
+}
+
+void Sound::stop() {
+	if (g_system->getMixer()->isSoundHandleActive(_soundHandle))
+		g_system->getMixer()->stopHandle(_soundHandle);
+}
+
+bool Sound::isPlaying() {
+	return g_system->getMixer()->isSoundHandleActive(_soundHandle);
+}
+
 // SoundMan
 
 SoundMan::SoundMan(IllusionsEngine *vm)
 	: _vm(vm), _musicNotifyThreadId(0) {
-	_musicPlayer = new MusicPlayer(_vm->_mixer);
-	_voicePlayer = new VoicePlayer(_vm->_mixer);
+	_musicPlayer = new MusicPlayer();
+	_voicePlayer = new VoicePlayer();
 }
 
 SoundMan::~SoundMan() {
 	delete _musicPlayer;
 	delete _voicePlayer;
+	unloadSounds(0);
 }
 
 void SoundMan::update() {
@@ -183,4 +227,38 @@ bool SoundMan::isVoiceCued() {
 	return _voicePlayer->isCued();
 }
 
+void SoundMan::loadSound(uint32 soundEffectId, uint32 soundGroupId, bool looping) {
+	Sound *soundEffect = new Sound(soundEffectId, soundGroupId, looping);
+	_sounds.push_front(soundEffect);
+}
+
+void SoundMan::playSound(uint32 soundEffectId, int16 volume, int16 pan) {
+	Sound *soundEffect = getSound(soundEffectId);
+	soundEffect->play(volume, pan);
+}
+
+void SoundMan::stopSound(uint32 soundEffectId) {
+	Sound *soundEffect = getSound(soundEffectId);
+	soundEffect->stop();
+}
+
+void SoundMan::unloadSounds(uint32 soundGroupId) {
+	SoundListIterator it = _sounds.begin();
+	while (it != _sounds.end()) {
+		Sound *soundEffect = *it;
+		if (soundGroupId == 0 || soundEffect->_soundGroupId == soundGroupId) {
+			delete soundEffect;
+			it = _sounds.erase(it);
+		} else
+			++it;
+	}
+}
+
+Sound *SoundMan::getSound(uint32 soundEffectId) {
+	for (SoundListIterator it = _sounds.begin(); it != _sounds.end(); ++it)
+		if ((*it)->_soundEffectId == soundEffectId)
+			return *it;
+	return 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index 9e2cd43..d2d91ac 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -35,13 +35,12 @@ class IllusionsEngine;
 
 class MusicPlayer {
 public:
-	MusicPlayer(Audio::Mixer *mixer);
+	MusicPlayer();
 	~MusicPlayer();
 	void play(uint32 musicId, bool looping, int16 volume, int16 pan);
 	void stop();
 	bool isPlaying();
 protected:
-	Audio::Mixer *_mixer;
 	Audio::SoundHandle _soundHandle;
 	uint32 _musicId;
 	uint _flags;
@@ -49,7 +48,7 @@ protected:
 
 class VoicePlayer {
 public:
-	VoicePlayer(Audio::Mixer *mixer);
+	VoicePlayer();
 	~VoicePlayer();
 	bool cue(const char *voiceName);
 	void stopCueing();
@@ -59,12 +58,29 @@ public:
 	bool isEnabled();
 	bool isCued();
 protected:
-	Audio::Mixer *_mixer;
 	Audio::SoundHandle _soundHandle;
 	Common::String _voiceName;
 	uint _voiceStatus;
 };
 
+class Sound {
+public:
+	Sound(uint32 soundEffectId, uint32 soundGroupId, bool looping);
+	~Sound();
+	void load();
+	void unload();
+	void play(int16 volume, int16 pan);
+	void stop();
+	bool isPlaying();
+public:
+	uint32 _soundEffectId;
+	uint32 _soundGroupId;
+protected:
+	Audio::RewindableAudioStream *_stream;
+	Audio::SoundHandle _soundHandle;
+	bool _looping;
+};
+
 class SoundMan {
 public:
 	SoundMan(IllusionsEngine *vm);
@@ -82,11 +98,20 @@ public:
 	bool isVoiceEnabled();
 	bool isVoiceCued();
 
+	void loadSound(uint32 soundEffectId, uint32 soundGroupId, bool looping);
+	void playSound(uint32 soundEffectId, int16 volume, int16 pan);
+	void stopSound(uint32 soundEffectId);
+	void unloadSounds(uint32 soundGroupId);
+
 protected:
+	typedef Common::List<Sound*> SoundList;
+	typedef SoundList::iterator SoundListIterator;
 	IllusionsEngine *_vm;
 	uint32 _musicNotifyThreadId;
 	MusicPlayer *_musicPlayer;
 	VoicePlayer *_voicePlayer;
+	SoundList _sounds;
+	Sound *getSound(uint32 soundEffectId);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/soundresource.cpp b/engines/illusions/soundresource.cpp
index e5cc5dd..8dbe798 100644
--- a/engines/illusions/soundresource.cpp
+++ b/engines/illusions/soundresource.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/soundresource.h"
+#include "illusions/sound.h"
 
 namespace Illusions {
 
@@ -30,20 +31,68 @@ namespace Illusions {
 void SoundGroupResourceLoader::load(Resource *resource) {
 	debug("SoundGroupResourceLoader::load() Loading sound group %08X...", resource->_resId);
 
-    // TODO
-    // Load all sounds in sfx/{SoundGroupId}/
+	SoundGroupResource *soundGroupResource = new SoundGroupResource();
+	soundGroupResource->load(resource->_data, resource->_dataSize);
+	resource->_refId = soundGroupResource;
+
+	for (uint i = 0; i < soundGroupResource->_soundEffectsCount; ++i) {
+		SoundEffect *soundEffect = &soundGroupResource->_soundEffects[i];
+		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
+	}
 	
 }
 
 void SoundGroupResourceLoader::unload(Resource *resource) {
+	_vm->_soundMan->unloadSounds(resource->_resId);
+	delete (SoundGroupResource*)resource->_refId;
 }
 
 void SoundGroupResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
+	resource->_filename = Common::String::format("%08X.sg", resource->_resId);
 }
 
 bool SoundGroupResourceLoader::isFlag(int flag) {
-	return false;
+	return
+		flag == kRlfLoadFile ||
+		flag == kRlfFreeDataAfterUse;
+}
+
+// SoundEffect
+
+void SoundEffect::load(Common::SeekableReadStream &stream) {
+	_soundEffectId = stream.readUint32LE();
+	_looping = stream.readUint16LE() != 0;
+	_field6 = stream.readUint16LE();
+	_volume = stream.readUint16LE();
+	_frequency = stream.readUint16LE();
+	stream.skip(32 + 4); // Skip name
+	debug("SoundEffect::load() _soundEffectId: %08X, _looping: %d, _field6: %d, _volume: %d, _frequency: %d",
+		_soundEffectId, _looping, _field6, _volume, _frequency);
+}
+
+// SoundGroupResource
+
+SoundGroupResource::SoundGroupResource()
+	: _soundEffects(0) {
+}
+
+SoundGroupResource::~SoundGroupResource() {
+	delete[] _soundEffects;
+}
+
+void SoundGroupResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	stream.skip(4);
+	_soundEffectsCount = stream.readUint16LE();
+	stream.skip(2);
+	uint32 soundEffectsOffs = stream.readUint32LE();
+	debug("_soundEffectsCount: %d; soundEffectsOffs: %08X", _soundEffectsCount, soundEffectsOffs);
+	_soundEffects = new SoundEffect[_soundEffectsCount];
+	stream.seek(soundEffectsOffs);
+	for (uint i = 0; i < _soundEffectsCount; ++i)
+		_soundEffects[i].load(stream);
+
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/soundresource.h b/engines/illusions/soundresource.h
index d278a30..09b8817 100644
--- a/engines/illusions/soundresource.h
+++ b/engines/illusions/soundresource.h
@@ -42,6 +42,25 @@ protected:
 	IllusionsEngine *_vm;
 };
 
+struct SoundEffect {
+	uint32 _soundEffectId;
+	bool _looping;
+	int16 _field6;
+	int16 _volume;
+	int16 _frequency;
+	void load(Common::SeekableReadStream &stream);
+};
+
+class SoundGroupResource {
+public:
+	SoundGroupResource();
+	~SoundGroupResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	uint _soundEffectsCount;
+	SoundEffect *_soundEffects;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_SOUNDRESOURCE_H


Commit: 297996a0900acca177ecfd3ffa51907ad8d284fa
    https://github.com/scummvm/scummvm/commit/297996a0900acca177ecfd3ffa51907ad8d284fa
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add sound effects (still buggy)

Changed paths:
    engines/illusions/actorresource.cpp
    engines/illusions/backgroundresource.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/fontresource.cpp
    engines/illusions/graphics.cpp
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/input.cpp
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptthread.cpp
    engines/illusions/sound.cpp
    engines/illusions/soundresource.cpp
    engines/illusions/talkresource.cpp
    engines/illusions/textdrawer.cpp
    engines/illusions/thread.cpp


diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index d3762f9..f1276c0 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -94,7 +94,6 @@ void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	uint32 compressedPixelsOffs = stream.readUint32LE();
 	_compressedPixels = dataStart + compressedPixelsOffs;
 	_pointsConfig = dataStart + pointsConfigOffs;
-	
 	debug(5, "Frame::load() compressedPixelsOffs: %08X",
 		compressedPixelsOffs);
 }
@@ -104,10 +103,8 @@ void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_unk4 = stream.readUint32LE();
 	uint32 sequenceCodeOffs = stream.readUint32LE();
 	_sequenceCode = dataStart + sequenceCodeOffs;
-	
 	debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
 		_sequenceId, _unk4, sequenceCodeOffs);
-
 }
 
 void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
@@ -133,14 +130,12 @@ void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_pointsConfig = dataStart + pointsConfigOffs;
 	stream.seek(namedPointsOffs);
 	_namedPoints.load(namedPointsCount, stream);
-
 	debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
 		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
 	debug(5, "ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
 		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
 	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
 		_priorityLayerIndex, _regionLayerIndex,_flags);
-		
 }
 
 // ActorResource
@@ -154,7 +149,6 @@ ActorResource::~ActorResource() {
 void ActorResource::load(Resource *resource) {
 	byte *data = resource->_data;
 	uint32 dataSize = resource->_dataSize;
-	
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
 
 	_totalSize = stream.readUint32LE();
@@ -207,7 +201,6 @@ void ActorResource::load(Resource *resource) {
 	}
 	
 	debug("ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
-
 }
 
 bool ActorResource::containsSequence(Sequence *sequence) {
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index a39412c..818f704 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -99,7 +99,6 @@ void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	stream.skip(4); // Unknown
 	uint32 mapOffs = stream.pos();
 	_map = dataStart + mapOffs;
-	
 	debug(0, "TileMap::load() _width: %d; _height: %d",
 		_width, _height);
 }
@@ -117,7 +116,6 @@ void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	stream.seek(tileMapOffs);
 	_tileMap.load(dataStart, stream);
 	_tilePixels = dataStart + tilePixelsOffs;
-	
 	debug(0, "BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
 		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
 }
@@ -134,7 +132,6 @@ void PriorityLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_mapHeight = READ_LE_UINT16(_map + 2);
 	_map += 8;
 	_values = dataStart + valuesOffs;
-	
 	debug(0, "PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
 		_width, _height, mapOffs, valuesOffs, _mapWidth, _mapHeight);
 }
@@ -153,7 +150,6 @@ void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	stream.skip(2);
 	uint32 valuesOffs = stream.readUint32LE();
 	_values = dataStart + valuesOffs;
-	
 	debug(0, "ScaleLayer::load() _height: %d; valuesOffs: %08X",
 		_height, valuesOffs);
 }
@@ -178,7 +174,6 @@ void RegionLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_mapWidth = READ_LE_UINT16(_map + 0);
 	_mapHeight = READ_LE_UINT16(_map + 2);
 	_map += 8;
-
 	debug("RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
 		_unk, regionSequenceIdsOffs, _width, _height, mapOffs, valuesOffs);
 }
@@ -213,7 +208,6 @@ void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream)
 	_priority = stream.readUint16LE();
 	uint32 pointsConfigOffs = stream.readUint32LE();
 	_pointsConfig = dataStart + pointsConfigOffs;
-	
 	debug(0, "BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
 		_objectId, _flags, _priority, pointsConfigOffs);
 }
@@ -264,7 +258,6 @@ BackgroundResource::~BackgroundResource() {
 
 void BackgroundResource::load(byte *data, uint32 dataSize) {
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-	// TODO A lot
 	
 	stream.seek(8);
 	_paletteIndex = stream.readUint16LE();
@@ -510,7 +503,6 @@ void BackgroundItem::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, b
 }
 
 void BackgroundItem::pause() {
-	// TODO
 	++_pauseCtr;
 	if (_pauseCtr <= 1) {
 		for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
@@ -526,7 +518,6 @@ void BackgroundItem::pause() {
 }
 
 void BackgroundItem::unpause() {
-	// TODO
 	--_pauseCtr;
 	if (_pauseCtr <= 0) {
 		for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index c2ffd99..3c4e37c 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -117,7 +117,7 @@ InventorySlot *InventoryBag::findClosestSlot(Common::Point putPos, int index) {
 // BbdouInventory
 
 BbdouInventory::BbdouInventory(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
-	: _vm(vm), _bbdou(bbdou) {
+	: _vm(vm), _bbdou(bbdou), _activeInventorySceneId(0) {
 }
 
 void BbdouInventory::registerInventoryBag(uint32 sceneId) {
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index cb4f32a..eba37a8 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -69,6 +69,7 @@ void CauseThread::onTerminated() {
 }
 
 // RadarMicrophoneThread
+
 RadarMicrophoneThread::RadarMicrophoneThread(IllusionsEngine_BBDOU *vm, uint32 threadId,
 	uint32 callingThreadId, uint32 cursorObjectId)
 	: Thread(vm, threadId, callingThreadId, 0), _cursorObjectId(cursorObjectId), _zonesCount(0) {
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
index 9f7a075..c2c66ac 100644
--- a/engines/illusions/fontresource.cpp
+++ b/engines/illusions/fontresource.cpp
@@ -31,13 +31,10 @@ namespace Illusions {
 void FontResourceLoader::load(Resource *resource) {
 	debug("FontResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
 
-	// TODO
 	FontResource *fontResource = new FontResource();
 	fontResource->load(resource);
 	resource->_refId = fontResource;
-
 	_vm->_dict->addFont(resource->_resId, fontResource);
-	
 }
 
 void FontResourceLoader::unload(Resource *resource) {
@@ -102,7 +99,6 @@ void FontResource::load(Resource *resource) {
 	byte *data = resource->_data;
 	uint32 dataSize = resource->_dataSize;
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-
 	_totalSize = stream.readUint32LE();
 	_charHeight = stream.readUint16LE();
 	_field_6 = stream.readUint16LE();
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index a4fa0e6..eae6056 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -75,7 +75,6 @@ void NamedPoints::load(uint count, Common::SeekableReadStream &stream) {
 void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
 	pt.x = stream.readSint16LE();
 	pt.y = stream.readSint16LE();
-	
 	debug(0, "loadPoint() x: %d; y: %d",
 		pt.x, pt.y);
 }
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 658732d..41a6be6 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -65,8 +65,6 @@
 
 namespace Illusions {
 
-//typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> UpdateFunctionI;
-
 // TriggerFunction
 
 TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
@@ -188,7 +186,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
 
@@ -250,7 +248,6 @@ Common::Error IllusionsEngine_BBDOU::run() {
 		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
-		_system->delayMillis(10);
 	}
 
 	delete _stack;
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 1bbaf06..aee847e 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -162,7 +162,6 @@ Common::Error IllusionsEngine_Duckman::run() {
 		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
-		_system->delayMillis(10);
 	}
 
 	delete _stack;
@@ -897,7 +896,66 @@ void IllusionsEngine_Duckman::updateGameState2() {
 }
 
 void IllusionsEngine_Duckman::playSoundEffect(int index) {
-	// TODO
+	uint32 soundEffectId = 0;
+	uint32 *soundIds = _scriptResource->_soundIds;
+	switch (index) {
+	case 1:
+		soundEffectId = soundIds[0];
+		break;
+	case 2:
+		soundEffectId = soundIds[1];
+		break;
+	case 3:
+		soundEffectId = soundIds[2];
+		break;
+	case 4:
+		soundEffectId = soundIds[3];
+		break;
+	case 5:
+		soundEffectId = soundIds[4];
+		break;
+	case 6:
+		soundEffectId = soundIds[getRandom(4) + 5];
+		break;
+	case 7:
+		soundEffectId = soundIds[getRandom(4) + 9];
+		break;
+	case 8:
+		soundEffectId = soundIds[13];
+		break;
+	case 9:
+		soundEffectId = soundIds[14];
+		break;
+	case 10:
+		soundEffectId = soundIds[15];
+		break;
+	case 11:
+		soundEffectId = soundIds[16];
+		break;
+	case 12:
+		soundEffectId = soundIds[getRandom(4) + 17];
+		break;
+	case 13:
+		soundEffectId = soundIds[21];
+		break;
+	case 14:
+		soundEffectId = soundIds[22];
+		break;
+	case 15:
+		soundEffectId = soundIds[23];
+		break;
+	case 16:
+		soundEffectId = soundIds[24];
+		break;
+	case 17:
+		soundEffectId = soundIds[25];
+		break;
+	case 18:
+		soundEffectId = soundIds[26];
+		break;
+	}
+	if (soundEffectId)
+		_soundMan->playSound(soundEffectId, 255, 0);
 }
 
 bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 53b3291..e9bf25f 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -34,7 +34,6 @@ Input::Input() {
 	_cursorPos.y = 0;
 	_prevCursorPos.x = 0;
 	_prevCursorPos.y = 0;
-	// TODO Not sure if this is still needed newTimer(40, 0, 0, Input_onTimer);
 	initKeys();
 }
 
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index 30cd023..32350cd 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -513,12 +513,12 @@ void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCal
 	ARG_INT16(volume);
 	ARG_INT16(pan);
 	ARG_UINT32(soundEffectId);
-	// TODO _vm->startSound(soundEffectId, volume, pan);
+	_vm->_soundMan->playSound(soundEffectId, volume, pan);
 }
 void ScriptOpcodes_BBDOU::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(soundEffectId);
-	// TODO _vm->stopSound(soundEffectId);
+	_vm->_soundMan->stopSound(soundEffectId);
 }
 
 void ScriptOpcodes_BBDOU::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index e8daf6a..541ad18 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -30,6 +30,7 @@
 #include "illusions/scriptman.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
+#include "illusions/sound.h"
 #include "illusions/specialcode.h"
 #include "illusions/talkresource.h"
 
@@ -369,8 +370,6 @@ void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCa
 	ARG_INT16(firstIndex);
 	ARG_INT16(lastIndex);
 	_vm->startFader(duration, minValue, maxValue, firstIndex, lastIndex, opCall._threadId);
-	//DEBUG Resume calling thread, later done when the fading is finished
-	//_vm->notifyThreadId(opCall._threadId);
 }
 
 void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
@@ -560,13 +559,13 @@ void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall
 void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(volume);
 	ARG_UINT32(soundEffectId);
-	// TODO _vm->startSound(soundEffectId, volume, pan);
+	_vm->_soundMan->playSound(soundEffectId, volume, 0);
 }
 
 void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(soundEffectId);
-	// TODO _vm->stopSound(soundEffectId);
+	_vm->_soundMan->stopSound(soundEffectId);
 }
 
 void ScriptOpcodes_Duckman::opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index 73ef8ec..ba072e9 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -52,11 +52,6 @@ int ScriptThread::onUpdate() {
 }
 
 void ScriptThread::loadOpcode(OpCall &opCall) {
-#if 0
-	for (uint i = 0; i < 16; ++i)
-		debugN("%02X ", _scriptCodeIp[i]);
-	debug(".");
-#endif
 	if (_vm->getGameId() == kGameIdDuckman) {
 		opCall._op = _scriptCodeIp[0] & 0x7F;
 		opCall._opSize = _scriptCodeIp[1];
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 23caf7e..3a0f5ef 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -82,7 +82,7 @@ VoicePlayer::~VoicePlayer() {
 }
 
 bool VoicePlayer::cue(const char *voiceName) {
-debug("VoicePlayer::cue(%s)", voiceName);
+	debug("VoicePlayer::cue(%s)", voiceName);
 	_voiceName = voiceName;
 	_voiceStatus = 2;
 	if (!isEnabled()) {
@@ -161,7 +161,7 @@ void Sound::play(int16 volume, int16 pan) {
 }
 
 void Sound::stop() {
-	if (g_system->getMixer()->isSoundHandleActive(_soundHandle))
+	if (isPlaying())
 		g_system->getMixer()->stopHandle(_soundHandle);
 }
 
@@ -228,26 +228,28 @@ bool SoundMan::isVoiceCued() {
 }
 
 void SoundMan::loadSound(uint32 soundEffectId, uint32 soundGroupId, bool looping) {
-	Sound *soundEffect = new Sound(soundEffectId, soundGroupId, looping);
-	_sounds.push_front(soundEffect);
+	Sound *sound = new Sound(soundEffectId, soundGroupId, looping);
+	_sounds.push_front(sound);
 }
 
 void SoundMan::playSound(uint32 soundEffectId, int16 volume, int16 pan) {
-	Sound *soundEffect = getSound(soundEffectId);
-	soundEffect->play(volume, pan);
+	Sound *sound = getSound(soundEffectId);
+	if (sound)
+		sound->play(volume, pan);
 }
 
 void SoundMan::stopSound(uint32 soundEffectId) {
-	Sound *soundEffect = getSound(soundEffectId);
-	soundEffect->stop();
+	Sound *sound = getSound(soundEffectId);
+	if (sound)
+		sound->stop();
 }
 
 void SoundMan::unloadSounds(uint32 soundGroupId) {
 	SoundListIterator it = _sounds.begin();
 	while (it != _sounds.end()) {
-		Sound *soundEffect = *it;
-		if (soundGroupId == 0 || soundEffect->_soundGroupId == soundGroupId) {
-			delete soundEffect;
+		Sound *sound = *it;
+		if (soundGroupId == 0 || sound->_soundGroupId == soundGroupId) {
+			delete sound;
 			it = _sounds.erase(it);
 		} else
 			++it;
diff --git a/engines/illusions/soundresource.cpp b/engines/illusions/soundresource.cpp
index 8dbe798..2e65843 100644
--- a/engines/illusions/soundresource.cpp
+++ b/engines/illusions/soundresource.cpp
@@ -30,7 +30,6 @@ namespace Illusions {
 
 void SoundGroupResourceLoader::load(Resource *resource) {
 	debug("SoundGroupResourceLoader::load() Loading sound group %08X...", resource->_resId);
-
 	SoundGroupResource *soundGroupResource = new SoundGroupResource();
 	soundGroupResource->load(resource->_data, resource->_dataSize);
 	resource->_refId = soundGroupResource;
@@ -39,10 +38,10 @@ void SoundGroupResourceLoader::load(Resource *resource) {
 		SoundEffect *soundEffect = &soundGroupResource->_soundEffects[i];
 		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
 	}
-	
 }
 
 void SoundGroupResourceLoader::unload(Resource *resource) {
+	debug("SoundGroupResourceLoader::unload() Unloading sound group %08X...", resource->_resId);
 	_vm->_soundMan->unloadSounds(resource->_resId);
 	delete (SoundGroupResource*)resource->_refId;
 }
@@ -53,8 +52,8 @@ void SoundGroupResourceLoader::buildFilename(Resource *resource) {
 
 bool SoundGroupResourceLoader::isFlag(int flag) {
 	return
-		flag == kRlfLoadFile ||
-		flag == kRlfFreeDataAfterUse;
+		flag == kRlfLoadFile/* ||
+		flag == kRlfFreeDataAfterUse*/;
 }
 
 // SoundEffect
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index 2487d61..f100aeb 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -65,7 +65,6 @@ void TalkEntry::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_text = dataStart + textOffs;
 	_tblPtr = dataStart + tblOffs;
 	_voiceName = dataStart + voiceNameOffs;
-	
 	debug(0, "TalkEntry::load() _talkId: %08X; textOffs: %08X; tblOffs: %08X; voiceNameOffs: %08X",
 		_talkId, textOffs, tblOffs, voiceNameOffs);
 }
@@ -82,17 +81,14 @@ TalkResource::~TalkResource() {
 
 void TalkResource::load(byte *data, uint32 dataSize) {
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-
 	stream.skip(4); // Skip size
 	_talkEntriesCount = stream.readUint16LE();
 	stream.skip(2); // Skip padding
-	
 	_talkEntries = new TalkEntry[_talkEntriesCount];
 	for (uint i = 0; i < _talkEntriesCount; ++i) {
 		stream.seek(8 + i * 0x14);
 		_talkEntries[i].load(data, stream);
 	}
-
 }
 
 // TalkItem
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index d3f9379..61c4455 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -186,10 +186,5 @@ int16 TextDrawer::getCharWidth(uint16 c) {
 int16 offsX = (int16)(offsPt.x * 0.75);
 int16 offsY = (int16)(offsPt.y * 1.5);
 */
-/*
-if (_surface)
-	drawChar(textPosX, textPosY, c);
-textPosX += getCharWidth(c);
-*/
 
 } // End of namespace Illusions
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 6c6a7d6..941706a 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -131,13 +131,14 @@ void ThreadList::startThread(Thread *thread) {
 }
 
 void ThreadList::updateThreads() {
+	// TODO Move outer loop to caller
 	while (1) {
 		Iterator it = _threads.begin();
 		while (it != _threads.end()) {
 			Thread *thread = *it;
 			if (thread->_terminated) {
-				it = _threads.erase(it);
 				delete thread;
+				it = _threads.erase(it);
 			} else {
 				int status = kTSRun;
 				while (!thread->_terminated && status != kTSTerminate && status != kTSYield)


Commit: 50d340de00ac23c0a75ad613ad3aa64f0f28fb57
    https://github.com/scummvm/scummvm/commit/50d340de00ac23c0a75ad613ad3aa64f0f28fb57
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Rename file scriptman,* to scriptstack.* and remove unneccessary includes of it

Changed paths:
  A engines/illusions/scriptstack.cpp
  A engines/illusions/scriptstack.h
  R engines/illusions/scriptman.cpp
  R engines/illusions/scriptman.h
    engines/illusions/abortablethread.cpp
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/module.mk
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptthread.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/talkthread.cpp
    engines/illusions/talkthread_duckman.cpp
    engines/illusions/timerthread.cpp


diff --git a/engines/illusions/abortablethread.cpp b/engines/illusions/abortablethread.cpp
index 5f46ca8..15c8f6c 100644
--- a/engines/illusions/abortablethread.cpp
+++ b/engines/illusions/abortablethread.cpp
@@ -23,7 +23,6 @@
 #include "illusions/illusions.h"
 #include "illusions/abortablethread.h"
 #include "illusions/input.h"
-#include "illusions/scriptman.h"
 #include "illusions/time.h"
 
 namespace Illusions {
diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index e2b5e91..b524c02 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -28,7 +28,6 @@
 #include "illusions/fixedpoint.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
-#include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/sequenceopcodes.h"
 #include "illusions/talkthread.h"
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 3c4e37c..5fdda62 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -28,7 +28,6 @@
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
-#include "illusions/scriptman.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index eba37a8..5d98784 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -29,7 +29,7 @@
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
-#include "illusions/scriptman.h"
+#include "illusions/scriptstack.h"
 #include "illusions/scriptopcodes.h"
 
 namespace Illusions {
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 993da5b..c51eb87 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -34,7 +34,6 @@
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
 #include "illusions/scriptresource.h"
-#include "illusions/scriptman.h"
 #include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 41a6be6..e98d0c1 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -33,9 +33,9 @@
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
+#include "illusions/scriptstack.h"
 #include "illusions/scriptopcodes_bbdou.h"
 #include "illusions/scriptresource.h"
-#include "illusions/scriptman.h"
 #include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
@@ -205,7 +205,6 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_screen = new Screen(this, 640, 480, 16);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
-	_scriptMan = new ScriptMan(this);
 	_actorItems = new ActorItems(this);
 	_backgroundItems = new BackgroundItems(this);
 	_camera = new Camera(this);
@@ -263,7 +262,6 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _camera;
 	delete _backgroundItems;
 	delete _actorItems;
-	delete _scriptMan;
 	delete _input;
 	delete _screenText;
 	delete _screen;
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index aee847e..5d845a4 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -36,7 +36,7 @@
 #include "illusions/screentext.h"
 #include "illusions/scriptopcodes_duckman.h"
 #include "illusions/scriptresource.h"
-#include "illusions/scriptman.h"
+#include "illusions/scriptstack.h"
 #include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 1bdec55..ec52049 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -26,7 +26,7 @@ MODULE_OBJS := \
 	resourcesystem.o \
 	screen.o \
 	screentext.o \
-	scriptman.o \
+	scriptstack.o \
 	scriptopcodes.o \
 	scriptopcodes_bbdou.o \
 	scriptopcodes_duckman.o \
diff --git a/engines/illusions/scriptman.cpp b/engines/illusions/scriptman.cpp
deleted file mode 100644
index ef4f462..0000000
--- a/engines/illusions/scriptman.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/scriptman.h"
-#include "illusions/actor.h"
-#include "illusions/camera.h"
-#include "illusions/scriptopcodes.h"
-
-namespace Illusions {
-
-// ScriptStack
-
-ScriptStack::ScriptStack() {
-	clear();
-}
-
-void ScriptStack::clear() {
-	for (uint i = 0; i < 256; ++i)
-		_stack[i] = (int16)0xEEEE;
-	_stackPos = 256;
-}
-
-void ScriptStack::push(int16 value) {
-	--_stackPos;
-	if (_stackPos > 0)
-		_stack[_stackPos] = value;
-}
-
-int16 ScriptStack::pop() {
-	int16 value = 0;
-	if (_stackPos < 256) {
-		value = _stack[_stackPos];
-		_stack[_stackPos] = (int16)0xEEEE;
-		++_stackPos;
-	}
-	return value;
-}
-
-int16 ScriptStack::peek() {
-	int16 value = 0;
-	if (_stackPos < 256)
-		value = _stack[_stackPos];
-	return value;
-}
-
-int16 *ScriptStack::topPtr() {
-	return &_stack[_stackPos];
-}
-
-// ScriptMan
-
-ScriptMan::ScriptMan(IllusionsEngine_BBDOU *vm)
-	: _vm(vm) {
-}
-
-ScriptMan::~ScriptMan() {
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/scriptman.h b/engines/illusions/scriptman.h
deleted file mode 100644
index d296d80..0000000
--- a/engines/illusions/scriptman.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SCRIPTMAN_H
-#define ILLUSIONS_SCRIPTMAN_H
-
-#include "illusions/illusions_bbdou.h"
-#include "illusions/scriptresource.h"
-#include "illusions/thread.h"
-#include "common/algorithm.h"
-#include "common/stack.h"
-
-namespace Illusions {
-
-class IllusionsEngine_BBDOU;
-
-class ScriptStack {
-public:
-	ScriptStack();
-	void clear();
-	void push(int16 value);
-	int16 pop();
-	int16 peek();
-	int16 *topPtr();
-protected:
-	int _stackPos;
-	int16 _stack[256];
-};
-
-class ScriptMan {
-public:
-	ScriptMan(IllusionsEngine_BBDOU *vm);
-	~ScriptMan();
-public:
-
-	IllusionsEngine_BBDOU *_vm;
-	
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SCRIPTMAN_H
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index 32350cd..e152c34 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -27,7 +27,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
-#include "illusions/scriptman.h"
+#include "illusions/scriptstack.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
 #include "illusions/sound.h"
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 541ad18..8dd6af1 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -27,7 +27,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screen.h"
-#include "illusions/scriptman.h"
+#include "illusions/scriptstack.h"
 #include "illusions/scriptresource.h"
 #include "illusions/scriptthread.h"
 #include "illusions/sound.h"
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 18a258a..71c8b89 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -22,7 +22,6 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptresource.h"
-#include "illusions/scriptman.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/scriptstack.cpp b/engines/illusions/scriptstack.cpp
new file mode 100644
index 0000000..36cd3cb
--- /dev/null
+++ b/engines/illusions/scriptstack.cpp
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/scriptstack.h"
+
+namespace Illusions {
+
+// ScriptStack
+
+ScriptStack::ScriptStack() {
+	clear();
+}
+
+void ScriptStack::clear() {
+	for (uint i = 0; i < 256; ++i)
+		_stack[i] = (int16)0xEEEE;
+	_stackPos = 256;
+}
+
+void ScriptStack::push(int16 value) {
+	--_stackPos;
+	if (_stackPos > 0)
+		_stack[_stackPos] = value;
+}
+
+int16 ScriptStack::pop() {
+	int16 value = 0;
+	if (_stackPos < 256) {
+		value = _stack[_stackPos];
+		_stack[_stackPos] = (int16)0xEEEE;
+		++_stackPos;
+	}
+	return value;
+}
+
+int16 ScriptStack::peek() {
+	int16 value = 0;
+	if (_stackPos < 256)
+		value = _stack[_stackPos];
+	return value;
+}
+
+int16 *ScriptStack::topPtr() {
+	return &_stack[_stackPos];
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/scriptstack.h b/engines/illusions/scriptstack.h
new file mode 100644
index 0000000..b738102
--- /dev/null
+++ b/engines/illusions/scriptstack.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTSTACK_H
+#define ILLUSIONS_SCRIPTSTACK_H
+
+namespace Illusions {
+
+class ScriptStack {
+public:
+	ScriptStack();
+	void clear();
+	void push(int16 value);
+	int16 pop();
+	int16 peek();
+	int16 *topPtr();
+protected:
+	int _stackPos;
+	int16 _stack[256];
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTSTACK_H
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
index ba072e9..a820289 100644
--- a/engines/illusions/scriptthread.cpp
+++ b/engines/illusions/scriptthread.cpp
@@ -22,7 +22,6 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptthread.h"
-#include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 
 namespace Illusions {
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 5ef5158..ef76c78 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -26,7 +26,6 @@
 #include "illusions/actorresource.h"
 #include "illusions/dictionary.h"
 #include "illusions/screen.h"
-#include "illusions/scriptman.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/sound.h"
 
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
index 783e9d2..7d97588 100644
--- a/engines/illusions/talkthread.cpp
+++ b/engines/illusions/talkthread.cpp
@@ -25,7 +25,6 @@
 #include "illusions/actor.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
-#include "illusions/scriptman.h"
 #include "illusions/sound.h"
 #include "illusions/talkresource.h"
 #include "illusions/time.h"
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
index 7881268..6807c98 100644
--- a/engines/illusions/talkthread_duckman.cpp
+++ b/engines/illusions/talkthread_duckman.cpp
@@ -26,7 +26,6 @@
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/screentext.h"
-#include "illusions/scriptman.h"
 #include "illusions/sound.h"
 #include "illusions/talkresource.h"
 #include "illusions/time.h"
diff --git a/engines/illusions/timerthread.cpp b/engines/illusions/timerthread.cpp
index 8cd5ea2..95b30fe 100644
--- a/engines/illusions/timerthread.cpp
+++ b/engines/illusions/timerthread.cpp
@@ -23,7 +23,6 @@
 #include "illusions/illusions.h"
 #include "illusions/timerthread.h"
 #include "illusions/input.h"
-#include "illusions/scriptman.h"
 #include "illusions/time.h"
 
 namespace Illusions {


Commit: d7dd74cbe99f8d6d8072f828707e89e43517caca
    https://github.com/scummvm/scummvm/commit/d7dd74cbe99f8d6d8072f828707e89e43517caca
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Very minor comment fixes

Changed paths:
    engines/illusions/actorresource.cpp
    engines/illusions/backgroundresource.cpp
    engines/illusions/causethread_duckman.cpp
    engines/illusions/scriptresource.cpp


diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index f1276c0..bcff522 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -86,6 +86,8 @@ bool ActorResourceLoader::isFlag(int flag) {
 		flag == kRlfLoadFile;
 }
 
+// Frame
+
 void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_flags = stream.readUint16LE();
 	stream.skip(2); // Skip padding
@@ -98,6 +100,8 @@ void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		compressedPixelsOffs);
 }
 
+// Sequence
+
 void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_sequenceId = stream.readUint32LE();
 	_unk4 = stream.readUint32LE();
@@ -107,6 +111,8 @@ void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
 		_sequenceId, _unk4, sequenceCodeOffs);
 }
 
+// ActorType
+
 void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_actorTypeId = stream.readUint32LE();
 	_surfInfo.load(stream);
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 818f704..404ac6f 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -145,6 +145,8 @@ int PriorityLayer::getPriority(Common::Point pos) {
 	return _values[mapIndex * 32 * 8 + sx + sy * 32];
 }
 
+// ScaleLayer
+
 void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_height = stream.readUint16LE();
 	stream.skip(2);
diff --git a/engines/illusions/causethread_duckman.cpp b/engines/illusions/causethread_duckman.cpp
index a10b03f..4fbe49e 100644
--- a/engines/illusions/causethread_duckman.cpp
+++ b/engines/illusions/causethread_duckman.cpp
@@ -27,7 +27,7 @@
 
 namespace Illusions {
 
-// TalkThread
+// CauseThread_Duckman
 
 CauseThread_Duckman::CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	uint32 triggerThreadId)
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 71c8b89..5d93e2a 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -39,6 +39,7 @@ void ScriptResourceLoader::load(Resource *resource) {
 
 void ScriptResourceLoader::unload(Resource *resource) {
 	delete _vm->_scriptResource;
+	_vm->_scriptResource = 0;
 }
 
 void ScriptResourceLoader::buildFilename(Resource *resource) {
@@ -50,6 +51,8 @@ bool ScriptResourceLoader::isFlag(int flag) {
 		flag == kRlfLoadFile;
 }
 
+// Properties
+
 Properties::Properties()
 	: _count(0), _properties(0) {
 }


Commit: e4ce8ad4c9e9c5156270bb4c1c6f4952a011b70f
    https://github.com/scummvm/scummvm/commit/e4ce8ad4c9e9c5156270bb4c1c6f4952a011b70f
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start refactoring the resource system (not done yet)

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/camera.cpp
    engines/illusions/camera.h
    engines/illusions/fontresource.cpp
    engines/illusions/fontresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/soundresource.cpp
    engines/illusions/talkresource.cpp
    engines/illusions/talkresource.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index b524c02..65eff5d 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -688,7 +688,7 @@ void Control::startSubSequence(int linkIndex, uint32 sequenceId) {
 
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 	linkedActor->_seqCodeIp = sequence->_sequenceCode;
-	linkedActor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
+	linkedActor->_frames = _vm->findActorSequenceFrames(sequence);
 	linkedActor->_seqCodeValue3 = 0;
 	linkedActor->_seqCodeValue1 = 0;
 	linkedActor->_seqCodeValue2 = 600;
@@ -778,7 +778,7 @@ PointArray *Control::createPath(Common::Point destPt) {
 	PointArray *walkPoints = (_actor->_flags & 2) ? _actor->_pathWalkPoints->_points : 0;
 	PathLines *walkRects = (_actor->_flags & 0x10) ? _actor->_pathWalkRects->_rects : 0;
 	PathFinder pathFinder;
-	WidthHeight bgDimensions = _vm->_backgroundItems->getMasterBgDimensions();
+	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
 	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
 	for (uint i = 0; i < path->size(); ++i) {
 		debug(0, "Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
@@ -963,7 +963,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	}
 
 	_actor->_seqCodeIp = sequence->_sequenceCode;
-	_actor->_frames = _vm->_actorItems->findSequenceFrames(sequence);
+	_actor->_frames = _vm->findActorSequenceFrames(sequence);
 	
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
@@ -1052,7 +1052,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	actor->_scale = actorType->_scale;
 	actor->_namedPoints = &actorType->_namedPoints;
 	
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	if (actorType->_pathWalkPointsIndex) {
 		actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
 		actor->_flags |= 0x02;
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index bcff522..43cb314 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -29,52 +29,11 @@ namespace Illusions {
 // ActorResourceLoader
 
 void ActorResourceLoader::load(Resource *resource) {
-	debug("ActorResourceLoader::load() Loading actor %08X from %s...", resource->_resId, resource->_filename.c_str());
-
-	ActorResource *actorResource = new ActorResource();
-	actorResource->load(resource);
-	resource->_refId = actorResource;
-	
-	ActorItem *actorItem = _vm->_actorItems->allocActorItem();
-	actorItem->_tag = resource->_tag;
-	actorItem->_pauseCtr = 0;
-	actorItem->_actRes = actorResource;
-	
-	for (uint i = 0; i < actorResource->_actorTypes.size(); ++i) {
-		ActorType *actorType = &actorResource->_actorTypes[i];
-		ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
-		if (actorType2) {
-			actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
-				actorType2->_surfInfo._dimensions._width);
-			actorType->_surfInfo._dimensions._height = MAX(actorType->_surfInfo._dimensions._height,
-				actorType2->_surfInfo._dimensions._height);
-			if (actorType->_color.r == 255 && actorType->_color.g == 255 && actorType->_color.b == 255)
-				actorType->_color = actorType2->_color;
-			if (actorType->_value1E == 0)
-				actorType->_value1E = actorType2->_value1E;
-		}
-		_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
-	}
-
-	for (uint i = 0; i < actorResource->_sequences.size(); ++i) {
-		Sequence *sequence = &actorResource->_sequences[i];
-		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-	}
-	
+	resource->_instance = _vm->_actorItems->createActorInstance(resource);
 }
 
 void ActorResourceLoader::unload(Resource *resource) {
-	debug("ActorResourceLoader::unload() Unloading actor %08X...", resource->_resId);
-	// TODO Move to ActorItems
-	ActorItem *actorItem = _vm->_actorItems->findActorByResource((ActorResource*)resource->_refId);
-	if (actorItem->_pauseCtr <= 0) {
-		for (uint i = 0; i < actorItem->_actRes->_actorTypes.size(); ++i)
-			_vm->_dict->removeActorType(actorItem->_actRes->_actorTypes[i]._actorTypeId);
-		for (uint i = 0; i < actorItem->_actRes->_sequences.size(); ++i)
-			_vm->_dict->removeSequence(actorItem->_actRes->_sequences[i]._sequenceId);
-	}
-	_vm->_actorItems->freeActorItem(actorItem);
-	delete actorItem->_actRes;
+	// TODO Remove method
 }
 
 void ActorResourceLoader::buildFilename(Resource *resource) {
@@ -220,91 +179,128 @@ bool ActorResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	return _namedPoints.findNamedPoint(namedPointId, pt);
 }
 
-// ActorItem
+// ActorInstance
 
-ActorItem::ActorItem(IllusionsEngine *vm)
+ActorInstance::ActorInstance(IllusionsEngine *vm)
 	: _vm(vm) {
 }
 
-ActorItem::~ActorItem() {
+void ActorInstance::load(Resource *resource) {
+	_actorResource = new ActorResource();
+	_actorResource->load(resource);
+	_tag = resource->_tag;
+	_pauseCtr = 0;
+	initActorTypes();
+	registerResources();
+}
+
+void ActorInstance::unload() {
+	if (_pauseCtr <= 0)
+		unregisterResources();
+	_vm->_actorItems->removeActorInstance(this);
+	delete _actorResource;
 }
 
-void ActorItem::pause() {
+void ActorInstance::pause() {
 	++_pauseCtr;
-	if (_pauseCtr == 1) {
-		for (uint i = 0; i < _actRes->_actorTypes.size(); ++i)
-			_vm->_dict->removeActorType(_actRes->_actorTypes[i]._actorTypeId);
-		for (uint i = 0; i < _actRes->_sequences.size(); ++i)
-			_vm->_dict->removeSequence(_actRes->_sequences[i]._sequenceId);
-	}
+	if (_pauseCtr == 1)
+		unregisterResources();
 }
 
-void ActorItem::unpause() {
+void ActorInstance::unpause() {
 	--_pauseCtr;
-	if (_pauseCtr == 0) {
-		for (uint i = 0; i < _actRes->_actorTypes.size(); ++i) {
-			ActorType *actorType = &_actRes->_actorTypes[i];
-			_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
-		}
-		for (uint i = 0; i < _actRes->_sequences.size(); ++i) {
-			Sequence *sequence = &_actRes->_sequences[i];
-			_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	if (_pauseCtr == 0)
+		registerResources();
+}
+
+void ActorInstance::initActorTypes() {
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
+		ActorType *actorType = &_actorResource->_actorTypes[i];
+		ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
+		if (actorType2) {
+			actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
+				actorType2->_surfInfo._dimensions._width);
+			actorType->_surfInfo._dimensions._height = MAX(actorType->_surfInfo._dimensions._height,
+				actorType2->_surfInfo._dimensions._height);
+			if (actorType->_color.r == 255 && actorType->_color.g == 255 && actorType->_color.b == 255)
+				actorType->_color = actorType2->_color;
+			if (actorType->_value1E == 0)
+				actorType->_value1E = actorType2->_value1E;
 		}
 	}
 }
 
-// ActorItems
+void ActorInstance::registerResources() {
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
+		ActorType *actorType = &_actorResource->_actorTypes[i];
+		_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
+	}
+	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
+		Sequence *sequence = &_actorResource->_sequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	}
+}	
+
+void ActorInstance::unregisterResources() {
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i)
+		_vm->_dict->removeActorType(_actorResource->_actorTypes[i]._actorTypeId);
+	for (uint i = 0; i < _actorResource->_sequences.size(); ++i)
+		_vm->_dict->removeSequence(_actorResource->_sequences[i]._sequenceId);
+}
+
+// ActorInstanceList
 
-ActorItems::ActorItems(IllusionsEngine *vm)
+ActorInstanceList::ActorInstanceList(IllusionsEngine *vm)
 	: _vm(vm) {
 }
 
-ActorItems::~ActorItems() {
+ActorInstanceList::~ActorInstanceList() {
 }
 
-ActorItem *ActorItems::allocActorItem() {
-	ActorItem *actorItem = new ActorItem(_vm);
-	_items.push_back(actorItem);
-	return actorItem;
+ActorInstance *ActorInstanceList::createActorInstance(Resource *resource) {
+	ActorInstance *actorInstance = new ActorInstance(_vm);
+	actorInstance->load(resource);
+	_items.push_back(actorInstance);
+	return actorInstance;
 }
 
-void ActorItems::freeActorItem(ActorItem *actorItem) {
-	_items.remove(actorItem);
-	delete actorItem;
+void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
+	_items.remove(actorInstance);
+	debug("removeActorInstance() AFTER _items.size(): %d", _items.size());
 }
 
-void ActorItems::pauseByTag(uint32 tag) {
+void ActorInstanceList::pauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
 			(*it)->pause();
 }
 
-void ActorItems::unpauseByTag(uint32 tag) {
+void ActorInstanceList::unpauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
 			(*it)->unpause();
 }
 
-FramesList *ActorItems::findSequenceFrames(Sequence *sequence) {
+FramesList *ActorInstanceList::findSequenceFrames(Sequence *sequence) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
-		ActorItem *actorItem = *it;
-		if (actorItem->_pauseCtr <= 0 && actorItem->_actRes->containsSequence(sequence))
-			return &actorItem->_actRes->_frames;
+		ActorInstance *actorInstance = *it;
+		if (actorInstance->_pauseCtr <= 0 && actorInstance->_actorResource->containsSequence(sequence))
+			return &actorInstance->_actorResource->_frames;
 	}
 	return 0;
 }
 
-ActorItem *ActorItems::findActorByResource(ActorResource *actorResource) {
+ActorInstance *ActorInstanceList::findActorByResource(ActorResource *actorResource) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_actRes == actorResource)
+		if ((*it)->_actorResource == actorResource)
 			return (*it);
 	return 0;
 }
 
-bool ActorItems::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+bool ActorInstanceList::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
-		ActorItem *actorItem = *it;
-		if (actorItem->_pauseCtr == 0 && actorItem->_actRes->findNamedPoint(namedPointId, pt))
+		ActorInstance *actorInstance = *it;
+		if (actorInstance->_pauseCtr == 0 && actorInstance->_actorResource->findNamedPoint(namedPointId, pt))
 			return true;
 	}
 	return false;
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index 3a4c462..cc8feeb 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -94,32 +94,37 @@ public:
 	NamedPoints _namedPoints;
 };
 
-class ActorItem {
+class ActorInstance : public ResourceInstance {
 public:
-	ActorItem(IllusionsEngine *vm);              
-	~ActorItem();
+	ActorInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
 	void pause();
 	void unpause();
 public:
 	IllusionsEngine *_vm;
 	uint32 _tag;
 	int _pauseCtr;
-	ActorResource *_actRes;
+	ActorResource *_actorResource;
+protected:
+	void initActorTypes();
+	void registerResources();	
+	void unregisterResources();	
 };
 
-class ActorItems {
+class ActorInstanceList {
 public:
-	ActorItems(IllusionsEngine *vm);
-	~ActorItems();
-	ActorItem *allocActorItem();
-	void freeActorItem(ActorItem *actorItem);
+	ActorInstanceList(IllusionsEngine *vm);
+	~ActorInstanceList();
+	ActorInstance *createActorInstance(Resource *resource);
+	void removeActorInstance(ActorInstance *actorInstance);
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
 	FramesList *findSequenceFrames(Sequence *sequence);
-	ActorItem *findActorByResource(ActorResource *actorResource);
+	ActorInstance *findActorByResource(ActorResource *actorResource);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 protected:
-	typedef Common::List<ActorItem*> Items;
+	typedef Common::List<ActorInstance*> Items;
 	typedef Items::iterator ItemsIterator;
 	IllusionsEngine *_vm;
 	Items _items;
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 404ac6f..34a42c7 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -34,52 +34,11 @@ namespace Illusions {
 // BackgroundResourceLoader
 
 void BackgroundResourceLoader::load(Resource *resource) {
-	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
-
-	BackgroundResource *backgroundResource = new BackgroundResource();
-	backgroundResource->load(resource->_data, resource->_dataSize);
-	resource->_refId = backgroundResource;
-
-	// TODO Move to BackgroundItems
-	BackgroundItem *backgroundItem = _vm->_backgroundItems->allocBackgroundItem();
-	backgroundItem->_bgRes = backgroundResource;
-	backgroundItem->_tag = resource->_tag;
-	backgroundItem->initSurface();
-	
-	// Insert background objects
-	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
-		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
-
-	// Insert region sequences
-	for (uint i = 0; i < backgroundResource->_regionSequencesCount; ++i) {
-		Sequence *sequence = &backgroundResource->_regionSequences[i];
-		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-	}
-
-	// TODO camera_fadeClear();
-	int index = backgroundItem->_bgRes->findMasterBgIndex();
-	_vm->_camera->set(backgroundItem->_bgRes->_bgInfos[index - 1]._panPoint, backgroundItem->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
-
-	if (backgroundItem->_bgRes->_palettesCount > 0) {
-		Palette *palette = backgroundItem->_bgRes->getPalette(backgroundItem->_bgRes->_paletteIndex - 1);
-		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
-	}
-	
+	resource->_instance = _vm->_backgroundInstances->createBackgroundInstance(resource);
 }
 
 void BackgroundResourceLoader::unload(Resource *resource) {
-	debug("BackgroundResourceLoader::unload() Unloading background %08X...", resource->_resId);
-	// TODO Move to BackgroundItems
-	BackgroundItem *backgroundItem = _vm->_backgroundItems->findBackgroundByResource((BackgroundResource*)resource->_refId);
-	backgroundItem->freeSurface();
-	for (uint i = 0; i < backgroundItem->_bgRes->_regionSequencesCount; ++i) {
-		Sequence *sequence = &backgroundItem->_bgRes->_regionSequences[i];
-		_vm->_dict->removeSequence(sequence->_sequenceId);
-	}
-	delete backgroundItem->_bgRes;
-	_vm->_backgroundItems->freeBackgroundItem(backgroundItem);
-	_vm->setDefaultTextCoords();
-	debug("BackgroundResourceLoader::unload() Unloading background %08X OK", resource->_resId);
+	// TODO Remove method
 }
 
 void BackgroundResourceLoader::buildFilename(Resource *resource) {
@@ -414,15 +373,49 @@ bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt)
 	return _namedPoints.findNamedPoint(namedPointId, pt);
 }
 
-// BackgroundItem
+// BackgroundInstance
+
+BackgroundInstance::BackgroundInstance(IllusionsEngine *vm)
+	: _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
+}
+
+void BackgroundInstance::load(Resource *resource) {
+	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+	BackgroundResource *backgroundResource = new BackgroundResource();
+	backgroundResource->load(resource->_data, resource->_dataSize);
+
+	_bgRes = backgroundResource;
+	_tag = resource->_tag;
+	initSurface();
+	
+	// Insert background objects
+	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
+		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
+		
+	registerResources();
+
+	// TODO camera_fadeClear();
+	int index = _bgRes->findMasterBgIndex();
+	_vm->_camera->set(_bgRes->_bgInfos[index - 1]._panPoint, _bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
+
+	if (_bgRes->_palettesCount > 0) {
+		Palette *palette = _bgRes->getPalette(_bgRes->_paletteIndex - 1);
+		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
+	}
 
-BackgroundItem::BackgroundItem(IllusionsEngine *vm) : _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
 }
 
-BackgroundItem::~BackgroundItem() {
+void BackgroundInstance::unload() {
+	debug("BackgroundInstance::unload()");
+	freeSurface();
+	unregisterResources();
+	delete _bgRes;
+	_vm->_backgroundInstances->removeBackgroundInstance(this);
+	_vm->setDefaultTextCoords();
 }
 
-void BackgroundItem::initSurface() {
+void BackgroundInstance::initSurface() {
 	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
 		_surfaces[i] = 0;
 	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
@@ -433,7 +426,7 @@ void BackgroundItem::initSurface() {
 	}
 }
 
-void BackgroundItem::freeSurface() {
+void BackgroundInstance::freeSurface() {
 	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i)
 		if (_surfaces[i]) {
 			_surfaces[i]->free();
@@ -442,7 +435,7 @@ void BackgroundItem::freeSurface() {
 		}
 }
 
-void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+void BackgroundInstance::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
 	switch (_vm->getGameId()) {
 	case kGameIdDuckman:
 		drawTiles8(surface, tileMap, tilePixels);
@@ -453,7 +446,21 @@ void BackgroundItem::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byt
 	}
 }
 
-void BackgroundItem::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+void BackgroundInstance::registerResources() {
+	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &_bgRes->_regionSequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	}
+}
+	
+void BackgroundInstance::unregisterResources() {
+	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &_bgRes->_regionSequences[i];
+		_vm->_dict->removeSequence(sequence->_sequenceId);
+	}
+}	
+
+void BackgroundInstance::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
 	const int kTileWidth = 32;
 	const int kTileHeight = 8;
 	const int kTileSize = kTileWidth * kTileHeight;
@@ -477,7 +484,7 @@ void BackgroundItem::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, by
 	}
 }
 
-void BackgroundItem::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+void BackgroundInstance::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
 	const int kTileWidth = 32;
 	const int kTileHeight = 8;
 	const int kTileSize = kTileWidth * kTileHeight * 2;
@@ -504,13 +511,10 @@ void BackgroundItem::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, b
 	}
 }
 
-void BackgroundItem::pause() {
+void BackgroundInstance::pause() {
 	++_pauseCtr;
 	if (_pauseCtr <= 1) {
-		for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
-			Sequence *sequence = &_bgRes->_regionSequences[i];
-			_vm->_dict->removeSequence(sequence->_sequenceId);
-		}
+		unregisterResources();
 		_vm->setDefaultTextCoords();
 		_vm->_camera->getActiveState(_savedCameraState);
 		_savedPalette = new byte[1024];
@@ -519,91 +523,89 @@ void BackgroundItem::pause() {
 	}
 }
 
-void BackgroundItem::unpause() {
+void BackgroundInstance::unpause() {
 	--_pauseCtr;
 	if (_pauseCtr <= 0) {
-		for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
-			Sequence *sequence = &_bgRes->_regionSequences[i];
-			_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-		}
+		registerResources();
 		initSurface();
 		_vm->_screen->setPalette(_savedPalette, 1, 256);
 		delete[] _savedPalette;
 		_savedPalette = 0;
 		// TODO _vm->_screen->_fadeClear();
 		_vm->_camera->setActiveState(_savedCameraState);
-		_vm->_backgroundItems->refreshPan();
+		_vm->_backgroundInstances->refreshPan();
 	}
 }
 
-// BackgroundItems
+// BackgroundInstanceList
 
-BackgroundItems::BackgroundItems(IllusionsEngine *vm)
+BackgroundInstanceList::BackgroundInstanceList(IllusionsEngine *vm)
 	: _vm(vm) {
 }
 
-BackgroundItems::~BackgroundItems() {
+BackgroundInstanceList::~BackgroundInstanceList() {
 }
 
-BackgroundItem *BackgroundItems::allocBackgroundItem() {
-	BackgroundItem *backgroundItem = new BackgroundItem(_vm);
-	_items.push_back(backgroundItem);
-	return backgroundItem;
+BackgroundInstance *BackgroundInstanceList::createBackgroundInstance(Resource *resource) {
+	BackgroundInstance *backgroundInstance = new BackgroundInstance(_vm);
+	backgroundInstance->load(resource);
+	_items.push_back(backgroundInstance);
+	return backgroundInstance;
 }
 
-void BackgroundItems::freeBackgroundItem(BackgroundItem *backgroundItem) {
-	_items.remove(backgroundItem);
-	delete backgroundItem;
+void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgroundInstance) {
+	_items.remove(backgroundInstance);
+	debug("removeActorInstance() AFTER _items.size(): %d", _items.size());
 }
 
-void BackgroundItems::pauseByTag(uint32 tag) {
+void BackgroundInstanceList::pauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
 			(*it)->pause();
 }
 
-void BackgroundItems::unpauseByTag(uint32 tag) {
+void BackgroundInstanceList::unpauseByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
 			(*it)->unpause();
 }
 
-BackgroundItem *BackgroundItems::findActiveBackground() {
+BackgroundInstance *BackgroundInstanceList::findActiveBackground() {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_pauseCtr == 0)
 			return (*it);
 	return 0;
 }
 
-BackgroundItem *BackgroundItems::findBackgroundByResource(BackgroundResource *backgroundResource) {
+BackgroundInstance *BackgroundInstanceList::findBackgroundByResource(BackgroundResource *backgroundResource) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_bgRes == backgroundResource)
 			return (*it);
 	return 0;
 }
 
-BackgroundResource *BackgroundItems::getActiveBgResource() {
-	BackgroundItem *background = findActiveBackground();
+BackgroundResource *BackgroundInstanceList::getActiveBgResource() {
+	BackgroundInstance *background = findActiveBackground();
 	if (background)
 		return background->_bgRes;
 	return 0;
 }
 
-WidthHeight BackgroundItems::getMasterBgDimensions() {
-	BackgroundItem *backgroundItem = findActiveBackground();
-	int16 index = backgroundItem->_bgRes->findMasterBgIndex();
-	return backgroundItem->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
+WidthHeight BackgroundInstanceList::getMasterBgDimensions() {
+	BackgroundInstance *backgroundInstance = findActiveBackground();
+	int16 index = backgroundInstance->_bgRes->findMasterBgIndex();
+	return backgroundInstance->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
 }
 
-void BackgroundItems::refreshPan() {
-	BackgroundItem *backgroundItem = findActiveBackground();
-	if (backgroundItem) {
+void BackgroundInstanceList::refreshPan() {
+	BackgroundInstance *backgroundInstance = findActiveBackground();
+	if (backgroundInstance) {
 		WidthHeight dimensions = getMasterBgDimensions();
-		_vm->_camera->refreshPan(backgroundItem, dimensions);
+		_vm->_camera->refreshPan(backgroundInstance, dimensions);
 	}
 }
 
-bool BackgroundItems::findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt) {
+bool BackgroundInstanceList::findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt) {
 	BackgroundResource *backgroundResource = getActiveBgResource();
 	return backgroundResource ? backgroundResource->findNamedPoint(namedPointId, pt) : false;
 }
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 8d31deb..673f69f 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -183,10 +183,11 @@ public:
 
 const uint kMaxBackgroundItemSurfaces = 3;
 
-class BackgroundItem {
+class BackgroundInstance : public ResourceInstance {
 public:
-	BackgroundItem(IllusionsEngine *vm);
-	~BackgroundItem();
+	BackgroundInstance(IllusionsEngine *vm);
+	virtual void load(Resource *resource);
+	virtual void unload();
 	void initSurface();
 	void freeSurface();
 	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
@@ -201,27 +202,29 @@ public:
 	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
 	CameraState _savedCameraState;
 	byte *_savedPalette;
+	void registerResources();	
+	void unregisterResources();	
 	void drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 	void drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 };
 
-class BackgroundItems {
+class BackgroundInstanceList {
 public:
-	BackgroundItems(IllusionsEngine *vm);
-	~BackgroundItems();
-	BackgroundItem *allocBackgroundItem();
-	void freeBackgroundItem(BackgroundItem *backgroundItem);
+	BackgroundInstanceList(IllusionsEngine *vm);
+	~BackgroundInstanceList();
+	BackgroundInstance *createBackgroundInstance(Resource *resource);
+	void removeBackgroundInstance(BackgroundInstance *backgroundInstance);
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
-	BackgroundItem *findActiveBackground();
-	BackgroundItem *findBackgroundByResource(BackgroundResource *backgroundResource);
+	BackgroundInstance *findActiveBackground();
+	BackgroundInstance *findBackgroundByResource(BackgroundResource *backgroundResource);
 	BackgroundResource *getActiveBgResource();
 	WidthHeight getMasterBgDimensions();
 	void refreshPan();
 	bool findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt);
 //protected:
 public:
-	typedef Common::List<BackgroundItem*> Items;
+	typedef Common::List<BackgroundInstance*> Items;
 	typedef Items::iterator ItemsIterator;
 	IllusionsEngine *_vm;
 	Items _items;
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 92783a1..4e6281a 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -72,7 +72,7 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 	_activeState._currPan = _activeState._panTargetPoint;
 	_activeState._panXShl = _activeState._currPan.x << 16;
 	_activeState._panYShl = _activeState._currPan.y << 16;
-	_vm->_backgroundItems->refreshPan();
+	_vm->_backgroundInstances->refreshPan();
 	_activeState._panToPositionPtr = 0;
 	_activeState._panObjectId = 0;
 	_activeState._panNotifyId = 0;
@@ -169,7 +169,7 @@ void Camera::stopPan() {
 	_activeState._panObjectId = 0;
 	_activeState._panNotifyId = 0;
 	_activeState._pointFlags = 0;
-	_vm->_backgroundItems->refreshPan();
+	_vm->_backgroundInstances->refreshPan();
 }
 
 void Camera::pause() {
@@ -267,7 +267,7 @@ void Camera::update(uint32 currTime) {
 			if (_activeState._cameraMode == 1 || _activeState._cameraMode == 5)
 				nullsub_2();
 			*/
-			_vm->_backgroundItems->refreshPan();
+			_vm->_backgroundInstances->refreshPan();
 		}
 
 		if (isPanFinished()) {
@@ -336,7 +336,7 @@ void Camera::getActiveState(CameraState &state) {
 	state = _activeState;
 }
 
-void Camera::refreshPan(BackgroundItem *backgroundItem, WidthHeight &dimensions) {
+void Camera::refreshPan(BackgroundInstance *backgroundItem, WidthHeight &dimensions) {
 	Common::Point screenOffs = getScreenOffset();
 	int x = dimensions._width - _screenWidth;
 	int y = dimensions._height - _screenHeight;
diff --git a/engines/illusions/camera.h b/engines/illusions/camera.h
index 85d308d..2e4b760 100644
--- a/engines/illusions/camera.h
+++ b/engines/illusions/camera.h
@@ -29,7 +29,7 @@
 
 namespace Illusions {
 
-class BackgroundItem;
+class BackgroundInstance;
 
 struct CameraState {
 	int _cameraMode;
@@ -87,7 +87,7 @@ public:
 	bool isAtPanLimit(int limitNum);
 	void setActiveState(CameraState &state);
 	void getActiveState(CameraState &state);
-	void refreshPan(BackgroundItem *backgroundItem, WidthHeight &dimensions);
+	void refreshPan(BackgroundInstance *backgroundItem, WidthHeight &dimensions);
 protected:
 	IllusionsEngine *_vm;
 	CameraState _activeState;
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
index c2c66ac..4d4caf6 100644
--- a/engines/illusions/fontresource.cpp
+++ b/engines/illusions/fontresource.cpp
@@ -29,18 +29,13 @@ namespace Illusions {
 // FontResourceLoader
 
 void FontResourceLoader::load(Resource *resource) {
-	debug("FontResourceLoader::load() Loading font %08X from %s...", resource->_resId, resource->_filename.c_str());
-
-	FontResource *fontResource = new FontResource();
-	fontResource->load(resource);
-	resource->_refId = fontResource;
-	_vm->_dict->addFont(resource->_resId, fontResource);
+	FontInstance *fontInstance = new FontInstance(_vm);
+	fontInstance->load(resource);
+	resource->_instance = fontInstance;
 }
 
 void FontResourceLoader::unload(Resource *resource) {
-	FontResource *fontResource = _vm->_dict->findFont(resource->_resId);
-	delete fontResource;
-	_vm->_dict->removeFont(resource->_resId);
+	// TODO Remove method
 }
 
 void FontResourceLoader::buildFilename(Resource *resource) {
@@ -123,4 +118,21 @@ CharInfo *FontResource::getCharInfo(uint16 c) {
 	return 0;
 }
 
+// FontInstance
+
+FontInstance::FontInstance(IllusionsEngine *vm) : _vm(vm) {
+}
+
+void FontInstance::load(Resource *resource) {
+	_fontResource = new FontResource();
+	_fontResource->load(resource);
+	_resId = resource->_resId;
+	_vm->_dict->addFont(resource->_resId, _fontResource);
+}
+
+void FontInstance::unload() {
+	delete _fontResource;
+	_vm->_dict->removeFont(_resId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/fontresource.h b/engines/illusions/fontresource.h
index 33360d7..d658ddc 100644
--- a/engines/illusions/fontresource.h
+++ b/engines/illusions/fontresource.h
@@ -79,6 +79,17 @@ public:
 	CharRange *getCharRange(uint16 c);
 };
 
+class FontInstance : public ResourceInstance {
+public:
+	FontInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+	FontResource *_fontResource;
+	uint32 _resId;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_FONTRESOURCE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index c51eb87..771afb7 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -147,8 +147,8 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	_camera->update(currTime);
 	updateFader();
 
-	// TODO Move to BackgroundItems class
-	BackgroundItem *backgroundItem = _backgroundItems->findActiveBackground();
+	// TODO Move to BackgroundInstanceList class
+	BackgroundInstance *backgroundItem = _backgroundInstances->findActiveBackground();
 	if (backgroundItem) {
 		BackgroundResource *bgRes = backgroundItem->_bgRes;
 		for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
@@ -325,4 +325,8 @@ void IllusionsEngine::setDefaultTextPosition(Common::Point &position) {
 	_defaultTextPosition = position;
 }
 
+FramesList *IllusionsEngine::findActorSequenceFrames(Sequence *sequence) {
+	return _actorItems->findSequenceFrames(sequence);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 522d1dc..59de64c 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -51,10 +51,10 @@ class ResourceSystem;
 struct SurfInfo;
 
 class ActorItem;
-class ActorItems;
+class ActorInstanceList;
 class ActorType;
 class BackgroundItem;
-class BackgroundItems;
+class BackgroundInstanceList;
 class BackgroundResource;
 class Camera;
 class Control;
@@ -106,8 +106,8 @@ public:
 	Screen *_screen;
 	ScreenText *_screenText;
 	Input *_input;
-	ActorItems *_actorItems;
-	BackgroundItems *_backgroundItems;
+	ActorInstanceList *_actorItems;
+	BackgroundInstanceList *_backgroundInstances;
 	Camera *_camera;
 	Controls *_controls;
 	TalkItems *_talkItems;
@@ -168,6 +168,8 @@ public:
 	void getDefaultTextPosition(Common::Point &position);
 	void setDefaultTextPosition(Common::Point &position);
 
+	FramesList *findActorSequenceFrames(Sequence *sequence);
+
 	virtual void setDefaultTextCoords() = 0;
 	virtual void loadSpecialCode(uint32 resId) = 0;
 	virtual void unloadSpecialCode(uint32 resId) = 0;
@@ -188,7 +190,7 @@ public:
 	virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
 	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
-
+		
 #if 0
 
 	// Savegame API
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index e98d0c1..c73d9f4 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -205,8 +205,8 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_screen = new Screen(this, 640, 480, 16);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
-	_actorItems = new ActorItems(this);
-	_backgroundItems = new BackgroundItems(this);
+	_actorItems = new ActorInstanceList(this);
+	_backgroundInstances = new BackgroundInstanceList(this);
 	_camera = new Camera(this);
 	_controls = new Controls(this);
 	_cursor = new Cursor(this);
@@ -260,7 +260,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _cursor;
 	delete _controls;
 	delete _camera;
-	delete _backgroundItems;
+	delete _backgroundInstances;
 	delete _actorItems;
 	delete _input;
 	delete _screenText;
@@ -370,7 +370,7 @@ Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
 
 Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
 	Common::Point pt;
-	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt) ||
+	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt) ||
 		_actorItems->findNamedPoint(namedPointId, pt) ||
 		_controls->findNamedPoint(namedPointId, pt))
 		return pt;
@@ -554,13 +554,13 @@ void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
 	_threads->suspendThreadsByTag(sceneId, threadId);
 	_controls->pauseControlsByTag(sceneId);
 	_actorItems->pauseByTag(sceneId);
-	_backgroundItems->pauseByTag(sceneId);
+	_backgroundInstances->pauseByTag(sceneId);
 	_activeScenes.pauseActiveScene();
 }
 
 void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
 	uint32 sceneId = _activeScenes.getCurrentScene();
-	_backgroundItems->unpauseByTag(sceneId);
+	_backgroundInstances->unpauseByTag(sceneId);
 	_actorItems->unpauseByTag(sceneId);
 	_controls->unpauseControlsByTag(sceneId);
 	_threads->notifyThreadsByTag(sceneId, threadId);
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 5d845a4..bc5b667 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -99,8 +99,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_screen = new Screen(this, 320, 200, 8);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
-	_actorItems = new ActorItems(this);
-	_backgroundItems = new BackgroundItems(this);
+	_actorItems = new ActorInstanceList(this);
+	_backgroundInstances = new BackgroundInstanceList(this);
 	_camera = new Camera(this);
 	_controls = new Controls(this);
 	_talkItems = new TalkItems(this);
@@ -175,7 +175,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _talkItems;
 	delete _controls;
 	delete _camera;
-	delete _backgroundItems;
+	delete _backgroundInstances;
 	delete _actorItems;
 	delete _input;
 	delete _screenText;
@@ -347,7 +347,7 @@ Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
 Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
 	Common::Point pt;
 	Common::Point currPan = _camera->getCurrentPan();
-	if (_backgroundItems->findActiveBackgroundNamedPoint(namedPointId, pt)) {
+	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt)) {
 		return pt;
 	} else if (namedPointId - 0x00070001 > 209) {
 		if (_controls->findNamedPoint(namedPointId, pt)) {
@@ -727,11 +727,11 @@ void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
 	_threads->suspendThreads(threadId);
 	_controls->pauseControls();
 	_actorItems->pauseByTag(sceneId);
-	_backgroundItems->pauseByTag(sceneId);
+	_backgroundInstances->pauseByTag(sceneId);
 }
 
 void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
-	_backgroundItems->unpauseByTag(sceneId);
+	_backgroundInstances->unpauseByTag(sceneId);
 	_actorItems->unpauseByTag(sceneId);
 	_controls->unpauseControls();
 	_threads->notifyThreads(threadId);
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index 3d99541..bb6048b 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -28,6 +28,17 @@
 
 namespace Illusions {
 
+// ResourceInstance
+
+void ResourceInstance::load(Resource *resource) {
+}
+
+void ResourceInstance::unload() {
+}
+
+ResourceInstance::~ResourceInstance() {
+}
+
 // Resource
 
 void Resource::loadData() {
@@ -45,7 +56,7 @@ void Resource::loadData() {
 void Resource::unloadData() {
 	debug("Resource::unloadData()");
 	
-	delete _data;
+	free(_data);
 	_data = 0;
 	_dataSize = 0;
 }
@@ -75,7 +86,6 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 	resource->_resId = resId;
 	resource->_tag = tag;
 	resource->_threadId = threadId;
-	resource->_resourceLoader = resourceLoader;
 	resource->_gameId = _vm->getGameId();
 
 	resourceLoader->buildFilename(resource);
@@ -87,8 +97,8 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 	
 	resourceLoader->load(resource);
 	
-	if (resourceLoader->isFlag(kRlfFreeDataAfterUse)) {
-		debug("ResourceSystem::loadResource() kRlfFreeDataAfterUse");
+	if (resourceLoader->isFlag(kRlfFreeDataAfterLoad)) {
+		debug("ResourceSystem::loadResource() kRlfFreeDataAfterLoad");
 		resource->unloadData();
 	}
 	
@@ -135,7 +145,6 @@ Resource *ResourceSystem::getResource(uint32 resId) {
 
 void ResourceSystem::unloadResource(Resource *resource) {
 	debug("Unloading %08X... (tag: %08X)", resource->_resId, resource->_tag);
-	resource->_resourceLoader->unload(resource);
 	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByValue(resource));
 	if (it != _resources.end())
 		_resources.remove_at(it - _resources.begin());
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index 0a214c5..37e843e 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -37,6 +37,14 @@ namespace Illusions {
 
 class BaseResourceLoader;
 class IllusionsEngine;
+struct Resource;
+
+class ResourceInstance {
+public:
+	virtual void load(Resource *resource);
+	virtual void unload();
+	virtual ~ResourceInstance();
+};
 
 struct Resource {
 	bool _loaded;
@@ -45,28 +53,24 @@ struct Resource {
 	uint32 _threadId;
 	byte *_data;
 	uint32 _dataSize;
-	BaseResourceLoader *_resourceLoader;
-	void *_refId;
 	int _gameId;
 	Common::String _filename;
-	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0),
-	_resourceLoader(0), _refId(0) {}
+	ResourceInstance *_instance;
+	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0), _instance(0) {
+	}
 	~Resource() {
+		if (_instance)
+			_instance->unload();
+		delete _instance;
 		unloadData();
 	}
 	void loadData();
 	void unloadData();
 };
 
-struct ResourceLoaderInfo {
-	Resource *_res;
-	byte *_data;
-	uint32 _dataSize;
-};
-
 enum {
 	kRlfLoadFile,
-	kRlfFreeDataAfterUse
+	kRlfFreeDataAfterLoad
 };
 
 class BaseResourceLoader {
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 5d93e2a..28e56da 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -28,18 +28,13 @@ namespace Illusions {
 // ScriptResourceLoader
 
 void ScriptResourceLoader::load(Resource *resource) {
-	debug(2, "ScriptResourceLoader::load() Loading script %08X from %s...", resource->_resId, resource->_filename.c_str());
-
-	ScriptResource *scriptResource = new ScriptResource();
-	scriptResource->load(resource);
-
-	_vm->_scriptResource = scriptResource;
-	
+	ScriptInstance *scriptInstance = new ScriptInstance(_vm);
+	scriptInstance->load(resource);
+	resource->_instance = scriptInstance;
 }
 
 void ScriptResourceLoader::unload(Resource *resource) {
-	delete _vm->_scriptResource;
-	_vm->_scriptResource = 0;
+	// TODO Remove method
 }
 
 void ScriptResourceLoader::buildFilename(Resource *resource) {
@@ -370,4 +365,20 @@ void ScriptResource::fixupProgInfosDuckman() {
 		_progInfos[i].fixupProgInfosDuckman();
 }
 
+// ScriptInstance
+
+ScriptInstance::ScriptInstance(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void ScriptInstance::load(Resource *resource) {
+	_vm->_scriptResource = new ScriptResource();
+	_vm->_scriptResource->load(resource);
+}
+
+void ScriptInstance::unload() {
+	delete _vm->_scriptResource;
+	_vm->_scriptResource = 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index 35c3ed0..c168294 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -134,6 +134,15 @@ public:
 	void fixupProgInfosDuckman();
 };
 
+class ScriptInstance : public ResourceInstance {
+public:
+	ScriptInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_ACTORRESOURCE_H
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index ef76c78..f2a0225 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -283,7 +283,7 @@ void SequenceOpcodes::opSetPathCtrY(Control *control, OpCall &opCall) {
 
 void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkPointsIndex);
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	control->_actor->_flags |= 2;
 	control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
 }
@@ -301,7 +301,7 @@ void SequenceOpcodes::opSetScale(Control *control, OpCall &opCall) {
 
 void SequenceOpcodes::opSetScaleLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(scaleLayerIndex);
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	control->_actor->_flags |= 4;
 	control->_actor->_scaleLayer = bgRes->getScaleLayer(scaleLayerIndex - 1);
 	int scale = control->_actor->_scaleLayer->getScale(control->_actor->_position);
@@ -314,7 +314,7 @@ void SequenceOpcodes::opDeactivatePathWalkRects(Control *control, OpCall &opCall
 
 void SequenceOpcodes::opSetPathWalkRects(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkRectsIndex);
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	control->_actor->_flags |= 0x0010;
 	control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
 }
@@ -327,7 +327,7 @@ void SequenceOpcodes::opSetPriority(Control *control, OpCall &opCall) {
 
 void SequenceOpcodes::opSetPriorityLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(priorityLayerIndex);
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	control->_actor->_flags |= 8;
 	control->_actor->_priorityLayer = bgRes->getPriorityLayer(priorityLayerIndex - 1);
 	int priority = control->_actor->_priorityLayer->getPriority(control->_actor->_position);
@@ -340,7 +340,7 @@ void SequenceOpcodes::opDisableAutoRegionLayer(Control *control, OpCall &opCall)
 
 void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(regionLayerIndex);
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	control->_actor->_flags |= 0x20;
 	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
 }
@@ -348,7 +348,7 @@ void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 void SequenceOpcodes::opSetPalette(Control *control, OpCall &opCall) {
 	ARG_INT16(paletteIndex);
 	ARG_BYTE(fromIndex);
-	BackgroundResource *bgRes = _vm->_backgroundItems->getActiveBgResource();
+	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	Palette *palette = bgRes->getPalette(paletteIndex - 1);
 	_vm->_screen->setPalette(palette->_palette, fromIndex, palette->_count);
 }
diff --git a/engines/illusions/soundresource.cpp b/engines/illusions/soundresource.cpp
index 2e65843..1f14e2c 100644
--- a/engines/illusions/soundresource.cpp
+++ b/engines/illusions/soundresource.cpp
@@ -29,6 +29,7 @@ namespace Illusions {
 // SoundGroupResourceLoader
 
 void SoundGroupResourceLoader::load(Resource *resource) {
+#if 0 // ### TODO
 	debug("SoundGroupResourceLoader::load() Loading sound group %08X...", resource->_resId);
 	SoundGroupResource *soundGroupResource = new SoundGroupResource();
 	soundGroupResource->load(resource->_data, resource->_dataSize);
@@ -38,12 +39,15 @@ void SoundGroupResourceLoader::load(Resource *resource) {
 		SoundEffect *soundEffect = &soundGroupResource->_soundEffects[i];
 		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
 	}
+#endif	
 }
 
 void SoundGroupResourceLoader::unload(Resource *resource) {
+#if 0 // ### TODO
 	debug("SoundGroupResourceLoader::unload() Unloading sound group %08X...", resource->_resId);
 	_vm->_soundMan->unloadSounds(resource->_resId);
 	delete (SoundGroupResource*)resource->_refId;
+#endif	
 }
 
 void SoundGroupResourceLoader::buildFilename(Resource *resource) {
@@ -53,7 +57,7 @@ void SoundGroupResourceLoader::buildFilename(Resource *resource) {
 bool SoundGroupResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile/* ||
-		flag == kRlfFreeDataAfterUse*/;
+		flag == kRlfFreeDataAfterLoad*/;
 }
 
 // SoundEffect
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index f100aeb..e9d5433 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -29,20 +29,11 @@ namespace Illusions {
 // TalkResourceLoader
 
 void TalkResourceLoader::load(Resource *resource) {
-	debug("TalkResourceLoader::load() Loading text %08X from %s...", resource->_resId, resource->_filename.c_str());
-
-	TalkResource *talkResource = new TalkResource();
-	talkResource->load(resource->_data, resource->_dataSize);
-	resource->_refId = talkResource;
-
-	TalkItem *talkItem = _vm->_talkItems->newTalkItem(resource->_resId, resource->_tag, talkResource);
-	talkItem->registerResources();
+	resource->_instance = _vm->_talkItems->createTalkInstance(resource);
 }
 
 void TalkResourceLoader::unload(Resource *resource) {
-	TalkItem *talkItem = _vm->_talkItems->findTalkItem(resource->_resId);
-	talkItem->unregisterResources();
-	_vm->_talkItems->freeTalkItem(talkItem);
+	// TODO Remove method
 }
 
 void TalkResourceLoader::buildFilename(Resource *resource) {
@@ -91,41 +82,53 @@ void TalkResource::load(byte *data, uint32 dataSize) {
 	}
 }
 
-// TalkItem
+// TalkInstance
 
-TalkItem::TalkItem(IllusionsEngine *vm, uint32 talkId, uint32 tag, TalkResource *talkResource)
-	: _vm(vm), _talkId(talkId), _tag(tag), _talkRes(talkResource), _pauseCtr(0) {
+TalkInstance::TalkInstance(IllusionsEngine *vm)
+	: _vm(vm), _pauseCtr(0) {
 }
 
-TalkItem::~TalkItem() {
-}
-
-void TalkItem::registerResources() {
-	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
-		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
-		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
-	}
+void TalkInstance::load(Resource *resource) {
+	TalkResource *talkResource = new TalkResource();
+	talkResource->load(resource->_data, resource->_dataSize);
+	_talkRes = talkResource;
+	_talkId = resource->_resId;
+	_tag = resource->_tag;
+	registerResources();
 }
 
-void TalkItem::unregisterResources() {
-	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
-		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
-		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
-	}
+void TalkInstance::unload() {
+	unregisterResources();
+	_vm->_talkItems->removeTalkInstance(this);
+	delete _talkRes;
 }
 
-void TalkItem::pause() {
+void TalkInstance::pause() {
 	++_pauseCtr;
 	if (_pauseCtr == 1)
 		unregisterResources();
 }
 
-void TalkItem::unpause() {
+void TalkInstance::unpause() {
 	--_pauseCtr;
 	if (_pauseCtr == 0)
 		registerResources();
 }
 
+void TalkInstance::registerResources() {
+	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
+		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
+	}
+}
+
+void TalkInstance::unregisterResources() {
+	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
+		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
+	}
+}
+
 // TalkItems
 
 TalkItems::TalkItems(IllusionsEngine *vm)
@@ -135,25 +138,25 @@ TalkItems::TalkItems(IllusionsEngine *vm)
 TalkItems::~TalkItems() {
 }
 
-TalkItem *TalkItems::newTalkItem(uint32 talkId, uint32 tag, TalkResource *talkResource) {
-	TalkItem *talkItem = new TalkItem(_vm, talkId, tag, talkResource);
-	_items.push_back(talkItem);
-	return talkItem;
+TalkInstance *TalkItems::createTalkInstance(Resource *resource) {
+	TalkInstance *talkInstance = new TalkInstance(_vm);
+	talkInstance->load(resource);
+	_items.push_back(talkInstance);
+	return talkInstance;
 }
 
-void TalkItems::freeTalkItem(TalkItem *talkItem) {
-	_items.remove(talkItem);
-	delete talkItem;
+void TalkItems::removeTalkInstance(TalkInstance *talkInstance) {
+	_items.remove(talkInstance);
 }
 
-TalkItem *TalkItems::findTalkItem(uint32 talkId) {
+TalkInstance *TalkItems::findTalkItem(uint32 talkId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_talkId == talkId)
 			return (*it);
 	return 0;
 }
 
-TalkItem *TalkItems::findTalkItemByTag(uint32 tag) {
+TalkInstance *TalkItems::findTalkItemByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
 			return (*it);
@@ -161,15 +164,15 @@ TalkItem *TalkItems::findTalkItemByTag(uint32 tag) {
 }
 
 void TalkItems::pauseByTag(uint32 tag) {
-	TalkItem *talkItem = findTalkItemByTag(tag);
-	if (talkItem)
-		talkItem->pause();
+	TalkInstance *talkInstance = findTalkItemByTag(tag);
+	if (talkInstance)
+		talkInstance->pause();
 }
 
 void TalkItems::unpauseByTag(uint32 tag) {
-	TalkItem *talkItem = findTalkItemByTag(tag);
-	if (talkItem)
-		talkItem->unpause();
+	TalkInstance *talkInstance = findTalkItemByTag(tag);
+	if (talkInstance)
+		talkInstance->unpause();
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/talkresource.h b/engines/illusions/talkresource.h
index ff82645..7f86a36 100644
--- a/engines/illusions/talkresource.h
+++ b/engines/illusions/talkresource.h
@@ -61,12 +61,11 @@ public:
 	TalkEntry *_talkEntries;
 };
 
-class TalkItem {
+class TalkInstance : public ResourceInstance {
 public:
-	TalkItem(IllusionsEngine *vm, uint32 talkId, uint32 tag, TalkResource *talkResource);
-	~TalkItem();
-	void registerResources();
-	void unregisterResources();
+	TalkInstance(IllusionsEngine *vm);
+	virtual void load(Resource *resource);
+	virtual void unload();
 	void pause();
 	void unpause();
 public:
@@ -75,21 +74,23 @@ public:
 	uint32 _tag;
 	TalkResource *_talkRes;
 	int _pauseCtr;
+	void registerResources();
+	void unregisterResources();
 };
 
 class TalkItems {
 public:
 	TalkItems(IllusionsEngine *vm);
 	~TalkItems();
-	TalkItem *newTalkItem(uint32 talkId, uint32 tag, TalkResource *talkResource);
-	void freeTalkItem(TalkItem *talkItem);
-	TalkItem *findTalkItem(uint32 talkId);
-	TalkItem *findTalkItemByTag(uint32 tag);
+	TalkInstance *createTalkInstance(Resource *resource);
+	void removeTalkInstance(TalkInstance *talkInstance);
+	TalkInstance *findTalkItem(uint32 talkId);
+	TalkInstance *findTalkItemByTag(uint32 tag);
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
 //protected:
 public:
-	typedef Common::List<TalkItem*> Items;
+	typedef Common::List<TalkInstance*> Items;
 	typedef Items::iterator ItemsIterator;
 	IllusionsEngine *_vm;
 	Items _items;


Commit: ace0d042ec9d31956bdbb2c2f2e116a8e9860983
    https://github.com/scummvm/scummvm/commit/ace0d042ec9d31956bdbb2c2f2e116a8e9860983
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Refactoring the resource system (not done yet)

Changed paths:
    engines/illusions/actorresource.cpp
    engines/illusions/actorresource.h
    engines/illusions/backgroundresource.cpp
    engines/illusions/backgroundresource.h
    engines/illusions/fontresource.cpp
    engines/illusions/fontresource.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/midiresource.cpp
    engines/illusions/midiresource.h
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h
    engines/illusions/scriptresource.cpp
    engines/illusions/scriptresource.h
    engines/illusions/soundresource.cpp
    engines/illusions/soundresource.h
    engines/illusions/talkresource.cpp
    engines/illusions/talkresource.h


diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
index 43cb314..3cca917 100644
--- a/engines/illusions/actorresource.cpp
+++ b/engines/illusions/actorresource.cpp
@@ -29,11 +29,7 @@ namespace Illusions {
 // ActorResourceLoader
 
 void ActorResourceLoader::load(Resource *resource) {
-	resource->_instance = _vm->_actorItems->createActorInstance(resource);
-}
-
-void ActorResourceLoader::unload(Resource *resource) {
-	// TODO Remove method
+	resource->_instance = _vm->_actorInstances->createActorInstance(resource);
 }
 
 void ActorResourceLoader::buildFilename(Resource *resource) {
@@ -197,7 +193,7 @@ void ActorInstance::load(Resource *resource) {
 void ActorInstance::unload() {
 	if (_pauseCtr <= 0)
 		unregisterResources();
-	_vm->_actorItems->removeActorInstance(this);
+	_vm->_actorInstances->removeActorInstance(this);
 	delete _actorResource;
 }
 
@@ -266,7 +262,6 @@ ActorInstance *ActorInstanceList::createActorInstance(Resource *resource) {
 
 void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
 	_items.remove(actorInstance);
-	debug("removeActorInstance() AFTER _items.size(): %d", _items.size());
 }
 
 void ActorInstanceList::pauseByTag(uint32 tag) {
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
index cc8feeb..2ccb9ef 100644
--- a/engines/illusions/actorresource.h
+++ b/engines/illusions/actorresource.h
@@ -36,7 +36,6 @@ public:
 	ActorResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~ActorResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
@@ -99,8 +98,8 @@ public:
 	ActorInstance(IllusionsEngine *vm);              
 	virtual void load(Resource *resource);
 	virtual void unload();
-	void pause();
-	void unpause();
+	virtual void pause();
+	virtual void unpause();
 public:
 	IllusionsEngine *_vm;
 	uint32 _tag;
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
index 34a42c7..e8059bc 100644
--- a/engines/illusions/backgroundresource.cpp
+++ b/engines/illusions/backgroundresource.cpp
@@ -37,10 +37,6 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	resource->_instance = _vm->_backgroundInstances->createBackgroundInstance(resource);
 }
 
-void BackgroundResourceLoader::unload(Resource *resource) {
-	// TODO Remove method
-}
-
 void BackgroundResourceLoader::buildFilename(Resource *resource) {
 	resource->_filename = Common::String::format("%08X.bg", resource->_resId);
 }
@@ -415,6 +411,46 @@ void BackgroundInstance::unload() {
 	_vm->setDefaultTextCoords();
 }
 
+void BackgroundInstance::pause() {
+	++_pauseCtr;
+	if (_pauseCtr <= 1) {
+		unregisterResources();
+		_vm->setDefaultTextCoords();
+		_vm->_camera->getActiveState(_savedCameraState);
+		_savedPalette = new byte[1024];
+		_vm->_screen->getPalette(_savedPalette);
+		freeSurface();
+	}
+}
+
+void BackgroundInstance::unpause() {
+	--_pauseCtr;
+	if (_pauseCtr <= 0) {
+		registerResources();
+		initSurface();
+		_vm->_screen->setPalette(_savedPalette, 1, 256);
+		delete[] _savedPalette;
+		_savedPalette = 0;
+		// TODO _vm->_screen->_fadeClear();
+		_vm->_camera->setActiveState(_savedCameraState);
+		_vm->_backgroundInstances->refreshPan();
+	}
+}
+
+void BackgroundInstance::registerResources() {
+	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &_bgRes->_regionSequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	}
+}
+	
+void BackgroundInstance::unregisterResources() {
+	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &_bgRes->_regionSequences[i];
+		_vm->_dict->removeSequence(sequence->_sequenceId);
+	}
+}	
+
 void BackgroundInstance::initSurface() {
 	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
 		_surfaces[i] = 0;
@@ -446,20 +482,6 @@ void BackgroundInstance::drawTiles(Graphics::Surface *surface, TileMap &tileMap,
 	}
 }
 
-void BackgroundInstance::registerResources() {
-	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
-		Sequence *sequence = &_bgRes->_regionSequences[i];
-		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-	}
-}
-	
-void BackgroundInstance::unregisterResources() {
-	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
-		Sequence *sequence = &_bgRes->_regionSequences[i];
-		_vm->_dict->removeSequence(sequence->_sequenceId);
-	}
-}	
-
 void BackgroundInstance::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
 	const int kTileWidth = 32;
 	const int kTileHeight = 8;
@@ -511,32 +533,6 @@ void BackgroundInstance::drawTiles16(Graphics::Surface *surface, TileMap &tileMa
 	}
 }
 
-void BackgroundInstance::pause() {
-	++_pauseCtr;
-	if (_pauseCtr <= 1) {
-		unregisterResources();
-		_vm->setDefaultTextCoords();
-		_vm->_camera->getActiveState(_savedCameraState);
-		_savedPalette = new byte[1024];
-		_vm->_screen->getPalette(_savedPalette);
-		freeSurface();
-	}
-}
-
-void BackgroundInstance::unpause() {
-	--_pauseCtr;
-	if (_pauseCtr <= 0) {
-		registerResources();
-		initSurface();
-		_vm->_screen->setPalette(_savedPalette, 1, 256);
-		delete[] _savedPalette;
-		_savedPalette = 0;
-		// TODO _vm->_screen->_fadeClear();
-		_vm->_camera->setActiveState(_savedCameraState);
-		_vm->_backgroundInstances->refreshPan();
-	}
-}
-
 // BackgroundInstanceList
 
 BackgroundInstanceList::BackgroundInstanceList(IllusionsEngine *vm)
@@ -555,7 +551,6 @@ BackgroundInstance *BackgroundInstanceList::createBackgroundInstance(Resource *r
 
 void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgroundInstance) {
 	_items.remove(backgroundInstance);
-	debug("removeActorInstance() AFTER _items.size(): %d", _items.size());
 }
 
 void BackgroundInstanceList::pauseByTag(uint32 tag) {
@@ -570,7 +565,7 @@ void BackgroundInstanceList::unpauseByTag(uint32 tag) {
 			(*it)->unpause();
 }
 
-BackgroundInstance *BackgroundInstanceList::findActiveBackground() {
+BackgroundInstance *BackgroundInstanceList::findActiveBackgroundInstance() {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_pauseCtr == 0)
 			return (*it);
@@ -585,20 +580,20 @@ BackgroundInstance *BackgroundInstanceList::findBackgroundByResource(BackgroundR
 }
 
 BackgroundResource *BackgroundInstanceList::getActiveBgResource() {
-	BackgroundInstance *background = findActiveBackground();
+	BackgroundInstance *background = findActiveBackgroundInstance();
 	if (background)
 		return background->_bgRes;
 	return 0;
 }
 
 WidthHeight BackgroundInstanceList::getMasterBgDimensions() {
-	BackgroundInstance *backgroundInstance = findActiveBackground();
+	BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
 	int16 index = backgroundInstance->_bgRes->findMasterBgIndex();
 	return backgroundInstance->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
 }
 
 void BackgroundInstanceList::refreshPan() {
-	BackgroundInstance *backgroundInstance = findActiveBackground();
+	BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
 	if (backgroundInstance) {
 		WidthHeight dimensions = getMasterBgDimensions();
 		_vm->_camera->refreshPan(backgroundInstance, dimensions);
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
index 673f69f..9438501 100644
--- a/engines/illusions/backgroundresource.h
+++ b/engines/illusions/backgroundresource.h
@@ -47,7 +47,6 @@ public:
 	BackgroundResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~BackgroundResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
@@ -188,11 +187,8 @@ public:
 	BackgroundInstance(IllusionsEngine *vm);
 	virtual void load(Resource *resource);
 	virtual void unload();
-	void initSurface();
-	void freeSurface();
-	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
-	void pause();
-	void unpause();
+	virtual void pause();
+	virtual void unpause();
 public:
 	IllusionsEngine *_vm;
 	uint32 _tag;
@@ -204,6 +200,9 @@ public:
 	byte *_savedPalette;
 	void registerResources();	
 	void unregisterResources();	
+	void initSurface();
+	void freeSurface();
+	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 	void drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 	void drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
 };
@@ -216,7 +215,7 @@ public:
 	void removeBackgroundInstance(BackgroundInstance *backgroundInstance);
 	void pauseByTag(uint32 tag);
 	void unpauseByTag(uint32 tag);
-	BackgroundInstance *findActiveBackground();
+	BackgroundInstance *findActiveBackgroundInstance();
 	BackgroundInstance *findBackgroundByResource(BackgroundResource *backgroundResource);
 	BackgroundResource *getActiveBgResource();
 	WidthHeight getMasterBgDimensions();
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
index 4d4caf6..91007fc 100644
--- a/engines/illusions/fontresource.cpp
+++ b/engines/illusions/fontresource.cpp
@@ -34,10 +34,6 @@ void FontResourceLoader::load(Resource *resource) {
 	resource->_instance = fontInstance;
 }
 
-void FontResourceLoader::unload(Resource *resource) {
-	// TODO Remove method
-}
-
 void FontResourceLoader::buildFilename(Resource *resource) {
 	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
 }
diff --git a/engines/illusions/fontresource.h b/engines/illusions/fontresource.h
index d658ddc..8ea059f 100644
--- a/engines/illusions/fontresource.h
+++ b/engines/illusions/fontresource.h
@@ -35,7 +35,6 @@ public:
 	FontResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~FontResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 771afb7..95b0027 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -148,7 +148,7 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	updateFader();
 
 	// TODO Move to BackgroundInstanceList class
-	BackgroundInstance *backgroundItem = _backgroundInstances->findActiveBackground();
+	BackgroundInstance *backgroundItem = _backgroundInstances->findActiveBackgroundInstance();
 	if (backgroundItem) {
 		BackgroundResource *bgRes = backgroundItem->_bgRes;
 		for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
@@ -326,7 +326,7 @@ void IllusionsEngine::setDefaultTextPosition(Common::Point &position) {
 }
 
 FramesList *IllusionsEngine::findActorSequenceFrames(Sequence *sequence) {
-	return _actorItems->findSequenceFrames(sequence);
+	return _actorInstances->findSequenceFrames(sequence);
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 59de64c..9980698 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -72,7 +72,7 @@ class ScriptStack;
 class Sequence;
 class SoundMan;
 class SpecialCode;
-class TalkItems;
+class TalkInstanceList;
 class ThreadList;
 class UpdateFunctions;
 
@@ -106,11 +106,11 @@ public:
 	Screen *_screen;
 	ScreenText *_screenText;
 	Input *_input;
-	ActorInstanceList *_actorItems;
+	ActorInstanceList *_actorInstances;
 	BackgroundInstanceList *_backgroundInstances;
 	Camera *_camera;
 	Controls *_controls;
-	TalkItems *_talkItems;
+	TalkInstanceList *_talkItems;
 	ScriptOpcodes *_scriptOpcodes;
 	SpecialCode *_specialCode;
 	ThreadList *_threads;
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index c73d9f4..b71f4fe 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -205,12 +205,12 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_screen = new Screen(this, 640, 480, 16);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
-	_actorItems = new ActorInstanceList(this);
+	_actorInstances = new ActorInstanceList(this);
 	_backgroundInstances = new BackgroundInstanceList(this);
 	_camera = new Camera(this);
 	_controls = new Controls(this);
 	_cursor = new Cursor(this);
-	_talkItems = new TalkItems(this);
+	_talkItems = new TalkInstanceList(this);
 	_triggerFunctions = new TriggerFunctions();
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
@@ -261,7 +261,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _controls;
 	delete _camera;
 	delete _backgroundInstances;
-	delete _actorItems;
+	delete _actorInstances;
 	delete _input;
 	delete _screenText;
 	delete _screen;
@@ -371,7 +371,7 @@ Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
 Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
 	Common::Point pt;
 	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt) ||
-		_actorItems->findNamedPoint(namedPointId, pt) ||
+		_actorInstances->findNamedPoint(namedPointId, pt) ||
 		_controls->findNamedPoint(namedPointId, pt))
 		return pt;
 	// TODO
@@ -553,7 +553,7 @@ void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
 	_camera->pushCameraMode();
 	_threads->suspendThreadsByTag(sceneId, threadId);
 	_controls->pauseControlsByTag(sceneId);
-	_actorItems->pauseByTag(sceneId);
+	_actorInstances->pauseByTag(sceneId);
 	_backgroundInstances->pauseByTag(sceneId);
 	_activeScenes.pauseActiveScene();
 }
@@ -561,7 +561,7 @@ void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
 void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
 	uint32 sceneId = _activeScenes.getCurrentScene();
 	_backgroundInstances->unpauseByTag(sceneId);
-	_actorItems->unpauseByTag(sceneId);
+	_actorInstances->unpauseByTag(sceneId);
 	_controls->unpauseControlsByTag(sceneId);
 	_threads->notifyThreadsByTag(sceneId, threadId);
 	_camera->popCameraMode();
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index bc5b667..f9705b6 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -40,7 +40,6 @@
 #include "illusions/sound.h"
 #include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
-//TODO#include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/talkresource.h"
 #include "illusions/textdrawer.h"
 #include "illusions/thread.h"
@@ -99,11 +98,11 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_screen = new Screen(this, 320, 200, 8);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
-	_actorItems = new ActorInstanceList(this);
+	_actorInstances = new ActorInstanceList(this);
 	_backgroundInstances = new BackgroundInstanceList(this);
 	_camera = new Camera(this);
 	_controls = new Controls(this);
-	_talkItems = new TalkItems(this);
+	_talkItems = new TalkInstanceList(this);
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
@@ -176,7 +175,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _controls;
 	delete _camera;
 	delete _backgroundInstances;
-	delete _actorItems;
+	delete _actorInstances;
 	delete _input;
 	delete _screenText;
 	delete _screen;
@@ -291,13 +290,11 @@ void IllusionsEngine_Duckman::setDefaultTextCoords() {
 }
 
 void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
-//TODO	_specialCode = new BbdouSpecialCode(this);
-//TODO	_specialCode->init();
+	//TODO?
 }
 
 void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
-//TODO	delete _specialCode;
-//TODO	_specialCode = 0;
+	//TODO?
 }
 
 void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
@@ -726,13 +723,13 @@ bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint3
 void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
 	_threads->suspendThreads(threadId);
 	_controls->pauseControls();
-	_actorItems->pauseByTag(sceneId);
+	_actorInstances->pauseByTag(sceneId);
 	_backgroundInstances->pauseByTag(sceneId);
 }
 
 void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
 	_backgroundInstances->unpauseByTag(sceneId);
-	_actorItems->unpauseByTag(sceneId);
+	_actorInstances->unpauseByTag(sceneId);
 	_controls->unpauseControls();
 	_threads->notifyThreads(threadId);
 }
diff --git a/engines/illusions/midiresource.cpp b/engines/illusions/midiresource.cpp
index b6e181e..fc088ab 100644
--- a/engines/illusions/midiresource.cpp
+++ b/engines/illusions/midiresource.cpp
@@ -34,9 +34,6 @@ void MidiGroupResourceLoader::load(Resource *resource) {
 	
 }
 
-void MidiGroupResourceLoader::unload(Resource *resource) {
-}
-
 void MidiGroupResourceLoader::buildFilename(Resource *resource) {
 	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
 }
diff --git a/engines/illusions/midiresource.h b/engines/illusions/midiresource.h
index 54ad876..9032e99 100644
--- a/engines/illusions/midiresource.h
+++ b/engines/illusions/midiresource.h
@@ -35,7 +35,6 @@ public:
 	MidiGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~MidiGroupResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index bb6048b..9e81f1f 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -36,6 +36,12 @@ void ResourceInstance::load(Resource *resource) {
 void ResourceInstance::unload() {
 }
 
+void ResourceInstance::pause() {
+}
+
+void ResourceInstance::unpause() {
+}
+
 ResourceInstance::~ResourceInstance() {
 }
 
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index 37e843e..edf1cdd 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -43,6 +43,8 @@ class ResourceInstance {
 public:
 	virtual void load(Resource *resource);
 	virtual void unload();
+	virtual void pause();
+	virtual void unpause();
 	virtual ~ResourceInstance();
 };
 
@@ -77,7 +79,6 @@ class BaseResourceLoader {
 public:
 	virtual ~BaseResourceLoader() {}
 	virtual void load(Resource *resource) = 0;
-	virtual void unload(Resource *resource) = 0;
 	virtual void buildFilename(Resource *resource) = 0;
 	virtual bool isFlag(int flag) = 0;
 };
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
index 28e56da..775ca73 100644
--- a/engines/illusions/scriptresource.cpp
+++ b/engines/illusions/scriptresource.cpp
@@ -33,10 +33,6 @@ void ScriptResourceLoader::load(Resource *resource) {
 	resource->_instance = scriptInstance;
 }
 
-void ScriptResourceLoader::unload(Resource *resource) {
-	// TODO Remove method
-}
-
 void ScriptResourceLoader::buildFilename(Resource *resource) {
 	resource->_filename = Common::String::format("%08X.scr", resource->_resId);
 }
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
index c168294..c2119b0 100644
--- a/engines/illusions/scriptresource.h
+++ b/engines/illusions/scriptresource.h
@@ -34,7 +34,6 @@ public:
 	ScriptResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~ScriptResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
diff --git a/engines/illusions/soundresource.cpp b/engines/illusions/soundresource.cpp
index 1f14e2c..efc16df 100644
--- a/engines/illusions/soundresource.cpp
+++ b/engines/illusions/soundresource.cpp
@@ -29,25 +29,9 @@ namespace Illusions {
 // SoundGroupResourceLoader
 
 void SoundGroupResourceLoader::load(Resource *resource) {
-#if 0 // ### TODO
-	debug("SoundGroupResourceLoader::load() Loading sound group %08X...", resource->_resId);
-	SoundGroupResource *soundGroupResource = new SoundGroupResource();
-	soundGroupResource->load(resource->_data, resource->_dataSize);
-	resource->_refId = soundGroupResource;
-
-	for (uint i = 0; i < soundGroupResource->_soundEffectsCount; ++i) {
-		SoundEffect *soundEffect = &soundGroupResource->_soundEffects[i];
-		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
-	}
-#endif	
-}
-
-void SoundGroupResourceLoader::unload(Resource *resource) {
-#if 0 // ### TODO
-	debug("SoundGroupResourceLoader::unload() Unloading sound group %08X...", resource->_resId);
-	_vm->_soundMan->unloadSounds(resource->_resId);
-	delete (SoundGroupResource*)resource->_refId;
-#endif	
+	SoundGroupInstance *soundGroupInstance = new SoundGroupInstance(_vm);
+	soundGroupInstance->load(resource);
+	resource->_instance = soundGroupInstance;
 }
 
 void SoundGroupResourceLoader::buildFilename(Resource *resource) {
@@ -98,4 +82,24 @@ void SoundGroupResource::load(byte *data, uint32 dataSize) {
 
 }
 
+// SoundGroupInstance
+
+SoundGroupInstance::SoundGroupInstance(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void SoundGroupInstance::load(Resource *resource) {
+	_soundGroupResource = new SoundGroupResource();
+	_soundGroupResource->load(resource->_data, resource->_dataSize);
+	for (uint i = 0; i < _soundGroupResource->_soundEffectsCount; ++i) {
+		SoundEffect *soundEffect = &_soundGroupResource->_soundEffects[i];
+		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
+	}
+	_resId = resource->_resId;
+}
+
+void SoundGroupInstance::unload() {
+	_vm->_soundMan->unloadSounds(_resId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/soundresource.h b/engines/illusions/soundresource.h
index 09b8817..638e8df 100644
--- a/engines/illusions/soundresource.h
+++ b/engines/illusions/soundresource.h
@@ -35,7 +35,6 @@ public:
 	SoundGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~SoundGroupResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
@@ -61,6 +60,17 @@ public:
 	SoundEffect *_soundEffects;
 };
 
+class SoundGroupInstance : public ResourceInstance {
+public:
+	SoundGroupInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+	SoundGroupResource *_soundGroupResource;
+	uint32 _resId;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
index e9d5433..17672ba 100644
--- a/engines/illusions/talkresource.cpp
+++ b/engines/illusions/talkresource.cpp
@@ -32,10 +32,6 @@ void TalkResourceLoader::load(Resource *resource) {
 	resource->_instance = _vm->_talkItems->createTalkInstance(resource);
 }
 
-void TalkResourceLoader::unload(Resource *resource) {
-	// TODO Remove method
-}
-
 void TalkResourceLoader::buildFilename(Resource *resource) {
 	resource->_filename = Common::String::format("%08X.tlk", resource->_resId);
 }
@@ -129,47 +125,47 @@ void TalkInstance::unregisterResources() {
 	}
 }
 
-// TalkItems
+// TalkInstanceList
 
-TalkItems::TalkItems(IllusionsEngine *vm)
+TalkInstanceList::TalkInstanceList(IllusionsEngine *vm)
 	: _vm(vm) {
 }
 
-TalkItems::~TalkItems() {
+TalkInstanceList::~TalkInstanceList() {
 }
 
-TalkInstance *TalkItems::createTalkInstance(Resource *resource) {
+TalkInstance *TalkInstanceList::createTalkInstance(Resource *resource) {
 	TalkInstance *talkInstance = new TalkInstance(_vm);
 	talkInstance->load(resource);
 	_items.push_back(talkInstance);
 	return talkInstance;
 }
 
-void TalkItems::removeTalkInstance(TalkInstance *talkInstance) {
+void TalkInstanceList::removeTalkInstance(TalkInstance *talkInstance) {
 	_items.remove(talkInstance);
 }
 
-TalkInstance *TalkItems::findTalkItem(uint32 talkId) {
+TalkInstance *TalkInstanceList::findTalkItem(uint32 talkId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_talkId == talkId)
 			return (*it);
 	return 0;
 }
 
-TalkInstance *TalkItems::findTalkItemByTag(uint32 tag) {
+TalkInstance *TalkInstanceList::findTalkItemByTag(uint32 tag) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
 		if ((*it)->_tag == tag)
 			return (*it);
 	return 0;
 }
 
-void TalkItems::pauseByTag(uint32 tag) {
+void TalkInstanceList::pauseByTag(uint32 tag) {
 	TalkInstance *talkInstance = findTalkItemByTag(tag);
 	if (talkInstance)
 		talkInstance->pause();
 }
 
-void TalkItems::unpauseByTag(uint32 tag) {
+void TalkInstanceList::unpauseByTag(uint32 tag) {
 	TalkInstance *talkInstance = findTalkItemByTag(tag);
 	if (talkInstance)
 		talkInstance->unpause();
diff --git a/engines/illusions/talkresource.h b/engines/illusions/talkresource.h
index 7f86a36..5e3797e 100644
--- a/engines/illusions/talkresource.h
+++ b/engines/illusions/talkresource.h
@@ -35,7 +35,6 @@ public:
 	TalkResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~TalkResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void unload(Resource *resource);
 	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
@@ -66,8 +65,8 @@ public:
 	TalkInstance(IllusionsEngine *vm);
 	virtual void load(Resource *resource);
 	virtual void unload();
-	void pause();
-	void unpause();
+	virtual void pause();
+	virtual void unpause();
 public:
 	IllusionsEngine *_vm;
 	uint32 _talkId;
@@ -78,10 +77,10 @@ public:
 	void unregisterResources();
 };
 
-class TalkItems {
+class TalkInstanceList {
 public:
-	TalkItems(IllusionsEngine *vm);
-	~TalkItems();
+	TalkInstanceList(IllusionsEngine *vm);
+	~TalkInstanceList();
 	TalkInstance *createTalkInstance(Resource *resource);
 	void removeTalkInstance(TalkInstance *talkInstance);
 	TalkInstance *findTalkItem(uint32 talkId);


Commit: faf7b31ff802786ded2bd177da6503e98fca3ebf
    https://github.com/scummvm/scummvm/commit/faf7b31ff802786ded2bd177da6503e98fca3ebf
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move thread-related files into threads subdirectory

Changed paths:
  A engines/illusions/threads/abortablethread.cpp
  A engines/illusions/threads/abortablethread.h
  A engines/illusions/threads/causethread_duckman.cpp
  A engines/illusions/threads/causethread_duckman.h
  A engines/illusions/threads/scriptthread.cpp
  A engines/illusions/threads/scriptthread.h
  A engines/illusions/threads/talkthread.cpp
  A engines/illusions/threads/talkthread.h
  A engines/illusions/threads/talkthread_duckman.cpp
  A engines/illusions/threads/talkthread_duckman.h
  A engines/illusions/threads/timerthread.cpp
  A engines/illusions/threads/timerthread.h
  R engines/illusions/abortablethread.cpp
  R engines/illusions/abortablethread.h
  R engines/illusions/causethread_duckman.cpp
  R engines/illusions/causethread_duckman.h
  R engines/illusions/scriptthread.cpp
  R engines/illusions/scriptthread.h
  R engines/illusions/talkthread.cpp
  R engines/illusions/talkthread.h
  R engines/illusions/talkthread_duckman.cpp
  R engines/illusions/talkthread_duckman.h
  R engines/illusions/timerthread.cpp
  R engines/illusions/timerthread.h
    engines/illusions/actor.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/module.mk
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/abortablethread.cpp b/engines/illusions/abortablethread.cpp
deleted file mode 100644
index 15c8f6c..0000000
--- a/engines/illusions/abortablethread.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/abortablethread.h"
-#include "illusions/input.h"
-#include "illusions/time.h"
-
-namespace Illusions {
-
-// AbortableThread
-
-AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	uint32 scriptThreadId, byte *scriptCodeIp)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId), 
-	_scriptCodeIp(scriptCodeIp), _status(1) {
-	_type = kTTAbortableThread;
-	_tag = _vm->getCurrentScene();
-	_vm->_input->discardButtons(8);
-}
-
-int AbortableThread::onUpdate() {
-	if (_status != 1 || _pauseCtr < 0)
-		return kTSTerminate;
-	if (_vm->_input->pollButton(8)) {
-		_vm->_threads->killThread(_scriptThreadId);
-		++_pauseCtr;
-		_vm->startTempScriptThread(_scriptCodeIp, _threadId, 0, 0, 0);
-		_status = 2;
-		return kTSSuspend;
-	}
-	return kTSYield;
-}
-
-void AbortableThread::onSuspend() {
-}
-
-void AbortableThread::onNotify() {
-}
-
-void AbortableThread::onPause() {
-}
-
-void AbortableThread::onResume() {
-}
-
-void AbortableThread::onTerminated() {
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/abortablethread.h b/engines/illusions/abortablethread.h
deleted file mode 100644
index 8fc2a47..0000000
--- a/engines/illusions/abortablethread.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_ABORTABLETHREAD_H
-#define ILLUSIONS_ABORTABLETHREAD_H
-
-#include "illusions/thread.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class AbortableThread : public Thread {
-public:
-	AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		uint32 scriptThreadId, byte *scriptCodeIp);
-	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
-public:
-	int _status;
-	byte *_scriptCodeIp;
-	uint32 _scriptThreadId;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_ABORTABLETHREAD_H
diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 65eff5d..bf7b0cc 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -30,8 +30,8 @@
 #include "illusions/screen.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/sequenceopcodes.h"
-#include "illusions/talkthread.h"
 #include "illusions/thread.h"
+#include "illusions/threads/talkthread.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/causethread_duckman.cpp b/engines/illusions/causethread_duckman.cpp
deleted file mode 100644
index 4fbe49e..0000000
--- a/engines/illusions/causethread_duckman.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions_duckman.h"
-#include "illusions/causethread_duckman.h"
-#include "illusions/actor.h"
-#include "illusions/input.h"
-
-namespace Illusions {
-
-// CauseThread_Duckman
-
-CauseThread_Duckman::CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	uint32 triggerThreadId)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _triggerThreadId(triggerThreadId), _flag(false) {
-	_type = kTTCauseThread;
-	_tag = _vm->getCurrentScene();
-}
-
-int CauseThread_Duckman::onUpdate() {
-	if (_flag) {
-		if (_vm->getCurrentScene() == _tag) {
-			Control *cursorCursor = _vm->getObjectControl(0x40004);
-			cursorCursor->appearActor();
-			_vm->_input->discardButtons(1);
-		}
-		return kTSTerminate;
-	} else {
-		_tag = _vm->getCurrentScene();
-		Control *cursorCursor = _vm->getObjectControl(0x40004);
-		cursorCursor->disappearActor();
-		_vm->_input->discardButtons(1);
-		_vm->startScriptThread(_triggerThreadId, _threadId);
-		_flag = true;
-		return kTSSuspend;
-	}
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/causethread_duckman.h b/engines/illusions/causethread_duckman.h
deleted file mode 100644
index 97bf00e..0000000
--- a/engines/illusions/causethread_duckman.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_CAUSETHREAD_DUCKMAN_H
-#define ILLUSIONS_CAUSETHREAD_DUCKMAN_H
-
-#include "illusions/thread.h"
-
-namespace Illusions {
-
-class IllusionsEngine_Duckman;
-
-class CauseThread_Duckman : public Thread {
-public:
-	CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		uint32 triggerThreadId);
-	virtual int onUpdate();
-public:
-	IllusionsEngine_Duckman *_vm;
-	bool _flag;
-	uint32 _triggerThreadId;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_CAUSETHREAD_DUCKMAN_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 95b0027..93619a0 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -42,7 +42,7 @@
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
 
-#include "illusions/talkthread.h"
+#include "illusions/threads/talkthread.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index b71f4fe..3766c00 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -45,10 +45,10 @@
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
 
-#include "illusions/abortablethread.h"
-#include "illusions/scriptthread.h"
-#include "illusions/talkthread.h"
-#include "illusions/timerthread.h"
+#include "illusions/threads/abortablethread.h"
+#include "illusions/threads/scriptthread.h"
+#include "illusions/threads/talkthread.h"
+#include "illusions/threads/timerthread.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index f9705b6..50aea3d 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -46,11 +46,11 @@
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
 
-#include "illusions/abortablethread.h"
-#include "illusions/causethread_duckman.h"
-#include "illusions/scriptthread.h"
-#include "illusions/talkthread_duckman.h"
-#include "illusions/timerthread.h"
+#include "illusions/threads/abortablethread.h"
+#include "illusions/threads/causethread_duckman.h"
+#include "illusions/threads/scriptthread.h"
+#include "illusions/threads/talkthread_duckman.h"
+#include "illusions/threads/timerthread.h"
 
 #include "audio/audiostream.h"
 #include "common/config-manager.h"
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index ec52049..63e0210 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -1,7 +1,6 @@
 MODULE := engines/illusions
 
 MODULE_OBJS := \
-	abortablethread.o \
 	actor.o \
 	actorresource.o \
 	backgroundresource.o \
@@ -10,7 +9,6 @@ MODULE_OBJS := \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
 	camera.o \
-	causethread_duckman.o \
 	cursor.o \
 	detection.o \
 	dictionary.o \
@@ -31,18 +29,20 @@ MODULE_OBJS := \
 	scriptopcodes_bbdou.o \
 	scriptopcodes_duckman.o \
 	scriptresource.o \
-	scriptthread.o \
 	sequenceopcodes.o \
 	sound.o \
 	soundresource.o \
 	specialcode.o \
 	talkresource.o \
-	talkthread.o \
-	talkthread_duckman.o \
 	textdrawer.o \
 	thread.o \
+	threads/abortablethread.o \
+	threads/causethread_duckman.o \
+	threads/scriptthread.o \
+	threads/talkthread.o \
+	threads/talkthread_duckman.o \
+	threads/timerthread.o \
 	time.o \
-	timerthread.o \
 	updatefunctions.o
 
 # This module can be built as a plugin
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index c8817f6..7d515a8 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -22,7 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/scriptopcodes.h"
-#include "illusions/scriptthread.h"
+#include "illusions/threads/scriptthread.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index e152c34..03990c8 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -29,10 +29,10 @@
 #include "illusions/screen.h"
 #include "illusions/scriptstack.h"
 #include "illusions/scriptresource.h"
-#include "illusions/scriptthread.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
 #include "illusions/talkresource.h"
+#include "illusions/threads/scriptthread.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 8dd6af1..076a400 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -29,10 +29,10 @@
 #include "illusions/screen.h"
 #include "illusions/scriptstack.h"
 #include "illusions/scriptresource.h"
-#include "illusions/scriptthread.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
 #include "illusions/talkresource.h"
+#include "illusions/threads/scriptthread.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/scriptthread.cpp b/engines/illusions/scriptthread.cpp
deleted file mode 100644
index a820289..0000000
--- a/engines/illusions/scriptthread.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/scriptthread.h"
-#include "illusions/scriptopcodes.h"
-
-namespace Illusions {
-
-// ScriptThread
-
-ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptCodeIp(scriptCodeIp), _value8(value8),
-	_valueC(valueC), _value10(value10), _sequenceStalled(0) {
-	_type = kTTScriptThread;
-	_tag = _vm->getCurrentScene();
-}
-
-int ScriptThread::onUpdate() {
-	OpCall opCall;
-	opCall._result = kTSRun;
-	opCall._callerThreadId = _threadId;
-	while (!_terminated && opCall._result == kTSRun) {
-		loadOpcode(opCall);
-		execOpcode(opCall);
-		_scriptCodeIp += opCall._deltaOfs;
-	}
-	if (_terminated)
-		opCall._result = kTSTerminate;
-	return opCall._result;
-}
-
-void ScriptThread::loadOpcode(OpCall &opCall) {
-	if (_vm->getGameId() == kGameIdDuckman) {
-		opCall._op = _scriptCodeIp[0] & 0x7F;
-		opCall._opSize = _scriptCodeIp[1];
-		opCall._threadId = _scriptCodeIp[0] & 0x80 ? _threadId : 0;
-	} else {
-		opCall._op = _scriptCodeIp[0];
-		opCall._opSize = _scriptCodeIp[1] >> 1;
-		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
-	}
-	opCall._code = _scriptCodeIp + 2;
-	opCall._deltaOfs = opCall._opSize;
-}
-
-void ScriptThread::execOpcode(OpCall &opCall) {
-	_vm->_scriptOpcodes->execOpcode(this, opCall);
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/scriptthread.h b/engines/illusions/scriptthread.h
deleted file mode 100644
index 0306c4f..0000000
--- a/engines/illusions/scriptthread.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SCRIPTTHREAD_H
-#define ILLUSIONS_SCRIPTTHREAD_H
-
-#include "illusions/thread.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-struct OpCall;
-
-class ScriptThread : public Thread {
-public:
-	ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
-	virtual int onUpdate();
-public:
-	int16 _sequenceStalled;
-	byte *_scriptCodeIp;
-	uint32 _value8;
-	uint32 _valueC;
-	uint32 _value10;
-	void loadOpcode(OpCall &opCall);
-	void execOpcode(OpCall &opCall);
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SCRIPTTHREAD_H
diff --git a/engines/illusions/talkthread.cpp b/engines/illusions/talkthread.cpp
deleted file mode 100644
index 7d97588..0000000
--- a/engines/illusions/talkthread.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/talkthread.h"
-#include "illusions/actor.h"
-#include "illusions/dictionary.h"
-#include "illusions/input.h"
-#include "illusions/sound.h"
-#include "illusions/talkresource.h"
-#include "illusions/time.h"
-
-namespace Illusions {
-
-// TalkThread
-
-TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
-	uint32 namedPointId)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _objectId(objectId), _talkId(talkId),
-	_sequenceId1(0), _sequenceId2(0) {
-	_type = kTTTalkThread;
-
-	if (sequenceId1 && _vm->_dict->getObjectControl(objectId)) {
-		_sequenceId1 = sequenceId1;
-		_sequenceId2 = sequenceId2;
-	}
-
-	if (!callingThreadId)
-		_sequenceId2 = 0;
-
-	_namedPointId = namedPointId;
-
-	if (duration)
-		_status = 1;
-	else if (_vm->checkActiveTalkThreads())
-		_status = 2;
-	else
-		_status = 3;
-	
-	_flags = 0x0E;
-	
-	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
-	_textDuration = _durationMult;
-	_defDurationMult = _vm->clipTextDuration(240);
-	_textStartTime = 0;
-	_textEndTime = 0;
-	_textDurationElapsed = 0;
-	_entryText = 0;
-	_currEntryText = 0;
-	_voiceDurationElapsed = 0;
-	_voiceDuration = duration;
-	_voiceStartTime = getCurrentTime();
-	_voiceEndTime = _voiceStartTime + duration;
-	_entryTblPtr = 0;
-
-	if (callingThreadId) {
-		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
-		if (callingThread)
-			_tag = callingThread->_tag;
-	}
-
-	//debug("New talk thread: %08X %08X", _threadId, _talkId);
-}
-
-int TalkThread::onUpdate() {
-
-	TalkEntry *talkEntry;
-
-	switch (_status) {
-
-	case 1:
-		if (isTimerExpired(_voiceStartTime, _voiceEndTime)) {
-			if (_vm->checkActiveTalkThreads())
-				_status = 2;
-			else
-				_status = 3;
-		}
-		return kTSYield;
-
-	case 2:
-		if (_vm->checkActiveTalkThreads())
-			return kTSYield;
-		_status = 3;
-		// Fallthrough to status 3
-
-	case 3:
-		talkEntry = getTalkResourceEntry(_talkId);
-		_flags = 0;
-		_currEntryText = 0;
-		_entryText = talkEntry->_text;
-		_entryTblPtr = talkEntry->_tblPtr;
-		if (_sequenceId1) {
-			// TODO _field30 = v6;
-			_pauseCtr = 0;
-		} else {
-			// TODO _field30 = 0;
-			_flags |= 2;
-			_flags |= 1;
-		}
-		if (_vm->isSoundActive()) {
-			if (!_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName) && !_durationMult)
-				_durationMult = _defDurationMult;
-		} else {
-			_flags |= 4;
-			if (_durationMult == 0)
-				_durationMult = _defDurationMult;
-		}
-		if (_objectId == 0 || _durationMult == 0)
-			_flags |= 8;
-		_status = 4;
-		// Fallthrough to status 4
-
-	case 4:
-		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
-			return kTSYield;
-		_status = 5;
-		// Fallthrough to status 5
-		
-	case 5:
-		if (!(_flags & 8))
-			refreshText();
-		if (!(_flags & 2)) {
-			Control *control = _vm->_dict->getObjectControl(_objectId);
-			control->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
-		}
-		if (!(_flags & 4)) {
-			int16 panX = 0;
-			if (_namedPointId) {
-				// TODO pt.x = (unsigned int)artcntrlGetNamedPointPosition((Point)_namedPointId);
-				// TODO panX = convertPanXCoord(pt.x);
-			}
-			_vm->_soundMan->startVoice(255, panX);
-		}
-		_vm->_input->discardButtons(0x10);
-		_status = 6;
-		return kTSYield;
-
-	case 6:
-		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
-			_flags |= 4;
-		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
-			// TODO _vm->removeText();
-			if (_entryText && *_entryText) {
-				refreshText();
-				_vm->_input->discardButtons(0x10);
-			} else {
-				_flags |= 8;
-			}
-		}
-		if ((_flags & 4) && (_flags & 8)) {
-			if (_sequenceId2) {
-				Control *control = _vm->_dict->getObjectControl(_objectId);
-				control->startSequenceActor(_sequenceId2, 2, 0);
-			}
-			if (_sequenceId1) {
-				Control *control = _vm->_dict->getObjectControl(_objectId);
-				control->clearNotifyThreadId2();
-			}
-			_flags |= 2;
-		}
-		if (_objectId && _vm->_input->pollButton(0x10)) {
-			if (!(_flags & 8)) {
-				// TODO _vm->removeText();
-				if (_entryText && *_entryText)
-					refreshText();
-				else
-					_flags |= 8;
-			}
-			if (_flags & 8) {
-				if (!(_flags & 4)) {
-					_vm->_soundMan->stopVoice();
-					_flags |= 4;
-				}
-				if (!(_flags & 2)) {
-					if (_sequenceId2) {
-						Control *control = _vm->_dict->getObjectControl(_objectId);
-						control->startSequenceActor(_sequenceId2, 2, 0);
-					}
-					if (_sequenceId1) {
-						Control *control = _vm->_dict->getObjectControl(_objectId);
-						control->clearNotifyThreadId2();
-					}
-					_flags |= 2;
-				}
-			}
-		}
-		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
-			_vm->_input->discardButtons(0x10);
-			_status = 7;
-			return kTSTerminate;
-		}
-		return kTSYield;
-
-	case 7:
-		if (!(_flags & 2)) {
-			if (_sequenceId2) {
-				Control *control = _vm->_dict->getObjectControl(_objectId);
-				control->startSequenceActor(_sequenceId2, 2, 0);
-			}
-			if (_sequenceId1) {
-				Control *control = _vm->_dict->getObjectControl(_objectId);
-				control->clearNotifyThreadId2();
-			}
-			_flags |= 2;
-		}
-		if (!(_flags & 8)) {
-			// TODO _vm->removeText();
-			_flags |= 8;
-		}
-		if (!(_flags & 4)) {
-			_vm->_soundMan->stopVoice();
-			_flags |= 4;
-		}
-		return kTSTerminate;
-
-	}
-	
-	return kTSTerminate;
-
-}
-
-void TalkThread::onSuspend() {
-}
-
-void TalkThread::onNotify() {
-}
-
-void TalkThread::onPause() {
-}
-
-void TalkThread::onResume() {
-}
-
-void TalkThread::onTerminated() {
-}
-
-void TalkThread::onKill() {
-	_callingThreadId = 0;
-	sendMessage(kMsgClearSequenceId1, 0);
-	sendMessage(kMsgClearSequenceId2, 0);
-}
-
-uint32 TalkThread::sendMessage(int msgNum, uint32 msgValue) {
-	// TODO
-	switch (msgNum) {
-	case kMsgQueryTalkThreadActive:
-		if (_status != 1 && _status != 2)
-			return 1;
-		break;
-	case kMsgClearSequenceId1:
-		_sequenceId1 = 0;
-		_flags |= 3;
-		// TODO _field30 = 0;
-		break;
-	case kMsgClearSequenceId2:
-		_sequenceId2 = 0;
-		break;
-	}
-	return 0;
-}
-
-void TalkThread::refreshText() {
-	_currEntryText = _entryText;
-	int charCount = insertText();
-	uint32 duration = _durationMult;
-	if (charCount < 80) {
-		duration = _durationMult * charCount / 80;
-		if (duration < 25 * _durationMult / 100)
-			duration = 25 * _durationMult / 100;
-		if (duration < 60)
-			duration = 60;
-	}
-	_textDuration = duration;
-	_textStartTime = getCurrentTime();
-	_textEndTime = _textStartTime + _textDuration;
-}
-
-static char *debugW2I(byte *wstr) {
-	static char buf[65];
-	char *p = buf;
-	while (*wstr != 0) {
-		*p++ = *wstr;
-		wstr += 2;
-	}
-	*p = 0;
-	return buf;
-}
-
-int TalkThread::insertText() {
-	int charCount = 100;
-	
-	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
-	_entryText = 0;
-	
-	// TODO _vm->getDimensions1(&dimensions);
-	// TODO _vm->insertText(_currEntryText, _vm->_currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
-	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
-	// TODO _entryText = outTextPtr;
-	// TODO _vm->getPoint1(&pt);
-	// TODO _vm->updateTextInfoPosition(pt);
-	return charCount >> 1;
-}
-
-TalkEntry *TalkThread::getTalkResourceEntry(uint32 talkId) {
-	TalkEntry *talkEntry = _vm->_dict->findTalkEntry(talkId);
-	return talkEntry;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/talkthread.h b/engines/illusions/talkthread.h
deleted file mode 100644
index ada593b..0000000
--- a/engines/illusions/talkthread.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_TALKTHREAD_H
-#define ILLUSIONS_TALKTHREAD_H
-
-#include "illusions/thread.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-struct TalkEntry;
-
-enum {
-	kMsgQueryTalkThreadActive    = 0,
-	kMsgClearSequenceId1         = 1,
-	kMsgClearSequenceId2         = 2
-};
-
-class TalkThread : public Thread {
-public:
-	TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
-		uint32 namedPointId);
-	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
-	virtual void onKill();
-	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
-public:
-	//field0 dw
-	int _status;
-	uint _flags;
-	uint32 _textStartTime;
-	uint32 _textEndTime;
-	uint32 _textDuration;
-	uint32 _defDurationMult;
-	uint32 _textDurationElapsed;
-	uint32 _durationMult;
-	//field12 dw
-	uint32 _objectId;
-	uint32 _talkId;
-	uint32 _sequenceId1;
-	uint32 _sequenceId2;
-	byte *_entryTblPtr;
-	byte *_entryText;
-	byte *_currEntryText;
-	//field30 dd
-	uint32 _namedPointId;
-	uint32 _voiceStartTime;
-	uint32 _voiceEndTime;
-	uint32 _voiceDuration;
-	uint32 _voiceDurationElapsed;
-	void refreshText();
-	int insertText();
-	TalkEntry *getTalkResourceEntry(uint32 talkId);
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_TALKTHREAD_H
diff --git a/engines/illusions/talkthread_duckman.cpp b/engines/illusions/talkthread_duckman.cpp
deleted file mode 100644
index 6807c98..0000000
--- a/engines/illusions/talkthread_duckman.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions_duckman.h"
-#include "illusions/talkthread_duckman.h"
-#include "illusions/actor.h"
-#include "illusions/dictionary.h"
-#include "illusions/input.h"
-#include "illusions/screentext.h"
-#include "illusions/sound.h"
-#include "illusions/talkresource.h"
-#include "illusions/time.h"
-
-namespace Illusions {
-
-// TalkThread_Duckman
-
-TalkThread_Duckman::TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _objectId(objectId), _talkId(talkId) {
-	_type = kTTTalkThread;
-
-	if ((sequenceId1 & 0xFFFF0000) == 0x60000) {
-		_sequenceId1 = sequenceId1;
-		_sequenceId2 = sequenceId2;
-		_namedPointId1 = 0;
-		_namedPointId2 = 0;
-	} else {
-		_sequenceId1 = 0;
-		_sequenceId2 = 0;
-		_namedPointId1 = sequenceId1;
-		_namedPointId2 = sequenceId2;
-	}
-
-	if (_vm->checkActiveTalkThreads())
-		_status = 1;
-	else
-		_status = 2;
-		
-	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
-	_textDuration = _durationMult;
-	_defDurationMult = _vm->clipTextDuration(240);
-	
-	_tag = _vm->getCurrentScene();
-
-}
-
-int TalkThread_Duckman::onUpdate() {
-
-	TalkEntry *talkEntry;
-
-	switch (_status) {
-
-	case 1:
-		if (_vm->checkActiveTalkThreads())
-			return kTSYield;
-		_status = 3;
-		// Fallthrough to status 2
-
-	case 2:
-		talkEntry = getTalkResourceEntry(_talkId);
-		_flags = 0;
-		_currEntryText = 0;
-		_entryText = talkEntry->_text;
-		_entryTblPtr = talkEntry->_tblPtr;
-		if (_sequenceId1) {
-			_pauseCtrPtr = &_pauseCtr;
-			_pauseCtr = 0;
-		} else {
-			_pauseCtrPtr = 0;
-			_flags |= 2;
-			_flags |= 1;
-		}
-		if (_vm->isSoundActive()) {
-			if (!_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName) && !_durationMult)
-				_durationMult = _defDurationMult;
-		} else {
-			_flags |= 4;
-			if (_durationMult == 0)
-				_durationMult = _defDurationMult;
-		}
-		if (_objectId == 0 || _durationMult == 0)
-			_flags |= 8;
-		_status = 3;
-		// Fallthrough to status 3 
-
-	case 3:
-		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
-			return kTSYield;
-		_status = 4;
-		// Fallthrough to status 4
-		
-	case 4:
-		if (!(_flags & 8) ) {
-			uint32 actorTypeId = _vm->getObjectActorTypeId(_objectId);
-			getActorTypeColor(actorTypeId, _color);
-			refreshText();
-		}
-		if (!(_flags & 2)) {
-			Control *control = _vm->_dict->getObjectControl(_objectId);
-			control->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
-		}
-		if (!(_flags & 4)) {
-			int16 panX = 0;
-			if (_flags & 1) {
-				if (_namedPointId2) {
-					panX = _vm->getNamedPointPosition(_namedPointId2).x;
-					panX = _vm->convertPanXCoord(panX);
-				}
-			} else {
-				Control *control = _vm->_dict->getObjectControl(_objectId);
-				panX = control->getActorPosition().x;
-				panX = _vm->convertPanXCoord(panX);
-			}
-			_vm->_soundMan->startVoice(255, panX);
-		}
-		_vm->_input->discardButtons(0x20);
-		_status = 5;
-		return kTSYield;
-
-	case 5:
-		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
-			_flags |= 4;
-		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
-			_vm->_screenText->removeText();
-			if (_entryText && *_entryText) {
-				refreshText();
-				_vm->_input->discardButtons(0x20);
-			} else {
-				_flags |= 8;
-			}
-		}
-		if (!(_flags & 2)) {
-			if (*_pauseCtrPtr < 0) {
-				++(*_pauseCtrPtr);
-				Control *control = _vm->_dict->getObjectControl(_objectId);
-				control->startSequenceActor(_sequenceId2, 2, 0);
-				_flags |= 2;
-			}
-		}
-		if (_objectId && _vm->_input->pollButton(0x20)) {
-			if (!(_flags & 8)) {
-				_vm->_screenText->removeText();
-				if (_entryText && *_entryText)
-					refreshText();
-				else
-					_flags |= 8;
-			}
-			if (_flags & 8) {
-				if (!(_flags & 4)) {
-					_vm->_soundMan->stopVoice();
-					_flags |= 4;
-				}
-				if (!(_flags & 2)) {
-					Control *control = _vm->_dict->getObjectControl(_objectId);
-					control->clearNotifyThreadId1();
-					control->startSequenceActor(_sequenceId2, 2, 0);
-					_flags |= 2;
-				}
-			}
-		}
-		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
-			_vm->_input->discardButtons(0x20);
-			return kTSTerminate;
-		}
-		return kTSYield;
-
-	case 6:
-		if (!(_flags & 2)) {
-			Control *control = _vm->_dict->getObjectControl(_objectId);
-			if (*_pauseCtrPtr >= 0) {
-				control->clearNotifyThreadId1();
-			} else {
-				++(*_pauseCtrPtr);
-			}
-			control->startSequenceActor(_sequenceId2, 2, 0);
-			_flags |= 2;
-		}
-		return kTSTerminate;
-		
-	}
-
-	return kTSTerminate;
-
-}
-
-void TalkThread_Duckman::onSuspend() {
-}
-
-void TalkThread_Duckman::onNotify() {
-}
-
-void TalkThread_Duckman::onPause() {
-}
-
-void TalkThread_Duckman::onResume() {
-}
-
-void TalkThread_Duckman::onTerminated() {
-	if (_status == 5) {
-		if (!(_flags & 4))
-			_vm->_soundMan->stopVoice();
-		if (!(_flags & 8)) {
-			_vm->_screenText->removeText();
-		}
-		if (!(_flags & 2)) {
-			Control *control = _vm->_dict->getObjectControl(_objectId);
-			if (control) {
-				control->clearNotifyThreadId1();
-				control->startSequenceActor(_sequenceId2, 2, 0);
-			}
-		}
-	}
-}
-
-void TalkThread_Duckman::onKill() {
-	_callingThreadId = 0;
-	sendMessage(kMsgClearSequenceId1, 0);
-	sendMessage(kMsgClearSequenceId2, 0);
-}
-
-uint32 TalkThread_Duckman::sendMessage(int msgNum, uint32 msgValue) {
-	switch (msgNum) {
-	case kMsgQueryTalkThreadActive:
-		if (_status != 1)
-			return 1;
-		break;
-	case kMsgClearSequenceId1:
-		_sequenceId1 = 0;
-		_flags |= 3;
-		// TODO _pauseCtrPtr = 0;
-		break;
-	case kMsgClearSequenceId2:
-		_sequenceId2 = 0;
-		break;
-	}
-	return 0;
-}
-
-void TalkThread_Duckman::refreshText() {
-	_currEntryText = _entryText;
-	int charCount = insertText();
-	uint32 duration = _durationMult;
-	if (charCount < 80) {
-		duration = _durationMult * charCount / 80;
-		if (duration < 25 * _durationMult / 100)
-			duration = 25 * _durationMult / 100;
-		if (duration < 60)
-			duration = 60;
-	}
-	_textDuration = duration;
-	_textStartTime = getCurrentTime();
-	_textEndTime = _textStartTime + _textDuration;
-}
-
-static char *debugW2I(byte *wstr) {
-	static char buf[65];
-	char *p = buf;
-	while (*wstr != 0) {
-		*p++ = *wstr;
-		wstr += 2;
-	}
-	*p = 0;
-	return buf;
-}
-
-int TalkThread_Duckman::insertText() {
-	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
-	WidthHeight dimensions;
-	_vm->getDefaultTextDimensions(dimensions);
-	uint16 *outTextPtr;
-	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
-		Common::Point(0, 0), 2, 0, 0, _color.r, _color.r, _color.r, outTextPtr);
-	_entryText = (byte*)outTextPtr;
-	Common::Point pt;
-	_vm->getDefaultTextPosition(pt);
-	_vm->_screenText->updateTextInfoPosition(pt);
-	int charCount = (_entryText - _currEntryText) / 2;
-	return charCount;
-}
-
-TalkEntry *TalkThread_Duckman::getTalkResourceEntry(uint32 talkId) {
-	TalkEntry *talkEntry = _vm->_dict->findTalkEntry(talkId);
-	return talkEntry;
-}
-
-void TalkThread_Duckman::getActorTypeColor(uint32 actorTypeId, RGB &color) {
-	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
-	color = actorType->_color;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/talkthread_duckman.h b/engines/illusions/talkthread_duckman.h
deleted file mode 100644
index b729ad2..0000000
--- a/engines/illusions/talkthread_duckman.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_TALKTHREAD_DUCKMAN_H
-#define ILLUSIONS_TALKTHREAD_DUCKMAN_H
-
-#include "illusions/thread.h"
-
-namespace Illusions {
-
-class IllusionsEngine_Duckman;
-struct TalkEntry;
-
-enum {
-	kMsgQueryTalkThreadActive    = 0,
-	kMsgClearSequenceId1         = 1,
-	kMsgClearSequenceId2         = 2
-};
-
-class TalkThread_Duckman : public Thread {
-public:
-	TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2);
-	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
-	virtual void onKill();
-	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
-public:
-	IllusionsEngine_Duckman *_vm;
-	//field0 dw
-	int _status;
-	uint _flags;
-	uint32 _textStartTime;
-	uint32 _textEndTime;
-	uint32 _textDuration;
-	uint32 _defDurationMult;
-	uint32 _textDurationElapsed;
-	uint32 _durationMult;
-	//field12 dw
-	uint32 _objectId;
-	uint32 _talkId;
-	uint32 _sequenceId1;
-	uint32 _sequenceId2;
-	uint32 _namedPointId1;
-	uint32 _namedPointId2;
-	byte *_entryTblPtr;
-	byte *_entryText;
-	byte *_currEntryText;
-	//field30 dd
-	uint32 _voiceStartTime;
-	uint32 _voiceEndTime;
-	uint32 _voiceDuration;
-	uint32 _voiceDurationElapsed;
-	int *_pauseCtrPtr;
-	RGB _color;
-	void refreshText();
-	int insertText();
-	TalkEntry *getTalkResourceEntry(uint32 talkId);
-	void getActorTypeColor(uint32 actorTypeId, RGB &color);
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_TALKTHREAD_H
diff --git a/engines/illusions/threads/abortablethread.cpp b/engines/illusions/threads/abortablethread.cpp
new file mode 100644
index 0000000..348bc9c
--- /dev/null
+++ b/engines/illusions/threads/abortablethread.cpp
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/threads/abortablethread.h"
+#include "illusions/input.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// AbortableThread
+
+AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 scriptThreadId, byte *scriptCodeIp)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId), 
+	_scriptCodeIp(scriptCodeIp), _status(1) {
+	_type = kTTAbortableThread;
+	_tag = _vm->getCurrentScene();
+	_vm->_input->discardButtons(8);
+}
+
+int AbortableThread::onUpdate() {
+	if (_status != 1 || _pauseCtr < 0)
+		return kTSTerminate;
+	if (_vm->_input->pollButton(8)) {
+		_vm->_threads->killThread(_scriptThreadId);
+		++_pauseCtr;
+		_vm->startTempScriptThread(_scriptCodeIp, _threadId, 0, 0, 0);
+		_status = 2;
+		return kTSSuspend;
+	}
+	return kTSYield;
+}
+
+void AbortableThread::onSuspend() {
+}
+
+void AbortableThread::onNotify() {
+}
+
+void AbortableThread::onPause() {
+}
+
+void AbortableThread::onResume() {
+}
+
+void AbortableThread::onTerminated() {
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/threads/abortablethread.h b/engines/illusions/threads/abortablethread.h
new file mode 100644
index 0000000..8fc2a47
--- /dev/null
+++ b/engines/illusions/threads/abortablethread.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ABORTABLETHREAD_H
+#define ILLUSIONS_ABORTABLETHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class AbortableThread : public Thread {
+public:
+	AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 scriptThreadId, byte *scriptCodeIp);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	int _status;
+	byte *_scriptCodeIp;
+	uint32 _scriptThreadId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ABORTABLETHREAD_H
diff --git a/engines/illusions/threads/causethread_duckman.cpp b/engines/illusions/threads/causethread_duckman.cpp
new file mode 100644
index 0000000..091b85c
--- /dev/null
+++ b/engines/illusions/threads/causethread_duckman.cpp
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/threads/causethread_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/input.h"
+
+namespace Illusions {
+
+// CauseThread_Duckman
+
+CauseThread_Duckman::CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 triggerThreadId)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _triggerThreadId(triggerThreadId), _flag(false) {
+	_type = kTTCauseThread;
+	_tag = _vm->getCurrentScene();
+}
+
+int CauseThread_Duckman::onUpdate() {
+	if (_flag) {
+		if (_vm->getCurrentScene() == _tag) {
+			Control *cursorCursor = _vm->getObjectControl(0x40004);
+			cursorCursor->appearActor();
+			_vm->_input->discardButtons(1);
+		}
+		return kTSTerminate;
+	} else {
+		_tag = _vm->getCurrentScene();
+		Control *cursorCursor = _vm->getObjectControl(0x40004);
+		cursorCursor->disappearActor();
+		_vm->_input->discardButtons(1);
+		_vm->startScriptThread(_triggerThreadId, _threadId);
+		_flag = true;
+		return kTSSuspend;
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/threads/causethread_duckman.h b/engines/illusions/threads/causethread_duckman.h
new file mode 100644
index 0000000..97bf00e
--- /dev/null
+++ b/engines/illusions/threads/causethread_duckman.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_CAUSETHREAD_DUCKMAN_H
+#define ILLUSIONS_CAUSETHREAD_DUCKMAN_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+class CauseThread_Duckman : public Thread {
+public:
+	CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 triggerThreadId);
+	virtual int onUpdate();
+public:
+	IllusionsEngine_Duckman *_vm;
+	bool _flag;
+	uint32 _triggerThreadId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_CAUSETHREAD_DUCKMAN_H
diff --git a/engines/illusions/threads/scriptthread.cpp b/engines/illusions/threads/scriptthread.cpp
new file mode 100644
index 0000000..f8bbae9
--- /dev/null
+++ b/engines/illusions/threads/scriptthread.cpp
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/threads/scriptthread.h"
+#include "illusions/scriptopcodes.h"
+
+namespace Illusions {
+
+// ScriptThread
+
+ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptCodeIp(scriptCodeIp), _value8(value8),
+	_valueC(valueC), _value10(value10), _sequenceStalled(0) {
+	_type = kTTScriptThread;
+	_tag = _vm->getCurrentScene();
+}
+
+int ScriptThread::onUpdate() {
+	OpCall opCall;
+	opCall._result = kTSRun;
+	opCall._callerThreadId = _threadId;
+	while (!_terminated && opCall._result == kTSRun) {
+		loadOpcode(opCall);
+		execOpcode(opCall);
+		_scriptCodeIp += opCall._deltaOfs;
+	}
+	if (_terminated)
+		opCall._result = kTSTerminate;
+	return opCall._result;
+}
+
+void ScriptThread::loadOpcode(OpCall &opCall) {
+	if (_vm->getGameId() == kGameIdDuckman) {
+		opCall._op = _scriptCodeIp[0] & 0x7F;
+		opCall._opSize = _scriptCodeIp[1];
+		opCall._threadId = _scriptCodeIp[0] & 0x80 ? _threadId : 0;
+	} else {
+		opCall._op = _scriptCodeIp[0];
+		opCall._opSize = _scriptCodeIp[1] >> 1;
+		opCall._threadId = _scriptCodeIp[1] & 1 ? _threadId : 0;
+	}
+	opCall._code = _scriptCodeIp + 2;
+	opCall._deltaOfs = opCall._opSize;
+}
+
+void ScriptThread::execOpcode(OpCall &opCall) {
+	_vm->_scriptOpcodes->execOpcode(this, opCall);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/threads/scriptthread.h b/engines/illusions/threads/scriptthread.h
new file mode 100644
index 0000000..0306c4f
--- /dev/null
+++ b/engines/illusions/threads/scriptthread.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTTHREAD_H
+#define ILLUSIONS_SCRIPTTHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+struct OpCall;
+
+class ScriptThread : public Thread {
+public:
+	ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
+	virtual int onUpdate();
+public:
+	int16 _sequenceStalled;
+	byte *_scriptCodeIp;
+	uint32 _value8;
+	uint32 _valueC;
+	uint32 _value10;
+	void loadOpcode(OpCall &opCall);
+	void execOpcode(OpCall &opCall);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SCRIPTTHREAD_H
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
new file mode 100644
index 0000000..3a4d286
--- /dev/null
+++ b/engines/illusions/threads/talkthread.cpp
@@ -0,0 +1,329 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/threads/talkthread.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/sound.h"
+#include "illusions/talkresource.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// TalkThread
+
+TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
+	uint32 namedPointId)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _objectId(objectId), _talkId(talkId),
+	_sequenceId1(0), _sequenceId2(0) {
+	_type = kTTTalkThread;
+
+	if (sequenceId1 && _vm->_dict->getObjectControl(objectId)) {
+		_sequenceId1 = sequenceId1;
+		_sequenceId2 = sequenceId2;
+	}
+
+	if (!callingThreadId)
+		_sequenceId2 = 0;
+
+	_namedPointId = namedPointId;
+
+	if (duration)
+		_status = 1;
+	else if (_vm->checkActiveTalkThreads())
+		_status = 2;
+	else
+		_status = 3;
+	
+	_flags = 0x0E;
+	
+	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
+	_textDuration = _durationMult;
+	_defDurationMult = _vm->clipTextDuration(240);
+	_textStartTime = 0;
+	_textEndTime = 0;
+	_textDurationElapsed = 0;
+	_entryText = 0;
+	_currEntryText = 0;
+	_voiceDurationElapsed = 0;
+	_voiceDuration = duration;
+	_voiceStartTime = getCurrentTime();
+	_voiceEndTime = _voiceStartTime + duration;
+	_entryTblPtr = 0;
+
+	if (callingThreadId) {
+		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
+		if (callingThread)
+			_tag = callingThread->_tag;
+	}
+
+	//debug("New talk thread: %08X %08X", _threadId, _talkId);
+}
+
+int TalkThread::onUpdate() {
+
+	TalkEntry *talkEntry;
+
+	switch (_status) {
+
+	case 1:
+		if (isTimerExpired(_voiceStartTime, _voiceEndTime)) {
+			if (_vm->checkActiveTalkThreads())
+				_status = 2;
+			else
+				_status = 3;
+		}
+		return kTSYield;
+
+	case 2:
+		if (_vm->checkActiveTalkThreads())
+			return kTSYield;
+		_status = 3;
+		// Fallthrough to status 3
+
+	case 3:
+		talkEntry = getTalkResourceEntry(_talkId);
+		_flags = 0;
+		_currEntryText = 0;
+		_entryText = talkEntry->_text;
+		_entryTblPtr = talkEntry->_tblPtr;
+		if (_sequenceId1) {
+			// TODO _field30 = v6;
+			_pauseCtr = 0;
+		} else {
+			// TODO _field30 = 0;
+			_flags |= 2;
+			_flags |= 1;
+		}
+		if (_vm->isSoundActive()) {
+			if (!_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName) && !_durationMult)
+				_durationMult = _defDurationMult;
+		} else {
+			_flags |= 4;
+			if (_durationMult == 0)
+				_durationMult = _defDurationMult;
+		}
+		if (_objectId == 0 || _durationMult == 0)
+			_flags |= 8;
+		_status = 4;
+		// Fallthrough to status 4
+
+	case 4:
+		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
+			return kTSYield;
+		_status = 5;
+		// Fallthrough to status 5
+		
+	case 5:
+		if (!(_flags & 8))
+			refreshText();
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			control->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
+		}
+		if (!(_flags & 4)) {
+			int16 panX = 0;
+			if (_namedPointId) {
+				// TODO pt.x = (unsigned int)artcntrlGetNamedPointPosition((Point)_namedPointId);
+				// TODO panX = convertPanXCoord(pt.x);
+			}
+			_vm->_soundMan->startVoice(255, panX);
+		}
+		_vm->_input->discardButtons(0x10);
+		_status = 6;
+		return kTSYield;
+
+	case 6:
+		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
+			_flags |= 4;
+		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
+			// TODO _vm->removeText();
+			if (_entryText && *_entryText) {
+				refreshText();
+				_vm->_input->discardButtons(0x10);
+			} else {
+				_flags |= 8;
+			}
+		}
+		if ((_flags & 4) && (_flags & 8)) {
+			if (_sequenceId2) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+			if (_sequenceId1) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->clearNotifyThreadId2();
+			}
+			_flags |= 2;
+		}
+		if (_objectId && _vm->_input->pollButton(0x10)) {
+			if (!(_flags & 8)) {
+				// TODO _vm->removeText();
+				if (_entryText && *_entryText)
+					refreshText();
+				else
+					_flags |= 8;
+			}
+			if (_flags & 8) {
+				if (!(_flags & 4)) {
+					_vm->_soundMan->stopVoice();
+					_flags |= 4;
+				}
+				if (!(_flags & 2)) {
+					if (_sequenceId2) {
+						Control *control = _vm->_dict->getObjectControl(_objectId);
+						control->startSequenceActor(_sequenceId2, 2, 0);
+					}
+					if (_sequenceId1) {
+						Control *control = _vm->_dict->getObjectControl(_objectId);
+						control->clearNotifyThreadId2();
+					}
+					_flags |= 2;
+				}
+			}
+		}
+		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
+			_vm->_input->discardButtons(0x10);
+			_status = 7;
+			return kTSTerminate;
+		}
+		return kTSYield;
+
+	case 7:
+		if (!(_flags & 2)) {
+			if (_sequenceId2) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+			if (_sequenceId1) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->clearNotifyThreadId2();
+			}
+			_flags |= 2;
+		}
+		if (!(_flags & 8)) {
+			// TODO _vm->removeText();
+			_flags |= 8;
+		}
+		if (!(_flags & 4)) {
+			_vm->_soundMan->stopVoice();
+			_flags |= 4;
+		}
+		return kTSTerminate;
+
+	}
+	
+	return kTSTerminate;
+
+}
+
+void TalkThread::onSuspend() {
+}
+
+void TalkThread::onNotify() {
+}
+
+void TalkThread::onPause() {
+}
+
+void TalkThread::onResume() {
+}
+
+void TalkThread::onTerminated() {
+}
+
+void TalkThread::onKill() {
+	_callingThreadId = 0;
+	sendMessage(kMsgClearSequenceId1, 0);
+	sendMessage(kMsgClearSequenceId2, 0);
+}
+
+uint32 TalkThread::sendMessage(int msgNum, uint32 msgValue) {
+	// TODO
+	switch (msgNum) {
+	case kMsgQueryTalkThreadActive:
+		if (_status != 1 && _status != 2)
+			return 1;
+		break;
+	case kMsgClearSequenceId1:
+		_sequenceId1 = 0;
+		_flags |= 3;
+		// TODO _field30 = 0;
+		break;
+	case kMsgClearSequenceId2:
+		_sequenceId2 = 0;
+		break;
+	}
+	return 0;
+}
+
+void TalkThread::refreshText() {
+	_currEntryText = _entryText;
+	int charCount = insertText();
+	uint32 duration = _durationMult;
+	if (charCount < 80) {
+		duration = _durationMult * charCount / 80;
+		if (duration < 25 * _durationMult / 100)
+			duration = 25 * _durationMult / 100;
+		if (duration < 60)
+			duration = 60;
+	}
+	_textDuration = duration;
+	_textStartTime = getCurrentTime();
+	_textEndTime = _textStartTime + _textDuration;
+}
+
+static char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	while (*wstr != 0) {
+		*p++ = *wstr;
+		wstr += 2;
+	}
+	*p = 0;
+	return buf;
+}
+
+int TalkThread::insertText() {
+	int charCount = 100;
+	
+	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
+	_entryText = 0;
+	
+	// TODO _vm->getDimensions1(&dimensions);
+	// TODO _vm->insertText(_currEntryText, _vm->_currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
+	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
+	// TODO _entryText = outTextPtr;
+	// TODO _vm->getPoint1(&pt);
+	// TODO _vm->updateTextInfoPosition(pt);
+	return charCount >> 1;
+}
+
+TalkEntry *TalkThread::getTalkResourceEntry(uint32 talkId) {
+	TalkEntry *talkEntry = _vm->_dict->findTalkEntry(talkId);
+	return talkEntry;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/threads/talkthread.h b/engines/illusions/threads/talkthread.h
new file mode 100644
index 0000000..ada593b
--- /dev/null
+++ b/engines/illusions/threads/talkthread.h
@@ -0,0 +1,83 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TALKTHREAD_H
+#define ILLUSIONS_TALKTHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+struct TalkEntry;
+
+enum {
+	kMsgQueryTalkThreadActive    = 0,
+	kMsgClearSequenceId1         = 1,
+	kMsgClearSequenceId2         = 2
+};
+
+class TalkThread : public Thread {
+public:
+	TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
+		uint32 namedPointId);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+	virtual void onKill();
+	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
+public:
+	//field0 dw
+	int _status;
+	uint _flags;
+	uint32 _textStartTime;
+	uint32 _textEndTime;
+	uint32 _textDuration;
+	uint32 _defDurationMult;
+	uint32 _textDurationElapsed;
+	uint32 _durationMult;
+	//field12 dw
+	uint32 _objectId;
+	uint32 _talkId;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	byte *_entryTblPtr;
+	byte *_entryText;
+	byte *_currEntryText;
+	//field30 dd
+	uint32 _namedPointId;
+	uint32 _voiceStartTime;
+	uint32 _voiceEndTime;
+	uint32 _voiceDuration;
+	uint32 _voiceDurationElapsed;
+	void refreshText();
+	int insertText();
+	TalkEntry *getTalkResourceEntry(uint32 talkId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKTHREAD_H
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
new file mode 100644
index 0000000..67c394c
--- /dev/null
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -0,0 +1,311 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions_duckman.h"
+#include "illusions/threads/talkthread_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/screentext.h"
+#include "illusions/sound.h"
+#include "illusions/talkresource.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// TalkThread_Duckman
+
+TalkThread_Duckman::TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _objectId(objectId), _talkId(talkId) {
+	_type = kTTTalkThread;
+
+	if ((sequenceId1 & 0xFFFF0000) == 0x60000) {
+		_sequenceId1 = sequenceId1;
+		_sequenceId2 = sequenceId2;
+		_namedPointId1 = 0;
+		_namedPointId2 = 0;
+	} else {
+		_sequenceId1 = 0;
+		_sequenceId2 = 0;
+		_namedPointId1 = sequenceId1;
+		_namedPointId2 = sequenceId2;
+	}
+
+	if (_vm->checkActiveTalkThreads())
+		_status = 1;
+	else
+		_status = 2;
+		
+	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
+	_textDuration = _durationMult;
+	_defDurationMult = _vm->clipTextDuration(240);
+	
+	_tag = _vm->getCurrentScene();
+
+}
+
+int TalkThread_Duckman::onUpdate() {
+
+	TalkEntry *talkEntry;
+
+	switch (_status) {
+
+	case 1:
+		if (_vm->checkActiveTalkThreads())
+			return kTSYield;
+		_status = 3;
+		// Fallthrough to status 2
+
+	case 2:
+		talkEntry = getTalkResourceEntry(_talkId);
+		_flags = 0;
+		_currEntryText = 0;
+		_entryText = talkEntry->_text;
+		_entryTblPtr = talkEntry->_tblPtr;
+		if (_sequenceId1) {
+			_pauseCtrPtr = &_pauseCtr;
+			_pauseCtr = 0;
+		} else {
+			_pauseCtrPtr = 0;
+			_flags |= 2;
+			_flags |= 1;
+		}
+		if (_vm->isSoundActive()) {
+			if (!_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName) && !_durationMult)
+				_durationMult = _defDurationMult;
+		} else {
+			_flags |= 4;
+			if (_durationMult == 0)
+				_durationMult = _defDurationMult;
+		}
+		if (_objectId == 0 || _durationMult == 0)
+			_flags |= 8;
+		_status = 3;
+		// Fallthrough to status 3 
+
+	case 3:
+		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
+			return kTSYield;
+		_status = 4;
+		// Fallthrough to status 4
+		
+	case 4:
+		if (!(_flags & 8) ) {
+			uint32 actorTypeId = _vm->getObjectActorTypeId(_objectId);
+			getActorTypeColor(actorTypeId, _color);
+			refreshText();
+		}
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			control->startTalkActor(_sequenceId1, _entryTblPtr, _threadId);
+		}
+		if (!(_flags & 4)) {
+			int16 panX = 0;
+			if (_flags & 1) {
+				if (_namedPointId2) {
+					panX = _vm->getNamedPointPosition(_namedPointId2).x;
+					panX = _vm->convertPanXCoord(panX);
+				}
+			} else {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				panX = control->getActorPosition().x;
+				panX = _vm->convertPanXCoord(panX);
+			}
+			_vm->_soundMan->startVoice(255, panX);
+		}
+		_vm->_input->discardButtons(0x20);
+		_status = 5;
+		return kTSYield;
+
+	case 5:
+		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
+			_flags |= 4;
+		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
+			_vm->_screenText->removeText();
+			if (_entryText && *_entryText) {
+				refreshText();
+				_vm->_input->discardButtons(0x20);
+			} else {
+				_flags |= 8;
+			}
+		}
+		if (!(_flags & 2)) {
+			if (*_pauseCtrPtr < 0) {
+				++(*_pauseCtrPtr);
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+				_flags |= 2;
+			}
+		}
+		if (_objectId && _vm->_input->pollButton(0x20)) {
+			if (!(_flags & 8)) {
+				_vm->_screenText->removeText();
+				if (_entryText && *_entryText)
+					refreshText();
+				else
+					_flags |= 8;
+			}
+			if (_flags & 8) {
+				if (!(_flags & 4)) {
+					_vm->_soundMan->stopVoice();
+					_flags |= 4;
+				}
+				if (!(_flags & 2)) {
+					Control *control = _vm->_dict->getObjectControl(_objectId);
+					control->clearNotifyThreadId1();
+					control->startSequenceActor(_sequenceId2, 2, 0);
+					_flags |= 2;
+				}
+			}
+		}
+		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
+			_vm->_input->discardButtons(0x20);
+			return kTSTerminate;
+		}
+		return kTSYield;
+
+	case 6:
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			if (*_pauseCtrPtr >= 0) {
+				control->clearNotifyThreadId1();
+			} else {
+				++(*_pauseCtrPtr);
+			}
+			control->startSequenceActor(_sequenceId2, 2, 0);
+			_flags |= 2;
+		}
+		return kTSTerminate;
+		
+	}
+
+	return kTSTerminate;
+
+}
+
+void TalkThread_Duckman::onSuspend() {
+}
+
+void TalkThread_Duckman::onNotify() {
+}
+
+void TalkThread_Duckman::onPause() {
+}
+
+void TalkThread_Duckman::onResume() {
+}
+
+void TalkThread_Duckman::onTerminated() {
+	if (_status == 5) {
+		if (!(_flags & 4))
+			_vm->_soundMan->stopVoice();
+		if (!(_flags & 8)) {
+			_vm->_screenText->removeText();
+		}
+		if (!(_flags & 2)) {
+			Control *control = _vm->_dict->getObjectControl(_objectId);
+			if (control) {
+				control->clearNotifyThreadId1();
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+		}
+	}
+}
+
+void TalkThread_Duckman::onKill() {
+	_callingThreadId = 0;
+	sendMessage(kMsgClearSequenceId1, 0);
+	sendMessage(kMsgClearSequenceId2, 0);
+}
+
+uint32 TalkThread_Duckman::sendMessage(int msgNum, uint32 msgValue) {
+	switch (msgNum) {
+	case kMsgQueryTalkThreadActive:
+		if (_status != 1)
+			return 1;
+		break;
+	case kMsgClearSequenceId1:
+		_sequenceId1 = 0;
+		_flags |= 3;
+		// TODO _pauseCtrPtr = 0;
+		break;
+	case kMsgClearSequenceId2:
+		_sequenceId2 = 0;
+		break;
+	}
+	return 0;
+}
+
+void TalkThread_Duckman::refreshText() {
+	_currEntryText = _entryText;
+	int charCount = insertText();
+	uint32 duration = _durationMult;
+	if (charCount < 80) {
+		duration = _durationMult * charCount / 80;
+		if (duration < 25 * _durationMult / 100)
+			duration = 25 * _durationMult / 100;
+		if (duration < 60)
+			duration = 60;
+	}
+	_textDuration = duration;
+	_textStartTime = getCurrentTime();
+	_textEndTime = _textStartTime + _textDuration;
+}
+
+static char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	while (*wstr != 0) {
+		*p++ = *wstr;
+		wstr += 2;
+	}
+	*p = 0;
+	return buf;
+}
+
+int TalkThread_Duckman::insertText() {
+	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
+	WidthHeight dimensions;
+	_vm->getDefaultTextDimensions(dimensions);
+	uint16 *outTextPtr;
+	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
+		Common::Point(0, 0), 2, 0, 0, _color.r, _color.r, _color.r, outTextPtr);
+	_entryText = (byte*)outTextPtr;
+	Common::Point pt;
+	_vm->getDefaultTextPosition(pt);
+	_vm->_screenText->updateTextInfoPosition(pt);
+	int charCount = (_entryText - _currEntryText) / 2;
+	return charCount;
+}
+
+TalkEntry *TalkThread_Duckman::getTalkResourceEntry(uint32 talkId) {
+	TalkEntry *talkEntry = _vm->_dict->findTalkEntry(talkId);
+	return talkEntry;
+}
+
+void TalkThread_Duckman::getActorTypeColor(uint32 actorTypeId, RGB &color) {
+	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
+	color = actorType->_color;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/threads/talkthread_duckman.h b/engines/illusions/threads/talkthread_duckman.h
new file mode 100644
index 0000000..b729ad2
--- /dev/null
+++ b/engines/illusions/threads/talkthread_duckman.h
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TALKTHREAD_DUCKMAN_H
+#define ILLUSIONS_TALKTHREAD_DUCKMAN_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+struct TalkEntry;
+
+enum {
+	kMsgQueryTalkThreadActive    = 0,
+	kMsgClearSequenceId1         = 1,
+	kMsgClearSequenceId2         = 2
+};
+
+class TalkThread_Duckman : public Thread {
+public:
+	TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+	virtual void onKill();
+	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
+public:
+	IllusionsEngine_Duckman *_vm;
+	//field0 dw
+	int _status;
+	uint _flags;
+	uint32 _textStartTime;
+	uint32 _textEndTime;
+	uint32 _textDuration;
+	uint32 _defDurationMult;
+	uint32 _textDurationElapsed;
+	uint32 _durationMult;
+	//field12 dw
+	uint32 _objectId;
+	uint32 _talkId;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	uint32 _namedPointId1;
+	uint32 _namedPointId2;
+	byte *_entryTblPtr;
+	byte *_entryText;
+	byte *_currEntryText;
+	//field30 dd
+	uint32 _voiceStartTime;
+	uint32 _voiceEndTime;
+	uint32 _voiceDuration;
+	uint32 _voiceDurationElapsed;
+	int *_pauseCtrPtr;
+	RGB _color;
+	void refreshText();
+	int insertText();
+	TalkEntry *getTalkResourceEntry(uint32 talkId);
+	void getActorTypeColor(uint32 actorTypeId, RGB &color);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKTHREAD_H
diff --git a/engines/illusions/threads/timerthread.cpp b/engines/illusions/threads/timerthread.cpp
new file mode 100644
index 0000000..714c719
--- /dev/null
+++ b/engines/illusions/threads/timerthread.cpp
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/threads/timerthread.h"
+#include "illusions/input.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// TimerThread
+
+TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	uint32 duration, bool isAbortable)
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _duration(duration), _isAbortable(isAbortable) {
+	_type = kTTTimerThread;
+	_startTime = getCurrentTime();
+	_endTime = _startTime + _duration;
+
+	if (callingThreadId) {
+		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
+		if (callingThread)
+			_tag = callingThread->_tag;
+	}
+
+}
+
+int TimerThread::onUpdate() {
+	if (isTimerExpired(_startTime, _endTime) ||
+		(_isAbortable && _vm->_input->pollButton(8)))
+		return kTSTerminate;
+	return kTSYield;
+}
+
+void TimerThread::onSuspend() {
+	_durationElapsed = getDurationElapsed(_startTime, _endTime);
+}
+
+void TimerThread::onNotify() {
+	uint32 currTime = getCurrentTime();
+	_startTime = currTime;
+	if (_duration <= _durationElapsed)
+		_endTime = currTime;
+	else
+		_endTime = currTime + _duration - _durationElapsed;
+	_durationElapsed = 0;
+}
+
+void TimerThread::onPause() {
+	onSuspend();
+}
+
+void TimerThread::onResume() {
+	onNotify();
+}
+
+void TimerThread::onTerminated() {
+	// Empty
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/threads/timerthread.h b/engines/illusions/threads/timerthread.h
new file mode 100644
index 0000000..d283dc4
--- /dev/null
+++ b/engines/illusions/threads/timerthread.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TIMERTHREAD_H
+#define ILLUSIONS_TIMERTHREAD_H
+
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class TimerThread : public Thread {
+public:
+	TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		uint32 duration, bool isAbortable);
+	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onNotify();
+	virtual void onPause();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	uint32 _startTime, _endTime;
+	uint32 _duration, _durationElapsed;
+	bool _isAbortable;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TIMERTHREAD_H
diff --git a/engines/illusions/timerthread.cpp b/engines/illusions/timerthread.cpp
deleted file mode 100644
index 95b30fe..0000000
--- a/engines/illusions/timerthread.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/timerthread.h"
-#include "illusions/input.h"
-#include "illusions/time.h"
-
-namespace Illusions {
-
-// TimerThread
-
-TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	uint32 duration, bool isAbortable)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _duration(duration), _isAbortable(isAbortable) {
-	_type = kTTTimerThread;
-	_startTime = getCurrentTime();
-	_endTime = _startTime + _duration;
-
-	if (callingThreadId) {
-		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
-		if (callingThread)
-			_tag = callingThread->_tag;
-	}
-
-}
-
-int TimerThread::onUpdate() {
-	if (isTimerExpired(_startTime, _endTime) ||
-		(_isAbortable && _vm->_input->pollButton(8)))
-		return kTSTerminate;
-	return kTSYield;
-}
-
-void TimerThread::onSuspend() {
-	_durationElapsed = getDurationElapsed(_startTime, _endTime);
-}
-
-void TimerThread::onNotify() {
-	uint32 currTime = getCurrentTime();
-	_startTime = currTime;
-	if (_duration <= _durationElapsed)
-		_endTime = currTime;
-	else
-		_endTime = currTime + _duration - _durationElapsed;
-	_durationElapsed = 0;
-}
-
-void TimerThread::onPause() {
-	onSuspend();
-}
-
-void TimerThread::onResume() {
-	onNotify();
-}
-
-void TimerThread::onTerminated() {
-	// Empty
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/timerthread.h b/engines/illusions/timerthread.h
deleted file mode 100644
index d283dc4..0000000
--- a/engines/illusions/timerthread.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_TIMERTHREAD_H
-#define ILLUSIONS_TIMERTHREAD_H
-
-#include "illusions/thread.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class TimerThread : public Thread {
-public:
-	TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		uint32 duration, bool isAbortable);
-	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
-public:
-	uint32 _startTime, _endTime;
-	uint32 _duration, _durationElapsed;
-	bool _isAbortable;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_TIMERTHREAD_H


Commit: a173329ae9ed9c666d998553e6e414a258231436
    https://github.com/scummvm/scummvm/commit/a173329ae9ed9c666d998553e6e414a258231436
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move resource-related files into resources subdirectory

Changed paths:
  A engines/illusions/resources/actorresource.cpp
  A engines/illusions/resources/actorresource.h
  A engines/illusions/resources/backgroundresource.cpp
  A engines/illusions/resources/backgroundresource.h
  A engines/illusions/resources/fontresource.cpp
  A engines/illusions/resources/fontresource.h
  A engines/illusions/resources/midiresource.cpp
  A engines/illusions/resources/midiresource.h
  A engines/illusions/resources/scriptresource.cpp
  A engines/illusions/resources/scriptresource.h
  A engines/illusions/resources/soundresource.cpp
  A engines/illusions/resources/soundresource.h
  A engines/illusions/resources/talkresource.cpp
  A engines/illusions/resources/talkresource.h
  R engines/illusions/actorresource.cpp
  R engines/illusions/actorresource.h
  R engines/illusions/backgroundresource.cpp
  R engines/illusions/backgroundresource.h
  R engines/illusions/fontresource.cpp
  R engines/illusions/fontresource.h
  R engines/illusions/midiresource.cpp
  R engines/illusions/midiresource.h
  R engines/illusions/scriptresource.cpp
  R engines/illusions/scriptresource.h
  R engines/illusions/soundresource.cpp
  R engines/illusions/soundresource.h
  R engines/illusions/talkresource.cpp
  R engines/illusions/talkresource.h
    engines/illusions/actor.h
    engines/illusions/camera.cpp
    engines/illusions/dictionary.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions_bbdou.cpp
    engines/illusions/illusions_duckman.cpp
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screentext.cpp
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/textdrawer.h
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 5f95559..c4ae052 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -23,8 +23,8 @@
 #ifndef ILLUSIONS_ACTOR_H
 #define ILLUSIONS_ACTOR_H
 
-#include "illusions/actorresource.h"
-#include "illusions/backgroundresource.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
 #include "illusions/graphics.h"
 #include "illusions/pathfinder.h"
 #include "common/algorithm.h"
diff --git a/engines/illusions/actorresource.cpp b/engines/illusions/actorresource.cpp
deleted file mode 100644
index 3cca917..0000000
--- a/engines/illusions/actorresource.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/actorresource.h"
-#include "illusions/dictionary.h"
-
-namespace Illusions {
-
-// ActorResourceLoader
-
-void ActorResourceLoader::load(Resource *resource) {
-	resource->_instance = _vm->_actorInstances->createActorInstance(resource);
-}
-
-void ActorResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.act", resource->_resId);
-}
-
-bool ActorResourceLoader::isFlag(int flag) {
-	return
-		flag == kRlfLoadFile;
-}
-
-// Frame
-
-void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_flags = stream.readUint16LE();
-	stream.skip(2); // Skip padding
-	uint32 pointsConfigOffs = stream.readUint32LE();
-	_surfInfo.load(stream);
-	uint32 compressedPixelsOffs = stream.readUint32LE();
-	_compressedPixels = dataStart + compressedPixelsOffs;
-	_pointsConfig = dataStart + pointsConfigOffs;
-	debug(5, "Frame::load() compressedPixelsOffs: %08X",
-		compressedPixelsOffs);
-}
-
-// Sequence
-
-void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_sequenceId = stream.readUint32LE();
-	_unk4 = stream.readUint32LE();
-	uint32 sequenceCodeOffs = stream.readUint32LE();
-	_sequenceCode = dataStart + sequenceCodeOffs;
-	debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
-		_sequenceId, _unk4, sequenceCodeOffs);
-}
-
-// ActorType
-
-void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_actorTypeId = stream.readUint32LE();
-	_surfInfo.load(stream);
-	uint32 pointsConfigOffs = stream.readUint32LE();
-	uint namedPointsCount = stream.readUint16LE();
-	stream.skip(2); // Skip padding
-	uint32 namedPointsOffs = stream.readUint32LE();
-	_color.r = stream.readByte();
-	_color.g = stream.readByte();
-	_color.b = stream.readByte();
-	stream.readByte(); // Skip padding
-	_scale = stream.readByte();
-	_priority = stream.readByte();
-	_value1E = stream.readUint16LE();
-	_pathWalkPointsIndex = stream.readUint16LE();
-	_scaleLayerIndex = stream.readUint16LE();
-	_pathWalkRectIndex = stream.readUint16LE();
-	_priorityLayerIndex = stream.readUint16LE();
-	_regionLayerIndex = stream.readUint16LE();
-	_flags = stream.readUint16LE();
-	_pointsConfig = dataStart + pointsConfigOffs;
-	stream.seek(namedPointsOffs);
-	_namedPoints.load(namedPointsCount, stream);
-	debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
-		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
-	debug(5, "ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
-		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
-	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
-		_priorityLayerIndex, _regionLayerIndex,_flags);
-}
-
-// ActorResource
-
-ActorResource::ActorResource() {
-}
-
-ActorResource::~ActorResource() {
-}
-
-void ActorResource::load(Resource *resource) {
-	byte *data = resource->_data;
-	uint32 dataSize = resource->_dataSize;
-	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-
-	_totalSize = stream.readUint32LE();
-
-	// Load actor types	
-	stream.seek(0x06);
-	uint actorTypesCount = stream.readUint16LE();
-	stream.seek(0x10);
-	uint32 actorTypesOffs = stream.readUint32LE();
-	_actorTypes.reserve(actorTypesCount);
-	for (uint i = 0; i < actorTypesCount; ++i) {
-		ActorType actorType;
-		stream.seek(actorTypesOffs + i * 0x2C);
-		actorType.load(data, stream);
-		_actorTypes.push_back(actorType);
-	}
-
-	// Load sequences	
-	stream.seek(0x08);
-	uint sequencesCount = stream.readUint16LE();
-	stream.seek(0x14);
-	uint32 sequencesOffs = stream.readUint32LE();
-	stream.seek(sequencesOffs);
-	_sequences.reserve(sequencesCount);
-	for (uint i = 0; i < sequencesCount; ++i) {
-		Sequence sequence;
-		sequence.load(data, stream);
-		_sequences.push_back(sequence);
-	}
-
-	// Load frames	
-	stream.seek(0x0A);
-	uint framesCount = stream.readUint16LE();
-	stream.seek(0x18);
-	uint32 framesOffs = stream.readUint32LE();
-	stream.seek(framesOffs);
-	_frames.reserve(framesCount);
-	for (uint i = 0; i < framesCount; ++i) {
-		Frame frame;
-		frame.load(data, stream);
-		_frames.push_back(frame);
-	}
-	
-	// Load named points
-	if (resource->_gameId == kGameIdBBDOU) {
-		// The count isn't stored explicitly so calculate it
-		uint namedPointsCount = (actorTypesOffs - 0x20) / 8;
-		stream.seek(0x20);
-		_namedPoints.load(namedPointsCount, stream);
-	}
-	
-	debug("ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
-}
-
-bool ActorResource::containsSequence(Sequence *sequence) {
-	for (uint i = 0; i < _sequences.size(); ++i)
-		if (sequence == &_sequences[i])
-			return true;
-	return false;
-}
-
-bool ActorResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
-	return _namedPoints.findNamedPoint(namedPointId, pt);
-}
-
-// ActorInstance
-
-ActorInstance::ActorInstance(IllusionsEngine *vm)
-	: _vm(vm) {
-}
-
-void ActorInstance::load(Resource *resource) {
-	_actorResource = new ActorResource();
-	_actorResource->load(resource);
-	_tag = resource->_tag;
-	_pauseCtr = 0;
-	initActorTypes();
-	registerResources();
-}
-
-void ActorInstance::unload() {
-	if (_pauseCtr <= 0)
-		unregisterResources();
-	_vm->_actorInstances->removeActorInstance(this);
-	delete _actorResource;
-}
-
-void ActorInstance::pause() {
-	++_pauseCtr;
-	if (_pauseCtr == 1)
-		unregisterResources();
-}
-
-void ActorInstance::unpause() {
-	--_pauseCtr;
-	if (_pauseCtr == 0)
-		registerResources();
-}
-
-void ActorInstance::initActorTypes() {
-	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
-		ActorType *actorType = &_actorResource->_actorTypes[i];
-		ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
-		if (actorType2) {
-			actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
-				actorType2->_surfInfo._dimensions._width);
-			actorType->_surfInfo._dimensions._height = MAX(actorType->_surfInfo._dimensions._height,
-				actorType2->_surfInfo._dimensions._height);
-			if (actorType->_color.r == 255 && actorType->_color.g == 255 && actorType->_color.b == 255)
-				actorType->_color = actorType2->_color;
-			if (actorType->_value1E == 0)
-				actorType->_value1E = actorType2->_value1E;
-		}
-	}
-}
-
-void ActorInstance::registerResources() {
-	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
-		ActorType *actorType = &_actorResource->_actorTypes[i];
-		_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
-	}
-	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
-		Sequence *sequence = &_actorResource->_sequences[i];
-		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-	}
-}	
-
-void ActorInstance::unregisterResources() {
-	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i)
-		_vm->_dict->removeActorType(_actorResource->_actorTypes[i]._actorTypeId);
-	for (uint i = 0; i < _actorResource->_sequences.size(); ++i)
-		_vm->_dict->removeSequence(_actorResource->_sequences[i]._sequenceId);
-}
-
-// ActorInstanceList
-
-ActorInstanceList::ActorInstanceList(IllusionsEngine *vm)
-	: _vm(vm) {
-}
-
-ActorInstanceList::~ActorInstanceList() {
-}
-
-ActorInstance *ActorInstanceList::createActorInstance(Resource *resource) {
-	ActorInstance *actorInstance = new ActorInstance(_vm);
-	actorInstance->load(resource);
-	_items.push_back(actorInstance);
-	return actorInstance;
-}
-
-void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
-	_items.remove(actorInstance);
-}
-
-void ActorInstanceList::pauseByTag(uint32 tag) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
-			(*it)->pause();
-}
-
-void ActorInstanceList::unpauseByTag(uint32 tag) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
-			(*it)->unpause();
-}
-
-FramesList *ActorInstanceList::findSequenceFrames(Sequence *sequence) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
-		ActorInstance *actorInstance = *it;
-		if (actorInstance->_pauseCtr <= 0 && actorInstance->_actorResource->containsSequence(sequence))
-			return &actorInstance->_actorResource->_frames;
-	}
-	return 0;
-}
-
-ActorInstance *ActorInstanceList::findActorByResource(ActorResource *actorResource) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_actorResource == actorResource)
-			return (*it);
-	return 0;
-}
-
-bool ActorInstanceList::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
-		ActorInstance *actorInstance = *it;
-		if (actorInstance->_pauseCtr == 0 && actorInstance->_actorResource->findNamedPoint(namedPointId, pt))
-			return true;
-	}
-	return false;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/actorresource.h b/engines/illusions/actorresource.h
deleted file mode 100644
index 2ccb9ef..0000000
--- a/engines/illusions/actorresource.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_ACTORRESOURCE_H
-#define ILLUSIONS_ACTORRESOURCE_H
-
-#include "illusions/graphics.h"
-#include "illusions/resourcesystem.h"
-#include "graphics/surface.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class ActorResourceLoader : public BaseResourceLoader {
-public:
-	ActorResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~ActorResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-struct Frame {
-	uint16 _flags;
-	byte *_pointsConfig;
-	SurfInfo _surfInfo;
-	byte *_compressedPixels;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct Sequence {
-	uint32 _sequenceId;
-	uint32 _unk4;
-	byte *_sequenceCode;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct ActorType {
-	uint32 _actorTypeId;
-	SurfInfo _surfInfo;
-	byte *_pointsConfig;
-	NamedPoints _namedPoints;
-	RGB _color;
-	byte _scale;
-	byte _priority;
-	int16 _value1E;
-	uint16 _pathWalkPointsIndex;
-	uint16 _scaleLayerIndex;
-	uint16 _pathWalkRectIndex;
-	uint16 _priorityLayerIndex;
-	uint16 _regionLayerIndex;
-	uint16 _flags;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-class FramesList : public Common::Array<Frame> {
-};
-
-class ActorResource {
-public:
-	ActorResource();
-	~ActorResource();
-	void load(Resource *resource);
-	bool containsSequence(Sequence *sequence);
-	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
-public:
-	uint32 _totalSize;
-	Common::Array<ActorType> _actorTypes;
-	Common::Array<Sequence> _sequences;
-	FramesList _frames;
-	NamedPoints _namedPoints;
-};
-
-class ActorInstance : public ResourceInstance {
-public:
-	ActorInstance(IllusionsEngine *vm);              
-	virtual void load(Resource *resource);
-	virtual void unload();
-	virtual void pause();
-	virtual void unpause();
-public:
-	IllusionsEngine *_vm;
-	uint32 _tag;
-	int _pauseCtr;
-	ActorResource *_actorResource;
-protected:
-	void initActorTypes();
-	void registerResources();	
-	void unregisterResources();	
-};
-
-class ActorInstanceList {
-public:
-	ActorInstanceList(IllusionsEngine *vm);
-	~ActorInstanceList();
-	ActorInstance *createActorInstance(Resource *resource);
-	void removeActorInstance(ActorInstance *actorInstance);
-	void pauseByTag(uint32 tag);
-	void unpauseByTag(uint32 tag);
-	FramesList *findSequenceFrames(Sequence *sequence);
-	ActorInstance *findActorByResource(ActorResource *actorResource);
-	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
-protected:
-	typedef Common::List<ActorInstance*> Items;
-	typedef Items::iterator ItemsIterator;
-	IllusionsEngine *_vm;
-	Items _items;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_ACTORRESOURCE_H
diff --git a/engines/illusions/backgroundresource.cpp b/engines/illusions/backgroundresource.cpp
deleted file mode 100644
index e8059bc..0000000
--- a/engines/illusions/backgroundresource.cpp
+++ /dev/null
@@ -1,608 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/backgroundresource.h"
-#include "illusions/actor.h"
-#include "illusions/actorresource.h"
-#include "illusions/camera.h"
-#include "illusions/dictionary.h"
-#include "illusions/screen.h"
-#include "common/str.h"
-
-namespace Illusions {
-
-// BackgroundResourceLoader
-
-void BackgroundResourceLoader::load(Resource *resource) {
-	resource->_instance = _vm->_backgroundInstances->createBackgroundInstance(resource);
-}
-
-void BackgroundResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.bg", resource->_resId);
-}
-
-bool BackgroundResourceLoader::isFlag(int flag) {
-	return
-		flag == kRlfLoadFile;
-}
-
-// TileMap
-
-void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_width = stream.readSint16LE();
-	_height = stream.readSint16LE();
-	stream.skip(4); // Unknown
-	uint32 mapOffs = stream.pos();
-	_map = dataStart + mapOffs;
-	debug(0, "TileMap::load() _width: %d; _height: %d",
-		_width, _height);
-}
-
-// BgInfo
-
-void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_flags = stream.readUint32LE();
-	stream.skip(2); // Unknown
-	_priorityBase = stream.readSint16LE();
-	_surfInfo.load(stream);
-	loadPoint(stream, _panPoint);
-	uint32 tileMapOffs = stream.readUint32LE();
-	uint32 tilePixelsOffs = stream.readUint32LE();
-	stream.seek(tileMapOffs);
-	_tileMap.load(dataStart, stream);
-	_tilePixels = dataStart + tilePixelsOffs;
-	debug(0, "BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
-		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
-}
-
-// PriorityLayer
-
-void PriorityLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_width = stream.readUint16LE();
-	_height = stream.readUint16LE();
-	uint32 mapOffs = stream.readUint32LE();
-	uint32 valuesOffs = stream.readUint32LE();
-	_map = dataStart + mapOffs;
-	_mapWidth = READ_LE_UINT16(_map + 0);
-	_mapHeight = READ_LE_UINT16(_map + 2);
-	_map += 8;
-	_values = dataStart + valuesOffs;
-	debug(0, "PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
-		_width, _height, mapOffs, valuesOffs, _mapWidth, _mapHeight);
-}
-
-int PriorityLayer::getPriority(Common::Point pos) {
-	pos.x = CLIP<int16>(pos.x, 0, _width - 1);
-	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
-	const int16 tx = pos.x / 32, sx = pos.x % 32;
-	const int16 ty = pos.y / 8, sy = pos.y % 8;
-	uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
-	return _values[mapIndex * 32 * 8 + sx + sy * 32];
-}
-
-// ScaleLayer
-
-void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_height = stream.readUint16LE();
-	stream.skip(2);
-	uint32 valuesOffs = stream.readUint32LE();
-	_values = dataStart + valuesOffs;
-	debug(0, "ScaleLayer::load() _height: %d; valuesOffs: %08X",
-		_height, valuesOffs);
-}
-
-int ScaleLayer::getScale(Common::Point pos) {
-	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
-	return _values[pos.y];
-}
-
-// RegionLayer
-
-void RegionLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_unk = stream.readUint32LE();
-	uint32 regionSequenceIdsOffs = stream.readUint32LE();
-	_width = stream.readUint16LE();
-	_height = stream.readUint16LE();
-	uint32 mapOffs = stream.readUint32LE();
-	uint32 valuesOffs = stream.readUint32LE();
-	_regionSequenceIds = dataStart + regionSequenceIdsOffs;
-	_map = dataStart + mapOffs;
-	_values = dataStart + valuesOffs;
-	_mapWidth = READ_LE_UINT16(_map + 0);
-	_mapHeight = READ_LE_UINT16(_map + 2);
-	_map += 8;
-	debug("RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
-		_unk, regionSequenceIdsOffs, _width, _height, mapOffs, valuesOffs);
-}
-
-int RegionLayer::getRegionIndex(Common::Point pos) {
-	pos.x = CLIP<int16>(pos.x, 0, _width - 1);
-	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
-	const int16 tx = pos.x / 32, sx = pos.x % 32;
-	const int16 ty = pos.y / 8, sy = pos.y % 8;
-	uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
-	return _values[mapIndex * 32 * 8 + sx + sy * 32];
-}
-
-uint32 RegionLayer::getRegionSequenceId(int regionIndex) {
-	return READ_LE_UINT32(_regionSequenceIds + 4 * regionIndex);
-}
-
-// Palette
-
-void Palette::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_count = stream.readUint16LE();
-	_unk = stream.readUint16LE();
-	uint32 paletteOffs = stream.readUint32LE();
-	_palette = dataStart + paletteOffs;
-}
-
-// BackgroundObject
-
-void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_objectId = stream.readUint32LE();
-	_flags = stream.readUint16LE();
-	_priority = stream.readUint16LE();
-	uint32 pointsConfigOffs = stream.readUint32LE();
-	_pointsConfig = dataStart + pointsConfigOffs;
-	debug(0, "BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
-		_objectId, _flags, _priority, pointsConfigOffs);
-}
-
-// PathWalkPoints
-
-void PathWalkPoints::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_points = new PointArray();
-	uint count = stream.readUint32LE();
-	uint32 pointsOffs = stream.readUint32LE();
-	_points->reserve(count);
-	stream.seek(pointsOffs);
-	for (uint i = 0; i < count; ++i) {
-		Common::Point pt;
-		loadPoint(stream, pt);
-		_points->push_back(pt);
-	}
-	debug(0, "PathWalkPoints::load() count: %d; pointsOffs: %08X",
-		count, pointsOffs);
-}
-
-// PathWalkRects
-
-void PathWalkRects::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_rects = new PathLines();
-	uint count = stream.readUint32LE();
-	uint32 rectsOffs = stream.readUint32LE();
-	_rects->reserve(count);
-	stream.seek(rectsOffs);
-	for (uint i = 0; i < count; ++i) {
-		PathLine rect;
-		loadPoint(stream, rect.p0);
-		loadPoint(stream, rect.p1);
-		_rects->push_back(rect);
-	}
-	debug(0, "PathWalkRects::load() count: %d; rectsOffs: %08X",
-		count, rectsOffs);
-}
-
-// BackgroundResource
-
-BackgroundResource::BackgroundResource() {
-}
-
-BackgroundResource::~BackgroundResource() {
-	// TODO Free stuff
-}
-
-void BackgroundResource::load(byte *data, uint32 dataSize) {
-	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-	
-	stream.seek(8);
-	_paletteIndex = stream.readUint16LE();
-	
-	// Load background pixels
-	stream.seek(0x0A);
-	_bgInfosCount = stream.readUint16LE();
-	_bgInfos = new BgInfo[_bgInfosCount];
-	stream.seek(0x20);
-	uint32 bgInfosOffs = stream.readUint32LE();
-	for (uint i = 0; i < _bgInfosCount; ++i) {
-		stream.seek(bgInfosOffs + i * 0x1C);
-		_bgInfos[i].load(data, stream);
-	}
-
-	// Load scale layers
-	stream.seek(0x10);
-	_scaleLayersCount = stream.readUint16LE();
-	_scaleLayers = new ScaleLayer[_scaleLayersCount];
-	stream.seek(0x2C);
-	uint32 scaleLayersOffs = stream.readUint32LE();
-	debug(0, "_scaleLayersCount: %d", _scaleLayersCount);
-	for (uint i = 0; i < _scaleLayersCount; ++i) {
-		stream.seek(scaleLayersOffs + i * 8);
-		_scaleLayers[i].load(data, stream);
-	}
-
-	// Load priority layers
-	stream.seek(0x14);
-	_priorityLayersCount = stream.readUint16LE();
-	_priorityLayers = new PriorityLayer[_priorityLayersCount];
-	stream.seek(0x34);
-	uint32 priorityLayersOffs = stream.readUint32LE();
-	debug("_priorityLayersCount: %d", _priorityLayersCount);
-	for (uint i = 0; i < _priorityLayersCount; ++i) {
-		stream.seek(priorityLayersOffs + i * 12);
-		_priorityLayers[i].load(data, stream);
-	}
-
-	// Load region layers
-	stream.seek(0x16);
-	_regionLayersCount = stream.readUint16LE();
-	_regionLayers = new RegionLayer[_regionLayersCount];
-	stream.seek(0x38);
-	uint32 regionLayersOffs = stream.readUint32LE();
-	debug("_regionLayersCount: %d", _regionLayersCount);
-	for (uint i = 0; i < _regionLayersCount; ++i) {
-		stream.seek(regionLayersOffs + i * 20);
-		_regionLayers[i].load(data, stream);
-	}
-
-	// Load region sequences
-	stream.seek(0x1E);
-	_regionSequencesCount = stream.readUint16LE();
-	_regionSequences = new Sequence[_regionSequencesCount];
-	stream.seek(0x48);
-	uint32 regionSequencesOffs = stream.readUint32LE();
-	stream.seek(regionSequencesOffs);
-	for (uint i = 0; i < _regionSequencesCount; ++i)
-		_regionSequences[i].load(data, stream);
-
-	// Load background objects
-	stream.seek(0x1C);
-	_backgroundObjectsCount = stream.readUint16LE();
-	_backgroundObjects = new BackgroundObject[_backgroundObjectsCount];
-	stream.seek(0x44);
-	uint32 backgroundObjectsOffs = stream.readUint32LE();
-	debug(0, "_backgroundObjectsCount: %d", _backgroundObjectsCount);
-	for (uint i = 0; i < _backgroundObjectsCount; ++i) {
-		stream.seek(backgroundObjectsOffs + i * 12);
-		_backgroundObjects[i].load(data, stream);
-	}
-	
-	// Load path walk points
-	stream.seek(0x0E);
-	_pathWalkPointsCount = stream.readUint16LE();
-	debug("_pathWalkPointsCount: %d", _pathWalkPointsCount);
-	_pathWalkPoints = new PathWalkPoints[_pathWalkPointsCount];
-	stream.seek(0x28);
-	uint32 pathWalkPointsOffs = stream.readUint32LE();
-	for (uint i = 0; i < _pathWalkPointsCount; ++i) {
-		stream.seek(pathWalkPointsOffs + i * 8);
-		_pathWalkPoints[i].load(data, stream);
-	}
-
-	// Load path walk rects
-	stream.seek(0x12);
-	_pathWalkRectsCount = stream.readUint16LE();
-	debug("_pathWalkRectsCount: %d", _pathWalkRectsCount);
-	_pathWalkRects = new PathWalkRects[_pathWalkRectsCount];
-	stream.seek(0x30);
-	uint32 pathWalkRectsOffs = stream.readUint32LE();
-	for (uint i = 0; i < _pathWalkRectsCount; ++i) {
-		stream.seek(pathWalkRectsOffs + i * 8);
-		_pathWalkRects[i].load(data, stream);
-	}
-
-	// Load named points
-	stream.seek(0xC);
-	uint namedPointsCount = stream.readUint16LE();
-	stream.seek(0x24);
-	uint32 namedPointsOffs = stream.readUint32LE();
-	stream.seek(namedPointsOffs);
-	_namedPoints.load(namedPointsCount, stream);
-
-	// Load palettes
-	stream.seek(0x18);
-	_palettesCount = stream.readUint16LE();
-	_palettes = new Palette[_palettesCount];
-	stream.seek(0x3C);
-	uint32 palettesOffs = stream.readUint32LE();
-	debug(0, "_palettesCount: %d", _palettesCount);
-	for (uint i = 0; i < _palettesCount; ++i) {
-		stream.seek(palettesOffs + i * 8);
-		_palettes[i].load(data, stream);
-	}
-
-}
-
-int BackgroundResource::findMasterBgIndex() {
-	int index = 1;
-	while (!_bgInfos[index - 1]._flags & 1)
-		++index;
-	return index;
-}
-
-PriorityLayer *BackgroundResource::getPriorityLayer(uint index) {
-	return &_priorityLayers[index];
-}
-
-ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
-	return &_scaleLayers[index];
-}
-
-RegionLayer *BackgroundResource::getRegionLayer(uint index) {
-	return &_regionLayers[index];
-}
-
-PathWalkPoints *BackgroundResource::getPathWalkPoints(uint index) {
-	return &_pathWalkPoints[index];
-}
-
-PathWalkRects *BackgroundResource::getPathWalkRects(uint index) {
-	return &_pathWalkRects[index];
-}
-
-Palette *BackgroundResource::getPalette(uint index) {
-	return &_palettes[index];
-}
-
-bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
-	return _namedPoints.findNamedPoint(namedPointId, pt);
-}
-
-// BackgroundInstance
-
-BackgroundInstance::BackgroundInstance(IllusionsEngine *vm)
-	: _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
-}
-
-void BackgroundInstance::load(Resource *resource) {
-	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
-
-	BackgroundResource *backgroundResource = new BackgroundResource();
-	backgroundResource->load(resource->_data, resource->_dataSize);
-
-	_bgRes = backgroundResource;
-	_tag = resource->_tag;
-	initSurface();
-	
-	// Insert background objects
-	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
-		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
-		
-	registerResources();
-
-	// TODO camera_fadeClear();
-	int index = _bgRes->findMasterBgIndex();
-	_vm->_camera->set(_bgRes->_bgInfos[index - 1]._panPoint, _bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
-
-	if (_bgRes->_palettesCount > 0) {
-		Palette *palette = _bgRes->getPalette(_bgRes->_paletteIndex - 1);
-		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
-	}
-
-}
-
-void BackgroundInstance::unload() {
-	debug("BackgroundInstance::unload()");
-	freeSurface();
-	unregisterResources();
-	delete _bgRes;
-	_vm->_backgroundInstances->removeBackgroundInstance(this);
-	_vm->setDefaultTextCoords();
-}
-
-void BackgroundInstance::pause() {
-	++_pauseCtr;
-	if (_pauseCtr <= 1) {
-		unregisterResources();
-		_vm->setDefaultTextCoords();
-		_vm->_camera->getActiveState(_savedCameraState);
-		_savedPalette = new byte[1024];
-		_vm->_screen->getPalette(_savedPalette);
-		freeSurface();
-	}
-}
-
-void BackgroundInstance::unpause() {
-	--_pauseCtr;
-	if (_pauseCtr <= 0) {
-		registerResources();
-		initSurface();
-		_vm->_screen->setPalette(_savedPalette, 1, 256);
-		delete[] _savedPalette;
-		_savedPalette = 0;
-		// TODO _vm->_screen->_fadeClear();
-		_vm->_camera->setActiveState(_savedCameraState);
-		_vm->_backgroundInstances->refreshPan();
-	}
-}
-
-void BackgroundInstance::registerResources() {
-	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
-		Sequence *sequence = &_bgRes->_regionSequences[i];
-		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-	}
-}
-	
-void BackgroundInstance::unregisterResources() {
-	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
-		Sequence *sequence = &_bgRes->_regionSequences[i];
-		_vm->_dict->removeSequence(sequence->_sequenceId);
-	}
-}	
-
-void BackgroundInstance::initSurface() {
-	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
-		_surfaces[i] = 0;
-	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
-		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
-		_panPoints[i] = bgInfo->_panPoint;
-		_surfaces[i] = _vm->_screen->allocSurface(bgInfo->_surfInfo);
-		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
-	}
-}
-
-void BackgroundInstance::freeSurface() {
-	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i)
-		if (_surfaces[i]) {
-			_surfaces[i]->free();
-			delete _surfaces[i];
-			_surfaces[i] = 0;
-		}
-}
-
-void BackgroundInstance::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
-	switch (_vm->getGameId()) {
-	case kGameIdDuckman:
-		drawTiles8(surface, tileMap, tilePixels);
-		break;
-	case kGameIdBBDOU:
-		drawTiles16(surface, tileMap, tilePixels);
-		break;
-	}
-}
-
-void BackgroundInstance::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
-	const int kTileWidth = 32;
-	const int kTileHeight = 8;
-	const int kTileSize = kTileWidth * kTileHeight;
-	uint tileMapIndex = 0;
-	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
-		int tileDestY = tileY * kTileHeight;
-		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
-		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
-			int tileDestX = tileX * kTileWidth;
-			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
-			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
-			++tileMapIndex;
-			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
-			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
-			for (int h = 0; h < tileDestH; ++h) {
-				memcpy(dst, src, tileDestW);
-				dst += surface->pitch;
-				src += kTileWidth;
-			}
-		}
-	}
-}
-
-void BackgroundInstance::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
-	const int kTileWidth = 32;
-	const int kTileHeight = 8;
-	const int kTileSize = kTileWidth * kTileHeight * 2;
-	uint tileMapIndex = 0;
-	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
-		int tileDestY = tileY * kTileHeight;
-		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
-		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
-			int tileDestX = tileX * kTileWidth;
-			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
-			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
-			++tileMapIndex;
-			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
-			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
-			for (int h = 0; h < tileDestH; ++h) {
-				for (int w = 0; w < tileDestW; ++w) {
-					uint16 pixel = READ_LE_UINT16(src + w * 2);
-					WRITE_LE_UINT16(dst + w * 2, pixel);
-				}
-				dst += surface->pitch;
-				src += kTileWidth * 2;
-			}
-		}
-	}
-}
-
-// BackgroundInstanceList
-
-BackgroundInstanceList::BackgroundInstanceList(IllusionsEngine *vm)
-	: _vm(vm) {
-}
-
-BackgroundInstanceList::~BackgroundInstanceList() {
-}
-
-BackgroundInstance *BackgroundInstanceList::createBackgroundInstance(Resource *resource) {
-	BackgroundInstance *backgroundInstance = new BackgroundInstance(_vm);
-	backgroundInstance->load(resource);
-	_items.push_back(backgroundInstance);
-	return backgroundInstance;
-}
-
-void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgroundInstance) {
-	_items.remove(backgroundInstance);
-}
-
-void BackgroundInstanceList::pauseByTag(uint32 tag) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
-			(*it)->pause();
-}
-
-void BackgroundInstanceList::unpauseByTag(uint32 tag) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
-			(*it)->unpause();
-}
-
-BackgroundInstance *BackgroundInstanceList::findActiveBackgroundInstance() {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_pauseCtr == 0)
-			return (*it);
-	return 0;
-}
-
-BackgroundInstance *BackgroundInstanceList::findBackgroundByResource(BackgroundResource *backgroundResource) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_bgRes == backgroundResource)
-			return (*it);
-	return 0;
-}
-
-BackgroundResource *BackgroundInstanceList::getActiveBgResource() {
-	BackgroundInstance *background = findActiveBackgroundInstance();
-	if (background)
-		return background->_bgRes;
-	return 0;
-}
-
-WidthHeight BackgroundInstanceList::getMasterBgDimensions() {
-	BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
-	int16 index = backgroundInstance->_bgRes->findMasterBgIndex();
-	return backgroundInstance->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
-}
-
-void BackgroundInstanceList::refreshPan() {
-	BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
-	if (backgroundInstance) {
-		WidthHeight dimensions = getMasterBgDimensions();
-		_vm->_camera->refreshPan(backgroundInstance, dimensions);
-	}
-}
-
-bool BackgroundInstanceList::findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt) {
-	BackgroundResource *backgroundResource = getActiveBgResource();
-	return backgroundResource ? backgroundResource->findNamedPoint(namedPointId, pt) : false;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/backgroundresource.h b/engines/illusions/backgroundresource.h
deleted file mode 100644
index 9438501..0000000
--- a/engines/illusions/backgroundresource.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_BACKGROUNDRESOURCE_H
-#define ILLUSIONS_BACKGROUNDRESOURCE_H
-
-#include "illusions/camera.h"
-#include "illusions/graphics.h"
-#include "illusions/pathfinder.h"
-#include "illusions/resourcesystem.h"
-#include "graphics/surface.h"
-
-#include "common/array.h"
-#include "common/file.h"
-#include "common/list.h"
-#include "common/memstream.h"
-#include "common/rect.h"
-#include "common/substream.h"
-#include "common/system.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-struct Sequence;
-
-class BackgroundResourceLoader : public BaseResourceLoader {
-public:
-	BackgroundResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~BackgroundResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-struct TileMap {
-	int16 _width, _height;
-	//field_4 dd
-	byte *_map;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct BgInfo {
-	uint32 _flags;
-	//field_4 dw
-	int16 _priorityBase;
-	SurfInfo _surfInfo;
-	Common::Point _panPoint;
-	TileMap _tileMap;
-	byte *_tilePixels;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-class PriorityLayer {
-public:
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-	int getPriority(Common::Point pos);
-protected:
-	int16 _width, _height;
-	int16 _mapWidth, _mapHeight;
-	byte *_map, *_values;
-};
-
-class ScaleLayer {
-public:
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-	int getScale(Common::Point pos);
-protected:
-	int16 _height;
-	byte *_values;
-};
-
-class RegionLayer {
-public:
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-	int getRegionIndex(Common::Point pos);
-	uint32 getRegionSequenceId(int regionIndex);
-protected:
-	uint32 _unk;
-	byte *_regionSequenceIds;
-	int16 _width, _height;
-	int16 _mapWidth, _mapHeight;
-	byte *_map, *_values;
-};
-
-struct Palette {
-	uint16 _count;
-	uint16 _unk;
-	byte *_palette;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct BackgroundObject {
-	uint32 _objectId;
-	uint16 _flags;
-	int16 _priority;
-	byte *_pointsConfig;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct PathWalkPoints {
-	PointArray *_points;
-	PathWalkPoints() : _points(0) {}
-	~PathWalkPoints() { delete _points; }
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct PathWalkRects {
-	PathLines *_rects;
-	PathWalkRects() : _rects(0) {}
-	~PathWalkRects() { delete _rects; }
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-class BackgroundResource {
-public:
-	BackgroundResource();
-	~BackgroundResource();
-	void load(byte *data, uint32 dataSize);
-	int findMasterBgIndex();
-	PriorityLayer *getPriorityLayer(uint index);
-	ScaleLayer *getScaleLayer(uint index);
-	RegionLayer *getRegionLayer(uint index);
-	PathWalkPoints *getPathWalkPoints(uint index);
-	PathWalkRects *getPathWalkRects(uint index);
-	Palette *getPalette(uint index);
-	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
-public:
-
-	uint _paletteIndex;
-
-	uint _bgInfosCount;
-	BgInfo *_bgInfos;
-	
-	uint _priorityLayersCount;
-	PriorityLayer *_priorityLayers;
-
-	uint _scaleLayersCount;
-	ScaleLayer *_scaleLayers;
-
-	uint _regionLayersCount;
-	RegionLayer *_regionLayers;
-
-	uint _regionSequencesCount;
-	Sequence *_regionSequences;
-
-	uint _backgroundObjectsCount;
-	BackgroundObject *_backgroundObjects;
-
-	uint _pathWalkPointsCount;
-	PathWalkPoints *_pathWalkPoints;
-
-	uint _pathWalkRectsCount;
-	PathWalkRects *_pathWalkRects;
-
-	NamedPoints _namedPoints;
-	
-	uint _palettesCount;
-	Palette *_palettes;
-
-};
-
-const uint kMaxBackgroundItemSurfaces = 3;
-
-class BackgroundInstance : public ResourceInstance {
-public:
-	BackgroundInstance(IllusionsEngine *vm);
-	virtual void load(Resource *resource);
-	virtual void unload();
-	virtual void pause();
-	virtual void unpause();
-public:
-	IllusionsEngine *_vm;
-	uint32 _tag;
-	int _pauseCtr;
-	BackgroundResource *_bgRes;
-	Common::Point _panPoints[kMaxBackgroundItemSurfaces];
-	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
-	CameraState _savedCameraState;
-	byte *_savedPalette;
-	void registerResources();	
-	void unregisterResources();	
-	void initSurface();
-	void freeSurface();
-	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
-	void drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
-	void drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
-};
-
-class BackgroundInstanceList {
-public:
-	BackgroundInstanceList(IllusionsEngine *vm);
-	~BackgroundInstanceList();
-	BackgroundInstance *createBackgroundInstance(Resource *resource);
-	void removeBackgroundInstance(BackgroundInstance *backgroundInstance);
-	void pauseByTag(uint32 tag);
-	void unpauseByTag(uint32 tag);
-	BackgroundInstance *findActiveBackgroundInstance();
-	BackgroundInstance *findBackgroundByResource(BackgroundResource *backgroundResource);
-	BackgroundResource *getActiveBgResource();
-	WidthHeight getMasterBgDimensions();
-	void refreshPan();
-	bool findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt);
-//protected:
-public:
-	typedef Common::List<BackgroundInstance*> Items;
-	typedef Items::iterator ItemsIterator;
-	IllusionsEngine *_vm;
-	Items _items;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_BACKGROUNDRESOURCE_H
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 4e6281a..ff774b6 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -22,8 +22,8 @@
 
 #include "illusions/illusions.h"
 #include "illusions/camera.h"
-#include "illusions/backgroundresource.h"
 #include "illusions/fixedpoint.h"
+#include "illusions/resources/backgroundresource.h"
 #include "illusions/time.h"
 
 namespace Illusions {
diff --git a/engines/illusions/dictionary.cpp b/engines/illusions/dictionary.cpp
index 5cf4cef..d1b43ce 100644
--- a/engines/illusions/dictionary.cpp
+++ b/engines/illusions/dictionary.cpp
@@ -22,10 +22,10 @@
 
 #include "illusions/illusions.h"
 #include "illusions/dictionary.h"
-#include "illusions/actorresource.h"
-#include "illusions/backgroundresource.h"
-#include "illusions/fontresource.h"
-#include "illusions/talkresource.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/resources/talkresource.h"
 
 namespace Illusions {
 
diff --git a/engines/illusions/fontresource.cpp b/engines/illusions/fontresource.cpp
deleted file mode 100644
index 91007fc..0000000
--- a/engines/illusions/fontresource.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/fontresource.h"
-#include "illusions/dictionary.h"
-
-namespace Illusions {
-
-// FontResourceLoader
-
-void FontResourceLoader::load(Resource *resource) {
-	FontInstance *fontInstance = new FontInstance(_vm);
-	fontInstance->load(resource);
-	resource->_instance = fontInstance;
-}
-
-void FontResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
-}
-
-bool FontResourceLoader::isFlag(int flag) {
-	return
-		flag == kRlfLoadFile;
-}
-
-// CharInfo
-
-void CharInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_width = stream.readUint16LE();
-	_field_2 = stream.readUint16LE();
-	uint32 pixelsOffs = stream.readUint32LE();
-	_pixels = dataStart + pixelsOffs;
-	debug(2, "CharInfo::load() _width: %d; _field_2: %d; pixelsOffs: %08X",
-		_width, _field_2, pixelsOffs);
-}
-
-// CharRange
-
-void CharRange::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_firstChar = stream.readUint16LE();
-	_lastChar = stream.readUint16LE();
-	uint count = _lastChar - _firstChar + 1;
-	uint32 charInfosOffs = stream.readUint32LE();
-	_charInfos = new CharInfo[count];
-	for (uint i = 0; i < count; ++i) {
-		stream.seek(charInfosOffs + i * 8);
-		_charInfos[i].load(dataStart, stream);
-	}
-	debug(2, "CharRange::load() _firstChar: %d; _lastChar: %d; charInfosOffs: %08X",
-		_firstChar, _lastChar, charInfosOffs);
-}
-
-CharInfo *CharRange::getCharInfo(uint16 c) {
-	return &_charInfos[c - _firstChar];
-}
-
-bool CharRange::containsChar(uint16 c) {
-	return c >= _firstChar && c <= _lastChar;
-}
-
-// FontResource
-
-FontResource::FontResource() {
-}
-
-FontResource::~FontResource() {
-}
-
-void FontResource::load(Resource *resource) {
-	byte *data = resource->_data;
-	uint32 dataSize = resource->_dataSize;
-	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-	_totalSize = stream.readUint32LE();
-	_charHeight = stream.readUint16LE();
-	_field_6 = stream.readUint16LE();
-	_colorIndex = stream.readUint16LE();
-	_lineIncr = stream.readUint16LE();
-	_widthC = stream.readUint16LE();
-	_charRangesCount = stream.readUint16LE();
-	uint32 charRangesOffs = stream.pos();
-	_charRanges = new CharRange[_charRangesCount];
-	for (uint i = 0; i < _charRangesCount; ++i) {
-		stream.seek(charRangesOffs + i * 8);
-		_charRanges[i].load(data, stream);
-	}
-	debug(2, "FontResource::load() _charHeight: %d; _field_6: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
-		_charHeight, _field_6, _colorIndex, _lineIncr, _widthC, _charRangesCount);
-}
-
-CharInfo *FontResource::getCharInfo(uint16 c) {
-	for (uint i = 0; i < _charRangesCount; ++i)
-		if (_charRanges[i].containsChar(c))
-			return _charRanges[i].getCharInfo(c);
-	return 0;
-}
-
-// FontInstance
-
-FontInstance::FontInstance(IllusionsEngine *vm) : _vm(vm) {
-}
-
-void FontInstance::load(Resource *resource) {
-	_fontResource = new FontResource();
-	_fontResource->load(resource);
-	_resId = resource->_resId;
-	_vm->_dict->addFont(resource->_resId, _fontResource);
-}
-
-void FontInstance::unload() {
-	delete _fontResource;
-	_vm->_dict->removeFont(_resId);
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/fontresource.h b/engines/illusions/fontresource.h
deleted file mode 100644
index 8ea059f..0000000
--- a/engines/illusions/fontresource.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_FONTRESOURCE_H
-#define ILLUSIONS_FONTRESOURCE_H
-
-#include "illusions/graphics.h"
-#include "illusions/resourcesystem.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class FontResourceLoader : public BaseResourceLoader {
-public:
-	FontResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~FontResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-// TODO
-
-struct CharInfo {
-	int16 _width;
-	int16 _field_2;
-	byte *_pixels;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-struct CharRange {
-	uint16 _firstChar;
-	uint16 _lastChar;
-	CharInfo *_charInfos;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-	CharInfo *getCharInfo(uint16 c);
-	bool containsChar(uint16 c);
-};
-
-class FontResource {
-public:
-	FontResource();
-	~FontResource();
-	void load(Resource *resource);
-	CharInfo *getCharInfo(uint16 c);
-	int16 getColorIndex() const { return _colorIndex; }
-public:
-	uint32 _totalSize;
-	int16 _charHeight;
-	int16 _field_6;
-	int16 _colorIndex;
-	int16 _lineIncr;
-	int16 _widthC;
-	uint _charRangesCount;
-	CharRange *_charRanges;
-	CharRange *getCharRange(uint16 c);
-};
-
-class FontInstance : public ResourceInstance {
-public:
-	FontInstance(IllusionsEngine *vm);              
-	virtual void load(Resource *resource);
-	virtual void unload();
-public:
-	IllusionsEngine *_vm;	
-	FontResource *_fontResource;
-	uint32 _resId;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_FONTRESOURCE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 93619a0..4c8c7dc 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -22,22 +22,22 @@
 
 #include "illusions/illusions.h"
 #include "illusions/actor.h"
-#include "illusions/actorresource.h"
-#include "illusions/backgroundresource.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
 #include "illusions/dictionary.h"
-#include "illusions/fontresource.h"
+#include "illusions/resources/fontresource.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
-#include "illusions/scriptresource.h"
 #include "illusions/sound.h"
-#include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
-#include "illusions/talkresource.h"
 #include "illusions/thread.h"
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
index 3766c00..31f8ab4 100644
--- a/engines/illusions/illusions_bbdou.cpp
+++ b/engines/illusions/illusions_bbdou.cpp
@@ -22,25 +22,25 @@
 
 #include "illusions/illusions_bbdou.h"
 #include "illusions/actor.h"
-#include "illusions/actorresource.h"
-#include "illusions/backgroundresource.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
 #include "illusions/dictionary.h"
-#include "illusions/fontresource.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
 #include "illusions/scriptstack.h"
 #include "illusions/scriptopcodes_bbdou.h"
-#include "illusions/scriptresource.h"
 #include "illusions/sound.h"
-#include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
-#include "illusions/talkresource.h"
 #include "illusions/thread.h"
 #include "illusions/time.h"
 #include "illusions/updatefunctions.h"
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
index 50aea3d..4d97343 100644
--- a/engines/illusions/illusions_duckman.cpp
+++ b/engines/illusions/illusions_duckman.cpp
@@ -22,25 +22,25 @@
 
 #include "illusions/illusions_duckman.h"
 #include "illusions/actor.h"
-#include "illusions/actorresource.h"
-#include "illusions/backgroundresource.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
 #include "illusions/dictionary.h"
-#include "illusions/fontresource.h"
+#include "illusions/resources/fontresource.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
-#include "illusions/midiresource.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/midiresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
 #include "illusions/scriptopcodes_duckman.h"
-#include "illusions/scriptresource.h"
 #include "illusions/scriptstack.h"
 #include "illusions/sound.h"
-#include "illusions/soundresource.h"
 #include "illusions/specialcode.h"
-#include "illusions/talkresource.h"
 #include "illusions/textdrawer.h"
 #include "illusions/thread.h"
 #include "illusions/time.h"
diff --git a/engines/illusions/midiresource.cpp b/engines/illusions/midiresource.cpp
deleted file mode 100644
index fc088ab..0000000
--- a/engines/illusions/midiresource.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/midiresource.h"
-
-namespace Illusions {
-
-// MidiGroupResourceLoader
-
-void MidiGroupResourceLoader::load(Resource *resource) {
-	debug("MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
-
-    // TODO
-	
-}
-
-void MidiGroupResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
-}
-
-bool MidiGroupResourceLoader::isFlag(int flag) {
-	return false;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/midiresource.h b/engines/illusions/midiresource.h
deleted file mode 100644
index 9032e99..0000000
--- a/engines/illusions/midiresource.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_MIDIRESOURCE_H
-#define ILLUSIONS_MIDIRESOURCE_H
-
-#include "illusions/graphics.h"
-#include "illusions/resourcesystem.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class MidiGroupResourceLoader : public BaseResourceLoader {
-public:
-	MidiGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~MidiGroupResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 63e0210..05cdb22 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -2,8 +2,6 @@ MODULE := engines/illusions
 
 MODULE_OBJS := \
 	actor.o \
-	actorresource.o \
-	backgroundresource.o \
 	bbdou/bbdou_bubble.o \
 	bbdou/bbdou_cursor.o \
 	bbdou/bbdou_inventory.o \
@@ -13,14 +11,19 @@ MODULE_OBJS := \
 	detection.o \
 	dictionary.o \
 	fixedpoint.o \
-	fontresource.o \
 	graphics.o \
 	illusions.o \
 	illusions_bbdou.o \
 	illusions_duckman.o \
 	input.o \
-	midiresource.o \
 	pathfinder.o \
+	resources/actorresource.o \
+	resources/backgroundresource.o \
+	resources/fontresource.o \
+	resources/midiresource.o \
+	resources/scriptresource.o \
+	resources/soundresource.o \
+	resources/talkresource.o \
 	resourcesystem.o \
 	screen.o \
 	screentext.o \
@@ -28,20 +31,17 @@ MODULE_OBJS := \
 	scriptopcodes.o \
 	scriptopcodes_bbdou.o \
 	scriptopcodes_duckman.o \
-	scriptresource.o \
 	sequenceopcodes.o \
 	sound.o \
-	soundresource.o \
 	specialcode.o \
-	talkresource.o \
 	textdrawer.o \
-	thread.o \
 	threads/abortablethread.o \
 	threads/causethread_duckman.o \
 	threads/scriptthread.o \
 	threads/talkthread.o \
 	threads/talkthread_duckman.o \
 	threads/timerthread.o \
+	thread.o \
 	time.o \
 	updatefunctions.o
 
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
new file mode 100644
index 0000000..a8d62e5
--- /dev/null
+++ b/engines/illusions/resources/actorresource.cpp
@@ -0,0 +1,304 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/dictionary.h"
+
+namespace Illusions {
+
+// ActorResourceLoader
+
+void ActorResourceLoader::load(Resource *resource) {
+	resource->_instance = _vm->_actorInstances->createActorInstance(resource);
+}
+
+void ActorResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.act", resource->_resId);
+}
+
+bool ActorResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+// Frame
+
+void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_flags = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	uint32 pointsConfigOffs = stream.readUint32LE();
+	_surfInfo.load(stream);
+	uint32 compressedPixelsOffs = stream.readUint32LE();
+	_compressedPixels = dataStart + compressedPixelsOffs;
+	_pointsConfig = dataStart + pointsConfigOffs;
+	debug(5, "Frame::load() compressedPixelsOffs: %08X",
+		compressedPixelsOffs);
+}
+
+// Sequence
+
+void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_sequenceId = stream.readUint32LE();
+	_unk4 = stream.readUint32LE();
+	uint32 sequenceCodeOffs = stream.readUint32LE();
+	_sequenceCode = dataStart + sequenceCodeOffs;
+	debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
+		_sequenceId, _unk4, sequenceCodeOffs);
+}
+
+// ActorType
+
+void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_actorTypeId = stream.readUint32LE();
+	_surfInfo.load(stream);
+	uint32 pointsConfigOffs = stream.readUint32LE();
+	uint namedPointsCount = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	uint32 namedPointsOffs = stream.readUint32LE();
+	_color.r = stream.readByte();
+	_color.g = stream.readByte();
+	_color.b = stream.readByte();
+	stream.readByte(); // Skip padding
+	_scale = stream.readByte();
+	_priority = stream.readByte();
+	_value1E = stream.readUint16LE();
+	_pathWalkPointsIndex = stream.readUint16LE();
+	_scaleLayerIndex = stream.readUint16LE();
+	_pathWalkRectIndex = stream.readUint16LE();
+	_priorityLayerIndex = stream.readUint16LE();
+	_regionLayerIndex = stream.readUint16LE();
+	_flags = stream.readUint16LE();
+	_pointsConfig = dataStart + pointsConfigOffs;
+	stream.seek(namedPointsOffs);
+	_namedPoints.load(namedPointsCount, stream);
+	debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
+		_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
+	debug(5, "ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
+		_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
+	debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
+		_priorityLayerIndex, _regionLayerIndex,_flags);
+}
+
+// ActorResource
+
+ActorResource::ActorResource() {
+}
+
+ActorResource::~ActorResource() {
+}
+
+void ActorResource::load(Resource *resource) {
+	byte *data = resource->_data;
+	uint32 dataSize = resource->_dataSize;
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	_totalSize = stream.readUint32LE();
+
+	// Load actor types	
+	stream.seek(0x06);
+	uint actorTypesCount = stream.readUint16LE();
+	stream.seek(0x10);
+	uint32 actorTypesOffs = stream.readUint32LE();
+	_actorTypes.reserve(actorTypesCount);
+	for (uint i = 0; i < actorTypesCount; ++i) {
+		ActorType actorType;
+		stream.seek(actorTypesOffs + i * 0x2C);
+		actorType.load(data, stream);
+		_actorTypes.push_back(actorType);
+	}
+
+	// Load sequences	
+	stream.seek(0x08);
+	uint sequencesCount = stream.readUint16LE();
+	stream.seek(0x14);
+	uint32 sequencesOffs = stream.readUint32LE();
+	stream.seek(sequencesOffs);
+	_sequences.reserve(sequencesCount);
+	for (uint i = 0; i < sequencesCount; ++i) {
+		Sequence sequence;
+		sequence.load(data, stream);
+		_sequences.push_back(sequence);
+	}
+
+	// Load frames	
+	stream.seek(0x0A);
+	uint framesCount = stream.readUint16LE();
+	stream.seek(0x18);
+	uint32 framesOffs = stream.readUint32LE();
+	stream.seek(framesOffs);
+	_frames.reserve(framesCount);
+	for (uint i = 0; i < framesCount; ++i) {
+		Frame frame;
+		frame.load(data, stream);
+		_frames.push_back(frame);
+	}
+	
+	// Load named points
+	if (resource->_gameId == kGameIdBBDOU) {
+		// The count isn't stored explicitly so calculate it
+		uint namedPointsCount = (actorTypesOffs - 0x20) / 8;
+		stream.seek(0x20);
+		_namedPoints.load(namedPointsCount, stream);
+	}
+	
+	debug("ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
+}
+
+bool ActorResource::containsSequence(Sequence *sequence) {
+	for (uint i = 0; i < _sequences.size(); ++i)
+		if (sequence == &_sequences[i])
+			return true;
+	return false;
+}
+
+bool ActorResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	return _namedPoints.findNamedPoint(namedPointId, pt);
+}
+
+// ActorInstance
+
+ActorInstance::ActorInstance(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void ActorInstance::load(Resource *resource) {
+	_actorResource = new ActorResource();
+	_actorResource->load(resource);
+	_tag = resource->_tag;
+	_pauseCtr = 0;
+	initActorTypes();
+	registerResources();
+}
+
+void ActorInstance::unload() {
+	if (_pauseCtr <= 0)
+		unregisterResources();
+	_vm->_actorInstances->removeActorInstance(this);
+	delete _actorResource;
+}
+
+void ActorInstance::pause() {
+	++_pauseCtr;
+	if (_pauseCtr == 1)
+		unregisterResources();
+}
+
+void ActorInstance::unpause() {
+	--_pauseCtr;
+	if (_pauseCtr == 0)
+		registerResources();
+}
+
+void ActorInstance::initActorTypes() {
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
+		ActorType *actorType = &_actorResource->_actorTypes[i];
+		ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
+		if (actorType2) {
+			actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
+				actorType2->_surfInfo._dimensions._width);
+			actorType->_surfInfo._dimensions._height = MAX(actorType->_surfInfo._dimensions._height,
+				actorType2->_surfInfo._dimensions._height);
+			if (actorType->_color.r == 255 && actorType->_color.g == 255 && actorType->_color.b == 255)
+				actorType->_color = actorType2->_color;
+			if (actorType->_value1E == 0)
+				actorType->_value1E = actorType2->_value1E;
+		}
+	}
+}
+
+void ActorInstance::registerResources() {
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
+		ActorType *actorType = &_actorResource->_actorTypes[i];
+		_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
+	}
+	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
+		Sequence *sequence = &_actorResource->_sequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	}
+}	
+
+void ActorInstance::unregisterResources() {
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i)
+		_vm->_dict->removeActorType(_actorResource->_actorTypes[i]._actorTypeId);
+	for (uint i = 0; i < _actorResource->_sequences.size(); ++i)
+		_vm->_dict->removeSequence(_actorResource->_sequences[i]._sequenceId);
+}
+
+// ActorInstanceList
+
+ActorInstanceList::ActorInstanceList(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+ActorInstanceList::~ActorInstanceList() {
+}
+
+ActorInstance *ActorInstanceList::createActorInstance(Resource *resource) {
+	ActorInstance *actorInstance = new ActorInstance(_vm);
+	actorInstance->load(resource);
+	_items.push_back(actorInstance);
+	return actorInstance;
+}
+
+void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
+	_items.remove(actorInstance);
+}
+
+void ActorInstanceList::pauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->pause();
+}
+
+void ActorInstanceList::unpauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->unpause();
+}
+
+FramesList *ActorInstanceList::findSequenceFrames(Sequence *sequence) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
+		ActorInstance *actorInstance = *it;
+		if (actorInstance->_pauseCtr <= 0 && actorInstance->_actorResource->containsSequence(sequence))
+			return &actorInstance->_actorResource->_frames;
+	}
+	return 0;
+}
+
+ActorInstance *ActorInstanceList::findActorByResource(ActorResource *actorResource) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_actorResource == actorResource)
+			return (*it);
+	return 0;
+}
+
+bool ActorInstanceList::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
+		ActorInstance *actorInstance = *it;
+		if (actorInstance->_pauseCtr == 0 && actorInstance->_actorResource->findNamedPoint(namedPointId, pt))
+			return true;
+	}
+	return false;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/actorresource.h b/engines/illusions/resources/actorresource.h
new file mode 100644
index 0000000..2ccb9ef
--- /dev/null
+++ b/engines/illusions/resources/actorresource.h
@@ -0,0 +1,134 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ACTORRESOURCE_H
+#define ILLUSIONS_ACTORRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class ActorResourceLoader : public BaseResourceLoader {
+public:
+	ActorResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~ActorResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+struct Frame {
+	uint16 _flags;
+	byte *_pointsConfig;
+	SurfInfo _surfInfo;
+	byte *_compressedPixels;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct Sequence {
+	uint32 _sequenceId;
+	uint32 _unk4;
+	byte *_sequenceCode;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct ActorType {
+	uint32 _actorTypeId;
+	SurfInfo _surfInfo;
+	byte *_pointsConfig;
+	NamedPoints _namedPoints;
+	RGB _color;
+	byte _scale;
+	byte _priority;
+	int16 _value1E;
+	uint16 _pathWalkPointsIndex;
+	uint16 _scaleLayerIndex;
+	uint16 _pathWalkRectIndex;
+	uint16 _priorityLayerIndex;
+	uint16 _regionLayerIndex;
+	uint16 _flags;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class FramesList : public Common::Array<Frame> {
+};
+
+class ActorResource {
+public:
+	ActorResource();
+	~ActorResource();
+	void load(Resource *resource);
+	bool containsSequence(Sequence *sequence);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
+public:
+	uint32 _totalSize;
+	Common::Array<ActorType> _actorTypes;
+	Common::Array<Sequence> _sequences;
+	FramesList _frames;
+	NamedPoints _namedPoints;
+};
+
+class ActorInstance : public ResourceInstance {
+public:
+	ActorInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+	virtual void pause();
+	virtual void unpause();
+public:
+	IllusionsEngine *_vm;
+	uint32 _tag;
+	int _pauseCtr;
+	ActorResource *_actorResource;
+protected:
+	void initActorTypes();
+	void registerResources();	
+	void unregisterResources();	
+};
+
+class ActorInstanceList {
+public:
+	ActorInstanceList(IllusionsEngine *vm);
+	~ActorInstanceList();
+	ActorInstance *createActorInstance(Resource *resource);
+	void removeActorInstance(ActorInstance *actorInstance);
+	void pauseByTag(uint32 tag);
+	void unpauseByTag(uint32 tag);
+	FramesList *findSequenceFrames(Sequence *sequence);
+	ActorInstance *findActorByResource(ActorResource *actorResource);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
+protected:
+	typedef Common::List<ActorInstance*> Items;
+	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
+	Items _items;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ACTORRESOURCE_H
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
new file mode 100644
index 0000000..bc87311
--- /dev/null
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -0,0 +1,608 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/screen.h"
+#include "common/str.h"
+
+namespace Illusions {
+
+// BackgroundResourceLoader
+
+void BackgroundResourceLoader::load(Resource *resource) {
+	resource->_instance = _vm->_backgroundInstances->createBackgroundInstance(resource);
+}
+
+void BackgroundResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.bg", resource->_resId);
+}
+
+bool BackgroundResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+// TileMap
+
+void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_width = stream.readSint16LE();
+	_height = stream.readSint16LE();
+	stream.skip(4); // Unknown
+	uint32 mapOffs = stream.pos();
+	_map = dataStart + mapOffs;
+	debug(0, "TileMap::load() _width: %d; _height: %d",
+		_width, _height);
+}
+
+// BgInfo
+
+void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_flags = stream.readUint32LE();
+	stream.skip(2); // Unknown
+	_priorityBase = stream.readSint16LE();
+	_surfInfo.load(stream);
+	loadPoint(stream, _panPoint);
+	uint32 tileMapOffs = stream.readUint32LE();
+	uint32 tilePixelsOffs = stream.readUint32LE();
+	stream.seek(tileMapOffs);
+	_tileMap.load(dataStart, stream);
+	_tilePixels = dataStart + tilePixelsOffs;
+	debug(0, "BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
+		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
+}
+
+// PriorityLayer
+
+void PriorityLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_width = stream.readUint16LE();
+	_height = stream.readUint16LE();
+	uint32 mapOffs = stream.readUint32LE();
+	uint32 valuesOffs = stream.readUint32LE();
+	_map = dataStart + mapOffs;
+	_mapWidth = READ_LE_UINT16(_map + 0);
+	_mapHeight = READ_LE_UINT16(_map + 2);
+	_map += 8;
+	_values = dataStart + valuesOffs;
+	debug(0, "PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
+		_width, _height, mapOffs, valuesOffs, _mapWidth, _mapHeight);
+}
+
+int PriorityLayer::getPriority(Common::Point pos) {
+	pos.x = CLIP<int16>(pos.x, 0, _width - 1);
+	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
+	const int16 tx = pos.x / 32, sx = pos.x % 32;
+	const int16 ty = pos.y / 8, sy = pos.y % 8;
+	uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
+	return _values[mapIndex * 32 * 8 + sx + sy * 32];
+}
+
+// ScaleLayer
+
+void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_height = stream.readUint16LE();
+	stream.skip(2);
+	uint32 valuesOffs = stream.readUint32LE();
+	_values = dataStart + valuesOffs;
+	debug(0, "ScaleLayer::load() _height: %d; valuesOffs: %08X",
+		_height, valuesOffs);
+}
+
+int ScaleLayer::getScale(Common::Point pos) {
+	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
+	return _values[pos.y];
+}
+
+// RegionLayer
+
+void RegionLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_unk = stream.readUint32LE();
+	uint32 regionSequenceIdsOffs = stream.readUint32LE();
+	_width = stream.readUint16LE();
+	_height = stream.readUint16LE();
+	uint32 mapOffs = stream.readUint32LE();
+	uint32 valuesOffs = stream.readUint32LE();
+	_regionSequenceIds = dataStart + regionSequenceIdsOffs;
+	_map = dataStart + mapOffs;
+	_values = dataStart + valuesOffs;
+	_mapWidth = READ_LE_UINT16(_map + 0);
+	_mapHeight = READ_LE_UINT16(_map + 2);
+	_map += 8;
+	debug("RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
+		_unk, regionSequenceIdsOffs, _width, _height, mapOffs, valuesOffs);
+}
+
+int RegionLayer::getRegionIndex(Common::Point pos) {
+	pos.x = CLIP<int16>(pos.x, 0, _width - 1);
+	pos.y = CLIP<int16>(pos.y, 0, _height - 1);
+	const int16 tx = pos.x / 32, sx = pos.x % 32;
+	const int16 ty = pos.y / 8, sy = pos.y % 8;
+	uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
+	return _values[mapIndex * 32 * 8 + sx + sy * 32];
+}
+
+uint32 RegionLayer::getRegionSequenceId(int regionIndex) {
+	return READ_LE_UINT32(_regionSequenceIds + 4 * regionIndex);
+}
+
+// Palette
+
+void Palette::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_count = stream.readUint16LE();
+	_unk = stream.readUint16LE();
+	uint32 paletteOffs = stream.readUint32LE();
+	_palette = dataStart + paletteOffs;
+}
+
+// BackgroundObject
+
+void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_objectId = stream.readUint32LE();
+	_flags = stream.readUint16LE();
+	_priority = stream.readUint16LE();
+	uint32 pointsConfigOffs = stream.readUint32LE();
+	_pointsConfig = dataStart + pointsConfigOffs;
+	debug(0, "BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
+		_objectId, _flags, _priority, pointsConfigOffs);
+}
+
+// PathWalkPoints
+
+void PathWalkPoints::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_points = new PointArray();
+	uint count = stream.readUint32LE();
+	uint32 pointsOffs = stream.readUint32LE();
+	_points->reserve(count);
+	stream.seek(pointsOffs);
+	for (uint i = 0; i < count; ++i) {
+		Common::Point pt;
+		loadPoint(stream, pt);
+		_points->push_back(pt);
+	}
+	debug(0, "PathWalkPoints::load() count: %d; pointsOffs: %08X",
+		count, pointsOffs);
+}
+
+// PathWalkRects
+
+void PathWalkRects::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_rects = new PathLines();
+	uint count = stream.readUint32LE();
+	uint32 rectsOffs = stream.readUint32LE();
+	_rects->reserve(count);
+	stream.seek(rectsOffs);
+	for (uint i = 0; i < count; ++i) {
+		PathLine rect;
+		loadPoint(stream, rect.p0);
+		loadPoint(stream, rect.p1);
+		_rects->push_back(rect);
+	}
+	debug(0, "PathWalkRects::load() count: %d; rectsOffs: %08X",
+		count, rectsOffs);
+}
+
+// BackgroundResource
+
+BackgroundResource::BackgroundResource() {
+}
+
+BackgroundResource::~BackgroundResource() {
+	// TODO Free stuff
+}
+
+void BackgroundResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+	
+	stream.seek(8);
+	_paletteIndex = stream.readUint16LE();
+	
+	// Load background pixels
+	stream.seek(0x0A);
+	_bgInfosCount = stream.readUint16LE();
+	_bgInfos = new BgInfo[_bgInfosCount];
+	stream.seek(0x20);
+	uint32 bgInfosOffs = stream.readUint32LE();
+	for (uint i = 0; i < _bgInfosCount; ++i) {
+		stream.seek(bgInfosOffs + i * 0x1C);
+		_bgInfos[i].load(data, stream);
+	}
+
+	// Load scale layers
+	stream.seek(0x10);
+	_scaleLayersCount = stream.readUint16LE();
+	_scaleLayers = new ScaleLayer[_scaleLayersCount];
+	stream.seek(0x2C);
+	uint32 scaleLayersOffs = stream.readUint32LE();
+	debug(0, "_scaleLayersCount: %d", _scaleLayersCount);
+	for (uint i = 0; i < _scaleLayersCount; ++i) {
+		stream.seek(scaleLayersOffs + i * 8);
+		_scaleLayers[i].load(data, stream);
+	}
+
+	// Load priority layers
+	stream.seek(0x14);
+	_priorityLayersCount = stream.readUint16LE();
+	_priorityLayers = new PriorityLayer[_priorityLayersCount];
+	stream.seek(0x34);
+	uint32 priorityLayersOffs = stream.readUint32LE();
+	debug("_priorityLayersCount: %d", _priorityLayersCount);
+	for (uint i = 0; i < _priorityLayersCount; ++i) {
+		stream.seek(priorityLayersOffs + i * 12);
+		_priorityLayers[i].load(data, stream);
+	}
+
+	// Load region layers
+	stream.seek(0x16);
+	_regionLayersCount = stream.readUint16LE();
+	_regionLayers = new RegionLayer[_regionLayersCount];
+	stream.seek(0x38);
+	uint32 regionLayersOffs = stream.readUint32LE();
+	debug("_regionLayersCount: %d", _regionLayersCount);
+	for (uint i = 0; i < _regionLayersCount; ++i) {
+		stream.seek(regionLayersOffs + i * 20);
+		_regionLayers[i].load(data, stream);
+	}
+
+	// Load region sequences
+	stream.seek(0x1E);
+	_regionSequencesCount = stream.readUint16LE();
+	_regionSequences = new Sequence[_regionSequencesCount];
+	stream.seek(0x48);
+	uint32 regionSequencesOffs = stream.readUint32LE();
+	stream.seek(regionSequencesOffs);
+	for (uint i = 0; i < _regionSequencesCount; ++i)
+		_regionSequences[i].load(data, stream);
+
+	// Load background objects
+	stream.seek(0x1C);
+	_backgroundObjectsCount = stream.readUint16LE();
+	_backgroundObjects = new BackgroundObject[_backgroundObjectsCount];
+	stream.seek(0x44);
+	uint32 backgroundObjectsOffs = stream.readUint32LE();
+	debug(0, "_backgroundObjectsCount: %d", _backgroundObjectsCount);
+	for (uint i = 0; i < _backgroundObjectsCount; ++i) {
+		stream.seek(backgroundObjectsOffs + i * 12);
+		_backgroundObjects[i].load(data, stream);
+	}
+	
+	// Load path walk points
+	stream.seek(0x0E);
+	_pathWalkPointsCount = stream.readUint16LE();
+	debug("_pathWalkPointsCount: %d", _pathWalkPointsCount);
+	_pathWalkPoints = new PathWalkPoints[_pathWalkPointsCount];
+	stream.seek(0x28);
+	uint32 pathWalkPointsOffs = stream.readUint32LE();
+	for (uint i = 0; i < _pathWalkPointsCount; ++i) {
+		stream.seek(pathWalkPointsOffs + i * 8);
+		_pathWalkPoints[i].load(data, stream);
+	}
+
+	// Load path walk rects
+	stream.seek(0x12);
+	_pathWalkRectsCount = stream.readUint16LE();
+	debug("_pathWalkRectsCount: %d", _pathWalkRectsCount);
+	_pathWalkRects = new PathWalkRects[_pathWalkRectsCount];
+	stream.seek(0x30);
+	uint32 pathWalkRectsOffs = stream.readUint32LE();
+	for (uint i = 0; i < _pathWalkRectsCount; ++i) {
+		stream.seek(pathWalkRectsOffs + i * 8);
+		_pathWalkRects[i].load(data, stream);
+	}
+
+	// Load named points
+	stream.seek(0xC);
+	uint namedPointsCount = stream.readUint16LE();
+	stream.seek(0x24);
+	uint32 namedPointsOffs = stream.readUint32LE();
+	stream.seek(namedPointsOffs);
+	_namedPoints.load(namedPointsCount, stream);
+
+	// Load palettes
+	stream.seek(0x18);
+	_palettesCount = stream.readUint16LE();
+	_palettes = new Palette[_palettesCount];
+	stream.seek(0x3C);
+	uint32 palettesOffs = stream.readUint32LE();
+	debug(0, "_palettesCount: %d", _palettesCount);
+	for (uint i = 0; i < _palettesCount; ++i) {
+		stream.seek(palettesOffs + i * 8);
+		_palettes[i].load(data, stream);
+	}
+
+}
+
+int BackgroundResource::findMasterBgIndex() {
+	int index = 1;
+	while (!_bgInfos[index - 1]._flags & 1)
+		++index;
+	return index;
+}
+
+PriorityLayer *BackgroundResource::getPriorityLayer(uint index) {
+	return &_priorityLayers[index];
+}
+
+ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
+	return &_scaleLayers[index];
+}
+
+RegionLayer *BackgroundResource::getRegionLayer(uint index) {
+	return &_regionLayers[index];
+}
+
+PathWalkPoints *BackgroundResource::getPathWalkPoints(uint index) {
+	return &_pathWalkPoints[index];
+}
+
+PathWalkRects *BackgroundResource::getPathWalkRects(uint index) {
+	return &_pathWalkRects[index];
+}
+
+Palette *BackgroundResource::getPalette(uint index) {
+	return &_palettes[index];
+}
+
+bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	return _namedPoints.findNamedPoint(namedPointId, pt);
+}
+
+// BackgroundInstance
+
+BackgroundInstance::BackgroundInstance(IllusionsEngine *vm)
+	: _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
+}
+
+void BackgroundInstance::load(Resource *resource) {
+	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
+
+	BackgroundResource *backgroundResource = new BackgroundResource();
+	backgroundResource->load(resource->_data, resource->_dataSize);
+
+	_bgRes = backgroundResource;
+	_tag = resource->_tag;
+	initSurface();
+	
+	// Insert background objects
+	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
+		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
+		
+	registerResources();
+
+	// TODO camera_fadeClear();
+	int index = _bgRes->findMasterBgIndex();
+	_vm->_camera->set(_bgRes->_bgInfos[index - 1]._panPoint, _bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
+
+	if (_bgRes->_palettesCount > 0) {
+		Palette *palette = _bgRes->getPalette(_bgRes->_paletteIndex - 1);
+		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
+	}
+
+}
+
+void BackgroundInstance::unload() {
+	debug("BackgroundInstance::unload()");
+	freeSurface();
+	unregisterResources();
+	delete _bgRes;
+	_vm->_backgroundInstances->removeBackgroundInstance(this);
+	_vm->setDefaultTextCoords();
+}
+
+void BackgroundInstance::pause() {
+	++_pauseCtr;
+	if (_pauseCtr <= 1) {
+		unregisterResources();
+		_vm->setDefaultTextCoords();
+		_vm->_camera->getActiveState(_savedCameraState);
+		_savedPalette = new byte[1024];
+		_vm->_screen->getPalette(_savedPalette);
+		freeSurface();
+	}
+}
+
+void BackgroundInstance::unpause() {
+	--_pauseCtr;
+	if (_pauseCtr <= 0) {
+		registerResources();
+		initSurface();
+		_vm->_screen->setPalette(_savedPalette, 1, 256);
+		delete[] _savedPalette;
+		_savedPalette = 0;
+		// TODO _vm->_screen->_fadeClear();
+		_vm->_camera->setActiveState(_savedCameraState);
+		_vm->_backgroundInstances->refreshPan();
+	}
+}
+
+void BackgroundInstance::registerResources() {
+	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &_bgRes->_regionSequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+	}
+}
+	
+void BackgroundInstance::unregisterResources() {
+	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
+		Sequence *sequence = &_bgRes->_regionSequences[i];
+		_vm->_dict->removeSequence(sequence->_sequenceId);
+	}
+}	
+
+void BackgroundInstance::initSurface() {
+	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
+		_surfaces[i] = 0;
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
+		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
+		_panPoints[i] = bgInfo->_panPoint;
+		_surfaces[i] = _vm->_screen->allocSurface(bgInfo->_surfInfo);
+		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
+	}
+}
+
+void BackgroundInstance::freeSurface() {
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i)
+		if (_surfaces[i]) {
+			_surfaces[i]->free();
+			delete _surfaces[i];
+			_surfaces[i] = 0;
+		}
+}
+
+void BackgroundInstance::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	switch (_vm->getGameId()) {
+	case kGameIdDuckman:
+		drawTiles8(surface, tileMap, tilePixels);
+		break;
+	case kGameIdBBDOU:
+		drawTiles16(surface, tileMap, tilePixels);
+		break;
+	}
+}
+
+void BackgroundInstance::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	const int kTileWidth = 32;
+	const int kTileHeight = 8;
+	const int kTileSize = kTileWidth * kTileHeight;
+	uint tileMapIndex = 0;
+	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
+		int tileDestY = tileY * kTileHeight;
+		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
+		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
+			int tileDestX = tileX * kTileWidth;
+			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
+			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
+			++tileMapIndex;
+			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
+			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
+			for (int h = 0; h < tileDestH; ++h) {
+				memcpy(dst, src, tileDestW);
+				dst += surface->pitch;
+				src += kTileWidth;
+			}
+		}
+	}
+}
+
+void BackgroundInstance::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
+	const int kTileWidth = 32;
+	const int kTileHeight = 8;
+	const int kTileSize = kTileWidth * kTileHeight * 2;
+	uint tileMapIndex = 0;
+	for (int tileY = 0; tileY < tileMap._height; ++tileY) {
+		int tileDestY = tileY * kTileHeight;
+		int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
+		for (int tileX = 0; tileX < tileMap._width; ++tileX) {
+			int tileDestX = tileX * kTileWidth;
+			int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
+			uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
+			++tileMapIndex;
+			byte *src = tilePixels + (tileIndex - 1) * kTileSize;
+			byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
+			for (int h = 0; h < tileDestH; ++h) {
+				for (int w = 0; w < tileDestW; ++w) {
+					uint16 pixel = READ_LE_UINT16(src + w * 2);
+					WRITE_LE_UINT16(dst + w * 2, pixel);
+				}
+				dst += surface->pitch;
+				src += kTileWidth * 2;
+			}
+		}
+	}
+}
+
+// BackgroundInstanceList
+
+BackgroundInstanceList::BackgroundInstanceList(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+BackgroundInstanceList::~BackgroundInstanceList() {
+}
+
+BackgroundInstance *BackgroundInstanceList::createBackgroundInstance(Resource *resource) {
+	BackgroundInstance *backgroundInstance = new BackgroundInstance(_vm);
+	backgroundInstance->load(resource);
+	_items.push_back(backgroundInstance);
+	return backgroundInstance;
+}
+
+void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgroundInstance) {
+	_items.remove(backgroundInstance);
+}
+
+void BackgroundInstanceList::pauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->pause();
+}
+
+void BackgroundInstanceList::unpauseByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			(*it)->unpause();
+}
+
+BackgroundInstance *BackgroundInstanceList::findActiveBackgroundInstance() {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_pauseCtr == 0)
+			return (*it);
+	return 0;
+}
+
+BackgroundInstance *BackgroundInstanceList::findBackgroundByResource(BackgroundResource *backgroundResource) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_bgRes == backgroundResource)
+			return (*it);
+	return 0;
+}
+
+BackgroundResource *BackgroundInstanceList::getActiveBgResource() {
+	BackgroundInstance *background = findActiveBackgroundInstance();
+	if (background)
+		return background->_bgRes;
+	return 0;
+}
+
+WidthHeight BackgroundInstanceList::getMasterBgDimensions() {
+	BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
+	int16 index = backgroundInstance->_bgRes->findMasterBgIndex();
+	return backgroundInstance->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
+}
+
+void BackgroundInstanceList::refreshPan() {
+	BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
+	if (backgroundInstance) {
+		WidthHeight dimensions = getMasterBgDimensions();
+		_vm->_camera->refreshPan(backgroundInstance, dimensions);
+	}
+}
+
+bool BackgroundInstanceList::findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt) {
+	BackgroundResource *backgroundResource = getActiveBgResource();
+	return backgroundResource ? backgroundResource->findNamedPoint(namedPointId, pt) : false;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/backgroundresource.h b/engines/illusions/resources/backgroundresource.h
new file mode 100644
index 0000000..9438501
--- /dev/null
+++ b/engines/illusions/resources/backgroundresource.h
@@ -0,0 +1,234 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BACKGROUNDRESOURCE_H
+#define ILLUSIONS_BACKGROUNDRESOURCE_H
+
+#include "illusions/camera.h"
+#include "illusions/graphics.h"
+#include "illusions/pathfinder.h"
+#include "illusions/resourcesystem.h"
+#include "graphics/surface.h"
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/list.h"
+#include "common/memstream.h"
+#include "common/rect.h"
+#include "common/substream.h"
+#include "common/system.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+struct Sequence;
+
+class BackgroundResourceLoader : public BaseResourceLoader {
+public:
+	BackgroundResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~BackgroundResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+struct TileMap {
+	int16 _width, _height;
+	//field_4 dd
+	byte *_map;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct BgInfo {
+	uint32 _flags;
+	//field_4 dw
+	int16 _priorityBase;
+	SurfInfo _surfInfo;
+	Common::Point _panPoint;
+	TileMap _tileMap;
+	byte *_tilePixels;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class PriorityLayer {
+public:
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	int getPriority(Common::Point pos);
+protected:
+	int16 _width, _height;
+	int16 _mapWidth, _mapHeight;
+	byte *_map, *_values;
+};
+
+class ScaleLayer {
+public:
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	int getScale(Common::Point pos);
+protected:
+	int16 _height;
+	byte *_values;
+};
+
+class RegionLayer {
+public:
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	int getRegionIndex(Common::Point pos);
+	uint32 getRegionSequenceId(int regionIndex);
+protected:
+	uint32 _unk;
+	byte *_regionSequenceIds;
+	int16 _width, _height;
+	int16 _mapWidth, _mapHeight;
+	byte *_map, *_values;
+};
+
+struct Palette {
+	uint16 _count;
+	uint16 _unk;
+	byte *_palette;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct BackgroundObject {
+	uint32 _objectId;
+	uint16 _flags;
+	int16 _priority;
+	byte *_pointsConfig;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct PathWalkPoints {
+	PointArray *_points;
+	PathWalkPoints() : _points(0) {}
+	~PathWalkPoints() { delete _points; }
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct PathWalkRects {
+	PathLines *_rects;
+	PathWalkRects() : _rects(0) {}
+	~PathWalkRects() { delete _rects; }
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class BackgroundResource {
+public:
+	BackgroundResource();
+	~BackgroundResource();
+	void load(byte *data, uint32 dataSize);
+	int findMasterBgIndex();
+	PriorityLayer *getPriorityLayer(uint index);
+	ScaleLayer *getScaleLayer(uint index);
+	RegionLayer *getRegionLayer(uint index);
+	PathWalkPoints *getPathWalkPoints(uint index);
+	PathWalkRects *getPathWalkRects(uint index);
+	Palette *getPalette(uint index);
+	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
+public:
+
+	uint _paletteIndex;
+
+	uint _bgInfosCount;
+	BgInfo *_bgInfos;
+	
+	uint _priorityLayersCount;
+	PriorityLayer *_priorityLayers;
+
+	uint _scaleLayersCount;
+	ScaleLayer *_scaleLayers;
+
+	uint _regionLayersCount;
+	RegionLayer *_regionLayers;
+
+	uint _regionSequencesCount;
+	Sequence *_regionSequences;
+
+	uint _backgroundObjectsCount;
+	BackgroundObject *_backgroundObjects;
+
+	uint _pathWalkPointsCount;
+	PathWalkPoints *_pathWalkPoints;
+
+	uint _pathWalkRectsCount;
+	PathWalkRects *_pathWalkRects;
+
+	NamedPoints _namedPoints;
+	
+	uint _palettesCount;
+	Palette *_palettes;
+
+};
+
+const uint kMaxBackgroundItemSurfaces = 3;
+
+class BackgroundInstance : public ResourceInstance {
+public:
+	BackgroundInstance(IllusionsEngine *vm);
+	virtual void load(Resource *resource);
+	virtual void unload();
+	virtual void pause();
+	virtual void unpause();
+public:
+	IllusionsEngine *_vm;
+	uint32 _tag;
+	int _pauseCtr;
+	BackgroundResource *_bgRes;
+	Common::Point _panPoints[kMaxBackgroundItemSurfaces];
+	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
+	CameraState _savedCameraState;
+	byte *_savedPalette;
+	void registerResources();	
+	void unregisterResources();	
+	void initSurface();
+	void freeSurface();
+	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
+	void drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
+	void drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
+};
+
+class BackgroundInstanceList {
+public:
+	BackgroundInstanceList(IllusionsEngine *vm);
+	~BackgroundInstanceList();
+	BackgroundInstance *createBackgroundInstance(Resource *resource);
+	void removeBackgroundInstance(BackgroundInstance *backgroundInstance);
+	void pauseByTag(uint32 tag);
+	void unpauseByTag(uint32 tag);
+	BackgroundInstance *findActiveBackgroundInstance();
+	BackgroundInstance *findBackgroundByResource(BackgroundResource *backgroundResource);
+	BackgroundResource *getActiveBgResource();
+	WidthHeight getMasterBgDimensions();
+	void refreshPan();
+	bool findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt);
+//protected:
+public:
+	typedef Common::List<BackgroundInstance*> Items;
+	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
+	Items _items;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BACKGROUNDRESOURCE_H
diff --git a/engines/illusions/resources/fontresource.cpp b/engines/illusions/resources/fontresource.cpp
new file mode 100644
index 0000000..becc6cf
--- /dev/null
+++ b/engines/illusions/resources/fontresource.cpp
@@ -0,0 +1,134 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/dictionary.h"
+
+namespace Illusions {
+
+// FontResourceLoader
+
+void FontResourceLoader::load(Resource *resource) {
+	FontInstance *fontInstance = new FontInstance(_vm);
+	fontInstance->load(resource);
+	resource->_instance = fontInstance;
+}
+
+void FontResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
+}
+
+bool FontResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+// CharInfo
+
+void CharInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_width = stream.readUint16LE();
+	_field_2 = stream.readUint16LE();
+	uint32 pixelsOffs = stream.readUint32LE();
+	_pixels = dataStart + pixelsOffs;
+	debug(2, "CharInfo::load() _width: %d; _field_2: %d; pixelsOffs: %08X",
+		_width, _field_2, pixelsOffs);
+}
+
+// CharRange
+
+void CharRange::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_firstChar = stream.readUint16LE();
+	_lastChar = stream.readUint16LE();
+	uint count = _lastChar - _firstChar + 1;
+	uint32 charInfosOffs = stream.readUint32LE();
+	_charInfos = new CharInfo[count];
+	for (uint i = 0; i < count; ++i) {
+		stream.seek(charInfosOffs + i * 8);
+		_charInfos[i].load(dataStart, stream);
+	}
+	debug(2, "CharRange::load() _firstChar: %d; _lastChar: %d; charInfosOffs: %08X",
+		_firstChar, _lastChar, charInfosOffs);
+}
+
+CharInfo *CharRange::getCharInfo(uint16 c) {
+	return &_charInfos[c - _firstChar];
+}
+
+bool CharRange::containsChar(uint16 c) {
+	return c >= _firstChar && c <= _lastChar;
+}
+
+// FontResource
+
+FontResource::FontResource() {
+}
+
+FontResource::~FontResource() {
+}
+
+void FontResource::load(Resource *resource) {
+	byte *data = resource->_data;
+	uint32 dataSize = resource->_dataSize;
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+	_totalSize = stream.readUint32LE();
+	_charHeight = stream.readUint16LE();
+	_field_6 = stream.readUint16LE();
+	_colorIndex = stream.readUint16LE();
+	_lineIncr = stream.readUint16LE();
+	_widthC = stream.readUint16LE();
+	_charRangesCount = stream.readUint16LE();
+	uint32 charRangesOffs = stream.pos();
+	_charRanges = new CharRange[_charRangesCount];
+	for (uint i = 0; i < _charRangesCount; ++i) {
+		stream.seek(charRangesOffs + i * 8);
+		_charRanges[i].load(data, stream);
+	}
+	debug(2, "FontResource::load() _charHeight: %d; _field_6: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
+		_charHeight, _field_6, _colorIndex, _lineIncr, _widthC, _charRangesCount);
+}
+
+CharInfo *FontResource::getCharInfo(uint16 c) {
+	for (uint i = 0; i < _charRangesCount; ++i)
+		if (_charRanges[i].containsChar(c))
+			return _charRanges[i].getCharInfo(c);
+	return 0;
+}
+
+// FontInstance
+
+FontInstance::FontInstance(IllusionsEngine *vm) : _vm(vm) {
+}
+
+void FontInstance::load(Resource *resource) {
+	_fontResource = new FontResource();
+	_fontResource->load(resource);
+	_resId = resource->_resId;
+	_vm->_dict->addFont(resource->_resId, _fontResource);
+}
+
+void FontInstance::unload() {
+	delete _fontResource;
+	_vm->_dict->removeFont(_resId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
new file mode 100644
index 0000000..8ea059f
--- /dev/null
+++ b/engines/illusions/resources/fontresource.h
@@ -0,0 +1,94 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_FONTRESOURCE_H
+#define ILLUSIONS_FONTRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class FontResourceLoader : public BaseResourceLoader {
+public:
+	FontResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~FontResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+// TODO
+
+struct CharInfo {
+	int16 _width;
+	int16 _field_2;
+	byte *_pixels;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+struct CharRange {
+	uint16 _firstChar;
+	uint16 _lastChar;
+	CharInfo *_charInfos;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	CharInfo *getCharInfo(uint16 c);
+	bool containsChar(uint16 c);
+};
+
+class FontResource {
+public:
+	FontResource();
+	~FontResource();
+	void load(Resource *resource);
+	CharInfo *getCharInfo(uint16 c);
+	int16 getColorIndex() const { return _colorIndex; }
+public:
+	uint32 _totalSize;
+	int16 _charHeight;
+	int16 _field_6;
+	int16 _colorIndex;
+	int16 _lineIncr;
+	int16 _widthC;
+	uint _charRangesCount;
+	CharRange *_charRanges;
+	CharRange *getCharRange(uint16 c);
+};
+
+class FontInstance : public ResourceInstance {
+public:
+	FontInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+	FontResource *_fontResource;
+	uint32 _resId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_FONTRESOURCE_H
diff --git a/engines/illusions/resources/midiresource.cpp b/engines/illusions/resources/midiresource.cpp
new file mode 100644
index 0000000..a3aeef7
--- /dev/null
+++ b/engines/illusions/resources/midiresource.cpp
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/midiresource.h"
+
+namespace Illusions {
+
+// MidiGroupResourceLoader
+
+void MidiGroupResourceLoader::load(Resource *resource) {
+	debug("MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
+
+    // TODO
+	
+}
+
+void MidiGroupResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
+}
+
+bool MidiGroupResourceLoader::isFlag(int flag) {
+	return false;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/midiresource.h b/engines/illusions/resources/midiresource.h
new file mode 100644
index 0000000..9032e99
--- /dev/null
+++ b/engines/illusions/resources/midiresource.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_MIDIRESOURCE_H
+#define ILLUSIONS_MIDIRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class MidiGroupResourceLoader : public BaseResourceLoader {
+public:
+	MidiGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~MidiGroupResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
new file mode 100644
index 0000000..a29fd78
--- /dev/null
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -0,0 +1,380 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/scriptresource.h"
+
+namespace Illusions {
+
+// ScriptResourceLoader
+
+void ScriptResourceLoader::load(Resource *resource) {
+	ScriptInstance *scriptInstance = new ScriptInstance(_vm);
+	scriptInstance->load(resource);
+	resource->_instance = scriptInstance;
+}
+
+void ScriptResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.scr", resource->_resId);
+}
+
+bool ScriptResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+// Properties
+
+Properties::Properties()
+	: _count(0), _properties(0) {
+}
+
+void Properties::init(uint count, byte *properties) {
+	_count = count;
+	_properties = properties;
+}
+
+void Properties::clear() {
+	uint size = (_count >> 3) + 1;
+	for (uint i = 0; i < size; ++i)
+		_properties[i] = 0;
+}
+
+bool Properties::get(uint32 propertyId) {
+	uint index;
+	byte mask;
+	getProperyPos(propertyId, index, mask);
+	return (_properties[index] & mask) != 0;
+}
+
+void Properties::set(uint32 propertyId, bool value) {
+	uint index;
+	byte mask;
+	getProperyPos(propertyId, index, mask);
+	if (value)
+		_properties[index] |= mask;
+	else
+		_properties[index] &= ~mask;
+}
+
+void Properties::getProperyPos(uint32 propertyId, uint &index, byte &mask) {
+	propertyId &= 0xFFFF;
+	index = propertyId >> 3;
+	mask = 1 << (propertyId & 7);
+}
+
+// BlockCounters
+
+BlockCounters::BlockCounters()
+	: _count(0), _blockCounters(0) {
+}
+
+void BlockCounters::init(uint count, byte *blockCounters) {
+	_count = count;
+	_blockCounters = blockCounters;
+}
+
+void BlockCounters::clear() {
+	for (uint i = 0; i < _count; ++i)
+		_blockCounters[i] = 0;
+}
+
+byte BlockCounters::get(uint index) {
+	return _blockCounters[index - 1] & 0x3F;
+}
+
+void BlockCounters::set(uint index, byte value) {
+	_blockCounters[index - 1] ^= (_blockCounters[index - 1] ^ value) & 0x3F;
+}
+
+byte BlockCounters::getC0(uint index) {
+	return _blockCounters[index - 1] & 0xC0;
+}
+
+void BlockCounters::setC0(uint index, byte value) {
+	byte oldValue = _blockCounters[index - 1] & 0x3F;
+	if (value & 0x80)
+		value = value & 0xBF;
+	_blockCounters[index - 1] = oldValue | (value & 0xC0);
+}
+
+// TriggerCause
+
+void TriggerCause::load(Common::SeekableReadStream &stream) {
+	_verbId = stream.readUint32LE();
+	_objectId2 = stream.readUint32LE();
+	_codeOffs = stream.readUint32LE();
+	
+	debug(2, "TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
+		_verbId, _objectId2, _codeOffs);
+}
+
+// TriggerObject
+
+TriggerObject::TriggerObject()
+	: _causesCount(0), _causes(0) {
+}
+
+TriggerObject::~TriggerObject() {
+	delete[] _causes;
+}
+
+void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_objectId = stream.readUint32LE();
+	_causesCount = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	debug(2, "TriggerObject::load() _objectId: %08X; _causesCount: %d",
+		_objectId, _causesCount);
+	_causes = new TriggerCause[_causesCount];
+	for (uint i = 0; i < _causesCount; ++i)
+		_causes[i].load(stream);
+}
+
+bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
+	if ((verbId & 0xFFFF0000) == 0) {
+		for (uint i = 0; i < _causesCount; ++i) {
+			if ((verbId == 7 && ((_causes[i]._verbId == 7 && _causes[i]._objectId2 == objectId2) || _causes[i]._verbId == 8)) ||
+				(verbId != 7 && verbId == _causes[i]._verbId)) {
+				codeOffs = _causes[i]._codeOffs;
+				return true;
+			}
+		}
+	} else {
+		for (uint i = 0; i < _causesCount; ++i)
+			if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
+				codeOffs = _causes[i]._codeOffs;
+				return true;
+			}
+	}
+	return false;
+}
+
+void TriggerObject::fixupProgInfosDuckman() {
+	for (uint i = 0; i < _causesCount; ++i)
+		_causes[i]._verbId &= 0xFFFF;
+}
+
+// ProgInfo
+
+ProgInfo::ProgInfo()
+	: _triggerObjectsCount(0), _triggerObjects(0),
+	_resourcesCount(0), _resources(0) {
+}
+
+ProgInfo::~ProgInfo() {
+	delete[] _triggerObjects;
+	delete[] _resources;
+}
+
+char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	while (*wstr != 0) {
+		*p++ = *wstr;
+		wstr += 2;
+	}
+	*p = 0;
+	return buf;
+}
+
+void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_id = stream.readUint16LE();
+	_unk = stream.readUint16LE();
+	_name = dataStart + stream.pos();
+	stream.skip(128);
+	_triggerObjectsCount = stream.readUint16LE();
+	_resourcesCount = stream.readUint16LE();
+	debug(2, "\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
+		_id, _unk, debugW2I(_name));
+	uint32 triggerObjectsListOffs = stream.readUint32LE();
+	if (_resourcesCount > 0) {
+		_resources = new uint32[_resourcesCount];
+		for (uint i = 0; i < _resourcesCount; ++i)
+			_resources[i] = stream.readUint32LE();
+	}
+	if (_triggerObjectsCount > 0) {
+		_triggerObjects = new TriggerObject[_triggerObjectsCount];
+		for (uint i = 0; i < _triggerObjectsCount; ++i) {
+			stream.seek(triggerObjectsListOffs + i * 4);
+			uint32 triggerObjectOffs = stream.readUint32LE();
+			stream.seek(triggerObjectOffs);
+			_triggerObjects[i].load(dataStart, stream);
+		}
+	}
+}
+
+bool ProgInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	TriggerObject *triggerObject = findTriggerObject(objectId);
+	if (triggerObject)
+		return triggerObject->findTriggerCause(verbId, objectId2, codeOffs);
+	return false;
+}
+
+void ProgInfo::getResources(uint &resourcesCount, uint32 *&resources) {
+	resourcesCount = _resourcesCount;
+	resources = _resources;
+}
+
+TriggerObject *ProgInfo::findTriggerObject(uint32 objectId) {
+	for (uint i = 0; i < _triggerObjectsCount; ++i)
+		if (_triggerObjects[i]._objectId == objectId)
+			return &_triggerObjects[i];
+	return 0;
+}
+
+void ProgInfo::fixupProgInfosDuckman() {
+	for (uint i = 0; i < _triggerObjectsCount; ++i)
+		_triggerObjects[i].fixupProgInfosDuckman();
+}
+
+// ScriptResource
+
+ScriptResource::ScriptResource()
+	: _codeOffsets(0), _objectMap(0) {
+}
+
+ScriptResource::~ScriptResource() {
+	delete[] _codeOffsets;
+	delete[] _objectMap;
+}
+
+void ScriptResource::load(Resource *resource) {
+	_data = resource->_data;
+	_dataSize = resource->_dataSize;
+
+	Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
+	
+	uint32 objectMapOffs, progInfosOffs;
+	_objectMapCount = 0;
+	
+	if (resource->_gameId == kGameIdBBDOU) {
+		progInfosOffs = 0x18;
+	} else if (resource->_gameId == kGameIdDuckman) {
+		for (uint i = 0; i < 27; ++i)
+			_soundIds[i] = stream.readUint32LE();
+		progInfosOffs = 0x8C;
+	}
+	
+	stream.skip(4); // Skip unused
+
+	// Read item counts
+	uint propertiesCount = stream.readUint16LE();
+	uint blockCountersCount = stream.readUint16LE();
+	if (resource->_gameId == kGameIdDuckman)
+		_objectMapCount = stream.readUint16LE();
+	_codeCount = stream.readUint16LE();
+	_progInfosCount = stream.readUint16LE();
+	if (resource->_gameId == kGameIdDuckman)
+		stream.readUint16LE();//Unused?
+
+	// Read item offsets
+	uint32 propertiesOffs = stream.readUint32LE();
+	uint32 blockCountersOffs = stream.readUint32LE();
+	if (resource->_gameId == kGameIdDuckman)
+		objectMapOffs = stream.readUint32LE();
+	uint32 codeTblOffs = stream.readUint32LE();
+	
+	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _progInfosCount: %d; _objectMapCount: %d",
+		propertiesCount, blockCountersCount, _codeCount, _progInfosCount, _objectMapCount);
+	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X; objectMapOffs: %08X",
+		propertiesOffs, blockCountersOffs, codeTblOffs, objectMapOffs);
+	// Init properties
+	_properties.init(propertiesCount, _data + propertiesOffs);
+	
+	// Init blockcounters
+	_blockCounters.init(blockCountersCount, _data + blockCountersOffs);
+	
+	_codeOffsets = new uint32[_codeCount];
+	stream.seek(codeTblOffs);
+	for (uint i = 0; i < _codeCount; ++i)
+		_codeOffsets[i] = stream.readUint32LE();
+
+	_progInfos = new ProgInfo[_progInfosCount];
+	for (uint i = 0; i < _progInfosCount; ++i) {
+		stream.seek(progInfosOffs + i * 4);
+		uint32 progInfoOffs = stream.readUint32LE();
+		stream.seek(progInfoOffs);
+		_progInfos[i].load(_data, stream);
+	}
+	
+	if (_objectMapCount > 0) {
+		_objectMap = new uint32[_objectMapCount];
+		stream.seek(objectMapOffs);
+		for (uint i = 0; i < _objectMapCount; ++i) {
+			_objectMap[i] = stream.readUint32LE();
+			stream.skip(4);
+		}
+	}
+	
+	if (resource->_gameId == kGameIdDuckman) {
+		stream.seek(0x6C);
+		_mainActorObjectId = stream.readUint32LE();
+	} else if (resource->_gameId == kGameIdBBDOU) {
+		stream.seek(0);
+		_mainActorObjectId = stream.readUint32LE();
+	}
+	
+	if (resource->_gameId == kGameIdDuckman)
+		fixupProgInfosDuckman();
+
+}
+
+byte *ScriptResource::getThreadCode(uint32 threadId) {
+	return _data + _codeOffsets[(threadId & 0xFFFF) - 1];
+}
+
+byte *ScriptResource::getCode(uint32 codeOffs) {
+	return _data + codeOffs;
+}
+
+ProgInfo *ScriptResource::getProgInfo(uint32 index) {
+	if (index > 0 && index <= _progInfosCount)
+		return &_progInfos[index - 1];
+	return 0;
+}
+
+uint32 ScriptResource::getObjectActorTypeId(uint32 objectId) {
+	return _objectMap[(objectId & 0xFFFF) - 1];
+}
+
+void ScriptResource::fixupProgInfosDuckman() {
+	for (uint i = 0; i < _progInfosCount; ++i)
+		_progInfos[i].fixupProgInfosDuckman();
+}
+
+// ScriptInstance
+
+ScriptInstance::ScriptInstance(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void ScriptInstance::load(Resource *resource) {
+	_vm->_scriptResource = new ScriptResource();
+	_vm->_scriptResource->load(resource);
+}
+
+void ScriptInstance::unload() {
+	delete _vm->_scriptResource;
+	_vm->_scriptResource = 0;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/scriptresource.h b/engines/illusions/resources/scriptresource.h
new file mode 100644
index 0000000..c2119b0
--- /dev/null
+++ b/engines/illusions/resources/scriptresource.h
@@ -0,0 +1,147 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SCRIPTRESOURCE_H
+#define ILLUSIONS_SCRIPTRESOURCE_H
+
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class ScriptResourceLoader : public BaseResourceLoader {
+public:
+	ScriptResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~ScriptResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+class Properties {
+public:
+	Properties();
+	void init(uint count, byte *properties);
+	void clear();
+	bool get(uint32 propertyId);
+	void set(uint32 propertyId, bool value);
+public:
+	uint _count;
+	byte *_properties;
+	void getProperyPos(uint32 propertyId, uint &index, byte &mask);
+};
+
+class BlockCounters {
+public:
+	BlockCounters();
+	void init(uint count, byte *blockCounters);
+	void clear();
+	byte get(uint index);
+	void set(uint index, byte value);
+	byte getC0(uint index);
+	void setC0(uint index, byte value);
+public:
+	uint _count;
+	byte *_blockCounters;
+};
+
+struct TriggerCause {
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _codeOffs;
+	void load(Common::SeekableReadStream &stream);
+};
+
+class TriggerObject {
+public:
+	TriggerObject();
+	~TriggerObject();
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs);
+	void fixupProgInfosDuckman();
+public:
+	uint32 _objectId;
+	uint _causesCount;
+	TriggerCause *_causes;
+};
+
+class ProgInfo {
+public:
+	ProgInfo();
+	~ProgInfo();
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
+	void getResources(uint &resourcesCount, uint32 *&resources);
+	void fixupProgInfosDuckman();
+protected:
+	uint16 _id;
+	uint16 _unk;
+	byte *_name;
+	uint _triggerObjectsCount;
+	TriggerObject *_triggerObjects;
+	uint _resourcesCount;
+	uint32 *_resources;
+	TriggerObject *findTriggerObject(uint32 objectId);
+};
+
+class ScriptResource {
+public:
+	ScriptResource();
+	~ScriptResource();
+	void load(Resource *resource);
+	byte *getThreadCode(uint32 threadId);
+	byte *getCode(uint32 codeOffs);
+	ProgInfo *getProgInfo(uint32 index);
+	uint32 getObjectActorTypeId(uint32 objectId);
+	uint32 getMainActorObjectId() const { return _mainActorObjectId; }
+public:
+	byte *_data;
+	uint32 _dataSize;
+	Properties _properties;
+	BlockCounters _blockCounters;
+	uint _codeCount;
+	uint32 *_codeOffsets;
+	uint _progInfosCount;
+	ProgInfo *_progInfos;
+	// Duckman specific
+	uint32 _soundIds[27];
+	uint _objectMapCount;
+	uint32 *_objectMap;
+	uint32 _mainActorObjectId;
+	void fixupProgInfosDuckman();
+};
+
+class ScriptInstance : public ResourceInstance {
+public:
+	ScriptInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ACTORRESOURCE_H
diff --git a/engines/illusions/resources/soundresource.cpp b/engines/illusions/resources/soundresource.cpp
new file mode 100644
index 0000000..35017e3
--- /dev/null
+++ b/engines/illusions/resources/soundresource.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/sound.h"
+
+namespace Illusions {
+
+// SoundGroupResourceLoader
+
+void SoundGroupResourceLoader::load(Resource *resource) {
+	SoundGroupInstance *soundGroupInstance = new SoundGroupInstance(_vm);
+	soundGroupInstance->load(resource);
+	resource->_instance = soundGroupInstance;
+}
+
+void SoundGroupResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.sg", resource->_resId);
+}
+
+bool SoundGroupResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile/* ||
+		flag == kRlfFreeDataAfterLoad*/;
+}
+
+// SoundEffect
+
+void SoundEffect::load(Common::SeekableReadStream &stream) {
+	_soundEffectId = stream.readUint32LE();
+	_looping = stream.readUint16LE() != 0;
+	_field6 = stream.readUint16LE();
+	_volume = stream.readUint16LE();
+	_frequency = stream.readUint16LE();
+	stream.skip(32 + 4); // Skip name
+	debug("SoundEffect::load() _soundEffectId: %08X, _looping: %d, _field6: %d, _volume: %d, _frequency: %d",
+		_soundEffectId, _looping, _field6, _volume, _frequency);
+}
+
+// SoundGroupResource
+
+SoundGroupResource::SoundGroupResource()
+	: _soundEffects(0) {
+}
+
+SoundGroupResource::~SoundGroupResource() {
+	delete[] _soundEffects;
+}
+
+void SoundGroupResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	stream.skip(4);
+	_soundEffectsCount = stream.readUint16LE();
+	stream.skip(2);
+	uint32 soundEffectsOffs = stream.readUint32LE();
+	debug("_soundEffectsCount: %d; soundEffectsOffs: %08X", _soundEffectsCount, soundEffectsOffs);
+	_soundEffects = new SoundEffect[_soundEffectsCount];
+	stream.seek(soundEffectsOffs);
+	for (uint i = 0; i < _soundEffectsCount; ++i)
+		_soundEffects[i].load(stream);
+
+}
+
+// SoundGroupInstance
+
+SoundGroupInstance::SoundGroupInstance(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+void SoundGroupInstance::load(Resource *resource) {
+	_soundGroupResource = new SoundGroupResource();
+	_soundGroupResource->load(resource->_data, resource->_dataSize);
+	for (uint i = 0; i < _soundGroupResource->_soundEffectsCount; ++i) {
+		SoundEffect *soundEffect = &_soundGroupResource->_soundEffects[i];
+		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
+	}
+	_resId = resource->_resId;
+}
+
+void SoundGroupInstance::unload() {
+	_vm->_soundMan->unloadSounds(_resId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/soundresource.h b/engines/illusions/resources/soundresource.h
new file mode 100644
index 0000000..638e8df
--- /dev/null
+++ b/engines/illusions/resources/soundresource.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_SOUNDRESOURCE_H
+#define ILLUSIONS_SOUNDRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class SoundGroupResourceLoader : public BaseResourceLoader {
+public:
+	SoundGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~SoundGroupResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+struct SoundEffect {
+	uint32 _soundEffectId;
+	bool _looping;
+	int16 _field6;
+	int16 _volume;
+	int16 _frequency;
+	void load(Common::SeekableReadStream &stream);
+};
+
+class SoundGroupResource {
+public:
+	SoundGroupResource();
+	~SoundGroupResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	uint _soundEffectsCount;
+	SoundEffect *_soundEffects;
+};
+
+class SoundGroupInstance : public ResourceInstance {
+public:
+	SoundGroupInstance(IllusionsEngine *vm);              
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+	SoundGroupResource *_soundGroupResource;
+	uint32 _resId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/resources/talkresource.cpp b/engines/illusions/resources/talkresource.cpp
new file mode 100644
index 0000000..57392c7
--- /dev/null
+++ b/engines/illusions/resources/talkresource.cpp
@@ -0,0 +1,174 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/talkresource.h"
+#include "illusions/dictionary.h"
+
+namespace Illusions {
+
+// TalkResourceLoader
+
+void TalkResourceLoader::load(Resource *resource) {
+	resource->_instance = _vm->_talkItems->createTalkInstance(resource);
+}
+
+void TalkResourceLoader::buildFilename(Resource *resource) {
+	resource->_filename = Common::String::format("%08X.tlk", resource->_resId);
+}
+
+bool TalkResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+// TalkEntry
+
+void TalkEntry::load(byte *dataStart, Common::SeekableReadStream &stream) {
+	_talkId = stream.readUint32LE();
+	stream.readUint32LE(); // Skip unknown
+	uint32 textOffs = stream.readUint32LE();
+	uint32 tblOffs = stream.readUint32LE();
+	uint32 voiceNameOffs = stream.readUint32LE();
+	_text = dataStart + textOffs;
+	_tblPtr = dataStart + tblOffs;
+	_voiceName = dataStart + voiceNameOffs;
+	debug(0, "TalkEntry::load() _talkId: %08X; textOffs: %08X; tblOffs: %08X; voiceNameOffs: %08X",
+		_talkId, textOffs, tblOffs, voiceNameOffs);
+}
+
+// TalkResource
+
+TalkResource::TalkResource()
+	: _talkEntriesCount(0), _talkEntries(0) {
+}
+
+TalkResource::~TalkResource() {
+	delete[] _talkEntries;
+}
+
+void TalkResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+	stream.skip(4); // Skip size
+	_talkEntriesCount = stream.readUint16LE();
+	stream.skip(2); // Skip padding
+	_talkEntries = new TalkEntry[_talkEntriesCount];
+	for (uint i = 0; i < _talkEntriesCount; ++i) {
+		stream.seek(8 + i * 0x14);
+		_talkEntries[i].load(data, stream);
+	}
+}
+
+// TalkInstance
+
+TalkInstance::TalkInstance(IllusionsEngine *vm)
+	: _vm(vm), _pauseCtr(0) {
+}
+
+void TalkInstance::load(Resource *resource) {
+	TalkResource *talkResource = new TalkResource();
+	talkResource->load(resource->_data, resource->_dataSize);
+	_talkRes = talkResource;
+	_talkId = resource->_resId;
+	_tag = resource->_tag;
+	registerResources();
+}
+
+void TalkInstance::unload() {
+	unregisterResources();
+	_vm->_talkItems->removeTalkInstance(this);
+	delete _talkRes;
+}
+
+void TalkInstance::pause() {
+	++_pauseCtr;
+	if (_pauseCtr == 1)
+		unregisterResources();
+}
+
+void TalkInstance::unpause() {
+	--_pauseCtr;
+	if (_pauseCtr == 0)
+		registerResources();
+}
+
+void TalkInstance::registerResources() {
+	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
+		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
+	}
+}
+
+void TalkInstance::unregisterResources() {
+	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
+		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
+		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
+	}
+}
+
+// TalkInstanceList
+
+TalkInstanceList::TalkInstanceList(IllusionsEngine *vm)
+	: _vm(vm) {
+}
+
+TalkInstanceList::~TalkInstanceList() {
+}
+
+TalkInstance *TalkInstanceList::createTalkInstance(Resource *resource) {
+	TalkInstance *talkInstance = new TalkInstance(_vm);
+	talkInstance->load(resource);
+	_items.push_back(talkInstance);
+	return talkInstance;
+}
+
+void TalkInstanceList::removeTalkInstance(TalkInstance *talkInstance) {
+	_items.remove(talkInstance);
+}
+
+TalkInstance *TalkInstanceList::findTalkItem(uint32 talkId) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_talkId == talkId)
+			return (*it);
+	return 0;
+}
+
+TalkInstance *TalkInstanceList::findTalkItemByTag(uint32 tag) {
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+		if ((*it)->_tag == tag)
+			return (*it);
+	return 0;
+}
+
+void TalkInstanceList::pauseByTag(uint32 tag) {
+	TalkInstance *talkInstance = findTalkItemByTag(tag);
+	if (talkInstance)
+		talkInstance->pause();
+}
+
+void TalkInstanceList::unpauseByTag(uint32 tag) {
+	TalkInstance *talkInstance = findTalkItemByTag(tag);
+	if (talkInstance)
+		talkInstance->unpause();
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/talkresource.h b/engines/illusions/resources/talkresource.h
new file mode 100644
index 0000000..5e3797e
--- /dev/null
+++ b/engines/illusions/resources/talkresource.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_TALKRESOURCE_H
+#define ILLUSIONS_TALKRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class TalkResourceLoader : public BaseResourceLoader {
+public:
+	TalkResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~TalkResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual void buildFilename(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+struct TalkEntry {
+	uint32 _talkId;
+	//field_4 dd
+	byte *_text;
+	byte *_tblPtr;
+	byte *_voiceName;
+	void load(byte *dataStart, Common::SeekableReadStream &stream);
+};
+
+class TalkResource {
+public:
+	TalkResource();
+	~TalkResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	uint _talkEntriesCount;
+	TalkEntry *_talkEntries;
+};
+
+class TalkInstance : public ResourceInstance {
+public:
+	TalkInstance(IllusionsEngine *vm);
+	virtual void load(Resource *resource);
+	virtual void unload();
+	virtual void pause();
+	virtual void unpause();
+public:
+	IllusionsEngine *_vm;
+	uint32 _talkId;
+	uint32 _tag;
+	TalkResource *_talkRes;
+	int _pauseCtr;
+	void registerResources();
+	void unregisterResources();
+};
+
+class TalkInstanceList {
+public:
+	TalkInstanceList(IllusionsEngine *vm);
+	~TalkInstanceList();
+	TalkInstance *createTalkInstance(Resource *resource);
+	void removeTalkInstance(TalkInstance *talkInstance);
+	TalkInstance *findTalkItem(uint32 talkId);
+	TalkInstance *findTalkItemByTag(uint32 tag);
+	void pauseByTag(uint32 tag);
+	void unpauseByTag(uint32 tag);
+//protected:
+public:
+	typedef Common::List<TalkInstance*> Items;
+	typedef Items::iterator ItemsIterator;
+	IllusionsEngine *_vm;
+	Items _items;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_TALKRESOURCE_H
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 076906e..9c603a8 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -22,7 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/screen.h"
-#include "illusions/fontresource.h"
+#include "illusions/resources/fontresource.h"
 #include "engines/util.h"
 #include "graphics/palette.h"
 
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 37c52c0..018decb 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -23,7 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/screentext.h"
 #include "illusions/dictionary.h"
-#include "illusions/fontresource.h"
+#include "illusions/resources/fontresource.h"
 #include "illusions/screen.h"
 #include "illusions/textdrawer.h"
 #include "engines/util.h"
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index 03990c8..10c284d 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -26,12 +26,12 @@
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/screen.h"
 #include "illusions/scriptstack.h"
-#include "illusions/scriptresource.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
-#include "illusions/talkresource.h"
 #include "illusions/threads/scriptthread.h"
 
 namespace Illusions {
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 076a400..0fb65cc 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -26,12 +26,12 @@
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/screen.h"
 #include "illusions/scriptstack.h"
-#include "illusions/scriptresource.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
-#include "illusions/talkresource.h"
 #include "illusions/threads/scriptthread.h"
 
 namespace Illusions {
diff --git a/engines/illusions/scriptresource.cpp b/engines/illusions/scriptresource.cpp
deleted file mode 100644
index 775ca73..0000000
--- a/engines/illusions/scriptresource.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/scriptresource.h"
-
-namespace Illusions {
-
-// ScriptResourceLoader
-
-void ScriptResourceLoader::load(Resource *resource) {
-	ScriptInstance *scriptInstance = new ScriptInstance(_vm);
-	scriptInstance->load(resource);
-	resource->_instance = scriptInstance;
-}
-
-void ScriptResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.scr", resource->_resId);
-}
-
-bool ScriptResourceLoader::isFlag(int flag) {
-	return
-		flag == kRlfLoadFile;
-}
-
-// Properties
-
-Properties::Properties()
-	: _count(0), _properties(0) {
-}
-
-void Properties::init(uint count, byte *properties) {
-	_count = count;
-	_properties = properties;
-}
-
-void Properties::clear() {
-	uint size = (_count >> 3) + 1;
-	for (uint i = 0; i < size; ++i)
-		_properties[i] = 0;
-}
-
-bool Properties::get(uint32 propertyId) {
-	uint index;
-	byte mask;
-	getProperyPos(propertyId, index, mask);
-	return (_properties[index] & mask) != 0;
-}
-
-void Properties::set(uint32 propertyId, bool value) {
-	uint index;
-	byte mask;
-	getProperyPos(propertyId, index, mask);
-	if (value)
-		_properties[index] |= mask;
-	else
-		_properties[index] &= ~mask;
-}
-
-void Properties::getProperyPos(uint32 propertyId, uint &index, byte &mask) {
-	propertyId &= 0xFFFF;
-	index = propertyId >> 3;
-	mask = 1 << (propertyId & 7);
-}
-
-// BlockCounters
-
-BlockCounters::BlockCounters()
-	: _count(0), _blockCounters(0) {
-}
-
-void BlockCounters::init(uint count, byte *blockCounters) {
-	_count = count;
-	_blockCounters = blockCounters;
-}
-
-void BlockCounters::clear() {
-	for (uint i = 0; i < _count; ++i)
-		_blockCounters[i] = 0;
-}
-
-byte BlockCounters::get(uint index) {
-	return _blockCounters[index - 1] & 0x3F;
-}
-
-void BlockCounters::set(uint index, byte value) {
-	_blockCounters[index - 1] ^= (_blockCounters[index - 1] ^ value) & 0x3F;
-}
-
-byte BlockCounters::getC0(uint index) {
-	return _blockCounters[index - 1] & 0xC0;
-}
-
-void BlockCounters::setC0(uint index, byte value) {
-	byte oldValue = _blockCounters[index - 1] & 0x3F;
-	if (value & 0x80)
-		value = value & 0xBF;
-	_blockCounters[index - 1] = oldValue | (value & 0xC0);
-}
-
-// TriggerCause
-
-void TriggerCause::load(Common::SeekableReadStream &stream) {
-	_verbId = stream.readUint32LE();
-	_objectId2 = stream.readUint32LE();
-	_codeOffs = stream.readUint32LE();
-	
-	debug(2, "TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
-		_verbId, _objectId2, _codeOffs);
-}
-
-// TriggerObject
-
-TriggerObject::TriggerObject()
-	: _causesCount(0), _causes(0) {
-}
-
-TriggerObject::~TriggerObject() {
-	delete[] _causes;
-}
-
-void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_objectId = stream.readUint32LE();
-	_causesCount = stream.readUint16LE();
-	stream.skip(2); // Skip padding
-	debug(2, "TriggerObject::load() _objectId: %08X; _causesCount: %d",
-		_objectId, _causesCount);
-	_causes = new TriggerCause[_causesCount];
-	for (uint i = 0; i < _causesCount; ++i)
-		_causes[i].load(stream);
-}
-
-bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
-	if ((verbId & 0xFFFF0000) == 0) {
-		for (uint i = 0; i < _causesCount; ++i) {
-			if ((verbId == 7 && ((_causes[i]._verbId == 7 && _causes[i]._objectId2 == objectId2) || _causes[i]._verbId == 8)) ||
-				(verbId != 7 && verbId == _causes[i]._verbId)) {
-				codeOffs = _causes[i]._codeOffs;
-				return true;
-			}
-		}
-	} else {
-		for (uint i = 0; i < _causesCount; ++i)
-			if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
-				codeOffs = _causes[i]._codeOffs;
-				return true;
-			}
-	}
-	return false;
-}
-
-void TriggerObject::fixupProgInfosDuckman() {
-	for (uint i = 0; i < _causesCount; ++i)
-		_causes[i]._verbId &= 0xFFFF;
-}
-
-// ProgInfo
-
-ProgInfo::ProgInfo()
-	: _triggerObjectsCount(0), _triggerObjects(0),
-	_resourcesCount(0), _resources(0) {
-}
-
-ProgInfo::~ProgInfo() {
-	delete[] _triggerObjects;
-	delete[] _resources;
-}
-
-char *debugW2I(byte *wstr) {
-	static char buf[65];
-	char *p = buf;
-	while (*wstr != 0) {
-		*p++ = *wstr;
-		wstr += 2;
-	}
-	*p = 0;
-	return buf;
-}
-
-void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_id = stream.readUint16LE();
-	_unk = stream.readUint16LE();
-	_name = dataStart + stream.pos();
-	stream.skip(128);
-	_triggerObjectsCount = stream.readUint16LE();
-	_resourcesCount = stream.readUint16LE();
-	debug(2, "\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
-		_id, _unk, debugW2I(_name));
-	uint32 triggerObjectsListOffs = stream.readUint32LE();
-	if (_resourcesCount > 0) {
-		_resources = new uint32[_resourcesCount];
-		for (uint i = 0; i < _resourcesCount; ++i)
-			_resources[i] = stream.readUint32LE();
-	}
-	if (_triggerObjectsCount > 0) {
-		_triggerObjects = new TriggerObject[_triggerObjectsCount];
-		for (uint i = 0; i < _triggerObjectsCount; ++i) {
-			stream.seek(triggerObjectsListOffs + i * 4);
-			uint32 triggerObjectOffs = stream.readUint32LE();
-			stream.seek(triggerObjectOffs);
-			_triggerObjects[i].load(dataStart, stream);
-		}
-	}
-}
-
-bool ProgInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
-	TriggerObject *triggerObject = findTriggerObject(objectId);
-	if (triggerObject)
-		return triggerObject->findTriggerCause(verbId, objectId2, codeOffs);
-	return false;
-}
-
-void ProgInfo::getResources(uint &resourcesCount, uint32 *&resources) {
-	resourcesCount = _resourcesCount;
-	resources = _resources;
-}
-
-TriggerObject *ProgInfo::findTriggerObject(uint32 objectId) {
-	for (uint i = 0; i < _triggerObjectsCount; ++i)
-		if (_triggerObjects[i]._objectId == objectId)
-			return &_triggerObjects[i];
-	return 0;
-}
-
-void ProgInfo::fixupProgInfosDuckman() {
-	for (uint i = 0; i < _triggerObjectsCount; ++i)
-		_triggerObjects[i].fixupProgInfosDuckman();
-}
-
-// ScriptResource
-
-ScriptResource::ScriptResource()
-	: _codeOffsets(0), _objectMap(0) {
-}
-
-ScriptResource::~ScriptResource() {
-	delete[] _codeOffsets;
-	delete[] _objectMap;
-}
-
-void ScriptResource::load(Resource *resource) {
-	_data = resource->_data;
-	_dataSize = resource->_dataSize;
-
-	Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
-	
-	uint32 objectMapOffs, progInfosOffs;
-	_objectMapCount = 0;
-	
-	if (resource->_gameId == kGameIdBBDOU) {
-		progInfosOffs = 0x18;
-	} else if (resource->_gameId == kGameIdDuckman) {
-		for (uint i = 0; i < 27; ++i)
-			_soundIds[i] = stream.readUint32LE();
-		progInfosOffs = 0x8C;
-	}
-	
-	stream.skip(4); // Skip unused
-
-	// Read item counts
-	uint propertiesCount = stream.readUint16LE();
-	uint blockCountersCount = stream.readUint16LE();
-	if (resource->_gameId == kGameIdDuckman)
-		_objectMapCount = stream.readUint16LE();
-	_codeCount = stream.readUint16LE();
-	_progInfosCount = stream.readUint16LE();
-	if (resource->_gameId == kGameIdDuckman)
-		stream.readUint16LE();//Unused?
-
-	// Read item offsets
-	uint32 propertiesOffs = stream.readUint32LE();
-	uint32 blockCountersOffs = stream.readUint32LE();
-	if (resource->_gameId == kGameIdDuckman)
-		objectMapOffs = stream.readUint32LE();
-	uint32 codeTblOffs = stream.readUint32LE();
-	
-	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _progInfosCount: %d; _objectMapCount: %d",
-		propertiesCount, blockCountersCount, _codeCount, _progInfosCount, _objectMapCount);
-	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X; objectMapOffs: %08X",
-		propertiesOffs, blockCountersOffs, codeTblOffs, objectMapOffs);
-	// Init properties
-	_properties.init(propertiesCount, _data + propertiesOffs);
-	
-	// Init blockcounters
-	_blockCounters.init(blockCountersCount, _data + blockCountersOffs);
-	
-	_codeOffsets = new uint32[_codeCount];
-	stream.seek(codeTblOffs);
-	for (uint i = 0; i < _codeCount; ++i)
-		_codeOffsets[i] = stream.readUint32LE();
-
-	_progInfos = new ProgInfo[_progInfosCount];
-	for (uint i = 0; i < _progInfosCount; ++i) {
-		stream.seek(progInfosOffs + i * 4);
-		uint32 progInfoOffs = stream.readUint32LE();
-		stream.seek(progInfoOffs);
-		_progInfos[i].load(_data, stream);
-	}
-	
-	if (_objectMapCount > 0) {
-		_objectMap = new uint32[_objectMapCount];
-		stream.seek(objectMapOffs);
-		for (uint i = 0; i < _objectMapCount; ++i) {
-			_objectMap[i] = stream.readUint32LE();
-			stream.skip(4);
-		}
-	}
-	
-	if (resource->_gameId == kGameIdDuckman) {
-		stream.seek(0x6C);
-		_mainActorObjectId = stream.readUint32LE();
-	} else if (resource->_gameId == kGameIdBBDOU) {
-		stream.seek(0);
-		_mainActorObjectId = stream.readUint32LE();
-	}
-	
-	if (resource->_gameId == kGameIdDuckman)
-		fixupProgInfosDuckman();
-
-}
-
-byte *ScriptResource::getThreadCode(uint32 threadId) {
-	return _data + _codeOffsets[(threadId & 0xFFFF) - 1];
-}
-
-byte *ScriptResource::getCode(uint32 codeOffs) {
-	return _data + codeOffs;
-}
-
-ProgInfo *ScriptResource::getProgInfo(uint32 index) {
-	if (index > 0 && index <= _progInfosCount)
-		return &_progInfos[index - 1];
-	return 0;
-}
-
-uint32 ScriptResource::getObjectActorTypeId(uint32 objectId) {
-	return _objectMap[(objectId & 0xFFFF) - 1];
-}
-
-void ScriptResource::fixupProgInfosDuckman() {
-	for (uint i = 0; i < _progInfosCount; ++i)
-		_progInfos[i].fixupProgInfosDuckman();
-}
-
-// ScriptInstance
-
-ScriptInstance::ScriptInstance(IllusionsEngine *vm)
-	: _vm(vm) {
-}
-
-void ScriptInstance::load(Resource *resource) {
-	_vm->_scriptResource = new ScriptResource();
-	_vm->_scriptResource->load(resource);
-}
-
-void ScriptInstance::unload() {
-	delete _vm->_scriptResource;
-	_vm->_scriptResource = 0;
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/scriptresource.h b/engines/illusions/scriptresource.h
deleted file mode 100644
index c2119b0..0000000
--- a/engines/illusions/scriptresource.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SCRIPTRESOURCE_H
-#define ILLUSIONS_SCRIPTRESOURCE_H
-
-#include "illusions/resourcesystem.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class ScriptResourceLoader : public BaseResourceLoader {
-public:
-	ScriptResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~ScriptResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-class Properties {
-public:
-	Properties();
-	void init(uint count, byte *properties);
-	void clear();
-	bool get(uint32 propertyId);
-	void set(uint32 propertyId, bool value);
-public:
-	uint _count;
-	byte *_properties;
-	void getProperyPos(uint32 propertyId, uint &index, byte &mask);
-};
-
-class BlockCounters {
-public:
-	BlockCounters();
-	void init(uint count, byte *blockCounters);
-	void clear();
-	byte get(uint index);
-	void set(uint index, byte value);
-	byte getC0(uint index);
-	void setC0(uint index, byte value);
-public:
-	uint _count;
-	byte *_blockCounters;
-};
-
-struct TriggerCause {
-	uint32 _verbId;
-	uint32 _objectId2;
-	uint32 _codeOffs;
-	void load(Common::SeekableReadStream &stream);
-};
-
-class TriggerObject {
-public:
-	TriggerObject();
-	~TriggerObject();
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs);
-	void fixupProgInfosDuckman();
-public:
-	uint32 _objectId;
-	uint _causesCount;
-	TriggerCause *_causes;
-};
-
-class ProgInfo {
-public:
-	ProgInfo();
-	~ProgInfo();
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
-	void getResources(uint &resourcesCount, uint32 *&resources);
-	void fixupProgInfosDuckman();
-protected:
-	uint16 _id;
-	uint16 _unk;
-	byte *_name;
-	uint _triggerObjectsCount;
-	TriggerObject *_triggerObjects;
-	uint _resourcesCount;
-	uint32 *_resources;
-	TriggerObject *findTriggerObject(uint32 objectId);
-};
-
-class ScriptResource {
-public:
-	ScriptResource();
-	~ScriptResource();
-	void load(Resource *resource);
-	byte *getThreadCode(uint32 threadId);
-	byte *getCode(uint32 codeOffs);
-	ProgInfo *getProgInfo(uint32 index);
-	uint32 getObjectActorTypeId(uint32 objectId);
-	uint32 getMainActorObjectId() const { return _mainActorObjectId; }
-public:
-	byte *_data;
-	uint32 _dataSize;
-	Properties _properties;
-	BlockCounters _blockCounters;
-	uint _codeCount;
-	uint32 *_codeOffsets;
-	uint _progInfosCount;
-	ProgInfo *_progInfos;
-	// Duckman specific
-	uint32 _soundIds[27];
-	uint _objectMapCount;
-	uint32 *_objectMap;
-	uint32 _mainActorObjectId;
-	void fixupProgInfosDuckman();
-};
-
-class ScriptInstance : public ResourceInstance {
-public:
-	ScriptInstance(IllusionsEngine *vm);              
-	virtual void load(Resource *resource);
-	virtual void unload();
-public:
-	IllusionsEngine *_vm;	
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_ACTORRESOURCE_H
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index f2a0225..fe08850 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -23,8 +23,8 @@
 #include "illusions/illusions.h"
 #include "illusions/sequenceopcodes.h"
 #include "illusions/actor.h"
-#include "illusions/actorresource.h"
 #include "illusions/dictionary.h"
+#include "illusions/resources/actorresource.h"
 #include "illusions/screen.h"
 #include "illusions/scriptopcodes.h"
 #include "illusions/sound.h"
diff --git a/engines/illusions/soundresource.cpp b/engines/illusions/soundresource.cpp
deleted file mode 100644
index efc16df..0000000
--- a/engines/illusions/soundresource.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/soundresource.h"
-#include "illusions/sound.h"
-
-namespace Illusions {
-
-// SoundGroupResourceLoader
-
-void SoundGroupResourceLoader::load(Resource *resource) {
-	SoundGroupInstance *soundGroupInstance = new SoundGroupInstance(_vm);
-	soundGroupInstance->load(resource);
-	resource->_instance = soundGroupInstance;
-}
-
-void SoundGroupResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.sg", resource->_resId);
-}
-
-bool SoundGroupResourceLoader::isFlag(int flag) {
-	return
-		flag == kRlfLoadFile/* ||
-		flag == kRlfFreeDataAfterLoad*/;
-}
-
-// SoundEffect
-
-void SoundEffect::load(Common::SeekableReadStream &stream) {
-	_soundEffectId = stream.readUint32LE();
-	_looping = stream.readUint16LE() != 0;
-	_field6 = stream.readUint16LE();
-	_volume = stream.readUint16LE();
-	_frequency = stream.readUint16LE();
-	stream.skip(32 + 4); // Skip name
-	debug("SoundEffect::load() _soundEffectId: %08X, _looping: %d, _field6: %d, _volume: %d, _frequency: %d",
-		_soundEffectId, _looping, _field6, _volume, _frequency);
-}
-
-// SoundGroupResource
-
-SoundGroupResource::SoundGroupResource()
-	: _soundEffects(0) {
-}
-
-SoundGroupResource::~SoundGroupResource() {
-	delete[] _soundEffects;
-}
-
-void SoundGroupResource::load(byte *data, uint32 dataSize) {
-	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-
-	stream.skip(4);
-	_soundEffectsCount = stream.readUint16LE();
-	stream.skip(2);
-	uint32 soundEffectsOffs = stream.readUint32LE();
-	debug("_soundEffectsCount: %d; soundEffectsOffs: %08X", _soundEffectsCount, soundEffectsOffs);
-	_soundEffects = new SoundEffect[_soundEffectsCount];
-	stream.seek(soundEffectsOffs);
-	for (uint i = 0; i < _soundEffectsCount; ++i)
-		_soundEffects[i].load(stream);
-
-}
-
-// SoundGroupInstance
-
-SoundGroupInstance::SoundGroupInstance(IllusionsEngine *vm)
-	: _vm(vm) {
-}
-
-void SoundGroupInstance::load(Resource *resource) {
-	_soundGroupResource = new SoundGroupResource();
-	_soundGroupResource->load(resource->_data, resource->_dataSize);
-	for (uint i = 0; i < _soundGroupResource->_soundEffectsCount; ++i) {
-		SoundEffect *soundEffect = &_soundGroupResource->_soundEffects[i];
-		_vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
-	}
-	_resId = resource->_resId;
-}
-
-void SoundGroupInstance::unload() {
-	_vm->_soundMan->unloadSounds(_resId);
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/soundresource.h b/engines/illusions/soundresource.h
deleted file mode 100644
index 638e8df..0000000
--- a/engines/illusions/soundresource.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SOUNDRESOURCE_H
-#define ILLUSIONS_SOUNDRESOURCE_H
-
-#include "illusions/graphics.h"
-#include "illusions/resourcesystem.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class SoundGroupResourceLoader : public BaseResourceLoader {
-public:
-	SoundGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~SoundGroupResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-struct SoundEffect {
-	uint32 _soundEffectId;
-	bool _looping;
-	int16 _field6;
-	int16 _volume;
-	int16 _frequency;
-	void load(Common::SeekableReadStream &stream);
-};
-
-class SoundGroupResource {
-public:
-	SoundGroupResource();
-	~SoundGroupResource();
-	void load(byte *data, uint32 dataSize);
-public:
-	uint _soundEffectsCount;
-	SoundEffect *_soundEffects;
-};
-
-class SoundGroupInstance : public ResourceInstance {
-public:
-	SoundGroupInstance(IllusionsEngine *vm);              
-	virtual void load(Resource *resource);
-	virtual void unload();
-public:
-	IllusionsEngine *_vm;	
-	SoundGroupResource *_soundGroupResource;
-	uint32 _resId;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/talkresource.cpp b/engines/illusions/talkresource.cpp
deleted file mode 100644
index 17672ba..0000000
--- a/engines/illusions/talkresource.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions.h"
-#include "illusions/talkresource.h"
-#include "illusions/dictionary.h"
-
-namespace Illusions {
-
-// TalkResourceLoader
-
-void TalkResourceLoader::load(Resource *resource) {
-	resource->_instance = _vm->_talkItems->createTalkInstance(resource);
-}
-
-void TalkResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.tlk", resource->_resId);
-}
-
-bool TalkResourceLoader::isFlag(int flag) {
-	return
-		flag == kRlfLoadFile;
-}
-
-// TalkEntry
-
-void TalkEntry::load(byte *dataStart, Common::SeekableReadStream &stream) {
-	_talkId = stream.readUint32LE();
-	stream.readUint32LE(); // Skip unknown
-	uint32 textOffs = stream.readUint32LE();
-	uint32 tblOffs = stream.readUint32LE();
-	uint32 voiceNameOffs = stream.readUint32LE();
-	_text = dataStart + textOffs;
-	_tblPtr = dataStart + tblOffs;
-	_voiceName = dataStart + voiceNameOffs;
-	debug(0, "TalkEntry::load() _talkId: %08X; textOffs: %08X; tblOffs: %08X; voiceNameOffs: %08X",
-		_talkId, textOffs, tblOffs, voiceNameOffs);
-}
-
-// TalkResource
-
-TalkResource::TalkResource()
-	: _talkEntriesCount(0), _talkEntries(0) {
-}
-
-TalkResource::~TalkResource() {
-	delete[] _talkEntries;
-}
-
-void TalkResource::load(byte *data, uint32 dataSize) {
-	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-	stream.skip(4); // Skip size
-	_talkEntriesCount = stream.readUint16LE();
-	stream.skip(2); // Skip padding
-	_talkEntries = new TalkEntry[_talkEntriesCount];
-	for (uint i = 0; i < _talkEntriesCount; ++i) {
-		stream.seek(8 + i * 0x14);
-		_talkEntries[i].load(data, stream);
-	}
-}
-
-// TalkInstance
-
-TalkInstance::TalkInstance(IllusionsEngine *vm)
-	: _vm(vm), _pauseCtr(0) {
-}
-
-void TalkInstance::load(Resource *resource) {
-	TalkResource *talkResource = new TalkResource();
-	talkResource->load(resource->_data, resource->_dataSize);
-	_talkRes = talkResource;
-	_talkId = resource->_resId;
-	_tag = resource->_tag;
-	registerResources();
-}
-
-void TalkInstance::unload() {
-	unregisterResources();
-	_vm->_talkItems->removeTalkInstance(this);
-	delete _talkRes;
-}
-
-void TalkInstance::pause() {
-	++_pauseCtr;
-	if (_pauseCtr == 1)
-		unregisterResources();
-}
-
-void TalkInstance::unpause() {
-	--_pauseCtr;
-	if (_pauseCtr == 0)
-		registerResources();
-}
-
-void TalkInstance::registerResources() {
-	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
-		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
-		_vm->_dict->addTalkEntry(talkEntry->_talkId, talkEntry);
-	}
-}
-
-void TalkInstance::unregisterResources() {
-	for (uint i = 0; i < _talkRes->_talkEntriesCount; ++i) {
-		TalkEntry *talkEntry = &_talkRes->_talkEntries[i];
-		_vm->_dict->removeTalkEntry(talkEntry->_talkId);
-	}
-}
-
-// TalkInstanceList
-
-TalkInstanceList::TalkInstanceList(IllusionsEngine *vm)
-	: _vm(vm) {
-}
-
-TalkInstanceList::~TalkInstanceList() {
-}
-
-TalkInstance *TalkInstanceList::createTalkInstance(Resource *resource) {
-	TalkInstance *talkInstance = new TalkInstance(_vm);
-	talkInstance->load(resource);
-	_items.push_back(talkInstance);
-	return talkInstance;
-}
-
-void TalkInstanceList::removeTalkInstance(TalkInstance *talkInstance) {
-	_items.remove(talkInstance);
-}
-
-TalkInstance *TalkInstanceList::findTalkItem(uint32 talkId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_talkId == talkId)
-			return (*it);
-	return 0;
-}
-
-TalkInstance *TalkInstanceList::findTalkItemByTag(uint32 tag) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
-			return (*it);
-	return 0;
-}
-
-void TalkInstanceList::pauseByTag(uint32 tag) {
-	TalkInstance *talkInstance = findTalkItemByTag(tag);
-	if (talkInstance)
-		talkInstance->pause();
-}
-
-void TalkInstanceList::unpauseByTag(uint32 tag) {
-	TalkInstance *talkInstance = findTalkItemByTag(tag);
-	if (talkInstance)
-		talkInstance->unpause();
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/talkresource.h b/engines/illusions/talkresource.h
deleted file mode 100644
index 5e3797e..0000000
--- a/engines/illusions/talkresource.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_TALKRESOURCE_H
-#define ILLUSIONS_TALKRESOURCE_H
-
-#include "illusions/graphics.h"
-#include "illusions/resourcesystem.h"
-
-namespace Illusions {
-
-class IllusionsEngine;
-
-class TalkResourceLoader : public BaseResourceLoader {
-public:
-	TalkResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
-	virtual ~TalkResourceLoader() {}
-	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
-	virtual bool isFlag(int flag);
-protected:
-	IllusionsEngine *_vm;
-};
-
-struct TalkEntry {
-	uint32 _talkId;
-	//field_4 dd
-	byte *_text;
-	byte *_tblPtr;
-	byte *_voiceName;
-	void load(byte *dataStart, Common::SeekableReadStream &stream);
-};
-
-class TalkResource {
-public:
-	TalkResource();
-	~TalkResource();
-	void load(byte *data, uint32 dataSize);
-public:
-	uint _talkEntriesCount;
-	TalkEntry *_talkEntries;
-};
-
-class TalkInstance : public ResourceInstance {
-public:
-	TalkInstance(IllusionsEngine *vm);
-	virtual void load(Resource *resource);
-	virtual void unload();
-	virtual void pause();
-	virtual void unpause();
-public:
-	IllusionsEngine *_vm;
-	uint32 _talkId;
-	uint32 _tag;
-	TalkResource *_talkRes;
-	int _pauseCtr;
-	void registerResources();
-	void unregisterResources();
-};
-
-class TalkInstanceList {
-public:
-	TalkInstanceList(IllusionsEngine *vm);
-	~TalkInstanceList();
-	TalkInstance *createTalkInstance(Resource *resource);
-	void removeTalkInstance(TalkInstance *talkInstance);
-	TalkInstance *findTalkItem(uint32 talkId);
-	TalkInstance *findTalkItemByTag(uint32 tag);
-	void pauseByTag(uint32 tag);
-	void unpauseByTag(uint32 tag);
-//protected:
-public:
-	typedef Common::List<TalkInstance*> Items;
-	typedef Items::iterator ItemsIterator;
-	IllusionsEngine *_vm;
-	Items _items;
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_TALKRESOURCE_H
diff --git a/engines/illusions/textdrawer.h b/engines/illusions/textdrawer.h
index a5f6102..290d6c7 100644
--- a/engines/illusions/textdrawer.h
+++ b/engines/illusions/textdrawer.h
@@ -24,7 +24,7 @@
 #define ILLUSIONS_TEXTDRAWER_H
 
 #include "illusions/graphics.h"
-#include "illusions/fontresource.h"
+#include "illusions/resources/fontresource.h"
 #include "common/array.h"
 #include "common/rect.h"
 #include "graphics/surface.h"
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 3a4d286..54bc207 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -25,8 +25,8 @@
 #include "illusions/actor.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/sound.h"
-#include "illusions/talkresource.h"
 #include "illusions/time.h"
 
 namespace Illusions {
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 67c394c..84aa039 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -25,9 +25,9 @@
 #include "illusions/actor.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/resources/talkresource.h"
 #include "illusions/screentext.h"
 #include "illusions/sound.h"
-#include "illusions/talkresource.h"
 #include "illusions/time.h"
 
 namespace Illusions {


Commit: e4a85c7da6486f0221ef34dc0d2e82100533a213
    https://github.com/scummvm/scummvm/commit/e4a85c7da6486f0221ef34dc0d2e82100533a213
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move game-specific files into own subdirectories

Changed paths:
  A engines/illusions/bbdou/illusions_bbdou.cpp
  A engines/illusions/bbdou/illusions_bbdou.h
  A engines/illusions/duckman/illusions_duckman.cpp
  A engines/illusions/duckman/illusions_duckman.h
  R engines/illusions/illusions_bbdou.cpp
  R engines/illusions/illusions_bbdou.h
  R engines/illusions/illusions_duckman.cpp
  R engines/illusions/illusions_duckman.h
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/detection.cpp
    engines/illusions/module.mk
    engines/illusions/scriptopcodes_bbdou.cpp
    engines/illusions/scriptopcodes_duckman.cpp
    engines/illusions/threads/causethread_duckman.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index 7ccd0ea..d24e16e 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_bbdou.h"
+#include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_bubble.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index c158472..88f0fac 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_bbdou.h"
+#include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/actor.h"
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 5fdda62..064be4e 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_bbdou.h"
+#include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_inventory.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 5d98784..5369bbb 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_bbdou.h"
+#include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/bbdou/bbdou_bubble.h"
 #include "illusions/bbdou/bbdou_inventory.h"
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
new file mode 100644
index 0000000..21fa5d0
--- /dev/null
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -0,0 +1,602 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/cursor.h"
+#include "illusions/dictionary.h"
+#include "illusions/graphics.h"
+#include "illusions/input.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/resources/talkresource.h"
+#include "illusions/resourcesystem.h"
+#include "illusions/screen.h"
+#include "illusions/screentext.h"
+#include "illusions/scriptstack.h"
+#include "illusions/scriptopcodes_bbdou.h"
+#include "illusions/sound.h"
+#include "illusions/specialcode.h"
+#include "illusions/bbdou/bbdou_specialcode.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+
+#include "illusions/threads/abortablethread.h"
+#include "illusions/threads/scriptthread.h"
+#include "illusions/threads/talkthread.h"
+#include "illusions/threads/timerthread.h"
+
+#include "audio/audiostream.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/timer.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/font.h"
+#include "graphics/fontman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+// TriggerFunction
+
+TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
+	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
+}
+
+TriggerFunction::~TriggerFunction() {
+	delete _callback;
+}
+
+void TriggerFunction::run(uint32 callingThreadId) {
+	(*_callback)(this, callingThreadId);
+}
+
+// TriggerFunctions
+
+void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end()) {
+		delete *it;
+		_triggerFunctions.erase(it);
+	}
+	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
+}
+
+TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end())
+		return (*it);
+	return 0;
+}
+
+void TriggerFunctions::removeBySceneId(uint32 sceneId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	while (it != _triggerFunctions.end()) {
+		if ((*it)->_sceneId == sceneId) {
+			delete *it;
+			it = _triggerFunctions.erase(it);
+		} else
+			++it;
+	}
+}
+
+TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	for (; it != _triggerFunctions.end(); ++it) {
+		TriggerFunction *triggerFunction = *it;
+		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
+			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
+			break;
+	}
+	return it;		
+}
+
+// ActiveScenes
+
+ActiveScenes::ActiveScenes() {
+	clear();
+}
+
+void ActiveScenes::clear() {
+	_stack.clear();
+}
+
+void ActiveScenes::push(uint32 sceneId) {
+	ActiveScene activeScene;
+	activeScene._sceneId = sceneId;
+	activeScene._pauseCtr = 0;
+	_stack.push(activeScene);
+}
+
+void ActiveScenes::pop() {
+	_stack.pop();
+}
+
+void ActiveScenes::pauseActiveScene() {
+	++_stack.top()._pauseCtr;
+}
+
+void ActiveScenes::unpauseActiveScene() {
+	--_stack.top()._pauseCtr;
+}
+
+uint ActiveScenes::getActiveScenesCount() {
+	return _stack.size();
+}
+
+void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
+	if (sceneId)
+		*sceneId = _stack[index - 1]._sceneId;
+	if (pauseCtr)
+		*pauseCtr = _stack[index - 1]._pauseCtr;
+}
+
+uint32 ActiveScenes::getCurrentScene() {
+	if (_stack.size() > 0)
+		return _stack.top()._sceneId;
+	return 0;
+}
+
+bool ActiveScenes::isSceneActive(uint32 sceneId) {
+	for (uint i = 0; i < _stack.size(); ++i)
+		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
+			return true;
+	return false;
+}
+
+// IllusionsEngine_BBDOU
+
+IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd)
+	: IllusionsEngine(syst, gd) {
+}
+
+Common::Error IllusionsEngine_BBDOU::run() {
+
+	// Init search paths
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
+	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
+
+	_dict = new Dictionary();
+
+	_resSys = new ResourceSystem(this);
+	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
+	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
+	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
+	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
+	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
+
+	_screen = new Screen(this, 640, 480, 16);
+	_screenText = new ScreenText(this);
+	_input = new Input();	
+	_actorInstances = new ActorInstanceList(this);
+	_backgroundInstances = new BackgroundInstanceList(this);
+	_camera = new Camera(this);
+	_controls = new Controls(this);
+	_cursor = new Cursor(this);
+	_talkItems = new TalkInstanceList(this);
+	_triggerFunctions = new TriggerFunctions();
+	_threads = new ThreadList(this);
+	_updateFunctions = new UpdateFunctions();
+	_soundMan = new SoundMan(this);
+
+	initUpdateFunctions();
+
+	_fader = 0;
+
+	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
+	_stack = new ScriptStack();
+	
+	// TODO Move to own class
+	_resGetCtr = 0;
+	_unpauseControlActorFlag = false;
+	_lastUpdateTime = 0;
+
+	_pauseCtr = 0;
+	_field8 = 1;
+	_fieldA = 0;
+	_fieldE = 240;
+	
+	_globalSceneId = 0x00010003;	
+	
+	setDefaultTextCoords();
+	
+	_resSys->loadResource(0x000D0001, 0, 0);
+
+	_doScriptThreadInit = false;
+	startScriptThread(0x00020004, 0, 0, 0, 0);
+	_doScriptThreadInit = true;
+
+	while (!shouldQuit()) {
+		runUpdateFunctions();
+		_system->updateScreen();
+		updateEvents();
+	}
+
+	delete _stack;
+	delete _scriptOpcodes;
+
+    delete _soundMan;
+	delete _updateFunctions;
+	delete _threads;
+	delete _triggerFunctions;
+	delete _talkItems;
+	delete _cursor;
+	delete _controls;
+	delete _camera;
+	delete _backgroundInstances;
+	delete _actorInstances;
+	delete _input;
+	delete _screenText;
+	delete _screen;
+	delete _resSys;
+	delete _dict;
+	
+	debug("Ok");
+	
+	return Common::kNoError;
+}
+
+bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
+	return
+		false;
+		/*
+		(f == kSupportsRTL) ||
+		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsSavingDuringRuntime);
+		*/
+}
+
+#define UPDATEFUNCTION(priority, tag, callback) \
+	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
+		(this, &IllusionsEngine_BBDOU::callback));
+
+void IllusionsEngine_BBDOU::initUpdateFunctions() {
+	UPDATEFUNCTION(30, 0, updateScript);
+	UPDATEFUNCTION(50, 0, updateActors);
+	UPDATEFUNCTION(60, 0, updateSequences);
+	UPDATEFUNCTION(70, 0, updateGraphics);
+	UPDATEFUNCTION(90, 0, updateSprites);
+	UPDATEFUNCTION(120, 0, updateSoundMan);
+}
+
+#undef UPDATEFUNCTION
+
+int IllusionsEngine_BBDOU::updateScript(uint flags) {
+	_threads->updateThreads();
+	return 1;
+}
+
+bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	uint32 codeOffs;
+	return 
+		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
+		findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
+}
+
+void IllusionsEngine_BBDOU::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
+}
+
+uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
+	uint32 codeOffs;
+	uint32 causeThreadId = 0;
+	TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
+	if (triggerFunction) {
+		triggerFunction->run(callingThreadId);
+	} else if (findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
+		//debug("Run cause at %08X", codeOffs);
+		causeThreadId = startTempScriptThread(_scriptResource->getCode(codeOffs),
+			callingThreadId, verbId, objectId2, objectId);
+	}
+	return causeThreadId;
+}
+
+void IllusionsEngine_BBDOU::setDefaultTextCoords() {
+	WidthHeight dimensions;
+	dimensions._width = 480;
+	dimensions._height = 48;
+	Common::Point pt(320, 448);
+	setDefaultTextDimensions(dimensions);
+	setDefaultTextPosition(pt);
+}
+
+void IllusionsEngine_BBDOU::loadSpecialCode(uint32 resId) {
+	_specialCode = new BbdouSpecialCode(this);
+	_specialCode->init();
+}
+
+void IllusionsEngine_BBDOU::unloadSpecialCode(uint32 resId) {
+	delete _specialCode;
+	_specialCode = 0;
+}
+
+void IllusionsEngine_BBDOU::notifyThreadId(uint32 &threadId) {
+	if (threadId) {
+		uint32 tempThreadId = threadId;
+		threadId = 0;
+		_threads->notifyId(tempThreadId);
+	}
+}
+
+bool IllusionsEngine_BBDOU::testMainActorFastWalk(Control *control) {
+	return false;
+}
+
+bool IllusionsEngine_BBDOU::testMainActorCollision(Control *control) {
+	// Not used in BBDOU
+	return false;
+}
+
+Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
+	return _dict->getObjectControl(objectId);
+}
+
+Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
+	Common::Point pt;
+	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt) ||
+		_actorInstances->findNamedPoint(namedPointId, pt) ||
+		_controls->findNamedPoint(namedPointId, pt))
+		return pt;
+	// TODO
+	switch (namedPointId) {
+	case 0x70001:
+		return Common::Point(0, 0);
+	case 0x70002:
+		return Common::Point(640, 0);
+	case 0x70023:
+		return Common::Point(320, 240);
+	}
+	debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+	return Common::Point(0, 0);
+}
+
+uint32 IllusionsEngine_BBDOU::getPriorityFromBase(int16 priority) {
+	return 32000000 * priority;
+}
+
+uint32 IllusionsEngine_BBDOU::getCurrentScene() {
+	return _activeScenes.getCurrentScene();
+}
+
+uint32 IllusionsEngine_BBDOU::getPrevScene() {
+	return _prevSceneId;
+}
+
+bool IllusionsEngine_BBDOU::isCursorObject(uint32 actorTypeId, uint32 objectId) {
+	return actorTypeId == 0x50001 && objectId == 0x40004;
+}
+
+void IllusionsEngine_BBDOU::setCursorControlRoutine(Control *control) {
+	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_BBDOU>
+		(this, &IllusionsEngine_BBDOU::cursorControlRoutine));
+}
+
+void IllusionsEngine_BBDOU::placeCursorControl(Control *control, uint32 sequenceId) {
+	_cursor->place(control, sequenceId);
+}
+
+void IllusionsEngine_BBDOU::setCursorControl(Control *control) {
+	_cursor->setControl(control);
+}
+
+void IllusionsEngine_BBDOU::showCursor() {
+	_cursor->show();
+}
+
+void IllusionsEngine_BBDOU::hideCursor() {
+	_cursor->hide();
+}
+
+void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
+	control->_actor->_seqCodeValue1 = 100 * deltaTime;
+	if (control->_actor->_flags & 1) {
+		switch (_cursor->_status) {
+		case 2:
+			// Unused nullsub_1(control);
+			break;
+		case 3:
+			// TODO _vm->_shellMgr->handleMouse(control);
+			break;
+		}
+	}
+}
+
+void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
+	startScriptThread(threadId, callingThreadId, 0, 0, 0);
+}
+
+void IllusionsEngine_BBDOU::startScriptThread(uint32 threadId, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug(2, "Starting script thread %08X", threadId);
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+}
+
+void IllusionsEngine_BBDOU::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	debug(2, "Starting anonymous script thread %08X", threadId);
+	uint32 tempThreadId = newTempThreadId();
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+}
+
+uint32 IllusionsEngine_BBDOU::startAbortableTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, true);
+}
+
+uint32 IllusionsEngine_BBDOU::startTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, false);
+}
+
+uint32 IllusionsEngine_BBDOU::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting abortable thread %08X", tempThreadId);
+	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
+	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
+		scriptThreadId, scriptCodeIp2);
+	_threads->startThread(abortableThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
+	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
+	debug(2, "Starting talk thread");
+	uint32 tempThreadId = newTempThreadId();
+	_threads->endTalkThreadsNoNotify();
+	TalkThread *talkThread = new TalkThread(this, tempThreadId, callingThreadId, 0,
+		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
+	_threads->startThread(talkThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting temp script thread %08X", tempThreadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
+	return tempThreadId;
+}
+
+void IllusionsEngine_BBDOU::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
+	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
+		scriptCodeIp, value8, valueC, value10);
+	_threads->startThread(scriptThread);
+	if (_pauseCtr > 0)
+		scriptThread->pause();
+	if (_doScriptThreadInit) {
+		int updateResult = kTSRun;
+		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
+			updateResult = scriptThread->update();
+	}
+}
+
+uint32 IllusionsEngine_BBDOU::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
+	uint32 tempThreadId = newTempThreadId();
+	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
+		duration, isAbortable);
+	_threads->startThread(timerThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_BBDOU::newTempThreadId() {
+	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
+	if (threadId > 65535) {
+		_nextTempThreadId = 0;
+		threadId = 2 * _scriptResource->_codeCount;
+	}
+	++_nextTempThreadId;
+	return 0x00020000 | threadId;
+}
+
+bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (!progInfo) {
+		dumpActiveScenes(_globalSceneId, threadId);
+		sceneId = _theSceneId;
+	}
+	_activeScenes.push(sceneId);
+	return progInfo != 0;
+}
+
+void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	// TODO krnfileDump(sceneId);
+	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
+	_threads->terminateThreadsByTag(sceneId, threadId);
+	_controls->destroyControlsByTag(sceneId);
+	_triggerFunctions->removeBySceneId(sceneId);
+	_resSys->unloadResourcesByTag(sceneId);
+	_activeScenes.pop();
+}
+
+void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	_camera->pushCameraMode();
+	_threads->suspendThreadsByTag(sceneId, threadId);
+	_controls->pauseControlsByTag(sceneId);
+	_actorInstances->pauseByTag(sceneId);
+	_backgroundInstances->pauseByTag(sceneId);
+	_activeScenes.pauseActiveScene();
+}
+
+void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
+	uint32 sceneId = _activeScenes.getCurrentScene();
+	_backgroundInstances->unpauseByTag(sceneId);
+	_actorInstances->unpauseByTag(sceneId);
+	_controls->unpauseControlsByTag(sceneId);
+	_threads->notifyThreadsByTag(sceneId, threadId);
+	_camera->popCameraMode();
+	_activeScenes.unpauseActiveScene();
+}
+
+void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
+	uint activeScenesCount = _activeScenes.getActiveScenesCount();
+	while (activeScenesCount > 0) {
+		uint32 activeSceneId;
+		_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, 0);
+		if (activeSceneId == sceneId)
+			break;
+		exitScene(threadId);
+		--activeScenesCount;
+	}
+	_camera->clearCameraModeStack();
+}
+
+void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
+	_theSceneId = theSceneId;
+	_theThreadId = theThreadId;
+}
+
+bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (progInfo)
+		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	return false;
+}
+
+void IllusionsEngine_BBDOU::reset() {
+	_scriptResource->_blockCounters.clear();
+	_scriptResource->_properties.clear();
+	// TODO script_sub_417FF0(1, 0);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
new file mode 100644
index 0000000..fccfb59
--- /dev/null
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -0,0 +1,160 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ILLUSIONS_BBDOU_H
+#define ILLUSIONS_ILLUSIONS_BBDOU_H
+
+#include "illusions/illusions.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+class Dictionary;
+class ScriptMan;
+class ScriptStack;
+class TriggerFunctions;
+class TriggerFunction;
+
+typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
+
+struct TriggerFunction {
+	uint32 _sceneId;
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _objectId;
+	TriggerFunctionCallback *_callback;
+	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	~TriggerFunction();
+	void run(uint32 callingThreadId);
+};
+
+class TriggerFunctions {
+public:
+	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void removeBySceneId(uint32 sceneId);
+public:
+	typedef Common::List<TriggerFunction*> Items;
+	typedef Items::iterator ItemsIterator;
+	Items _triggerFunctions;
+	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+};
+
+struct ActiveScene {
+	uint32 _sceneId;
+	int _pauseCtr;
+};
+
+class ActiveScenes {
+public:
+	ActiveScenes();
+	void clear();
+	void push(uint32 sceneId);
+	void pop();
+	void pauseActiveScene();
+	void unpauseActiveScene();
+	uint getActiveScenesCount();
+	void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
+	uint32 getCurrentScene();
+	bool isSceneActive(uint32 sceneId);
+protected:
+	Common::FixedStack<ActiveScene, 16> _stack;
+};
+
+class IllusionsEngine_BBDOU : public IllusionsEngine {
+public:
+	IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd);
+protected:
+	virtual Common::Error run();
+	virtual bool hasFeature(EngineFeature f) const;
+public:	
+	ScriptMan *_scriptMan;
+	TriggerFunctions *_triggerFunctions;
+	Cursor *_cursor;
+
+	ActiveScenes _activeScenes;
+	uint32 _prevSceneId;
+	uint32 _theSceneId;
+	uint32 _theThreadId;
+	uint32 _globalSceneId;
+
+	void initUpdateFunctions();
+	int updateScript(uint flags);
+
+	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
+
+    void setDefaultTextCoords();
+
+	void loadSpecialCode(uint32 resId);
+	void unloadSpecialCode(uint32 resId);
+	void notifyThreadId(uint32 &threadId);
+	bool testMainActorFastWalk(Control *control);
+	bool testMainActorCollision(Control *control);
+	Control *getObjectControl(uint32 objectId);
+	Common::Point getNamedPointPosition(uint32 namedPointId);
+	uint32 getPriorityFromBase(int16 priority);
+	uint32 getCurrentScene();
+	uint32 getPrevScene();	
+	
+	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
+	void setCursorControlRoutine(Control *control);
+	void placeCursorControl(Control *control, uint32 sequenceId);
+	void setCursorControl(Control *control);
+	void showCursor();
+	void hideCursor();
+	void cursorControlRoutine(Control *control, uint32 deltaTime);
+
+	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
+	void startScriptThread(uint32 threadId, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void startAnonScriptThread(int32 threadId, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
+	uint32 startTimerThread(uint32 duration, uint32 threadId);
+	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
+	uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
+		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
+	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
+	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
+	uint32 newTempThreadId();
+
+	bool enterScene(uint32 sceneId, uint32 threadId);
+	void exitScene(uint32 threadId);
+	void enterPause(uint32 threadId);
+	void leavePause(uint32 threadId);
+	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
+
+	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
+	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
+	void reset();
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index 5c1e53a..53e7a4b 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -21,8 +21,8 @@
  */
 
 #include "illusions/illusions.h"
-#include "illusions/illusions_bbdou.h"
-#include "illusions/illusions_duckman.h"
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/duckman/illusions_duckman.h"
 
 #include "common/config-manager.h"
 #include "engines/advancedDetector.h"
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
new file mode 100644
index 0000000..a180f2d
--- /dev/null
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -0,0 +1,1584 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/cursor.h"
+#include "illusions/dictionary.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/graphics.h"
+#include "illusions/input.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/midiresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/resources/talkresource.h"
+#include "illusions/resourcesystem.h"
+#include "illusions/screen.h"
+#include "illusions/screentext.h"
+#include "illusions/scriptopcodes_duckman.h"
+#include "illusions/scriptstack.h"
+#include "illusions/sound.h"
+#include "illusions/specialcode.h"
+#include "illusions/textdrawer.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+
+#include "illusions/threads/abortablethread.h"
+#include "illusions/threads/causethread_duckman.h"
+#include "illusions/threads/scriptthread.h"
+#include "illusions/threads/talkthread_duckman.h"
+#include "illusions/threads/timerthread.h"
+
+#include "audio/audiostream.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/timer.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/font.h"
+#include "graphics/fontman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+// IllusionsEngine_Duckman
+
+IllusionsEngine_Duckman::IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd)
+	: IllusionsEngine(syst, gd) {
+}
+
+Common::Error IllusionsEngine_Duckman::run() {
+
+	// Init search paths
+	const Common::FSNode gameDataDir(ConfMan.get("path"));
+	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
+	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
+	SearchMan.addSubDirectoryMatching(gameDataDir, "x");// DEBUG until gam reader is done
+
+	_dict = new Dictionary();
+
+	_resSys = new ResourceSystem(this);
+	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
+	_resSys->addResourceLoader(0x000A0000, new MidiGroupResourceLoader(this));
+	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
+	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
+	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
+	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
+	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
+
+	_screen = new Screen(this, 320, 200, 8);
+	_screenText = new ScreenText(this);
+	_input = new Input();	
+	_actorInstances = new ActorInstanceList(this);
+	_backgroundInstances = new BackgroundInstanceList(this);
+	_camera = new Camera(this);
+	_controls = new Controls(this);
+	_talkItems = new TalkInstanceList(this);
+	_threads = new ThreadList(this);
+	_updateFunctions = new UpdateFunctions();
+	_soundMan = new SoundMan(this);
+
+	_fader = new Fader();
+
+	initUpdateFunctions();
+
+	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
+	_stack = new ScriptStack();
+	
+	// TODO Move to own class
+	_resGetCtr = 0;
+	_unpauseControlActorFlag = false;
+	_lastUpdateTime = 0;
+
+	_currWalkOverlappedControl = 0;
+
+	_pauseCtr = 0;
+	_doScriptThreadInit = false;
+	_field8 = 1;
+	_fieldA = 0;
+	_fieldE = 240;
+	
+	_propertyTimersActive = false;
+	_propertyTimersPaused = false;
+
+	_globalSceneId = 0x00010003;
+
+	initInventory();
+	initSpecialCode();
+	setDefaultTextCoords();
+	initCursor();
+	initActiveScenes();
+
+	_resSys->loadResource(0x120001, 0x00010001, 0);
+	_resSys->loadResource(0x120002, 0x00010001, 0);
+	_resSys->loadResource(0x120003, 0x00010001, 0);
+
+	_resSys->loadResource(0x000D0001, 0x00010001, 0);
+	startScriptThread(0x00020004, 0);
+	_doScriptThreadInit = true;
+
+#if 0
+	//DEBUG
+	_scriptResource->_properties.set(0x000E003A, true);
+	_scriptResource->_properties.set(0x000E0020, true);
+	_scriptResource->_properties.set(0x000E003A, true);
+	_scriptResource->_properties.set(0x000E003B, true);
+	_scriptResource->_properties.set(0x000E0009, true);
+	_scriptResource->_properties.set(0x000E003D, true);
+	_scriptResource->_properties.set(0x000E0024, true);
+#endif
+
+	while (!shouldQuit()) {
+		runUpdateFunctions();
+		_system->updateScreen();
+		updateEvents();
+	}
+
+	delete _stack;
+	delete _scriptOpcodes;
+
+	delete _fader;
+
+	delete _soundMan;
+	delete _updateFunctions;
+	delete _threads;
+	delete _talkItems;
+	delete _controls;
+	delete _camera;
+	delete _backgroundInstances;
+	delete _actorInstances;
+	delete _input;
+	delete _screenText;
+	delete _screen;
+	delete _resSys;
+	delete _dict;
+	
+	debug("Ok");
+	
+	return Common::kNoError;
+}
+
+bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
+	return
+		false;
+		/*
+		(f == kSupportsRTL) ||
+		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsSavingDuringRuntime);
+		*/
+}
+
+#define UPDATEFUNCTION(priority, tag, callback) \
+	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
+		(this, &IllusionsEngine_Duckman::callback));
+
+void IllusionsEngine_Duckman::initUpdateFunctions() {
+	UPDATEFUNCTION(30, 0, updateScript);
+	UPDATEFUNCTION(50, 0, updateActors);
+	UPDATEFUNCTION(60, 0, updateSequences);
+	UPDATEFUNCTION(70, 0, updateGraphics);
+	UPDATEFUNCTION(90, 0, updateSprites);
+	UPDATEFUNCTION(120, 0, updateSoundMan);
+}
+
+#undef UPDATEFUNCTION
+
+int IllusionsEngine_Duckman::updateScript(uint flags) {
+	// TODO Some more stuff
+	_threads->updateThreads();
+	return 1;
+}
+
+void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId) {
+	_screenShaker = new ScreenShaker();
+	_screenShaker->_pointsIndex = 0;
+	_screenShaker->_pointsCount = pointsCount;
+	_screenShaker->_finished = false;
+	_screenShaker->_duration = duration;
+	_screenShaker->_nextTime = duration + getCurrentTime();
+	_screenShaker->_points = points;
+	_screenShaker->_notifyThreadId = threadId;
+	_updateFunctions->add(71, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
+		(this, &IllusionsEngine_Duckman::updateScreenShaker));
+}
+
+int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
+	if (_pauseCtr > 0 || getCurrentScene() == 0x10038) {
+		_screenShaker->_nextTime = getCurrentTime();
+		return 1;
+	}
+
+	if (flags & 1)
+		_screenShaker->_finished = true;
+
+	if (!_screenShaker->_finished) {
+		if (getCurrentTime() >= _screenShaker->_nextTime) {
+			++_screenShaker->_pointsIndex;
+			if (_screenShaker->_pointsIndex <= _screenShaker->_pointsCount) {
+				ScreenShakerPoint shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
+				if (shakePt.x == (int16)0x8000) {
+					// Loop
+					_screenShaker->_pointsIndex = 1;
+					shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
+				}
+				_screenShaker->_nextTime = getCurrentTime() + _screenShaker->_duration;
+				_screen->setScreenOffset(Common::Point(shakePt.x, shakePt.y));
+			} else
+				_screenShaker->_finished = true;
+		}
+	}
+
+	if (_screenShaker->_finished) {
+		notifyThreadId(_screenShaker->_notifyThreadId);
+		delete _screenShaker;
+		_screenShaker = 0;
+		_screen->setScreenOffset(Common::Point(0, 0));
+		return 2;
+	}
+
+	return 1;
+}
+
+void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
+	_fader->_active = true;
+	_fader->_currValue = minValue;
+	_fader->_minValue = minValue;
+	_fader->_maxValue = maxValue;
+	_fader->_firstIndex = firstIndex;
+	_fader->_lastIndex = lastIndex;
+	_fader->_startTime = getCurrentTime();
+	_fader->_duration = duration;
+	_fader->_notifyThreadId = threadId;
+}
+
+void IllusionsEngine_Duckman::setDefaultTextCoords() {
+	WidthHeight dimensions;
+	dimensions._width = 300;
+	dimensions._height = 32;
+	Common::Point pt(160, 176);
+	setDefaultTextDimensions(dimensions);
+	setDefaultTextPosition(pt);
+}
+
+void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
+	//TODO?
+}
+
+void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
+	//TODO?
+}
+
+void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
+	if (threadId) {
+		uint32 tempThreadId = threadId;
+		threadId = 0;
+		_threads->notifyId(tempThreadId);
+	}
+}
+
+bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
+	return
+		control->_objectId == _scriptResource->getMainActorObjectId() &&
+		_input->pollButton(0x20);
+}
+
+bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
+	bool result = false;
+	Control *overlappedControl;
+	if (_controls->getOverlappedWalkObject(control, control->_actor->_position, &overlappedControl)) {
+		if (_currWalkOverlappedControl != overlappedControl) {
+			_currWalkOverlappedControl = overlappedControl;
+			if (runTriggerCause(9, 0, overlappedControl->_objectId)) {
+				delete control->_actor->_pathNode;
+				control->_actor->_flags &= ~0x0400;
+				control->_actor->_pathNode = 0;
+				control->_actor->_pathPoints = 0;
+				control->_actor->_pathPointsCount = 0;
+				_threads->terminateThreadChain(control->_actor->_walkCallerThreadId1);
+				if (control->_actor->_notifyId3C) {
+					notifyThreadId(control->_actor->_notifyId3C);
+					control->_actor->_walkCallerThreadId1 = 0;
+				}
+				result = true;
+			}
+		}
+	} else {
+		_currWalkOverlappedControl = 0;
+	}
+	return result;
+}
+
+Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
+	return _dict->getObjectControl(objectId);
+}
+
+Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
+	Common::Point pt;
+	Common::Point currPan = _camera->getCurrentPan();
+	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt)) {
+		return pt;
+	} else if (namedPointId - 0x00070001 > 209) {
+		if (_controls->findNamedPoint(namedPointId, pt)) {
+			return pt;
+		} else {
+			return currPan;
+		}
+	} else {
+		// TODO
+		debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+		return Common::Point(0, 0);
+	}
+}
+
+uint32 IllusionsEngine_Duckman::getPriorityFromBase(int16 priority) {
+	return priority << 16;
+}
+
+uint32 IllusionsEngine_Duckman::getCurrentScene() {
+	return _activeScenes[_activeScenesCount];
+}
+
+uint32 IllusionsEngine_Duckman::getPrevScene() {
+	uint index = _activeScenesCount - 1;
+	if (_activeScenesCount == 1)
+		index = 5;
+	return _activeScenes[index];
+}
+
+bool IllusionsEngine_Duckman::isCursorObject(uint32 actorTypeId, uint32 objectId) {
+	return actorTypeId == 0x50001;
+}
+
+void IllusionsEngine_Duckman::setCursorControlRoutine(Control *control) {
+	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_Duckman>
+		(this, &IllusionsEngine_Duckman::cursorControlRoutine));
+}
+
+void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequenceId) {
+	_cursor._gameState = 2;
+	_cursor._control = control;
+	_cursor._actorIndex = 1;
+	_cursor._savedActorIndex = 1;
+	_cursor._currOverlappedControl = 0;
+	_cursor._sequenceId1 = sequenceId;
+	_cursor._field14[0] = true;
+	_cursor._field14[1] = true;
+	_cursor._field14[2] = false;
+	_cursor._field14[3] = false;
+	_cursor._field14[4] = false;
+	_cursor._field14[5] = false;
+	_cursor._field14[9] = false;
+	_cursor._field14[10] = false;
+	_cursor._field14[11] = false;
+	_cursor._field14[12] = false;
+	_cursor._field14[6] = _cursor._sequenceId2 != 0 && _cursor._objectId != 0;
+	_cursor._field14[7] = false;
+	_cursor._field14[8] = false;
+	_cursor._op113_choiceOfsPtr = 0;
+	_cursor._notifyThreadId30 = 0;
+	_cursor._dialogItemsCount = 0;
+	_cursor._overlappedObjectId = 0;
+	_cursor._field40 = 0;
+	control->_flags |= 8;
+	setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	// TODO Input_setMousePos(cursorControl->actor->position);
+	// TODO
+	//control->_actor->_actorIndex = 2;
+	// TODO _cursor->place(control, sequenceId);
+}
+
+void IllusionsEngine_Duckman::setCursorControl(Control *control) {
+	_cursor._control = control;
+}
+
+void IllusionsEngine_Duckman::showCursor() {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::hideCursor() {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::initCursor() {
+	_cursor._gameState = 1;
+	_cursor._control = 0;
+	_cursor._position.x = 160;
+	_cursor._position.y = 100;
+	_cursor._objectId = 0;
+	_cursor._actorIndex = 1;
+	_cursor._savedActorIndex = 1;
+	_cursor._currOverlappedControl = 0;
+	_cursor._sequenceId1 = 0;
+	_cursor._sequenceId2 = 0;
+	_cursor._field14[0] = true;
+	_cursor._field14[1] = true;
+	_cursor._field14[2] = false;
+	_cursor._field14[3] = false;
+	_cursor._field14[4] = false;
+	_cursor._field14[5] = false;
+	_cursor._field14[6] = false;
+	_cursor._field14[7] = false;
+	_cursor._field14[8] = false;
+	_cursor._field14[9] = false;
+	_cursor._field14[10] = false;
+	_cursor._field14[11] = false;
+	_cursor._field14[12] = false;
+	_cursor._op113_choiceOfsPtr = 0;
+	_cursor._notifyThreadId30 = 0;
+	_cursor._dialogItemsCount = 0;
+	_cursor._overlappedObjectId = 0;
+	_cursor._field40 = 0;
+}
+
+void IllusionsEngine_Duckman::setCursorActorIndex(int actorIndex, int a, int b) {
+	static int kCursorMap[13][2][2] = {
+		{{ 1,  2}, { 0,  0}},
+		{{ 3,  4}, { 0,  0}},
+		{{ 5,  6}, {13, 14}},
+		{{ 7,  8}, { 0,  0}},
+		{{ 9, 10}, { 0,  0}},
+		{{11, 12}, { 0,  0}},
+		{{ 1,  2}, { 0,  0}},
+		{{ 0,  0}, { 0,  0}},
+		{{ 0,  0}, { 0,  0}},
+		{{15, 16}, { 0,  0}},
+		{{17, 18}, { 0,  0}},
+		{{19, 20}, { 0,  0}},
+		{{21, 22}, { 0,  0}}
+	};
+	_cursor._control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
+}
+
+void IllusionsEngine_Duckman::enableCursorVerb(int verbNum) {
+	if (verbNum != 7 || _cursor._sequenceId2)
+		_cursor._field14[verbNum - 1] = true;
+}
+
+void IllusionsEngine_Duckman::disableCursorVerb(int verbNum) {
+	_cursor._field14[verbNum - 1] = false;
+	if (_cursor._actorIndex == verbNum) {
+		_cursor._actorIndex = getCursorActorIndex();
+		setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		startCursorSequence();
+		_cursor._currOverlappedControl = 0;
+	}
+}
+
+void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
+	if (mode == 1) {
+		enableCursorVerb(4);
+		disableCursorVerb(1);
+		disableCursorVerb(2);
+		disableCursorVerb(7);
+		_cursor._actorIndex = 4;
+	} else {
+		enableCursorVerb(1);
+		enableCursorVerb(2);
+		enableCursorVerb(7);
+		disableCursorVerb(4);
+		_cursor._actorIndex = 1;
+	}
+	_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+	if (_cursor._currOverlappedControl)
+		setCursorActorIndex(_cursor._actorIndex, 2, 0);
+	else
+		setCursorActorIndex(_cursor._actorIndex, 1, 0);
+}
+
+void IllusionsEngine_Duckman::setCursorInventoryMode(int mode, int value) {
+	_cursor._control = _cursor._control;
+	if (mode == 1) {
+		_savedInventoryActorIndex = _cursor._actorIndex;
+		if (_cursor._actorIndex == 3 || _cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
+			_cursor._savedActorIndex = _cursor._savedActorIndex;
+			if (_cursor._savedActorIndex == 1 || _cursor._savedActorIndex == 2 || _cursor._savedActorIndex == 7)
+				_savedInventoryActorIndex = _cursor._savedActorIndex;
+			else
+				_savedInventoryActorIndex = 0;
+		}
+		if (value == 1 && _cursor._objectId && _savedInventoryActorIndex != 7) {
+			_cursor._actorIndex = 7;
+			stopCursorHoldingObject();
+			_cursor._actorIndex = _savedInventoryActorIndex;
+		}
+	} else if (mode == 2) {
+		if (_savedInventoryActorIndex)
+			_cursor._actorIndex = _savedInventoryActorIndex;
+		else
+			_cursor._actorIndex = 1;
+		if (_cursor._actorIndex == 7)
+			_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
+		else
+			_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		_savedInventoryActorIndex = 0;
+	}
+}
+
+void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 sequenceId) {
+	_cursor._objectId = objectId;
+	_cursor._sequenceId2 = sequenceId;
+	_cursor._actorIndex = 7;
+	_cursor._savedActorIndex = 7;
+	_cursor._field14[_cursor._actorIndex - 1] = true;
+	_cursor._control->startSequenceActor(sequenceId, 2, 0);
+	setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	_cursor._currOverlappedControl = 0;
+}
+
+void IllusionsEngine_Duckman::stopCursorHoldingObject() {
+	_cursor._field14[6] = false;
+	_cursor._objectId = 0;
+	_cursor._sequenceId2 = 0;
+	if (_cursor._actorIndex == 7) {
+		_cursor._actorIndex = getCursorActorIndex();
+		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	}
+}
+
+void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
+	control->_actor->_seqCodeValue1 = 100 * deltaTime;
+	if (control->_actor->_flags & 1) {
+		switch (_cursor._gameState) {
+		case 2:
+			updateGameState2();
+			break;
+		case 3:
+			updateDialogState();
+			break;
+		case 4:
+			// TODO ShellMgr_update(_cursor._control);
+			break;
+		}
+	}
+}
+
+void IllusionsEngine_Duckman::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
+	startScriptThread(threadId, callingThreadId);
+}
+
+void IllusionsEngine_Duckman::startScriptThread(uint32 threadId, uint32 callingThreadId) {
+	debug(2, "Starting script thread %08X", threadId);
+	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
+	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp);
+}
+
+uint32 IllusionsEngine_Duckman::startAbortableTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, true);
+}
+
+uint32 IllusionsEngine_Duckman::startTimerThread(uint32 duration, uint32 threadId) {
+	return newTimerThread(duration, threadId, false);
+}
+
+uint32 IllusionsEngine_Duckman::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting abortable thread %08X", tempThreadId);
+	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
+	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
+		scriptThreadId, scriptCodeIp2);
+	_threads->startThread(abortableThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_Duckman::startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
+	uint32 sequenceId2, uint32 callingThreadId) {
+	debug(2, "Starting talk thread");
+	uint32 tempThreadId = newTempThreadId();
+	TalkThread_Duckman *talkThread = new TalkThread_Duckman(this, tempThreadId, callingThreadId, 0,
+		objectId, talkId, sequenceId1, sequenceId2);
+	_threads->startThread(talkThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_Duckman::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+	uint32 value8, uint32 valueC, uint32 value10) {
+	uint32 tempThreadId = newTempThreadId();
+	debug(2, "Starting temp script thread %08X", tempThreadId);
+	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp);
+	return tempThreadId;
+}
+
+void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+	byte *scriptCodeIp) {
+	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
+		scriptCodeIp, 0, 0, 0);
+	_threads->startThread(scriptThread);
+}
+
+uint32 IllusionsEngine_Duckman::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
+	uint32 tempThreadId = newTempThreadId();
+	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
+		duration, isAbortable);
+	_threads->startThread(timerThread);
+	return tempThreadId;
+}
+
+uint32 IllusionsEngine_Duckman::newTempThreadId() {
+	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
+	if (threadId > 65535) {
+		_nextTempThreadId = 0;
+		threadId = 2 * _scriptResource->_codeCount;
+	}
+	++_nextTempThreadId;
+	return 0x00020000 | threadId;
+}
+
+void IllusionsEngine_Duckman::initActiveScenes() {
+	_activeScenesCount = 0;
+	_activeScenes[0] = 0xEFEF;
+	pushActiveScene(0x10000);
+}
+
+void IllusionsEngine_Duckman::pushActiveScene(uint32 sceneId) {
+	++_activeScenesCount;
+	if (_activeScenesCount >= 6)
+		_activeScenesCount = 1;
+	_activeScenes[_activeScenesCount] = sceneId;
+}
+
+void IllusionsEngine_Duckman::popActiveScene() {
+	--_activeScenesCount;
+	if (_activeScenesCount == 0)
+		_activeScenesCount = 5;
+}
+
+bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (!progInfo)
+		return false;
+	pushActiveScene(sceneId);
+	uint resourcesCount;
+	uint32 *resources;
+	progInfo->getResources(resourcesCount, resources);
+	for (uint i = 0; i < resourcesCount; ++i)
+		_resSys->loadResource(resources[i], sceneId, 0);
+	return true;
+}
+
+bool IllusionsEngine_Duckman::enterScene(uint32 sceneId, uint32 threadId) {
+	if (loadScene(sceneId)) {
+		if (threadId)
+			startScriptThread(threadId, 0);
+		return true;
+	}
+	// TODO startScriptThread2(0x10002, 0x20001, 0);
+	return false;
+}
+
+void IllusionsEngine_Duckman::exitScene() {
+	popActiveScene();
+}
+
+bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId) {
+	uint32 currSceneId = getCurrentScene();
+	if (currSceneId != 0x10003)
+		dumpCurrSceneFiles(currSceneId, callerThreadId);
+	_threads->terminateThreads(callerThreadId);
+	_controls->destroyControls();
+	_resSys->unloadSceneResources(0x10003, 0x10001);
+	if (enterScene(sceneId, threadId)) {
+		// TODO GameStates_writeStates(sceneId, threadId);
+		return true;
+	}
+	return false;
+}
+
+void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
+	_threads->suspendThreads(threadId);
+	_controls->pauseControls();
+	_actorInstances->pauseByTag(sceneId);
+	_backgroundInstances->pauseByTag(sceneId);
+}
+
+void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
+	_backgroundInstances->unpauseByTag(sceneId);
+	_actorInstances->unpauseByTag(sceneId);
+	_controls->unpauseControls();
+	_threads->notifyThreads(threadId);
+}
+
+void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
+	// TODO
+}
+
+void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
+	// TODO UpdateFunctions_disableByTag(sceneId);
+	_threads->terminateActiveThreads(threadId);
+	_threads->terminateThreadsByTag(sceneId, threadId);
+	_controls->destroyActiveControls();
+	_resSys->unloadResourcesByTag(sceneId);
+}
+
+void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
+	_theSceneId = theSceneId;
+	_theThreadId = theThreadId;
+}
+
+bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
+	if (progInfo)
+		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	return false;
+}
+
+void IllusionsEngine_Duckman::reset() {
+	_scriptResource->_blockCounters.clear();
+	_scriptResource->_properties.clear();
+	// TODO script_sub_417FF0(1, 0);
+}
+
+uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
+	return _scriptResource->getObjectActorTypeId(objectId);
+}
+
+Common::Point IllusionsEngine_Duckman::convertMousePos(Common::Point mousePos) {
+	Common::Point screenOffsPt = _camera->getScreenOffset();
+	mousePos.x += screenOffsPt.x;
+	mousePos.y += screenOffsPt.y;
+	return mousePos;
+}
+
+void IllusionsEngine_Duckman::startCursorSequence() {
+	// NOTE Calls to startCursorSequence were put after calls to setCursorActorIndex
+	// to make the cursor switch more immediate. In the original these calls are swapped.
+	if (_cursor._actorIndex == 7)
+		_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
+	else
+		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+}
+
+int IllusionsEngine_Duckman::getCursorActorIndex() {
+	int result = _cursor._actorIndex;
+	do {
+		++result;
+		if (result > 13)
+			result = 1;
+	} while (!_cursor._field14[result - 1]);
+	return result;
+}
+
+void IllusionsEngine_Duckman::updateGameState2() {
+	Common::Point cursorPos = _input->getCursorPosition();
+	Common::Point convMousePos = convertMousePos(cursorPos);
+	int trackCursorIndex = -1;
+	bool foundOverlapped;
+	Control *overlappedControl;
+
+	_cursor._control->_actor->_position = cursorPos;
+
+	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
+
+	if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
+		trackCursorIndex = 10;
+	} else if (cursorPos.y >= 192 && !_camera->isAtPanLimit(2)) {
+		trackCursorIndex = 11;
+	} else if (cursorPos.x < 8 && !_camera->isAtPanLimit(3)) {
+		trackCursorIndex = 12;
+	} else if (cursorPos.x >= 312 && !_camera->isAtPanLimit(4)) {
+		trackCursorIndex = 13;
+	} else if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
+		_cursor._actorIndex = _cursor._savedActorIndex;
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		startCursorSequence();
+	}
+
+	if (trackCursorIndex >= 0) {
+		if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
+			_cursor._savedActorIndex = _cursor._actorIndex;
+		if (_cursor._actorIndex != trackCursorIndex) {
+			_cursor._actorIndex = trackCursorIndex;
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+			startCursorSequence();
+		}
+		_cursor._currOverlappedControl = 0;
+		foundOverlapped = false;
+	}
+
+	if (foundOverlapped) {
+		if (_cursor._currOverlappedControl != overlappedControl) {
+			int cursorValue2 = 0;
+			if (overlappedControl->_flags & 2) {
+				if (_cursor._actorIndex != 3) {
+					_cursor._savedActorIndex = _cursor._actorIndex;
+					_cursor._actorIndex = 3;
+				}
+				if (overlappedControl->_flags & 0x40)
+					cursorValue2 = 1;
+			} else if (_cursor._actorIndex == 3) {
+				_cursor._actorIndex = _cursor._savedActorIndex;
+			}
+			setCursorActorIndex(_cursor._actorIndex, 2, cursorValue2);
+			startCursorSequence();
+			_cursor._currOverlappedControl = overlappedControl;
+		}
+	} else if (_cursor._currOverlappedControl) {
+		if (_cursor._actorIndex == 3)
+			_cursor._actorIndex = _cursor._savedActorIndex;
+		setCursorActorIndex(_cursor._actorIndex, 1, 0);
+		startCursorSequence();
+		_cursor._currOverlappedControl = 0;
+	}
+
+	if (_input->pollButton(1)) {
+		if (_cursor._currOverlappedControl) {
+			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
+		} else {
+			_cursor._position = convertMousePos(_cursor._control->_actor->_position);
+			// TODO clipMousePos(&_cursor._position);
+			if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13)
+				runTriggerCause(1, _cursor._objectId, 0x40003);
+			else
+				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
+		}
+	} else if (_input->pollButton(2)) {
+		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
+			int newActorIndex = getCursorActorIndex();
+			if (newActorIndex != _cursor._actorIndex) {
+				_cursor._actorIndex = newActorIndex;
+				if (_cursor._currOverlappedControl)
+					setCursorActorIndex(_cursor._actorIndex, 2, 0);
+				else
+					setCursorActorIndex(_cursor._actorIndex, 1, 0);
+				startCursorSequence();
+			}
+		}
+	} else if (_input->pollButton(8)) {
+		if (_cursor._field14[0] == 1) {
+			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
+		} else if (_cursor._field14[1] == 1) {
+			runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
+		}
+	}
+
+}
+
+void IllusionsEngine_Duckman::playSoundEffect(int index) {
+	uint32 soundEffectId = 0;
+	uint32 *soundIds = _scriptResource->_soundIds;
+	switch (index) {
+	case 1:
+		soundEffectId = soundIds[0];
+		break;
+	case 2:
+		soundEffectId = soundIds[1];
+		break;
+	case 3:
+		soundEffectId = soundIds[2];
+		break;
+	case 4:
+		soundEffectId = soundIds[3];
+		break;
+	case 5:
+		soundEffectId = soundIds[4];
+		break;
+	case 6:
+		soundEffectId = soundIds[getRandom(4) + 5];
+		break;
+	case 7:
+		soundEffectId = soundIds[getRandom(4) + 9];
+		break;
+	case 8:
+		soundEffectId = soundIds[13];
+		break;
+	case 9:
+		soundEffectId = soundIds[14];
+		break;
+	case 10:
+		soundEffectId = soundIds[15];
+		break;
+	case 11:
+		soundEffectId = soundIds[16];
+		break;
+	case 12:
+		soundEffectId = soundIds[getRandom(4) + 17];
+		break;
+	case 13:
+		soundEffectId = soundIds[21];
+		break;
+	case 14:
+		soundEffectId = soundIds[22];
+		break;
+	case 15:
+		soundEffectId = soundIds[23];
+		break;
+	case 16:
+		soundEffectId = soundIds[24];
+		break;
+	case 17:
+		soundEffectId = soundIds[25];
+		break;
+	case 18:
+		soundEffectId = soundIds[26];
+		break;
+	}
+	if (soundEffectId)
+		_soundMan->playSound(soundEffectId, 255, 0);
+}
+
+bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
+	ProgInfo *progInfo = _scriptResource->getProgInfo(getCurrentScene() & 0xFFFF);
+	bool found =
+		progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
+		progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
+	if (!found) {
+		progInfo = _scriptResource->getProgInfo(3);
+		found =
+			progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
+			progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
+	}
+	return found;
+}
+
+uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
+	// TODO
+	debug("runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
+	uint32 triggerThreadId;
+
+	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
+		return 0;
+
+	bool flag = false;
+	if (_scriptResource->_properties.get(0x000E003C)) {
+		if (verbId == 7 && objectId == 0x40003) {
+			playSoundEffect(7);
+			flag = true;
+		} else if (objectId == 0x40003) {
+			playSoundEffect(14);
+			flag = true;
+		} else if (verbId == 3) {
+			playSoundEffect(16);
+			flag = true;
+		} else if (verbId == 2) {
+			flag = true;
+		}
+	}
+
+	if (!flag) {
+		if (objectId == 0x40003) {
+			playSoundEffect(14);
+		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) {
+			playSoundEffect(15);
+		} else if (verbId == 7 && _scriptResource->getMainActorObjectId() == objectId) {
+			playSoundEffect(15);
+		} else if (verbId == 1) {
+			playSoundEffect(1);
+		} else if (verbId == 2) {
+			playSoundEffect(2);
+		} else if (verbId == 3) {
+			playSoundEffect(3);
+		} else if (verbId == 4 || verbId == 7) {
+			playSoundEffect(4);
+		} else if (verbId == 9) {
+			playSoundEffect(5);
+		}
+	}
+
+	uint32 tempThreadId = newTempThreadId();
+	debug("Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
+	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
+		triggerThreadId);
+	_threads->startThread(causeThread);
+
+	return tempThreadId;
+}
+
+void IllusionsEngine_Duckman::addDialogItem(int16 choiceJumpOffs, uint32 sequenceId) {
+	DialogItem dialogItem;
+	dialogItem._choiceJumpOffs = choiceJumpOffs;
+	dialogItem._sequenceId = sequenceId;
+	_dialogItems.push_back(dialogItem);
+}
+
+void IllusionsEngine_Duckman::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId) {
+	static const uint32 kDialogSequenceIds[] = {
+		0,
+		0x6049C, 0x6049C, 0x6047A, 0x6049D,
+		0x60479, 0x6049E, 0x6049F, 0x60468
+	};
+	if (_dialogItems.size() == 1) {
+		*choiceOfsPtr = _dialogItems[0]._choiceJumpOffs;
+		notifyThreadId(callerThreadId);
+	} else {
+		if (!_cursor._control) {
+			Common::Point pos = getNamedPointPosition(0x70001);
+			_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
+			_cursor._control = _dict->getObjectControl(0x40004);
+		}
+		_cursor._control->appearActor();
+		setCursorActorIndex(6, 1, 0);
+
+		_cursor._gameState = 3;
+		_cursor._notifyThreadId30 = callerThreadId;
+		_cursor._dialogItemsCount = 0;
+		_cursor._overlappedObjectId = 0;
+		_cursor._op113_choiceOfsPtr = choiceOfsPtr;
+		_cursor._currOverlappedControl = 0;
+
+		/* TODO?
+		if (!_input->getCursorMouseMode())
+			_input->setMousePos((Point)0xBC0014);
+		*/
+
+		_cursor._dialogItemsCount = _dialogItems.size();
+		Common::Point placePt(20, 188);
+
+		for (uint i = 1; i <= _dialogItems.size(); ++i) {
+			DialogItem &dialogItem = _dialogItems[_dialogItems.size() - i];
+			_controls->placeDialogItem(i + 1, actorTypeId, dialogItem._sequenceId, placePt, dialogItem._choiceJumpOffs);
+			placePt.x += 40;
+		}
+
+		Common::Point placePt2 = getNamedPointPosition(0x700C3);
+		_controls->placeActor(0x5006E, placePt2, kDialogSequenceIds[_dialogItems.size()], 0x40148, 0);
+		Control *control = _dict->getObjectControl(0x40148);
+		control->_flags |= 8;
+		playSoundEffect(8);
+	}
+
+	_dialogItems.clear();
+
+}
+
+void IllusionsEngine_Duckman::updateDialogState() {
+	Common::Point mousePos = _input->getCursorPosition();
+	// TODO Handle keyboard input
+	_cursor._control->_actor->_position = mousePos;
+	mousePos = convertMousePos(mousePos);
+
+	Control *currOverlappedControl = _cursor._currOverlappedControl;
+	Control *newOverlappedControl;
+	
+	if (_controls->getDialogItemAtPos(_cursor._control, mousePos, &newOverlappedControl)) {
+		if (currOverlappedControl != newOverlappedControl) {
+			newOverlappedControl->setActorIndex(2);
+			newOverlappedControl->startSequenceActor(newOverlappedControl->_actor->_sequenceId, 2, 0);
+			if (currOverlappedControl) {
+				currOverlappedControl->setActorIndex(1);
+				currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
+			}
+			playSoundEffect(10);
+			startCursorSequence();
+			setCursorActorIndex(6, 2, 0);
+			_cursor._currOverlappedControl = newOverlappedControl;
+			_cursor._overlappedObjectId = newOverlappedControl->_objectId;
+		}
+	} else if (currOverlappedControl) {
+		currOverlappedControl->setActorIndex(1);
+		currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
+		playSoundEffect(10);
+		_cursor._currOverlappedControl = 0;
+		_cursor._overlappedObjectId = 0;
+		startCursorSequence();
+		setCursorActorIndex(6, 1, 0);
+	}
+
+	if (_input->pollButton(1)) {
+		if (_cursor._currOverlappedControl) {
+			playSoundEffect(9);
+			*_cursor._op113_choiceOfsPtr = _cursor._currOverlappedControl->_actor->_choiceJumpOffs;
+			_controls->destroyDialogItems();
+			Control *control = _dict->getObjectControl(0x40148);
+			_controls->destroyControl(control);
+			notifyThreadId(_cursor._notifyThreadId30);
+			_cursor._notifyThreadId30 = 0;
+			_cursor._gameState = 2;
+			_cursor._dialogItemsCount = 0;
+			_cursor._overlappedObjectId = 0;
+			_cursor._op113_choiceOfsPtr = 0;
+			_cursor._control->disappearActor();
+		}
+	}
+
+}
+
+void IllusionsEngine_Duckman::initInventory() {
+	_inventorySlots.push_back(DMInventorySlot( 64,  52));
+	_inventorySlots.push_back(DMInventorySlot(112,  52));
+	_inventorySlots.push_back(DMInventorySlot(160,  52));
+	_inventorySlots.push_back(DMInventorySlot(208,  52));
+	_inventorySlots.push_back(DMInventorySlot(255,  52));
+	_inventorySlots.push_back(DMInventorySlot( 64,  84));
+	_inventorySlots.push_back(DMInventorySlot(112,  84));
+	_inventorySlots.push_back(DMInventorySlot(160,  84));
+	_inventorySlots.push_back(DMInventorySlot(208,  84));
+	_inventorySlots.push_back(DMInventorySlot(255,  84));
+	_inventorySlots.push_back(DMInventorySlot( 64, 116));
+	_inventorySlots.push_back(DMInventorySlot(112, 116));
+	_inventorySlots.push_back(DMInventorySlot(160, 116));
+	_inventorySlots.push_back(DMInventorySlot(208, 116));
+	_inventorySlots.push_back(DMInventorySlot(255, 116));
+	_inventorySlots.push_back(DMInventorySlot( 64, 148));
+	_inventorySlots.push_back(DMInventorySlot(112, 148));
+	_inventorySlots.push_back(DMInventorySlot(160, 148));
+	_inventorySlots.push_back(DMInventorySlot(208, 148));
+	_inventorySlots.push_back(DMInventorySlot(255, 148));
+	_inventoyItems.push_back(DMInventoryItem(0x40011, 0xE005B));
+	_inventoyItems.push_back(DMInventoryItem(0x40099, 0xE001B));
+	_inventoyItems.push_back(DMInventoryItem(0x4000F, 0xE000C));
+	_inventoyItems.push_back(DMInventoryItem(0x40042, 0xE0012));
+	_inventoyItems.push_back(DMInventoryItem(0x40044, 0xE000F));
+	_inventoyItems.push_back(DMInventoryItem(0x40029, 0xE000D));
+	_inventoyItems.push_back(DMInventoryItem(0x400A7, 0xE005D));
+	_inventoyItems.push_back(DMInventoryItem(0x40096, 0xE001C));
+	_inventoyItems.push_back(DMInventoryItem(0x40077, 0xE0010));
+	_inventoyItems.push_back(DMInventoryItem(0x4008A, 0xE0033));
+	_inventoyItems.push_back(DMInventoryItem(0x4004B, 0xE0045));
+	_inventoyItems.push_back(DMInventoryItem(0x40054, 0xE0021));
+	_inventoyItems.push_back(DMInventoryItem(0x400C6, 0xE005A));
+	_inventoyItems.push_back(DMInventoryItem(0x4000B, 0xE005E));
+	_inventoyItems.push_back(DMInventoryItem(0x4005F, 0xE0016));
+	_inventoyItems.push_back(DMInventoryItem(0x40072, 0xE0017));
+	_inventoyItems.push_back(DMInventoryItem(0x400AA, 0xE005F));
+	_inventoyItems.push_back(DMInventoryItem(0x400B8, 0xE0050));
+	_inventoyItems.push_back(DMInventoryItem(0x4001F, 0xE001A));
+	_inventoyItems.push_back(DMInventoryItem(0x40095, 0xE0060));
+	_inventoyItems.push_back(DMInventoryItem(0x40041, 0xE0053));
+	_savedInventoryActorIndex = 0;
+}
+
+void IllusionsEngine_Duckman::openInventory() {
+
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
+		DMInventorySlot *inventorySlot = &_inventorySlots[i];
+		if (inventorySlot->_objectId) {
+			DMInventoryItem *inventoryItem = findInventoryItem(inventorySlot->_objectId);
+			if (!_scriptResource->_properties.get(inventoryItem->_propertyId))
+				inventorySlot->_objectId = 0;
+		}
+	}
+
+	for (uint i = 0; i < _inventoyItems.size(); ++i) {
+		DMInventoryItem *inventoryItem = &_inventoyItems[i];
+		if (_scriptResource->_properties.get(inventoryItem->_propertyId)) {
+			DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
+			if (inventorySlot) {
+				Control *control = getObjectControl(inventoryItem->_objectId);
+				control->setActorPosition(inventorySlot->_position);
+				control->appearActor();
+			} else {
+				addInventoryItem(inventoryItem->_objectId);
+			}
+		}
+	}
+
+}
+
+void IllusionsEngine_Duckman::addInventoryItem(uint32 objectId) {
+	DMInventorySlot *DMInventorySlot = findInventorySlot(0);
+	DMInventorySlot->_objectId = objectId;
+	Control *control = getObjectControl(objectId);
+	control->setActorPosition(DMInventorySlot->_position);
+	control->appearActor();
+}
+
+void IllusionsEngine_Duckman::clearInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]._objectId == objectId)
+			_inventorySlots[i]._objectId = 0;
+}
+
+void IllusionsEngine_Duckman::putBackInventoryItem() {
+	Common::Point mousePos = _input->getCursorPosition();
+	if (_cursor._objectId) {
+		DMInventorySlot *inventorySlot = findInventorySlot(_cursor._objectId);
+		if (inventorySlot)
+			inventorySlot->_objectId = 0;
+		inventorySlot = findClosestInventorySlot(mousePos);
+		inventorySlot->_objectId = _cursor._objectId;
+		Control *control = getObjectControl(_cursor._objectId);
+		control->setActorPosition(inventorySlot->_position);
+		control->appearActor();
+		_cursor._actorIndex = 7;
+		stopCursorHoldingObject();
+		_cursor._actorIndex = 2;
+		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
+		if (_cursor._currOverlappedControl)
+			setCursorActorIndex(_cursor._actorIndex, 2, 0);
+		else
+			setCursorActorIndex(_cursor._actorIndex, 1, 0);
+	}
+}
+
+DMInventorySlot *IllusionsEngine_Duckman::findInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]._objectId == objectId)
+			return &_inventorySlots[i];
+	return 0;
+}
+
+DMInventoryItem *IllusionsEngine_Duckman::findInventoryItem(uint32 objectId) {
+	for (uint i = 0; i < _inventoyItems.size(); ++i)
+		if (_inventoyItems[i]._objectId == objectId)
+			return &_inventoyItems[i];
+	return 0;
+}
+
+DMInventorySlot *IllusionsEngine_Duckman::findClosestInventorySlot(Common::Point pos) {
+	int minDistance = 0xFFFFFF;
+	DMInventorySlot *minInventorySlot = 0;
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
+		DMInventorySlot *inventorySlot = &_inventorySlots[i];
+		if (inventorySlot->_objectId == 0) {
+			int16 deltaX = ABS(inventorySlot->_position.x - pos.x);
+			int16 deltaY = ABS(inventorySlot->_position.y - pos.y);
+			int distance = deltaX * deltaX + deltaY * deltaY;
+			if (inventorySlot->_objectId == 0 && distance < minDistance) {
+				minDistance = distance;
+				minInventorySlot = inventorySlot;
+			}
+		}
+	}
+	return minInventorySlot;
+}
+
+void IllusionsEngine_Duckman::addPropertyTimer(uint32 propertyId) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer) || findPropertyTimer(0, propertyTimer)) {
+		propertyTimer->_propertyId = propertyId;
+		propertyTimer->_startTime = 0;
+		propertyTimer->_duration = 0;
+		propertyTimer->_endTime = 0;
+	}
+}
+
+void IllusionsEngine_Duckman::setPropertyTimer(uint32 propertyId, uint32 duration) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer)) {
+		propertyTimer->_startTime = getCurrentTime();
+		propertyTimer->_duration = duration;
+		propertyTimer->_endTime = duration + propertyTimer->_startTime;
+	}
+	_scriptResource->_properties.set(propertyId, false);
+	if (!_propertyTimersActive) {
+		_updateFunctions->add(29, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
+			(this, &IllusionsEngine_Duckman::updatePropertyTimers));
+		_propertyTimersActive = true;
+	}
+}
+
+void IllusionsEngine_Duckman::removePropertyTimer(uint32 propertyId) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer))
+		propertyTimer->_propertyId = 0;
+	_scriptResource->_properties.set(propertyId, true);
+}
+
+bool IllusionsEngine_Duckman::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
+	for (uint i = 0; i < kPropertyTimersCount; ++i)
+		if (_propertyTimers[i]._propertyId == propertyId) {
+			propertyTimer = &_propertyTimers[i];
+			return true;
+		}
+	return false;
+}
+
+int IllusionsEngine_Duckman::updatePropertyTimers(uint flags) {
+	int result = 1;
+	uint32 currTime = getCurrentTime();
+	if (_pauseCtr <= 0) {
+		if (_propertyTimersPaused) {
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				propertyTimer._startTime = currTime;
+				propertyTimer._endTime = currTime + propertyTimer._duration;
+			}
+			_propertyTimersPaused = false;
+		}
+		if (flags & 1) {
+			_propertyTimersActive = false;
+			_propertyTimersPaused = false;
+			result = 2;
+		} else {
+			bool timersActive = false;
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				if (propertyTimer._propertyId) {
+					timersActive = true;
+					if (!_scriptResource->_properties.get(propertyTimer._propertyId) &&
+						isTimerExpired(propertyTimer._startTime, propertyTimer._endTime))
+						_scriptResource->_properties.set(propertyTimer._propertyId, true);
+				}
+			}
+			if (!timersActive) {
+				_propertyTimersActive = false;
+				_propertyTimersPaused = false;
+				result = 2;
+			}
+		}
+	} else {
+		if (!_propertyTimersPaused) {
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				propertyTimer._duration -= getDurationElapsed(propertyTimer._startTime, propertyTimer._endTime);
+			}
+			_propertyTimersPaused = true;
+		}
+		result = 1;
+	}
+	return result;
+}
+
+// Special code
+
+typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
+#define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &IllusionsEngine_Duckman::func);
+
+void IllusionsEngine_Duckman::initSpecialCode() {
+	SPECIAL(0x00160001, spcStartScreenShaker);
+	SPECIAL(0x00160002, spcSetCursorHandMode);
+	SPECIAL(0x00160003, spcResetChinesePuzzle);
+	SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
+	SPECIAL(0x00160005, spcOpenInventory);
+	SPECIAL(0x00160007, spcPutBackInventoryItem);
+	SPECIAL(0x00160008, spcClearInventorySlot);
+	SPECIAL(0x0016000A, spcAddPropertyTimer);
+	SPECIAL(0x0016000B, spcSetPropertyTimer);
+	SPECIAL(0x0016000C, spcRemovePropertyTimer);
+	SPECIAL(0x00160010, spcCenterNewspaper);
+	SPECIAL(0x00160014, spcUpdateObject272Sequence);
+	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
+}
+
+#undef SPECIAL
+
+void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCall) {
+	SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
+	if (it != _specialCodeMap.end()) {
+		(*(*it)._value)(opCall);
+	} else {
+		debug("IllusionsEngine_Duckman::runSpecialCode() Unimplemented special code %08X", specialCodeId);
+		notifyThreadId(opCall._threadId);
+	}
+}
+
+// TODO Move to separate file
+
+static const ScreenShakerPoint kShakerPoints0[] = {
+	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
+};
+
+static const ScreenShakeEffect kShakerEffect0 = {
+	6, 5, kShakerPoints0
+};
+
+static const ScreenShakerPoint kShakerPoints1[] = {
+	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
+	{ 1,  2}, {0, -1} 
+};
+
+static const ScreenShakeEffect kShakerEffect1 = {
+	9, 2, kShakerPoints1
+};
+
+static const ScreenShakerPoint kShakerPoints2[] = {
+	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
+	{0,  1}, {0, -1},
+};
+
+static const ScreenShakeEffect kShakerEffect2 = {
+	9, 2, kShakerPoints2
+};
+
+static const ScreenShakerPoint kShakerPoints3[] = {
+	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect3 = {
+	5, 2, kShakerPoints3
+};
+
+static const ScreenShakerPoint kShakerPoints4[] = {
+	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
+};
+
+static const ScreenShakeEffect kShakerEffect4 = {
+	8, 5, kShakerPoints4
+};
+
+static const ScreenShakerPoint kShakerPoints5[] = {
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect5 = {
+	31, 2, kShakerPoints5
+};
+
+static const ScreenShakeEffect *kShakerEffects[] = {
+	&kShakerEffect0,
+	&kShakerEffect1,
+	&kShakerEffect2,
+	&kShakerEffect3,
+	&kShakerEffect4,
+	&kShakerEffect5
+};
+
+void IllusionsEngine_Duckman::spcStartScreenShaker(OpCall &opCall) {
+	ARG_BYTE(effect);
+	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
+	startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	setCursorHandMode(mode);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcResetChinesePuzzle(OpCall &opCall) {
+	_scriptResource->_properties.set(0x000E0018, false);
+	_scriptResource->_properties.set(0x000E0019, false);
+	_chinesePuzzleIndex = 0;
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcAddChinesePuzzleAnswer(OpCall &opCall) {
+	ARG_BYTE(answer);
+	_chinesePuzzleAnswers[_chinesePuzzleIndex++] = answer;
+	if (_chinesePuzzleIndex == 3) {
+		_scriptResource->_properties.set(0x000E0018, true);
+		if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 5) ||
+			(_chinesePuzzleAnswers[0] == 5 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
+			_scriptResource->_properties.set(0x000E0019, true);
+		else if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 1) ||
+			(_chinesePuzzleAnswers[0] == 1 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
+			_scriptResource->_properties.set(0x000E00A0, true);
+	}
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
+	openInventory();
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcPutBackInventoryItem(OpCall &opCall) {
+	putBackInventoryItem();
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcClearInventorySlot(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	clearInventorySlot(objectId);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcAddPropertyTimer(OpCall &opCall) {
+	ARG_UINT32(propertyId);
+	addPropertyTimer(propertyId);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcSetPropertyTimer(OpCall &opCall) {
+	ARG_INT16(propertyNum);
+	ARG_INT16(duration);
+	setPropertyTimer(propertyNum | 0xE0000, duration);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcRemovePropertyTimer(OpCall &opCall) {
+	ARG_UINT32(propertyId);
+	removePropertyTimer(propertyId);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcCenterNewspaper(OpCall &opCall) {
+	Control *control = getObjectControl(0x40017);
+	control->_flags |= 8;
+	control->_actor->_position.x = 160;
+	control->_actor->_position.y = 100;
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcSetCursorInventoryMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	ARG_BYTE(value);
+	setCursorInventoryMode(mode, value);
+	notifyThreadId(opCall._threadId);
+}
+
+void IllusionsEngine_Duckman::spcUpdateObject272Sequence(OpCall &opCall) {
+	byte flags = 0;
+	uint32 sequenceId;
+	if (_scriptResource->_properties.get(0x000E0085))
+		flags |= 1;
+	if (_scriptResource->_properties.get(0x000E0083))
+		flags |= 2;
+	if (_scriptResource->_properties.get(0x000E0084))
+		flags |= 4;
+	switch (flags) {
+	case 0:
+		sequenceId = 0x603C1;
+		break;
+	case 1:
+		sequenceId = 0x603BF;
+		break;
+	case 2:
+		sequenceId = 0x603C2;
+		break;
+	case 3:
+		sequenceId = 0x603C0;
+		break;
+	case 4:
+		sequenceId = 0x603C3;
+		break;
+	case 5:
+		sequenceId = 0x603C5;
+		break;
+	case 6:
+		sequenceId = 0x603C4;
+		break;
+	case 7:
+		sequenceId = 0x603C6;
+		break;
+	default:
+		sequenceId = 0x603C1;
+		break;
+	}
+	Control *control = getObjectControl(0x40110);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
new file mode 100644
index 0000000..7f6c427
--- /dev/null
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -0,0 +1,264 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_ILLUSIONS_DUCKMAN_H
+#define ILLUSIONS_ILLUSIONS_DUCKMAN_H
+
+#include "illusions/illusions.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+class Dictionary;
+class ScriptStack;
+
+struct Cursor_Duckman {
+	int _gameState;
+	Control *_control;
+	Common::Point _position;
+	uint32 _objectId;
+	int _actorIndex;
+	int _savedActorIndex;
+	bool _field14[14];
+	Control *_currOverlappedControl;
+	uint32 _sequenceId1;
+	uint32 _sequenceId2;
+	uint32 _notifyThreadId30;
+	int16 *_op113_choiceOfsPtr;
+	int _dialogItemsCount;
+	uint32 _overlappedObjectId;
+	uint32 _field3C;
+	uint32 _field40;
+};
+
+struct DialogItem {
+	int16 _choiceJumpOffs;
+	uint32 _sequenceId;
+};
+
+struct DMInventorySlot {
+	Common::Point _position;
+	uint32 _objectId;
+	DMInventorySlot() : _objectId(0) {}
+	DMInventorySlot(int16 x, int16 y) : _objectId(0), _position(x, y) {}
+};
+
+struct DMInventoryItem {
+	uint32 _objectId;
+	uint32 _propertyId;
+	DMInventoryItem() : _objectId(0) {}
+	DMInventoryItem(uint32 objectId, uint32 propertyId)
+		: _objectId(objectId), _propertyId(propertyId) {}
+};
+
+struct ScreenShakerPoint {
+	int16 x, y;
+};
+
+struct ScreenShakeEffect {
+	uint32 _duration;
+	uint _pointsCount;
+	const ScreenShakerPoint *_points;
+};
+
+struct ScreenShaker {
+	uint _pointsIndex;
+	uint _pointsCount;
+	bool _finished;
+	uint32 _duration;
+	uint32 _nextTime;
+	uint32 _notifyThreadId;
+	const ScreenShakerPoint *_points;
+};
+
+struct PropertyTimer {
+	uint32 _propertyId;
+	uint32 _startTime;
+	uint32 _duration;
+	uint32 _endTime;
+	PropertyTimer() : _propertyId(0) {}
+};
+
+const uint kPropertyTimersCount = 6;
+
+struct OpCall;
+
+typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
+typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
+typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
+
+class IllusionsEngine_Duckman : public IllusionsEngine {
+public:
+	IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd);
+protected:
+	virtual Common::Error run();
+	virtual bool hasFeature(EngineFeature f) const;
+public:	
+
+	// TODO ActiveScenes _activeScenes;
+	uint32 _prevSceneId;
+	uint32 _theSceneId;
+	uint32 _theThreadId;
+	uint32 _globalSceneId;
+
+	uint _activeScenesCount;
+	uint32 _activeScenes[6];
+
+	Cursor_Duckman _cursor;
+	Control *_currWalkOverlappedControl;
+
+	Common::Array<DialogItem> _dialogItems;
+
+	int _savedInventoryActorIndex;
+	Common::Array<DMInventorySlot> _inventorySlots;
+	Common::Array<DMInventoryItem> _inventoyItems;
+
+	ScreenShaker *_screenShaker;
+
+	PropertyTimer _propertyTimers[kPropertyTimersCount];
+	bool _propertyTimersActive;
+	bool _propertyTimersPaused;
+
+	uint _chinesePuzzleIndex;
+	byte _chinesePuzzleAnswers[3];
+
+	SpecialCodeMap _specialCodeMap;
+
+	void initUpdateFunctions();
+	int updateScript(uint flags);
+
+	void startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId);
+	int updateScreenShaker(uint flags);
+
+	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
+
+	void setDefaultTextCoords();
+
+	void loadSpecialCode(uint32 resId);
+	void unloadSpecialCode(uint32 resId);
+	void notifyThreadId(uint32 &threadId);
+	bool testMainActorFastWalk(Control *control);
+	bool testMainActorCollision(Control *control);
+	Control *getObjectControl(uint32 objectId);
+	Common::Point getNamedPointPosition(uint32 namedPointId);
+	uint32 getPriorityFromBase(int16 priority);
+	uint32 getCurrentScene();
+	uint32 getPrevScene();
+
+	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
+	void setCursorControlRoutine(Control *control);
+	void placeCursorControl(Control *control, uint32 sequenceId);
+	void setCursorControl(Control *control);
+	void showCursor();
+	void hideCursor();
+	void initCursor();
+	void setCursorActorIndex(int actorIndex, int a, int b);
+	void enableCursorVerb(int verbNum);
+	void disableCursorVerb(int verbNum);
+	void setCursorHandMode(int mode);
+	void setCursorInventoryMode(int mode, int value);
+	void startCursorHoldingObject(uint32 objectId, uint32 sequenceId);
+	void stopCursorHoldingObject();
+	void cursorControlRoutine(Control *control, uint32 deltaTime);
+
+	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
+	void startScriptThread(uint32 threadId, uint32 callingThreadId);
+	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
+	uint32 startTimerThread(uint32 duration, uint32 threadId);
+	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
+	uint32 startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
+		uint32 sequenceId2, uint32 callingThreadId);
+	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
+		uint32 value8, uint32 valueC, uint32 value10);
+	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
+		byte *scriptCodeIp);
+	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
+	uint32 newTempThreadId();
+
+	void initActiveScenes();
+	void pushActiveScene(uint32 sceneId);
+	void popActiveScene();
+	bool loadScene(uint32 sceneId);
+	bool enterScene(uint32 sceneId, uint32 threadId);
+	void exitScene();
+	bool changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId);
+	void enterPause(uint32 sceneId, uint32 threadId);
+	void leavePause(uint32 sceneId, uint32 threadId);
+	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
+	void dumpCurrSceneFiles(uint32 sceneId, uint32 threadId);
+
+	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
+	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
+	void reset();
+	
+	uint32 getObjectActorTypeId(uint32 objectId);
+	
+	Common::Point convertMousePos(Common::Point mousePos);
+	void startCursorSequence();
+	int getCursorActorIndex();
+	void updateGameState2();
+	void playSoundEffect(int index);
+	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
+	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
+
+	void addDialogItem(int16 choiceJumpOffs, uint32 sequenceId);
+	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
+	void updateDialogState();
+
+	void initInventory();
+	void openInventory();
+	void addInventoryItem(uint32 objectId);
+	void clearInventorySlot(uint32 objectId);
+	void putBackInventoryItem();
+	DMInventorySlot *findInventorySlot(uint32 objectId);
+	DMInventoryItem *findInventoryItem(uint32 objectId);
+	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
+
+	void addPropertyTimer(uint32 propertyId);
+	void setPropertyTimer(uint32 propertyId, uint32 duration);
+	void removePropertyTimer(uint32 propertyId);
+	bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
+	int updatePropertyTimers(uint flags);
+
+	// Special code
+	void initSpecialCode();
+	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
+	void spcStartScreenShaker(OpCall &opCall);
+	void spcSetCursorHandMode(OpCall &opCall);
+	void spcResetChinesePuzzle(OpCall &opCall);
+	void spcAddChinesePuzzleAnswer(OpCall &opCall);
+	void spcOpenInventory(OpCall &opCall);
+	void spcPutBackInventoryItem(OpCall &opCall);
+	void spcClearInventorySlot(OpCall &opCall);
+	void spcAddPropertyTimer(OpCall &opCall);
+	void spcSetPropertyTimer(OpCall &opCall);
+	void spcRemovePropertyTimer(OpCall &opCall);
+	void spcCenterNewspaper(OpCall &opCall);
+	void spcSetCursorInventoryMode(OpCall &opCall);
+	void spcUpdateObject272Sequence(OpCall &opCall);
+
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/illusions_bbdou.cpp b/engines/illusions/illusions_bbdou.cpp
deleted file mode 100644
index 31f8ab4..0000000
--- a/engines/illusions/illusions_bbdou.cpp
+++ /dev/null
@@ -1,602 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions_bbdou.h"
-#include "illusions/actor.h"
-#include "illusions/camera.h"
-#include "illusions/cursor.h"
-#include "illusions/dictionary.h"
-#include "illusions/graphics.h"
-#include "illusions/input.h"
-#include "illusions/resources/actorresource.h"
-#include "illusions/resources/backgroundresource.h"
-#include "illusions/resources/fontresource.h"
-#include "illusions/resources/scriptresource.h"
-#include "illusions/resources/soundresource.h"
-#include "illusions/resources/talkresource.h"
-#include "illusions/resourcesystem.h"
-#include "illusions/screen.h"
-#include "illusions/screentext.h"
-#include "illusions/scriptstack.h"
-#include "illusions/scriptopcodes_bbdou.h"
-#include "illusions/sound.h"
-#include "illusions/specialcode.h"
-#include "illusions/bbdou/bbdou_specialcode.h"
-#include "illusions/thread.h"
-#include "illusions/time.h"
-#include "illusions/updatefunctions.h"
-
-#include "illusions/threads/abortablethread.h"
-#include "illusions/threads/scriptthread.h"
-#include "illusions/threads/talkthread.h"
-#include "illusions/threads/timerthread.h"
-
-#include "audio/audiostream.h"
-#include "common/config-manager.h"
-#include "common/debug-channels.h"
-#include "common/error.h"
-#include "common/fs.h"
-#include "common/timer.h"
-#include "engines/util.h"
-#include "graphics/cursorman.h"
-#include "graphics/font.h"
-#include "graphics/fontman.h"
-#include "graphics/palette.h"
-#include "graphics/surface.h"
-
-namespace Illusions {
-
-// TriggerFunction
-
-TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
-	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
-}
-
-TriggerFunction::~TriggerFunction() {
-	delete _callback;
-}
-
-void TriggerFunction::run(uint32 callingThreadId) {
-	(*_callback)(this, callingThreadId);
-}
-
-// TriggerFunctions
-
-void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
-	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
-	if (it != _triggerFunctions.end()) {
-		delete *it;
-		_triggerFunctions.erase(it);
-	}
-	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
-}
-
-TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
-	if (it != _triggerFunctions.end())
-		return (*it);
-	return 0;
-}
-
-void TriggerFunctions::removeBySceneId(uint32 sceneId) {
-	ItemsIterator it = _triggerFunctions.begin();
-	while (it != _triggerFunctions.end()) {
-		if ((*it)->_sceneId == sceneId) {
-			delete *it;
-			it = _triggerFunctions.erase(it);
-		} else
-			++it;
-	}
-}
-
-TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	ItemsIterator it = _triggerFunctions.begin();
-	for (; it != _triggerFunctions.end(); ++it) {
-		TriggerFunction *triggerFunction = *it;
-		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
-			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
-			break;
-	}
-	return it;		
-}
-
-// ActiveScenes
-
-ActiveScenes::ActiveScenes() {
-	clear();
-}
-
-void ActiveScenes::clear() {
-	_stack.clear();
-}
-
-void ActiveScenes::push(uint32 sceneId) {
-	ActiveScene activeScene;
-	activeScene._sceneId = sceneId;
-	activeScene._pauseCtr = 0;
-	_stack.push(activeScene);
-}
-
-void ActiveScenes::pop() {
-	_stack.pop();
-}
-
-void ActiveScenes::pauseActiveScene() {
-	++_stack.top()._pauseCtr;
-}
-
-void ActiveScenes::unpauseActiveScene() {
-	--_stack.top()._pauseCtr;
-}
-
-uint ActiveScenes::getActiveScenesCount() {
-	return _stack.size();
-}
-
-void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
-	if (sceneId)
-		*sceneId = _stack[index - 1]._sceneId;
-	if (pauseCtr)
-		*pauseCtr = _stack[index - 1]._pauseCtr;
-}
-
-uint32 ActiveScenes::getCurrentScene() {
-	if (_stack.size() > 0)
-		return _stack.top()._sceneId;
-	return 0;
-}
-
-bool ActiveScenes::isSceneActive(uint32 sceneId) {
-	for (uint i = 0; i < _stack.size(); ++i)
-		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
-			return true;
-	return false;
-}
-
-// IllusionsEngine_BBDOU
-
-IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd)
-	: IllusionsEngine(syst, gd) {
-}
-
-Common::Error IllusionsEngine_BBDOU::run() {
-
-	// Init search paths
-	const Common::FSNode gameDataDir(ConfMan.get("path"));
-	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
-	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
-
-	_dict = new Dictionary();
-
-	_resSys = new ResourceSystem(this);
-	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
-	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
-	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
-	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
-	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
-	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
-	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
-	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
-
-	_screen = new Screen(this, 640, 480, 16);
-	_screenText = new ScreenText(this);
-	_input = new Input();	
-	_actorInstances = new ActorInstanceList(this);
-	_backgroundInstances = new BackgroundInstanceList(this);
-	_camera = new Camera(this);
-	_controls = new Controls(this);
-	_cursor = new Cursor(this);
-	_talkItems = new TalkInstanceList(this);
-	_triggerFunctions = new TriggerFunctions();
-	_threads = new ThreadList(this);
-	_updateFunctions = new UpdateFunctions();
-	_soundMan = new SoundMan(this);
-
-	initUpdateFunctions();
-
-	_fader = 0;
-
-	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
-	_stack = new ScriptStack();
-	
-	// TODO Move to own class
-	_resGetCtr = 0;
-	_unpauseControlActorFlag = false;
-	_lastUpdateTime = 0;
-
-	_pauseCtr = 0;
-	_field8 = 1;
-	_fieldA = 0;
-	_fieldE = 240;
-	
-	_globalSceneId = 0x00010003;	
-	
-	setDefaultTextCoords();
-	
-	_resSys->loadResource(0x000D0001, 0, 0);
-
-	_doScriptThreadInit = false;
-	startScriptThread(0x00020004, 0, 0, 0, 0);
-	_doScriptThreadInit = true;
-
-	while (!shouldQuit()) {
-		runUpdateFunctions();
-		_system->updateScreen();
-		updateEvents();
-	}
-
-	delete _stack;
-	delete _scriptOpcodes;
-
-    delete _soundMan;
-	delete _updateFunctions;
-	delete _threads;
-	delete _triggerFunctions;
-	delete _talkItems;
-	delete _cursor;
-	delete _controls;
-	delete _camera;
-	delete _backgroundInstances;
-	delete _actorInstances;
-	delete _input;
-	delete _screenText;
-	delete _screen;
-	delete _resSys;
-	delete _dict;
-	
-	debug("Ok");
-	
-	return Common::kNoError;
-}
-
-bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
-	return
-		false;
-		/*
-		(f == kSupportsRTL) ||
-		(f == kSupportsLoadingDuringRuntime) ||
-		(f == kSupportsSavingDuringRuntime);
-		*/
-}
-
-#define UPDATEFUNCTION(priority, tag, callback) \
-	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
-		(this, &IllusionsEngine_BBDOU::callback));
-
-void IllusionsEngine_BBDOU::initUpdateFunctions() {
-	UPDATEFUNCTION(30, 0, updateScript);
-	UPDATEFUNCTION(50, 0, updateActors);
-	UPDATEFUNCTION(60, 0, updateSequences);
-	UPDATEFUNCTION(70, 0, updateGraphics);
-	UPDATEFUNCTION(90, 0, updateSprites);
-	UPDATEFUNCTION(120, 0, updateSoundMan);
-}
-
-#undef UPDATEFUNCTION
-
-int IllusionsEngine_BBDOU::updateScript(uint flags) {
-	_threads->updateThreads();
-	return 1;
-}
-
-bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	uint32 codeOffs;
-	return 
-		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
-		findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
-}
-
-void IllusionsEngine_BBDOU::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
-	_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
-}
-
-uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
-	uint32 codeOffs;
-	uint32 causeThreadId = 0;
-	TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
-	if (triggerFunction) {
-		triggerFunction->run(callingThreadId);
-	} else if (findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
-		//debug("Run cause at %08X", codeOffs);
-		causeThreadId = startTempScriptThread(_scriptResource->getCode(codeOffs),
-			callingThreadId, verbId, objectId2, objectId);
-	}
-	return causeThreadId;
-}
-
-void IllusionsEngine_BBDOU::setDefaultTextCoords() {
-	WidthHeight dimensions;
-	dimensions._width = 480;
-	dimensions._height = 48;
-	Common::Point pt(320, 448);
-	setDefaultTextDimensions(dimensions);
-	setDefaultTextPosition(pt);
-}
-
-void IllusionsEngine_BBDOU::loadSpecialCode(uint32 resId) {
-	_specialCode = new BbdouSpecialCode(this);
-	_specialCode->init();
-}
-
-void IllusionsEngine_BBDOU::unloadSpecialCode(uint32 resId) {
-	delete _specialCode;
-	_specialCode = 0;
-}
-
-void IllusionsEngine_BBDOU::notifyThreadId(uint32 &threadId) {
-	if (threadId) {
-		uint32 tempThreadId = threadId;
-		threadId = 0;
-		_threads->notifyId(tempThreadId);
-	}
-}
-
-bool IllusionsEngine_BBDOU::testMainActorFastWalk(Control *control) {
-	return false;
-}
-
-bool IllusionsEngine_BBDOU::testMainActorCollision(Control *control) {
-	// Not used in BBDOU
-	return false;
-}
-
-Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
-	return _dict->getObjectControl(objectId);
-}
-
-Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
-	Common::Point pt;
-	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt) ||
-		_actorInstances->findNamedPoint(namedPointId, pt) ||
-		_controls->findNamedPoint(namedPointId, pt))
-		return pt;
-	// TODO
-	switch (namedPointId) {
-	case 0x70001:
-		return Common::Point(0, 0);
-	case 0x70002:
-		return Common::Point(640, 0);
-	case 0x70023:
-		return Common::Point(320, 240);
-	}
-	debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
-	return Common::Point(0, 0);
-}
-
-uint32 IllusionsEngine_BBDOU::getPriorityFromBase(int16 priority) {
-	return 32000000 * priority;
-}
-
-uint32 IllusionsEngine_BBDOU::getCurrentScene() {
-	return _activeScenes.getCurrentScene();
-}
-
-uint32 IllusionsEngine_BBDOU::getPrevScene() {
-	return _prevSceneId;
-}
-
-bool IllusionsEngine_BBDOU::isCursorObject(uint32 actorTypeId, uint32 objectId) {
-	return actorTypeId == 0x50001 && objectId == 0x40004;
-}
-
-void IllusionsEngine_BBDOU::setCursorControlRoutine(Control *control) {
-	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_BBDOU>
-		(this, &IllusionsEngine_BBDOU::cursorControlRoutine));
-}
-
-void IllusionsEngine_BBDOU::placeCursorControl(Control *control, uint32 sequenceId) {
-	_cursor->place(control, sequenceId);
-}
-
-void IllusionsEngine_BBDOU::setCursorControl(Control *control) {
-	_cursor->setControl(control);
-}
-
-void IllusionsEngine_BBDOU::showCursor() {
-	_cursor->show();
-}
-
-void IllusionsEngine_BBDOU::hideCursor() {
-	_cursor->hide();
-}
-
-void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
-	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (control->_actor->_flags & 1) {
-		switch (_cursor->_status) {
-		case 2:
-			// Unused nullsub_1(control);
-			break;
-		case 3:
-			// TODO _vm->_shellMgr->handleMouse(control);
-			break;
-		}
-	}
-}
-
-void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
-	startScriptThread(threadId, callingThreadId, 0, 0, 0);
-}
-
-void IllusionsEngine_BBDOU::startScriptThread(uint32 threadId, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	debug(2, "Starting script thread %08X", threadId);
-	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
-}
-
-void IllusionsEngine_BBDOU::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	debug(2, "Starting anonymous script thread %08X", threadId);
-	uint32 tempThreadId = newTempThreadId();
-	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
-}
-
-uint32 IllusionsEngine_BBDOU::startAbortableTimerThread(uint32 duration, uint32 threadId) {
-	return newTimerThread(duration, threadId, true);
-}
-
-uint32 IllusionsEngine_BBDOU::startTimerThread(uint32 duration, uint32 threadId) {
-	return newTimerThread(duration, threadId, false);
-}
-
-uint32 IllusionsEngine_BBDOU::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
-	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting abortable thread %08X", tempThreadId);
-	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
-	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
-		scriptThreadId, scriptCodeIp2);
-	_threads->startThread(abortableThread);
-	return tempThreadId;
-}
-
-uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
-	uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
-	debug(2, "Starting talk thread");
-	uint32 tempThreadId = newTempThreadId();
-	_threads->endTalkThreadsNoNotify();
-	TalkThread *talkThread = new TalkThread(this, tempThreadId, callingThreadId, 0,
-		duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
-	_threads->startThread(talkThread);
-	return tempThreadId;
-}
-
-uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting temp script thread %08X", tempThreadId);
-	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
-	return tempThreadId;
-}
-
-void IllusionsEngine_BBDOU::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
-	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
-		scriptCodeIp, value8, valueC, value10);
-	_threads->startThread(scriptThread);
-	if (_pauseCtr > 0)
-		scriptThread->pause();
-	if (_doScriptThreadInit) {
-		int updateResult = kTSRun;
-		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
-			updateResult = scriptThread->update();
-	}
-}
-
-uint32 IllusionsEngine_BBDOU::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
-	uint32 tempThreadId = newTempThreadId();
-	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
-		duration, isAbortable);
-	_threads->startThread(timerThread);
-	return tempThreadId;
-}
-
-uint32 IllusionsEngine_BBDOU::newTempThreadId() {
-	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
-	if (threadId > 65535) {
-		_nextTempThreadId = 0;
-		threadId = 2 * _scriptResource->_codeCount;
-	}
-	++_nextTempThreadId;
-	return 0x00020000 | threadId;
-}
-
-bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (!progInfo) {
-		dumpActiveScenes(_globalSceneId, threadId);
-		sceneId = _theSceneId;
-	}
-	_activeScenes.push(sceneId);
-	return progInfo != 0;
-}
-
-void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
-	uint32 sceneId = _activeScenes.getCurrentScene();
-	// TODO krnfileDump(sceneId);
-	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
-	_threads->terminateThreadsByTag(sceneId, threadId);
-	_controls->destroyControlsByTag(sceneId);
-	_triggerFunctions->removeBySceneId(sceneId);
-	_resSys->unloadResourcesByTag(sceneId);
-	_activeScenes.pop();
-}
-
-void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
-	uint32 sceneId = _activeScenes.getCurrentScene();
-	_camera->pushCameraMode();
-	_threads->suspendThreadsByTag(sceneId, threadId);
-	_controls->pauseControlsByTag(sceneId);
-	_actorInstances->pauseByTag(sceneId);
-	_backgroundInstances->pauseByTag(sceneId);
-	_activeScenes.pauseActiveScene();
-}
-
-void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
-	uint32 sceneId = _activeScenes.getCurrentScene();
-	_backgroundInstances->unpauseByTag(sceneId);
-	_actorInstances->unpauseByTag(sceneId);
-	_controls->unpauseControlsByTag(sceneId);
-	_threads->notifyThreadsByTag(sceneId, threadId);
-	_camera->popCameraMode();
-	_activeScenes.unpauseActiveScene();
-}
-
-void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
-	uint activeScenesCount = _activeScenes.getActiveScenesCount();
-	while (activeScenesCount > 0) {
-		uint32 activeSceneId;
-		_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, 0);
-		if (activeSceneId == sceneId)
-			break;
-		exitScene(threadId);
-		--activeScenesCount;
-	}
-	_camera->clearCameraModeStack();
-}
-
-void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
-	_theSceneId = theSceneId;
-	_theThreadId = theThreadId;
-}
-
-bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (progInfo)
-		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
-	return false;
-}
-
-void IllusionsEngine_BBDOU::reset() {
-	_scriptResource->_blockCounters.clear();
-	_scriptResource->_properties.clear();
-	// TODO script_sub_417FF0(1, 0);
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/illusions_bbdou.h b/engines/illusions/illusions_bbdou.h
deleted file mode 100644
index fccfb59..0000000
--- a/engines/illusions/illusions_bbdou.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_ILLUSIONS_BBDOU_H
-#define ILLUSIONS_ILLUSIONS_BBDOU_H
-
-#include "illusions/illusions.h"
-#include "common/algorithm.h"
-#include "common/stack.h"
-
-namespace Illusions {
-
-class Dictionary;
-class ScriptMan;
-class ScriptStack;
-class TriggerFunctions;
-class TriggerFunction;
-
-typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
-
-struct TriggerFunction {
-	uint32 _sceneId;
-	uint32 _verbId;
-	uint32 _objectId2;
-	uint32 _objectId;
-	TriggerFunctionCallback *_callback;
-	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	~TriggerFunction();
-	void run(uint32 callingThreadId);
-};
-
-class TriggerFunctions {
-public:
-	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-	void removeBySceneId(uint32 sceneId);
-public:
-	typedef Common::List<TriggerFunction*> Items;
-	typedef Items::iterator ItemsIterator;
-	Items _triggerFunctions;
-	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-};
-
-struct ActiveScene {
-	uint32 _sceneId;
-	int _pauseCtr;
-};
-
-class ActiveScenes {
-public:
-	ActiveScenes();
-	void clear();
-	void push(uint32 sceneId);
-	void pop();
-	void pauseActiveScene();
-	void unpauseActiveScene();
-	uint getActiveScenesCount();
-	void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
-	uint32 getCurrentScene();
-	bool isSceneActive(uint32 sceneId);
-protected:
-	Common::FixedStack<ActiveScene, 16> _stack;
-};
-
-class IllusionsEngine_BBDOU : public IllusionsEngine {
-public:
-	IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd);
-protected:
-	virtual Common::Error run();
-	virtual bool hasFeature(EngineFeature f) const;
-public:	
-	ScriptMan *_scriptMan;
-	TriggerFunctions *_triggerFunctions;
-	Cursor *_cursor;
-
-	ActiveScenes _activeScenes;
-	uint32 _prevSceneId;
-	uint32 _theSceneId;
-	uint32 _theThreadId;
-	uint32 _globalSceneId;
-
-	void initUpdateFunctions();
-	int updateScript(uint flags);
-
-	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
-
-    void setDefaultTextCoords();
-
-	void loadSpecialCode(uint32 resId);
-	void unloadSpecialCode(uint32 resId);
-	void notifyThreadId(uint32 &threadId);
-	bool testMainActorFastWalk(Control *control);
-	bool testMainActorCollision(Control *control);
-	Control *getObjectControl(uint32 objectId);
-	Common::Point getNamedPointPosition(uint32 namedPointId);
-	uint32 getPriorityFromBase(int16 priority);
-	uint32 getCurrentScene();
-	uint32 getPrevScene();	
-	
-	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
-	void setCursorControlRoutine(Control *control);
-	void placeCursorControl(Control *control, uint32 sequenceId);
-	void setCursorControl(Control *control);
-	void showCursor();
-	void hideCursor();
-	void cursorControlRoutine(Control *control, uint32 deltaTime);
-
-	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
-	void startScriptThread(uint32 threadId, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	void startAnonScriptThread(int32 threadId, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
-	uint32 startTimerThread(uint32 duration, uint32 threadId);
-	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
-	uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
-		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
-	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
-	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
-	uint32 newTempThreadId();
-
-	bool enterScene(uint32 sceneId, uint32 threadId);
-	void exitScene(uint32 threadId);
-	void enterPause(uint32 threadId);
-	void leavePause(uint32 threadId);
-	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
-
-	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
-	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
-	void reset();
-	
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/illusions_duckman.cpp b/engines/illusions/illusions_duckman.cpp
deleted file mode 100644
index 4d97343..0000000
--- a/engines/illusions/illusions_duckman.cpp
+++ /dev/null
@@ -1,1584 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/illusions_duckman.h"
-#include "illusions/actor.h"
-#include "illusions/camera.h"
-#include "illusions/cursor.h"
-#include "illusions/dictionary.h"
-#include "illusions/resources/fontresource.h"
-#include "illusions/graphics.h"
-#include "illusions/input.h"
-#include "illusions/resources/actorresource.h"
-#include "illusions/resources/backgroundresource.h"
-#include "illusions/resources/midiresource.h"
-#include "illusions/resources/scriptresource.h"
-#include "illusions/resources/soundresource.h"
-#include "illusions/resources/talkresource.h"
-#include "illusions/resourcesystem.h"
-#include "illusions/screen.h"
-#include "illusions/screentext.h"
-#include "illusions/scriptopcodes_duckman.h"
-#include "illusions/scriptstack.h"
-#include "illusions/sound.h"
-#include "illusions/specialcode.h"
-#include "illusions/textdrawer.h"
-#include "illusions/thread.h"
-#include "illusions/time.h"
-#include "illusions/updatefunctions.h"
-
-#include "illusions/threads/abortablethread.h"
-#include "illusions/threads/causethread_duckman.h"
-#include "illusions/threads/scriptthread.h"
-#include "illusions/threads/talkthread_duckman.h"
-#include "illusions/threads/timerthread.h"
-
-#include "audio/audiostream.h"
-#include "common/config-manager.h"
-#include "common/debug-channels.h"
-#include "common/error.h"
-#include "common/fs.h"
-#include "common/timer.h"
-#include "engines/util.h"
-#include "graphics/cursorman.h"
-#include "graphics/font.h"
-#include "graphics/fontman.h"
-#include "graphics/palette.h"
-#include "graphics/surface.h"
-
-namespace Illusions {
-
-// IllusionsEngine_Duckman
-
-IllusionsEngine_Duckman::IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd)
-	: IllusionsEngine(syst, gd) {
-}
-
-Common::Error IllusionsEngine_Duckman::run() {
-
-	// Init search paths
-	const Common::FSNode gameDataDir(ConfMan.get("path"));
-	SearchMan.addSubDirectoryMatching(gameDataDir, "music");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
-	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "x");// DEBUG until gam reader is done
-
-	_dict = new Dictionary();
-
-	_resSys = new ResourceSystem(this);
-	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
-	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
-	_resSys->addResourceLoader(0x000A0000, new MidiGroupResourceLoader(this));
-	_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
-	_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
-	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
-	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
-	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
-
-	_screen = new Screen(this, 320, 200, 8);
-	_screenText = new ScreenText(this);
-	_input = new Input();	
-	_actorInstances = new ActorInstanceList(this);
-	_backgroundInstances = new BackgroundInstanceList(this);
-	_camera = new Camera(this);
-	_controls = new Controls(this);
-	_talkItems = new TalkInstanceList(this);
-	_threads = new ThreadList(this);
-	_updateFunctions = new UpdateFunctions();
-	_soundMan = new SoundMan(this);
-
-	_fader = new Fader();
-
-	initUpdateFunctions();
-
-	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
-	_stack = new ScriptStack();
-	
-	// TODO Move to own class
-	_resGetCtr = 0;
-	_unpauseControlActorFlag = false;
-	_lastUpdateTime = 0;
-
-	_currWalkOverlappedControl = 0;
-
-	_pauseCtr = 0;
-	_doScriptThreadInit = false;
-	_field8 = 1;
-	_fieldA = 0;
-	_fieldE = 240;
-	
-	_propertyTimersActive = false;
-	_propertyTimersPaused = false;
-
-	_globalSceneId = 0x00010003;
-
-	initInventory();
-	initSpecialCode();
-	setDefaultTextCoords();
-	initCursor();
-	initActiveScenes();
-
-	_resSys->loadResource(0x120001, 0x00010001, 0);
-	_resSys->loadResource(0x120002, 0x00010001, 0);
-	_resSys->loadResource(0x120003, 0x00010001, 0);
-
-	_resSys->loadResource(0x000D0001, 0x00010001, 0);
-	startScriptThread(0x00020004, 0);
-	_doScriptThreadInit = true;
-
-#if 0
-	//DEBUG
-	_scriptResource->_properties.set(0x000E003A, true);
-	_scriptResource->_properties.set(0x000E0020, true);
-	_scriptResource->_properties.set(0x000E003A, true);
-	_scriptResource->_properties.set(0x000E003B, true);
-	_scriptResource->_properties.set(0x000E0009, true);
-	_scriptResource->_properties.set(0x000E003D, true);
-	_scriptResource->_properties.set(0x000E0024, true);
-#endif
-
-	while (!shouldQuit()) {
-		runUpdateFunctions();
-		_system->updateScreen();
-		updateEvents();
-	}
-
-	delete _stack;
-	delete _scriptOpcodes;
-
-	delete _fader;
-
-	delete _soundMan;
-	delete _updateFunctions;
-	delete _threads;
-	delete _talkItems;
-	delete _controls;
-	delete _camera;
-	delete _backgroundInstances;
-	delete _actorInstances;
-	delete _input;
-	delete _screenText;
-	delete _screen;
-	delete _resSys;
-	delete _dict;
-	
-	debug("Ok");
-	
-	return Common::kNoError;
-}
-
-bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
-	return
-		false;
-		/*
-		(f == kSupportsRTL) ||
-		(f == kSupportsLoadingDuringRuntime) ||
-		(f == kSupportsSavingDuringRuntime);
-		*/
-}
-
-#define UPDATEFUNCTION(priority, tag, callback) \
-	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
-		(this, &IllusionsEngine_Duckman::callback));
-
-void IllusionsEngine_Duckman::initUpdateFunctions() {
-	UPDATEFUNCTION(30, 0, updateScript);
-	UPDATEFUNCTION(50, 0, updateActors);
-	UPDATEFUNCTION(60, 0, updateSequences);
-	UPDATEFUNCTION(70, 0, updateGraphics);
-	UPDATEFUNCTION(90, 0, updateSprites);
-	UPDATEFUNCTION(120, 0, updateSoundMan);
-}
-
-#undef UPDATEFUNCTION
-
-int IllusionsEngine_Duckman::updateScript(uint flags) {
-	// TODO Some more stuff
-	_threads->updateThreads();
-	return 1;
-}
-
-void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId) {
-	_screenShaker = new ScreenShaker();
-	_screenShaker->_pointsIndex = 0;
-	_screenShaker->_pointsCount = pointsCount;
-	_screenShaker->_finished = false;
-	_screenShaker->_duration = duration;
-	_screenShaker->_nextTime = duration + getCurrentTime();
-	_screenShaker->_points = points;
-	_screenShaker->_notifyThreadId = threadId;
-	_updateFunctions->add(71, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
-		(this, &IllusionsEngine_Duckman::updateScreenShaker));
-}
-
-int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
-	if (_pauseCtr > 0 || getCurrentScene() == 0x10038) {
-		_screenShaker->_nextTime = getCurrentTime();
-		return 1;
-	}
-
-	if (flags & 1)
-		_screenShaker->_finished = true;
-
-	if (!_screenShaker->_finished) {
-		if (getCurrentTime() >= _screenShaker->_nextTime) {
-			++_screenShaker->_pointsIndex;
-			if (_screenShaker->_pointsIndex <= _screenShaker->_pointsCount) {
-				ScreenShakerPoint shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
-				if (shakePt.x == (int16)0x8000) {
-					// Loop
-					_screenShaker->_pointsIndex = 1;
-					shakePt = _screenShaker->_points[_screenShaker->_pointsIndex - 1];
-				}
-				_screenShaker->_nextTime = getCurrentTime() + _screenShaker->_duration;
-				_screen->setScreenOffset(Common::Point(shakePt.x, shakePt.y));
-			} else
-				_screenShaker->_finished = true;
-		}
-	}
-
-	if (_screenShaker->_finished) {
-		notifyThreadId(_screenShaker->_notifyThreadId);
-		delete _screenShaker;
-		_screenShaker = 0;
-		_screen->setScreenOffset(Common::Point(0, 0));
-		return 2;
-	}
-
-	return 1;
-}
-
-void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId) {
-	_fader->_active = true;
-	_fader->_currValue = minValue;
-	_fader->_minValue = minValue;
-	_fader->_maxValue = maxValue;
-	_fader->_firstIndex = firstIndex;
-	_fader->_lastIndex = lastIndex;
-	_fader->_startTime = getCurrentTime();
-	_fader->_duration = duration;
-	_fader->_notifyThreadId = threadId;
-}
-
-void IllusionsEngine_Duckman::setDefaultTextCoords() {
-	WidthHeight dimensions;
-	dimensions._width = 300;
-	dimensions._height = 32;
-	Common::Point pt(160, 176);
-	setDefaultTextDimensions(dimensions);
-	setDefaultTextPosition(pt);
-}
-
-void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
-	//TODO?
-}
-
-void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
-	//TODO?
-}
-
-void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
-	if (threadId) {
-		uint32 tempThreadId = threadId;
-		threadId = 0;
-		_threads->notifyId(tempThreadId);
-	}
-}
-
-bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
-	return
-		control->_objectId == _scriptResource->getMainActorObjectId() &&
-		_input->pollButton(0x20);
-}
-
-bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
-	bool result = false;
-	Control *overlappedControl;
-	if (_controls->getOverlappedWalkObject(control, control->_actor->_position, &overlappedControl)) {
-		if (_currWalkOverlappedControl != overlappedControl) {
-			_currWalkOverlappedControl = overlappedControl;
-			if (runTriggerCause(9, 0, overlappedControl->_objectId)) {
-				delete control->_actor->_pathNode;
-				control->_actor->_flags &= ~0x0400;
-				control->_actor->_pathNode = 0;
-				control->_actor->_pathPoints = 0;
-				control->_actor->_pathPointsCount = 0;
-				_threads->terminateThreadChain(control->_actor->_walkCallerThreadId1);
-				if (control->_actor->_notifyId3C) {
-					notifyThreadId(control->_actor->_notifyId3C);
-					control->_actor->_walkCallerThreadId1 = 0;
-				}
-				result = true;
-			}
-		}
-	} else {
-		_currWalkOverlappedControl = 0;
-	}
-	return result;
-}
-
-Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
-	return _dict->getObjectControl(objectId);
-}
-
-Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
-	Common::Point pt;
-	Common::Point currPan = _camera->getCurrentPan();
-	if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt)) {
-		return pt;
-	} else if (namedPointId - 0x00070001 > 209) {
-		if (_controls->findNamedPoint(namedPointId, pt)) {
-			return pt;
-		} else {
-			return currPan;
-		}
-	} else {
-		// TODO
-		debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
-		return Common::Point(0, 0);
-	}
-}
-
-uint32 IllusionsEngine_Duckman::getPriorityFromBase(int16 priority) {
-	return priority << 16;
-}
-
-uint32 IllusionsEngine_Duckman::getCurrentScene() {
-	return _activeScenes[_activeScenesCount];
-}
-
-uint32 IllusionsEngine_Duckman::getPrevScene() {
-	uint index = _activeScenesCount - 1;
-	if (_activeScenesCount == 1)
-		index = 5;
-	return _activeScenes[index];
-}
-
-bool IllusionsEngine_Duckman::isCursorObject(uint32 actorTypeId, uint32 objectId) {
-	return actorTypeId == 0x50001;
-}
-
-void IllusionsEngine_Duckman::setCursorControlRoutine(Control *control) {
-	control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_Duckman>
-		(this, &IllusionsEngine_Duckman::cursorControlRoutine));
-}
-
-void IllusionsEngine_Duckman::placeCursorControl(Control *control, uint32 sequenceId) {
-	_cursor._gameState = 2;
-	_cursor._control = control;
-	_cursor._actorIndex = 1;
-	_cursor._savedActorIndex = 1;
-	_cursor._currOverlappedControl = 0;
-	_cursor._sequenceId1 = sequenceId;
-	_cursor._field14[0] = true;
-	_cursor._field14[1] = true;
-	_cursor._field14[2] = false;
-	_cursor._field14[3] = false;
-	_cursor._field14[4] = false;
-	_cursor._field14[5] = false;
-	_cursor._field14[9] = false;
-	_cursor._field14[10] = false;
-	_cursor._field14[11] = false;
-	_cursor._field14[12] = false;
-	_cursor._field14[6] = _cursor._sequenceId2 != 0 && _cursor._objectId != 0;
-	_cursor._field14[7] = false;
-	_cursor._field14[8] = false;
-	_cursor._op113_choiceOfsPtr = 0;
-	_cursor._notifyThreadId30 = 0;
-	_cursor._dialogItemsCount = 0;
-	_cursor._overlappedObjectId = 0;
-	_cursor._field40 = 0;
-	control->_flags |= 8;
-	setCursorActorIndex(_cursor._actorIndex, 1, 0);
-	// TODO Input_setMousePos(cursorControl->actor->position);
-	// TODO
-	//control->_actor->_actorIndex = 2;
-	// TODO _cursor->place(control, sequenceId);
-}
-
-void IllusionsEngine_Duckman::setCursorControl(Control *control) {
-	_cursor._control = control;
-}
-
-void IllusionsEngine_Duckman::showCursor() {
-	// TODO
-}
-
-void IllusionsEngine_Duckman::hideCursor() {
-	// TODO
-}
-
-void IllusionsEngine_Duckman::initCursor() {
-	_cursor._gameState = 1;
-	_cursor._control = 0;
-	_cursor._position.x = 160;
-	_cursor._position.y = 100;
-	_cursor._objectId = 0;
-	_cursor._actorIndex = 1;
-	_cursor._savedActorIndex = 1;
-	_cursor._currOverlappedControl = 0;
-	_cursor._sequenceId1 = 0;
-	_cursor._sequenceId2 = 0;
-	_cursor._field14[0] = true;
-	_cursor._field14[1] = true;
-	_cursor._field14[2] = false;
-	_cursor._field14[3] = false;
-	_cursor._field14[4] = false;
-	_cursor._field14[5] = false;
-	_cursor._field14[6] = false;
-	_cursor._field14[7] = false;
-	_cursor._field14[8] = false;
-	_cursor._field14[9] = false;
-	_cursor._field14[10] = false;
-	_cursor._field14[11] = false;
-	_cursor._field14[12] = false;
-	_cursor._op113_choiceOfsPtr = 0;
-	_cursor._notifyThreadId30 = 0;
-	_cursor._dialogItemsCount = 0;
-	_cursor._overlappedObjectId = 0;
-	_cursor._field40 = 0;
-}
-
-void IllusionsEngine_Duckman::setCursorActorIndex(int actorIndex, int a, int b) {
-	static int kCursorMap[13][2][2] = {
-		{{ 1,  2}, { 0,  0}},
-		{{ 3,  4}, { 0,  0}},
-		{{ 5,  6}, {13, 14}},
-		{{ 7,  8}, { 0,  0}},
-		{{ 9, 10}, { 0,  0}},
-		{{11, 12}, { 0,  0}},
-		{{ 1,  2}, { 0,  0}},
-		{{ 0,  0}, { 0,  0}},
-		{{ 0,  0}, { 0,  0}},
-		{{15, 16}, { 0,  0}},
-		{{17, 18}, { 0,  0}},
-		{{19, 20}, { 0,  0}},
-		{{21, 22}, { 0,  0}}
-	};
-	_cursor._control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
-}
-
-void IllusionsEngine_Duckman::enableCursorVerb(int verbNum) {
-	if (verbNum != 7 || _cursor._sequenceId2)
-		_cursor._field14[verbNum - 1] = true;
-}
-
-void IllusionsEngine_Duckman::disableCursorVerb(int verbNum) {
-	_cursor._field14[verbNum - 1] = false;
-	if (_cursor._actorIndex == verbNum) {
-		_cursor._actorIndex = getCursorActorIndex();
-		setCursorActorIndex(_cursor._actorIndex, 1, 0);
-		startCursorSequence();
-		_cursor._currOverlappedControl = 0;
-	}
-}
-
-void IllusionsEngine_Duckman::setCursorHandMode(int mode) {
-	if (mode == 1) {
-		enableCursorVerb(4);
-		disableCursorVerb(1);
-		disableCursorVerb(2);
-		disableCursorVerb(7);
-		_cursor._actorIndex = 4;
-	} else {
-		enableCursorVerb(1);
-		enableCursorVerb(2);
-		enableCursorVerb(7);
-		disableCursorVerb(4);
-		_cursor._actorIndex = 1;
-	}
-	_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
-	if (_cursor._currOverlappedControl)
-		setCursorActorIndex(_cursor._actorIndex, 2, 0);
-	else
-		setCursorActorIndex(_cursor._actorIndex, 1, 0);
-}
-
-void IllusionsEngine_Duckman::setCursorInventoryMode(int mode, int value) {
-	_cursor._control = _cursor._control;
-	if (mode == 1) {
-		_savedInventoryActorIndex = _cursor._actorIndex;
-		if (_cursor._actorIndex == 3 || _cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
-			_cursor._savedActorIndex = _cursor._savedActorIndex;
-			if (_cursor._savedActorIndex == 1 || _cursor._savedActorIndex == 2 || _cursor._savedActorIndex == 7)
-				_savedInventoryActorIndex = _cursor._savedActorIndex;
-			else
-				_savedInventoryActorIndex = 0;
-		}
-		if (value == 1 && _cursor._objectId && _savedInventoryActorIndex != 7) {
-			_cursor._actorIndex = 7;
-			stopCursorHoldingObject();
-			_cursor._actorIndex = _savedInventoryActorIndex;
-		}
-	} else if (mode == 2) {
-		if (_savedInventoryActorIndex)
-			_cursor._actorIndex = _savedInventoryActorIndex;
-		else
-			_cursor._actorIndex = 1;
-		if (_cursor._actorIndex == 7)
-			_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
-		else
-			_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
-		if (_cursor._currOverlappedControl)
-			setCursorActorIndex(_cursor._actorIndex, 2, 0);
-		else
-			setCursorActorIndex(_cursor._actorIndex, 1, 0);
-		_savedInventoryActorIndex = 0;
-	}
-}
-
-void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 sequenceId) {
-	_cursor._objectId = objectId;
-	_cursor._sequenceId2 = sequenceId;
-	_cursor._actorIndex = 7;
-	_cursor._savedActorIndex = 7;
-	_cursor._field14[_cursor._actorIndex - 1] = true;
-	_cursor._control->startSequenceActor(sequenceId, 2, 0);
-	setCursorActorIndex(_cursor._actorIndex, 1, 0);
-	_cursor._currOverlappedControl = 0;
-}
-
-void IllusionsEngine_Duckman::stopCursorHoldingObject() {
-	_cursor._field14[6] = false;
-	_cursor._objectId = 0;
-	_cursor._sequenceId2 = 0;
-	if (_cursor._actorIndex == 7) {
-		_cursor._actorIndex = getCursorActorIndex();
-		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
-		if (_cursor._currOverlappedControl)
-			setCursorActorIndex(_cursor._actorIndex, 2, 0);
-		else
-			setCursorActorIndex(_cursor._actorIndex, 1, 0);
-	}
-}
-
-void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
-	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (control->_actor->_flags & 1) {
-		switch (_cursor._gameState) {
-		case 2:
-			updateGameState2();
-			break;
-		case 3:
-			updateDialogState();
-			break;
-		case 4:
-			// TODO ShellMgr_update(_cursor._control);
-			break;
-		}
-	}
-}
-
-void IllusionsEngine_Duckman::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
-	startScriptThread(threadId, callingThreadId);
-}
-
-void IllusionsEngine_Duckman::startScriptThread(uint32 threadId, uint32 callingThreadId) {
-	debug(2, "Starting script thread %08X", threadId);
-	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
-	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp);
-}
-
-uint32 IllusionsEngine_Duckman::startAbortableTimerThread(uint32 duration, uint32 threadId) {
-	return newTimerThread(duration, threadId, true);
-}
-
-uint32 IllusionsEngine_Duckman::startTimerThread(uint32 duration, uint32 threadId) {
-	return newTimerThread(duration, threadId, false);
-}
-
-uint32 IllusionsEngine_Duckman::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
-	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting abortable thread %08X", tempThreadId);
-	uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
-	AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
-		scriptThreadId, scriptCodeIp2);
-	_threads->startThread(abortableThread);
-	return tempThreadId;
-}
-
-uint32 IllusionsEngine_Duckman::startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
-	uint32 sequenceId2, uint32 callingThreadId) {
-	debug(2, "Starting talk thread");
-	uint32 tempThreadId = newTempThreadId();
-	TalkThread_Duckman *talkThread = new TalkThread_Duckman(this, tempThreadId, callingThreadId, 0,
-		objectId, talkId, sequenceId1, sequenceId2);
-	_threads->startThread(talkThread);
-	return tempThreadId;
-}
-
-uint32 IllusionsEngine_Duckman::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
-	uint32 value8, uint32 valueC, uint32 value10) {
-	uint32 tempThreadId = newTempThreadId();
-	debug(2, "Starting temp script thread %08X", tempThreadId);
-	newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp);
-	return tempThreadId;
-}
-
-void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-	byte *scriptCodeIp) {
-	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
-		scriptCodeIp, 0, 0, 0);
-	_threads->startThread(scriptThread);
-}
-
-uint32 IllusionsEngine_Duckman::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
-	uint32 tempThreadId = newTempThreadId();
-	TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
-		duration, isAbortable);
-	_threads->startThread(timerThread);
-	return tempThreadId;
-}
-
-uint32 IllusionsEngine_Duckman::newTempThreadId() {
-	uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
-	if (threadId > 65535) {
-		_nextTempThreadId = 0;
-		threadId = 2 * _scriptResource->_codeCount;
-	}
-	++_nextTempThreadId;
-	return 0x00020000 | threadId;
-}
-
-void IllusionsEngine_Duckman::initActiveScenes() {
-	_activeScenesCount = 0;
-	_activeScenes[0] = 0xEFEF;
-	pushActiveScene(0x10000);
-}
-
-void IllusionsEngine_Duckman::pushActiveScene(uint32 sceneId) {
-	++_activeScenesCount;
-	if (_activeScenesCount >= 6)
-		_activeScenesCount = 1;
-	_activeScenes[_activeScenesCount] = sceneId;
-}
-
-void IllusionsEngine_Duckman::popActiveScene() {
-	--_activeScenesCount;
-	if (_activeScenesCount == 0)
-		_activeScenesCount = 5;
-}
-
-bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (!progInfo)
-		return false;
-	pushActiveScene(sceneId);
-	uint resourcesCount;
-	uint32 *resources;
-	progInfo->getResources(resourcesCount, resources);
-	for (uint i = 0; i < resourcesCount; ++i)
-		_resSys->loadResource(resources[i], sceneId, 0);
-	return true;
-}
-
-bool IllusionsEngine_Duckman::enterScene(uint32 sceneId, uint32 threadId) {
-	if (loadScene(sceneId)) {
-		if (threadId)
-			startScriptThread(threadId, 0);
-		return true;
-	}
-	// TODO startScriptThread2(0x10002, 0x20001, 0);
-	return false;
-}
-
-void IllusionsEngine_Duckman::exitScene() {
-	popActiveScene();
-}
-
-bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId) {
-	uint32 currSceneId = getCurrentScene();
-	if (currSceneId != 0x10003)
-		dumpCurrSceneFiles(currSceneId, callerThreadId);
-	_threads->terminateThreads(callerThreadId);
-	_controls->destroyControls();
-	_resSys->unloadSceneResources(0x10003, 0x10001);
-	if (enterScene(sceneId, threadId)) {
-		// TODO GameStates_writeStates(sceneId, threadId);
-		return true;
-	}
-	return false;
-}
-
-void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
-	_threads->suspendThreads(threadId);
-	_controls->pauseControls();
-	_actorInstances->pauseByTag(sceneId);
-	_backgroundInstances->pauseByTag(sceneId);
-}
-
-void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
-	_backgroundInstances->unpauseByTag(sceneId);
-	_actorInstances->unpauseByTag(sceneId);
-	_controls->unpauseControls();
-	_threads->notifyThreads(threadId);
-}
-
-void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
-	// TODO
-}
-
-void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
-	// TODO UpdateFunctions_disableByTag(sceneId);
-	_threads->terminateActiveThreads(threadId);
-	_threads->terminateThreadsByTag(sceneId, threadId);
-	_controls->destroyActiveControls();
-	_resSys->unloadResourcesByTag(sceneId);
-}
-
-void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
-	_theSceneId = theSceneId;
-	_theThreadId = theThreadId;
-}
-
-bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (progInfo)
-		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
-	return false;
-}
-
-void IllusionsEngine_Duckman::reset() {
-	_scriptResource->_blockCounters.clear();
-	_scriptResource->_properties.clear();
-	// TODO script_sub_417FF0(1, 0);
-}
-
-uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
-	return _scriptResource->getObjectActorTypeId(objectId);
-}
-
-Common::Point IllusionsEngine_Duckman::convertMousePos(Common::Point mousePos) {
-	Common::Point screenOffsPt = _camera->getScreenOffset();
-	mousePos.x += screenOffsPt.x;
-	mousePos.y += screenOffsPt.y;
-	return mousePos;
-}
-
-void IllusionsEngine_Duckman::startCursorSequence() {
-	// NOTE Calls to startCursorSequence were put after calls to setCursorActorIndex
-	// to make the cursor switch more immediate. In the original these calls are swapped.
-	if (_cursor._actorIndex == 7)
-		_cursor._control->startSequenceActor(_cursor._sequenceId2, 2, 0);
-	else
-		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
-}
-
-int IllusionsEngine_Duckman::getCursorActorIndex() {
-	int result = _cursor._actorIndex;
-	do {
-		++result;
-		if (result > 13)
-			result = 1;
-	} while (!_cursor._field14[result - 1]);
-	return result;
-}
-
-void IllusionsEngine_Duckman::updateGameState2() {
-	Common::Point cursorPos = _input->getCursorPosition();
-	Common::Point convMousePos = convertMousePos(cursorPos);
-	int trackCursorIndex = -1;
-	bool foundOverlapped;
-	Control *overlappedControl;
-
-	_cursor._control->_actor->_position = cursorPos;
-
-	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
-
-	if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
-		trackCursorIndex = 10;
-	} else if (cursorPos.y >= 192 && !_camera->isAtPanLimit(2)) {
-		trackCursorIndex = 11;
-	} else if (cursorPos.x < 8 && !_camera->isAtPanLimit(3)) {
-		trackCursorIndex = 12;
-	} else if (cursorPos.x >= 312 && !_camera->isAtPanLimit(4)) {
-		trackCursorIndex = 13;
-	} else if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13) {
-		_cursor._actorIndex = _cursor._savedActorIndex;
-		if (_cursor._currOverlappedControl)
-			setCursorActorIndex(_cursor._actorIndex, 2, 0);
-		else
-			setCursorActorIndex(_cursor._actorIndex, 1, 0);
-		startCursorSequence();
-	}
-
-	if (trackCursorIndex >= 0) {
-		if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
-			_cursor._savedActorIndex = _cursor._actorIndex;
-		if (_cursor._actorIndex != trackCursorIndex) {
-			_cursor._actorIndex = trackCursorIndex;
-			setCursorActorIndex(_cursor._actorIndex, 1, 0);
-			startCursorSequence();
-		}
-		_cursor._currOverlappedControl = 0;
-		foundOverlapped = false;
-	}
-
-	if (foundOverlapped) {
-		if (_cursor._currOverlappedControl != overlappedControl) {
-			int cursorValue2 = 0;
-			if (overlappedControl->_flags & 2) {
-				if (_cursor._actorIndex != 3) {
-					_cursor._savedActorIndex = _cursor._actorIndex;
-					_cursor._actorIndex = 3;
-				}
-				if (overlappedControl->_flags & 0x40)
-					cursorValue2 = 1;
-			} else if (_cursor._actorIndex == 3) {
-				_cursor._actorIndex = _cursor._savedActorIndex;
-			}
-			setCursorActorIndex(_cursor._actorIndex, 2, cursorValue2);
-			startCursorSequence();
-			_cursor._currOverlappedControl = overlappedControl;
-		}
-	} else if (_cursor._currOverlappedControl) {
-		if (_cursor._actorIndex == 3)
-			_cursor._actorIndex = _cursor._savedActorIndex;
-		setCursorActorIndex(_cursor._actorIndex, 1, 0);
-		startCursorSequence();
-		_cursor._currOverlappedControl = 0;
-	}
-
-	if (_input->pollButton(1)) {
-		if (_cursor._currOverlappedControl) {
-			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
-		} else {
-			_cursor._position = convertMousePos(_cursor._control->_actor->_position);
-			// TODO clipMousePos(&_cursor._position);
-			if (_cursor._actorIndex == 10 || _cursor._actorIndex == 11 || _cursor._actorIndex == 12 || _cursor._actorIndex == 13)
-				runTriggerCause(1, _cursor._objectId, 0x40003);
-			else
-				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
-		}
-	} else if (_input->pollButton(2)) {
-		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
-			int newActorIndex = getCursorActorIndex();
-			if (newActorIndex != _cursor._actorIndex) {
-				_cursor._actorIndex = newActorIndex;
-				if (_cursor._currOverlappedControl)
-					setCursorActorIndex(_cursor._actorIndex, 2, 0);
-				else
-					setCursorActorIndex(_cursor._actorIndex, 1, 0);
-				startCursorSequence();
-			}
-		}
-	} else if (_input->pollButton(8)) {
-		if (_cursor._field14[0] == 1) {
-			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
-		} else if (_cursor._field14[1] == 1) {
-			runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
-		}
-	}
-
-}
-
-void IllusionsEngine_Duckman::playSoundEffect(int index) {
-	uint32 soundEffectId = 0;
-	uint32 *soundIds = _scriptResource->_soundIds;
-	switch (index) {
-	case 1:
-		soundEffectId = soundIds[0];
-		break;
-	case 2:
-		soundEffectId = soundIds[1];
-		break;
-	case 3:
-		soundEffectId = soundIds[2];
-		break;
-	case 4:
-		soundEffectId = soundIds[3];
-		break;
-	case 5:
-		soundEffectId = soundIds[4];
-		break;
-	case 6:
-		soundEffectId = soundIds[getRandom(4) + 5];
-		break;
-	case 7:
-		soundEffectId = soundIds[getRandom(4) + 9];
-		break;
-	case 8:
-		soundEffectId = soundIds[13];
-		break;
-	case 9:
-		soundEffectId = soundIds[14];
-		break;
-	case 10:
-		soundEffectId = soundIds[15];
-		break;
-	case 11:
-		soundEffectId = soundIds[16];
-		break;
-	case 12:
-		soundEffectId = soundIds[getRandom(4) + 17];
-		break;
-	case 13:
-		soundEffectId = soundIds[21];
-		break;
-	case 14:
-		soundEffectId = soundIds[22];
-		break;
-	case 15:
-		soundEffectId = soundIds[23];
-		break;
-	case 16:
-		soundEffectId = soundIds[24];
-		break;
-	case 17:
-		soundEffectId = soundIds[25];
-		break;
-	case 18:
-		soundEffectId = soundIds[26];
-		break;
-	}
-	if (soundEffectId)
-		_soundMan->playSound(soundEffectId, 255, 0);
-}
-
-bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(getCurrentScene() & 0xFFFF);
-	bool found =
-		progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
-		progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
-	if (!found) {
-		progInfo = _scriptResource->getProgInfo(3);
-		found =
-			progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
-			progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
-	}
-	return found;
-}
-
-uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
-	// TODO
-	debug("runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
-	uint32 triggerThreadId;
-
-	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
-		return 0;
-
-	bool flag = false;
-	if (_scriptResource->_properties.get(0x000E003C)) {
-		if (verbId == 7 && objectId == 0x40003) {
-			playSoundEffect(7);
-			flag = true;
-		} else if (objectId == 0x40003) {
-			playSoundEffect(14);
-			flag = true;
-		} else if (verbId == 3) {
-			playSoundEffect(16);
-			flag = true;
-		} else if (verbId == 2) {
-			flag = true;
-		}
-	}
-
-	if (!flag) {
-		if (objectId == 0x40003) {
-			playSoundEffect(14);
-		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) {
-			playSoundEffect(15);
-		} else if (verbId == 7 && _scriptResource->getMainActorObjectId() == objectId) {
-			playSoundEffect(15);
-		} else if (verbId == 1) {
-			playSoundEffect(1);
-		} else if (verbId == 2) {
-			playSoundEffect(2);
-		} else if (verbId == 3) {
-			playSoundEffect(3);
-		} else if (verbId == 4 || verbId == 7) {
-			playSoundEffect(4);
-		} else if (verbId == 9) {
-			playSoundEffect(5);
-		}
-	}
-
-	uint32 tempThreadId = newTempThreadId();
-	debug("Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
-	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
-		triggerThreadId);
-	_threads->startThread(causeThread);
-
-	return tempThreadId;
-}
-
-void IllusionsEngine_Duckman::addDialogItem(int16 choiceJumpOffs, uint32 sequenceId) {
-	DialogItem dialogItem;
-	dialogItem._choiceJumpOffs = choiceJumpOffs;
-	dialogItem._sequenceId = sequenceId;
-	_dialogItems.push_back(dialogItem);
-}
-
-void IllusionsEngine_Duckman::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId) {
-	static const uint32 kDialogSequenceIds[] = {
-		0,
-		0x6049C, 0x6049C, 0x6047A, 0x6049D,
-		0x60479, 0x6049E, 0x6049F, 0x60468
-	};
-	if (_dialogItems.size() == 1) {
-		*choiceOfsPtr = _dialogItems[0]._choiceJumpOffs;
-		notifyThreadId(callerThreadId);
-	} else {
-		if (!_cursor._control) {
-			Common::Point pos = getNamedPointPosition(0x70001);
-			_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
-			_cursor._control = _dict->getObjectControl(0x40004);
-		}
-		_cursor._control->appearActor();
-		setCursorActorIndex(6, 1, 0);
-
-		_cursor._gameState = 3;
-		_cursor._notifyThreadId30 = callerThreadId;
-		_cursor._dialogItemsCount = 0;
-		_cursor._overlappedObjectId = 0;
-		_cursor._op113_choiceOfsPtr = choiceOfsPtr;
-		_cursor._currOverlappedControl = 0;
-
-		/* TODO?
-		if (!_input->getCursorMouseMode())
-			_input->setMousePos((Point)0xBC0014);
-		*/
-
-		_cursor._dialogItemsCount = _dialogItems.size();
-		Common::Point placePt(20, 188);
-
-		for (uint i = 1; i <= _dialogItems.size(); ++i) {
-			DialogItem &dialogItem = _dialogItems[_dialogItems.size() - i];
-			_controls->placeDialogItem(i + 1, actorTypeId, dialogItem._sequenceId, placePt, dialogItem._choiceJumpOffs);
-			placePt.x += 40;
-		}
-
-		Common::Point placePt2 = getNamedPointPosition(0x700C3);
-		_controls->placeActor(0x5006E, placePt2, kDialogSequenceIds[_dialogItems.size()], 0x40148, 0);
-		Control *control = _dict->getObjectControl(0x40148);
-		control->_flags |= 8;
-		playSoundEffect(8);
-	}
-
-	_dialogItems.clear();
-
-}
-
-void IllusionsEngine_Duckman::updateDialogState() {
-	Common::Point mousePos = _input->getCursorPosition();
-	// TODO Handle keyboard input
-	_cursor._control->_actor->_position = mousePos;
-	mousePos = convertMousePos(mousePos);
-
-	Control *currOverlappedControl = _cursor._currOverlappedControl;
-	Control *newOverlappedControl;
-	
-	if (_controls->getDialogItemAtPos(_cursor._control, mousePos, &newOverlappedControl)) {
-		if (currOverlappedControl != newOverlappedControl) {
-			newOverlappedControl->setActorIndex(2);
-			newOverlappedControl->startSequenceActor(newOverlappedControl->_actor->_sequenceId, 2, 0);
-			if (currOverlappedControl) {
-				currOverlappedControl->setActorIndex(1);
-				currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
-			}
-			playSoundEffect(10);
-			startCursorSequence();
-			setCursorActorIndex(6, 2, 0);
-			_cursor._currOverlappedControl = newOverlappedControl;
-			_cursor._overlappedObjectId = newOverlappedControl->_objectId;
-		}
-	} else if (currOverlappedControl) {
-		currOverlappedControl->setActorIndex(1);
-		currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
-		playSoundEffect(10);
-		_cursor._currOverlappedControl = 0;
-		_cursor._overlappedObjectId = 0;
-		startCursorSequence();
-		setCursorActorIndex(6, 1, 0);
-	}
-
-	if (_input->pollButton(1)) {
-		if (_cursor._currOverlappedControl) {
-			playSoundEffect(9);
-			*_cursor._op113_choiceOfsPtr = _cursor._currOverlappedControl->_actor->_choiceJumpOffs;
-			_controls->destroyDialogItems();
-			Control *control = _dict->getObjectControl(0x40148);
-			_controls->destroyControl(control);
-			notifyThreadId(_cursor._notifyThreadId30);
-			_cursor._notifyThreadId30 = 0;
-			_cursor._gameState = 2;
-			_cursor._dialogItemsCount = 0;
-			_cursor._overlappedObjectId = 0;
-			_cursor._op113_choiceOfsPtr = 0;
-			_cursor._control->disappearActor();
-		}
-	}
-
-}
-
-void IllusionsEngine_Duckman::initInventory() {
-	_inventorySlots.push_back(DMInventorySlot( 64,  52));
-	_inventorySlots.push_back(DMInventorySlot(112,  52));
-	_inventorySlots.push_back(DMInventorySlot(160,  52));
-	_inventorySlots.push_back(DMInventorySlot(208,  52));
-	_inventorySlots.push_back(DMInventorySlot(255,  52));
-	_inventorySlots.push_back(DMInventorySlot( 64,  84));
-	_inventorySlots.push_back(DMInventorySlot(112,  84));
-	_inventorySlots.push_back(DMInventorySlot(160,  84));
-	_inventorySlots.push_back(DMInventorySlot(208,  84));
-	_inventorySlots.push_back(DMInventorySlot(255,  84));
-	_inventorySlots.push_back(DMInventorySlot( 64, 116));
-	_inventorySlots.push_back(DMInventorySlot(112, 116));
-	_inventorySlots.push_back(DMInventorySlot(160, 116));
-	_inventorySlots.push_back(DMInventorySlot(208, 116));
-	_inventorySlots.push_back(DMInventorySlot(255, 116));
-	_inventorySlots.push_back(DMInventorySlot( 64, 148));
-	_inventorySlots.push_back(DMInventorySlot(112, 148));
-	_inventorySlots.push_back(DMInventorySlot(160, 148));
-	_inventorySlots.push_back(DMInventorySlot(208, 148));
-	_inventorySlots.push_back(DMInventorySlot(255, 148));
-	_inventoyItems.push_back(DMInventoryItem(0x40011, 0xE005B));
-	_inventoyItems.push_back(DMInventoryItem(0x40099, 0xE001B));
-	_inventoyItems.push_back(DMInventoryItem(0x4000F, 0xE000C));
-	_inventoyItems.push_back(DMInventoryItem(0x40042, 0xE0012));
-	_inventoyItems.push_back(DMInventoryItem(0x40044, 0xE000F));
-	_inventoyItems.push_back(DMInventoryItem(0x40029, 0xE000D));
-	_inventoyItems.push_back(DMInventoryItem(0x400A7, 0xE005D));
-	_inventoyItems.push_back(DMInventoryItem(0x40096, 0xE001C));
-	_inventoyItems.push_back(DMInventoryItem(0x40077, 0xE0010));
-	_inventoyItems.push_back(DMInventoryItem(0x4008A, 0xE0033));
-	_inventoyItems.push_back(DMInventoryItem(0x4004B, 0xE0045));
-	_inventoyItems.push_back(DMInventoryItem(0x40054, 0xE0021));
-	_inventoyItems.push_back(DMInventoryItem(0x400C6, 0xE005A));
-	_inventoyItems.push_back(DMInventoryItem(0x4000B, 0xE005E));
-	_inventoyItems.push_back(DMInventoryItem(0x4005F, 0xE0016));
-	_inventoyItems.push_back(DMInventoryItem(0x40072, 0xE0017));
-	_inventoyItems.push_back(DMInventoryItem(0x400AA, 0xE005F));
-	_inventoyItems.push_back(DMInventoryItem(0x400B8, 0xE0050));
-	_inventoyItems.push_back(DMInventoryItem(0x4001F, 0xE001A));
-	_inventoyItems.push_back(DMInventoryItem(0x40095, 0xE0060));
-	_inventoyItems.push_back(DMInventoryItem(0x40041, 0xE0053));
-	_savedInventoryActorIndex = 0;
-}
-
-void IllusionsEngine_Duckman::openInventory() {
-
-	for (uint i = 0; i < _inventorySlots.size(); ++i) {
-		DMInventorySlot *inventorySlot = &_inventorySlots[i];
-		if (inventorySlot->_objectId) {
-			DMInventoryItem *inventoryItem = findInventoryItem(inventorySlot->_objectId);
-			if (!_scriptResource->_properties.get(inventoryItem->_propertyId))
-				inventorySlot->_objectId = 0;
-		}
-	}
-
-	for (uint i = 0; i < _inventoyItems.size(); ++i) {
-		DMInventoryItem *inventoryItem = &_inventoyItems[i];
-		if (_scriptResource->_properties.get(inventoryItem->_propertyId)) {
-			DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
-			if (inventorySlot) {
-				Control *control = getObjectControl(inventoryItem->_objectId);
-				control->setActorPosition(inventorySlot->_position);
-				control->appearActor();
-			} else {
-				addInventoryItem(inventoryItem->_objectId);
-			}
-		}
-	}
-
-}
-
-void IllusionsEngine_Duckman::addInventoryItem(uint32 objectId) {
-	DMInventorySlot *DMInventorySlot = findInventorySlot(0);
-	DMInventorySlot->_objectId = objectId;
-	Control *control = getObjectControl(objectId);
-	control->setActorPosition(DMInventorySlot->_position);
-	control->appearActor();
-}
-
-void IllusionsEngine_Duckman::clearInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
-		if (_inventorySlots[i]._objectId == objectId)
-			_inventorySlots[i]._objectId = 0;
-}
-
-void IllusionsEngine_Duckman::putBackInventoryItem() {
-	Common::Point mousePos = _input->getCursorPosition();
-	if (_cursor._objectId) {
-		DMInventorySlot *inventorySlot = findInventorySlot(_cursor._objectId);
-		if (inventorySlot)
-			inventorySlot->_objectId = 0;
-		inventorySlot = findClosestInventorySlot(mousePos);
-		inventorySlot->_objectId = _cursor._objectId;
-		Control *control = getObjectControl(_cursor._objectId);
-		control->setActorPosition(inventorySlot->_position);
-		control->appearActor();
-		_cursor._actorIndex = 7;
-		stopCursorHoldingObject();
-		_cursor._actorIndex = 2;
-		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
-		if (_cursor._currOverlappedControl)
-			setCursorActorIndex(_cursor._actorIndex, 2, 0);
-		else
-			setCursorActorIndex(_cursor._actorIndex, 1, 0);
-	}
-}
-
-DMInventorySlot *IllusionsEngine_Duckman::findInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
-		if (_inventorySlots[i]._objectId == objectId)
-			return &_inventorySlots[i];
-	return 0;
-}
-
-DMInventoryItem *IllusionsEngine_Duckman::findInventoryItem(uint32 objectId) {
-	for (uint i = 0; i < _inventoyItems.size(); ++i)
-		if (_inventoyItems[i]._objectId == objectId)
-			return &_inventoyItems[i];
-	return 0;
-}
-
-DMInventorySlot *IllusionsEngine_Duckman::findClosestInventorySlot(Common::Point pos) {
-	int minDistance = 0xFFFFFF;
-	DMInventorySlot *minInventorySlot = 0;
-	for (uint i = 0; i < _inventorySlots.size(); ++i) {
-		DMInventorySlot *inventorySlot = &_inventorySlots[i];
-		if (inventorySlot->_objectId == 0) {
-			int16 deltaX = ABS(inventorySlot->_position.x - pos.x);
-			int16 deltaY = ABS(inventorySlot->_position.y - pos.y);
-			int distance = deltaX * deltaX + deltaY * deltaY;
-			if (inventorySlot->_objectId == 0 && distance < minDistance) {
-				minDistance = distance;
-				minInventorySlot = inventorySlot;
-			}
-		}
-	}
-	return minInventorySlot;
-}
-
-void IllusionsEngine_Duckman::addPropertyTimer(uint32 propertyId) {
-	PropertyTimer *propertyTimer;
-	if (findPropertyTimer(propertyId, propertyTimer) || findPropertyTimer(0, propertyTimer)) {
-		propertyTimer->_propertyId = propertyId;
-		propertyTimer->_startTime = 0;
-		propertyTimer->_duration = 0;
-		propertyTimer->_endTime = 0;
-	}
-}
-
-void IllusionsEngine_Duckman::setPropertyTimer(uint32 propertyId, uint32 duration) {
-	PropertyTimer *propertyTimer;
-	if (findPropertyTimer(propertyId, propertyTimer)) {
-		propertyTimer->_startTime = getCurrentTime();
-		propertyTimer->_duration = duration;
-		propertyTimer->_endTime = duration + propertyTimer->_startTime;
-	}
-	_scriptResource->_properties.set(propertyId, false);
-	if (!_propertyTimersActive) {
-		_updateFunctions->add(29, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
-			(this, &IllusionsEngine_Duckman::updatePropertyTimers));
-		_propertyTimersActive = true;
-	}
-}
-
-void IllusionsEngine_Duckman::removePropertyTimer(uint32 propertyId) {
-	PropertyTimer *propertyTimer;
-	if (findPropertyTimer(propertyId, propertyTimer))
-		propertyTimer->_propertyId = 0;
-	_scriptResource->_properties.set(propertyId, true);
-}
-
-bool IllusionsEngine_Duckman::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
-	for (uint i = 0; i < kPropertyTimersCount; ++i)
-		if (_propertyTimers[i]._propertyId == propertyId) {
-			propertyTimer = &_propertyTimers[i];
-			return true;
-		}
-	return false;
-}
-
-int IllusionsEngine_Duckman::updatePropertyTimers(uint flags) {
-	int result = 1;
-	uint32 currTime = getCurrentTime();
-	if (_pauseCtr <= 0) {
-		if (_propertyTimersPaused) {
-			for (uint i = 0; i < kPropertyTimersCount; ++i) {
-				PropertyTimer &propertyTimer = _propertyTimers[i];
-				propertyTimer._startTime = currTime;
-				propertyTimer._endTime = currTime + propertyTimer._duration;
-			}
-			_propertyTimersPaused = false;
-		}
-		if (flags & 1) {
-			_propertyTimersActive = false;
-			_propertyTimersPaused = false;
-			result = 2;
-		} else {
-			bool timersActive = false;
-			for (uint i = 0; i < kPropertyTimersCount; ++i) {
-				PropertyTimer &propertyTimer = _propertyTimers[i];
-				if (propertyTimer._propertyId) {
-					timersActive = true;
-					if (!_scriptResource->_properties.get(propertyTimer._propertyId) &&
-						isTimerExpired(propertyTimer._startTime, propertyTimer._endTime))
-						_scriptResource->_properties.set(propertyTimer._propertyId, true);
-				}
-			}
-			if (!timersActive) {
-				_propertyTimersActive = false;
-				_propertyTimersPaused = false;
-				result = 2;
-			}
-		}
-	} else {
-		if (!_propertyTimersPaused) {
-			for (uint i = 0; i < kPropertyTimersCount; ++i) {
-				PropertyTimer &propertyTimer = _propertyTimers[i];
-				propertyTimer._duration -= getDurationElapsed(propertyTimer._startTime, propertyTimer._endTime);
-			}
-			_propertyTimersPaused = true;
-		}
-		result = 1;
-	}
-	return result;
-}
-
-// Special code
-
-typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
-#define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &IllusionsEngine_Duckman::func);
-
-void IllusionsEngine_Duckman::initSpecialCode() {
-	SPECIAL(0x00160001, spcStartScreenShaker);
-	SPECIAL(0x00160002, spcSetCursorHandMode);
-	SPECIAL(0x00160003, spcResetChinesePuzzle);
-	SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
-	SPECIAL(0x00160005, spcOpenInventory);
-	SPECIAL(0x00160007, spcPutBackInventoryItem);
-	SPECIAL(0x00160008, spcClearInventorySlot);
-	SPECIAL(0x0016000A, spcAddPropertyTimer);
-	SPECIAL(0x0016000B, spcSetPropertyTimer);
-	SPECIAL(0x0016000C, spcRemovePropertyTimer);
-	SPECIAL(0x00160010, spcCenterNewspaper);
-	SPECIAL(0x00160014, spcUpdateObject272Sequence);
-	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
-}
-
-#undef SPECIAL
-
-void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCall) {
-	SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
-	if (it != _specialCodeMap.end()) {
-		(*(*it)._value)(opCall);
-	} else {
-		debug("IllusionsEngine_Duckman::runSpecialCode() Unimplemented special code %08X", specialCodeId);
-		notifyThreadId(opCall._threadId);
-	}
-}
-
-// TODO Move to separate file
-
-static const ScreenShakerPoint kShakerPoints0[] = {
-	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
-};
-
-static const ScreenShakeEffect kShakerEffect0 = {
-	6, 5, kShakerPoints0
-};
-
-static const ScreenShakerPoint kShakerPoints1[] = {
-	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
-	{ 1,  2}, {0, -1} 
-};
-
-static const ScreenShakeEffect kShakerEffect1 = {
-	9, 2, kShakerPoints1
-};
-
-static const ScreenShakerPoint kShakerPoints2[] = {
-	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
-	{0,  1}, {0, -1},
-};
-
-static const ScreenShakeEffect kShakerEffect2 = {
-	9, 2, kShakerPoints2
-};
-
-static const ScreenShakerPoint kShakerPoints3[] = {
-	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
-};
-
-static const ScreenShakeEffect kShakerEffect3 = {
-	5, 2, kShakerPoints3
-};
-
-static const ScreenShakerPoint kShakerPoints4[] = {
-	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
-};
-
-static const ScreenShakeEffect kShakerEffect4 = {
-	8, 5, kShakerPoints4
-};
-
-static const ScreenShakerPoint kShakerPoints5[] = {
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
-};
-
-static const ScreenShakeEffect kShakerEffect5 = {
-	31, 2, kShakerPoints5
-};
-
-static const ScreenShakeEffect *kShakerEffects[] = {
-	&kShakerEffect0,
-	&kShakerEffect1,
-	&kShakerEffect2,
-	&kShakerEffect3,
-	&kShakerEffect4,
-	&kShakerEffect5
-};
-
-void IllusionsEngine_Duckman::spcStartScreenShaker(OpCall &opCall) {
-	ARG_BYTE(effect);
-	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
-	startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
-	ARG_BYTE(mode);
-	setCursorHandMode(mode);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcResetChinesePuzzle(OpCall &opCall) {
-	_scriptResource->_properties.set(0x000E0018, false);
-	_scriptResource->_properties.set(0x000E0019, false);
-	_chinesePuzzleIndex = 0;
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcAddChinesePuzzleAnswer(OpCall &opCall) {
-	ARG_BYTE(answer);
-	_chinesePuzzleAnswers[_chinesePuzzleIndex++] = answer;
-	if (_chinesePuzzleIndex == 3) {
-		_scriptResource->_properties.set(0x000E0018, true);
-		if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 5) ||
-			(_chinesePuzzleAnswers[0] == 5 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
-			_scriptResource->_properties.set(0x000E0019, true);
-		else if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 1) ||
-			(_chinesePuzzleAnswers[0] == 1 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
-			_scriptResource->_properties.set(0x000E00A0, true);
-	}
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
-	openInventory();
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcPutBackInventoryItem(OpCall &opCall) {
-	putBackInventoryItem();
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcClearInventorySlot(OpCall &opCall) {
-	ARG_UINT32(objectId);
-	clearInventorySlot(objectId);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcAddPropertyTimer(OpCall &opCall) {
-	ARG_UINT32(propertyId);
-	addPropertyTimer(propertyId);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcSetPropertyTimer(OpCall &opCall) {
-	ARG_INT16(propertyNum);
-	ARG_INT16(duration);
-	setPropertyTimer(propertyNum | 0xE0000, duration);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcRemovePropertyTimer(OpCall &opCall) {
-	ARG_UINT32(propertyId);
-	removePropertyTimer(propertyId);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcCenterNewspaper(OpCall &opCall) {
-	Control *control = getObjectControl(0x40017);
-	control->_flags |= 8;
-	control->_actor->_position.x = 160;
-	control->_actor->_position.y = 100;
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcSetCursorInventoryMode(OpCall &opCall) {
-	ARG_BYTE(mode);
-	ARG_BYTE(value);
-	setCursorInventoryMode(mode, value);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcUpdateObject272Sequence(OpCall &opCall) {
-	byte flags = 0;
-	uint32 sequenceId;
-	if (_scriptResource->_properties.get(0x000E0085))
-		flags |= 1;
-	if (_scriptResource->_properties.get(0x000E0083))
-		flags |= 2;
-	if (_scriptResource->_properties.get(0x000E0084))
-		flags |= 4;
-	switch (flags) {
-	case 0:
-		sequenceId = 0x603C1;
-		break;
-	case 1:
-		sequenceId = 0x603BF;
-		break;
-	case 2:
-		sequenceId = 0x603C2;
-		break;
-	case 3:
-		sequenceId = 0x603C0;
-		break;
-	case 4:
-		sequenceId = 0x603C3;
-		break;
-	case 5:
-		sequenceId = 0x603C5;
-		break;
-	case 6:
-		sequenceId = 0x603C4;
-		break;
-	case 7:
-		sequenceId = 0x603C6;
-		break;
-	default:
-		sequenceId = 0x603C1;
-		break;
-	}
-	Control *control = getObjectControl(0x40110);
-	control->startSequenceActor(sequenceId, 2, opCall._threadId);
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/illusions_duckman.h b/engines/illusions/illusions_duckman.h
deleted file mode 100644
index 7f6c427..0000000
--- a/engines/illusions/illusions_duckman.h
+++ /dev/null
@@ -1,264 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_ILLUSIONS_DUCKMAN_H
-#define ILLUSIONS_ILLUSIONS_DUCKMAN_H
-
-#include "illusions/illusions.h"
-#include "common/algorithm.h"
-#include "common/stack.h"
-
-namespace Illusions {
-
-class Dictionary;
-class ScriptStack;
-
-struct Cursor_Duckman {
-	int _gameState;
-	Control *_control;
-	Common::Point _position;
-	uint32 _objectId;
-	int _actorIndex;
-	int _savedActorIndex;
-	bool _field14[14];
-	Control *_currOverlappedControl;
-	uint32 _sequenceId1;
-	uint32 _sequenceId2;
-	uint32 _notifyThreadId30;
-	int16 *_op113_choiceOfsPtr;
-	int _dialogItemsCount;
-	uint32 _overlappedObjectId;
-	uint32 _field3C;
-	uint32 _field40;
-};
-
-struct DialogItem {
-	int16 _choiceJumpOffs;
-	uint32 _sequenceId;
-};
-
-struct DMInventorySlot {
-	Common::Point _position;
-	uint32 _objectId;
-	DMInventorySlot() : _objectId(0) {}
-	DMInventorySlot(int16 x, int16 y) : _objectId(0), _position(x, y) {}
-};
-
-struct DMInventoryItem {
-	uint32 _objectId;
-	uint32 _propertyId;
-	DMInventoryItem() : _objectId(0) {}
-	DMInventoryItem(uint32 objectId, uint32 propertyId)
-		: _objectId(objectId), _propertyId(propertyId) {}
-};
-
-struct ScreenShakerPoint {
-	int16 x, y;
-};
-
-struct ScreenShakeEffect {
-	uint32 _duration;
-	uint _pointsCount;
-	const ScreenShakerPoint *_points;
-};
-
-struct ScreenShaker {
-	uint _pointsIndex;
-	uint _pointsCount;
-	bool _finished;
-	uint32 _duration;
-	uint32 _nextTime;
-	uint32 _notifyThreadId;
-	const ScreenShakerPoint *_points;
-};
-
-struct PropertyTimer {
-	uint32 _propertyId;
-	uint32 _startTime;
-	uint32 _duration;
-	uint32 _endTime;
-	PropertyTimer() : _propertyId(0) {}
-};
-
-const uint kPropertyTimersCount = 6;
-
-struct OpCall;
-
-typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
-typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
-typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
-
-class IllusionsEngine_Duckman : public IllusionsEngine {
-public:
-	IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd);
-protected:
-	virtual Common::Error run();
-	virtual bool hasFeature(EngineFeature f) const;
-public:	
-
-	// TODO ActiveScenes _activeScenes;
-	uint32 _prevSceneId;
-	uint32 _theSceneId;
-	uint32 _theThreadId;
-	uint32 _globalSceneId;
-
-	uint _activeScenesCount;
-	uint32 _activeScenes[6];
-
-	Cursor_Duckman _cursor;
-	Control *_currWalkOverlappedControl;
-
-	Common::Array<DialogItem> _dialogItems;
-
-	int _savedInventoryActorIndex;
-	Common::Array<DMInventorySlot> _inventorySlots;
-	Common::Array<DMInventoryItem> _inventoyItems;
-
-	ScreenShaker *_screenShaker;
-
-	PropertyTimer _propertyTimers[kPropertyTimersCount];
-	bool _propertyTimersActive;
-	bool _propertyTimersPaused;
-
-	uint _chinesePuzzleIndex;
-	byte _chinesePuzzleAnswers[3];
-
-	SpecialCodeMap _specialCodeMap;
-
-	void initUpdateFunctions();
-	int updateScript(uint flags);
-
-	void startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId);
-	int updateScreenShaker(uint flags);
-
-	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
-
-	void setDefaultTextCoords();
-
-	void loadSpecialCode(uint32 resId);
-	void unloadSpecialCode(uint32 resId);
-	void notifyThreadId(uint32 &threadId);
-	bool testMainActorFastWalk(Control *control);
-	bool testMainActorCollision(Control *control);
-	Control *getObjectControl(uint32 objectId);
-	Common::Point getNamedPointPosition(uint32 namedPointId);
-	uint32 getPriorityFromBase(int16 priority);
-	uint32 getCurrentScene();
-	uint32 getPrevScene();
-
-	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
-	void setCursorControlRoutine(Control *control);
-	void placeCursorControl(Control *control, uint32 sequenceId);
-	void setCursorControl(Control *control);
-	void showCursor();
-	void hideCursor();
-	void initCursor();
-	void setCursorActorIndex(int actorIndex, int a, int b);
-	void enableCursorVerb(int verbNum);
-	void disableCursorVerb(int verbNum);
-	void setCursorHandMode(int mode);
-	void setCursorInventoryMode(int mode, int value);
-	void startCursorHoldingObject(uint32 objectId, uint32 sequenceId);
-	void stopCursorHoldingObject();
-	void cursorControlRoutine(Control *control, uint32 deltaTime);
-
-	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
-	void startScriptThread(uint32 threadId, uint32 callingThreadId);
-	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
-	uint32 startTimerThread(uint32 duration, uint32 threadId);
-	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
-	uint32 startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
-		uint32 sequenceId2, uint32 callingThreadId);
-	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
-		uint32 value8, uint32 valueC, uint32 value10);
-	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
-		byte *scriptCodeIp);
-	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
-	uint32 newTempThreadId();
-
-	void initActiveScenes();
-	void pushActiveScene(uint32 sceneId);
-	void popActiveScene();
-	bool loadScene(uint32 sceneId);
-	bool enterScene(uint32 sceneId, uint32 threadId);
-	void exitScene();
-	bool changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId);
-	void enterPause(uint32 sceneId, uint32 threadId);
-	void leavePause(uint32 sceneId, uint32 threadId);
-	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
-	void dumpCurrSceneFiles(uint32 sceneId, uint32 threadId);
-
-	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
-	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
-	void reset();
-	
-	uint32 getObjectActorTypeId(uint32 objectId);
-	
-	Common::Point convertMousePos(Common::Point mousePos);
-	void startCursorSequence();
-	int getCursorActorIndex();
-	void updateGameState2();
-	void playSoundEffect(int index);
-	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
-	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
-
-	void addDialogItem(int16 choiceJumpOffs, uint32 sequenceId);
-	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
-	void updateDialogState();
-
-	void initInventory();
-	void openInventory();
-	void addInventoryItem(uint32 objectId);
-	void clearInventorySlot(uint32 objectId);
-	void putBackInventoryItem();
-	DMInventorySlot *findInventorySlot(uint32 objectId);
-	DMInventoryItem *findInventoryItem(uint32 objectId);
-	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
-
-	void addPropertyTimer(uint32 propertyId);
-	void setPropertyTimer(uint32 propertyId, uint32 duration);
-	void removePropertyTimer(uint32 propertyId);
-	bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
-	int updatePropertyTimers(uint flags);
-
-	// Special code
-	void initSpecialCode();
-	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
-	void spcStartScreenShaker(OpCall &opCall);
-	void spcSetCursorHandMode(OpCall &opCall);
-	void spcResetChinesePuzzle(OpCall &opCall);
-	void spcAddChinesePuzzleAnswer(OpCall &opCall);
-	void spcOpenInventory(OpCall &opCall);
-	void spcPutBackInventoryItem(OpCall &opCall);
-	void spcClearInventorySlot(OpCall &opCall);
-	void spcAddPropertyTimer(OpCall &opCall);
-	void spcSetPropertyTimer(OpCall &opCall);
-	void spcRemovePropertyTimer(OpCall &opCall);
-	void spcCenterNewspaper(OpCall &opCall);
-	void spcSetCursorInventoryMode(OpCall &opCall);
-	void spcUpdateObject272Sequence(OpCall &opCall);
-
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 05cdb22..05876d2 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -6,15 +6,15 @@ MODULE_OBJS := \
 	bbdou/bbdou_cursor.o \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
+	bbdou/illusions_bbdou.o \
 	camera.o \
 	cursor.o \
 	detection.o \
 	dictionary.o \
+	duckman/illusions_duckman.o \
 	fixedpoint.o \
 	graphics.o \
 	illusions.o \
-	illusions_bbdou.o \
-	illusions_duckman.o \
 	input.o \
 	pathfinder.o \
 	resources/actorresource.o \
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
index 10c284d..3e7241b 100644
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/scriptopcodes_bbdou.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_bbdou.h"
+#include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/scriptopcodes_bbdou.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 0fb65cc..3c7909e 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_duckman.h"
+#include "illusions/duckman/illusions_duckman.h"
 #include "illusions/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
diff --git a/engines/illusions/threads/causethread_duckman.cpp b/engines/illusions/threads/causethread_duckman.cpp
index 091b85c..df5b1fa 100644
--- a/engines/illusions/threads/causethread_duckman.cpp
+++ b/engines/illusions/threads/causethread_duckman.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_duckman.h"
+#include "illusions/duckman/illusions_duckman.h"
 #include "illusions/threads/causethread_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/input.h"
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 84aa039..20df1e4 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "illusions/illusions_duckman.h"
+#include "illusions/duckman/illusions_duckman.h"
 #include "illusions/threads/talkthread_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/dictionary.h"


Commit: 70f83137b142d3158efb5dbcf82e7ab36174693a
    https://github.com/scummvm/scummvm/commit/70f83137b142d3158efb5dbcf82e7ab36174693a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move Duckman special code to own class and file

Changed paths:
  A engines/illusions/duckman/duckman_specialcode.cpp
  A engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
new file mode 100644
index 0000000..8626fd1
--- /dev/null
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -0,0 +1,265 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/actor.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/scriptopcodes_duckman.h"
+#include "illusions/specialcode.h"
+
+#include "engines/util.h"
+
+namespace Illusions {
+
+// Duckman_SpecialCode
+
+DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
+	: _vm(vm) {
+}
+
+DuckmanSpecialCode::~DuckmanSpecialCode() {
+}
+
+typedef Common::Functor1Mem<OpCall&, void, DuckmanSpecialCode> SpecialCodeFunctionDM;
+#define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &DuckmanSpecialCode::func);
+
+void DuckmanSpecialCode::init() {
+	// TODO
+	SPECIAL(0x00160001, spcStartScreenShaker);
+	SPECIAL(0x00160002, spcSetCursorHandMode);
+	SPECIAL(0x00160003, spcResetChinesePuzzle);
+	SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
+	SPECIAL(0x00160005, spcOpenInventory);
+	SPECIAL(0x00160007, spcPutBackInventoryItem);
+	SPECIAL(0x00160008, spcClearInventorySlot);
+	SPECIAL(0x0016000A, spcAddPropertyTimer);
+	SPECIAL(0x0016000B, spcSetPropertyTimer);
+	SPECIAL(0x0016000C, spcRemovePropertyTimer);
+	SPECIAL(0x00160010, spcCenterNewspaper);
+	SPECIAL(0x00160014, spcUpdateObject272Sequence);
+	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
+}
+
+#undef SPECIAL
+
+void DuckmanSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
+	SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
+	if (it != _specialCodeMap.end()) {
+		(*(*it)._value)(opCall);
+	} else {
+		debug("DuckmanSpecialCode::run() Unimplemented special code %08X", specialCodeId);
+		_vm->notifyThreadId(opCall._threadId);
+	}
+}
+
+// TODO Move to separate file
+
+static const ScreenShakerPoint kShakerPoints0[] = {
+	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
+};
+
+static const ScreenShakeEffect kShakerEffect0 = {
+	6, 5, kShakerPoints0
+};
+
+static const ScreenShakerPoint kShakerPoints1[] = {
+	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
+	{ 1,  2}, {0, -1} 
+};
+
+static const ScreenShakeEffect kShakerEffect1 = {
+	9, 2, kShakerPoints1
+};
+
+static const ScreenShakerPoint kShakerPoints2[] = {
+	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
+	{0,  1}, {0, -1},
+};
+
+static const ScreenShakeEffect kShakerEffect2 = {
+	9, 2, kShakerPoints2
+};
+
+static const ScreenShakerPoint kShakerPoints3[] = {
+	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect3 = {
+	5, 2, kShakerPoints3
+};
+
+static const ScreenShakerPoint kShakerPoints4[] = {
+	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
+};
+
+static const ScreenShakeEffect kShakerEffect4 = {
+	8, 5, kShakerPoints4
+};
+
+static const ScreenShakerPoint kShakerPoints5[] = {
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect5 = {
+	31, 2, kShakerPoints5
+};
+
+static const ScreenShakeEffect *kShakerEffects[] = {
+	&kShakerEffect0,
+	&kShakerEffect1,
+	&kShakerEffect2,
+	&kShakerEffect3,
+	&kShakerEffect4,
+	&kShakerEffect5
+};
+
+void DuckmanSpecialCode::spcStartScreenShaker(OpCall &opCall) {
+	ARG_BYTE(effect);
+	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
+	_vm->startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcSetCursorHandMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	_vm->setCursorHandMode(mode);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcResetChinesePuzzle(OpCall &opCall) {
+	_vm->_scriptResource->_properties.set(0x000E0018, false);
+	_vm->_scriptResource->_properties.set(0x000E0019, false);
+	_chinesePuzzleIndex = 0;
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcAddChinesePuzzleAnswer(OpCall &opCall) {
+	ARG_BYTE(answer);
+	_chinesePuzzleAnswers[_chinesePuzzleIndex++] = answer;
+	if (_chinesePuzzleIndex == 3) {
+		_vm->_scriptResource->_properties.set(0x000E0018, true);
+		if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 5) ||
+			(_chinesePuzzleAnswers[0] == 5 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
+			_vm->_scriptResource->_properties.set(0x000E0019, true);
+		else if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 1) ||
+			(_chinesePuzzleAnswers[0] == 1 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
+			_vm->_scriptResource->_properties.set(0x000E00A0, true);
+	}
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcOpenInventory(OpCall &opCall) {
+	_vm->openInventory();
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcPutBackInventoryItem(OpCall &opCall) {
+	_vm->putBackInventoryItem();
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcClearInventorySlot(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	_vm->clearInventorySlot(objectId);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcAddPropertyTimer(OpCall &opCall) {
+	ARG_UINT32(propertyId);
+	_vm->addPropertyTimer(propertyId);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcSetPropertyTimer(OpCall &opCall) {
+	ARG_INT16(propertyNum);
+	ARG_INT16(duration);
+	_vm->setPropertyTimer(propertyNum | 0xE0000, duration);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcRemovePropertyTimer(OpCall &opCall) {
+	ARG_UINT32(propertyId);
+	_vm->removePropertyTimer(propertyId);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcCenterNewspaper(OpCall &opCall) {
+	Control *control = _vm->getObjectControl(0x40017);
+	control->_flags |= 8;
+	control->_actor->_position.x = 160;
+	control->_actor->_position.y = 100;
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcSetCursorInventoryMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	ARG_BYTE(value);
+	_vm->setCursorInventoryMode(mode, value);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
+	byte flags = 0;
+	uint32 sequenceId;
+	if (_vm->_scriptResource->_properties.get(0x000E0085))
+		flags |= 1;
+	if (_vm->_scriptResource->_properties.get(0x000E0083))
+		flags |= 2;
+	if (_vm->_scriptResource->_properties.get(0x000E0084))
+		flags |= 4;
+	switch (flags) {
+	case 0:
+		sequenceId = 0x603C1;
+		break;
+	case 1:
+		sequenceId = 0x603BF;
+		break;
+	case 2:
+		sequenceId = 0x603C2;
+		break;
+	case 3:
+		sequenceId = 0x603C0;
+		break;
+	case 4:
+		sequenceId = 0x603C3;
+		break;
+	case 5:
+		sequenceId = 0x603C5;
+		break;
+	case 6:
+		sequenceId = 0x603C4;
+		break;
+	case 7:
+		sequenceId = 0x603C6;
+		break;
+	default:
+		sequenceId = 0x603C1;
+		break;
+	}
+	Control *control = _vm->getObjectControl(0x40110);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
new file mode 100644
index 0000000..366a736
--- /dev/null
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_SPECIALCODE_H
+#define ILLUSIONS_DUCKMAN_SPECIALCODE_H
+
+#include "illusions/illusions.h"
+#include "illusions/specialcode.h"
+#include "common/algorithm.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
+
+class DuckmanSpecialCode : public SpecialCode {
+public:
+	DuckmanSpecialCode(IllusionsEngine_Duckman *vm);
+	~DuckmanSpecialCode();
+	virtual void init();
+	virtual void run(uint32 specialCodeId, OpCall &opCall);
+public:	
+	typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
+	typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
+
+	IllusionsEngine_Duckman *_vm;
+	SpecialCodeMap _specialCodeMap;
+
+	uint _chinesePuzzleIndex;
+	byte _chinesePuzzleAnswers[3];
+
+	// Special code interface functions
+	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
+	void spcStartScreenShaker(OpCall &opCall);
+	void spcSetCursorHandMode(OpCall &opCall);
+	void spcResetChinesePuzzle(OpCall &opCall);
+	void spcAddChinesePuzzleAnswer(OpCall &opCall);
+	void spcOpenInventory(OpCall &opCall);
+	void spcPutBackInventoryItem(OpCall &opCall);
+	void spcClearInventorySlot(OpCall &opCall);
+	void spcAddPropertyTimer(OpCall &opCall);
+	void spcSetPropertyTimer(OpCall &opCall);
+	void spcRemovePropertyTimer(OpCall &opCall);
+	void spcCenterNewspaper(OpCall &opCall);
+	void spcSetCursorInventoryMode(OpCall &opCall);
+	void spcUpdateObject272Sequence(OpCall &opCall);
+
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index a180f2d..600a347 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_specialcode.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
@@ -133,7 +134,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_globalSceneId = 0x00010003;
 
 	initInventory();
-	initSpecialCode();
+	loadSpecialCode(0);
 	setDefaultTextCoords();
 	initCursor();
 	initActiveScenes();
@@ -141,7 +142,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_resSys->loadResource(0x120001, 0x00010001, 0);
 	_resSys->loadResource(0x120002, 0x00010001, 0);
 	_resSys->loadResource(0x120003, 0x00010001, 0);
-
+	
 	_resSys->loadResource(0x000D0001, 0x00010001, 0);
 	startScriptThread(0x00020004, 0);
 	_doScriptThreadInit = true;
@@ -163,6 +164,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 		updateEvents();
 	}
 
+	unloadSpecialCode(0);
+
 	delete _stack;
 	delete _scriptOpcodes;
 
@@ -290,7 +293,8 @@ void IllusionsEngine_Duckman::setDefaultTextCoords() {
 }
 
 void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
-	//TODO?
+	_specialCode = new DuckmanSpecialCode(this);
+	_specialCode->init();
 }
 
 void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
@@ -1358,227 +1362,4 @@ int IllusionsEngine_Duckman::updatePropertyTimers(uint flags) {
 	return result;
 }
 
-// Special code
-
-typedef Common::Functor1Mem<OpCall&, void, IllusionsEngine_Duckman> SpecialCodeFunctionDM;
-#define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &IllusionsEngine_Duckman::func);
-
-void IllusionsEngine_Duckman::initSpecialCode() {
-	SPECIAL(0x00160001, spcStartScreenShaker);
-	SPECIAL(0x00160002, spcSetCursorHandMode);
-	SPECIAL(0x00160003, spcResetChinesePuzzle);
-	SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
-	SPECIAL(0x00160005, spcOpenInventory);
-	SPECIAL(0x00160007, spcPutBackInventoryItem);
-	SPECIAL(0x00160008, spcClearInventorySlot);
-	SPECIAL(0x0016000A, spcAddPropertyTimer);
-	SPECIAL(0x0016000B, spcSetPropertyTimer);
-	SPECIAL(0x0016000C, spcRemovePropertyTimer);
-	SPECIAL(0x00160010, spcCenterNewspaper);
-	SPECIAL(0x00160014, spcUpdateObject272Sequence);
-	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
-}
-
-#undef SPECIAL
-
-void IllusionsEngine_Duckman::runSpecialCode(uint32 specialCodeId, OpCall &opCall) {
-	SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
-	if (it != _specialCodeMap.end()) {
-		(*(*it)._value)(opCall);
-	} else {
-		debug("IllusionsEngine_Duckman::runSpecialCode() Unimplemented special code %08X", specialCodeId);
-		notifyThreadId(opCall._threadId);
-	}
-}
-
-// TODO Move to separate file
-
-static const ScreenShakerPoint kShakerPoints0[] = {
-	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
-};
-
-static const ScreenShakeEffect kShakerEffect0 = {
-	6, 5, kShakerPoints0
-};
-
-static const ScreenShakerPoint kShakerPoints1[] = {
-	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
-	{ 1,  2}, {0, -1} 
-};
-
-static const ScreenShakeEffect kShakerEffect1 = {
-	9, 2, kShakerPoints1
-};
-
-static const ScreenShakerPoint kShakerPoints2[] = {
-	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
-	{0,  1}, {0, -1},
-};
-
-static const ScreenShakeEffect kShakerEffect2 = {
-	9, 2, kShakerPoints2
-};
-
-static const ScreenShakerPoint kShakerPoints3[] = {
-	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
-};
-
-static const ScreenShakeEffect kShakerEffect3 = {
-	5, 2, kShakerPoints3
-};
-
-static const ScreenShakerPoint kShakerPoints4[] = {
-	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
-};
-
-static const ScreenShakeEffect kShakerEffect4 = {
-	8, 5, kShakerPoints4
-};
-
-static const ScreenShakerPoint kShakerPoints5[] = {
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
-};
-
-static const ScreenShakeEffect kShakerEffect5 = {
-	31, 2, kShakerPoints5
-};
-
-static const ScreenShakeEffect *kShakerEffects[] = {
-	&kShakerEffect0,
-	&kShakerEffect1,
-	&kShakerEffect2,
-	&kShakerEffect3,
-	&kShakerEffect4,
-	&kShakerEffect5
-};
-
-void IllusionsEngine_Duckman::spcStartScreenShaker(OpCall &opCall) {
-	ARG_BYTE(effect);
-	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
-	startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcSetCursorHandMode(OpCall &opCall) {
-	ARG_BYTE(mode);
-	setCursorHandMode(mode);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcResetChinesePuzzle(OpCall &opCall) {
-	_scriptResource->_properties.set(0x000E0018, false);
-	_scriptResource->_properties.set(0x000E0019, false);
-	_chinesePuzzleIndex = 0;
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcAddChinesePuzzleAnswer(OpCall &opCall) {
-	ARG_BYTE(answer);
-	_chinesePuzzleAnswers[_chinesePuzzleIndex++] = answer;
-	if (_chinesePuzzleIndex == 3) {
-		_scriptResource->_properties.set(0x000E0018, true);
-		if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 5) ||
-			(_chinesePuzzleAnswers[0] == 5 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
-			_scriptResource->_properties.set(0x000E0019, true);
-		else if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 1) ||
-			(_chinesePuzzleAnswers[0] == 1 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
-			_scriptResource->_properties.set(0x000E00A0, true);
-	}
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcOpenInventory(OpCall &opCall) {
-	openInventory();
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcPutBackInventoryItem(OpCall &opCall) {
-	putBackInventoryItem();
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcClearInventorySlot(OpCall &opCall) {
-	ARG_UINT32(objectId);
-	clearInventorySlot(objectId);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcAddPropertyTimer(OpCall &opCall) {
-	ARG_UINT32(propertyId);
-	addPropertyTimer(propertyId);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcSetPropertyTimer(OpCall &opCall) {
-	ARG_INT16(propertyNum);
-	ARG_INT16(duration);
-	setPropertyTimer(propertyNum | 0xE0000, duration);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcRemovePropertyTimer(OpCall &opCall) {
-	ARG_UINT32(propertyId);
-	removePropertyTimer(propertyId);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcCenterNewspaper(OpCall &opCall) {
-	Control *control = getObjectControl(0x40017);
-	control->_flags |= 8;
-	control->_actor->_position.x = 160;
-	control->_actor->_position.y = 100;
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcSetCursorInventoryMode(OpCall &opCall) {
-	ARG_BYTE(mode);
-	ARG_BYTE(value);
-	setCursorInventoryMode(mode, value);
-	notifyThreadId(opCall._threadId);
-}
-
-void IllusionsEngine_Duckman::spcUpdateObject272Sequence(OpCall &opCall) {
-	byte flags = 0;
-	uint32 sequenceId;
-	if (_scriptResource->_properties.get(0x000E0085))
-		flags |= 1;
-	if (_scriptResource->_properties.get(0x000E0083))
-		flags |= 2;
-	if (_scriptResource->_properties.get(0x000E0084))
-		flags |= 4;
-	switch (flags) {
-	case 0:
-		sequenceId = 0x603C1;
-		break;
-	case 1:
-		sequenceId = 0x603BF;
-		break;
-	case 2:
-		sequenceId = 0x603C2;
-		break;
-	case 3:
-		sequenceId = 0x603C0;
-		break;
-	case 4:
-		sequenceId = 0x603C3;
-		break;
-	case 5:
-		sequenceId = 0x603C5;
-		break;
-	case 6:
-		sequenceId = 0x603C4;
-		break;
-	case 7:
-		sequenceId = 0x603C6;
-		break;
-	default:
-		sequenceId = 0x603C1;
-		break;
-	}
-	Control *control = getObjectControl(0x40110);
-	control->startSequenceActor(sequenceId, 2, opCall._threadId);
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 7f6c427..b21a83d 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -103,10 +103,6 @@ const uint kPropertyTimersCount = 6;
 
 struct OpCall;
 
-typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
-typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
-typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
-
 class IllusionsEngine_Duckman : public IllusionsEngine {
 public:
 	IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd);
@@ -139,11 +135,6 @@ public:
 	bool _propertyTimersActive;
 	bool _propertyTimersPaused;
 
-	uint _chinesePuzzleIndex;
-	byte _chinesePuzzleAnswers[3];
-
-	SpecialCodeMap _specialCodeMap;
-
 	void initUpdateFunctions();
 	int updateScript(uint flags);
 
@@ -240,23 +231,6 @@ public:
 	bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
 	int updatePropertyTimers(uint flags);
 
-	// Special code
-	void initSpecialCode();
-	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
-	void spcStartScreenShaker(OpCall &opCall);
-	void spcSetCursorHandMode(OpCall &opCall);
-	void spcResetChinesePuzzle(OpCall &opCall);
-	void spcAddChinesePuzzleAnswer(OpCall &opCall);
-	void spcOpenInventory(OpCall &opCall);
-	void spcPutBackInventoryItem(OpCall &opCall);
-	void spcClearInventorySlot(OpCall &opCall);
-	void spcAddPropertyTimer(OpCall &opCall);
-	void spcSetPropertyTimer(OpCall &opCall);
-	void spcRemovePropertyTimer(OpCall &opCall);
-	void spcCenterNewspaper(OpCall &opCall);
-	void spcSetCursorInventoryMode(OpCall &opCall);
-	void spcUpdateObject272Sequence(OpCall &opCall);
-
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 05876d2..bf16310 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	cursor.o \
 	detection.o \
 	dictionary.o \
+	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
 	fixedpoint.o \
 	graphics.o \
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index 3c7909e..b7ec463 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -553,7 +553,9 @@ void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCa
 void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeId);
-	_vm->runSpecialCode(specialCodeId, opCall);
+debug("run(%08X)", specialCodeId);	
+	_vm->_specialCode->run(specialCodeId, opCall);
+debug("run(%08X) OK", specialCodeId);	
 }
 
 void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {


Commit: 18553cb17aaa3b0c9e167cb62269896bd1e42fd3
    https://github.com/scummvm/scummvm/commit/18553cb17aaa3b0c9e167cb62269896bd1e42fd3
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move Duckman property timer code to own class and file

Changed paths:
  A engines/illusions/duckman/propertytimers.cpp
  A engines/illusions/duckman/propertytimers.h
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/module.mk


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index 8626fd1..b9204ba 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/duckman/propertytimers.h"
 #include "illusions/actor.h"
 #include "illusions/resources/scriptresource.h"
 #include "illusions/scriptopcodes_duckman.h"
@@ -35,9 +36,12 @@ namespace Illusions {
 
 DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 	: _vm(vm) {
+
+	_propertyTimers = new PropertyTimers(_vm);
 }
 
 DuckmanSpecialCode::~DuckmanSpecialCode() {
+	delete _propertyTimers;
 }
 
 typedef Common::Functor1Mem<OpCall&, void, DuckmanSpecialCode> SpecialCodeFunctionDM;
@@ -188,20 +192,20 @@ void DuckmanSpecialCode::spcClearInventorySlot(OpCall &opCall) {
 
 void DuckmanSpecialCode::spcAddPropertyTimer(OpCall &opCall) {
 	ARG_UINT32(propertyId);
-	_vm->addPropertyTimer(propertyId);
+	_propertyTimers->addPropertyTimer(propertyId);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
 void DuckmanSpecialCode::spcSetPropertyTimer(OpCall &opCall) {
 	ARG_INT16(propertyNum);
 	ARG_INT16(duration);
-	_vm->setPropertyTimer(propertyNum | 0xE0000, duration);
+	_propertyTimers->setPropertyTimer(propertyNum | 0xE0000, duration);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
 void DuckmanSpecialCode::spcRemovePropertyTimer(OpCall &opCall) {
 	ARG_UINT32(propertyId);
-	_vm->removePropertyTimer(propertyId);
+	_propertyTimers->removePropertyTimer(propertyId);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index 366a736..4c956f0 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -30,6 +30,7 @@
 namespace Illusions {
 
 class IllusionsEngine_Duckman;
+class PropertyTimers;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
@@ -48,6 +49,8 @@ public:
 
 	uint _chinesePuzzleIndex;
 	byte _chinesePuzzleAnswers[3];
+	
+	PropertyTimers *_propertyTimers;
 
 	// Special code interface functions
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 600a347..345ec63 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -128,9 +128,6 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_fieldA = 0;
 	_fieldE = 240;
 	
-	_propertyTimersActive = false;
-	_propertyTimersPaused = false;
-
 	_globalSceneId = 0x00010003;
 
 	initInventory();
@@ -1275,91 +1272,4 @@ DMInventorySlot *IllusionsEngine_Duckman::findClosestInventorySlot(Common::Point
 	return minInventorySlot;
 }
 
-void IllusionsEngine_Duckman::addPropertyTimer(uint32 propertyId) {
-	PropertyTimer *propertyTimer;
-	if (findPropertyTimer(propertyId, propertyTimer) || findPropertyTimer(0, propertyTimer)) {
-		propertyTimer->_propertyId = propertyId;
-		propertyTimer->_startTime = 0;
-		propertyTimer->_duration = 0;
-		propertyTimer->_endTime = 0;
-	}
-}
-
-void IllusionsEngine_Duckman::setPropertyTimer(uint32 propertyId, uint32 duration) {
-	PropertyTimer *propertyTimer;
-	if (findPropertyTimer(propertyId, propertyTimer)) {
-		propertyTimer->_startTime = getCurrentTime();
-		propertyTimer->_duration = duration;
-		propertyTimer->_endTime = duration + propertyTimer->_startTime;
-	}
-	_scriptResource->_properties.set(propertyId, false);
-	if (!_propertyTimersActive) {
-		_updateFunctions->add(29, getCurrentScene(), new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman>
-			(this, &IllusionsEngine_Duckman::updatePropertyTimers));
-		_propertyTimersActive = true;
-	}
-}
-
-void IllusionsEngine_Duckman::removePropertyTimer(uint32 propertyId) {
-	PropertyTimer *propertyTimer;
-	if (findPropertyTimer(propertyId, propertyTimer))
-		propertyTimer->_propertyId = 0;
-	_scriptResource->_properties.set(propertyId, true);
-}
-
-bool IllusionsEngine_Duckman::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
-	for (uint i = 0; i < kPropertyTimersCount; ++i)
-		if (_propertyTimers[i]._propertyId == propertyId) {
-			propertyTimer = &_propertyTimers[i];
-			return true;
-		}
-	return false;
-}
-
-int IllusionsEngine_Duckman::updatePropertyTimers(uint flags) {
-	int result = 1;
-	uint32 currTime = getCurrentTime();
-	if (_pauseCtr <= 0) {
-		if (_propertyTimersPaused) {
-			for (uint i = 0; i < kPropertyTimersCount; ++i) {
-				PropertyTimer &propertyTimer = _propertyTimers[i];
-				propertyTimer._startTime = currTime;
-				propertyTimer._endTime = currTime + propertyTimer._duration;
-			}
-			_propertyTimersPaused = false;
-		}
-		if (flags & 1) {
-			_propertyTimersActive = false;
-			_propertyTimersPaused = false;
-			result = 2;
-		} else {
-			bool timersActive = false;
-			for (uint i = 0; i < kPropertyTimersCount; ++i) {
-				PropertyTimer &propertyTimer = _propertyTimers[i];
-				if (propertyTimer._propertyId) {
-					timersActive = true;
-					if (!_scriptResource->_properties.get(propertyTimer._propertyId) &&
-						isTimerExpired(propertyTimer._startTime, propertyTimer._endTime))
-						_scriptResource->_properties.set(propertyTimer._propertyId, true);
-				}
-			}
-			if (!timersActive) {
-				_propertyTimersActive = false;
-				_propertyTimersPaused = false;
-				result = 2;
-			}
-		}
-	} else {
-		if (!_propertyTimersPaused) {
-			for (uint i = 0; i < kPropertyTimersCount; ++i) {
-				PropertyTimer &propertyTimer = _propertyTimers[i];
-				propertyTimer._duration -= getDurationElapsed(propertyTimer._startTime, propertyTimer._endTime);
-			}
-			_propertyTimersPaused = true;
-		}
-		result = 1;
-	}
-	return result;
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index b21a83d..d2d526f 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -91,16 +91,6 @@ struct ScreenShaker {
 	const ScreenShakerPoint *_points;
 };
 
-struct PropertyTimer {
-	uint32 _propertyId;
-	uint32 _startTime;
-	uint32 _duration;
-	uint32 _endTime;
-	PropertyTimer() : _propertyId(0) {}
-};
-
-const uint kPropertyTimersCount = 6;
-
 struct OpCall;
 
 class IllusionsEngine_Duckman : public IllusionsEngine {
@@ -130,11 +120,7 @@ public:
 	Common::Array<DMInventoryItem> _inventoyItems;
 
 	ScreenShaker *_screenShaker;
-
-	PropertyTimer _propertyTimers[kPropertyTimersCount];
-	bool _propertyTimersActive;
-	bool _propertyTimersPaused;
-
+	
 	void initUpdateFunctions();
 	int updateScript(uint flags);
 
@@ -225,12 +211,6 @@ public:
 	DMInventoryItem *findInventoryItem(uint32 objectId);
 	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
 
-	void addPropertyTimer(uint32 propertyId);
-	void setPropertyTimer(uint32 propertyId, uint32 duration);
-	void removePropertyTimer(uint32 propertyId);
-	bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
-	int updatePropertyTimers(uint flags);
-
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/propertytimers.cpp b/engines/illusions/duckman/propertytimers.cpp
new file mode 100644
index 0000000..60e5886
--- /dev/null
+++ b/engines/illusions/duckman/propertytimers.cpp
@@ -0,0 +1,129 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/propertytimers.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+#include "engines/util.h"
+
+namespace Illusions {
+
+// PropertyTimers
+
+PropertyTimers::PropertyTimers(IllusionsEngine_Duckman *vm) {
+	_propertyTimersActive = false;
+	_propertyTimersPaused = false;
+}
+
+PropertyTimers::~PropertyTimers() {
+}
+
+void PropertyTimers::addPropertyTimer(uint32 propertyId) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer) || findPropertyTimer(0, propertyTimer)) {
+		propertyTimer->_propertyId = propertyId;
+		propertyTimer->_startTime = 0;
+		propertyTimer->_duration = 0;
+		propertyTimer->_endTime = 0;
+	}
+}
+
+void PropertyTimers::setPropertyTimer(uint32 propertyId, uint32 duration) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer)) {
+		propertyTimer->_startTime = getCurrentTime();
+		propertyTimer->_duration = duration;
+		propertyTimer->_endTime = duration + propertyTimer->_startTime;
+	}
+	_vm->_scriptResource->_properties.set(propertyId, false);
+	if (!_propertyTimersActive) {
+		_vm->_updateFunctions->add(29, _vm->getCurrentScene(), new Common::Functor1Mem<uint, int, PropertyTimers>
+			(this, &PropertyTimers::updatePropertyTimers));
+		_propertyTimersActive = true;
+	}
+}
+
+void PropertyTimers::removePropertyTimer(uint32 propertyId) {
+	PropertyTimer *propertyTimer;
+	if (findPropertyTimer(propertyId, propertyTimer))
+		propertyTimer->_propertyId = 0;
+	_vm->_scriptResource->_properties.set(propertyId, true);
+}
+
+bool PropertyTimers::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
+	for (uint i = 0; i < kPropertyTimersCount; ++i)
+		if (_propertyTimers[i]._propertyId == propertyId) {
+			propertyTimer = &_propertyTimers[i];
+			return true;
+		}
+	return false;
+}
+
+int PropertyTimers::updatePropertyTimers(uint flags) {
+	int result = 1;
+	uint32 currTime = getCurrentTime();
+	if (_vm->_pauseCtr <= 0) {
+		if (_propertyTimersPaused) {
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				propertyTimer._startTime = currTime;
+				propertyTimer._endTime = currTime + propertyTimer._duration;
+			}
+			_propertyTimersPaused = false;
+		}
+		if (flags & 1) {
+			_propertyTimersActive = false;
+			_propertyTimersPaused = false;
+			result = 2;
+		} else {
+			bool timersActive = false;
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				if (propertyTimer._propertyId) {
+					timersActive = true;
+					if (!_vm->_scriptResource->_properties.get(propertyTimer._propertyId) &&
+						isTimerExpired(propertyTimer._startTime, propertyTimer._endTime))
+						_vm->_scriptResource->_properties.set(propertyTimer._propertyId, true);
+				}
+			}
+			if (!timersActive) {
+				_propertyTimersActive = false;
+				_propertyTimersPaused = false;
+				result = 2;
+			}
+		}
+	} else {
+		if (!_propertyTimersPaused) {
+			for (uint i = 0; i < kPropertyTimersCount; ++i) {
+				PropertyTimer &propertyTimer = _propertyTimers[i];
+				propertyTimer._duration -= getDurationElapsed(propertyTimer._startTime, propertyTimer._endTime);
+			}
+			_propertyTimersPaused = true;
+		}
+		result = 1;
+	}
+	return result;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/propertytimers.h b/engines/illusions/duckman/propertytimers.h
new file mode 100644
index 0000000..d4b7306
--- /dev/null
+++ b/engines/illusions/duckman/propertytimers.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_PROPERTYTIMERS_H
+#define ILLUSIONS_DUCKMAN_PROPERTYTIMERS_H
+
+#include "illusions/illusions.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+struct PropertyTimer {
+	uint32 _propertyId;
+	uint32 _startTime;
+	uint32 _duration;
+	uint32 _endTime;
+	PropertyTimer() : _propertyId(0) {}
+};
+
+const uint kPropertyTimersCount = 6;
+
+class PropertyTimers {
+public:
+	PropertyTimers(IllusionsEngine_Duckman *vm);
+	~PropertyTimers();
+public:
+	IllusionsEngine_Duckman *_vm;	
+	PropertyTimer _propertyTimers[kPropertyTimersCount];
+	bool _propertyTimersActive;
+	bool _propertyTimersPaused;
+	void addPropertyTimer(uint32 propertyId);
+	void setPropertyTimer(uint32 propertyId, uint32 duration);
+	void removePropertyTimer(uint32 propertyId);
+	bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
+	int updatePropertyTimers(uint flags);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index bf16310..f915e9f 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS := \
 	dictionary.o \
 	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
+	duckman/propertytimers.o \
 	fixedpoint.o \
 	graphics.o \
 	illusions.o \


Commit: dec9ef3123dc2cf66983813fd58f4acff6806ce3
    https://github.com/scummvm/scummvm/commit/dec9ef3123dc2cf66983813fd58f4acff6806ce3
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move Duckman inventory code to own class and file

Changed paths:
  A engines/illusions/duckman/duckman_inventory.cpp
  A engines/illusions/duckman/duckman_inventory.h
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/duckman/duckman_inventory.cpp b/engines/illusions/duckman/duckman_inventory.cpp
new file mode 100644
index 0000000..2196826
--- /dev/null
+++ b/engines/illusions/duckman/duckman_inventory.cpp
@@ -0,0 +1,182 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_inventory.h"
+#include "illusions/actor.h"
+#include "illusions/cursor.h"
+#include "illusions/input.h"
+#include "illusions/resources/scriptresource.h"
+#include "engines/util.h"
+
+namespace Illusions {
+
+// DuckmanInventory
+
+DuckmanInventory::DuckmanInventory(IllusionsEngine_Duckman *vm)
+	: _vm(vm) {
+	initInventory();
+}
+
+DuckmanInventory::~DuckmanInventory() {
+}
+
+void DuckmanInventory::initInventory() {
+	_inventorySlots.push_back(DMInventorySlot( 64,  52));
+	_inventorySlots.push_back(DMInventorySlot(112,  52));
+	_inventorySlots.push_back(DMInventorySlot(160,  52));
+	_inventorySlots.push_back(DMInventorySlot(208,  52));
+	_inventorySlots.push_back(DMInventorySlot(255,  52));
+	_inventorySlots.push_back(DMInventorySlot( 64,  84));
+	_inventorySlots.push_back(DMInventorySlot(112,  84));
+	_inventorySlots.push_back(DMInventorySlot(160,  84));
+	_inventorySlots.push_back(DMInventorySlot(208,  84));
+	_inventorySlots.push_back(DMInventorySlot(255,  84));
+	_inventorySlots.push_back(DMInventorySlot( 64, 116));
+	_inventorySlots.push_back(DMInventorySlot(112, 116));
+	_inventorySlots.push_back(DMInventorySlot(160, 116));
+	_inventorySlots.push_back(DMInventorySlot(208, 116));
+	_inventorySlots.push_back(DMInventorySlot(255, 116));
+	_inventorySlots.push_back(DMInventorySlot( 64, 148));
+	_inventorySlots.push_back(DMInventorySlot(112, 148));
+	_inventorySlots.push_back(DMInventorySlot(160, 148));
+	_inventorySlots.push_back(DMInventorySlot(208, 148));
+	_inventorySlots.push_back(DMInventorySlot(255, 148));
+	_inventoyItems.push_back(DMInventoryItem(0x40011, 0xE005B));
+	_inventoyItems.push_back(DMInventoryItem(0x40099, 0xE001B));
+	_inventoyItems.push_back(DMInventoryItem(0x4000F, 0xE000C));
+	_inventoyItems.push_back(DMInventoryItem(0x40042, 0xE0012));
+	_inventoyItems.push_back(DMInventoryItem(0x40044, 0xE000F));
+	_inventoyItems.push_back(DMInventoryItem(0x40029, 0xE000D));
+	_inventoyItems.push_back(DMInventoryItem(0x400A7, 0xE005D));
+	_inventoyItems.push_back(DMInventoryItem(0x40096, 0xE001C));
+	_inventoyItems.push_back(DMInventoryItem(0x40077, 0xE0010));
+	_inventoyItems.push_back(DMInventoryItem(0x4008A, 0xE0033));
+	_inventoyItems.push_back(DMInventoryItem(0x4004B, 0xE0045));
+	_inventoyItems.push_back(DMInventoryItem(0x40054, 0xE0021));
+	_inventoyItems.push_back(DMInventoryItem(0x400C6, 0xE005A));
+	_inventoyItems.push_back(DMInventoryItem(0x4000B, 0xE005E));
+	_inventoyItems.push_back(DMInventoryItem(0x4005F, 0xE0016));
+	_inventoyItems.push_back(DMInventoryItem(0x40072, 0xE0017));
+	_inventoyItems.push_back(DMInventoryItem(0x400AA, 0xE005F));
+	_inventoyItems.push_back(DMInventoryItem(0x400B8, 0xE0050));
+	_inventoyItems.push_back(DMInventoryItem(0x4001F, 0xE001A));
+	_inventoyItems.push_back(DMInventoryItem(0x40095, 0xE0060));
+	_inventoyItems.push_back(DMInventoryItem(0x40041, 0xE0053));
+}
+
+void DuckmanInventory::openInventory() {
+
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
+		DMInventorySlot *inventorySlot = &_inventorySlots[i];
+		if (inventorySlot->_objectId) {
+			DMInventoryItem *inventoryItem = findInventoryItem(inventorySlot->_objectId);
+			if (!_vm->_scriptResource->_properties.get(inventoryItem->_propertyId))
+				inventorySlot->_objectId = 0;
+		}
+	}
+
+	for (uint i = 0; i < _inventoyItems.size(); ++i) {
+		DMInventoryItem *inventoryItem = &_inventoyItems[i];
+		if (_vm->_scriptResource->_properties.get(inventoryItem->_propertyId)) {
+			DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
+			if (inventorySlot) {
+				Control *control = _vm->getObjectControl(inventoryItem->_objectId);
+				control->setActorPosition(inventorySlot->_position);
+				control->appearActor();
+			} else {
+				addInventoryItem(inventoryItem->_objectId);
+			}
+		}
+	}
+
+}
+
+void DuckmanInventory::addInventoryItem(uint32 objectId) {
+	DMInventorySlot *DMInventorySlot = findInventorySlot(0);
+	DMInventorySlot->_objectId = objectId;
+	Control *control = _vm->getObjectControl(objectId);
+	control->setActorPosition(DMInventorySlot->_position);
+	control->appearActor();
+}
+
+void DuckmanInventory::clearInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]._objectId == objectId)
+			_inventorySlots[i]._objectId = 0;
+}
+
+void DuckmanInventory::putBackInventoryItem() {
+	Common::Point mousePos = _vm->_input->getCursorPosition();
+	if (_vm->_cursor._objectId) {
+		DMInventorySlot *inventorySlot = findInventorySlot(_vm->_cursor._objectId);
+		if (inventorySlot)
+			inventorySlot->_objectId = 0;
+		inventorySlot = findClosestInventorySlot(mousePos);
+		inventorySlot->_objectId = _vm->_cursor._objectId;
+		Control *control = _vm->getObjectControl(_vm->_cursor._objectId);
+		control->setActorPosition(inventorySlot->_position);
+		control->appearActor();
+		_vm->_cursor._actorIndex = 7;
+		_vm->stopCursorHoldingObject();
+		_vm->_cursor._actorIndex = 2;
+		_vm->_cursor._control->startSequenceActor(_vm->_cursor._sequenceId1, 2, 0);
+		if (_vm->_cursor._currOverlappedControl)
+			_vm->setCursorActorIndex(_vm->_cursor._actorIndex, 2, 0);
+		else
+			_vm->setCursorActorIndex(_vm->_cursor._actorIndex, 1, 0);
+	}
+}
+
+DMInventorySlot *DuckmanInventory::findInventorySlot(uint32 objectId) {
+	for (uint i = 0; i < _inventorySlots.size(); ++i)
+		if (_inventorySlots[i]._objectId == objectId)
+			return &_inventorySlots[i];
+	return 0;
+}
+
+DMInventoryItem *DuckmanInventory::findInventoryItem(uint32 objectId) {
+	for (uint i = 0; i < _inventoyItems.size(); ++i)
+		if (_inventoyItems[i]._objectId == objectId)
+			return &_inventoyItems[i];
+	return 0;
+}
+
+DMInventorySlot *DuckmanInventory::findClosestInventorySlot(Common::Point pos) {
+	int minDistance = 0xFFFFFF;
+	DMInventorySlot *minInventorySlot = 0;
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
+		DMInventorySlot *inventorySlot = &_inventorySlots[i];
+		if (inventorySlot->_objectId == 0) {
+			int16 deltaX = ABS(inventorySlot->_position.x - pos.x);
+			int16 deltaY = ABS(inventorySlot->_position.y - pos.y);
+			int distance = deltaX * deltaX + deltaY * deltaY;
+			if (inventorySlot->_objectId == 0 && distance < minDistance) {
+				minDistance = distance;
+				minInventorySlot = inventorySlot;
+			}
+		}
+	}
+	return minInventorySlot;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_inventory.h b/engines/illusions/duckman/duckman_inventory.h
new file mode 100644
index 0000000..e1abbeb
--- /dev/null
+++ b/engines/illusions/duckman/duckman_inventory.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_DUCKMAN_INVENTORY_H
+#define ILLUSIONS_DUCKMAN_DUCKMAN_INVENTORY_H
+
+#include "illusions/illusions.h"
+
+namespace Illusions {
+
+struct DMInventorySlot {
+	Common::Point _position;
+	uint32 _objectId;
+	DMInventorySlot() : _objectId(0) {}
+	DMInventorySlot(int16 x, int16 y) : _objectId(0), _position(x, y) {}
+};
+
+struct DMInventoryItem {
+	uint32 _objectId;
+	uint32 _propertyId;
+	DMInventoryItem() : _objectId(0) {}
+	DMInventoryItem(uint32 objectId, uint32 propertyId)
+		: _objectId(objectId), _propertyId(propertyId) {}
+};
+
+class DuckmanInventory {
+public:
+	DuckmanInventory(IllusionsEngine_Duckman *vm);
+	~DuckmanInventory();
+public:
+	IllusionsEngine_Duckman *_vm;	
+	Common::Array<DMInventorySlot> _inventorySlots;
+	Common::Array<DMInventoryItem> _inventoyItems;
+	void initInventory();
+	void openInventory();
+	void addInventoryItem(uint32 objectId);
+	void clearInventorySlot(uint32 objectId);
+	void putBackInventoryItem();
+	DMInventorySlot *findInventorySlot(uint32 objectId);
+	DMInventoryItem *findInventoryItem(uint32 objectId);
+	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_DUCKMAN_INVENTORY_H
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index b9204ba..6553631 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/duckman/duckman_inventory.h"
 #include "illusions/duckman/propertytimers.h"
 #include "illusions/actor.h"
 #include "illusions/resources/scriptresource.h"
@@ -38,10 +39,12 @@ DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 	: _vm(vm) {
 
 	_propertyTimers = new PropertyTimers(_vm);
+	_inventory = new DuckmanInventory(_vm);
 }
 
 DuckmanSpecialCode::~DuckmanSpecialCode() {
 	delete _propertyTimers;
+	delete _inventory;
 }
 
 typedef Common::Functor1Mem<OpCall&, void, DuckmanSpecialCode> SpecialCodeFunctionDM;
@@ -175,18 +178,18 @@ void DuckmanSpecialCode::spcAddChinesePuzzleAnswer(OpCall &opCall) {
 }
 
 void DuckmanSpecialCode::spcOpenInventory(OpCall &opCall) {
-	_vm->openInventory();
+	_inventory->openInventory();
 	_vm->notifyThreadId(opCall._threadId);
 }
 
 void DuckmanSpecialCode::spcPutBackInventoryItem(OpCall &opCall) {
-	_vm->putBackInventoryItem();
+	_inventory->putBackInventoryItem();
 	_vm->notifyThreadId(opCall._threadId);
 }
 
 void DuckmanSpecialCode::spcClearInventorySlot(OpCall &opCall) {
 	ARG_UINT32(objectId);
-	_vm->clearInventorySlot(objectId);
+	_inventory->clearInventorySlot(objectId);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index 4c956f0..53012d6 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -30,6 +30,7 @@
 namespace Illusions {
 
 class IllusionsEngine_Duckman;
+class DuckmanInventory;
 class PropertyTimers;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
@@ -51,6 +52,7 @@ public:
 	byte _chinesePuzzleAnswers[3];
 	
 	PropertyTimers *_propertyTimers;
+	DuckmanInventory *_inventory;
 
 	// Special code interface functions
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 345ec63..2aa6d00 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -130,7 +130,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	
 	_globalSceneId = 0x00010003;
 
-	initInventory();
+	_savedInventoryActorIndex = 0;
+
 	loadSpecialCode(0);
 	setDefaultTextCoords();
 	initCursor();
@@ -1132,144 +1133,4 @@ void IllusionsEngine_Duckman::updateDialogState() {
 
 }
 
-void IllusionsEngine_Duckman::initInventory() {
-	_inventorySlots.push_back(DMInventorySlot( 64,  52));
-	_inventorySlots.push_back(DMInventorySlot(112,  52));
-	_inventorySlots.push_back(DMInventorySlot(160,  52));
-	_inventorySlots.push_back(DMInventorySlot(208,  52));
-	_inventorySlots.push_back(DMInventorySlot(255,  52));
-	_inventorySlots.push_back(DMInventorySlot( 64,  84));
-	_inventorySlots.push_back(DMInventorySlot(112,  84));
-	_inventorySlots.push_back(DMInventorySlot(160,  84));
-	_inventorySlots.push_back(DMInventorySlot(208,  84));
-	_inventorySlots.push_back(DMInventorySlot(255,  84));
-	_inventorySlots.push_back(DMInventorySlot( 64, 116));
-	_inventorySlots.push_back(DMInventorySlot(112, 116));
-	_inventorySlots.push_back(DMInventorySlot(160, 116));
-	_inventorySlots.push_back(DMInventorySlot(208, 116));
-	_inventorySlots.push_back(DMInventorySlot(255, 116));
-	_inventorySlots.push_back(DMInventorySlot( 64, 148));
-	_inventorySlots.push_back(DMInventorySlot(112, 148));
-	_inventorySlots.push_back(DMInventorySlot(160, 148));
-	_inventorySlots.push_back(DMInventorySlot(208, 148));
-	_inventorySlots.push_back(DMInventorySlot(255, 148));
-	_inventoyItems.push_back(DMInventoryItem(0x40011, 0xE005B));
-	_inventoyItems.push_back(DMInventoryItem(0x40099, 0xE001B));
-	_inventoyItems.push_back(DMInventoryItem(0x4000F, 0xE000C));
-	_inventoyItems.push_back(DMInventoryItem(0x40042, 0xE0012));
-	_inventoyItems.push_back(DMInventoryItem(0x40044, 0xE000F));
-	_inventoyItems.push_back(DMInventoryItem(0x40029, 0xE000D));
-	_inventoyItems.push_back(DMInventoryItem(0x400A7, 0xE005D));
-	_inventoyItems.push_back(DMInventoryItem(0x40096, 0xE001C));
-	_inventoyItems.push_back(DMInventoryItem(0x40077, 0xE0010));
-	_inventoyItems.push_back(DMInventoryItem(0x4008A, 0xE0033));
-	_inventoyItems.push_back(DMInventoryItem(0x4004B, 0xE0045));
-	_inventoyItems.push_back(DMInventoryItem(0x40054, 0xE0021));
-	_inventoyItems.push_back(DMInventoryItem(0x400C6, 0xE005A));
-	_inventoyItems.push_back(DMInventoryItem(0x4000B, 0xE005E));
-	_inventoyItems.push_back(DMInventoryItem(0x4005F, 0xE0016));
-	_inventoyItems.push_back(DMInventoryItem(0x40072, 0xE0017));
-	_inventoyItems.push_back(DMInventoryItem(0x400AA, 0xE005F));
-	_inventoyItems.push_back(DMInventoryItem(0x400B8, 0xE0050));
-	_inventoyItems.push_back(DMInventoryItem(0x4001F, 0xE001A));
-	_inventoyItems.push_back(DMInventoryItem(0x40095, 0xE0060));
-	_inventoyItems.push_back(DMInventoryItem(0x40041, 0xE0053));
-	_savedInventoryActorIndex = 0;
-}
-
-void IllusionsEngine_Duckman::openInventory() {
-
-	for (uint i = 0; i < _inventorySlots.size(); ++i) {
-		DMInventorySlot *inventorySlot = &_inventorySlots[i];
-		if (inventorySlot->_objectId) {
-			DMInventoryItem *inventoryItem = findInventoryItem(inventorySlot->_objectId);
-			if (!_scriptResource->_properties.get(inventoryItem->_propertyId))
-				inventorySlot->_objectId = 0;
-		}
-	}
-
-	for (uint i = 0; i < _inventoyItems.size(); ++i) {
-		DMInventoryItem *inventoryItem = &_inventoyItems[i];
-		if (_scriptResource->_properties.get(inventoryItem->_propertyId)) {
-			DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
-			if (inventorySlot) {
-				Control *control = getObjectControl(inventoryItem->_objectId);
-				control->setActorPosition(inventorySlot->_position);
-				control->appearActor();
-			} else {
-				addInventoryItem(inventoryItem->_objectId);
-			}
-		}
-	}
-
-}
-
-void IllusionsEngine_Duckman::addInventoryItem(uint32 objectId) {
-	DMInventorySlot *DMInventorySlot = findInventorySlot(0);
-	DMInventorySlot->_objectId = objectId;
-	Control *control = getObjectControl(objectId);
-	control->setActorPosition(DMInventorySlot->_position);
-	control->appearActor();
-}
-
-void IllusionsEngine_Duckman::clearInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
-		if (_inventorySlots[i]._objectId == objectId)
-			_inventorySlots[i]._objectId = 0;
-}
-
-void IllusionsEngine_Duckman::putBackInventoryItem() {
-	Common::Point mousePos = _input->getCursorPosition();
-	if (_cursor._objectId) {
-		DMInventorySlot *inventorySlot = findInventorySlot(_cursor._objectId);
-		if (inventorySlot)
-			inventorySlot->_objectId = 0;
-		inventorySlot = findClosestInventorySlot(mousePos);
-		inventorySlot->_objectId = _cursor._objectId;
-		Control *control = getObjectControl(_cursor._objectId);
-		control->setActorPosition(inventorySlot->_position);
-		control->appearActor();
-		_cursor._actorIndex = 7;
-		stopCursorHoldingObject();
-		_cursor._actorIndex = 2;
-		_cursor._control->startSequenceActor(_cursor._sequenceId1, 2, 0);
-		if (_cursor._currOverlappedControl)
-			setCursorActorIndex(_cursor._actorIndex, 2, 0);
-		else
-			setCursorActorIndex(_cursor._actorIndex, 1, 0);
-	}
-}
-
-DMInventorySlot *IllusionsEngine_Duckman::findInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
-		if (_inventorySlots[i]._objectId == objectId)
-			return &_inventorySlots[i];
-	return 0;
-}
-
-DMInventoryItem *IllusionsEngine_Duckman::findInventoryItem(uint32 objectId) {
-	for (uint i = 0; i < _inventoyItems.size(); ++i)
-		if (_inventoyItems[i]._objectId == objectId)
-			return &_inventoyItems[i];
-	return 0;
-}
-
-DMInventorySlot *IllusionsEngine_Duckman::findClosestInventorySlot(Common::Point pos) {
-	int minDistance = 0xFFFFFF;
-	DMInventorySlot *minInventorySlot = 0;
-	for (uint i = 0; i < _inventorySlots.size(); ++i) {
-		DMInventorySlot *inventorySlot = &_inventorySlots[i];
-		if (inventorySlot->_objectId == 0) {
-			int16 deltaX = ABS(inventorySlot->_position.x - pos.x);
-			int16 deltaY = ABS(inventorySlot->_position.y - pos.y);
-			int distance = deltaX * deltaX + deltaY * deltaY;
-			if (inventorySlot->_objectId == 0 && distance < minDistance) {
-				minDistance = distance;
-				minInventorySlot = inventorySlot;
-			}
-		}
-	}
-	return minInventorySlot;
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index d2d526f..ce76d36 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -56,21 +56,6 @@ struct DialogItem {
 	uint32 _sequenceId;
 };
 
-struct DMInventorySlot {
-	Common::Point _position;
-	uint32 _objectId;
-	DMInventorySlot() : _objectId(0) {}
-	DMInventorySlot(int16 x, int16 y) : _objectId(0), _position(x, y) {}
-};
-
-struct DMInventoryItem {
-	uint32 _objectId;
-	uint32 _propertyId;
-	DMInventoryItem() : _objectId(0) {}
-	DMInventoryItem(uint32 objectId, uint32 propertyId)
-		: _objectId(objectId), _propertyId(propertyId) {}
-};
-
 struct ScreenShakerPoint {
 	int16 x, y;
 };
@@ -116,8 +101,6 @@ public:
 	Common::Array<DialogItem> _dialogItems;
 
 	int _savedInventoryActorIndex;
-	Common::Array<DMInventorySlot> _inventorySlots;
-	Common::Array<DMInventoryItem> _inventoyItems;
 
 	ScreenShaker *_screenShaker;
 	
@@ -202,15 +185,6 @@ public:
 	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
 	void updateDialogState();
 
-	void initInventory();
-	void openInventory();
-	void addInventoryItem(uint32 objectId);
-	void clearInventorySlot(uint32 objectId);
-	void putBackInventoryItem();
-	DMInventorySlot *findInventorySlot(uint32 objectId);
-	DMInventoryItem *findInventoryItem(uint32 objectId);
-	DMInventorySlot *findClosestInventorySlot(Common::Point pos);
-
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index f915e9f..99580e4 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	cursor.o \
 	detection.o \
 	dictionary.o \
+	duckman/duckman_inventory.o \
 	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
 	duckman/propertytimers.o \
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index b7ec463..e8aa7f7 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -247,7 +247,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 	_vm->enterScene(sceneId, 0);
 }
 
-static uint dsceneId = 0, dthreadId = 0;
+//static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
 //static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
@@ -260,7 +260,7 @@ static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 //static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
-//static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
+static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
 //static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
 


Commit: d6e2f61155f1e0e312391b76eded58c169046282
    https://github.com/scummvm/scummvm/commit/d6e2f61155f1e0e312391b76eded58c169046282
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move Duckman dialog code to own file

Changed paths:
  A engines/illusions/duckman/duckman_dialog.cpp
  A engines/illusions/duckman/duckman_dialog.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/module.mk
    engines/illusions/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/duckman/duckman_dialog.cpp b/engines/illusions/duckman/duckman_dialog.cpp
new file mode 100644
index 0000000..b0b74df
--- /dev/null
+++ b/engines/illusions/duckman/duckman_dialog.cpp
@@ -0,0 +1,178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_dialog.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/cursor.h"
+#include "illusions/dictionary.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/graphics.h"
+#include "illusions/input.h"
+#include "illusions/resources/actorresource.h"
+#include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/midiresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/soundresource.h"
+#include "illusions/resources/talkresource.h"
+#include "illusions/resourcesystem.h"
+#include "illusions/screen.h"
+#include "illusions/screentext.h"
+#include "illusions/scriptopcodes_duckman.h"
+#include "illusions/scriptstack.h"
+#include "illusions/sound.h"
+#include "illusions/specialcode.h"
+#include "illusions/textdrawer.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+
+#include "illusions/threads/abortablethread.h"
+#include "illusions/threads/causethread_duckman.h"
+#include "illusions/threads/scriptthread.h"
+#include "illusions/threads/talkthread_duckman.h"
+#include "illusions/threads/timerthread.h"
+
+#include "engines/util.h"
+
+namespace Illusions {
+
+// DuckmanDialogSystem
+
+DuckmanDialogSystem::DuckmanDialogSystem(IllusionsEngine_Duckman *vm)
+	: _vm(vm) {
+}
+
+DuckmanDialogSystem::~DuckmanDialogSystem() {
+}
+
+void DuckmanDialogSystem::addDialogItem(int16 choiceJumpOffs, uint32 sequenceId) {
+	DialogItem dialogItem;
+	dialogItem._choiceJumpOffs = choiceJumpOffs;
+	dialogItem._sequenceId = sequenceId;
+	_dialogItems.push_back(dialogItem);
+}
+
+void DuckmanDialogSystem::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId) {
+	static const uint32 kDialogSequenceIds[] = {
+		0,
+		0x6049C, 0x6049C, 0x6047A, 0x6049D,
+		0x60479, 0x6049E, 0x6049F, 0x60468
+	};
+	if (_dialogItems.size() == 1) {
+		*choiceOfsPtr = _dialogItems[0]._choiceJumpOffs;
+		_vm->notifyThreadId(callerThreadId);
+	} else {
+		if (!_vm->_cursor._control) {
+			Common::Point pos = _vm->getNamedPointPosition(0x70001);
+			_vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
+			_vm->_cursor._control = _vm->_dict->getObjectControl(0x40004);
+		}
+		_vm->_cursor._control->appearActor();
+		_vm->setCursorActorIndex(6, 1, 0);
+
+		_vm->_cursor._gameState = 3;
+		_vm->_cursor._notifyThreadId30 = callerThreadId;
+		_vm->_cursor._dialogItemsCount = 0;
+		_vm->_cursor._overlappedObjectId = 0;
+		_vm->_cursor._op113_choiceOfsPtr = choiceOfsPtr;
+		_vm->_cursor._currOverlappedControl = 0;
+
+		/* TODO?
+		if (!_vm->_input->getCursorMouseMode())
+			_vm->_input->setMousePos((Point)0xBC0014);
+		*/
+
+		_vm->_cursor._dialogItemsCount = _dialogItems.size();
+		Common::Point placePt(20, 188);
+
+		for (uint i = 1; i <= _dialogItems.size(); ++i) {
+			DialogItem &dialogItem = _dialogItems[_dialogItems.size() - i];
+			_vm->_controls->placeDialogItem(i + 1, actorTypeId, dialogItem._sequenceId, placePt, dialogItem._choiceJumpOffs);
+			placePt.x += 40;
+		}
+
+		Common::Point placePt2 = _vm->getNamedPointPosition(0x700C3);
+		_vm->_controls->placeActor(0x5006E, placePt2, kDialogSequenceIds[_dialogItems.size()], 0x40148, 0);
+		Control *control = _vm->_dict->getObjectControl(0x40148);
+		control->_flags |= 8;
+		_vm->playSoundEffect(8);
+	}
+
+	_dialogItems.clear();
+
+}
+
+void DuckmanDialogSystem::updateDialogState() {
+	Common::Point mousePos = _vm->_input->getCursorPosition();
+	// TODO Handle keyboard input
+	_vm->_cursor._control->_actor->_position = mousePos;
+	mousePos = _vm->convertMousePos(mousePos);
+
+	Control *currOverlappedControl = _vm->_cursor._currOverlappedControl;
+	Control *newOverlappedControl;
+	
+	if (_vm->_controls->getDialogItemAtPos(_vm->_cursor._control, mousePos, &newOverlappedControl)) {
+		if (currOverlappedControl != newOverlappedControl) {
+			newOverlappedControl->setActorIndex(2);
+			newOverlappedControl->startSequenceActor(newOverlappedControl->_actor->_sequenceId, 2, 0);
+			if (currOverlappedControl) {
+				currOverlappedControl->setActorIndex(1);
+				currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
+			}
+			_vm->playSoundEffect(10);
+			_vm->startCursorSequence();
+			_vm->setCursorActorIndex(6, 2, 0);
+			_vm->_cursor._currOverlappedControl = newOverlappedControl;
+			_vm->_cursor._overlappedObjectId = newOverlappedControl->_objectId;
+		}
+	} else if (currOverlappedControl) {
+		currOverlappedControl->setActorIndex(1);
+		currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
+		_vm->playSoundEffect(10);
+		_vm->_cursor._currOverlappedControl = 0;
+		_vm->_cursor._overlappedObjectId = 0;
+		_vm->startCursorSequence();
+		_vm->setCursorActorIndex(6, 1, 0);
+	}
+
+	if (_vm->_input->pollButton(1)) {
+		if (_vm->_cursor._currOverlappedControl) {
+			_vm->playSoundEffect(9);
+			*_vm->_cursor._op113_choiceOfsPtr = _vm->_cursor._currOverlappedControl->_actor->_choiceJumpOffs;
+			_vm->_controls->destroyDialogItems();
+			Control *control = _vm->_dict->getObjectControl(0x40148);
+			_vm->_controls->destroyControl(control);
+			_vm->notifyThreadId(_vm->_cursor._notifyThreadId30);
+			_vm->_cursor._notifyThreadId30 = 0;
+			_vm->_cursor._gameState = 2;
+			_vm->_cursor._dialogItemsCount = 0;
+			_vm->_cursor._overlappedObjectId = 0;
+			_vm->_cursor._op113_choiceOfsPtr = 0;
+			_vm->_cursor._control->disappearActor();
+		}
+	}
+
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_dialog.h b/engines/illusions/duckman/duckman_dialog.h
new file mode 100644
index 0000000..9c7cfad
--- /dev/null
+++ b/engines/illusions/duckman/duckman_dialog.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_DUCKMAN_DIALOG_H
+#define ILLUSIONS_DUCKMAN_DUCKMAN_DIALOG_H
+
+#include "illusions/illusions.h"
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+struct DialogItem {
+	int16 _choiceJumpOffs;
+	uint32 _sequenceId;
+};
+
+class DuckmanDialogSystem {
+public:
+	DuckmanDialogSystem(IllusionsEngine_Duckman *vm);
+	~DuckmanDialogSystem();
+	void addDialogItem(int16 choiceJumpOffs, uint32 sequenceId);
+	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
+	void updateDialogState();
+public:
+	IllusionsEngine_Duckman *_vm;	
+	Common::Array<DialogItem> _dialogItems;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_DUCKMAN_DIALOG_H
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 2aa6d00..17c930b 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_dialog.h"
 #include "illusions/duckman/duckman_specialcode.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -109,6 +110,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_soundMan = new SoundMan(this);
 
 	_fader = new Fader();
+	
+	_dialogSys = new DuckmanDialogSystem(this);
 
 	initUpdateFunctions();
 
@@ -167,6 +170,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
+	delete _dialogSys;
+
 	delete _fader;
 
 	delete _soundMan;
@@ -582,7 +587,7 @@ void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 delt
 			updateGameState2();
 			break;
 		case 3:
-			updateDialogState();
+			_dialogSys->updateDialogState();
 			break;
 		case 4:
 			// TODO ShellMgr_update(_cursor._control);
@@ -1024,113 +1029,4 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	return tempThreadId;
 }
 
-void IllusionsEngine_Duckman::addDialogItem(int16 choiceJumpOffs, uint32 sequenceId) {
-	DialogItem dialogItem;
-	dialogItem._choiceJumpOffs = choiceJumpOffs;
-	dialogItem._sequenceId = sequenceId;
-	_dialogItems.push_back(dialogItem);
-}
-
-void IllusionsEngine_Duckman::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId) {
-	static const uint32 kDialogSequenceIds[] = {
-		0,
-		0x6049C, 0x6049C, 0x6047A, 0x6049D,
-		0x60479, 0x6049E, 0x6049F, 0x60468
-	};
-	if (_dialogItems.size() == 1) {
-		*choiceOfsPtr = _dialogItems[0]._choiceJumpOffs;
-		notifyThreadId(callerThreadId);
-	} else {
-		if (!_cursor._control) {
-			Common::Point pos = getNamedPointPosition(0x70001);
-			_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
-			_cursor._control = _dict->getObjectControl(0x40004);
-		}
-		_cursor._control->appearActor();
-		setCursorActorIndex(6, 1, 0);
-
-		_cursor._gameState = 3;
-		_cursor._notifyThreadId30 = callerThreadId;
-		_cursor._dialogItemsCount = 0;
-		_cursor._overlappedObjectId = 0;
-		_cursor._op113_choiceOfsPtr = choiceOfsPtr;
-		_cursor._currOverlappedControl = 0;
-
-		/* TODO?
-		if (!_input->getCursorMouseMode())
-			_input->setMousePos((Point)0xBC0014);
-		*/
-
-		_cursor._dialogItemsCount = _dialogItems.size();
-		Common::Point placePt(20, 188);
-
-		for (uint i = 1; i <= _dialogItems.size(); ++i) {
-			DialogItem &dialogItem = _dialogItems[_dialogItems.size() - i];
-			_controls->placeDialogItem(i + 1, actorTypeId, dialogItem._sequenceId, placePt, dialogItem._choiceJumpOffs);
-			placePt.x += 40;
-		}
-
-		Common::Point placePt2 = getNamedPointPosition(0x700C3);
-		_controls->placeActor(0x5006E, placePt2, kDialogSequenceIds[_dialogItems.size()], 0x40148, 0);
-		Control *control = _dict->getObjectControl(0x40148);
-		control->_flags |= 8;
-		playSoundEffect(8);
-	}
-
-	_dialogItems.clear();
-
-}
-
-void IllusionsEngine_Duckman::updateDialogState() {
-	Common::Point mousePos = _input->getCursorPosition();
-	// TODO Handle keyboard input
-	_cursor._control->_actor->_position = mousePos;
-	mousePos = convertMousePos(mousePos);
-
-	Control *currOverlappedControl = _cursor._currOverlappedControl;
-	Control *newOverlappedControl;
-	
-	if (_controls->getDialogItemAtPos(_cursor._control, mousePos, &newOverlappedControl)) {
-		if (currOverlappedControl != newOverlappedControl) {
-			newOverlappedControl->setActorIndex(2);
-			newOverlappedControl->startSequenceActor(newOverlappedControl->_actor->_sequenceId, 2, 0);
-			if (currOverlappedControl) {
-				currOverlappedControl->setActorIndex(1);
-				currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
-			}
-			playSoundEffect(10);
-			startCursorSequence();
-			setCursorActorIndex(6, 2, 0);
-			_cursor._currOverlappedControl = newOverlappedControl;
-			_cursor._overlappedObjectId = newOverlappedControl->_objectId;
-		}
-	} else if (currOverlappedControl) {
-		currOverlappedControl->setActorIndex(1);
-		currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
-		playSoundEffect(10);
-		_cursor._currOverlappedControl = 0;
-		_cursor._overlappedObjectId = 0;
-		startCursorSequence();
-		setCursorActorIndex(6, 1, 0);
-	}
-
-	if (_input->pollButton(1)) {
-		if (_cursor._currOverlappedControl) {
-			playSoundEffect(9);
-			*_cursor._op113_choiceOfsPtr = _cursor._currOverlappedControl->_actor->_choiceJumpOffs;
-			_controls->destroyDialogItems();
-			Control *control = _dict->getObjectControl(0x40148);
-			_controls->destroyControl(control);
-			notifyThreadId(_cursor._notifyThreadId30);
-			_cursor._notifyThreadId30 = 0;
-			_cursor._gameState = 2;
-			_cursor._dialogItemsCount = 0;
-			_cursor._overlappedObjectId = 0;
-			_cursor._op113_choiceOfsPtr = 0;
-			_cursor._control->disappearActor();
-		}
-	}
-
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index ce76d36..7638c3a 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -31,6 +31,7 @@ namespace Illusions {
 
 class Dictionary;
 class ScriptStack;
+class DuckmanDialogSystem;
 
 struct Cursor_Duckman {
 	int _gameState;
@@ -51,11 +52,6 @@ struct Cursor_Duckman {
 	uint32 _field40;
 };
 
-struct DialogItem {
-	int16 _choiceJumpOffs;
-	uint32 _sequenceId;
-};
-
 struct ScreenShakerPoint {
 	int16 x, y;
 };
@@ -97,8 +93,8 @@ public:
 
 	Cursor_Duckman _cursor;
 	Control *_currWalkOverlappedControl;
-
-	Common::Array<DialogItem> _dialogItems;
+	
+	DuckmanDialogSystem *_dialogSys;
 
 	int _savedInventoryActorIndex;
 
@@ -181,10 +177,6 @@ public:
 	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
 	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
 
-	void addDialogItem(int16 choiceJumpOffs, uint32 sequenceId);
-	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
-	void updateDialogState();
-
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 99580e4..f8d3278 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	cursor.o \
 	detection.o \
 	dictionary.o \
+	duckman/duckman_dialog.o \
 	duckman/duckman_inventory.o \
 	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
index e8aa7f7..fbc8656 100644
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ b/engines/illusions/scriptopcodes_duckman.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_dialog.h"
 #include "illusions/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -716,13 +717,13 @@ void ScriptOpcodes_Duckman::opAddDialogItem(ScriptThread *scriptThread, OpCall &
 	ARG_INT16(choiceJumpOffs);
 	ARG_UINT32(sequenceId);
 	if (index && (_vm->_scriptResource->_blockCounters.getC0(index) & 0x40))
-		_vm->addDialogItem(choiceJumpOffs, sequenceId);
+		_vm->_dialogSys->addDialogItem(choiceJumpOffs, sequenceId);
 }
 
 void ScriptOpcodes_Duckman::opStartDialog(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(actorTypeId);
-	_vm->startDialog(&_vm->_menuChoiceOfs, actorTypeId, opCall._callerThreadId);
+	_vm->_dialogSys->startDialog(&_vm->_menuChoiceOfs, actorTypeId, opCall._callerThreadId);
 }
 
 void ScriptOpcodes_Duckman::opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall) {


Commit: a078073e88c094c23e4eb51e5fb85e2cecc3ae9a
    https://github.com/scummvm/scummvm/commit/a078073e88c094c23e4eb51e5fb85e2cecc3ae9a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move script opcode files to game specific directories

Changed paths:
  A engines/illusions/bbdou/scriptopcodes_bbdou.cpp
  A engines/illusions/bbdou/scriptopcodes_bbdou.h
  A engines/illusions/duckman/scriptopcodes_duckman.cpp
  A engines/illusions/duckman/scriptopcodes_duckman.h
  R engines/illusions/scriptopcodes_bbdou.cpp
  R engines/illusions/scriptopcodes_bbdou.h
  R engines/illusions/scriptopcodes_duckman.cpp
  R engines/illusions/scriptopcodes_duckman.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/duckman_dialog.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 21fa5d0..339b61d 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -37,7 +37,7 @@
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
 #include "illusions/scriptstack.h"
-#include "illusions/scriptopcodes_bbdou.h"
+#include "illusions/bbdou/scriptopcodes_bbdou.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
 #include "illusions/bbdou/bbdou_specialcode.h"
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
new file mode 100644
index 0000000..2f1c705
--- /dev/null
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -0,0 +1,780 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/scriptopcodes_bbdou.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/talkresource.h"
+#include "illusions/screen.h"
+#include "illusions/scriptstack.h"
+#include "illusions/sound.h"
+#include "illusions/specialcode.h"
+#include "illusions/threads/scriptthread.h"
+
+namespace Illusions {
+
+// ScriptOpcodes_BBDOU
+
+ScriptOpcodes_BBDOU::ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm)
+	: ScriptOpcodes(vm), _vm(vm) {
+	initOpcodes();
+}
+
+ScriptOpcodes_BBDOU::~ScriptOpcodes_BBDOU() {
+	freeOpcodes();
+}
+
+typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> ScriptOpcodeI;
+#define OPCODE(op, func) \
+	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_BBDOU::func); \
+	_opcodeNames[op] = #func;
+
+void ScriptOpcodes_BBDOU::initOpcodes() {
+	// First clear everything
+	for (uint i = 0; i < 256; ++i)
+		_opcodes[i] = 0;
+	// Register opcodes
+	OPCODE(2, opSuspend);
+	OPCODE(3, opYield);
+	OPCODE(4, opTerminate);
+	OPCODE(5, opJump);
+	OPCODE(6, opStartScriptThread);
+	OPCODE(8, opStartTempScriptThread);
+	OPCODE(9, opStartTimerThread);
+	OPCODE(12, opNotifyThreadId);
+	OPCODE(14, opSetThreadSceneId);
+	OPCODE(15, opEndTalkThreads);
+	OPCODE(16, opLoadResource);
+	OPCODE(17, opUnloadResource);
+	OPCODE(20, opEnterScene);
+	OPCODE(25, opChangeScene);
+	OPCODE(26, opStartModalScene);
+	OPCODE(27, opExitModalScene);
+	OPCODE(30, opEnterCloseUpScene);
+	OPCODE(31, opExitCloseUpScene);
+	OPCODE(32, opPanCenterObject);
+	OPCODE(34, opPanToObject);
+	OPCODE(35, opPanToNamedPoint);
+	OPCODE(36, opPanToPoint);
+	OPCODE(37, opPanStop);
+	OPCODE(39, opSetDisplay);
+	OPCODE(42, opIncBlockCounter);
+	OPCODE(43, opClearBlockCounter);
+	OPCODE(45, opSetProperty);
+	OPCODE(46, opPlaceActor);
+	OPCODE(47, opFaceActor);
+	OPCODE(48, opFaceActorToObject);	
+	OPCODE(49, opStartSequenceActor);
+	OPCODE(51, opStartMoveActor);
+	OPCODE(53, opSetActorToNamedPoint);
+	OPCODE(56, opStartTalkThread);
+	OPCODE(57, opAppearActor);
+	OPCODE(58, opDisappearActor);
+	OPCODE(60, opActivateObject);
+	OPCODE(61, opDeactivateObject);
+	OPCODE(62, opSetDefaultSequence);
+	OPCODE(63, opSetSelectSfx);
+	OPCODE(64, opSetMoveSfx);
+	OPCODE(65, opSetDenySfx);
+	OPCODE(66, opSetAdjustUpSfx);
+	OPCODE(67, opSetAdjustDnSfx);
+	OPCODE(71, opStartSound);
+	OPCODE(74, opStopSound);
+	OPCODE(75, opStartMusic);
+	OPCODE(76, opStopMusic);
+	OPCODE(78, opStackPushRandom);
+	OPCODE(79, opIfLte);
+	OPCODE(80, opAddMenuChoice);
+	OPCODE(81, opDisplayMenu);
+	OPCODE(82, opSwitchMenuChoice);
+	OPCODE(84, opResetGame);
+	OPCODE(87, opDeactivateButton);
+	OPCODE(88, opActivateButton);
+	OPCODE(103, opJumpIf);
+	OPCODE(104, opIsPrevSceneId);
+	OPCODE(105, opIsCurrentSceneId);
+	OPCODE(106, opIsActiveSceneId);
+	OPCODE(107, opNot);
+	OPCODE(108, opAnd);
+	OPCODE(109, opOr);
+	OPCODE(110, opGetProperty);
+	OPCODE(111, opCompareBlockCounter);
+	OPCODE(126, opDebug126);
+	OPCODE(144, opPlayVideo);
+	OPCODE(146, opStackPop);
+	OPCODE(147, opStackDup);
+	OPCODE(148, opLoadSpecialCodeModule);
+	OPCODE(150, opRunSpecialCode);
+	OPCODE(160, opStopActor);
+	OPCODE(161, opSetActorUsePan);
+	OPCODE(168, opStartAbortableThread);
+	OPCODE(169, opKillThread);
+	OPCODE(175, opSetSceneIdThreadId);
+	OPCODE(176, opStackPush0);
+	OPCODE(177, opSetFontId);
+	OPCODE(178, opAddMenuKey);
+	OPCODE(179, opChangeSceneAll);
+}
+
+#undef OPCODE
+
+void ScriptOpcodes_BBDOU::freeOpcodes() {
+	for (uint i = 0; i < 256; ++i)
+		delete _opcodes[i];
+}
+
+// Opcodes
+
+void ScriptOpcodes_BBDOU::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_BBDOU::opYield(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_BBDOU::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes_BBDOU::opJump(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_BBDOU::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->startScriptThread(threadId, opCall._threadId,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_BBDOU::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(codeOffs);
+	_vm->startTempScriptThread(opCall._code + codeOffs,
+		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_BBDOU::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(isAbortable);
+	ARG_INT16(duration);
+	ARG_INT16(maxDuration);
+	if (maxDuration)
+		duration += _vm->getRandom(maxDuration);
+		
+duration = 1;//DEBUG Speeds up things		
+		
+	if (isAbortable)
+		_vm->startAbortableTimerThread(duration, opCall._threadId);
+	else
+		_vm->startTimerThread(duration, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	Thread *thread = _vm->_threads->findThread(opCall._callerThreadId);
+	if (!(thread->_notifyFlags & 1))
+		_vm->notifyThreadId(thread->_callingThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
+}
+
+void ScriptOpcodes_BBDOU::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_threads->endTalkThreads();
+}
+
+void ScriptOpcodes_BBDOU::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	uint32 sceneId = _vm->getCurrentScene();
+	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_resSys->unloadResourceById(resourceId);
+}
+
+void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
+	if (scenesCount > 0) {
+		uint32 currSceneId;
+		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
+		// TODO krnfileDump(currSceneId);
+	}
+	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
+		opCall._result = kTSTerminate;
+}
+
+//DEBUG Scenes
+//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
+//uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
+//uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
+//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+//uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
+//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
+//uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
+//uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
+//uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
+//uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
+//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
+uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
+
+void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+
+	if (dsceneId) {
+		sceneId = dsceneId;
+		threadId = dthreadId;
+		dsceneId = 0;
+	}
+	
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_prevSceneId = _vm->getCurrentScene();
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	_vm->startScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_BBDOU::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+}
+
+void ScriptOpcodes_BBDOU::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_BBDOU::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	_vm->_camera->panCenterObject(objectId, speed);
+}
+
+void ScriptOpcodes_BBDOU::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);	
+	ARG_INT16(x);	
+	ARG_INT16(y);	
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_camera->stopPan();
+}
+
+void ScriptOpcodes_BBDOU::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flag);
+	_vm->_screen->setDisplayOn(flag != 0);
+}
+
+void ScriptOpcodes_BBDOU::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
+	if (value <= 63)
+		_vm->_scriptResource->_blockCounters.set(index, value);
+}
+
+void ScriptOpcodes_BBDOU::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.set(index, 0);
+}
+
+void ScriptOpcodes_BBDOU::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value);	
+	ARG_UINT32(propertyId);	
+	_vm->_scriptResource->_properties.set(propertyId, value != 0);
+}
+
+void ScriptOpcodes_BBDOU::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(actorTypeId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->faceActor(facing);
+}
+
+void ScriptOpcodes_BBDOU::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Control *control2 = _vm->_dict->getObjectControl(objectId2);
+	Common::Point pos1 = control1->getActorPosition();
+	Common::Point pos2 = control2->getActorPosition();
+	uint facing;
+	if (_vm->calcPointDirection(pos1, pos2, facing))
+		control1->faceActor(facing);
+}
+
+void ScriptOpcodes_BBDOU::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	// NOTE Skipped checking for stalled sequence, not sure if needed
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->stopActor();
+	control->setActorPosition(pos);
+}
+
+void ScriptOpcodes_BBDOU::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(duration);	
+	ARG_UINT32(objectId);
+	ARG_UINT32(talkId);
+	ARG_UINT32(sequenceId1);
+	ARG_UINT32(sequenceId2);
+	ARG_UINT32(namedPointId);
+	_vm->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (!control) {
+		Common::Point pos = _vm->getNamedPointPosition(0x70023);
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
+		control = _vm->_dict->getObjectControl(objectId);
+		control->startSequenceActor(0x60001, 2, 0);
+	}
+	control->appearActor();
+}
+
+void ScriptOpcodes_BBDOU::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->disappearActor();
+}
+
+void ScriptOpcodes_BBDOU::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (control)
+		control->activateObject();
+}
+
+void ScriptOpcodes_BBDOU::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->deactivateObject();
+}
+
+void ScriptOpcodes_BBDOU::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(defaultSequenceId);
+	ARG_UINT32(sequenceId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
+}
+
+void ScriptOpcodes_BBDOU::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setSelectSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setMoveSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setDenySfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustUpSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustDnSfx(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(volume);
+	ARG_INT16(pan);
+	ARG_UINT32(soundEffectId);
+	_vm->_soundMan->playSound(soundEffectId, volume, pan);
+}
+void ScriptOpcodes_BBDOU::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	_vm->_soundMan->stopSound(soundEffectId);
+}
+
+void ScriptOpcodes_BBDOU::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(volume);
+	ARG_INT16(pan);
+	ARG_UINT32(musicId);
+	ARG_UINT32(type);
+	_vm->_soundMan->playMusic(musicId, type, volume, pan, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_soundMan->stopMusic();
+}
+
+void ScriptOpcodes_BBDOU::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(maxValue);
+	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
+}
+
+void ScriptOpcodes_BBDOU::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(rvalue);
+	ARG_INT16(elseJumpOffs);
+	int16 lvalue = _vm->_stack->pop();
+	if (!(lvalue <= rvalue))
+		opCall._deltaOfs += elseJumpOffs;
+}
+
+void ScriptOpcodes_BBDOU::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(jumpOffs);
+	ARG_INT16(endMarker);
+	_vm->_stack->push(endMarker);
+	_vm->_stack->push(jumpOffs);
+}
+
+void ScriptOpcodes_BBDOU::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(unk1);
+	ARG_UINT32(menuId);
+	ARG_UINT32(unk2);
+	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
+	// Remove menu choices from the stack
+	do {
+		_vm->_stack->pop();
+	} while (_vm->_stack->pop() == 0);
+
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+
+}
+
+void ScriptOpcodes_BBDOU::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+_vm->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
+
+	opCall._deltaOfs += _vm->_menuChoiceOfs;
+}
+
+void ScriptOpcodes_BBDOU::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_threads->terminateThreads(opCall._callerThreadId);
+	_vm->reset();
+	_vm->_input->activateButton(0xFFFF);
+	// TODO _vm->stopMusic();
+	// TODO _vm->_gameStates->clear();
+}
+
+void ScriptOpcodes_BBDOU::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->deactivateButton(button);
+}
+
+void ScriptOpcodes_BBDOU::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->activateButton(button);
+}
+
+void ScriptOpcodes_BBDOU::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	int16 value = _vm->_stack->pop();
+	if (value == 0)
+		opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_BBDOU::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opNot(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->pop();
+	_vm->_stack->push(value != 0 ? 0 : 1);
+}
+
+void ScriptOpcodes_BBDOU::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 & value2);
+}
+
+void ScriptOpcodes_BBDOU::opOr(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 | value2);
+}
+
+void ScriptOpcodes_BBDOU::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(propertyId)
+	bool value = _vm->_scriptResource->_properties.get(propertyId);
+	_vm->_stack->push(value ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	ARG_INT16(compareOp);	
+	ARG_INT16(rvalue);
+	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
+	bool compareResult = false;
+	switch (compareOp) {
+	case 1:
+		compareResult = lvalue == rvalue;
+		break;
+	case 2:
+		compareResult = lvalue != rvalue;
+		break;
+	case 3:
+		compareResult = lvalue < rvalue;
+		break;
+	case 4:
+		compareResult = lvalue > rvalue;
+		break;
+	case 5:
+		compareResult = lvalue >= rvalue;
+		break;
+	case 6:
+		compareResult = lvalue <= rvalue;
+		break;
+	}
+	_vm->_stack->push(compareResult ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG] %s", (char*)opCall._code);
+}
+
+void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(videoId);
+	ARG_UINT32(priority);
+	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
+	
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+	
+}
+
+void ScriptOpcodes_BBDOU::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->pop(); 
+}
+
+void ScriptOpcodes_BBDOU::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->peek();
+	_vm->_stack->push(value); 
+}
+
+void ScriptOpcodes_BBDOU::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeModuleId);
+	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
+}
+
+void ScriptOpcodes_BBDOU::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeId);
+	_vm->_specialCode->run(specialCodeId, opCall);
+}
+
+void ScriptOpcodes_BBDOU::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->stopActor();
+}
+
+void ScriptOpcodes_BBDOU::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(usePan)
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorUsePan(usePan);
+}
+
+void ScriptOpcodes_BBDOU::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(codeOffs);
+	ARG_INT16(skipOffs);
+	_vm->startAbortableThread(opCall._code + codeOffs,
+		opCall._code + skipOffs, opCall._threadId);
+}
+
+void ScriptOpcodes_BBDOU::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->killThread(threadId);
+}
+
+void ScriptOpcodes_BBDOU::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->setSceneIdThreadId(sceneId, threadId);
+}
+
+void ScriptOpcodes_BBDOU::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->push(0);
+}
+
+void ScriptOpcodes_BBDOU::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(fontId);
+	_vm->setCurrFontId(fontId);
+}
+
+void ScriptOpcodes_BBDOU::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(key);
+	ARG_UINT32(threadId);
+	// TODO _vm->addMenuKey(key, threadId);
+}
+
+void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	// NOTE Skipped checking for stalled resources
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_prevSceneId = _vm->getCurrentScene();
+	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
new file mode 100644
index 0000000..89f73dc
--- /dev/null
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -0,0 +1,129 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_SCRIPTOPCODES_BBDOU_H
+#define ILLUSIONS_BBDOU_SCRIPTOPCODES_BBDOU_H
+
+#include "illusions/scriptopcodes.h"
+#include "common/func.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+class ScriptThread;
+
+class ScriptOpcodes_BBDOU : public ScriptOpcodes {
+public:
+	ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm);
+	~ScriptOpcodes_BBDOU();
+	void initOpcodes();
+	void freeOpcodes();
+protected:
+	IllusionsEngine_BBDOU *_vm;
+
+	// Opcodes
+	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
+	void opYield(ScriptThread *scriptThread, OpCall &opCall);
+	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
+	void opJump(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
+	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
+	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
+	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opNot(ScriptThread *scriptThread, OpCall &opCall);
+	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
+	void opOr(ScriptThread *scriptThread, OpCall &opCall);
+	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
+	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
+	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_SCRIPTOPCODES_BBDOU_H
diff --git a/engines/illusions/duckman/duckman_dialog.cpp b/engines/illusions/duckman/duckman_dialog.cpp
index b0b74df..b30a2be 100644
--- a/engines/illusions/duckman/duckman_dialog.cpp
+++ b/engines/illusions/duckman/duckman_dialog.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/duckman_dialog.h"
+#include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
@@ -38,7 +39,6 @@
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
-#include "illusions/scriptopcodes_duckman.h"
 #include "illusions/scriptstack.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index 6553631..8d33aa7 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -24,9 +24,9 @@
 #include "illusions/duckman/duckman_specialcode.h"
 #include "illusions/duckman/duckman_inventory.h"
 #include "illusions/duckman/propertytimers.h"
+#include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/resources/scriptresource.h"
-#include "illusions/scriptopcodes_duckman.h"
 #include "illusions/specialcode.h"
 
 #include "engines/util.h"
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 17c930b..52567f2 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -23,6 +23,7 @@
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/duckman_dialog.h"
 #include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
@@ -39,7 +40,6 @@
 #include "illusions/resourcesystem.h"
 #include "illusions/screen.h"
 #include "illusions/screentext.h"
-#include "illusions/scriptopcodes_duckman.h"
 #include "illusions/scriptstack.h"
 #include "illusions/sound.h"
 #include "illusions/specialcode.h"
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
new file mode 100644
index 0000000..0e79774
--- /dev/null
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -0,0 +1,959 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/scriptopcodes_duckman.h"
+#include "illusions/duckman/duckman_dialog.h"
+#include "illusions/actor.h"
+#include "illusions/camera.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/resources/talkresource.h"
+#include "illusions/screen.h"
+#include "illusions/scriptstack.h"
+#include "illusions/sound.h"
+#include "illusions/specialcode.h"
+#include "illusions/threads/scriptthread.h"
+
+namespace Illusions {
+
+// ScriptOpcodes_Duckman
+
+ScriptOpcodes_Duckman::ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm)
+	: ScriptOpcodes(vm), _vm(vm) {
+	initOpcodes();
+}
+
+ScriptOpcodes_Duckman::~ScriptOpcodes_Duckman() {
+	freeOpcodes();
+}
+
+typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_Duckman> ScriptOpcodeI;
+#define OPCODE(op, func) \
+	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_Duckman::func); \
+	_opcodeNames[op] = #func;
+
+void ScriptOpcodes_Duckman::initOpcodes() {
+	// First clear everything
+	for (uint i = 0; i < 256; ++i)
+		_opcodes[i] = 0;
+	OPCODE(1, opNop);
+	OPCODE(2, opSuspend);
+	OPCODE(3, opYield);
+	OPCODE(4, opTerminate);
+	OPCODE(5, opJump);
+	OPCODE(6, opStartScriptThread);
+	OPCODE(7, opStartTimerThread);
+	OPCODE(9, opNotifyThread);
+	OPCODE(10, opSuspendThread);
+	OPCODE(16, opLoadResource);
+	OPCODE(17, opUnloadResource);
+	OPCODE(18, opEnterScene18);
+	OPCODE(20, opChangeScene);
+	OPCODE(22, opStartModalScene);
+	OPCODE(23, opExitModalScene);
+	OPCODE(24, opEnterScene24);
+	OPCODE(25, opLeaveScene24);
+	OPCODE(32, opPanCenterObject);
+	OPCODE(33, opPanTrackObject);
+	OPCODE(34, opPanToObject);
+	OPCODE(35, opPanToNamedPoint);
+	OPCODE(36, opPanToPoint);
+	OPCODE(37, opPanStop);
+	OPCODE(38, opStartFade);
+	OPCODE(39, opSetDisplay);
+	OPCODE(40, opSetCameraBounds);
+	OPCODE(48, opSetProperty);
+	OPCODE(49, opPlaceActor);
+	OPCODE(50, opFaceActor);
+	OPCODE(51, opFaceActorToObject);
+	OPCODE(52, opStartSequenceActor);
+	OPCODE(53, opStartSequenceActorAtPosition);
+	OPCODE(54, opStartMoveActor);
+	OPCODE(55, opStartMoveActorToObject);
+	OPCODE(56, opStartTalkThread);
+	OPCODE(57, opAppearActor);
+	OPCODE(58, opDisappearActor);
+	OPCODE(59, opActivateObject);
+	OPCODE(60, opDeactivateObject);
+	OPCODE(61, opSetDefaultSequence);
+	OPCODE(64, opStopCursorHoldingObject);
+	OPCODE(65, opStartCursorHoldingObject);
+	OPCODE(66, opPlayVideo);
+	OPCODE(69, opRunSpecialCode);
+	OPCODE(72, opStartSound);
+	OPCODE(75, opStopSound);
+	OPCODE(76, opStartMidiMusic);
+	OPCODE(77, opStopMidiMusic);
+	OPCODE(78, opFadeMidiMusic);
+	OPCODE(80, opAddMenuChoice);
+	OPCODE(81, opDisplayMenu);
+	OPCODE(82, opSwitchMenuChoice);
+	OPCODE(84, opResetGame);
+	OPCODE(87, opDeactivateButton);
+	OPCODE(88, opActivateButton);
+	OPCODE(96, opIncBlockCounter);
+	OPCODE(97, opClearBlockCounter);
+	OPCODE(104, opJumpIf);
+	OPCODE(105, opIsPrevSceneId);
+	OPCODE(106, opNot);
+	OPCODE(107, opAnd);
+	OPCODE(108, opOr);
+	OPCODE(109, opGetProperty);
+	OPCODE(110, opCompareBlockCounter);
+	OPCODE(112, opAddDialogItem);
+	OPCODE(113, opStartDialog);
+	OPCODE(114, opJumpToDialogChoice);
+	OPCODE(115, opSetBlockCounter115);
+	OPCODE(116, opSetBlockCounter116);
+	OPCODE(117, opSetBlockCounter117);
+	OPCODE(118, opSetBlockCounter118);
+	OPCODE(126, opDebug126);
+	OPCODE(127, opDebug127);
+#if 0		
+	// Register opcodes
+	OPCODE(8, opStartTempScriptThread);
+	OPCODE(14, opSetThreadSceneId);
+	OPCODE(15, opEndTalkThreads);
+	OPCODE(20, opEnterScene);
+	OPCODE(30, opEnterCloseUpScene);
+	OPCODE(31, opExitCloseUpScene);
+	OPCODE(53, opSetActorToNamedPoint);
+	OPCODE(63, opSetSelectSfx);
+	OPCODE(64, opSetMoveSfx);
+	OPCODE(65, opSetDenySfx);
+	OPCODE(66, opSetAdjustUpSfx);
+	OPCODE(67, opSetAdjustDnSfx);
+	OPCODE(78, opStackPushRandom);
+	OPCODE(79, opIfLte);
+	OPCODE(105, opIsCurrentSceneId);
+	OPCODE(106, opIsActiveSceneId);
+	OPCODE(146, opStackPop);
+	OPCODE(147, opStackDup);
+	OPCODE(148, opLoadSpecialCodeModule);
+	OPCODE(160, opStopActor);
+	OPCODE(161, opSetActorUsePan);
+	OPCODE(168, opStartAbortableThread);
+	OPCODE(169, opKillThread);
+	OPCODE(175, opSetSceneIdThreadId);
+	OPCODE(176, opStackPush0);
+	OPCODE(177, opSetFontId);
+	OPCODE(178, opAddMenuKey);
+	OPCODE(179, opChangeSceneAll);
+#endif	
+}
+
+#undef OPCODE
+
+void ScriptOpcodes_Duckman::freeOpcodes() {
+	for (uint i = 0; i < 256; ++i)
+		delete _opcodes[i];
+}
+
+// Opcodes
+
+void ScriptOpcodes_Duckman::opNop(ScriptThread *scriptThread, OpCall &opCall) {
+}
+
+void ScriptOpcodes_Duckman::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_Duckman::opYield(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_Duckman::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes_Duckman::opJump(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_Duckman::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->startScriptThread(threadId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(isAbortable);
+	ARG_INT16(duration);
+	ARG_INT16(maxDuration);
+	if (maxDuration)
+		duration += _vm->getRandom(maxDuration);
+		
+//duration = 1;//DEBUG Speeds up things
+duration = 5;
+		
+	if (isAbortable)
+		_vm->startAbortableTimerThread(duration, opCall._threadId);
+	else
+		_vm->startTimerThread(duration, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opNotifyThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->notifyId(threadId);
+	_vm->_threads->notifyTimerThreads(threadId);
+}
+
+void ScriptOpcodes_Duckman::opSuspendThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->suspendId(threadId);
+	_vm->_threads->suspendTimerThreads(threadId);
+}
+
+void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	uint32 sceneId = _vm->getCurrentScene();
+	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(resourceId);
+	_vm->_resSys->unloadResourceById(resourceId);
+}
+
+void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->enterScene(sceneId, 0);
+}
+
+//static uint dsceneId = 0, dthreadId = 0;
+//static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
+//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+//static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
+//static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
+//static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
+//static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
+//static uint dsceneId = 0x00010022, dthreadId = 0x00020114;
+//static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
+//static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
+//static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
+//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
+//static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
+static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
+//static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
+//static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
+
+void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->_input->discardButtons(0xFFFF);
+	
+	debug("changeScene(%08X, %08X)", sceneId, threadId);
+	
+	//DEBUG
+	if (dsceneId) {
+		sceneId = dsceneId;
+		threadId = dthreadId;
+		dsceneId = 0;
+	}
+	
+	if (_vm->_scriptResource->_properties.get(31)) {
+		_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
+	} else {
+		_vm->changeScene(sceneId, threadId, opCall._callerThreadId);
+	}
+}
+
+void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
+	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->enterScene(sceneId, threadId);
+	opCall._result = kTSSuspend;
+}
+
+void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_input->discardButtons(0xFFFF);
+	if (_vm->_scriptResource->_properties.get(0x000E0027)) {
+		// TODO _vm->startScriptThread2(0x10002, 0x20001, 0);
+		opCall._result = kTSTerminate;
+	} else {
+		_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
+		_vm->exitScene();
+		_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
+		_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+	}
+}
+
+void ScriptOpcodes_Duckman::opEnterScene24(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
+	_vm->enterScene(sceneId, 0);
+}
+
+void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
+	_vm->exitScene();
+	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
+}
+
+void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_UINT32(objectId);
+	_vm->_camera->panCenterObject(objectId, speed);
+}
+
+void ScriptOpcodes_Duckman::opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	_vm->_camera->panTrackObject(objectId);
+}
+
+void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(speed);
+	ARG_INT16(x);
+	ARG_INT16(y);
+	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_camera->stopPan();
+}
+
+void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(duration);
+	ARG_INT16(minValue);
+	ARG_INT16(maxValue);
+	ARG_INT16(firstIndex);
+	ARG_INT16(lastIndex);
+	_vm->startFader(duration, minValue, maxValue, firstIndex, lastIndex, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flag);
+	_vm->_screen->setDisplayOn(flag != 0);
+}
+
+void ScriptOpcodes_Duckman::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(x1);
+	ARG_INT16(y1);
+	ARG_INT16(x2);
+	ARG_INT16(y2);
+	_vm->_camera->setBounds(Common::Point(x1, y1), Common::Point(x2, y2));
+}
+
+void ScriptOpcodes_Duckman::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(value);
+	ARG_UINT32(propertyId);
+	_vm->_scriptResource->_properties.set(propertyId, value != 0);
+}
+
+void ScriptOpcodes_Duckman::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	uint32 actorTypeId = _vm->getObjectActorTypeId(objectId);
+	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(facing);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->faceActor(facing);
+}
+
+void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId2);
+	ARG_UINT32(objectId1);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Control *control2 = _vm->_dict->getObjectControl(objectId2);
+	Common::Point pos1 = control1->getActorPosition();
+	Common::Point pos2 = control2->getActorPosition();
+	uint facing;
+	if (_vm->calcPointDirection(pos1, pos2, facing))
+		control1->faceActor(facing);
+}
+
+void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorPosition(pos);
+	control->startSequenceActor(sequenceId, 2, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	ARG_UINT32(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	ARG_UINT32(sequenceId);
+	Control *control1 = _vm->_dict->getObjectControl(objectId1);
+	Common::Point pos;
+	if (objectId2 == 0x40003) {
+		pos = _vm->_cursor._position;
+	} else {
+		Control *control2 = _vm->_dict->getObjectControl(objectId2);
+		pos = control2->_feetPt;
+		if (control2->_actor) {
+			pos.x += control2->_actor->_position.x;
+			pos.y += control2->_actor->_position.y;
+		}
+	}
+	control1->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(talkId);
+	ARG_UINT32(sequenceId1);
+	ARG_UINT32(sequenceId2);
+	_vm->startTalkThread(objectId, talkId, sequenceId1, sequenceId2, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (!control) {
+		Common::Point pos = _vm->getNamedPointPosition(0x70001);		
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
+		control = _vm->_dict->getObjectControl(objectId);
+	}
+	control->appearActor();
+}
+
+void ScriptOpcodes_Duckman::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->disappearActor();
+}
+
+void ScriptOpcodes_Duckman::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	if (control)
+		control->activateObject();
+}
+
+void ScriptOpcodes_Duckman::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->deactivateObject();
+}
+
+void ScriptOpcodes_Duckman::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(defaultSequenceId);
+	ARG_UINT32(sequenceId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
+}
+
+void ScriptOpcodes_Duckman::opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flags);
+	_vm->stopCursorHoldingObject();
+	if (!(flags & 1))
+		_vm->playSoundEffect(7);
+}
+
+void ScriptOpcodes_Duckman::opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(flags);
+	ARG_UINT32(objectId);
+	ARG_UINT32(sequenceId);
+	_vm->startCursorHoldingObject(objectId, sequenceId);
+	if (!(flags & 1))
+		_vm->playSoundEffect(6);
+}
+
+void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	// NOTE This has no attached objectId or priority
+	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
+	
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._threadId);
+	
+}
+
+void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeId);
+debug("run(%08X)", specialCodeId);	
+	_vm->_specialCode->run(specialCodeId, opCall);
+debug("run(%08X) OK", specialCodeId);	
+}
+
+void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(volume);
+	ARG_UINT32(soundEffectId);
+	_vm->_soundMan->playSound(soundEffectId, volume, 0);
+}
+
+void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	_vm->_soundMan->stopSound(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(musicId);
+	// TODO _vm->playMidiMusic(musicId);
+}
+
+void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	// TODO _vm->stopMidiMusic();
+}
+
+void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(duration);
+	ARG_INT16(finalVolume);
+	// TODO _vm->fadeMidiMusic(2, finalVolume, duration, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(jumpOffs);
+	ARG_INT16(endMarker);
+	_vm->_stack->push(endMarker);
+	_vm->_stack->push(jumpOffs);
+}
+
+void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(unk1);
+	ARG_UINT32(menuId);
+	ARG_UINT32(unk2);
+	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
+	// Remove menu choices from the stack
+	do {
+		_vm->_stack->pop();
+	} while (_vm->_stack->pop() == 0);
+
+	//DEBUG Resume calling thread, later done by the video player
+	_vm->notifyThreadId(opCall._callerThreadId);
+
+}
+
+void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
+_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game"
+
+	opCall._deltaOfs += _vm->_menuChoiceOfs;
+}
+
+void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->reset();
+	_vm->_input->activateButton(0xFFFF);
+	// TODO _vm->stopMusic();
+	// TODO _vm->_gameStates->clear();
+}
+
+void ScriptOpcodes_Duckman::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->deactivateButton(button);
+}
+
+void ScriptOpcodes_Duckman::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(button)
+	_vm->_input->activateButton(button);
+}
+
+void ScriptOpcodes_Duckman::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
+	if (value <= 63)
+		_vm->_scriptResource->_blockCounters.set(index, value);
+}
+
+void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.set(index, 0);
+}
+
+void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(jumpOffs);
+	int16 value = _vm->_stack->pop();
+	if (value == 0)
+		opCall._deltaOfs += jumpOffs;
+}
+
+void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->getPrevScene() == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opNot(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->pop();
+	_vm->_stack->push(value != 0 ? 0 : 1);
+}
+
+void ScriptOpcodes_Duckman::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 & value2);
+}
+
+void ScriptOpcodes_Duckman::opOr(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value1 = _vm->_stack->pop();
+	int16 value2 = _vm->_stack->pop();
+	_vm->_stack->push(value1 | value2);
+}
+
+void ScriptOpcodes_Duckman::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(propertyId)
+	bool value = _vm->_scriptResource->_properties.get(propertyId);
+	_vm->_stack->push(value ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);	
+	ARG_INT16(compareOp);	
+	ARG_INT16(rvalue);
+	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
+	bool compareResult = false;
+	switch (compareOp) {
+	case 1:
+		compareResult = lvalue == rvalue;
+		break;
+	case 2:
+		compareResult = lvalue != rvalue;
+		break;
+	case 3:
+		compareResult = lvalue < rvalue;
+		break;
+	case 4:
+		compareResult = lvalue > rvalue;
+		break;
+	case 5:
+		compareResult = lvalue >= rvalue;
+		break;
+	case 6:
+		compareResult = lvalue <= rvalue;
+		break;
+	}
+	_vm->_stack->push(compareResult ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(index);
+	ARG_INT16(choiceJumpOffs);
+	ARG_UINT32(sequenceId);
+	if (index && (_vm->_scriptResource->_blockCounters.getC0(index) & 0x40))
+		_vm->_dialogSys->addDialogItem(choiceJumpOffs, sequenceId);
+}
+
+void ScriptOpcodes_Duckman::opStartDialog(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(actorTypeId);
+	_vm->_dialogSys->startDialog(&_vm->_menuChoiceOfs, actorTypeId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_Duckman::opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall) {
+	opCall._deltaOfs += _vm->_menuChoiceOfs;
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	if (_vm->_scriptResource->_blockCounters.getC0(index) & 0x80)
+		_vm->_scriptResource->_blockCounters.set(index, 0);
+	_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	if (!(_vm->_scriptResource->_blockCounters.getC0(index) & 0x80))
+		_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.setC0(index, 0);
+}
+
+void ScriptOpcodes_Duckman::opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(index);
+	_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
+}
+
+void ScriptOpcodes_Duckman::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG126] %s", (char*)opCall._code);
+}
+
+void ScriptOpcodes_Duckman::opDebug127(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG127] %s", (char*)opCall._code);
+}
+
+#if 0
+
+void ScriptOpcodes_Duckman::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(codeOffs);
+	_vm->startTempScriptThread(opCall._code + codeOffs,
+		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+void ScriptOpcodes_Duckman::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
+}
+
+void ScriptOpcodes_Duckman::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_threads->endTalkThreads();
+}
+
+void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
+	if (scenesCount > 0) {
+		uint32 currSceneId;
+		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
+		// TODO krnfileDump(currSceneId);
+	}
+	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
+		opCall._result = kTSTerminate;
+}
+
+void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_Duckman::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->exitScene(opCall._callerThreadId);
+	_vm->leavePause(opCall._callerThreadId);
+	opCall._result = kTSYield;
+}
+
+void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(namedPointId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	control->stopActor();
+	control->setActorPosition(pos);
+}
+
+void ScriptOpcodes_Duckman::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setSelectSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setMoveSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setDenySfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustUpSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(soundEffectId);
+	// TODO _vm->setAdjustDnSfx(soundEffectId);
+}
+
+void ScriptOpcodes_Duckman::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(maxValue);
+	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
+}
+
+void ScriptOpcodes_Duckman::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(rvalue);
+	ARG_INT16(elseJumpOffs);
+	int16 lvalue = _vm->_stack->pop();
+	if (!(lvalue <= rvalue))
+		opCall._deltaOfs += elseJumpOffs;
+}
+
+void ScriptOpcodes_Duckman::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->pop(); 
+}
+
+void ScriptOpcodes_Duckman::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
+	int16 value = _vm->_stack->peek();
+	_vm->_stack->push(value); 
+}
+
+void ScriptOpcodes_Duckman::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(specialCodeModuleId);
+	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
+}
+
+void ScriptOpcodes_Duckman::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->stopActor();
+}
+
+void ScriptOpcodes_Duckman::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(usePan)
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorUsePan(usePan);
+}
+
+void ScriptOpcodes_Duckman::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(codeOffs);
+	ARG_INT16(skipOffs);
+	_vm->startAbortableThread(opCall._code + codeOffs,
+		opCall._code + skipOffs, opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(threadId);
+	_vm->_threads->killThread(threadId);
+}
+
+void ScriptOpcodes_Duckman::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->setSceneIdThreadId(sceneId, threadId);
+}
+
+void ScriptOpcodes_Duckman::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->push(0);
+}
+
+void ScriptOpcodes_Duckman::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(fontId);
+	_vm->setCurrFontId(fontId);
+}
+
+void ScriptOpcodes_Duckman::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(key);
+	ARG_UINT32(threadId);
+	// TODO _vm->addMenuKey(key, threadId);
+}
+
+void ScriptOpcodes_Duckman::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	ARG_UINT32(threadId);
+	_vm->_input->discardButtons(0xFFFF);
+	_vm->_prevSceneId = _vm->getCurrentScene();
+	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
+	_vm->enterScene(sceneId, opCall._callerThreadId);
+	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->startAnonScriptThread(threadId, 0,
+		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
+}
+
+#endif
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
new file mode 100644
index 0000000..abb9982
--- /dev/null
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -0,0 +1,153 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_SCRIPTOPCODES_DUCKMAN_H
+#define ILLUSIONS_DUCKMAN_SCRIPTOPCODES_DUCKMAN_H
+
+#include "illusions/scriptopcodes.h"
+#include "common/func.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+class ScriptThread;
+
+class ScriptOpcodes_Duckman : public ScriptOpcodes {
+public:
+	ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm);
+	~ScriptOpcodes_Duckman();
+	void initOpcodes();
+	void freeOpcodes();
+protected:
+	IllusionsEngine_Duckman *_vm;
+
+	// Opcodes
+	
+	void opNop(ScriptThread *scriptThread, OpCall &opCall);
+	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
+	void opYield(ScriptThread *scriptThread, OpCall &opCall);
+	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
+	void opJump(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opNotifyThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSuspendThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
+	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
+	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opNot(ScriptThread *scriptThread, OpCall &opCall);
+	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
+	void opOr(ScriptThread *scriptThread, OpCall &opCall);
+	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
+	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartDialog(ScriptThread *scriptThread, OpCall &opCall);
+	void opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
+	
+#if 0	
+	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
+	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
+	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
+	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
+	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
+#endif	
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_SCRIPTOPCODES_DUCKMAN_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index f8d3278..0a01f7f 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
 	bbdou/illusions_bbdou.o \
+	bbdou/scriptopcodes_bbdou.o \
 	camera.o \
 	cursor.o \
 	detection.o \
@@ -16,6 +17,7 @@ MODULE_OBJS := \
 	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
 	duckman/propertytimers.o \
+	duckman/scriptopcodes_duckman.o \
 	fixedpoint.o \
 	graphics.o \
 	illusions.o \
@@ -33,8 +35,6 @@ MODULE_OBJS := \
 	screentext.o \
 	scriptstack.o \
 	scriptopcodes.o \
-	scriptopcodes_bbdou.o \
-	scriptopcodes_duckman.o \
 	sequenceopcodes.o \
 	sound.o \
 	specialcode.o \
diff --git a/engines/illusions/scriptopcodes_bbdou.cpp b/engines/illusions/scriptopcodes_bbdou.cpp
deleted file mode 100644
index 3e7241b..0000000
--- a/engines/illusions/scriptopcodes_bbdou.cpp
+++ /dev/null
@@ -1,780 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/bbdou/illusions_bbdou.h"
-#include "illusions/scriptopcodes_bbdou.h"
-#include "illusions/actor.h"
-#include "illusions/camera.h"
-#include "illusions/dictionary.h"
-#include "illusions/input.h"
-#include "illusions/resources/scriptresource.h"
-#include "illusions/resources/talkresource.h"
-#include "illusions/screen.h"
-#include "illusions/scriptstack.h"
-#include "illusions/sound.h"
-#include "illusions/specialcode.h"
-#include "illusions/threads/scriptthread.h"
-
-namespace Illusions {
-
-// ScriptOpcodes_BBDOU
-
-ScriptOpcodes_BBDOU::ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm)
-	: ScriptOpcodes(vm), _vm(vm) {
-	initOpcodes();
-}
-
-ScriptOpcodes_BBDOU::~ScriptOpcodes_BBDOU() {
-	freeOpcodes();
-}
-
-typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> ScriptOpcodeI;
-#define OPCODE(op, func) \
-	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_BBDOU::func); \
-	_opcodeNames[op] = #func;
-
-void ScriptOpcodes_BBDOU::initOpcodes() {
-	// First clear everything
-	for (uint i = 0; i < 256; ++i)
-		_opcodes[i] = 0;
-	// Register opcodes
-	OPCODE(2, opSuspend);
-	OPCODE(3, opYield);
-	OPCODE(4, opTerminate);
-	OPCODE(5, opJump);
-	OPCODE(6, opStartScriptThread);
-	OPCODE(8, opStartTempScriptThread);
-	OPCODE(9, opStartTimerThread);
-	OPCODE(12, opNotifyThreadId);
-	OPCODE(14, opSetThreadSceneId);
-	OPCODE(15, opEndTalkThreads);
-	OPCODE(16, opLoadResource);
-	OPCODE(17, opUnloadResource);
-	OPCODE(20, opEnterScene);
-	OPCODE(25, opChangeScene);
-	OPCODE(26, opStartModalScene);
-	OPCODE(27, opExitModalScene);
-	OPCODE(30, opEnterCloseUpScene);
-	OPCODE(31, opExitCloseUpScene);
-	OPCODE(32, opPanCenterObject);
-	OPCODE(34, opPanToObject);
-	OPCODE(35, opPanToNamedPoint);
-	OPCODE(36, opPanToPoint);
-	OPCODE(37, opPanStop);
-	OPCODE(39, opSetDisplay);
-	OPCODE(42, opIncBlockCounter);
-	OPCODE(43, opClearBlockCounter);
-	OPCODE(45, opSetProperty);
-	OPCODE(46, opPlaceActor);
-	OPCODE(47, opFaceActor);
-	OPCODE(48, opFaceActorToObject);	
-	OPCODE(49, opStartSequenceActor);
-	OPCODE(51, opStartMoveActor);
-	OPCODE(53, opSetActorToNamedPoint);
-	OPCODE(56, opStartTalkThread);
-	OPCODE(57, opAppearActor);
-	OPCODE(58, opDisappearActor);
-	OPCODE(60, opActivateObject);
-	OPCODE(61, opDeactivateObject);
-	OPCODE(62, opSetDefaultSequence);
-	OPCODE(63, opSetSelectSfx);
-	OPCODE(64, opSetMoveSfx);
-	OPCODE(65, opSetDenySfx);
-	OPCODE(66, opSetAdjustUpSfx);
-	OPCODE(67, opSetAdjustDnSfx);
-	OPCODE(71, opStartSound);
-	OPCODE(74, opStopSound);
-	OPCODE(75, opStartMusic);
-	OPCODE(76, opStopMusic);
-	OPCODE(78, opStackPushRandom);
-	OPCODE(79, opIfLte);
-	OPCODE(80, opAddMenuChoice);
-	OPCODE(81, opDisplayMenu);
-	OPCODE(82, opSwitchMenuChoice);
-	OPCODE(84, opResetGame);
-	OPCODE(87, opDeactivateButton);
-	OPCODE(88, opActivateButton);
-	OPCODE(103, opJumpIf);
-	OPCODE(104, opIsPrevSceneId);
-	OPCODE(105, opIsCurrentSceneId);
-	OPCODE(106, opIsActiveSceneId);
-	OPCODE(107, opNot);
-	OPCODE(108, opAnd);
-	OPCODE(109, opOr);
-	OPCODE(110, opGetProperty);
-	OPCODE(111, opCompareBlockCounter);
-	OPCODE(126, opDebug126);
-	OPCODE(144, opPlayVideo);
-	OPCODE(146, opStackPop);
-	OPCODE(147, opStackDup);
-	OPCODE(148, opLoadSpecialCodeModule);
-	OPCODE(150, opRunSpecialCode);
-	OPCODE(160, opStopActor);
-	OPCODE(161, opSetActorUsePan);
-	OPCODE(168, opStartAbortableThread);
-	OPCODE(169, opKillThread);
-	OPCODE(175, opSetSceneIdThreadId);
-	OPCODE(176, opStackPush0);
-	OPCODE(177, opSetFontId);
-	OPCODE(178, opAddMenuKey);
-	OPCODE(179, opChangeSceneAll);
-}
-
-#undef OPCODE
-
-void ScriptOpcodes_BBDOU::freeOpcodes() {
-	for (uint i = 0; i < 256; ++i)
-		delete _opcodes[i];
-}
-
-// Opcodes
-
-void ScriptOpcodes_BBDOU::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes_BBDOU::opYield(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes_BBDOU::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSTerminate;
-}
-
-void ScriptOpcodes_BBDOU::opJump(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs);
-	opCall._deltaOfs += jumpOffs;
-}
-
-void ScriptOpcodes_BBDOU::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->startScriptThread(threadId, opCall._threadId,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes_BBDOU::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(codeOffs);
-	_vm->startTempScriptThread(opCall._code + codeOffs,
-		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes_BBDOU::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(isAbortable);
-	ARG_INT16(duration);
-	ARG_INT16(maxDuration);
-	if (maxDuration)
-		duration += _vm->getRandom(maxDuration);
-		
-duration = 1;//DEBUG Speeds up things		
-		
-	if (isAbortable)
-		_vm->startAbortableTimerThread(duration, opCall._threadId);
-	else
-		_vm->startTimerThread(duration, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall) {
-	Thread *thread = _vm->_threads->findThread(opCall._callerThreadId);
-	if (!(thread->_notifyFlags & 1))
-		_vm->notifyThreadId(thread->_callingThreadId);
-}
-
-void ScriptOpcodes_BBDOU::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
-}
-
-void ScriptOpcodes_BBDOU::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_threads->endTalkThreads();
-}
-
-void ScriptOpcodes_BBDOU::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
-	uint32 sceneId = _vm->getCurrentScene();
-	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_resSys->unloadResourceById(resourceId);
-}
-
-void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
-	if (scenesCount > 0) {
-		uint32 currSceneId;
-		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
-		// TODO krnfileDump(currSceneId);
-	}
-	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
-		opCall._result = kTSTerminate;
-}
-
-//DEBUG Scenes
-//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
-//uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
-//uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
-//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
-//uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
-//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
-//uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
-//uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
-//uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
-//uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
-//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
-uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
-
-void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-
-	if (dsceneId) {
-		sceneId = dsceneId;
-		threadId = dthreadId;
-		dsceneId = 0;
-	}
-	
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_prevSceneId = _vm->getCurrentScene();
-	_vm->exitScene(opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
-	_vm->startAnonScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->enterPause(opCall._callerThreadId);
-	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-	_vm->startScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes_BBDOU::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->exitScene(opCall._callerThreadId);
-	_vm->leavePause(opCall._callerThreadId);
-	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
-}
-
-void ScriptOpcodes_BBDOU::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->enterPause(opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-}
-
-void ScriptOpcodes_BBDOU::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->exitScene(opCall._callerThreadId);
-	_vm->leavePause(opCall._callerThreadId);
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes_BBDOU::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(objectId);
-	_vm->_camera->panCenterObject(objectId, speed);
-}
-
-void ScriptOpcodes_BBDOU::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = control->getActorPosition();
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_INT16(x);	
-	ARG_INT16(y);	
-	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_camera->stopPan();
-}
-
-void ScriptOpcodes_BBDOU::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(flag);
-	_vm->_screen->setDisplayOn(flag != 0);
-}
-
-void ScriptOpcodes_BBDOU::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
-	if (value <= 63)
-		_vm->_scriptResource->_blockCounters.set(index, value);
-}
-
-void ScriptOpcodes_BBDOU::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	_vm->_scriptResource->_blockCounters.set(index, 0);
-}
-
-void ScriptOpcodes_BBDOU::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value);	
-	ARG_UINT32(propertyId);	
-	_vm->_scriptResource->_properties.set(propertyId, value != 0);
-}
-
-void ScriptOpcodes_BBDOU::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(actorTypeId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(facing);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->faceActor(facing);
-}
-
-void ScriptOpcodes_BBDOU::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId1);
-	ARG_UINT32(objectId2);
-	Control *control1 = _vm->_dict->getObjectControl(objectId1);
-	Control *control2 = _vm->_dict->getObjectControl(objectId2);
-	Common::Point pos1 = control1->getActorPosition();
-	Common::Point pos2 = control2->getActorPosition();
-	uint facing;
-	if (_vm->calcPointDirection(pos1, pos2, facing))
-		control1->faceActor(facing);
-}
-
-void ScriptOpcodes_BBDOU::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->startSequenceActor(sequenceId, 2, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	// NOTE Skipped checking for stalled sequence, not sure if needed
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(namedPointId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->stopActor();
-	control->setActorPosition(pos);
-}
-
-void ScriptOpcodes_BBDOU::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(duration);	
-	ARG_UINT32(objectId);
-	ARG_UINT32(talkId);
-	ARG_UINT32(sequenceId1);
-	ARG_UINT32(sequenceId2);
-	ARG_UINT32(namedPointId);
-	_vm->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	if (!control) {
-		Common::Point pos = _vm->getNamedPointPosition(0x70023);
-		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
-		control = _vm->_dict->getObjectControl(objectId);
-		control->startSequenceActor(0x60001, 2, 0);
-	}
-	control->appearActor();
-}
-
-void ScriptOpcodes_BBDOU::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->disappearActor();
-}
-
-void ScriptOpcodes_BBDOU::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	if (control)
-		control->activateObject();
-}
-
-void ScriptOpcodes_BBDOU::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->deactivateObject();
-}
-
-void ScriptOpcodes_BBDOU::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(defaultSequenceId);
-	ARG_UINT32(sequenceId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
-}
-
-void ScriptOpcodes_BBDOU::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setSelectSfx(soundEffectId);
-}
-
-void ScriptOpcodes_BBDOU::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setMoveSfx(soundEffectId);
-}
-
-void ScriptOpcodes_BBDOU::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setDenySfx(soundEffectId);
-}
-
-void ScriptOpcodes_BBDOU::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustUpSfx(soundEffectId);
-}
-
-void ScriptOpcodes_BBDOU::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustDnSfx(soundEffectId);
-}
-
-void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(volume);
-	ARG_INT16(pan);
-	ARG_UINT32(soundEffectId);
-	_vm->_soundMan->playSound(soundEffectId, volume, pan);
-}
-void ScriptOpcodes_BBDOU::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	_vm->_soundMan->stopSound(soundEffectId);
-}
-
-void ScriptOpcodes_BBDOU::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(volume);
-	ARG_INT16(pan);
-	ARG_UINT32(musicId);
-	ARG_UINT32(type);
-	_vm->_soundMan->playMusic(musicId, type, volume, pan, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_soundMan->stopMusic();
-}
-
-void ScriptOpcodes_BBDOU::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(maxValue);
-	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
-}
-
-void ScriptOpcodes_BBDOU::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(rvalue);
-	ARG_INT16(elseJumpOffs);
-	int16 lvalue = _vm->_stack->pop();
-	if (!(lvalue <= rvalue))
-		opCall._deltaOfs += elseJumpOffs;
-}
-
-void ScriptOpcodes_BBDOU::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(jumpOffs);
-	ARG_INT16(endMarker);
-	_vm->_stack->push(endMarker);
-	_vm->_stack->push(jumpOffs);
-}
-
-void ScriptOpcodes_BBDOU::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(unk1);
-	ARG_UINT32(menuId);
-	ARG_UINT32(unk2);
-	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
-	// Remove menu choices from the stack
-	do {
-		_vm->_stack->pop();
-	} while (_vm->_stack->pop() == 0);
-
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
-
-}
-
-void ScriptOpcodes_BBDOU::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-_vm->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
-
-	opCall._deltaOfs += _vm->_menuChoiceOfs;
-}
-
-void ScriptOpcodes_BBDOU::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_threads->terminateThreads(opCall._callerThreadId);
-	_vm->reset();
-	_vm->_input->activateButton(0xFFFF);
-	// TODO _vm->stopMusic();
-	// TODO _vm->_gameStates->clear();
-}
-
-void ScriptOpcodes_BBDOU::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(button)
-	_vm->_input->deactivateButton(button);
-}
-
-void ScriptOpcodes_BBDOU::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(button)
-	_vm->_input->activateButton(button);
-}
-
-void ScriptOpcodes_BBDOU::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs);
-	int16 value = _vm->_stack->pop();
-	if (value == 0)
-		opCall._deltaOfs += jumpOffs;
-}
-
-void ScriptOpcodes_BBDOU::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes_BBDOU::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes_BBDOU::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
-}
-
-void ScriptOpcodes_BBDOU::opNot(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_stack->pop();
-	_vm->_stack->push(value != 0 ? 0 : 1);
-}
-
-void ScriptOpcodes_BBDOU::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value1 = _vm->_stack->pop();
-	int16 value2 = _vm->_stack->pop();
-	_vm->_stack->push(value1 & value2);
-}
-
-void ScriptOpcodes_BBDOU::opOr(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value1 = _vm->_stack->pop();
-	int16 value2 = _vm->_stack->pop();
-	_vm->_stack->push(value1 | value2);
-}
-
-void ScriptOpcodes_BBDOU::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(propertyId)
-	bool value = _vm->_scriptResource->_properties.get(propertyId);
-	_vm->_stack->push(value ? 1 : 0);
-}
-
-void ScriptOpcodes_BBDOU::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	ARG_INT16(compareOp);	
-	ARG_INT16(rvalue);
-	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
-	bool compareResult = false;
-	switch (compareOp) {
-	case 1:
-		compareResult = lvalue == rvalue;
-		break;
-	case 2:
-		compareResult = lvalue != rvalue;
-		break;
-	case 3:
-		compareResult = lvalue < rvalue;
-		break;
-	case 4:
-		compareResult = lvalue > rvalue;
-		break;
-	case 5:
-		compareResult = lvalue >= rvalue;
-		break;
-	case 6:
-		compareResult = lvalue <= rvalue;
-		break;
-	}
-	_vm->_stack->push(compareResult ? 1 : 0);
-}
-
-void ScriptOpcodes_BBDOU::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Prints some debug text
-	debug(1, "[DBG] %s", (char*)opCall._code);
-}
-
-void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(videoId);
-	ARG_UINT32(priority);
-	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
-	
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
-	
-}
-
-void ScriptOpcodes_BBDOU::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->pop(); 
-}
-
-void ScriptOpcodes_BBDOU::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_stack->peek();
-	_vm->_stack->push(value); 
-}
-
-void ScriptOpcodes_BBDOU::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeModuleId);
-	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
-}
-
-void ScriptOpcodes_BBDOU::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeId);
-	_vm->_specialCode->run(specialCodeId, opCall);
-}
-
-void ScriptOpcodes_BBDOU::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->stopActor();
-}
-
-void ScriptOpcodes_BBDOU::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(usePan)
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->setActorUsePan(usePan);
-}
-
-void ScriptOpcodes_BBDOU::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(codeOffs);
-	ARG_INT16(skipOffs);
-	_vm->startAbortableThread(opCall._code + codeOffs,
-		opCall._code + skipOffs, opCall._threadId);
-}
-
-void ScriptOpcodes_BBDOU::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_threads->killThread(threadId);
-}
-
-void ScriptOpcodes_BBDOU::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->setSceneIdThreadId(sceneId, threadId);
-}
-
-void ScriptOpcodes_BBDOU::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->push(0);
-}
-
-void ScriptOpcodes_BBDOU::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(fontId);
-	_vm->setCurrFontId(fontId);
-}
-
-void ScriptOpcodes_BBDOU::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(key);
-	ARG_UINT32(threadId);
-	// TODO _vm->addMenuKey(key, threadId);
-}
-
-void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_prevSceneId = _vm->getCurrentScene();
-	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
-	_vm->startAnonScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-} // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes_bbdou.h b/engines/illusions/scriptopcodes_bbdou.h
deleted file mode 100644
index b132011..0000000
--- a/engines/illusions/scriptopcodes_bbdou.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SCRIPTOPCODES_BBDOU_H
-#define ILLUSIONS_SCRIPTOPCODES_BBDOU_H
-
-#include "illusions/scriptopcodes.h"
-#include "common/func.h"
-
-namespace Illusions {
-
-class IllusionsEngine_BBDOU;
-class ScriptThread;
-
-class ScriptOpcodes_BBDOU : public ScriptOpcodes {
-public:
-	ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm);
-	~ScriptOpcodes_BBDOU();
-	void initOpcodes();
-	void freeOpcodes();
-protected:
-	IllusionsEngine_BBDOU *_vm;
-
-	// Opcodes
-	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
-	void opYield(ScriptThread *scriptThread, OpCall &opCall);
-	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
-	void opJump(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
-	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
-	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
-	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
-	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
-	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
-	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opNot(ScriptThread *scriptThread, OpCall &opCall);
-	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
-	void opOr(ScriptThread *scriptThread, OpCall &opCall);
-	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
-	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
-	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
-	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
-	
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SCRIPTOPCODES_BBDOU_H
diff --git a/engines/illusions/scriptopcodes_duckman.cpp b/engines/illusions/scriptopcodes_duckman.cpp
deleted file mode 100644
index fbc8656..0000000
--- a/engines/illusions/scriptopcodes_duckman.cpp
+++ /dev/null
@@ -1,959 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "illusions/duckman/illusions_duckman.h"
-#include "illusions/duckman/duckman_dialog.h"
-#include "illusions/scriptopcodes_duckman.h"
-#include "illusions/actor.h"
-#include "illusions/camera.h"
-#include "illusions/dictionary.h"
-#include "illusions/input.h"
-#include "illusions/resources/scriptresource.h"
-#include "illusions/resources/talkresource.h"
-#include "illusions/screen.h"
-#include "illusions/scriptstack.h"
-#include "illusions/sound.h"
-#include "illusions/specialcode.h"
-#include "illusions/threads/scriptthread.h"
-
-namespace Illusions {
-
-// ScriptOpcodes_Duckman
-
-ScriptOpcodes_Duckman::ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm)
-	: ScriptOpcodes(vm), _vm(vm) {
-	initOpcodes();
-}
-
-ScriptOpcodes_Duckman::~ScriptOpcodes_Duckman() {
-	freeOpcodes();
-}
-
-typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_Duckman> ScriptOpcodeI;
-#define OPCODE(op, func) \
-	_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_Duckman::func); \
-	_opcodeNames[op] = #func;
-
-void ScriptOpcodes_Duckman::initOpcodes() {
-	// First clear everything
-	for (uint i = 0; i < 256; ++i)
-		_opcodes[i] = 0;
-	OPCODE(1, opNop);
-	OPCODE(2, opSuspend);
-	OPCODE(3, opYield);
-	OPCODE(4, opTerminate);
-	OPCODE(5, opJump);
-	OPCODE(6, opStartScriptThread);
-	OPCODE(7, opStartTimerThread);
-	OPCODE(9, opNotifyThread);
-	OPCODE(10, opSuspendThread);
-	OPCODE(16, opLoadResource);
-	OPCODE(17, opUnloadResource);
-	OPCODE(18, opEnterScene18);
-	OPCODE(20, opChangeScene);
-	OPCODE(22, opStartModalScene);
-	OPCODE(23, opExitModalScene);
-	OPCODE(24, opEnterScene24);
-	OPCODE(25, opLeaveScene24);
-	OPCODE(32, opPanCenterObject);
-	OPCODE(33, opPanTrackObject);
-	OPCODE(34, opPanToObject);
-	OPCODE(35, opPanToNamedPoint);
-	OPCODE(36, opPanToPoint);
-	OPCODE(37, opPanStop);
-	OPCODE(38, opStartFade);
-	OPCODE(39, opSetDisplay);
-	OPCODE(40, opSetCameraBounds);
-	OPCODE(48, opSetProperty);
-	OPCODE(49, opPlaceActor);
-	OPCODE(50, opFaceActor);
-	OPCODE(51, opFaceActorToObject);
-	OPCODE(52, opStartSequenceActor);
-	OPCODE(53, opStartSequenceActorAtPosition);
-	OPCODE(54, opStartMoveActor);
-	OPCODE(55, opStartMoveActorToObject);
-	OPCODE(56, opStartTalkThread);
-	OPCODE(57, opAppearActor);
-	OPCODE(58, opDisappearActor);
-	OPCODE(59, opActivateObject);
-	OPCODE(60, opDeactivateObject);
-	OPCODE(61, opSetDefaultSequence);
-	OPCODE(64, opStopCursorHoldingObject);
-	OPCODE(65, opStartCursorHoldingObject);
-	OPCODE(66, opPlayVideo);
-	OPCODE(69, opRunSpecialCode);
-	OPCODE(72, opStartSound);
-	OPCODE(75, opStopSound);
-	OPCODE(76, opStartMidiMusic);
-	OPCODE(77, opStopMidiMusic);
-	OPCODE(78, opFadeMidiMusic);
-	OPCODE(80, opAddMenuChoice);
-	OPCODE(81, opDisplayMenu);
-	OPCODE(82, opSwitchMenuChoice);
-	OPCODE(84, opResetGame);
-	OPCODE(87, opDeactivateButton);
-	OPCODE(88, opActivateButton);
-	OPCODE(96, opIncBlockCounter);
-	OPCODE(97, opClearBlockCounter);
-	OPCODE(104, opJumpIf);
-	OPCODE(105, opIsPrevSceneId);
-	OPCODE(106, opNot);
-	OPCODE(107, opAnd);
-	OPCODE(108, opOr);
-	OPCODE(109, opGetProperty);
-	OPCODE(110, opCompareBlockCounter);
-	OPCODE(112, opAddDialogItem);
-	OPCODE(113, opStartDialog);
-	OPCODE(114, opJumpToDialogChoice);
-	OPCODE(115, opSetBlockCounter115);
-	OPCODE(116, opSetBlockCounter116);
-	OPCODE(117, opSetBlockCounter117);
-	OPCODE(118, opSetBlockCounter118);
-	OPCODE(126, opDebug126);
-	OPCODE(127, opDebug127);
-#if 0		
-	// Register opcodes
-	OPCODE(8, opStartTempScriptThread);
-	OPCODE(14, opSetThreadSceneId);
-	OPCODE(15, opEndTalkThreads);
-	OPCODE(20, opEnterScene);
-	OPCODE(30, opEnterCloseUpScene);
-	OPCODE(31, opExitCloseUpScene);
-	OPCODE(53, opSetActorToNamedPoint);
-	OPCODE(63, opSetSelectSfx);
-	OPCODE(64, opSetMoveSfx);
-	OPCODE(65, opSetDenySfx);
-	OPCODE(66, opSetAdjustUpSfx);
-	OPCODE(67, opSetAdjustDnSfx);
-	OPCODE(78, opStackPushRandom);
-	OPCODE(79, opIfLte);
-	OPCODE(105, opIsCurrentSceneId);
-	OPCODE(106, opIsActiveSceneId);
-	OPCODE(146, opStackPop);
-	OPCODE(147, opStackDup);
-	OPCODE(148, opLoadSpecialCodeModule);
-	OPCODE(160, opStopActor);
-	OPCODE(161, opSetActorUsePan);
-	OPCODE(168, opStartAbortableThread);
-	OPCODE(169, opKillThread);
-	OPCODE(175, opSetSceneIdThreadId);
-	OPCODE(176, opStackPush0);
-	OPCODE(177, opSetFontId);
-	OPCODE(178, opAddMenuKey);
-	OPCODE(179, opChangeSceneAll);
-#endif	
-}
-
-#undef OPCODE
-
-void ScriptOpcodes_Duckman::freeOpcodes() {
-	for (uint i = 0; i < 256; ++i)
-		delete _opcodes[i];
-}
-
-// Opcodes
-
-void ScriptOpcodes_Duckman::opNop(ScriptThread *scriptThread, OpCall &opCall) {
-}
-
-void ScriptOpcodes_Duckman::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes_Duckman::opYield(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes_Duckman::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._result = kTSTerminate;
-}
-
-void ScriptOpcodes_Duckman::opJump(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs);
-	opCall._deltaOfs += jumpOffs;
-}
-
-void ScriptOpcodes_Duckman::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->startScriptThread(threadId, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(isAbortable);
-	ARG_INT16(duration);
-	ARG_INT16(maxDuration);
-	if (maxDuration)
-		duration += _vm->getRandom(maxDuration);
-		
-//duration = 1;//DEBUG Speeds up things
-duration = 5;
-		
-	if (isAbortable)
-		_vm->startAbortableTimerThread(duration, opCall._threadId);
-	else
-		_vm->startTimerThread(duration, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opNotifyThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_threads->notifyId(threadId);
-	_vm->_threads->notifyTimerThreads(threadId);
-}
-
-void ScriptOpcodes_Duckman::opSuspendThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_threads->suspendId(threadId);
-	_vm->_threads->suspendTimerThreads(threadId);
-}
-
-void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	uint32 sceneId = _vm->getCurrentScene();
-	_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
-	_vm->notifyThreadId(opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(resourceId);
-	_vm->_resSys->unloadResourceById(resourceId);
-}
-
-void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->enterScene(sceneId, 0);
-}
-
-//static uint dsceneId = 0, dthreadId = 0;
-//static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
-//static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
-//static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
-//static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
-//static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
-//static uint dsceneId = 0x00010022, dthreadId = 0x00020114;
-//static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
-//static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
-//static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
-//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
-//static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
-static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
-//static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
-//static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
-
-void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->_input->discardButtons(0xFFFF);
-	
-	debug("changeScene(%08X, %08X)", sceneId, threadId);
-	
-	//DEBUG
-	if (dsceneId) {
-		sceneId = dsceneId;
-		threadId = dthreadId;
-		dsceneId = 0;
-	}
-	
-	if (_vm->_scriptResource->_properties.get(31)) {
-		_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
-	} else {
-		_vm->changeScene(sceneId, threadId, opCall._callerThreadId);
-	}
-}
-
-void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
-	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
-	_vm->enterScene(sceneId, threadId);
-	opCall._result = kTSSuspend;
-}
-
-void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_input->discardButtons(0xFFFF);
-	if (_vm->_scriptResource->_properties.get(0x000E0027)) {
-		// TODO _vm->startScriptThread2(0x10002, 0x20001, 0);
-		opCall._result = kTSTerminate;
-	} else {
-		_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
-		_vm->exitScene();
-		_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
-		_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
-	}
-}
-
-void ScriptOpcodes_Duckman::opEnterScene24(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
-	_vm->enterScene(sceneId, 0);
-}
-
-void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
-	_vm->exitScene();
-	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
-}
-
-void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);
-	ARG_UINT32(objectId);
-	_vm->_camera->panCenterObject(objectId, speed);
-}
-
-void ScriptOpcodes_Duckman::opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	_vm->_camera->panTrackObject(objectId);
-}
-
-void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = control->getActorPosition();
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);
-	ARG_INT16(x);
-	ARG_INT16(y);
-	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_camera->stopPan();
-}
-
-void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(duration);
-	ARG_INT16(minValue);
-	ARG_INT16(maxValue);
-	ARG_INT16(firstIndex);
-	ARG_INT16(lastIndex);
-	_vm->startFader(duration, minValue, maxValue, firstIndex, lastIndex, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(flag);
-	_vm->_screen->setDisplayOn(flag != 0);
-}
-
-void ScriptOpcodes_Duckman::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(x1);
-	ARG_INT16(y1);
-	ARG_INT16(x2);
-	ARG_INT16(y2);
-	_vm->_camera->setBounds(Common::Point(x1, y1), Common::Point(x2, y2));
-}
-
-void ScriptOpcodes_Duckman::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value);
-	ARG_UINT32(propertyId);
-	_vm->_scriptResource->_properties.set(propertyId, value != 0);
-}
-
-void ScriptOpcodes_Duckman::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	uint32 actorTypeId = _vm->getObjectActorTypeId(objectId);
-	_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(facing);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->faceActor(facing);
-}
-
-void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId2);
-	ARG_UINT32(objectId1);
-	Control *control1 = _vm->_dict->getObjectControl(objectId1);
-	Control *control2 = _vm->_dict->getObjectControl(objectId2);
-	Common::Point pos1 = control1->getActorPosition();
-	Common::Point pos2 = control2->getActorPosition();
-	uint facing;
-	if (_vm->calcPointDirection(pos1, pos2, facing))
-		control1->faceActor(facing);
-}
-
-void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->startSequenceActor(sequenceId, 2, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->setActorPosition(pos);
-	control->startSequenceActor(sequenceId, 2, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	ARG_UINT32(namedPointId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId1);
-	ARG_UINT32(objectId2);
-	ARG_UINT32(sequenceId);
-	Control *control1 = _vm->_dict->getObjectControl(objectId1);
-	Common::Point pos;
-	if (objectId2 == 0x40003) {
-		pos = _vm->_cursor._position;
-	} else {
-		Control *control2 = _vm->_dict->getObjectControl(objectId2);
-		pos = control2->_feetPt;
-		if (control2->_actor) {
-			pos.x += control2->_actor->_position.x;
-			pos.y += control2->_actor->_position.y;
-		}
-	}
-	control1->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(talkId);
-	ARG_UINT32(sequenceId1);
-	ARG_UINT32(sequenceId2);
-	_vm->startTalkThread(objectId, talkId, sequenceId1, sequenceId2, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	if (!control) {
-		Common::Point pos = _vm->getNamedPointPosition(0x70001);		
-		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
-		control = _vm->_dict->getObjectControl(objectId);
-	}
-	control->appearActor();
-}
-
-void ScriptOpcodes_Duckman::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->disappearActor();
-}
-
-void ScriptOpcodes_Duckman::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	if (control)
-		control->activateObject();
-}
-
-void ScriptOpcodes_Duckman::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->deactivateObject();
-}
-
-void ScriptOpcodes_Duckman::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(defaultSequenceId);
-	ARG_UINT32(sequenceId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
-}
-
-void ScriptOpcodes_Duckman::opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(flags);
-	_vm->stopCursorHoldingObject();
-	if (!(flags & 1))
-		_vm->playSoundEffect(7);
-}
-
-void ScriptOpcodes_Duckman::opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(flags);
-	ARG_UINT32(objectId);
-	ARG_UINT32(sequenceId);
-	_vm->startCursorHoldingObject(objectId, sequenceId);
-	if (!(flags & 1))
-		_vm->playSoundEffect(6);
-}
-
-void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	// NOTE This has no attached objectId or priority
-	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
-	
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._threadId);
-	
-}
-
-void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeId);
-debug("run(%08X)", specialCodeId);	
-	_vm->_specialCode->run(specialCodeId, opCall);
-debug("run(%08X) OK", specialCodeId);	
-}
-
-void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(volume);
-	ARG_UINT32(soundEffectId);
-	_vm->_soundMan->playSound(soundEffectId, volume, 0);
-}
-
-void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	_vm->_soundMan->stopSound(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(musicId);
-	// TODO _vm->playMidiMusic(musicId);
-}
-
-void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	// TODO _vm->stopMidiMusic();
-}
-
-void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(duration);
-	ARG_INT16(finalVolume);
-	// TODO _vm->fadeMidiMusic(2, finalVolume, duration, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(jumpOffs);
-	ARG_INT16(endMarker);
-	_vm->_stack->push(endMarker);
-	_vm->_stack->push(jumpOffs);
-}
-
-void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(unk1);
-	ARG_UINT32(menuId);
-	ARG_UINT32(unk2);
-	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
-	// Remove menu choices from the stack
-	do {
-		_vm->_stack->pop();
-	} while (_vm->_stack->pop() == 0);
-
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
-
-}
-
-void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game"
-
-	opCall._deltaOfs += _vm->_menuChoiceOfs;
-}
-
-void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->reset();
-	_vm->_input->activateButton(0xFFFF);
-	// TODO _vm->stopMusic();
-	// TODO _vm->_gameStates->clear();
-}
-
-void ScriptOpcodes_Duckman::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(button)
-	_vm->_input->deactivateButton(button);
-}
-
-void ScriptOpcodes_Duckman::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(button)
-	_vm->_input->activateButton(button);
-}
-
-void ScriptOpcodes_Duckman::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
-	if (value <= 63)
-		_vm->_scriptResource->_blockCounters.set(index, value);
-}
-
-void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	_vm->_scriptResource->_blockCounters.set(index, 0);
-}
-
-void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(jumpOffs);
-	int16 value = _vm->_stack->pop();
-	if (value == 0)
-		opCall._deltaOfs += jumpOffs;
-}
-
-void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->getPrevScene() == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opNot(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_stack->pop();
-	_vm->_stack->push(value != 0 ? 0 : 1);
-}
-
-void ScriptOpcodes_Duckman::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value1 = _vm->_stack->pop();
-	int16 value2 = _vm->_stack->pop();
-	_vm->_stack->push(value1 & value2);
-}
-
-void ScriptOpcodes_Duckman::opOr(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value1 = _vm->_stack->pop();
-	int16 value2 = _vm->_stack->pop();
-	_vm->_stack->push(value1 | value2);
-}
-
-void ScriptOpcodes_Duckman::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(propertyId)
-	bool value = _vm->_scriptResource->_properties.get(propertyId);
-	_vm->_stack->push(value ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	ARG_INT16(compareOp);	
-	ARG_INT16(rvalue);
-	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
-	bool compareResult = false;
-	switch (compareOp) {
-	case 1:
-		compareResult = lvalue == rvalue;
-		break;
-	case 2:
-		compareResult = lvalue != rvalue;
-		break;
-	case 3:
-		compareResult = lvalue < rvalue;
-		break;
-	case 4:
-		compareResult = lvalue > rvalue;
-		break;
-	case 5:
-		compareResult = lvalue >= rvalue;
-		break;
-	case 6:
-		compareResult = lvalue <= rvalue;
-		break;
-	}
-	_vm->_stack->push(compareResult ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(index);
-	ARG_INT16(choiceJumpOffs);
-	ARG_UINT32(sequenceId);
-	if (index && (_vm->_scriptResource->_blockCounters.getC0(index) & 0x40))
-		_vm->_dialogSys->addDialogItem(choiceJumpOffs, sequenceId);
-}
-
-void ScriptOpcodes_Duckman::opStartDialog(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(actorTypeId);
-	_vm->_dialogSys->startDialog(&_vm->_menuChoiceOfs, actorTypeId, opCall._callerThreadId);
-}
-
-void ScriptOpcodes_Duckman::opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall) {
-	opCall._deltaOfs += _vm->_menuChoiceOfs;
-}
-
-void ScriptOpcodes_Duckman::opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	if (_vm->_scriptResource->_blockCounters.getC0(index) & 0x80)
-		_vm->_scriptResource->_blockCounters.set(index, 0);
-	_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
-}
-
-void ScriptOpcodes_Duckman::opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	if (!(_vm->_scriptResource->_blockCounters.getC0(index) & 0x80))
-		_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
-}
-
-void ScriptOpcodes_Duckman::opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	_vm->_scriptResource->_blockCounters.setC0(index, 0);
-}
-
-void ScriptOpcodes_Duckman::opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);
-	_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
-}
-
-void ScriptOpcodes_Duckman::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Prints some debug text
-	debug(1, "[DBG126] %s", (char*)opCall._code);
-}
-
-void ScriptOpcodes_Duckman::opDebug127(ScriptThread *scriptThread, OpCall &opCall) {
-	// NOTE Prints some debug text
-	debug(1, "[DBG127] %s", (char*)opCall._code);
-}
-
-#if 0
-
-void ScriptOpcodes_Duckman::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(codeOffs);
-	_vm->startTempScriptThread(opCall._code + codeOffs,
-		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes_Duckman::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
-}
-
-void ScriptOpcodes_Duckman::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_threads->endTalkThreads();
-}
-
-void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
-	if (scenesCount > 0) {
-		uint32 currSceneId;
-		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
-		// TODO krnfileDump(currSceneId);
-	}
-	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
-		opCall._result = kTSTerminate;
-}
-
-void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->enterPause(opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-}
-
-void ScriptOpcodes_Duckman::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->exitScene(opCall._callerThreadId);
-	_vm->leavePause(opCall._callerThreadId);
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(namedPointId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->stopActor();
-	control->setActorPosition(pos);
-}
-
-void ScriptOpcodes_Duckman::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setSelectSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setMoveSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setDenySfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustUpSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustDnSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(maxValue);
-	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
-}
-
-void ScriptOpcodes_Duckman::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(rvalue);
-	ARG_INT16(elseJumpOffs);
-	int16 lvalue = _vm->_stack->pop();
-	if (!(lvalue <= rvalue))
-		opCall._deltaOfs += elseJumpOffs;
-}
-
-void ScriptOpcodes_Duckman::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->pop(); 
-}
-
-void ScriptOpcodes_Duckman::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_stack->peek();
-	_vm->_stack->push(value); 
-}
-
-void ScriptOpcodes_Duckman::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeModuleId);
-	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
-}
-
-void ScriptOpcodes_Duckman::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->stopActor();
-}
-
-void ScriptOpcodes_Duckman::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(usePan)
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->setActorUsePan(usePan);
-}
-
-void ScriptOpcodes_Duckman::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(codeOffs);
-	ARG_INT16(skipOffs);
-	_vm->startAbortableThread(opCall._code + codeOffs,
-		opCall._code + skipOffs, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_threads->killThread(threadId);
-}
-
-void ScriptOpcodes_Duckman::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->setSceneIdThreadId(sceneId, threadId);
-}
-
-void ScriptOpcodes_Duckman::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->push(0);
-}
-
-void ScriptOpcodes_Duckman::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(fontId);
-	_vm->setCurrFontId(fontId);
-}
-
-void ScriptOpcodes_Duckman::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(key);
-	ARG_UINT32(threadId);
-	// TODO _vm->addMenuKey(key, threadId);
-}
-
-void ScriptOpcodes_Duckman::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->_input->discardButtons(0xFFFF);
-	_vm->_prevSceneId = _vm->getCurrentScene();
-	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
-	_vm->startAnonScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-#endif
-
-} // End of namespace Illusions
diff --git a/engines/illusions/scriptopcodes_duckman.h b/engines/illusions/scriptopcodes_duckman.h
deleted file mode 100644
index f9f536e..0000000
--- a/engines/illusions/scriptopcodes_duckman.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef ILLUSIONS_SCRIPTOPCODES_DUCKMAN_H
-#define ILLUSIONS_SCRIPTOPCODES_DUCKMAN_H
-
-#include "illusions/scriptopcodes.h"
-#include "common/func.h"
-
-namespace Illusions {
-
-class IllusionsEngine_Duckman;
-class ScriptThread;
-
-class ScriptOpcodes_Duckman : public ScriptOpcodes {
-public:
-	ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm);
-	~ScriptOpcodes_Duckman();
-	void initOpcodes();
-	void freeOpcodes();
-protected:
-	IllusionsEngine_Duckman *_vm;
-
-	// Opcodes
-	
-	void opNop(ScriptThread *scriptThread, OpCall &opCall);
-	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
-	void opYield(ScriptThread *scriptThread, OpCall &opCall);
-	void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
-	void opJump(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opNotifyThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opSuspendThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
-	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
-	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
-	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
-	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
-	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
-	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
-	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
-	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opNot(ScriptThread *scriptThread, OpCall &opCall);
-	void opAnd(ScriptThread *scriptThread, OpCall &opCall);
-	void opOr(ScriptThread *scriptThread, OpCall &opCall);
-	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
-	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartDialog(ScriptThread *scriptThread, OpCall &opCall);
-	void opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall);
-	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
-	void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
-	
-#if 0	
-	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
-	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
-	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
-	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
-	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
-	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
-	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
-#endif	
-};
-
-} // End of namespace Illusions
-
-#endif // ILLUSIONS_SCRIPTOPCODES_DUCKMAN_H


Commit: 36ec0fafdb186ad55a0d6c08e38b96ef84fa60a8
    https://github.com/scummvm/scummvm/commit/36ec0fafdb186ad55a0d6c08e38b96ef84fa60a8
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Refactor the input system

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/cursor.cpp
    engines/illusions/duckman/duckman_dialog.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/input.cpp
    engines/illusions/input.h
    engines/illusions/resources/soundresource.cpp
    engines/illusions/screen.cpp
    engines/illusions/sound.cpp
    engines/illusions/threads/abortablethread.cpp
    engines/illusions/threads/causethread_duckman.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp
    engines/illusions/threads/timerthread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index bf7b0cc..0e4bae7 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -258,7 +258,7 @@ void Control::appearActor() {
 				_actor->_flags |= 0x2000;
 				_actor->_flags |= 0x4000;
 			}
-			_vm->_input->discardButtons(0xFFFF);
+			_vm->_input->discardAllEvents();
 		}
 	} else {
 		if (_objectId == 0x40004) {
@@ -500,7 +500,6 @@ void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {
 
 	if (_actor) {
 		if (_actor->_scale != 100) {
-			// scaledValue = value * scale div 100
 			collisionRect.left = collisionRect.left * _actor->_scale / 100;
 			collisionRect.top = collisionRect.top * _actor->_scale / 100;
 			collisionRect.right = collisionRect.right * _actor->_scale / 100;
@@ -520,7 +519,6 @@ void Control::getCollisionRect(Common::Rect &collisionRect) {
 	collisionRect = Common::Rect(_unkPt.x, _unkPt.y, _pt.x, _pt.y);
 	if (_actor) {
 		if (_actor->_scale != 100) {
-			// scaledValue = value * scale div 100
 			collisionRect.left = collisionRect.left * _actor->_scale / 100;
 			collisionRect.top = collisionRect.top * _actor->_scale / 100;
 			collisionRect.right = collisionRect.right * _actor->_scale / 100;
@@ -762,19 +760,12 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 		_vm->notifyThreadId(_actor->_notifyId3C);
 		_actor->_notifyId3C = callerThreadId2;
 		_actor->_pathPointIndex = 0;
-
-		if (_vm->getGameId() == kGameIdBBDOU) {
-			_vm->_input->discardButtons(0x10);
-		} else if (_vm->getGameId() == kGameIdDuckman) {
-			_vm->_input->discardButtons(0x20);
-		}
-
+		_vm->_input->discardEvent(kEventSkip);
 	}
 
 }
 
 PointArray *Control::createPath(Common::Point destPt) {
-	// TODO Implement actual pathfinding
 	PointArray *walkPoints = (_actor->_flags & 2) ? _actor->_pathWalkPoints->_points : 0;
 	PathLines *walkRects = (_actor->_flags & 0x10) ? _actor->_pathWalkRects->_rects : 0;
 	PathFinder pathFinder;
@@ -791,12 +782,12 @@ void Control::updateActorMovement(uint32 deltaTime) {
 	// TODO Move while loop to caller
 
 	static const int16 kAngleTbl[] = {60, 0, 120, 0, 60, 0, 120, 0};
-	bool again = false;
+	bool fastWalked = false;
 
 	while (1) {
 
-	if (!again && _vm->testMainActorFastWalk(this)) {
-		again = true;
+	if (!fastWalked && _vm->testMainActorFastWalk(this)) {
+		fastWalked = true;
 		disappearActor();
 		_actor->_flags |= 0x8000;
 		_actor->_seqCodeIp = 0;
@@ -921,12 +912,12 @@ void Control::updateActorMovement(uint32 deltaTime) {
 				_vm->notifyThreadId(_actor->_notifyId3C);
 				_actor->_walkCallerThreadId1 = 0;
 			}
-			again = false;
+			fastWalked = false;
 		}
 		_actor->_pathFlag50 = false;
 	}
 
-	if (!again)
+	if (!fastWalked)
 		break;
 
 	}
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 88f0fac..4b9230b 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -89,7 +89,7 @@ void BbdouCursor::enable(uint32 objectId) {
 		_vm->_camera->panEdgeFollow(objectId, 360);
 		_data._idleCtr = 0;
 	}
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 }
 
 void BbdouCursor::disable(uint32 objectId) {
@@ -354,7 +354,7 @@ void BbdouCursor::hide(uint32 objectId) {
 		_bbdou->resetItem10(objectId, &_data._item10);
 		_vm->_camera->popCameraMode();
 	}
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 5369bbb..a705484 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -331,7 +331,7 @@ void BbdouSpecialCode::resetItem10(uint32 objectId, Item10 *item10) {
 		item10->_objectIds[0] = 0;
 		item10->_objectIds[1] = 0;
 	}
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 }
 
 bool BbdouSpecialCode::testValueRange(int value) {
@@ -407,7 +407,7 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 	control3->deactivateObject();
 	
 	item10->_playSound48 = 1;
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 
 }
 
@@ -540,14 +540,14 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 		}
 		cursorData._currOverlappedObjectId = 0;
 	} else if (cursorData._currOverlappedObjectId) {
-		if (_vm->_input->pollButton(1)) {
+		if (_vm->_input->pollEvent(kEventLeftClick)) {
 			cursorData._idleCtr = 0;
 			if (runCause(cursorControl, cursorData, cursorData._item10._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId, 1)) {
 				resetItem10(cursorControl->_objectId, &cursorData._item10);
 				cursorData._currOverlappedObjectId = 0;
 				cursorControl->setActorIndexTo1();
 			}
-		} else if (_vm->_input->pollButton(2)) {
+		} else if (_vm->_input->pollEvent(kEventRightClick)) {
 			uint32 verbId;
 			cursorData._idleCtr = 0;
 			if (cursorData._holdingObjectId) {
@@ -561,10 +561,10 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 			}
 		}
 	} else {
-		if (_vm->_input->pollButton(1)) {
+		if (_vm->_input->pollEvent(kEventLeftClick)) {
 			cursorData._idleCtr = 0;
 			runCause(cursorControl, cursorData, 0x1B0002, 0, 0x40003, 0);
-		} else if (_vm->_input->pollButton(4)) {
+		} else if (_vm->_input->pollEvent(kEventInventory)) {
 			cursorData._idleCtr = 0;
 			if (cursorData._item10._field58 <= 1)
 				runCause(cursorControl, cursorData, cursorData._holdingObjectId != 0 ? 0x1B000B : 0x1B0004, 0, 0x40003, 0);
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 339b61d..112aa3f 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -216,6 +216,8 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
 
+    initInput();
+
 	initUpdateFunctions();
 
 	_fader = 0;
@@ -283,6 +285,28 @@ bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
 		*/
 }
 
+void IllusionsEngine_BBDOU::initInput() {
+	_input->setInputEvent(kEventLeftClick, 0x01)
+		.addMouseButton(MOUSE_LEFT_BUTTON)
+		.addKey(Common::KEYCODE_RETURN);
+	_input->setInputEvent(kEventRightClick, 0x02)
+		.addMouseButton(MOUSE_RIGHT_BUTTON);
+	_input->setInputEvent(kEventInventory, 0x04)
+		.addMouseButton(MOUSE_RIGHT_BUTTON)
+		.addKey(Common::KEYCODE_TAB);
+	_input->setInputEvent(kEventAbort, 0x08)
+		.addKey(Common::KEYCODE_ESCAPE);
+	_input->setInputEvent(kEventSkip, 0x10)
+		.addKey(Common::KEYCODE_SPACE);
+	_input->setInputEvent(kEventF1, 0x20)
+		.addKey(Common::KEYCODE_F1);
+	_input->setInputEvent(kEventUp, 0x40)
+		.addKey(Common::KEYCODE_UP);
+	_input->setInputEvent(kEventDown, 0x80)
+		.addMouseButton(MOUSE_RIGHT_BUTTON)
+		.addKey(Common::KEYCODE_DOWN);
+}
+
 #define UPDATEFUNCTION(priority, tag, callback) \
 	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
 		(this, &IllusionsEngine_BBDOU::callback));
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index fccfb59..5317809 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -98,6 +98,8 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
+	void initInput();
+
 	void initUpdateFunctions();
 	int updateScript(uint flags);
 
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 2f1c705..efdc289 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -263,7 +263,7 @@ void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCa
 	}
 	
 	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->_prevSceneId = _vm->getCurrentScene();
 	_vm->exitScene(opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
@@ -277,7 +277,7 @@ void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
 	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->enterPause(opCall._callerThreadId);
 	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
 	_vm->enterScene(sceneId, opCall._callerThreadId);
@@ -288,7 +288,7 @@ void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &
 
 void ScriptOpcodes_BBDOU::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->exitScene(opCall._callerThreadId);
 	_vm->leavePause(opCall._callerThreadId);
 	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
@@ -298,7 +298,7 @@ void ScriptOpcodes_BBDOU::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->enterPause(opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
 }
@@ -768,7 +768,7 @@ void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &o
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
 	// NOTE Skipped checking for stalled resources
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->_prevSceneId = _vm->getCurrentScene();
 	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
index b591ae6..962ea93 100644
--- a/engines/illusions/cursor.cpp
+++ b/engines/illusions/cursor.cpp
@@ -67,7 +67,7 @@ void Cursor::show() {
 			_control->_actor->_flags |= 0x2000;
 			_control->_actor->_flags |= 0x4000;
 		}
-		_vm->_input->discardButtons(0xFFFF);
+		_vm->_input->discardAllEvents();
 	}
 }
 
diff --git a/engines/illusions/duckman/duckman_dialog.cpp b/engines/illusions/duckman/duckman_dialog.cpp
index b30a2be..3e42955 100644
--- a/engines/illusions/duckman/duckman_dialog.cpp
+++ b/engines/illusions/duckman/duckman_dialog.cpp
@@ -156,7 +156,7 @@ void DuckmanDialogSystem::updateDialogState() {
 		_vm->setCursorActorIndex(6, 1, 0);
 	}
 
-	if (_vm->_input->pollButton(1)) {
+	if (_vm->_input->pollEvent(kEventLeftClick)) {
 		if (_vm->_cursor._currOverlappedControl) {
 			_vm->playSoundEffect(9);
 			*_vm->_cursor._op113_choiceOfsPtr = _vm->_cursor._currOverlappedControl->_actor->_choiceJumpOffs;
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 52567f2..b861d73 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -113,6 +113,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	
 	_dialogSys = new DuckmanDialogSystem(this);
 
+    initInput();
+
 	initUpdateFunctions();
 
 	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
@@ -203,6 +205,29 @@ bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
 		*/
 }
 
+void IllusionsEngine_Duckman::initInput() {
+	// TODO Check if these are correct...
+	_input->setInputEvent(kEventLeftClick, 0x01)
+		.addMouseButton(MOUSE_LEFT_BUTTON)
+		.addKey(Common::KEYCODE_RETURN);
+	_input->setInputEvent(kEventRightClick, 0x02)
+		.addMouseButton(MOUSE_RIGHT_BUTTON)
+		.addKey(Common::KEYCODE_BACKSPACE);
+	// 0x04 is unused
+	_input->setInputEvent(kEventInventory, 0x08)
+		.addKey(Common::KEYCODE_TAB);
+	_input->setInputEvent(kEventAbort, 0x10)
+		.addKey(Common::KEYCODE_ESCAPE);
+	_input->setInputEvent(kEventSkip, 0x20)
+		.addMouseButton(MOUSE_LEFT_BUTTON)
+		.addKey(Common::KEYCODE_SPACE);
+	_input->setInputEvent(kEventUp, 0x40)
+		.addKey(Common::KEYCODE_UP);
+	_input->setInputEvent(kEventDown, 0x80)
+		.addMouseButton(MOUSE_RIGHT_BUTTON)
+		.addKey(Common::KEYCODE_DOWN);
+}
+
 #define UPDATEFUNCTION(priority, tag, callback) \
 	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
 		(this, &IllusionsEngine_Duckman::callback));
@@ -301,7 +326,7 @@ void IllusionsEngine_Duckman::loadSpecialCode(uint32 resId) {
 }
 
 void IllusionsEngine_Duckman::unloadSpecialCode(uint32 resId) {
-	//TODO?
+	delete _specialCode;
 }
 
 void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
@@ -315,7 +340,7 @@ void IllusionsEngine_Duckman::notifyThreadId(uint32 &threadId) {
 bool IllusionsEngine_Duckman::testMainActorFastWalk(Control *control) {
 	return
 		control->_objectId == _scriptResource->getMainActorObjectId() &&
-		_input->pollButton(0x20);
+		_input->pollEvent(kEventSkip);
 }
 
 bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
@@ -742,7 +767,7 @@ void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
 }
 
 void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
-	// TODO
+	// TODO?
 }
 
 void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
@@ -866,7 +891,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		_cursor._currOverlappedControl = 0;
 	}
 
-	if (_input->pollButton(1)) {
+	if (_input->pollEvent(kEventLeftClick)) {
 		if (_cursor._currOverlappedControl) {
 			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
 		} else {
@@ -877,7 +902,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 			else
 				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
 		}
-	} else if (_input->pollButton(2)) {
+	} else if (_input->pollEvent(kEventRightClick)) {
 		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
 			int newActorIndex = getCursorActorIndex();
 			if (newActorIndex != _cursor._actorIndex) {
@@ -889,7 +914,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 				startCursorSequence();
 			}
 		}
-	} else if (_input->pollButton(8)) {
+	} else if (_input->pollEvent(kEventInventory)) {
 		if (_cursor._field14[0] == 1) {
 			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
 		} else if (_cursor._field14[1] == 1) {
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 7638c3a..5fda0d6 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -100,6 +100,8 @@ public:
 
 	ScreenShaker *_screenShaker;
 	
+	void initInput();
+	
 	void initUpdateFunctions();
 	int updateScript(uint flags);
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 0e79774..3f12209 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -250,7 +250,7 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
@@ -261,15 +261,16 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 //static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
 //static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
-static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
+//static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
 //static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
 //static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
+//static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	
 	debug("changeScene(%08X, %08X)", sceneId, threadId);
 	
@@ -291,7 +292,7 @@ void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
 	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
 	_vm->enterScene(sceneId, threadId);
@@ -299,7 +300,7 @@ void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall
 }
 
 void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	if (_vm->_scriptResource->_properties.get(0x000E0027)) {
 		// TODO _vm->startScriptThread2(0x10002, 0x20001, 0);
 		opCall._result = kTSTerminate;
@@ -314,13 +315,13 @@ void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall
 void ScriptOpcodes_Duckman::opEnterScene24(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
 	_vm->enterScene(sceneId, 0);
 }
 
 void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
 	_vm->exitScene();
 	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
@@ -797,7 +798,7 @@ void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opC
 void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->enterPause(opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
 }
@@ -945,7 +946,7 @@ void ScriptOpcodes_Duckman::opChangeSceneAll(ScriptThread *scriptThread, OpCall
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
-	_vm->_input->discardButtons(0xFFFF);
+	_vm->_input->discardAllEvents();
 	_vm->_prevSceneId = _vm->getCurrentScene();
 	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index e9bf25f..1251aab 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -24,6 +24,59 @@
 
 namespace Illusions {
 
+// KeyMap
+
+void KeyMap::addKey(Common::KeyCode key) {
+	add(key, MOUSE_NONE);
+}
+
+void KeyMap::addMouseButton(int mouseButton) {
+	add(Common::KEYCODE_INVALID, mouseButton);
+}
+
+void KeyMap::add(Common::KeyCode key, int mouseButton) {
+	KeyMapping keyMapping;
+	keyMapping._key = key;
+	keyMapping._mouseButton = mouseButton;
+	keyMapping._down = false;
+	push_back(keyMapping);
+}
+
+// InputEvent
+
+InputEvent& InputEvent::setBitMask(uint bitMask) {
+	_bitMask = bitMask;
+	return *this;
+}
+
+InputEvent& InputEvent::addKey(Common::KeyCode key) {
+	_keyMap.addKey(key);
+	return *this;
+}
+
+InputEvent& InputEvent::addMouseButton(int mouseButton) {
+	_keyMap.addMouseButton(mouseButton);
+	return *this;
+}
+
+byte InputEvent::handle(Common::KeyCode key, int mouseButton, bool down) {
+	byte newKeys = 0;
+	for (KeyMap::iterator it = _keyMap.begin(); it != _keyMap.end(); ++it) {
+		KeyMapping &keyMapping = *it;
+		if ((keyMapping._key != Common::KEYCODE_INVALID && keyMapping._key == key) ||
+			(keyMapping._mouseButton != MOUSE_NONE && keyMapping._mouseButton == mouseButton)) {
+			if (down && !keyMapping._down) {
+				newKeys |= _bitMask;
+				keyMapping._down = true;
+			} else if (!down)
+				keyMapping._down = false;
+		}
+	}
+	return newKeys;
+}
+
+// Input
+
 Input::Input() {
 	_buttonStates = 0;
 	_newButtons = 0;
@@ -34,7 +87,6 @@ Input::Input() {
 	_cursorPos.y = 0;
 	_prevCursorPos.x = 0;
 	_prevCursorPos.y = 0;
-	initKeys();
 }
 
 void Input::processEvent(Common::Event event) {
@@ -51,53 +103,41 @@ void Input::processEvent(Common::Event event) {
 		_cursorPos.y = event.mouse.y;
 		break;
 	case Common::EVENT_LBUTTONDOWN:
-		handleMouseButton(MOUSE_BUTTON0, true);
+		handleMouseButton(MOUSE_LEFT_BUTTON, true);
 		break;
 	case Common::EVENT_LBUTTONUP:
-		handleMouseButton(MOUSE_BUTTON0, false);
+		handleMouseButton(MOUSE_LEFT_BUTTON, false);
 		break;
 	case Common::EVENT_RBUTTONDOWN:
-		handleMouseButton(MOUSE_BUTTON1, true);
+		handleMouseButton(MOUSE_RIGHT_BUTTON, true);
 		break;
 	case Common::EVENT_RBUTTONUP:
-		handleMouseButton(MOUSE_BUTTON1, false);
+		handleMouseButton(MOUSE_RIGHT_BUTTON, false);
 		break;
 	default:
 		break;
 	}
 }
 
-bool Input::pollButton(uint buttons) {
-	if (lookButtonStates(buttons)) {
-		_buttonStates &= ~buttons;
-		return true;
-	}
-	return false;
+bool Input::pollEvent(uint evt) {
+	return pollButton(_inputEvents[evt].getBitMask());
 }
 
-bool Input::lookButtonStates(uint buttons) {
-	return (buttons & (_buttonStates & _enabledButtons)) != 0;
+void Input::discardEvent(uint evt) {
+	discardButtons(_inputEvents[evt].getBitMask());
 }
 
-bool Input::lookNewButtons(uint buttons) {
-	return (buttons & (_newButtons & _enabledButtons)) != 0;
+void Input::discardAllEvents() {
+	discardButtons(0xFFFF);
 }
 
-void Input::setButtonState(uint buttons) {
-	_buttonStates |= _enabledButtons & buttons;
+void Input::activateButton(uint bitMask) {
+	_enabledButtons |= bitMask;
+	_buttonStates &= ~bitMask;
 }
 
-void Input::discardButtons(uint buttons) {
-	_buttonStates &= ~buttons;
-}
-
-void Input::activateButton(uint buttons) {
-	_enabledButtons |= buttons;
-	_buttonStates &= ~buttons;
-}
-
-void Input::deactivateButton(uint buttons) {
-	_enabledButtons &= ~buttons;
+void Input::deactivateButton(uint bitMask) {
+	_enabledButtons &= ~bitMask;
 }
 
 Common::Point Input::getCursorPosition() {
@@ -116,43 +156,14 @@ Common::Point Input::getCursorDelta() {
 	return deltaPos;
 }
 
-void Input::initKeys() {
-	// NOTE Skipped debugging keys of the original engine, not sure if used
-	// TODO Move this to the engine class and tidy up methods (one for mouse buttons, one for keys)
-	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON0, 0x01);
-	addKeyMapping(Common::KEYCODE_RETURN, MOUSE_NONE, 0x01);
-	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x02);
-	addKeyMapping(Common::KEYCODE_TAB, MOUSE_NONE, 0x04);
-	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x04);
-	addKeyMapping(Common::KEYCODE_ESCAPE, MOUSE_NONE, 0x08);
-	addKeyMapping(Common::KEYCODE_SPACE, MOUSE_NONE, 0x10);
-	addKeyMapping(Common::KEYCODE_F1, MOUSE_NONE, 0x20);
-	addKeyMapping(Common::KEYCODE_UP, MOUSE_NONE, 0x40);
-	addKeyMapping(Common::KEYCODE_DOWN, MOUSE_NONE, 0x80);
-	addKeyMapping(Common::KEYCODE_INVALID, MOUSE_BUTTON1, 0x80);
-}
-
-void Input::addKeyMapping(Common::KeyCode key, int mouseButton, uint bitMask) {
-	KeyMapping keyMapping;
-	keyMapping._key = key;
-	keyMapping._mouseButton = mouseButton;
-	keyMapping._bitMask = bitMask;
-	keyMapping._down = false;
-	_keyMap.push_back(keyMapping);
+InputEvent& Input::setInputEvent(uint evt, uint bitMask) {
+	InputEvent& inputEvent = _inputEvents[evt];
+	return inputEvent.setBitMask(bitMask);
 }
 
 void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
-	for (KeyMap::iterator it = _keyMap.begin(); it != _keyMap.end(); ++it) {
-		KeyMapping &keyMapping = *it;
-		if ((keyMapping._key != Common::KEYCODE_INVALID && keyMapping._key == key) ||
-			(keyMapping._mouseButton != MOUSE_NONE && keyMapping._mouseButton == mouseButton)) {
-			if (down && !keyMapping._down) {
-				_newKeys |= keyMapping._bitMask;
-				keyMapping._down = true;
-			} else if (!down)
-				keyMapping._down = false;
-		}
-	}
+	for (uint i = 0; i < kEventMax; ++i)
+		_newKeys |= _inputEvents[i].handle(key, mouseButton, down);
 	uint prevButtonStates = _buttonStates;
 	_buttonStates |= _newKeys;
 	_newKeys = 0;
@@ -167,4 +178,28 @@ void Input::handleMouseButton(int mouseButton, bool down) {
 	handleKey(Common::KEYCODE_INVALID, mouseButton, down);
 }
 
+bool Input::pollButton(uint bitMask) {
+	if (lookButtonStates(bitMask)) {
+		_buttonStates &= ~bitMask;
+		return true;
+	}
+	return false;
+}
+
+bool Input::lookButtonStates(uint bitMask) {
+	return (bitMask & (_buttonStates & _enabledButtons)) != 0;
+}
+
+bool Input::lookNewButtons(uint bitMask) {
+	return (bitMask & (_newButtons & _enabledButtons)) != 0;
+}
+
+void Input::setButtonState(uint bitMask) {
+	_buttonStates |= _enabledButtons & bitMask;
+}
+
+void Input::discardButtons(uint bitMask) {
+	_buttonStates &= ~bitMask;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index f3c9654..f203a64 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -31,43 +31,75 @@
 namespace Illusions {
 
 enum {
-	MOUSE_NONE    = 0,
-	MOUSE_BUTTON0 = 1,
-	MOUSE_BUTTON1 = 2
+	MOUSE_NONE         = 0,
+	MOUSE_LEFT_BUTTON  = 1,
+	MOUSE_RIGHT_BUTTON = 2
+};
+
+enum {
+	kEventLeftClick    = 0,
+	kEventRightClick   = 1,
+	kEventInventory    = 2,
+	kEventAbort        = 3,
+	kEventSkip         = 4,
+	kEventF1           = 5,
+	kEventUp           = 6,
+	kEventDown         = 7,
+	kEventMax
 };
 
 struct KeyMapping {
 	Common::KeyCode _key;
 	int _mouseButton;
-	uint _bitMask;
 	bool _down;
 };
 
+class KeyMap : public Common::Array<KeyMapping> {
+public:
+	void addKey(Common::KeyCode key);
+	void addMouseButton(int mouseButton);
+protected:
+	void add(Common::KeyCode key, int mouseButton);
+};
+
+class InputEvent {
+public:
+	InputEvent& setBitMask(uint bitMask);
+	InputEvent& addKey(Common::KeyCode key);
+	InputEvent& addMouseButton(int mouseButton);
+	byte handle(Common::KeyCode key, int mouseButton, bool down);
+	uint getBitMask() const { return _bitMask; }
+protected:
+	uint _bitMask;
+	KeyMap _keyMap;
+};
+
 class Input {
 public:
 	Input();
 	void processEvent(Common::Event event);
-	bool pollButton(uint buttons);
-	bool lookButtonStates(uint buttons);
-	bool lookNewButtons(uint buttons);
-	void setButtonState(uint buttons);
-	void discardButtons(uint buttons);
-	void activateButton(uint buttons);
-	void deactivateButton(uint buttons);
+	bool pollEvent(uint evt);
+	void discardEvent(uint evt);
+	void discardAllEvents();
+	void activateButton(uint bitMask);
+	void deactivateButton(uint bitMask);
 	Common::Point getCursorPosition();
 	void setCursorPosition(Common::Point mousePos);
 	Common::Point getCursorDelta();
+	InputEvent& setInputEvent(uint evt, uint bitMask);
 protected:
-	typedef Common::Array<KeyMapping> KeyMap;
 	uint _buttonStates, _newButtons, _buttonsDown;
 	uint _enabledButtons;
 	uint _newKeys;
 	Common::Point _cursorPos, _prevCursorPos;
-	KeyMap _keyMap;
-	void initKeys();
-	void addKeyMapping(Common::KeyCode key, int mouseButton, uint bitMask);
+	InputEvent _inputEvents[kEventMax];
 	void handleKey(Common::KeyCode key, int mouseButton, bool down);
 	void handleMouseButton(int mouseButton, bool down);
+	bool pollButton(uint bitMask);
+	void discardButtons(uint bitMask);
+	bool lookButtonStates(uint bitMask);
+	bool lookNewButtons(uint bitMask);
+	void setButtonState(uint bitMask);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/resources/soundresource.cpp b/engines/illusions/resources/soundresource.cpp
index 35017e3..73ec163 100644
--- a/engines/illusions/resources/soundresource.cpp
+++ b/engines/illusions/resources/soundresource.cpp
@@ -85,7 +85,7 @@ void SoundGroupResource::load(byte *data, uint32 dataSize) {
 // SoundGroupInstance
 
 SoundGroupInstance::SoundGroupInstance(IllusionsEngine *vm)
-	: _vm(vm) {
+	: _vm(vm), _soundGroupResource(0) {
 }
 
 void SoundGroupInstance::load(Resource *resource) {
@@ -100,6 +100,7 @@ void SoundGroupInstance::load(Resource *resource) {
 
 void SoundGroupInstance::unload() {
 	_vm->_soundMan->unloadSounds(_resId);
+	delete _soundGroupResource;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 9c603a8..955317b 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -782,7 +782,8 @@ static uint16 average(const uint16 a, const uint16 b) {
 	byte r1, g1, b1, r2, g2, b2;
 	g_system->getScreenFormat().colorToRGB(a, r1, g1, b1);
 	g_system->getScreenFormat().colorToRGB(b, r2, g2, b2);
-	return g_system->getScreenFormat().RGBToColor((r1 + r1 + r2) / 3, (g1 + g1 + g2) / 3, (b1 + b1 + b2) / 3);
+//	return g_system->getScreenFormat().RGBToColor((r1 + r1 + r2) / 3, (g1 + g1 + g2) / 3, (b1 + b1 + b2) / 3);
+	return g_system->getScreenFormat().RGBToColor((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2);
 }
 
 void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 3a0f5ef..5eebf00 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -147,6 +147,7 @@ void Sound::load() {
 }
 
 void Sound::unload() {
+	debug("Sound::unload() %08X", _soundEffectId);
 	stop();
 	delete _stream;
 	_stream = 0;
diff --git a/engines/illusions/threads/abortablethread.cpp b/engines/illusions/threads/abortablethread.cpp
index 348bc9c..b06debe 100644
--- a/engines/illusions/threads/abortablethread.cpp
+++ b/engines/illusions/threads/abortablethread.cpp
@@ -35,13 +35,13 @@ AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 ca
 	_scriptCodeIp(scriptCodeIp), _status(1) {
 	_type = kTTAbortableThread;
 	_tag = _vm->getCurrentScene();
-	_vm->_input->discardButtons(8);
+	_vm->_input->discardEvent(kEventAbort);
 }
 
 int AbortableThread::onUpdate() {
 	if (_status != 1 || _pauseCtr < 0)
 		return kTSTerminate;
-	if (_vm->_input->pollButton(8)) {
+	if (_vm->_input->pollEvent(kEventAbort)) {
 		_vm->_threads->killThread(_scriptThreadId);
 		++_pauseCtr;
 		_vm->startTempScriptThread(_scriptCodeIp, _threadId, 0, 0, 0);
diff --git a/engines/illusions/threads/causethread_duckman.cpp b/engines/illusions/threads/causethread_duckman.cpp
index df5b1fa..ee5fe75 100644
--- a/engines/illusions/threads/causethread_duckman.cpp
+++ b/engines/illusions/threads/causethread_duckman.cpp
@@ -41,14 +41,14 @@ int CauseThread_Duckman::onUpdate() {
 		if (_vm->getCurrentScene() == _tag) {
 			Control *cursorCursor = _vm->getObjectControl(0x40004);
 			cursorCursor->appearActor();
-			_vm->_input->discardButtons(1);
+			_vm->_input->discardEvent(kEventLeftClick);
 		}
 		return kTSTerminate;
 	} else {
 		_tag = _vm->getCurrentScene();
 		Control *cursorCursor = _vm->getObjectControl(0x40004);
 		cursorCursor->disappearActor();
-		_vm->_input->discardButtons(1);
+		_vm->_input->discardEvent(kEventLeftClick);
 		_vm->startScriptThread(_triggerThreadId, _threadId);
 		_flag = true;
 		return kTSSuspend;
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 54bc207..4fbb5a2 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -151,7 +151,7 @@ int TalkThread::onUpdate() {
 			}
 			_vm->_soundMan->startVoice(255, panX);
 		}
-		_vm->_input->discardButtons(0x10);
+		_vm->_input->discardEvent(kEventSkip);
 		_status = 6;
 		return kTSYield;
 
@@ -162,7 +162,7 @@ int TalkThread::onUpdate() {
 			// TODO _vm->removeText();
 			if (_entryText && *_entryText) {
 				refreshText();
-				_vm->_input->discardButtons(0x10);
+				_vm->_input->discardEvent(kEventSkip);
 			} else {
 				_flags |= 8;
 			}
@@ -178,7 +178,7 @@ int TalkThread::onUpdate() {
 			}
 			_flags |= 2;
 		}
-		if (_objectId && _vm->_input->pollButton(0x10)) {
+		if (_objectId && _vm->_input->pollEvent(kEventSkip)) {
 			if (!(_flags & 8)) {
 				// TODO _vm->removeText();
 				if (_entryText && *_entryText)
@@ -205,7 +205,7 @@ int TalkThread::onUpdate() {
 			}
 		}
 		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
-			_vm->_input->discardButtons(0x10);
+			_vm->_input->discardEvent(kEventSkip);
 			_status = 7;
 			return kTSTerminate;
 		}
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 20df1e4..f47dd7d 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -133,7 +133,7 @@ int TalkThread_Duckman::onUpdate() {
 			}
 			_vm->_soundMan->startVoice(255, panX);
 		}
-		_vm->_input->discardButtons(0x20);
+		_vm->_input->discardEvent(kEventSkip);
 		_status = 5;
 		return kTSYield;
 
@@ -144,7 +144,7 @@ int TalkThread_Duckman::onUpdate() {
 			_vm->_screenText->removeText();
 			if (_entryText && *_entryText) {
 				refreshText();
-				_vm->_input->discardButtons(0x20);
+				_vm->_input->discardEvent(kEventSkip);
 			} else {
 				_flags |= 8;
 			}
@@ -157,7 +157,7 @@ int TalkThread_Duckman::onUpdate() {
 				_flags |= 2;
 			}
 		}
-		if (_objectId && _vm->_input->pollButton(0x20)) {
+		if (_objectId && _vm->_input->pollEvent(kEventSkip)) {
 			if (!(_flags & 8)) {
 				_vm->_screenText->removeText();
 				if (_entryText && *_entryText)
@@ -179,7 +179,7 @@ int TalkThread_Duckman::onUpdate() {
 			}
 		}
 		if ((_flags & 8) && (_flags & 2) && (_flags & 4)) {
-			_vm->_input->discardButtons(0x20);
+			_vm->_input->discardEvent(kEventSkip);
 			return kTSTerminate;
 		}
 		return kTSYield;
diff --git a/engines/illusions/threads/timerthread.cpp b/engines/illusions/threads/timerthread.cpp
index 714c719..5842098 100644
--- a/engines/illusions/threads/timerthread.cpp
+++ b/engines/illusions/threads/timerthread.cpp
@@ -46,7 +46,7 @@ TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThr
 
 int TimerThread::onUpdate() {
 	if (isTimerExpired(_startTime, _endTime) ||
-		(_isAbortable && _vm->_input->pollButton(8)))
+		(_isAbortable && _vm->_input->pollEvent(kEventAbort)))
 		return kTSTerminate;
 	return kTSYield;
 }


Commit: 9d98f92298c7e0c494b0ae6455a5f0183080db70
    https://github.com/scummvm/scummvm/commit/9d98f92298c7e0c494b0ae6455a5f0183080db70
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add text drawing to BBDOU

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/resources/fontresource.cpp
    engines/illusions/resources/fontresource.h
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/screentext.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index a705484..a7dadc4 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -50,7 +50,7 @@ static const Struct10 kStruct10s[] = {
 	{0x1B000C,       0,       0,       0},
 };
 
-CauseThread::CauseThread(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
+CauseThread_BBDOU::CauseThread_BBDOU(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
 	BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId, uint32 verbId,
 	uint32 objectId2, uint32 objectId)
 	: Thread(vm, threadId, callingThreadId, 0), _bbdou(bbdou), _cursorObjectId(cursorObjectId),
@@ -58,12 +58,12 @@ CauseThread::CauseThread(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 call
 	_type = kTTSpecialThread;
 }
 		
-void CauseThread::onNotify() {
+void CauseThread_BBDOU::onNotify() {
 	_bbdou->_cursor->_data._causeThreadId1 = 0;
 	terminate();
 }
 
-void CauseThread::onTerminated() {
+void CauseThread_BBDOU::onTerminated() {
 	_bbdou->_cursor->_data._causeThreadId1 = 0;
 	_bbdou->_cursor->enable(_cursorObjectId);
 }
@@ -260,7 +260,6 @@ void BbdouSpecialCode::spcRemoveInventoryItem(OpCall &opCall) {
 void BbdouSpecialCode::spcHasInventoryItem(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_vm->_stack->push(_inventory->hasInventoryItem(objectId) ? 1 : 0);
-debug("_inventory->hasInventoryItem(%08X) = %d", objectId, _inventory->hasInventoryItem(objectId));	
 }
 
 void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
@@ -683,7 +682,7 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 uint32 BbdouSpecialCode::startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
 	uint32 tempThreadId = _vm->newTempThreadId();
 	debug(3, "Starting cause thread %08X...", tempThreadId);
-	CauseThread *causeThread = new CauseThread(_vm, tempThreadId, 0, this,
+	CauseThread_BBDOU *causeThread = new CauseThread_BBDOU(_vm, tempThreadId, 0, this,
 		cursorObjectId, sceneId, verbId, objectId2, objectId);
 	_vm->_threads->startThread(causeThread);
 	causeThread->suspend();
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 037d475..2462c2b 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -47,9 +47,9 @@ struct Struct10 {
 	uint32 _sequenceId3;
 };
 
-class CauseThread : public Thread {
+class CauseThread_BBDOU : public Thread {
 public:
-	CauseThread(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
+	CauseThread_BBDOU(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
 		BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId,
 		uint32 verbId, uint32 objectId2, uint32 objectId);
 	virtual void onNotify();
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 112aa3f..9aeb080 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -216,6 +216,8 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
 
+	_screen->setColorKey1(0xF81F);
+
     initInput();
 
 	initUpdateFunctions();
@@ -286,6 +288,7 @@ bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
 }
 
 void IllusionsEngine_BBDOU::initInput() {
+	// TODO Check if these are correct...
 	_input->setInputEvent(kEventLeftClick, 0x01)
 		.addMouseButton(MOUSE_LEFT_BUTTON)
 		.addKey(Common::KEYCODE_RETURN);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index b861d73..d30f17b 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -113,6 +113,8 @@ Common::Error IllusionsEngine_Duckman::run() {
 	
 	_dialogSys = new DuckmanDialogSystem(this);
 
+	_screen->setColorKey1(0);
+
     initInput();
 
 	initUpdateFunctions();
diff --git a/engines/illusions/resources/fontresource.cpp b/engines/illusions/resources/fontresource.cpp
index becc6cf..678e555 100644
--- a/engines/illusions/resources/fontresource.cpp
+++ b/engines/illusions/resources/fontresource.cpp
@@ -92,7 +92,7 @@ void FontResource::load(Resource *resource) {
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
 	_totalSize = stream.readUint32LE();
 	_charHeight = stream.readUint16LE();
-	_field_6 = stream.readUint16LE();
+	_defaultChar = stream.readUint16LE();
 	_colorIndex = stream.readUint16LE();
 	_lineIncr = stream.readUint16LE();
 	_widthC = stream.readUint16LE();
@@ -103,8 +103,8 @@ void FontResource::load(Resource *resource) {
 		stream.seek(charRangesOffs + i * 8);
 		_charRanges[i].load(data, stream);
 	}
-	debug(2, "FontResource::load() _charHeight: %d; _field_6: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
-		_charHeight, _field_6, _colorIndex, _lineIncr, _widthC, _charRangesCount);
+	debug(2, "FontResource::load() _charHeight: %d; _defaultChar: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
+		_charHeight, _defaultChar, _colorIndex, _lineIncr, _widthC, _charRangesCount);
 }
 
 CharInfo *FontResource::getCharInfo(uint16 c) {
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
index 8ea059f..579277c 100644
--- a/engines/illusions/resources/fontresource.h
+++ b/engines/illusions/resources/fontresource.h
@@ -69,7 +69,7 @@ public:
 public:
 	uint32 _totalSize;
 	int16 _charHeight;
-	int16 _field_6;
+	int16 _defaultChar;
 	int16 _colorIndex;
 	int16 _lineIncr;
 	int16 _widthC;
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 955317b..b9506f6 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -217,11 +217,10 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 // Screen
 
 Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
-	: _vm(vm), _colorKey2(0) {
+	: _vm(vm), _colorKey1(0), _colorKey2(0) {
 	_displayOn = true;
 	_decompressQueue = new SpriteDecompressQueue(this);
 	_drawQueue = new SpriteDrawQueue(this);
-	_colorKey1 = 0xF800 | 0x1F;
 	if (bpp == 8) {
 		initGraphics(width, height, false);
 	} else {
@@ -274,10 +273,6 @@ void Screen::setScreenOffset(Common::Point offsPt) {
 	}
 }
 
-uint16 Screen::getColorKey2() {
-	return _colorKey2;
-}
-
 void Screen::updateSprites() {
 	_decompressQueue->decompressAll();
 	// NOTE Skipped doShiftBrightness and related as it seems to be unused
@@ -475,11 +470,24 @@ void Screen::buildColorTransTbl() {
 }
 
 void Screen::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
+	switch (_backSurface->format.bytesPerPixel) {
+	case 1:
+		drawText8(font, surface, x, y, text, count);
+		break;
+	case 2:
+		drawText16(font, surface, x, y, text, count);
+		break;
+	default:
+		break;
+	}
+}
+
+void Screen::drawText8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
 	for (uint i = 0; i < count; ++i)
-		x += font->_widthC + drawChar(font, surface, x, y, *text++);
+		x += font->_widthC + drawChar8(font, surface, x, y, *text++);
 }
 
-int16 Screen::drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+int16 Screen::drawChar8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
 	const CharInfo *charInfo = font->getCharInfo(c);
 	const int16 charWidth = charInfo->_width;
 	byte *dst = (byte*)surface->getBasePtr(x, y);
@@ -494,6 +502,27 @@ int16 Screen::drawChar(FontResource *font, Graphics::Surface *surface, int16 x,
 	return charWidth;
 }
 
+void Screen::drawText16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
+	for (uint i = 0; i < count; ++i)
+		x += font->_widthC + drawChar16(font, surface, x, y, *text++);
+}
+
+int16 Screen::drawChar16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+	const CharInfo *charInfo = font->getCharInfo(c);
+	const int16 charWidth = charInfo->_width;
+	byte *pixels = charInfo->_pixels;
+	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
+		byte *dst = (byte*)surface->getBasePtr(x, y + yc);
+		for (int16 xc = 0; xc < charWidth; ++xc) {
+			if (pixels[xc])
+				WRITE_LE_UINT16(dst, convertFontColor(pixels[xc]));
+			dst += 2;
+		}
+		pixels += charWidth;
+	}
+	return charWidth;
+}
+
 void Screen::setSystemPalette(byte *palette) {
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 }
@@ -843,4 +872,21 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co
 
 }
 
+uint16 Screen::convertFontColor(byte color) {
+	if (color) {
+		byte r, g, b;
+		if (color == 204) {
+			r = 50;
+			g = 50;
+			b = 180;
+		} else {
+			r = 256 - color;
+			g = 256 - color;
+			b = 256 - color;
+		}
+		return g_system->getScreenFormat().RGBToColor(r, g, b);
+	}
+	return _colorKey1;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 1eef207..bdb7ea0 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -122,7 +122,6 @@ public:
 	bool isDisplayOn();
 	void setDisplayOn(bool isOn);
 	void setScreenOffset(Common::Point offsPt);
-	uint16 getColorKey2();
 	void updateSprites();
 	void clearScreenOffsetAreas();
 	void decompressSprite(SpriteDecompressQueueItem *item);
@@ -135,7 +134,9 @@ public:
 	void updateFaderPalette();
 	void setFader(int newValue, int firstIndex, int lastIndex);
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
-	int16 drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+	uint16 getColorKey1() const { return _colorKey1; }
+	void setColorKey1(uint16 colorKey) { _colorKey1 = colorKey; }
+	uint16 getColorKey2() const { return _colorKey2; }
 	int16 getScreenWidth() const { return _backSurface->w; }
 	int16 getScreenHeight() const { return _backSurface->h; }
 public:
@@ -161,6 +162,12 @@ public:
 	void setSystemPalette(byte *palette);
 	void buildColorTransTbl();
 
+	void drawText8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
+	int16 drawChar8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+
+	void drawText16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
+	int16 drawChar16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+
 	void decompressSprite8(SpriteDecompressQueueItem *item);
 	void drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
 	void drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
@@ -172,6 +179,8 @@ public:
 	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
 	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
 	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+	
+	uint16 convertFontColor(byte color);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 018decb..8c31705 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -56,15 +56,28 @@ void ScreenText::updateTextInfoPosition(Common::Point position) {
 }
 
 void ScreenText::clipTextInfoPosition(Common::Point &position) {
-	// TODO Set min/max for BBDOU
-	if (position.x < 2)
-		position.x = 2;
-	else if (position.x + _dimensions._width > 318)
-		position.x = 318 - _dimensions._width;
-	if (position.y < 2)
-		position.y = 2;
-	else if (position.y + _dimensions._height > 198)
-		position.y = 198 - _dimensions._height;
+	// TODO Move values outside
+	if (_vm->getGameId() == kGameIdBBDOU) {
+		// BBDOU
+		if (position.x < 2)
+			position.x = 2;
+		else if (position.x + _dimensions._width > 638)
+			position.x = 638 - _dimensions._width;
+		if (position.y < 2)
+			position.y = 2;
+		else if (position.y + _dimensions._height > 478)
+			position.y = 478 - _dimensions._height;
+	} else {
+		// Duckman
+		if (position.x < 2)
+			position.x = 2;
+		else if (position.x + _dimensions._width > 318)
+			position.x = 318 - _dimensions._width;
+		if (position.y < 2)
+			position.y = 2;
+		else if (position.y + _dimensions._height > 198)
+			position.y = 198 - _dimensions._height;
+	}
 }
 
 bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, Common::Point offsPt,
@@ -72,6 +85,7 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 	TextDrawer textDrawer;
 	bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr);
 	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
+	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1());
 	_dimensions = dimensions;
 	textDrawer.drawText(_vm->_screen, _surface, color2, color1);
 	return done;
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 4fbb5a2..7adf9e5 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -26,6 +26,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
 #include "illusions/resources/talkresource.h"
+#include "illusions/screentext.h"
 #include "illusions/sound.h"
 #include "illusions/time.h"
 
@@ -159,7 +160,7 @@ int TalkThread::onUpdate() {
 		if (!(_flags & 4) && !_vm->_soundMan->isVoicePlaying())
 			_flags |= 4;
 		if (!(_flags & 8) && isTimerExpired(_textStartTime, _textEndTime)) {
-			// TODO _vm->removeText();
+			_vm->_screenText->removeText();
 			if (_entryText && *_entryText) {
 				refreshText();
 				_vm->_input->discardEvent(kEventSkip);
@@ -180,7 +181,7 @@ int TalkThread::onUpdate() {
 		}
 		if (_objectId && _vm->_input->pollEvent(kEventSkip)) {
 			if (!(_flags & 8)) {
-				// TODO _vm->removeText();
+				_vm->_screenText->removeText();
 				if (_entryText && *_entryText)
 					refreshText();
 				else
@@ -224,7 +225,7 @@ int TalkThread::onUpdate() {
 			_flags |= 2;
 		}
 		if (!(_flags & 8)) {
-			// TODO _vm->removeText();
+			_vm->_screenText->removeText();
 			_flags |= 8;
 		}
 		if (!(_flags & 4)) {
@@ -307,11 +308,10 @@ static char *debugW2I(byte *wstr) {
 }
 
 int TalkThread::insertText() {
+/*
 	int charCount = 100;
-	
 	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
 	_entryText = 0;
-	
 	// TODO _vm->getDimensions1(&dimensions);
 	// TODO _vm->insertText(_currEntryText, _vm->_currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
 	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
@@ -319,6 +319,21 @@ int TalkThread::insertText() {
 	// TODO _vm->getPoint1(&pt);
 	// TODO _vm->updateTextInfoPosition(pt);
 	return charCount >> 1;
+*/	
+	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
+	WidthHeight dimensions;
+	_vm->getDefaultTextDimensions(dimensions);
+	uint16 *outTextPtr;
+	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
+		//Common::Point(0, 0), 2, 0, 0, _color.r, _color.g, _color.b, outTextPtr);
+		Common::Point(0, 0), 2, 0, 0, 0, 0, 0, outTextPtr);
+	_entryText = (byte*)outTextPtr;
+	Common::Point pt;
+	_vm->getDefaultTextPosition(pt);
+	_vm->_screenText->updateTextInfoPosition(pt);
+	//_vm->_screenText->updateTextInfoPosition(Common::Point(320, 200));
+	int charCount = (_entryText - _currEntryText) / 2;
+	return charCount;
 }
 
 TalkEntry *TalkThread::getTalkResourceEntry(uint32 talkId) {
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index f47dd7d..6d8224e 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -289,7 +289,7 @@ int TalkThread_Duckman::insertText() {
 	_vm->getDefaultTextDimensions(dimensions);
 	uint16 *outTextPtr;
 	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
-		Common::Point(0, 0), 2, 0, 0, _color.r, _color.r, _color.r, outTextPtr);
+		Common::Point(0, 0), 2, 0, 0, _color.r, _color.g, _color.b, outTextPtr);
 	_entryText = (byte*)outTextPtr;
 	Common::Point pt;
 	_vm->getDefaultTextPosition(pt);


Commit: e63eaabbdb5c54df03651edc34a6048946fb2f3f
    https://github.com/scummvm/scummvm/commit/e63eaabbdb5c54df03651edc34a6048946fb2f3f
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move trigger functions code to seperate file

Changed paths:
  A engines/illusions/bbdou/bbdou_triggerfunctions.cpp
  A engines/illusions/bbdou/bbdou_triggerfunctions.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/module.mk


diff --git a/engines/illusions/bbdou/bbdou_triggerfunctions.cpp b/engines/illusions/bbdou/bbdou_triggerfunctions.cpp
new file mode 100644
index 0000000..7c1ee47
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_triggerfunctions.cpp
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_triggerfunctions.h"
+
+namespace Illusions {
+
+// TriggerFunction
+
+TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
+	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
+}
+
+TriggerFunction::~TriggerFunction() {
+	delete _callback;
+}
+
+void TriggerFunction::run(uint32 callingThreadId) {
+	(*_callback)(this, callingThreadId);
+}
+
+// TriggerFunctions
+
+void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end()) {
+		delete *it;
+		_triggerFunctions.erase(it);
+	}
+	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
+}
+
+TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
+	if (it != _triggerFunctions.end())
+		return (*it);
+	return 0;
+}
+
+void TriggerFunctions::removeBySceneId(uint32 sceneId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	while (it != _triggerFunctions.end()) {
+		if ((*it)->_sceneId == sceneId) {
+			delete *it;
+			it = _triggerFunctions.erase(it);
+		} else
+			++it;
+	}
+}
+
+TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
+	ItemsIterator it = _triggerFunctions.begin();
+	for (; it != _triggerFunctions.end(); ++it) {
+		TriggerFunction *triggerFunction = *it;
+		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
+			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
+			break;
+	}
+	return it;		
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_triggerfunctions.h b/engines/illusions/bbdou/bbdou_triggerfunctions.h
new file mode 100644
index 0000000..a41bfba
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_triggerfunctions.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_TRIGGERFUNCTIONS_H
+#define ILLUSIONS_BBDOU_BBDOU_TRIGGERFUNCTIONS_H
+
+#include "common/algorithm.h"
+#include "common/stack.h"
+
+namespace Illusions {
+
+struct TriggerFunction;
+
+typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
+
+struct TriggerFunction {
+	uint32 _sceneId;
+	uint32 _verbId;
+	uint32 _objectId2;
+	uint32 _objectId;
+	TriggerFunctionCallback *_callback;
+	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	~TriggerFunction();
+	void run(uint32 callingThreadId);
+};
+
+class TriggerFunctions {
+public:
+	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
+	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+	void removeBySceneId(uint32 sceneId);
+public:
+	typedef Common::List<TriggerFunction*> Items;
+	typedef Items::iterator ItemsIterator;
+	Items _triggerFunctions;
+	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_TRIGGERFUNCTIONS_H
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 9aeb080..b2e5098 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -65,60 +65,6 @@
 
 namespace Illusions {
 
-// TriggerFunction
-
-TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
-	: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
-}
-
-TriggerFunction::~TriggerFunction() {
-	delete _callback;
-}
-
-void TriggerFunction::run(uint32 callingThreadId) {
-	(*_callback)(this, callingThreadId);
-}
-
-// TriggerFunctions
-
-void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
-	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
-	if (it != _triggerFunctions.end()) {
-		delete *it;
-		_triggerFunctions.erase(it);
-	}
-	_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
-}
-
-TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
-	if (it != _triggerFunctions.end())
-		return (*it);
-	return 0;
-}
-
-void TriggerFunctions::removeBySceneId(uint32 sceneId) {
-	ItemsIterator it = _triggerFunctions.begin();
-	while (it != _triggerFunctions.end()) {
-		if ((*it)->_sceneId == sceneId) {
-			delete *it;
-			it = _triggerFunctions.erase(it);
-		} else
-			++it;
-	}
-}
-
-TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
-	ItemsIterator it = _triggerFunctions.begin();
-	for (; it != _triggerFunctions.end(); ++it) {
-		TriggerFunction *triggerFunction = *it;
-		if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
-			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
-			break;
-	}
-	return it;		
-}
-
 // ActiveScenes
 
 ActiveScenes::ActiveScenes() {
@@ -348,7 +294,6 @@ uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32
 	if (triggerFunction) {
 		triggerFunction->run(callingThreadId);
 	} else if (findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
-		//debug("Run cause at %08X", codeOffs);
 		causeThreadId = startTempScriptThread(_scriptResource->getCode(codeOffs),
 			callingThreadId, verbId, objectId2, objectId);
 	}
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index 5317809..f2dbd1a 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -24,6 +24,7 @@
 #define ILLUSIONS_ILLUSIONS_BBDOU_H
 
 #include "illusions/illusions.h"
+#include "illusions/bbdou/bbdou_triggerfunctions.h"
 #include "common/algorithm.h"
 #include "common/stack.h"
 
@@ -32,33 +33,6 @@ namespace Illusions {
 class Dictionary;
 class ScriptMan;
 class ScriptStack;
-class TriggerFunctions;
-class TriggerFunction;
-
-typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
-
-struct TriggerFunction {
-	uint32 _sceneId;
-	uint32 _verbId;
-	uint32 _objectId2;
-	uint32 _objectId;
-	TriggerFunctionCallback *_callback;
-	TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	~TriggerFunction();
-	void run(uint32 callingThreadId);
-};
-
-class TriggerFunctions {
-public:
-	void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
-	TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-	void removeBySceneId(uint32 sceneId);
-public:
-	typedef Common::List<TriggerFunction*> Items;
-	typedef Items::iterator ItemsIterator;
-	Items _triggerFunctions;
-	ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
-};
 
 struct ActiveScene {
 	uint32 _sceneId;
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 0a01f7f..d1df55c 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	bbdou/bbdou_cursor.o \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
+	bbdou/bbdou_triggerfunctions.o \
 	bbdou/illusions_bbdou.o \
 	bbdou/scriptopcodes_bbdou.o \
 	camera.o \


Commit: 88ea89118473e149de077f2c620dc5424e1ed1a2
    https://github.com/scummvm/scummvm/commit/88ea89118473e149de077f2c620dc5424e1ed1a2
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Use ARRAYSIZE instead of hardcoded values in screen effect constants

Changed paths:
    engines/illusions/duckman/duckman_specialcode.cpp


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index 8d33aa7..af52183 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -86,7 +86,7 @@ static const ScreenShakerPoint kShakerPoints0[] = {
 };
 
 static const ScreenShakeEffect kShakerEffect0 = {
-	6, 5, kShakerPoints0
+	ARRAYSIZE(kShakerPoints0), 5, kShakerPoints0
 };
 
 static const ScreenShakerPoint kShakerPoints1[] = {
@@ -95,7 +95,7 @@ static const ScreenShakerPoint kShakerPoints1[] = {
 };
 
 static const ScreenShakeEffect kShakerEffect1 = {
-	9, 2, kShakerPoints1
+	ARRAYSIZE(kShakerPoints1), 2, kShakerPoints1
 };
 
 static const ScreenShakerPoint kShakerPoints2[] = {
@@ -104,7 +104,7 @@ static const ScreenShakerPoint kShakerPoints2[] = {
 };
 
 static const ScreenShakeEffect kShakerEffect2 = {
-	9, 2, kShakerPoints2
+	ARRAYSIZE(kShakerPoints2), 2, kShakerPoints2
 };
 
 static const ScreenShakerPoint kShakerPoints3[] = {
@@ -112,7 +112,7 @@ static const ScreenShakerPoint kShakerPoints3[] = {
 };
 
 static const ScreenShakeEffect kShakerEffect3 = {
-	5, 2, kShakerPoints3
+	ARRAYSIZE(kShakerPoints3), 2, kShakerPoints3
 };
 
 static const ScreenShakerPoint kShakerPoints4[] = {
@@ -120,7 +120,7 @@ static const ScreenShakerPoint kShakerPoints4[] = {
 };
 
 static const ScreenShakeEffect kShakerEffect4 = {
-	8, 5, kShakerPoints4
+	ARRAYSIZE(kShakerPoints4), 5, kShakerPoints4
 };
 
 static const ScreenShakerPoint kShakerPoints5[] = {
@@ -131,7 +131,7 @@ static const ScreenShakerPoint kShakerPoints5[] = {
 };
 
 static const ScreenShakeEffect kShakerEffect5 = {
-	31, 2, kShakerPoints5
+	ARRAYSIZE(kShakerPoints5), 2, kShakerPoints5
 };
 
 static const ScreenShakeEffect *kShakerEffects[] = {


Commit: a30a31868b44d3218e59c6eca42bf1d924b996e8
    https://github.com/scummvm/scummvm/commit/a30a31868b44d3218e59c6eca42bf1d924b996e8
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move screen shaker effects into separate file

Changed paths:
  A engines/illusions/duckman/duckman_screenshakereffects.cpp
  A engines/illusions/duckman/duckman_screenshakereffects.h
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/duckman/duckman_screenshakereffects.cpp b/engines/illusions/duckman/duckman_screenshakereffects.cpp
new file mode 100644
index 0000000..44f3ebc
--- /dev/null
+++ b/engines/illusions/duckman/duckman_screenshakereffects.cpp
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/duckman/duckman_inventory.h"
+#include "illusions/duckman/propertytimers.h"
+#include "illusions/duckman/scriptopcodes_duckman.h"
+#include "illusions/actor.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/specialcode.h"
+
+#include "engines/util.h"
+
+namespace Illusions {
+
+static const ScreenShakerPoint kShakerPoints0[] = {
+	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
+};
+
+static const ScreenShakeEffect kShakerEffect0 = {
+	ARRAYSIZE(kShakerPoints0), 5, kShakerPoints0
+};
+
+static const ScreenShakerPoint kShakerPoints1[] = {
+	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
+	{ 1,  2}, {0, -1} 
+};
+
+static const ScreenShakeEffect kShakerEffect1 = {
+	ARRAYSIZE(kShakerPoints1), 2, kShakerPoints1
+};
+
+static const ScreenShakerPoint kShakerPoints2[] = {
+	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
+	{0,  1}, {0, -1},
+};
+
+static const ScreenShakeEffect kShakerEffect2 = {
+	ARRAYSIZE(kShakerPoints2), 2, kShakerPoints2
+};
+
+static const ScreenShakerPoint kShakerPoints3[] = {
+	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect3 = {
+	ARRAYSIZE(kShakerPoints3), 2, kShakerPoints3
+};
+
+static const ScreenShakerPoint kShakerPoints4[] = {
+	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
+};
+
+static const ScreenShakeEffect kShakerEffect4 = {
+	ARRAYSIZE(kShakerPoints4), 5, kShakerPoints4
+};
+
+static const ScreenShakerPoint kShakerPoints5[] = {
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
+	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
+};
+
+static const ScreenShakeEffect kShakerEffect5 = {
+	ARRAYSIZE(kShakerPoints5), 2, kShakerPoints5
+};
+
+static const ScreenShakeEffect *kShakerEffects[] = {
+	&kShakerEffect0,
+	&kShakerEffect1,
+	&kShakerEffect2,
+	&kShakerEffect3,
+	&kShakerEffect4,
+	&kShakerEffect5
+};
+
+const ScreenShakeEffect *getScreenShakeEffect(byte index) {
+	return kShakerEffects[index];
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_screenshakereffects.h b/engines/illusions/duckman/duckman_screenshakereffects.h
new file mode 100644
index 0000000..c868ce4
--- /dev/null
+++ b/engines/illusions/duckman/duckman_screenshakereffects.h
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_SCREENSHAKEEFFECTS_H
+#define ILLUSIONS_DUCKMAN_SCREENSHAKEEFFECTS_H
+
+#include "illusions/illusions.h"
+
+namespace Illusions {
+
+const ScreenShakeEffect *getScreenShakeEffect(byte index);
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_SCREENSHAKEEFFECTS_H
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index af52183..d0d8a5c 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_screenshakereffects.h"
 #include "illusions/duckman/duckman_specialcode.h"
 #include "illusions/duckman/duckman_inventory.h"
 #include "illusions/duckman/propertytimers.h"
@@ -79,73 +80,9 @@ void DuckmanSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
 	}
 }
 
-// TODO Move to separate file
-
-static const ScreenShakerPoint kShakerPoints0[] = {
-	{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
-};
-
-static const ScreenShakeEffect kShakerEffect0 = {
-	ARRAYSIZE(kShakerPoints0), 5, kShakerPoints0
-};
-
-static const ScreenShakerPoint kShakerPoints1[] = {
-	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
-	{ 1,  2}, {0, -1} 
-};
-
-static const ScreenShakeEffect kShakerEffect1 = {
-	ARRAYSIZE(kShakerPoints1), 2, kShakerPoints1
-};
-
-static const ScreenShakerPoint kShakerPoints2[] = {
-	{0, -3}, {0,  3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
-	{0,  1}, {0, -1},
-};
-
-static const ScreenShakeEffect kShakerEffect2 = {
-	ARRAYSIZE(kShakerPoints2), 2, kShakerPoints2
-};
-
-static const ScreenShakerPoint kShakerPoints3[] = {
-	{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
-};
-
-static const ScreenShakeEffect kShakerEffect3 = {
-	ARRAYSIZE(kShakerPoints3), 2, kShakerPoints3
-};
-
-static const ScreenShakerPoint kShakerPoints4[] = {
-	{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
-};
-
-static const ScreenShakeEffect kShakerEffect4 = {
-	ARRAYSIZE(kShakerPoints4), 5, kShakerPoints4
-};
-
-static const ScreenShakerPoint kShakerPoints5[] = {
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
-	{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
-};
-
-static const ScreenShakeEffect kShakerEffect5 = {
-	ARRAYSIZE(kShakerPoints5), 2, kShakerPoints5
-};
-
-static const ScreenShakeEffect *kShakerEffects[] = {
-	&kShakerEffect0,
-	&kShakerEffect1,
-	&kShakerEffect2,
-	&kShakerEffect3,
-	&kShakerEffect4,
-	&kShakerEffect5
-};
-
 void DuckmanSpecialCode::spcStartScreenShaker(OpCall &opCall) {
 	ARG_BYTE(effect);
-	const ScreenShakeEffect *shakerEffect = kShakerEffects[effect];
+	const ScreenShakeEffect *shakerEffect = getScreenShakeEffect(effect);
 	_vm->startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
 }
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index d1df55c..083e2f5 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -15,6 +15,7 @@ MODULE_OBJS := \
 	dictionary.o \
 	duckman/duckman_dialog.o \
 	duckman/duckman_inventory.o \
+	duckman/duckman_screenshakereffects.o \
 	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
 	duckman/propertytimers.o \


Commit: a5ad1bc1062f37dc383db6c6cecc4dcbf0081bcf
    https://github.com/scummvm/scummvm/commit/a5ad1bc1062f37dc383db6c6cecc4dcbf0081bcf
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Remove unneeded includes

Changed paths:
    engines/illusions/duckman/duckman_screenshakereffects.cpp


diff --git a/engines/illusions/duckman/duckman_screenshakereffects.cpp b/engines/illusions/duckman/duckman_screenshakereffects.cpp
index 44f3ebc..66bfaea 100644
--- a/engines/illusions/duckman/duckman_screenshakereffects.cpp
+++ b/engines/illusions/duckman/duckman_screenshakereffects.cpp
@@ -21,13 +21,7 @@
  */
 
 #include "illusions/duckman/illusions_duckman.h"
-#include "illusions/duckman/duckman_specialcode.h"
-#include "illusions/duckman/duckman_inventory.h"
-#include "illusions/duckman/propertytimers.h"
-#include "illusions/duckman/scriptopcodes_duckman.h"
-#include "illusions/actor.h"
-#include "illusions/resources/scriptresource.h"
-#include "illusions/specialcode.h"
+#include "illusions/duckman/duckman_screenshakereffects.h"
 
 #include "engines/util.h"
 


Commit: 41978f466d423e9b5f3c3980666c1beb4219b937
    https://github.com/scummvm/scummvm/commit/41978f466d423e9b5f3c3980666c1beb4219b937
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add GAM archive reader for Duckman and adjust the resource reader/system

Changed paths:
  A engines/illusions/fileresourcereader.cpp
  A engines/illusions/fileresourcereader.h
  A engines/illusions/gamarchive.cpp
  A engines/illusions/gamarchive.h
  A engines/illusions/gamresourcereader.cpp
  A engines/illusions/gamresourcereader.h
  A engines/illusions/resourcereader.cpp
  A engines/illusions/resourcereader.h
    engines/illusions/actor.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/actorresource.h
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/resources/backgroundresource.h
    engines/illusions/resources/fontresource.cpp
    engines/illusions/resources/fontresource.h
    engines/illusions/resources/midiresource.cpp
    engines/illusions/resources/midiresource.h
    engines/illusions/resources/scriptresource.cpp
    engines/illusions/resources/scriptresource.h
    engines/illusions/resources/soundresource.cpp
    engines/illusions/resources/soundresource.h
    engines/illusions/resources/talkresource.cpp
    engines/illusions/resources/talkresource.h
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 0e4bae7..cf3416a 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -947,7 +947,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 
 	if (!sequence && _vm->getGameId() == kGameIdDuckman) {
-		debug("Load external sequence...");
+		debug(1, "Load external sequence...");
 		_vm->_resSys->loadResource(0x00060000 | (sequenceId & 0xFFFF), _vm->getCurrentScene(), 0);
 		sequence = _vm->_dict->findSequence(sequenceId);
 		_actor->_flags |= 0x800;
@@ -1378,7 +1378,7 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 				int savedSeqCodeValue1 = actor->_seqCodeValue1;
 				int savedSeqCodeValue3 = actor->_seqCodeValue3;
 				uint32 regionSequenceId = actor->_regionLayer->getRegionSequenceId(regionIndex);
-				debug("Running transition sequence %08X", regionSequenceId);
+				debug(1, "Running transition sequence %08X", regionSequenceId);
 				Sequence *sequence = _vm->_dict->findSequence(regionSequenceId);
 				actor->_sequenceId = regionSequenceId;
 				actor->_seqCodeIp = sequence->_sequenceCode;
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index b2e5098..98a4a24 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -25,6 +25,7 @@
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
 #include "illusions/dictionary.h"
+#include "illusions/fileresourcereader.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
 #include "illusions/resources/actorresource.h"
@@ -138,6 +139,8 @@ Common::Error IllusionsEngine_BBDOU::run() {
 
 	_dict = new Dictionary();
 
+	_resReader = new ResourceReaderFileReader();
+
 	_resSys = new ResourceSystem(this);
 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
@@ -216,6 +219,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _screenText;
 	delete _screen;
 	delete _resSys;
+	delete _resReader;
 	delete _dict;
 	
 	debug("Ok");
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index d30f17b..041e437 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -28,11 +28,12 @@
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
 #include "illusions/dictionary.h"
-#include "illusions/resources/fontresource.h"
+#include "illusions/gamresourcereader.h"
 #include "illusions/graphics.h"
 #include "illusions/input.h"
 #include "illusions/resources/actorresource.h"
 #include "illusions/resources/backgroundresource.h"
+#include "illusions/resources/fontresource.h"
 #include "illusions/resources/midiresource.h"
 #include "illusions/resources/scriptresource.h"
 #include "illusions/resources/soundresource.h"
@@ -83,9 +84,10 @@ Common::Error IllusionsEngine_Duckman::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
 	SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
-	SearchMan.addSubDirectoryMatching(gameDataDir, "x");// DEBUG until gam reader is done
 
 	_dict = new Dictionary();
+	
+	_resReader = new ResourceReaderGamArchive("duckman.gam");
 
 	_resSys = new ResourceSystem(this);
 	_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
@@ -115,7 +117,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	_screen->setColorKey1(0);
 
-    initInput();
+	initInput();
 
 	initUpdateFunctions();
 
@@ -190,6 +192,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _screenText;
 	delete _screen;
 	delete _resSys;
+	delete _resReader;
 	delete _dict;
 	
 	debug("Ok");
@@ -388,7 +391,7 @@ Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId
 		}
 	} else {
 		// TODO
-		debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+		debug(1, "getNamedPointPosition(%08X) UNKNOWN", namedPointId);
 		return Common::Point(0, 0);
 	}
 }
@@ -1005,7 +1008,7 @@ bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, u
 
 uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
 	// TODO
-	debug("runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
+	debug(1, "runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
 	uint32 triggerThreadId;
 
 	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
@@ -1048,7 +1051,7 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	}
 
 	uint32 tempThreadId = newTempThreadId();
-	debug("Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
+	debug(1, "Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
 	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
 		triggerThreadId);
 	_threads->startThread(causeThread);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 3f12209..107d898 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -272,7 +272,7 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	ARG_UINT32(threadId);
 	_vm->_input->discardAllEvents();
 	
-	debug("changeScene(%08X, %08X)", sceneId, threadId);
+	debug(1, "changeScene(%08X, %08X)", sceneId, threadId);
 	
 	//DEBUG
 	if (dsceneId) {
@@ -555,9 +555,7 @@ void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCa
 void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(specialCodeId);
-debug("run(%08X)", specialCodeId);	
 	_vm->_specialCode->run(specialCodeId, opCall);
-debug("run(%08X) OK", specialCodeId);	
 }
 
 void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/fileresourcereader.cpp b/engines/illusions/fileresourcereader.cpp
new file mode 100644
index 0000000..c707c65
--- /dev/null
+++ b/engines/illusions/fileresourcereader.cpp
@@ -0,0 +1,79 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/resourcesystem.h"
+#include "illusions/fileresourcereader.h"
+#include "illusions/illusions.h"
+#include "common/file.h"
+#include "common/str.h"
+
+namespace Illusions {
+
+byte *ResourceReaderFileReader::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
+	debug("ResourceReaderFileReader::readResource(%08X, %08X)", sceneId, resId);
+	
+	Common::String filename = buildResourceFilename(resId);
+	Common::File fd;
+	if (!fd.open(filename))
+		error("Resource::loadData() Could not open %s for reading", filename.c_str());
+	dataSize = fd.size();
+	byte *data = (byte*)malloc(dataSize);
+	fd.read(data, dataSize);
+	return data;
+}
+
+Common::String ResourceReaderFileReader::buildResourceFilename(uint32 resId) {
+	const char *ext = getResourceExtension(resId);
+	return Common::String::format("%08X%s", resId, ext);
+}
+
+const char *ResourceReaderFileReader::getResourceExtension(uint32 resId) {
+	// TODO Make constants
+	switch (ResourceTypeId(resId)) {
+	case 0x00060000:
+	case 0x00100000:
+		// ActorResource
+		return ".act";
+	case 0x00080000:
+		// SoundGroupResource
+		return ".sg";
+	case 0x000D0000:
+		// ScriptResource
+		return ".scr";
+	case 0x000F0000:
+		// TalkResource
+		return ".tlk";
+	case 0x00110000:
+		// BackgroundResource
+		return ".bg";
+	case 0x00120000:
+		// FontResource
+		return ".fnt";
+	case 0x00170000:
+		// SpecialCode
+		return "";
+	default:
+		return "";
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/fileresourcereader.h b/engines/illusions/fileresourcereader.h
new file mode 100644
index 0000000..70c2e5b
--- /dev/null
+++ b/engines/illusions/fileresourcereader.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_FILERESOURCEREADER_H
+#define ILLUSIONS_FILERESOURCEREADER_H
+
+#include "illusions/illusions.h"
+#include "illusions/resourcereader.h"
+
+namespace Illusions {
+
+class ResourceReaderFileReader : public BaseResourceReader {
+public:
+	byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize);
+protected:
+	Common::String buildResourceFilename(uint32 resId);
+	const char *getResourceExtension(uint32 resId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_FILERESOURCEREADER_H
diff --git a/engines/illusions/gamarchive.cpp b/engines/illusions/gamarchive.cpp
new file mode 100644
index 0000000..517882c
--- /dev/null
+++ b/engines/illusions/gamarchive.cpp
@@ -0,0 +1,101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/gamarchive.h"
+
+namespace Illusions {
+
+GamArchive::GamArchive(const char *filename)
+	: _fd(0), _groupCount(0), _groups(0) {
+	_fd = new Common::File();
+	if (!_fd->open(filename))
+		error("GamArchive::GamArchive() Could not open %s", filename);
+	loadDictionary();
+}
+
+GamArchive::~GamArchive() {
+	delete[] _groups;
+}
+
+byte *GamArchive::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
+	const GamFileEntry *fileEntry = getGroupFileEntry(sceneId, resId);
+	_fd->seek(fileEntry->_fileOffset);
+	dataSize = fileEntry->_fileSize;
+	byte *data = (byte*)malloc(dataSize);
+	_fd->read(data, dataSize);
+	return data;
+}
+
+void GamArchive::loadDictionary() {
+	_groupCount = _fd->readUint32LE();
+	_groups = new GamGroupEntry[_groupCount];
+	uint32 *groupOffsets = new uint32[_groupCount];
+	
+	for (uint i = 0; i < _groupCount; ++i) {
+		_groups[i]._id = _fd->readUint32LE();
+		groupOffsets[i] = _fd->readUint32LE();
+	}
+	
+	for (uint i = 0; i < _groupCount; ++i) {
+		_fd->seek(groupOffsets[i]);
+		uint32 fileCount = _fd->readUint32LE();
+		_groups[i]._fileCount = fileCount;
+		_groups[i]._files = new GamFileEntry[fileCount];
+		
+		debug("Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount);
+		
+		for (uint j = 0; j < fileCount; ++j) {
+			_groups[i]._files[j]._id = _fd->readUint32LE();
+			_groups[i]._files[j]._fileOffset = _fd->readUint32LE();
+			_groups[i]._files[j]._fileSize = _fd->readUint32LE();
+			debug("  %08X, %08X, %d", _groups[i]._files[j]._id, _groups[i]._files[j]._fileOffset, _groups[i]._files[j]._fileSize);
+		}
+	}
+
+	delete[] groupOffsets;
+}
+
+const GamGroupEntry *GamArchive::getGroupEntry(uint32 sceneId) {
+	for (uint i = 0; i < _groupCount; ++i)
+		if (_groups[i]._id == sceneId)
+			return &_groups[i];
+	return 0;
+}
+
+const GamFileEntry *GamArchive::getFileEntry(const GamGroupEntry *groupEntry, uint32 resId) {
+	for (uint i = 0; i < groupEntry->_fileCount; ++i)
+		if (groupEntry->_files[i]._id == resId)
+			return &groupEntry->_files[i];
+	return 0;
+}
+
+const GamFileEntry *GamArchive::getGroupFileEntry(uint32 sceneId, uint32 resId) {
+	const GamGroupEntry *groupEntry = getGroupEntry(sceneId);
+	if (!groupEntry)
+		error("GamArchive::getFileEntry() Group %08X not found", sceneId);
+	const GamFileEntry *fileEntry = getFileEntry(groupEntry, resId);
+	if (!fileEntry)
+		error("GamArchive::getFileEntry() File %08X in group %08X not found", resId, sceneId);
+	return fileEntry;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/gamarchive.h b/engines/illusions/gamarchive.h
new file mode 100644
index 0000000..3e43c53
--- /dev/null
+++ b/engines/illusions/gamarchive.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_GAMARCHIVE_H
+#define ILLUSIONS_GAMARCHIVE_H
+
+#include "illusions/illusions.h"
+#include "common/file.h"
+
+namespace Illusions {
+
+struct GamFileEntry {
+	uint32 _id;
+	uint32 _fileOffset;
+	uint32 _fileSize;
+};
+
+struct GamGroupEntry {
+	uint32 _id;
+	uint _fileCount;
+	GamFileEntry *_files;
+	GamGroupEntry() : _fileCount(0), _files(0) {
+	}
+	~GamGroupEntry() {
+		delete[] _files;
+	}
+};
+
+class GamArchive {
+public:
+	GamArchive(const char *filename);
+	~GamArchive();
+	byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize);
+protected:
+	Common::File *_fd;
+	uint _groupCount;
+	GamGroupEntry *_groups;
+	void loadDictionary();
+	const GamGroupEntry *getGroupEntry(uint32 sceneId);
+	const GamFileEntry *getFileEntry(const GamGroupEntry *groupEntry, uint32 resId);
+	const GamFileEntry *getGroupFileEntry(uint32 sceneId, uint32 resId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_GAMARCHIVE_H
diff --git a/engines/illusions/gamresourcereader.cpp b/engines/illusions/gamresourcereader.cpp
new file mode 100644
index 0000000..4b4f3e0
--- /dev/null
+++ b/engines/illusions/gamresourcereader.cpp
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/resourcesystem.h"
+#include "illusions/resourcereader_gamarchive.h"
+#include "illusions/gamarchive.h"
+#include "illusions/illusions.h"
+#include "common/file.h"
+#include "common/str.h"
+
+namespace Illusions {
+
+ResourceReaderGamArchive::ResourceReaderGamArchive(const char *filename) {
+	_gamArchive = new GamArchive(filename);
+}
+
+ResourceReaderGamArchive::~ResourceReaderGamArchive() {
+	delete _gamArchive;
+}
+
+byte *ResourceReaderGamArchive::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
+	return _gamArchive->readResource(sceneId, resId, dataSize);
+}
+	
+} // End of namespace Illusions
diff --git a/engines/illusions/gamresourcereader.h b/engines/illusions/gamresourcereader.h
new file mode 100644
index 0000000..ed8227e
--- /dev/null
+++ b/engines/illusions/gamresourcereader.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_RESOURCEREADER_GAMARCHIVE_H
+#define ILLUSIONS_RESOURCEREADER_GAMARCHIVE_H
+
+#include "illusions/illusions.h"
+#include "illusions/resourcereader.h"
+
+namespace Illusions {
+
+class GamArchive;
+
+class ResourceReaderGamArchive : public BaseResourceReader {
+public:
+	ResourceReaderGamArchive(const char *filename);
+	~ResourceReaderGamArchive();
+	byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize);
+protected:
+	GamArchive *_gamArchive;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_RESOURCEREADER_GAMARCHIVE_H
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 9980698..023a45f 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -47,6 +47,7 @@ namespace Illusions {
 #define ILLUSIONS_SAVEGAME_VERSION 0
 
 class ResourceSystem;
+class BaseResourceReader;
 
 struct SurfInfo;
 
@@ -99,6 +100,7 @@ public:
 	Common::RandomSource *_random;
 	Dictionary *_dict;
 	ResourceSystem *_resSys;
+	BaseResourceReader *_resReader;
 	UpdateFunctions *_updateFunctions;
 	
 	void updateEvents();
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 083e2f5..ee40fb3 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -20,7 +20,10 @@ MODULE_OBJS := \
 	duckman/illusions_duckman.o \
 	duckman/propertytimers.o \
 	duckman/scriptopcodes_duckman.o \
+	fileresourcereader.o \
 	fixedpoint.o \
+	gamarchive.o \
+	gamresourcereader.o \
 	graphics.o \
 	illusions.o \
 	input.o \
diff --git a/engines/illusions/resourcereader.cpp b/engines/illusions/resourcereader.cpp
new file mode 100644
index 0000000..e665bfb
--- /dev/null
+++ b/engines/illusions/resourcereader.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/resourcereader.h"
+#include "illusions/illusions.h"
+
+namespace Illusions {
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resourcereader.h b/engines/illusions/resourcereader.h
new file mode 100644
index 0000000..39ddbaf
--- /dev/null
+++ b/engines/illusions/resourcereader.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_RESOURCEREADER_H
+#define ILLUSIONS_RESOURCEREADER_H
+
+#include "illusions/illusions.h"
+
+namespace Illusions {
+
+class BaseResourceReader {
+public:
+	virtual ~BaseResourceReader() {}
+	virtual byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) = 0;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_RESOURCEREADER_H
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index a8d62e5..b93cf0f 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -32,10 +32,6 @@ void ActorResourceLoader::load(Resource *resource) {
 	resource->_instance = _vm->_actorInstances->createActorInstance(resource);
 }
 
-void ActorResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.act", resource->_resId);
-}
-
 bool ActorResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile;
@@ -161,7 +157,7 @@ void ActorResource::load(Resource *resource) {
 		_namedPoints.load(namedPointsCount, stream);
 	}
 	
-	debug("ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
+	debug(1, "ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
 }
 
 bool ActorResource::containsSequence(Sequence *sequence) {
diff --git a/engines/illusions/resources/actorresource.h b/engines/illusions/resources/actorresource.h
index 2ccb9ef..b78e515 100644
--- a/engines/illusions/resources/actorresource.h
+++ b/engines/illusions/resources/actorresource.h
@@ -36,7 +36,6 @@ public:
 	ActorResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~ActorResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index bc87311..e50f46b 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -37,10 +37,6 @@ void BackgroundResourceLoader::load(Resource *resource) {
 	resource->_instance = _vm->_backgroundInstances->createBackgroundInstance(resource);
 }
 
-void BackgroundResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.bg", resource->_resId);
-}
-
 bool BackgroundResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile;
@@ -131,7 +127,7 @@ void RegionLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_mapWidth = READ_LE_UINT16(_map + 0);
 	_mapHeight = READ_LE_UINT16(_map + 2);
 	_map += 8;
-	debug("RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
+	debug(1, "RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
 		_unk, regionSequenceIdsOffs, _width, _height, mapOffs, valuesOffs);
 }
 
@@ -248,7 +244,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_priorityLayers = new PriorityLayer[_priorityLayersCount];
 	stream.seek(0x34);
 	uint32 priorityLayersOffs = stream.readUint32LE();
-	debug("_priorityLayersCount: %d", _priorityLayersCount);
+	debug(1, "_priorityLayersCount: %d", _priorityLayersCount);
 	for (uint i = 0; i < _priorityLayersCount; ++i) {
 		stream.seek(priorityLayersOffs + i * 12);
 		_priorityLayers[i].load(data, stream);
@@ -260,7 +256,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	_regionLayers = new RegionLayer[_regionLayersCount];
 	stream.seek(0x38);
 	uint32 regionLayersOffs = stream.readUint32LE();
-	debug("_regionLayersCount: %d", _regionLayersCount);
+	debug(1, "_regionLayersCount: %d", _regionLayersCount);
 	for (uint i = 0; i < _regionLayersCount; ++i) {
 		stream.seek(regionLayersOffs + i * 20);
 		_regionLayers[i].load(data, stream);
@@ -291,7 +287,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	// Load path walk points
 	stream.seek(0x0E);
 	_pathWalkPointsCount = stream.readUint16LE();
-	debug("_pathWalkPointsCount: %d", _pathWalkPointsCount);
+	debug(1, "_pathWalkPointsCount: %d", _pathWalkPointsCount);
 	_pathWalkPoints = new PathWalkPoints[_pathWalkPointsCount];
 	stream.seek(0x28);
 	uint32 pathWalkPointsOffs = stream.readUint32LE();
@@ -303,7 +299,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	// Load path walk rects
 	stream.seek(0x12);
 	_pathWalkRectsCount = stream.readUint16LE();
-	debug("_pathWalkRectsCount: %d", _pathWalkRectsCount);
+	debug(1, "_pathWalkRectsCount: %d", _pathWalkRectsCount);
 	_pathWalkRects = new PathWalkRects[_pathWalkRectsCount];
 	stream.seek(0x30);
 	uint32 pathWalkRectsOffs = stream.readUint32LE();
@@ -376,7 +372,7 @@ BackgroundInstance::BackgroundInstance(IllusionsEngine *vm)
 }
 
 void BackgroundInstance::load(Resource *resource) {
-	debug("BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
+	debug(1, "BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
 
 	BackgroundResource *backgroundResource = new BackgroundResource();
 	backgroundResource->load(resource->_data, resource->_dataSize);
@@ -403,7 +399,7 @@ void BackgroundInstance::load(Resource *resource) {
 }
 
 void BackgroundInstance::unload() {
-	debug("BackgroundInstance::unload()");
+	debug(1, "BackgroundInstance::unload()");
 	freeSurface();
 	unregisterResources();
 	delete _bgRes;
diff --git a/engines/illusions/resources/backgroundresource.h b/engines/illusions/resources/backgroundresource.h
index 9438501..d118c3d 100644
--- a/engines/illusions/resources/backgroundresource.h
+++ b/engines/illusions/resources/backgroundresource.h
@@ -47,7 +47,6 @@ public:
 	BackgroundResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~BackgroundResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resources/fontresource.cpp b/engines/illusions/resources/fontresource.cpp
index 678e555..29aaf9b 100644
--- a/engines/illusions/resources/fontresource.cpp
+++ b/engines/illusions/resources/fontresource.cpp
@@ -34,10 +34,6 @@ void FontResourceLoader::load(Resource *resource) {
 	resource->_instance = fontInstance;
 }
 
-void FontResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
-}
-
 bool FontResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile;
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
index 579277c..e029145 100644
--- a/engines/illusions/resources/fontresource.h
+++ b/engines/illusions/resources/fontresource.h
@@ -35,7 +35,6 @@ public:
 	FontResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~FontResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resources/midiresource.cpp b/engines/illusions/resources/midiresource.cpp
index a3aeef7..203ed12 100644
--- a/engines/illusions/resources/midiresource.cpp
+++ b/engines/illusions/resources/midiresource.cpp
@@ -28,16 +28,12 @@ namespace Illusions {
 // MidiGroupResourceLoader
 
 void MidiGroupResourceLoader::load(Resource *resource) {
-	debug("MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
+	debug(1, "MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
 
     // TODO
 	
 }
 
-void MidiGroupResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.fnt", resource->_resId);
-}
-
 bool MidiGroupResourceLoader::isFlag(int flag) {
 	return false;
 }
diff --git a/engines/illusions/resources/midiresource.h b/engines/illusions/resources/midiresource.h
index 9032e99..1cd3f80 100644
--- a/engines/illusions/resources/midiresource.h
+++ b/engines/illusions/resources/midiresource.h
@@ -35,7 +35,6 @@ public:
 	MidiGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~MidiGroupResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index a29fd78..5ce7154 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -33,10 +33,6 @@ void ScriptResourceLoader::load(Resource *resource) {
 	resource->_instance = scriptInstance;
 }
 
-void ScriptResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.scr", resource->_resId);
-}
-
 bool ScriptResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile;
diff --git a/engines/illusions/resources/scriptresource.h b/engines/illusions/resources/scriptresource.h
index c2119b0..eb58c66 100644
--- a/engines/illusions/resources/scriptresource.h
+++ b/engines/illusions/resources/scriptresource.h
@@ -34,7 +34,6 @@ public:
 	ScriptResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~ScriptResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resources/soundresource.cpp b/engines/illusions/resources/soundresource.cpp
index 73ec163..5cfa240 100644
--- a/engines/illusions/resources/soundresource.cpp
+++ b/engines/illusions/resources/soundresource.cpp
@@ -34,10 +34,6 @@ void SoundGroupResourceLoader::load(Resource *resource) {
 	resource->_instance = soundGroupInstance;
 }
 
-void SoundGroupResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.sg", resource->_resId);
-}
-
 bool SoundGroupResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile/* ||
@@ -53,7 +49,7 @@ void SoundEffect::load(Common::SeekableReadStream &stream) {
 	_volume = stream.readUint16LE();
 	_frequency = stream.readUint16LE();
 	stream.skip(32 + 4); // Skip name
-	debug("SoundEffect::load() _soundEffectId: %08X, _looping: %d, _field6: %d, _volume: %d, _frequency: %d",
+	debug(1, "SoundEffect::load() _soundEffectId: %08X, _looping: %d, _field6: %d, _volume: %d, _frequency: %d",
 		_soundEffectId, _looping, _field6, _volume, _frequency);
 }
 
@@ -74,7 +70,7 @@ void SoundGroupResource::load(byte *data, uint32 dataSize) {
 	_soundEffectsCount = stream.readUint16LE();
 	stream.skip(2);
 	uint32 soundEffectsOffs = stream.readUint32LE();
-	debug("_soundEffectsCount: %d; soundEffectsOffs: %08X", _soundEffectsCount, soundEffectsOffs);
+	debug(1, "_soundEffectsCount: %d; soundEffectsOffs: %08X", _soundEffectsCount, soundEffectsOffs);
 	_soundEffects = new SoundEffect[_soundEffectsCount];
 	stream.seek(soundEffectsOffs);
 	for (uint i = 0; i < _soundEffectsCount; ++i)
diff --git a/engines/illusions/resources/soundresource.h b/engines/illusions/resources/soundresource.h
index 638e8df..97e6f89 100644
--- a/engines/illusions/resources/soundresource.h
+++ b/engines/illusions/resources/soundresource.h
@@ -35,7 +35,6 @@ public:
 	SoundGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~SoundGroupResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resources/talkresource.cpp b/engines/illusions/resources/talkresource.cpp
index 57392c7..9b03b37 100644
--- a/engines/illusions/resources/talkresource.cpp
+++ b/engines/illusions/resources/talkresource.cpp
@@ -32,10 +32,6 @@ void TalkResourceLoader::load(Resource *resource) {
 	resource->_instance = _vm->_talkItems->createTalkInstance(resource);
 }
 
-void TalkResourceLoader::buildFilename(Resource *resource) {
-	resource->_filename = Common::String::format("%08X.tlk", resource->_resId);
-}
-
 bool TalkResourceLoader::isFlag(int flag) {
 	return
 		flag == kRlfLoadFile;
diff --git a/engines/illusions/resources/talkresource.h b/engines/illusions/resources/talkresource.h
index 5e3797e..a29e58c 100644
--- a/engines/illusions/resources/talkresource.h
+++ b/engines/illusions/resources/talkresource.h
@@ -35,7 +35,6 @@ public:
 	TalkResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
 	virtual ~TalkResourceLoader() {}
 	virtual void load(Resource *resource);
-	virtual void buildFilename(Resource *resource);
 	virtual bool isFlag(int flag);
 protected:
 	IllusionsEngine *_vm;
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index 9e81f1f..13e4da6 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/resourcesystem.h"
+#include "illusions/resourcereader.h"
 #include "illusions/illusions.h"
 
 #include "common/algorithm.h"
@@ -47,21 +48,11 @@ ResourceInstance::~ResourceInstance() {
 
 // Resource
 
-void Resource::loadData() {
-	debug("Resource::loadData()");
-	
-	Common::File fd;
-	if (!fd.open(_filename))
-		error("Resource::loadData() Could not open %s for reading", _filename.c_str());
-	_dataSize = fd.size();
-	_data = (byte*)malloc(_dataSize);
-	fd.read(_data, _dataSize);
-	debug("Resource::loadData() OK");
+void Resource::loadData(BaseResourceReader *resReader) {
+	_data = resReader->readResource(_tag, _resId, _dataSize);
 }
 
 void Resource::unloadData() {
-	debug("Resource::unloadData()");
-	
 	free(_data);
 	_data = 0;
 	_dataSize = 0;
@@ -84,7 +75,7 @@ void ResourceSystem::addResourceLoader(uint32 resTypeId, BaseResourceLoader *res
 }
 
 void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
-	debug("ResourceSystem::loadResource(%08X, %08X, %08X)", resId, tag, threadId);
+	debug(1, "ResourceSystem::loadResource(%08X, %08X, %08X)", resId, tag, threadId);
 	BaseResourceLoader *resourceLoader = getResourceLoader(resId);
 
 	Resource *resource = new Resource();
@@ -94,24 +85,21 @@ void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
 	resource->_threadId = threadId;
 	resource->_gameId = _vm->getGameId();
 
-	resourceLoader->buildFilename(resource);
-
 	if (resourceLoader->isFlag(kRlfLoadFile)) {
-		debug("ResourceSystem::loadResource() kRlfLoadFile");
-		resource->loadData();
+		debug(1, "ResourceSystem::loadResource() kRlfLoadFile");
+		resource->loadData(_vm->_resReader);
 	}
 	
 	resourceLoader->load(resource);
 	
 	if (resourceLoader->isFlag(kRlfFreeDataAfterLoad)) {
-		debug("ResourceSystem::loadResource() kRlfFreeDataAfterLoad");
+		debug(1, "ResourceSystem::loadResource() kRlfFreeDataAfterLoad");
 		resource->unloadData();
 	}
 	
 	resource->_loaded = true;
 
 	_resources.push_back(resource);
-	// TODO? Not sure if this is needed krnfileAdd(filenameb, taga);
 
 }
 
@@ -150,7 +138,7 @@ Resource *ResourceSystem::getResource(uint32 resId) {
 }
 
 void ResourceSystem::unloadResource(Resource *resource) {
-	debug("Unloading %08X... (tag: %08X)", resource->_resId, resource->_tag);
+	debug(1, "Unloading %08X... (tag: %08X)", resource->_resId, resource->_tag);
 	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByValue(resource));
 	if (it != _resources.end())
 		_resources.remove_at(it - _resources.begin());
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index edf1cdd..a0c2c06 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -36,6 +36,7 @@ namespace Illusions {
 #define ResourceTypeId(x) ((x) & 0xFFFF0000)
 
 class BaseResourceLoader;
+class BaseResourceReader;
 class IllusionsEngine;
 struct Resource;
 
@@ -66,7 +67,7 @@ struct Resource {
 		delete _instance;
 		unloadData();
 	}
-	void loadData();
+	void loadData(BaseResourceReader *resReader);
 	void unloadData();
 };
 
@@ -79,7 +80,6 @@ class BaseResourceLoader {
 public:
 	virtual ~BaseResourceLoader() {}
 	virtual void load(Resource *resource) = 0;
-	virtual void buildFilename(Resource *resource) = 0;
 	virtual bool isFlag(int flag) = 0;
 };
 


Commit: 61a0b5badbd7098e0b96355a428408654b3705df
    https://github.com/scummvm/scummvm/commit/61a0b5badbd7098e0b96355a428408654b3705df
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Rename tag -> sceneId

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/gamresourcereader.cpp
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/actorresource.h
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/resources/backgroundresource.h
    engines/illusions/resources/talkresource.cpp
    engines/illusions/resources/talkresource.h
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h
    engines/illusions/thread.cpp
    engines/illusions/thread.h
    engines/illusions/threads/abortablethread.cpp
    engines/illusions/threads/causethread_duckman.cpp
    engines/illusions/threads/scriptthread.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp
    engines/illusions/threads/timerthread.cpp
    engines/illusions/updatefunctions.cpp
    engines/illusions/updatefunctions.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index cf3416a..bae77c7 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -212,7 +212,7 @@ Control::Control(IllusionsEngine *vm)
 	_position.y = 0;
 	_actorTypeId = 0;
 	_actor = 0;
-	_tag = _vm->getCurrentScene();
+	_sceneId = _vm->getCurrentScene();
 }
 
 Control::~Control() {
@@ -1188,10 +1188,10 @@ void Controls::destroyActiveControls() {
 	}
 }
 
-void Controls::destroyControlsByTag(uint32 tag) {
+void Controls::destroyControlsBySceneId(uint32 sceneId) {
 	ItemsIterator it = _controls.begin();
 	while (it != _controls.end()) {
-		if ((*it)->_tag == tag) {
+		if ((*it)->_sceneId == sceneId) {
 			destroyControlInternal(*it);
 			it = _controls.erase(it);
 		} else
@@ -1239,10 +1239,10 @@ void Controls::unpauseControls() {
 	}
 }
 
-void Controls::pauseControlsByTag(uint32 tag) {
+void Controls::pauseControlsBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *control = *it;
-		if (control->_tag == tag) {
+		if (control->_sceneId == sceneId) {
 			++control->_pauseCtr;
 			if (control->_pauseCtr == 1)
 				control->pause();
@@ -1250,10 +1250,10 @@ void Controls::pauseControlsByTag(uint32 tag) {
 	}
 }
 
-void Controls::unpauseControlsByTag(uint32 tag) {
+void Controls::unpauseControlsBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
 		Control *control = *it;
-		if (control->_tag == tag) {
+		if (control->_sceneId == sceneId) {
 			--control->_pauseCtr;
 			if (control->_pauseCtr == 0)
 				control->unpause();
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index c4ae052..295a0b5 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -207,7 +207,7 @@ public:
 	int16 _priority;
 	Actor *_actor;
 	//field_6 dw
-	uint32 _tag;
+	uint32 _sceneId;
 	uint32 _objectId;
 	uint32 _actorTypeId;
 	// TODO Move points into own struct
@@ -233,13 +233,13 @@ public:
 	void placeDialogItem(uint16 objectNum, uint32 actorTypeId, uint32 sequenceId, Common::Point placePt, int16 choiceJumpOffs);
 	void destroyControls();
 	void destroyActiveControls();
-	void destroyControlsByTag(uint32 tag);
+	void destroyControlsBySceneId(uint32 sceneId);
 	void destroyDialogItems();
 	void threadIsDead(uint32 threadId);
 	void pauseControls();
 	void unpauseControls();
-	void pauseControlsByTag(uint32 tag);
-	void unpauseControlsByTag(uint32 tag);
+	void pauseControlsBySceneId(uint32 sceneId);
+	void unpauseControlsBySceneId(uint32 sceneId);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
 	bool getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl);
 	bool getOverlappedWalkObject(Control *control, Common::Point pt, Control **outOverlappedControl);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index a7dadc4..5257a48 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -73,7 +73,7 @@ void CauseThread_BBDOU::onTerminated() {
 RadarMicrophoneThread::RadarMicrophoneThread(IllusionsEngine_BBDOU *vm, uint32 threadId,
 	uint32 callingThreadId, uint32 cursorObjectId)
 	: Thread(vm, threadId, callingThreadId, 0), _cursorObjectId(cursorObjectId), _zonesCount(0) {
-	_tag = _vm->getCurrentScene();
+	_sceneId = _vm->getCurrentScene();
 }
 
 int RadarMicrophoneThread::onUpdate() {
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 98a4a24..9c7e019 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -260,8 +260,8 @@ void IllusionsEngine_BBDOU::initInput() {
 		.addKey(Common::KEYCODE_DOWN);
 }
 
-#define UPDATEFUNCTION(priority, tag, callback) \
-	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
+#define UPDATEFUNCTION(priority, sceneId, callback) \
+	_updateFunctions->add(priority, sceneId, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
 		(this, &IllusionsEngine_BBDOU::callback));
 
 void IllusionsEngine_BBDOU::initUpdateFunctions() {
@@ -516,30 +516,30 @@ bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
 void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
 	uint32 sceneId = _activeScenes.getCurrentScene();
 	// TODO krnfileDump(sceneId);
-	// TODO UpdateFunctions_disableByTag__TODO_maybe(sceneId);
-	_threads->terminateThreadsByTag(sceneId, threadId);
-	_controls->destroyControlsByTag(sceneId);
+	// TODO UpdateFunctions_disableBySceneId__TODO_maybe(sceneId);
+	_threads->terminateThreadsBySceneId(sceneId, threadId);
+	_controls->destroyControlsBySceneId(sceneId);
 	_triggerFunctions->removeBySceneId(sceneId);
-	_resSys->unloadResourcesByTag(sceneId);
+	_resSys->unloadResourcesBySceneId(sceneId);
 	_activeScenes.pop();
 }
 
 void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
 	uint32 sceneId = _activeScenes.getCurrentScene();
 	_camera->pushCameraMode();
-	_threads->suspendThreadsByTag(sceneId, threadId);
-	_controls->pauseControlsByTag(sceneId);
-	_actorInstances->pauseByTag(sceneId);
-	_backgroundInstances->pauseByTag(sceneId);
+	_threads->suspendThreadsBySceneId(sceneId, threadId);
+	_controls->pauseControlsBySceneId(sceneId);
+	_actorInstances->pauseBySceneId(sceneId);
+	_backgroundInstances->pauseBySceneId(sceneId);
 	_activeScenes.pauseActiveScene();
 }
 
 void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
 	uint32 sceneId = _activeScenes.getCurrentScene();
-	_backgroundInstances->unpauseByTag(sceneId);
-	_actorInstances->unpauseByTag(sceneId);
-	_controls->unpauseControlsByTag(sceneId);
-	_threads->notifyThreadsByTag(sceneId, threadId);
+	_backgroundInstances->unpauseBySceneId(sceneId);
+	_actorInstances->unpauseBySceneId(sceneId);
+	_controls->unpauseControlsBySceneId(sceneId);
+	_threads->notifyThreadsBySceneId(sceneId, threadId);
 	_camera->popCameraMode();
 	_activeScenes.unpauseActiveScene();
 }
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index efdc289..945892f 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -279,7 +279,7 @@ void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &
 	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardAllEvents();
 	_vm->enterPause(opCall._callerThreadId);
-	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->_talkItems->pauseBySceneId(_vm->getCurrentScene());
 	_vm->enterScene(sceneId, opCall._callerThreadId);
 	_vm->startScriptThread(threadId, 0,
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
@@ -291,7 +291,7 @@ void ScriptOpcodes_BBDOU::opExitModalScene(ScriptThread *scriptThread, OpCall &o
 	_vm->_input->discardAllEvents();
 	_vm->exitScene(opCall._callerThreadId);
 	_vm->leavePause(opCall._callerThreadId);
-	_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+	_vm->_talkItems->unpauseBySceneId(_vm->getCurrentScene());
 }
 
 void ScriptOpcodes_BBDOU::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 041e437..3a6514f 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -233,8 +233,8 @@ void IllusionsEngine_Duckman::initInput() {
 		.addKey(Common::KEYCODE_DOWN);
 }
 
-#define UPDATEFUNCTION(priority, tag, callback) \
-	_updateFunctions->add(priority, tag, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
+#define UPDATEFUNCTION(priority, sceneId, callback) \
+	_updateFunctions->add(priority, sceneId, new Common::Functor1Mem<uint, int, IllusionsEngine_Duckman> \
 		(this, &IllusionsEngine_Duckman::callback));
 
 void IllusionsEngine_Duckman::initUpdateFunctions() {
@@ -760,13 +760,13 @@ bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint3
 void IllusionsEngine_Duckman::enterPause(uint32 sceneId, uint32 threadId) {
 	_threads->suspendThreads(threadId);
 	_controls->pauseControls();
-	_actorInstances->pauseByTag(sceneId);
-	_backgroundInstances->pauseByTag(sceneId);
+	_actorInstances->pauseBySceneId(sceneId);
+	_backgroundInstances->pauseBySceneId(sceneId);
 }
 
 void IllusionsEngine_Duckman::leavePause(uint32 sceneId, uint32 threadId) {
-	_backgroundInstances->unpauseByTag(sceneId);
-	_actorInstances->unpauseByTag(sceneId);
+	_backgroundInstances->unpauseBySceneId(sceneId);
+	_actorInstances->unpauseBySceneId(sceneId);
 	_controls->unpauseControls();
 	_threads->notifyThreads(threadId);
 }
@@ -776,11 +776,11 @@ void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId)
 }
 
 void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
-	// TODO UpdateFunctions_disableByTag(sceneId);
+	// TODO UpdateFunctions_disableBySceneId(sceneId);
 	_threads->terminateActiveThreads(threadId);
-	_threads->terminateThreadsByTag(sceneId, threadId);
+	_threads->terminateThreadsBySceneId(sceneId, threadId);
 	_controls->destroyActiveControls();
-	_resSys->unloadResourcesByTag(sceneId);
+	_resSys->unloadResourcesBySceneId(sceneId);
 }
 
 void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 107d898..09bf8c6 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -294,7 +294,7 @@ void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall
 	ARG_UINT32(threadId);
 	_vm->_input->discardAllEvents();
 	_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
-	_vm->_talkItems->pauseByTag(_vm->getCurrentScene());
+	_vm->_talkItems->pauseBySceneId(_vm->getCurrentScene());
 	_vm->enterScene(sceneId, threadId);
 	opCall._result = kTSSuspend;
 }
@@ -308,7 +308,7 @@ void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall
 		_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
 		_vm->exitScene();
 		_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
-		_vm->_talkItems->unpauseByTag(_vm->getCurrentScene());
+		_vm->_talkItems->unpauseBySceneId(_vm->getCurrentScene());
 	}
 }
 
diff --git a/engines/illusions/gamresourcereader.cpp b/engines/illusions/gamresourcereader.cpp
index 4b4f3e0..18d16be 100644
--- a/engines/illusions/gamresourcereader.cpp
+++ b/engines/illusions/gamresourcereader.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "illusions/resourcesystem.h"
-#include "illusions/resourcereader_gamarchive.h"
+#include "illusions/gamresourcereader.h"
 #include "illusions/gamarchive.h"
 #include "illusions/illusions.h"
 #include "common/file.h"
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index b93cf0f..8c65626 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -180,7 +180,7 @@ ActorInstance::ActorInstance(IllusionsEngine *vm)
 void ActorInstance::load(Resource *resource) {
 	_actorResource = new ActorResource();
 	_actorResource->load(resource);
-	_tag = resource->_tag;
+	_sceneId = resource->_sceneId;
 	_pauseCtr = 0;
 	initActorTypes();
 	registerResources();
@@ -260,15 +260,15 @@ void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
 	_items.remove(actorInstance);
 }
 
-void ActorInstanceList::pauseByTag(uint32 tag) {
+void ActorInstanceList::pauseBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
+		if ((*it)->_sceneId == sceneId)
 			(*it)->pause();
 }
 
-void ActorInstanceList::unpauseByTag(uint32 tag) {
+void ActorInstanceList::unpauseBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
+		if ((*it)->_sceneId == sceneId)
 			(*it)->unpause();
 }
 
diff --git a/engines/illusions/resources/actorresource.h b/engines/illusions/resources/actorresource.h
index b78e515..c24213d 100644
--- a/engines/illusions/resources/actorresource.h
+++ b/engines/illusions/resources/actorresource.h
@@ -101,7 +101,7 @@ public:
 	virtual void unpause();
 public:
 	IllusionsEngine *_vm;
-	uint32 _tag;
+	uint32 _sceneId;
 	int _pauseCtr;
 	ActorResource *_actorResource;
 protected:
@@ -116,8 +116,8 @@ public:
 	~ActorInstanceList();
 	ActorInstance *createActorInstance(Resource *resource);
 	void removeActorInstance(ActorInstance *actorInstance);
-	void pauseByTag(uint32 tag);
-	void unpauseByTag(uint32 tag);
+	void pauseBySceneId(uint32 sceneId);
+	void unpauseBySceneId(uint32 sceneId);
 	FramesList *findSequenceFrames(Sequence *sequence);
 	ActorInstance *findActorByResource(ActorResource *actorResource);
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index e50f46b..d18d46b 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -368,7 +368,7 @@ bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt)
 // BackgroundInstance
 
 BackgroundInstance::BackgroundInstance(IllusionsEngine *vm)
-	: _vm(vm), _tag(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
+	: _vm(vm), _sceneId(0), _pauseCtr(0), _bgRes(0), _savedPalette(0) {
 }
 
 void BackgroundInstance::load(Resource *resource) {
@@ -378,7 +378,7 @@ void BackgroundInstance::load(Resource *resource) {
 	backgroundResource->load(resource->_data, resource->_dataSize);
 
 	_bgRes = backgroundResource;
-	_tag = resource->_tag;
+	_sceneId = resource->_sceneId;
 	initSurface();
 	
 	// Insert background objects
@@ -549,15 +549,15 @@ void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgr
 	_items.remove(backgroundInstance);
 }
 
-void BackgroundInstanceList::pauseByTag(uint32 tag) {
+void BackgroundInstanceList::pauseBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
+		if ((*it)->_sceneId == sceneId)
 			(*it)->pause();
 }
 
-void BackgroundInstanceList::unpauseByTag(uint32 tag) {
+void BackgroundInstanceList::unpauseBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
+		if ((*it)->_sceneId == sceneId)
 			(*it)->unpause();
 }
 
diff --git a/engines/illusions/resources/backgroundresource.h b/engines/illusions/resources/backgroundresource.h
index d118c3d..d0c0c76 100644
--- a/engines/illusions/resources/backgroundresource.h
+++ b/engines/illusions/resources/backgroundresource.h
@@ -190,7 +190,7 @@ public:
 	virtual void unpause();
 public:
 	IllusionsEngine *_vm;
-	uint32 _tag;
+	uint32 _sceneId;
 	int _pauseCtr;
 	BackgroundResource *_bgRes;
 	Common::Point _panPoints[kMaxBackgroundItemSurfaces];
@@ -212,8 +212,8 @@ public:
 	~BackgroundInstanceList();
 	BackgroundInstance *createBackgroundInstance(Resource *resource);
 	void removeBackgroundInstance(BackgroundInstance *backgroundInstance);
-	void pauseByTag(uint32 tag);
-	void unpauseByTag(uint32 tag);
+	void pauseBySceneId(uint32 sceneId);
+	void unpauseBySceneId(uint32 sceneId);
 	BackgroundInstance *findActiveBackgroundInstance();
 	BackgroundInstance *findBackgroundByResource(BackgroundResource *backgroundResource);
 	BackgroundResource *getActiveBgResource();
diff --git a/engines/illusions/resources/talkresource.cpp b/engines/illusions/resources/talkresource.cpp
index 9b03b37..a75c370 100644
--- a/engines/illusions/resources/talkresource.cpp
+++ b/engines/illusions/resources/talkresource.cpp
@@ -85,7 +85,7 @@ void TalkInstance::load(Resource *resource) {
 	talkResource->load(resource->_data, resource->_dataSize);
 	_talkRes = talkResource;
 	_talkId = resource->_resId;
-	_tag = resource->_tag;
+	_sceneId = resource->_sceneId;
 	registerResources();
 }
 
@@ -148,21 +148,21 @@ TalkInstance *TalkInstanceList::findTalkItem(uint32 talkId) {
 	return 0;
 }
 
-TalkInstance *TalkInstanceList::findTalkItemByTag(uint32 tag) {
+TalkInstance *TalkInstanceList::findTalkItemBySceneId(uint32 sceneId) {
 	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
-		if ((*it)->_tag == tag)
+		if ((*it)->_sceneId == sceneId)
 			return (*it);
 	return 0;
 }
 
-void TalkInstanceList::pauseByTag(uint32 tag) {
-	TalkInstance *talkInstance = findTalkItemByTag(tag);
+void TalkInstanceList::pauseBySceneId(uint32 sceneId) {
+	TalkInstance *talkInstance = findTalkItemBySceneId(sceneId);
 	if (talkInstance)
 		talkInstance->pause();
 }
 
-void TalkInstanceList::unpauseByTag(uint32 tag) {
-	TalkInstance *talkInstance = findTalkItemByTag(tag);
+void TalkInstanceList::unpauseBySceneId(uint32 sceneId) {
+	TalkInstance *talkInstance = findTalkItemBySceneId(sceneId);
 	if (talkInstance)
 		talkInstance->unpause();
 }
diff --git a/engines/illusions/resources/talkresource.h b/engines/illusions/resources/talkresource.h
index a29e58c..ea38aa7 100644
--- a/engines/illusions/resources/talkresource.h
+++ b/engines/illusions/resources/talkresource.h
@@ -69,7 +69,7 @@ public:
 public:
 	IllusionsEngine *_vm;
 	uint32 _talkId;
-	uint32 _tag;
+	uint32 _sceneId;
 	TalkResource *_talkRes;
 	int _pauseCtr;
 	void registerResources();
@@ -83,9 +83,9 @@ public:
 	TalkInstance *createTalkInstance(Resource *resource);
 	void removeTalkInstance(TalkInstance *talkInstance);
 	TalkInstance *findTalkItem(uint32 talkId);
-	TalkInstance *findTalkItemByTag(uint32 tag);
-	void pauseByTag(uint32 tag);
-	void unpauseByTag(uint32 tag);
+	TalkInstance *findTalkItemBySceneId(uint32 sceneId);
+	void pauseBySceneId(uint32 sceneId);
+	void unpauseBySceneId(uint32 sceneId);
 //protected:
 public:
 	typedef Common::List<TalkInstance*> Items;
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index 13e4da6..4f6644f 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -49,7 +49,7 @@ ResourceInstance::~ResourceInstance() {
 // Resource
 
 void Resource::loadData(BaseResourceReader *resReader) {
-	_data = resReader->readResource(_tag, _resId, _dataSize);
+	_data = resReader->readResource(_sceneId, _resId, _dataSize);
 }
 
 void Resource::unloadData() {
@@ -74,14 +74,14 @@ void ResourceSystem::addResourceLoader(uint32 resTypeId, BaseResourceLoader *res
 	_resourceLoaders[resTypeId] = resourceLoader;
 }
 
-void ResourceSystem::loadResource(uint32 resId, uint32 tag, uint32 threadId) {
-	debug(1, "ResourceSystem::loadResource(%08X, %08X, %08X)", resId, tag, threadId);
+void ResourceSystem::loadResource(uint32 resId, uint32 sceneId, uint32 threadId) {
+	debug(1, "ResourceSystem::loadResource(%08X, %08X, %08X)", resId, sceneId, threadId);
 	BaseResourceLoader *resourceLoader = getResourceLoader(resId);
 
 	Resource *resource = new Resource();
 	resource->_loaded = false;
 	resource->_resId = resId;
-	resource->_tag = tag;
+	resource->_sceneId = sceneId;
 	resource->_threadId = threadId;
 	resource->_gameId = _vm->getGameId();
 
@@ -109,11 +109,11 @@ void ResourceSystem::unloadResourceById(uint32 resId) {
 		unloadResource(resource);
 }
 
-void ResourceSystem::unloadResourcesByTag(uint32 tag) {
-	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByTag(tag));
+void ResourceSystem::unloadResourcesBySceneId(uint32 sceneId) {
+	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualBySceneId(sceneId));
 	while (it != _resources.end()) {
 		unloadResource(*it);
-		it = Common::find_if(it, _resources.end(), ResourceEqualByTag(tag));
+		it = Common::find_if(it, _resources.end(), ResourceEqualBySceneId(sceneId));
 	}
 }
 
@@ -138,7 +138,7 @@ Resource *ResourceSystem::getResource(uint32 resId) {
 }
 
 void ResourceSystem::unloadResource(Resource *resource) {
-	debug(1, "Unloading %08X... (tag: %08X)", resource->_resId, resource->_tag);
+	debug(1, "Unloading %08X... (sceneId: %08X)", resource->_resId, resource->_sceneId);
 	ResourcesArrayIterator it = Common::find_if(_resources.begin(), _resources.end(), ResourceEqualByValue(resource));
 	if (it != _resources.end())
 		_resources.remove_at(it - _resources.begin());
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index a0c2c06..b761b94 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -52,14 +52,14 @@ public:
 struct Resource {
 	bool _loaded;
 	uint32 _resId;
-	uint32 _tag;
+	uint32 _sceneId;
 	uint32 _threadId;
 	byte *_data;
 	uint32 _dataSize;
 	int _gameId;
 	Common::String _filename;
 	ResourceInstance *_instance;
-	Resource() : _loaded(false), _resId(0), _tag(0), _threadId(0), _data(0), _dataSize(0), _instance(0) {
+	Resource() : _loaded(false), _resId(0), _sceneId(0), _threadId(0), _data(0), _dataSize(0), _instance(0) {
 	}
 	~Resource() {
 		if (_instance)
@@ -93,9 +93,9 @@ public:
 	void addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader);
 	
 	// TODO Handle threadId in caller as well as pausing of timer
-	void loadResource(uint32 resId, uint32 tag, uint32 threadId);
+	void loadResource(uint32 resId, uint32 sceneId, uint32 threadId);
 	void unloadResourceById(uint32 resId);
-	void unloadResourcesByTag(uint32 tag);
+	void unloadResourcesBySceneId(uint32 sceneId);
 	void unloadSceneResources(uint32 sceneId1, uint32 sceneId2);
 	
 protected:
@@ -125,11 +125,11 @@ protected:
 		}
 	};
 
-	struct ResourceEqualByTag : public Common::UnaryFunction<const Resource*, bool> {
-		uint32 _tag;
-		ResourceEqualByTag(uint32 tag) : _tag(tag) {}
+	struct ResourceEqualBySceneId : public Common::UnaryFunction<const Resource*, bool> {
+		uint32 _sceneId;
+		ResourceEqualBySceneId(uint32 sceneId) : _sceneId(sceneId) {}
 		bool operator()(const Resource *resource) const {
-			return resource->_tag == _tag;
+			return resource->_sceneId == _sceneId;
 		}
 	};
 
@@ -137,7 +137,7 @@ protected:
 		uint32 _sceneId1, _sceneId2;
 		ResourceNotEqualByScenes(uint32 sceneId1, uint32 sceneId2) : _sceneId1(sceneId1), _sceneId2(sceneId2) {}
 		bool operator()(const Resource *resource) const {
-			return resource->_tag != _sceneId1 && resource->_tag != _sceneId2;
+			return resource->_sceneId != _sceneId1 && resource->_sceneId != _sceneId2;
 		}
 	};
 
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 941706a..d12d11a 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -126,7 +126,7 @@ ThreadList::ThreadList(IllusionsEngine *vm)
 }
 
 void ThreadList::startThread(Thread *thread) {
-	// TODO tag has to be set by the Thread class
+	// TODO sceneId has to be set by the Thread class
 	_threads.push_back(thread);
 }
 
@@ -206,18 +206,18 @@ void ThreadList::terminateActiveThreads(uint32 threadId) {
 	}
 }
 
-void ThreadList::terminateThreadsByTag(uint32 tag, uint32 threadId) {
+void ThreadList::terminateThreadsBySceneId(uint32 sceneId, uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
-		if (thread->_tag == tag && thread->_threadId != threadId)
+		if (thread->_sceneId == sceneId && thread->_threadId != threadId)
 			thread->terminate();
 	}
 }
 
-void ThreadList::suspendThreadsByTag(uint32 tag, uint32 threadId) {
+void ThreadList::suspendThreadsBySceneId(uint32 sceneId, uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
-		if (thread->_tag == tag && thread->_threadId != threadId)
+		if (thread->_sceneId == sceneId && thread->_threadId != threadId)
 			thread->suspend();
 	}
 }
@@ -230,10 +230,10 @@ void ThreadList::notifyThreads(uint32 threadId) {
 	}
 }
 
-void ThreadList::notifyThreadsByTag(uint32 tag, uint32 threadId) {
+void ThreadList::notifyThreadsBySceneId(uint32 sceneId, uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
-		if (thread->_tag == tag && thread->_threadId != threadId)
+		if (thread->_sceneId == sceneId && thread->_threadId != threadId)
 			thread->notify();
 	}
 }
@@ -308,12 +308,12 @@ void ThreadList::killThread(uint32 threadId) {
 void ThreadList::setThreadSceneId(uint32 threadId, uint32 sceneId) {
 	Thread *thread = findThread(threadId);
 	if (thread)
-		thread->_tag = sceneId;
+		thread->_sceneId = sceneId;
 }
 
 uint32 ThreadList::getThreadSceneId(uint32 threadId) {
 	Thread *thread = findThread(threadId);
-	return thread ? thread->_tag : 0;
+	return thread ? thread->_sceneId : 0;
 }
 
 bool ThreadList::isActiveThread(int msgNum) {
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index 996226d..cde2a41 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -72,7 +72,7 @@ public:
 	uint _type;
 	uint32 _threadId;
 	uint32 _callingThreadId;
-	uint32 _tag;
+	uint32 _sceneId;
 	uint _notifyFlags;
 };
 
@@ -88,10 +88,10 @@ public:
 	void suspendTimerThreads(uint32 callingThreadId);
 	void terminateThreads(uint32 threadId);
 	void terminateActiveThreads(uint32 threadId);
-	void terminateThreadsByTag(uint32 tag, uint32 threadId);
-	void suspendThreadsByTag(uint32 tag, uint32 threadId);
+	void terminateThreadsBySceneId(uint32 sceneId, uint32 threadId);
+	void suspendThreadsBySceneId(uint32 sceneId, uint32 threadId);
 	void notifyThreads(uint32 threadId);
-	void notifyThreadsByTag(uint32 tag, uint32 threadId);
+	void notifyThreadsBySceneId(uint32 sceneId, uint32 threadId);
 	void pauseThreads(uint32 threadId);
 	void suspendThreads(uint32 threadId);
 	void resumeThreads(uint32 threadId);
diff --git a/engines/illusions/threads/abortablethread.cpp b/engines/illusions/threads/abortablethread.cpp
index b06debe..acbc1ad 100644
--- a/engines/illusions/threads/abortablethread.cpp
+++ b/engines/illusions/threads/abortablethread.cpp
@@ -34,7 +34,7 @@ AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 ca
 	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId), 
 	_scriptCodeIp(scriptCodeIp), _status(1) {
 	_type = kTTAbortableThread;
-	_tag = _vm->getCurrentScene();
+	_sceneId = _vm->getCurrentScene();
 	_vm->_input->discardEvent(kEventAbort);
 }
 
diff --git a/engines/illusions/threads/causethread_duckman.cpp b/engines/illusions/threads/causethread_duckman.cpp
index ee5fe75..8fb8364 100644
--- a/engines/illusions/threads/causethread_duckman.cpp
+++ b/engines/illusions/threads/causethread_duckman.cpp
@@ -33,19 +33,19 @@ CauseThread_Duckman::CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 thr
 	uint32 triggerThreadId)
 	: Thread(vm, threadId, callingThreadId, notifyFlags), _vm(vm), _triggerThreadId(triggerThreadId), _flag(false) {
 	_type = kTTCauseThread;
-	_tag = _vm->getCurrentScene();
+	_sceneId = _vm->getCurrentScene();
 }
 
 int CauseThread_Duckman::onUpdate() {
 	if (_flag) {
-		if (_vm->getCurrentScene() == _tag) {
+		if (_vm->getCurrentScene() == _sceneId) {
 			Control *cursorCursor = _vm->getObjectControl(0x40004);
 			cursorCursor->appearActor();
 			_vm->_input->discardEvent(kEventLeftClick);
 		}
 		return kTSTerminate;
 	} else {
-		_tag = _vm->getCurrentScene();
+		_sceneId = _vm->getCurrentScene();
 		Control *cursorCursor = _vm->getObjectControl(0x40004);
 		cursorCursor->disappearActor();
 		_vm->_input->discardEvent(kEventLeftClick);
diff --git a/engines/illusions/threads/scriptthread.cpp b/engines/illusions/threads/scriptthread.cpp
index f8bbae9..8fe3cdd 100644
--- a/engines/illusions/threads/scriptthread.cpp
+++ b/engines/illusions/threads/scriptthread.cpp
@@ -33,7 +33,7 @@ ScriptThread::ScriptThread(IllusionsEngine *vm, uint32 threadId, uint32 callingT
 	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptCodeIp(scriptCodeIp), _value8(value8),
 	_valueC(valueC), _value10(value10), _sequenceStalled(0) {
 	_type = kTTScriptThread;
-	_tag = _vm->getCurrentScene();
+	_sceneId = _vm->getCurrentScene();
 }
 
 int ScriptThread::onUpdate() {
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 7adf9e5..930b83c 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -77,7 +77,7 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 	if (callingThreadId) {
 		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
 		if (callingThread)
-			_tag = callingThread->_tag;
+			_sceneId = callingThread->_sceneId;
 	}
 
 	//debug("New talk thread: %08X %08X", _threadId, _talkId);
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 6d8224e..9b7ca4e 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -60,7 +60,7 @@ TalkThread_Duckman::TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threa
 	_textDuration = _durationMult;
 	_defDurationMult = _vm->clipTextDuration(240);
 	
-	_tag = _vm->getCurrentScene();
+	_sceneId = _vm->getCurrentScene();
 
 }
 
diff --git a/engines/illusions/threads/timerthread.cpp b/engines/illusions/threads/timerthread.cpp
index 5842098..417d113 100644
--- a/engines/illusions/threads/timerthread.cpp
+++ b/engines/illusions/threads/timerthread.cpp
@@ -39,7 +39,7 @@ TimerThread::TimerThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThr
 	if (callingThreadId) {
 		Thread *callingThread = _vm->_threads->findThread(callingThreadId);
 		if (callingThread)
-			_tag = callingThread->_tag;
+			_sceneId = callingThread->_sceneId;
 	}
 
 }
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index 6e6cf52..cfd2c44 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -38,10 +38,10 @@ UpdateFunctions::~UpdateFunctions() {
 		delete *it;
 }
 
-void UpdateFunctions::add(int priority, uint32 tag, UpdateFunctionCallback *callback) {
+void UpdateFunctions::add(int priority, uint32 sceneId, UpdateFunctionCallback *callback) {
 	UpdateFunction *updateFunction = new UpdateFunction();
 	updateFunction->_priority = priority;
-	updateFunction->_tag = tag;
+	updateFunction->_sceneId = sceneId;
 	updateFunction->_callback = callback;
 	UpdateFunctionListIterator insertionPos = Common::find_if(_updateFunctions.begin(), _updateFunctions.end(),
 		FindInsertionPosition(priority));
@@ -73,7 +73,7 @@ void UpdateFunctions::update() {
 void UpdateFunctions::terminateByScene(uint32 sceneId) {
 	UpdateFunctionListIterator it = _updateFunctions.begin();
 	while (it != _updateFunctions.end())
-		if ((*it)->_tag == sceneId)
+		if ((*it)->_sceneId == sceneId)
 			(*it)->terminate();
 }
 
diff --git a/engines/illusions/updatefunctions.h b/engines/illusions/updatefunctions.h
index 877bbc1..b89b2a8 100644
--- a/engines/illusions/updatefunctions.h
+++ b/engines/illusions/updatefunctions.h
@@ -38,10 +38,10 @@ typedef Common::Functor1<uint, int> UpdateFunctionCallback;
 class UpdateFunction {
 public:
 	int _priority;
-	uint32 _tag;
+	uint32 _sceneId;
 	uint _flags;
 	UpdateFunctionCallback *_callback;
-	UpdateFunction() : _priority(0), _tag(0), _flags(0), _callback(0) {}
+	UpdateFunction() : _priority(0), _sceneId(0), _flags(0), _callback(0) {}
 	~UpdateFunction() { delete _callback; }
 	void terminate() { _flags |= 1; }
 	int run() { return (*_callback)(_flags); }
@@ -51,7 +51,7 @@ class UpdateFunctions {
 public:
 	UpdateFunctions();
 	~UpdateFunctions();
-	void add(int priority, uint32 tag, UpdateFunctionCallback *callback);
+	void add(int priority, uint32 sceneId, UpdateFunctionCallback *callback);
 	void update();
 	void terminateByScene(uint32 sceneId);
 protected:


Commit: 8b48f2f63f5cc21d864c3e960ce307b50286b64b
    https://github.com/scummvm/scummvm/commit/8b48f2f63f5cc21d864c3e960ce307b50286b64b
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Rename ProgInfo -> SceneInfo

Changed paths:
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/resources/scriptresource.cpp
    engines/illusions/resources/scriptresource.h


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 9c7e019..b872a3c 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -504,13 +504,13 @@ uint32 IllusionsEngine_BBDOU::newTempThreadId() {
 }
 
 bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (!progInfo) {
+	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
+	if (!sceneInfo) {
 		dumpActiveScenes(_globalSceneId, threadId);
 		sceneId = _theSceneId;
 	}
 	_activeScenes.push(sceneId);
-	return progInfo != 0;
+	return sceneInfo != 0;
 }
 
 void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
@@ -563,9 +563,9 @@ void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThre
 }
 
 bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (progInfo)
-		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
+	if (sceneInfo)
+		return sceneInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
 	return false;
 }
 
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 3a6514f..86cd068 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -717,13 +717,13 @@ void IllusionsEngine_Duckman::popActiveScene() {
 }
 
 bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (!progInfo)
+	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
+	if (!sceneInfo)
 		return false;
 	pushActiveScene(sceneId);
 	uint resourcesCount;
 	uint32 *resources;
-	progInfo->getResources(resourcesCount, resources);
+	sceneInfo->getResources(resourcesCount, resources);
 	for (uint i = 0; i < resourcesCount; ++i)
 		_resSys->loadResource(resources[i], sceneId, 0);
 	return true;
@@ -789,9 +789,9 @@ void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theTh
 }
 
 bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(sceneId & 0xFFFF);
-	if (progInfo)
-		return progInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
+	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
+	if (sceneInfo)
+		return sceneInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
 	return false;
 }
 
@@ -993,15 +993,15 @@ void IllusionsEngine_Duckman::playSoundEffect(int index) {
 }
 
 bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId) {
-	ProgInfo *progInfo = _scriptResource->getProgInfo(getCurrentScene() & 0xFFFF);
+	SceneInfo *sceneInfo = _scriptResource->getSceneInfo(getCurrentScene() & 0xFFFF);
 	bool found =
-		progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
-		progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
+		sceneInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
+		sceneInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
 	if (!found) {
-		progInfo = _scriptResource->getProgInfo(3);
+		sceneInfo = _scriptResource->getSceneInfo(3);
 		found =
-			progInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
-			progInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
+			sceneInfo->findTriggerCause(verbId, objectId2, objectId, outThreadId) ||
+			sceneInfo->findTriggerCause(verbId, objectId2, 0x40001, outThreadId);
 	}
 	return found;
 }
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index 5ce7154..3038a86 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -164,19 +164,19 @@ bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &co
 	return false;
 }
 
-void TriggerObject::fixupProgInfosDuckman() {
+void TriggerObject::fixupSceneInfosDuckman() {
 	for (uint i = 0; i < _causesCount; ++i)
 		_causes[i]._verbId &= 0xFFFF;
 }
 
-// ProgInfo
+// SceneInfo
 
-ProgInfo::ProgInfo()
+SceneInfo::SceneInfo()
 	: _triggerObjectsCount(0), _triggerObjects(0),
 	_resourcesCount(0), _resources(0) {
 }
 
-ProgInfo::~ProgInfo() {
+SceneInfo::~SceneInfo() {
 	delete[] _triggerObjects;
 	delete[] _resources;
 }
@@ -192,14 +192,14 @@ char *debugW2I(byte *wstr) {
 	return buf;
 }
 
-void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
+void SceneInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_id = stream.readUint16LE();
 	_unk = stream.readUint16LE();
 	_name = dataStart + stream.pos();
 	stream.skip(128);
 	_triggerObjectsCount = stream.readUint16LE();
 	_resourcesCount = stream.readUint16LE();
-	debug(2, "\nProgInfo::load() _id: %d; _unk: %d; _name: [%s]",
+	debug(2, "\nSceneInfo::load() _id: %d; _unk: %d; _name: [%s]",
 		_id, _unk, debugW2I(_name));
 	uint32 triggerObjectsListOffs = stream.readUint32LE();
 	if (_resourcesCount > 0) {
@@ -218,28 +218,28 @@ void ProgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	}
 }
 
-bool ProgInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
+bool SceneInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
 	TriggerObject *triggerObject = findTriggerObject(objectId);
 	if (triggerObject)
 		return triggerObject->findTriggerCause(verbId, objectId2, codeOffs);
 	return false;
 }
 
-void ProgInfo::getResources(uint &resourcesCount, uint32 *&resources) {
+void SceneInfo::getResources(uint &resourcesCount, uint32 *&resources) {
 	resourcesCount = _resourcesCount;
 	resources = _resources;
 }
 
-TriggerObject *ProgInfo::findTriggerObject(uint32 objectId) {
+TriggerObject *SceneInfo::findTriggerObject(uint32 objectId) {
 	for (uint i = 0; i < _triggerObjectsCount; ++i)
 		if (_triggerObjects[i]._objectId == objectId)
 			return &_triggerObjects[i];
 	return 0;
 }
 
-void ProgInfo::fixupProgInfosDuckman() {
+void SceneInfo::fixupSceneInfosDuckman() {
 	for (uint i = 0; i < _triggerObjectsCount; ++i)
-		_triggerObjects[i].fixupProgInfosDuckman();
+		_triggerObjects[i].fixupSceneInfosDuckman();
 }
 
 // ScriptResource
@@ -259,15 +259,15 @@ void ScriptResource::load(Resource *resource) {
 
 	Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
 	
-	uint32 objectMapOffs, progInfosOffs;
+	uint32 objectMapOffs, sceneInfosOffs;
 	_objectMapCount = 0;
 	
 	if (resource->_gameId == kGameIdBBDOU) {
-		progInfosOffs = 0x18;
+		sceneInfosOffs = 0x18;
 	} else if (resource->_gameId == kGameIdDuckman) {
 		for (uint i = 0; i < 27; ++i)
 			_soundIds[i] = stream.readUint32LE();
-		progInfosOffs = 0x8C;
+		sceneInfosOffs = 0x8C;
 	}
 	
 	stream.skip(4); // Skip unused
@@ -278,7 +278,7 @@ void ScriptResource::load(Resource *resource) {
 	if (resource->_gameId == kGameIdDuckman)
 		_objectMapCount = stream.readUint16LE();
 	_codeCount = stream.readUint16LE();
-	_progInfosCount = stream.readUint16LE();
+	_sceneInfosCount = stream.readUint16LE();
 	if (resource->_gameId == kGameIdDuckman)
 		stream.readUint16LE();//Unused?
 
@@ -289,8 +289,8 @@ void ScriptResource::load(Resource *resource) {
 		objectMapOffs = stream.readUint32LE();
 	uint32 codeTblOffs = stream.readUint32LE();
 	
-	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _progInfosCount: %d; _objectMapCount: %d",
-		propertiesCount, blockCountersCount, _codeCount, _progInfosCount, _objectMapCount);
+	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _sceneInfosCount: %d; _objectMapCount: %d",
+		propertiesCount, blockCountersCount, _codeCount, _sceneInfosCount, _objectMapCount);
 	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X; objectMapOffs: %08X",
 		propertiesOffs, blockCountersOffs, codeTblOffs, objectMapOffs);
 	// Init properties
@@ -304,12 +304,12 @@ void ScriptResource::load(Resource *resource) {
 	for (uint i = 0; i < _codeCount; ++i)
 		_codeOffsets[i] = stream.readUint32LE();
 
-	_progInfos = new ProgInfo[_progInfosCount];
-	for (uint i = 0; i < _progInfosCount; ++i) {
-		stream.seek(progInfosOffs + i * 4);
-		uint32 progInfoOffs = stream.readUint32LE();
-		stream.seek(progInfoOffs);
-		_progInfos[i].load(_data, stream);
+	_sceneInfos = new SceneInfo[_sceneInfosCount];
+	for (uint i = 0; i < _sceneInfosCount; ++i) {
+		stream.seek(sceneInfosOffs + i * 4);
+		uint32 sceneInfoOffs = stream.readUint32LE();
+		stream.seek(sceneInfoOffs);
+		_sceneInfos[i].load(_data, stream);
 	}
 	
 	if (_objectMapCount > 0) {
@@ -330,7 +330,7 @@ void ScriptResource::load(Resource *resource) {
 	}
 	
 	if (resource->_gameId == kGameIdDuckman)
-		fixupProgInfosDuckman();
+		fixupSceneInfosDuckman();
 
 }
 
@@ -342,9 +342,9 @@ byte *ScriptResource::getCode(uint32 codeOffs) {
 	return _data + codeOffs;
 }
 
-ProgInfo *ScriptResource::getProgInfo(uint32 index) {
-	if (index > 0 && index <= _progInfosCount)
-		return &_progInfos[index - 1];
+SceneInfo *ScriptResource::getSceneInfo(uint32 index) {
+	if (index > 0 && index <= _sceneInfosCount)
+		return &_sceneInfos[index - 1];
 	return 0;
 }
 
@@ -352,9 +352,9 @@ uint32 ScriptResource::getObjectActorTypeId(uint32 objectId) {
 	return _objectMap[(objectId & 0xFFFF) - 1];
 }
 
-void ScriptResource::fixupProgInfosDuckman() {
-	for (uint i = 0; i < _progInfosCount; ++i)
-		_progInfos[i].fixupProgInfosDuckman();
+void ScriptResource::fixupSceneInfosDuckman() {
+	for (uint i = 0; i < _sceneInfosCount; ++i)
+		_sceneInfos[i].fixupSceneInfosDuckman();
 }
 
 // ScriptInstance
diff --git a/engines/illusions/resources/scriptresource.h b/engines/illusions/resources/scriptresource.h
index eb58c66..6debcb2 100644
--- a/engines/illusions/resources/scriptresource.h
+++ b/engines/illusions/resources/scriptresource.h
@@ -79,21 +79,21 @@ public:
 	~TriggerObject();
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs);
-	void fixupProgInfosDuckman();
+	void fixupSceneInfosDuckman();
 public:
 	uint32 _objectId;
 	uint _causesCount;
 	TriggerCause *_causes;
 };
 
-class ProgInfo {
+class SceneInfo {
 public:
-	ProgInfo();
-	~ProgInfo();
+	SceneInfo();
+	~SceneInfo();
 	void load(byte *dataStart, Common::SeekableReadStream &stream);
 	bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void getResources(uint &resourcesCount, uint32 *&resources);
-	void fixupProgInfosDuckman();
+	void fixupSceneInfosDuckman();
 protected:
 	uint16 _id;
 	uint16 _unk;
@@ -112,7 +112,7 @@ public:
 	void load(Resource *resource);
 	byte *getThreadCode(uint32 threadId);
 	byte *getCode(uint32 codeOffs);
-	ProgInfo *getProgInfo(uint32 index);
+	SceneInfo *getSceneInfo(uint32 index);
 	uint32 getObjectActorTypeId(uint32 objectId);
 	uint32 getMainActorObjectId() const { return _mainActorObjectId; }
 public:
@@ -122,14 +122,14 @@ public:
 	BlockCounters _blockCounters;
 	uint _codeCount;
 	uint32 *_codeOffsets;
-	uint _progInfosCount;
-	ProgInfo *_progInfos;
+	uint _sceneInfosCount;
+	SceneInfo *_sceneInfos;
 	// Duckman specific
 	uint32 _soundIds[27];
 	uint _objectMapCount;
 	uint32 *_objectMap;
 	uint32 _mainActorObjectId;
-	void fixupProgInfosDuckman();
+	void fixupSceneInfosDuckman();
 };
 
 class ScriptInstance : public ResourceInstance {


Commit: 601c6f408210344c73dcf4f3bab34b493132387a
    https://github.com/scummvm/scummvm/commit/601c6f408210344c73dcf4f3bab34b493132387a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Terminate update functions on scene exit; fix bugs and use constants

Changed paths:
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/propertytimers.cpp
    engines/illusions/illusions.cpp
    engines/illusions/updatefunctions.cpp
    engines/illusions/updatefunctions.h


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index b872a3c..6ce367f 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -515,8 +515,7 @@ bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
 
 void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
 	uint32 sceneId = _activeScenes.getCurrentScene();
-	// TODO krnfileDump(sceneId);
-	// TODO UpdateFunctions_disableBySceneId__TODO_maybe(sceneId);
+	_updateFunctions->terminateByScene(sceneId);
 	_threads->terminateThreadsBySceneId(sceneId, threadId);
 	_controls->destroyControlsBySceneId(sceneId);
 	_triggerFunctions->removeBySceneId(sceneId);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 86cd068..ed165f0 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -251,7 +251,7 @@ void IllusionsEngine_Duckman::initUpdateFunctions() {
 int IllusionsEngine_Duckman::updateScript(uint flags) {
 	// TODO Some more stuff
 	_threads->updateThreads();
-	return 1;
+	return kUFNext;
 }
 
 void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId) {
@@ -589,7 +589,7 @@ void IllusionsEngine_Duckman::startCursorHoldingObject(uint32 objectId, uint32 s
 	_cursor._sequenceId2 = sequenceId;
 	_cursor._actorIndex = 7;
 	_cursor._savedActorIndex = 7;
-	_cursor._field14[_cursor._actorIndex - 1] = true;
+	_cursor._field14[6] = true;
 	_cursor._control->startSequenceActor(sequenceId, 2, 0);
 	setCursorActorIndex(_cursor._actorIndex, 1, 0);
 	_cursor._currOverlappedControl = 0;
@@ -776,7 +776,7 @@ void IllusionsEngine_Duckman::dumpActiveScenes(uint32 sceneId, uint32 threadId)
 }
 
 void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId) {
-	// TODO UpdateFunctions_disableBySceneId(sceneId);
+	_updateFunctions->terminateByScene(sceneId);
 	_threads->terminateActiveThreads(threadId);
 	_threads->terminateThreadsBySceneId(sceneId, threadId);
 	_controls->destroyActiveControls();
diff --git a/engines/illusions/duckman/propertytimers.cpp b/engines/illusions/duckman/propertytimers.cpp
index 60e5886..47d8cb4 100644
--- a/engines/illusions/duckman/propertytimers.cpp
+++ b/engines/illusions/duckman/propertytimers.cpp
@@ -81,7 +81,7 @@ bool PropertyTimers::findPropertyTimer(uint32 propertyId, PropertyTimer *&proper
 }
 
 int PropertyTimers::updatePropertyTimers(uint flags) {
-	int result = 1;
+	int result = kUFNext;
 	uint32 currTime = getCurrentTime();
 	if (_vm->_pauseCtr <= 0) {
 		if (_propertyTimersPaused) {
@@ -95,7 +95,7 @@ int PropertyTimers::updatePropertyTimers(uint flags) {
 		if (flags & 1) {
 			_propertyTimersActive = false;
 			_propertyTimersPaused = false;
-			result = 2;
+			result = kUFTerminate;
 		} else {
 			bool timersActive = false;
 			for (uint i = 0; i < kPropertyTimersCount; ++i) {
@@ -110,7 +110,7 @@ int PropertyTimers::updatePropertyTimers(uint flags) {
 			if (!timersActive) {
 				_propertyTimersActive = false;
 				_propertyTimersPaused = false;
-				result = 2;
+				result = kUFTerminate;
 			}
 		}
 	} else {
@@ -121,7 +121,7 @@ int PropertyTimers::updatePropertyTimers(uint flags) {
 			}
 			_propertyTimersPaused = true;
 		}
-		result = 1;
+		result = kUFNext;
 	}
 	return result;
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 4c8c7dc..d3eb50f 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -125,7 +125,7 @@ int IllusionsEngine::updateActors(uint flags) {
 		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_controlRoutine)
 			control->_actor->runControlRoutine(control, deltaTime);
 	}
-	return 1;
+	return kUFNext;
 }
 
 int IllusionsEngine::updateSequences(uint flags) {
@@ -136,7 +136,7 @@ int IllusionsEngine::updateSequences(uint flags) {
 			control->sequenceActor();
 		}
 	}
-	return 1;
+	return kUFNext;
 }
 
 int IllusionsEngine::updateGraphics(uint flags) {
@@ -196,18 +196,18 @@ int IllusionsEngine::updateGraphics(uint flags) {
 			_screenText->_position, priority);
 	}
 
-	return 1;
+	return kUFNext;
 }
 
 int IllusionsEngine::updateSprites(uint flags) {
 	_screen->updateSprites();
 	_screen->updatePalette();
-	return 1;
+	return kUFNext;
 }
 
 int IllusionsEngine::updateSoundMan(uint flags) {
 	_soundMan->update();
-	return 1;
+	return kUFNext;
 }
 
 int IllusionsEngine::getRandom(int max) {
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index cfd2c44..86aaf55 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -71,8 +71,7 @@ void UpdateFunctions::update() {
 }
 
 void UpdateFunctions::terminateByScene(uint32 sceneId) {
-	UpdateFunctionListIterator it = _updateFunctions.begin();
-	while (it != _updateFunctions.end())
+	for (UpdateFunctionListIterator it = _updateFunctions.begin(); it != _updateFunctions.end(); ++it)
 		if ((*it)->_sceneId == sceneId)
 			(*it)->terminate();
 }
diff --git a/engines/illusions/updatefunctions.h b/engines/illusions/updatefunctions.h
index b89b2a8..c09d95b 100644
--- a/engines/illusions/updatefunctions.h
+++ b/engines/illusions/updatefunctions.h
@@ -29,8 +29,8 @@
 namespace Illusions {
 
 enum {
-	kUFNext         = 1,  // Run next update funtion
-	kUFTerminate    = 2   // Terminate update function
+	kUFNext      = 1,  // Run next update funtion
+	kUFTerminate = 2   // Terminate update function
 };
 
 typedef Common::Functor1<uint, int> UpdateFunctionCallback;


Commit: 09bbb482a8ccdfb8e36128d40364900b99aa2a13
    https://github.com/scummvm/scummvm/commit/09bbb482a8ccdfb8e36128d40364900b99aa2a13
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcodes 70/71 for pausing/unpausing during the menu

Also change some input functions to return uint istead of byte to allow bigger bitmasks.

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/input.cpp
    engines/illusions/input.h
    engines/illusions/screen.h
    engines/illusions/thread.cpp
    engines/illusions/thread.h
    engines/illusions/threads/talkthread_duckman.cpp
    engines/illusions/threads/talkthread_duckman.h
    engines/illusions/threads/timerthread.cpp
    engines/illusions/threads/timerthread.h


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index ed165f0..7255327 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -231,6 +231,8 @@ void IllusionsEngine_Duckman::initInput() {
 	_input->setInputEvent(kEventDown, 0x80)
 		.addMouseButton(MOUSE_RIGHT_BUTTON)
 		.addKey(Common::KEYCODE_DOWN);
+	_input->setInputEvent(kEventF1, 0x100)
+		.addKey(Common::KEYCODE_F1);
 }
 
 #define UPDATEFUNCTION(priority, sceneId, callback) \
@@ -250,6 +252,16 @@ void IllusionsEngine_Duckman::initUpdateFunctions() {
 
 int IllusionsEngine_Duckman::updateScript(uint flags) {
 	// TODO Some more stuff
+
+	if (_screen->isDisplayOn() && !_screen->isFaderActive() && _pauseCtr == 0) {
+		if (_input->pollEvent(kEventAbort)) {
+			startScriptThread(0x00020342, 0);
+		} else if (_input->pollEvent(kEventF1)) {
+			debug("F1");
+			startScriptThread(0x0002033F, 0);
+		}
+	}
+
 	_threads->updateThreads();
 	return kUFNext;
 }
@@ -783,6 +795,24 @@ void IllusionsEngine_Duckman::dumpCurrSceneFiles(uint32 sceneId, uint32 threadId
 	_resSys->unloadResourcesBySceneId(sceneId);
 }
 
+void IllusionsEngine_Duckman::pause(uint32 callerThreadId) {
+	if (++_pauseCtr == 1) {
+		_threads->pauseThreads(callerThreadId);
+		_camera->pause();
+		pauseFader();
+		// TODO largeObj_pauseControlActor(0x40004);
+	}
+}
+
+void IllusionsEngine_Duckman::unpause(uint32 callerThreadId) {
+	if (--_pauseCtr == 0) {
+		// TODO largeObj_unpauseControlActor(0x40004);
+		unpauseFader();
+		_camera->unpause();
+		_threads->unpauseThreads(callerThreadId);
+	}
+}
+
 void IllusionsEngine_Duckman::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
 	_theSceneId = theSceneId;
 	_theThreadId = theThreadId;
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 5fda0d6..39c421d 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -165,6 +165,9 @@ public:
 	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
 	void dumpCurrSceneFiles(uint32 sceneId, uint32 threadId);
 
+	void pause(uint32 callerThreadId);
+	void unpause(uint32 callerThreadId);
+
 	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
 	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void reset();
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 09bf8c6..401f2e3 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -74,6 +74,8 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
 	OPCODE(25, opLeaveScene24);
+	OPCODE(26, opEnterScene26);
+	OPCODE(27, opLeaveScene26);
 	OPCODE(32, opPanCenterObject);
 	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
@@ -101,6 +103,8 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(65, opStartCursorHoldingObject);
 	OPCODE(66, opPlayVideo);
 	OPCODE(69, opRunSpecialCode);
+	OPCODE(70, opPause);
+	OPCODE(71, opUnpause);
 	OPCODE(72, opStartSound);
 	OPCODE(75, opStopSound);
 	OPCODE(76, opStartMidiMusic);
@@ -327,6 +331,14 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o
 	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
 }
 
+void ScriptOpcodes_Duckman::opEnterScene26(ScriptThread *scriptThread, OpCall &opCall) {
+	// TODO
+}
+
+void ScriptOpcodes_Duckman::opLeaveScene26(ScriptThread *scriptThread, OpCall &opCall) {
+	// TODO
+}
+
 void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(speed);
 	ARG_UINT32(objectId);
@@ -558,6 +570,14 @@ void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall
 	_vm->_specialCode->run(specialCodeId, opCall);
 }
 
+void ScriptOpcodes_Duckman::opPause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->pause(opCall._threadId);
+}
+
+void ScriptOpcodes_Duckman::opUnpause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->unpause(opCall._threadId);
+}
+
 void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(volume);
 	ARG_UINT32(soundEffectId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index abb9982..5b2f089 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -59,6 +59,8 @@ protected:
 	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterScene26(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeaveScene26(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
@@ -86,6 +88,8 @@ protected:
 	void opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opPause(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnpause(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index d3eb50f..28c678c 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -283,6 +283,16 @@ void IllusionsEngine::updateFader() {
 	}
 }
 
+void IllusionsEngine::pauseFader() {
+	_fader->_paused = true;
+	_fader->_startTime = getCurrentTime() - _fader->_startTime;
+}
+
+void IllusionsEngine::unpauseFader() {
+	_fader->_startTime = getCurrentTime() - _fader->_startTime;
+	_fader->_paused = false;
+}
+
 void IllusionsEngine::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 023a45f..b3e8141 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -161,6 +161,8 @@ public:
 	bool isSoundActive();
 
 	void updateFader();
+	void pauseFader();
+	void unpauseFader();
 
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 1251aab..3956691 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -59,8 +59,8 @@ InputEvent& InputEvent::addMouseButton(int mouseButton) {
 	return *this;
 }
 
-byte InputEvent::handle(Common::KeyCode key, int mouseButton, bool down) {
-	byte newKeys = 0;
+uint InputEvent::handle(Common::KeyCode key, int mouseButton, bool down) {
+	uint newKeys = 0;
 	for (KeyMap::iterator it = _keyMap.begin(); it != _keyMap.end(); ++it) {
 		KeyMapping &keyMapping = *it;
 		if ((keyMapping._key != Common::KEYCODE_INVALID && keyMapping._key == key) ||
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index f203a64..093adad 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -67,7 +67,7 @@ public:
 	InputEvent& setBitMask(uint bitMask);
 	InputEvent& addKey(Common::KeyCode key);
 	InputEvent& addMouseButton(int mouseButton);
-	byte handle(Common::KeyCode key, int mouseButton, bool down);
+	uint handle(Common::KeyCode key, int mouseButton, bool down);
 	uint getBitMask() const { return _bitMask; }
 protected:
 	uint _bitMask;
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index bdb7ea0..6c3e83b 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -139,6 +139,7 @@ public:
 	uint16 getColorKey2() const { return _colorKey2; }
 	int16 getScreenWidth() const { return _backSurface->w; }
 	int16 getScreenHeight() const { return _backSurface->h; }
+	bool isFaderActive() const { return _isFaderActive; }
 public:
 	IllusionsEngine *_vm;
 	bool _displayOn;
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index d12d11a..cea0b97 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -49,6 +49,9 @@ void Thread::onNotify() {
 void Thread::onPause() {
 }
 
+void Thread::onUnpause() {
+}
+
 void Thread::onResume() {
 }
 
@@ -72,6 +75,14 @@ void Thread::pause() {
 	}
 }
 
+void Thread::unpause() {
+	if (!_terminated) {
+		--_pauseCtr;
+		if (_pauseCtr == 0)
+			onUnpause();
+	}
+}
+
 void Thread::resume() {
 	if (!_terminated) {
 		--_pauseCtr;
@@ -246,6 +257,14 @@ void ThreadList::pauseThreads(uint32 threadId) {
 	}
 }
 
+void ThreadList::unpauseThreads(uint32 threadId) {
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
+		Thread *thread = *it;
+		if (thread->_threadId != threadId)
+			thread->unpause();
+	}
+}
+
 void ThreadList::suspendThreads(uint32 threadId) {
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *thread = *it;
diff --git a/engines/illusions/thread.h b/engines/illusions/thread.h
index cde2a41..20edac0 100644
--- a/engines/illusions/thread.h
+++ b/engines/illusions/thread.h
@@ -53,11 +53,13 @@ public:
 	virtual void onSuspend();
 	virtual void onNotify();
 	virtual void onPause();
+	virtual void onUnpause();
 	virtual void onResume();
 	virtual void onTerminated();
 	virtual void onKill();
 	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
 	void pause();
+	void unpause();
 	void resume();
 	void suspend();
 	void notify();
@@ -93,6 +95,7 @@ public:
 	void notifyThreads(uint32 threadId);
 	void notifyThreadsBySceneId(uint32 sceneId, uint32 threadId);
 	void pauseThreads(uint32 threadId);
+	void unpauseThreads(uint32 threadId);
 	void suspendThreads(uint32 threadId);
 	void resumeThreads(uint32 threadId);
 	void endTalkThreads();
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 9b7ca4e..5511fc5 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -210,6 +210,33 @@ void TalkThread_Duckman::onNotify() {
 }
 
 void TalkThread_Duckman::onPause() {
+	if (_status == 5) {
+		if (!(_flags & 4)) {
+			// TODO audvocPauseVoice();
+		}
+		if (!(_flags & 8))
+			_textDurationElapsed = getDurationElapsed(_textStartTime, _textEndTime);
+	}
+}
+
+void TalkThread_Duckman::onUnpause() {
+	if (_status == 3) {
+		TalkEntry *talkEntry = getTalkResourceEntry(_talkId);
+		if (!_vm->isSoundActive())
+			_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName);
+	} else if (_status == 5) {
+		if (!(_flags & 4)) {
+			// TODO audvocUnpauseVoice();
+		}
+		if (!(_flags & 8)) {
+			_textStartTime = getCurrentTime();
+			if (_textDuration <= _textDurationElapsed)
+				_textEndTime = _textStartTime;
+			else
+				_textEndTime = _textStartTime + _textDuration - _textDurationElapsed;
+			_textDurationElapsed = 0;
+		}
+	}
 }
 
 void TalkThread_Duckman::onResume() {
diff --git a/engines/illusions/threads/talkthread_duckman.h b/engines/illusions/threads/talkthread_duckman.h
index b729ad2..6f4758d 100644
--- a/engines/illusions/threads/talkthread_duckman.h
+++ b/engines/illusions/threads/talkthread_duckman.h
@@ -44,6 +44,7 @@ public:
 	virtual void onSuspend();
 	virtual void onNotify();
 	virtual void onPause();
+	virtual void onUnpause();
 	virtual void onResume();
 	virtual void onTerminated();
 	virtual void onKill();
diff --git a/engines/illusions/threads/timerthread.cpp b/engines/illusions/threads/timerthread.cpp
index 417d113..de1502d 100644
--- a/engines/illusions/threads/timerthread.cpp
+++ b/engines/illusions/threads/timerthread.cpp
@@ -52,10 +52,18 @@ int TimerThread::onUpdate() {
 }
 
 void TimerThread::onSuspend() {
-	_durationElapsed = getDurationElapsed(_startTime, _endTime);
+	onPause();
 }
 
 void TimerThread::onNotify() {
+	onUnpause();
+}
+
+void TimerThread::onPause() {
+	_durationElapsed = getDurationElapsed(_startTime, _endTime);
+}
+
+void TimerThread::onUnpause() {
 	uint32 currTime = getCurrentTime();
 	_startTime = currTime;
 	if (_duration <= _durationElapsed)
@@ -65,10 +73,6 @@ void TimerThread::onNotify() {
 	_durationElapsed = 0;
 }
 
-void TimerThread::onPause() {
-	onSuspend();
-}
-
 void TimerThread::onResume() {
 	onNotify();
 }
diff --git a/engines/illusions/threads/timerthread.h b/engines/illusions/threads/timerthread.h
index d283dc4..7e1b613 100644
--- a/engines/illusions/threads/timerthread.h
+++ b/engines/illusions/threads/timerthread.h
@@ -37,6 +37,7 @@ public:
 	virtual void onSuspend();
 	virtual void onNotify();
 	virtual void onPause();
+	virtual void onUnpause();
 	virtual void onResume();
 	virtual void onTerminated();
 public:


Commit: fa17f684da1da7fded805746b75c454502ffe683
    https://github.com/scummvm/scummvm/commit/fa17f684da1da7fded805746b75c454502ffe683
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Start implementing the menu system

Still work-in-progress, missing functionality and buggy
Maybe needs some work for BBDOU where this isn't implemented yet.

Changed paths:
  A engines/illusions/duckman/menusystem_duckman.cpp
  A engines/illusions/duckman/menusystem_duckman.h
  A engines/illusions/menusystem.cpp
  A engines/illusions/menusystem.h
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h
    engines/illusions/gamarchive.cpp
    engines/illusions/input.cpp
    engines/illusions/input.h
    engines/illusions/module.mk
    engines/illusions/resources/fontresource.h
    engines/illusions/screentext.cpp
    engines/illusions/screentext.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/sound.cpp
    engines/illusions/textdrawer.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index bae77c7..9192946 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -929,6 +929,23 @@ void Control::refreshSequenceCode() {
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 }
 
+void Control::getActorFrameDimensions(WidthHeight &dimensions) {
+	dimensions._width = _actor->_surface->w;
+	dimensions._height = _actor->_surface->h;
+}
+
+void Control::drawActorRect(const Common::Rect r, byte color) {
+	_actor->_surface->fillRect(r, color);
+	_actor->_flags |= 0x4000;
+}
+
+void Control::fillActor(byte color) {
+	debug("FILL %d, %d", _actor->_surface->w, _actor->_surface->h);
+	Common::Rect r = Common::Rect(_actor->_surface->w, _actor->_surface->h);
+	_actor->_surface->fillRect(r, color);
+	_actor->_flags |= 0x4000;
+}
+
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 	stopActor();
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 295a0b5..add7519 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -34,6 +34,7 @@
 
 namespace Illusions {
 
+class Control;
 class IllusionsEngine;
 class SequenceOpcodes;
 struct OpCall;
@@ -200,6 +201,9 @@ public:
 	PointArray *createPath(Common::Point destPt);
 	void updateActorMovement(uint32 deltaTime);
 	void refreshSequenceCode();
+	void getActorFrameDimensions(WidthHeight &dimensions);
+	void drawActorRect(const Common::Rect r, byte color);
+	void fillActor(byte color);
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 7255327..db8f83c 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -23,6 +23,7 @@
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/duckman_dialog.h"
 #include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/duckman/menusystem_duckman.h"
 #include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -110,6 +111,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
+	_menuSystem = new DuckmanMenuSystem(this);
 
 	_fader = new Fader();
 	
@@ -180,6 +182,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _fader;
 
+	delete _menuSystem;
 	delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
@@ -231,8 +234,10 @@ void IllusionsEngine_Duckman::initInput() {
 	_input->setInputEvent(kEventDown, 0x80)
 		.addMouseButton(MOUSE_RIGHT_BUTTON)
 		.addKey(Common::KEYCODE_DOWN);
+	/* Not implemented, used for original debugging purposes
 	_input->setInputEvent(kEventF1, 0x100)
 		.addKey(Common::KEYCODE_F1);
+	*/
 }
 
 #define UPDATEFUNCTION(priority, sceneId, callback) \
@@ -256,8 +261,13 @@ int IllusionsEngine_Duckman::updateScript(uint flags) {
 	if (_screen->isDisplayOn() && !_screen->isFaderActive() && _pauseCtr == 0) {
 		if (_input->pollEvent(kEventAbort)) {
 			startScriptThread(0x00020342, 0);
+			//testMenu(this);//TODO DEBUG
+			
+			//BaseMenu *me = _menuSystem->getMenuById(kDuckmanPauseMenu);
+			//_menuSystem->openMenu(me);
+			//_menuSystem->runMenu(0x180002);
+			
 		} else if (_input->pollEvent(kEventF1)) {
-			debug("F1");
 			startScriptThread(0x0002033F, 0);
 		}
 	}
@@ -632,7 +642,7 @@ void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 delt
 			_dialogSys->updateDialogState();
 			break;
 		case 4:
-			// TODO ShellMgr_update(_cursor._control);
+			_menuSystem->update(_cursor._control);
 			break;
 		}
 	}
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 39c421d..e2b3223 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -32,6 +32,7 @@ namespace Illusions {
 class Dictionary;
 class ScriptStack;
 class DuckmanDialogSystem;
+class DuckmanMenuSystem;
 
 struct Cursor_Duckman {
 	int _gameState;
@@ -99,6 +100,7 @@ public:
 	int _savedInventoryActorIndex;
 
 	ScreenShaker *_screenShaker;
+	DuckmanMenuSystem *_menuSystem;
 	
 	void initInput();
 	
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
new file mode 100644
index 0000000..96d350a
--- /dev/null
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -0,0 +1,179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/actor.h"
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/menusystem_duckman.h"
+
+namespace Illusions {
+
+// DuckmanMenuSystem
+
+DuckmanMenuSystem::DuckmanMenuSystem(IllusionsEngine_Duckman *vm)
+	: BaseMenuSystem(vm), _vm(vm) {
+	clearMenus();
+}
+
+DuckmanMenuSystem::~DuckmanMenuSystem() {
+	freeMenus();
+}
+
+void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
+	uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
+
+	setTimeOutDuration(duration, timeOutMenuChoiceIndex);
+	setMenuCallerThreadId(menuCallerThreadId);
+	setMenuChoiceOffsets(menuChoiceOffsets, menuChoiceOffset);
+
+	int rootMenuId = convertRootMenuId(menuId | 0x180000);
+	BaseMenu *rootMenu = getMenuById(rootMenuId);
+	openMenu(rootMenu);
+
+}
+
+void DuckmanMenuSystem::clearMenus() {
+	for (int i = 0; i < kDuckmanLastMenuIndex; ++i)
+		_menus[i] = 0;
+}
+
+void DuckmanMenuSystem::freeMenus() {
+	for (int i = 0; i < kDuckmanLastMenuIndex; ++i)
+		delete _menus[i];
+}
+
+BaseMenu *DuckmanMenuSystem::getMenuById(int menuId) {
+	if (!_menus[menuId])
+		_menus[menuId] = createMenuById(menuId);
+	return _menus[menuId];
+}
+
+BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
+	switch (menuId) {
+	case kDuckmanMainMenu:
+		return createMainMenu();
+	case kDuckmanPauseMenu:
+		return createPauseMenu();
+	case kDuckmanQueryRestartMenu:
+		return createQueryRestartMenu();
+	case kDuckmanQueryQuitMenu:
+		return createQueryQuitMenu();
+	default:
+		error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId);
+	}
+}
+
+BaseMenu *DuckmanMenuSystem::createMainMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
+	menu->addMenuItem(new MenuItem("Start New Game", new MenuActionReturnChoice(this, 11)));
+
+	menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionReturnChoice(this, 0)));
+	menu->addMenuItem(new MenuItem("Options", new MenuActionReturnChoice(this, 0)));
+
+	// TODO menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionEnterMenu(this, kDuckmanLoadGameMenu)));
+	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
+	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 12)));
+	return menu;
+}
+
+BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
+	return 0; // TODO
+}
+
+BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
+	return 0; // TODO
+}
+
+BaseMenu *DuckmanMenuSystem::createPauseMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
+	menu->addText("   Game Paused");
+	menu->addText("-------------------");
+	menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 1)));
+	//menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
+	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
+	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 3)));
+	return menu;
+}
+
+BaseMenu *DuckmanMenuSystem::createQueryRestartMenu() {
+	return 0; // TODO
+}
+
+BaseMenu *DuckmanMenuSystem::createQueryQuitMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 2);
+	menu->addText("Do you really want to quit?");
+	menu->addText("-------------------------------");
+	menu->addMenuItem(new MenuItem("Yes, I'm outta here", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex())));
+	menu->addMenuItem(new MenuItem("No, just kidding", new MenuActionLeaveMenu(this)));
+	return menu;
+}
+
+int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
+	switch (menuId) {
+	case 0x180001:
+		return kDuckmanMainMenu;
+	case 0x180002:
+		return kDuckmanPauseMenu;
+	/* Debug menus, not implemented
+	case 0x180005:
+	case 0x180006:
+	case 0x180007:
+	*/
+	/* TODO CHECKME Another pause menu?
+	case 0x180008:
+		menuData = &g_menuDataPause;
+	*/
+	default:
+		error("DuckmanMenuSystem() Menu ID %08X not found", menuId);
+	}
+}
+
+bool DuckmanMenuSystem::initMenuCursor() {
+	bool cursorInitialVisibleFlag = false;
+	Control *cursorControl = _vm->getObjectControl(0x40004);
+	if (cursorControl) {
+		if (cursorControl->_flags & 1)
+			cursorInitialVisibleFlag = false;
+		cursorControl->appearActor();
+	} else {
+		Common::Point pos = _vm->getNamedPointPosition(0x70001);
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
+		cursorControl = _vm->getObjectControl(0x40004);
+	}
+	return cursorInitialVisibleFlag;
+}
+
+int DuckmanMenuSystem::getGameState() {
+	return _vm->_cursor._gameState;
+}
+
+void DuckmanMenuSystem::setMenuCursorNum(int cursorNum) {
+	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	_vm->setCursorActorIndex(5, cursorNum, 0);
+	mouseCursor->startSequenceActor(0x60001, 2, 0);
+}
+
+void DuckmanMenuSystem::setGameState(int gameState) {
+	_vm->_cursor._gameState = gameState;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
new file mode 100644
index 0000000..bb43619
--- /dev/null
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
+#define ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
+
+#include "illusions/menusystem.h"
+
+namespace Illusions {
+
+enum {
+	kDuckmanMainMenu,
+	kDuckmanLoadGameMenu,
+	kDuckmanOptionsMenu,
+	kDuckmanPauseMenu,
+	kDuckmanQueryQuitMenu,
+	kDuckmanQueryRestartMenu,
+	kDuckmanLastMenuIndex
+};
+
+class IllusionsEngine_Duckman;
+
+class DuckmanMenuSystem : public BaseMenuSystem {
+public:
+	DuckmanMenuSystem(IllusionsEngine_Duckman *vm);
+	~DuckmanMenuSystem();
+	void runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
+		uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId);
+public://protected:
+	IllusionsEngine_Duckman *_vm;
+	BaseMenu *_menus[kDuckmanLastMenuIndex];
+	void clearMenus();
+	void freeMenus();
+	BaseMenu *getMenuById(int menuId);
+	BaseMenu *createMenuById(int menuId);
+	BaseMenu *createMainMenu();
+	BaseMenu *createLoadGameMenu();
+	BaseMenu *createOptionsMenu();
+	BaseMenu *createPauseMenu();
+	BaseMenu *createQueryRestartMenu();		
+	BaseMenu *createQueryQuitMenu();
+	int convertRootMenuId(uint32 menuId);
+	virtual bool initMenuCursor();
+	virtual int getGameState();
+	virtual void setGameState(int gameState);
+	virtual void setMenuCursorNum(int cursorNum);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 401f2e3..0fa7361 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -23,10 +23,12 @@
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/duckman/duckman_dialog.h"
+#include "illusions/duckman/menusystem_duckman.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
 #include "illusions/input.h"
+#include "illusions/menusystem.h"
 #include "illusions/resources/scriptresource.h"
 #include "illusions/resources/talkresource.h"
 #include "illusions/screen.h"
@@ -57,6 +59,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	// First clear everything
 	for (uint i = 0; i < 256; ++i)
 		_opcodes[i] = 0;
+	// Register opcodes
 	OPCODE(1, opNop);
 	OPCODE(2, opSuspend);
 	OPCODE(3, opYield);
@@ -69,13 +72,15 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(16, opLoadResource);
 	OPCODE(17, opUnloadResource);
 	OPCODE(18, opEnterScene18);
+	OPCODE(19, opUnloadResourcesBySceneId);
 	OPCODE(20, opChangeScene);
 	OPCODE(22, opStartModalScene);
 	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
 	OPCODE(25, opLeaveScene24);
-	OPCODE(26, opEnterScene26);
-	OPCODE(27, opLeaveScene26);
+	OPCODE(26, opEnterDebugger);
+	OPCODE(27, opLeaveDebugger);
+	OPCODE(28, opDumpCurrentSceneFiles);
 	OPCODE(32, opPanCenterObject);
 	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
@@ -135,7 +140,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(126, opDebug126);
 	OPCODE(127, opDebug127);
 #if 0		
-	// Register opcodes
 	OPCODE(8, opStartTempScriptThread);
 	OPCODE(14, opSetThreadSceneId);
 	OPCODE(15, opEndTalkThreads);
@@ -252,6 +256,12 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o
 	_vm->enterScene(sceneId, 0);
 }
 
+void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->_resSys->unloadResourcesBySceneId(sceneId);
+}
+
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
 static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
@@ -279,11 +289,13 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	debug(1, "changeScene(%08X, %08X)", sceneId, threadId);
 	
 	//DEBUG
+	/*
 	if (dsceneId) {
 		sceneId = dsceneId;
 		threadId = dthreadId;
 		dsceneId = 0;
 	}
+	*/
 	
 	if (_vm->_scriptResource->_properties.get(31)) {
 		_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
@@ -331,12 +343,19 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o
 	_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
 }
 
-void ScriptOpcodes_Duckman::opEnterScene26(ScriptThread *scriptThread, OpCall &opCall) {
-	// TODO
+void ScriptOpcodes_Duckman::opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall) {
+	// Used for debugging purposes in the original engine
+	// This is not supported and only reachable by code not implemented here!
+	error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called");
+}
+
+void ScriptOpcodes_Duckman::opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall) {
+	// See opEnterDebugger
+	error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called");
 }
 
-void ScriptOpcodes_Duckman::opLeaveScene26(ScriptThread *scriptThread, OpCall &opCall) {
-	// TODO
+void ScriptOpcodes_Duckman::opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
 }
 
 void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
@@ -615,23 +634,32 @@ void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &
 }
 
 void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(unk1);
+	ARG_INT16(timeOutDuration);
 	ARG_UINT32(menuId);
-	ARG_UINT32(unk2);
-	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
-	// Remove menu choices from the stack
+	ARG_UINT32(timeOutMenuChoiceIndex);
+	
+	debug("timeOutMenuChoiceIndex: %d", timeOutMenuChoiceIndex);
+	
+	MenuChoiceOffsets menuChoiceOffsets;
+
+	// Load menu choices from the stack
 	do {
-		_vm->_stack->pop();
+		int16 choiceOffs = _vm->_stack->pop();
+		debug("choiceOffs: %04X", choiceOffs);
+		menuChoiceOffsets.push_back(choiceOffs);
 	} while (_vm->_stack->pop() == 0);
-
+	
+	_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs, 
+		menuId, timeOutDuration, timeOutMenuChoiceIndex,
+		opCall._threadId);
+	
 	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
+	//_vm->notifyThreadId(opCall._callerThreadId);
 
 }
 
 void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game"
-
+	//_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game"
 	opCall._deltaOfs += _vm->_menuChoiceOfs;
 }
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index 5b2f089..d50f967 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -54,13 +54,15 @@ protected:
 	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
-	void opEnterScene26(ScriptThread *scriptThread, OpCall &opCall);
-	void opLeaveScene26(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall);
+	void opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/gamarchive.cpp b/engines/illusions/gamarchive.cpp
index 517882c..098ad35 100644
--- a/engines/illusions/gamarchive.cpp
+++ b/engines/illusions/gamarchive.cpp
@@ -61,13 +61,13 @@ void GamArchive::loadDictionary() {
 		_groups[i]._fileCount = fileCount;
 		_groups[i]._files = new GamFileEntry[fileCount];
 		
-		debug("Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount);
+		debug(8, "Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount);
 		
 		for (uint j = 0; j < fileCount; ++j) {
 			_groups[i]._files[j]._id = _fd->readUint32LE();
 			_groups[i]._files[j]._fileOffset = _fd->readUint32LE();
 			_groups[i]._files[j]._fileSize = _fd->readUint32LE();
-			debug("  %08X, %08X, %d", _groups[i]._files[j]._id, _groups[i]._files[j]._fileOffset, _groups[i]._files[j]._fileSize);
+			debug(8, "  %08X, %08X, %d", _groups[i]._files[j]._id, _groups[i]._files[j]._fileOffset, _groups[i]._files[j]._fileSize);
 		}
 	}
 
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 3956691..37da087 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -77,12 +77,14 @@ uint InputEvent::handle(Common::KeyCode key, int mouseButton, bool down) {
 
 // Input
 
+const uint kAllButtons = 0xFFFF;
+
 Input::Input() {
 	_buttonStates = 0;
 	_newButtons = 0;
 	_buttonsDown = 0;
 	_newKeys = 0;
-	_enabledButtons = 0xFFFF;
+	_enabledButtons = kAllButtons;
 	_cursorPos.x = 0;
 	_cursorPos.y = 0;
 	_prevCursorPos.x = 0;
@@ -123,12 +125,16 @@ bool Input::pollEvent(uint evt) {
 	return pollButton(_inputEvents[evt].getBitMask());
 }
 
+bool Input::hasNewEvents() {
+	return lookNewButtons(kAllButtons);
+}
+
 void Input::discardEvent(uint evt) {
 	discardButtons(_inputEvents[evt].getBitMask());
 }
 
 void Input::discardAllEvents() {
-	discardButtons(0xFFFF);
+	discardButtons(kAllButtons);
 }
 
 void Input::activateButton(uint bitMask) {
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index 093adad..7d01ea6 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -79,6 +79,7 @@ public:
 	Input();
 	void processEvent(Common::Event event);
 	bool pollEvent(uint evt);
+	bool hasNewEvents();
 	void discardEvent(uint evt);
 	void discardAllEvents();
 	void activateButton(uint bitMask);
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
new file mode 100644
index 0000000..f26a4612
--- /dev/null
+++ b/engines/illusions/menusystem.cpp
@@ -0,0 +1,591 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/menusystem.h"
+#include "illusions/illusions.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+#include "illusions/screentext.h"
+#include "illusions/thread.h"
+#include "illusions/time.h"
+
+namespace Illusions {
+
+// MenuItem
+
+MenuItem::MenuItem(const Common::String text, BaseMenuAction *action)
+	: _text(text), _action(action) {
+}
+
+MenuItem::~MenuItem() {
+	delete _action;
+}
+
+void MenuItem::executeAction() {
+	_action->execute();
+}
+
+// BaseMenu
+
+BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE,
+	uint defaultMenuItemIndex)
+	: _menuSystem(menuSystem), _fontId(fontId), _field8(field8), _fieldA(fieldA), _fieldC(fieldC), _fieldE(fieldE),
+	_defaultMenuItemIndex(defaultMenuItemIndex)
+{
+}
+
+BaseMenu::~BaseMenu() {
+	for (MenuItems::iterator it = _menuItems.begin(); it != _menuItems.end(); ++it)
+		delete *it;
+}
+
+void BaseMenu::addText(const Common::String text) {
+	_text.push_back(text);
+}
+
+void BaseMenu::addMenuItem(MenuItem *menuItem) {
+	_menuItems.push_back(menuItem);
+}
+
+uint BaseMenu::getHeaderLinesCount() {
+	return _text.size();
+}
+
+const Common::String& BaseMenu::getHeaderLine(uint index) {
+	return _text[index];
+}
+
+uint BaseMenu::getMenuItemsCount() {
+	return _menuItems.size();
+}
+
+MenuItem *BaseMenu::getMenuItem(uint index) {
+	return _menuItems[index];
+}
+
+void BaseMenu::enterMenu() {
+	// Empty, implemented if neccessary by the inherited class when the menu is entered
+}
+
+// BaseMenuSystem
+
+BaseMenuSystem::BaseMenuSystem(IllusionsEngine *vm)
+	: _vm(vm), _isTimeOutEnabled(false), _menuChoiceOffset(0) {
+}
+
+BaseMenuSystem::~BaseMenuSystem() {
+}
+
+void BaseMenuSystem::playSoundEffect13() {
+	// TODO
+}
+
+void BaseMenuSystem::playSoundEffect14() {
+	// TODO
+}
+
+void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) {
+	if (choiceIndex > 0 && _menuChoiceOffset) {
+		*_menuChoiceOffset = _menuChoiceOffsets[choiceIndex - 1];
+		debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset);		
+	}
+	_vm->_threads->notifyId(_menuCallerThreadId);
+	_menuCallerThreadId = 0;
+	closeMenu();
+}
+
+void BaseMenuSystem::leaveMenu() {
+	playSoundEffect13();
+	if (!_menuStack.empty())
+		leaveSubMenu();
+	else
+		closeMenu();
+}
+
+void BaseMenuSystem::enterSubMenu(BaseMenu *menu) {
+	_menuStack.push(_activeMenu);
+	activateMenu(menu);
+	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
+	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
+	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+	placeActor318();
+	placeActor323();
+}
+
+void BaseMenuSystem::leaveSubMenu() {
+	_activeMenu = _menuStack.pop();
+	_field54 = _activeMenu->_field2C18;
+	_menuLinesCount = _activeMenu->getHeaderLinesCount();
+	_hoveredMenuItemIndex = 1;
+	_vm->_screenText->removeText();
+	_vm->_screenText->removeText();
+	activateMenu(_activeMenu);
+	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
+	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
+	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+	initActor318();
+	placeActor323();
+}
+
+void BaseMenuSystem::enterSubMenuById(int menuId) {
+	BaseMenu *menu = getMenuById(menuId);
+	enterSubMenu(menu);
+}
+
+uint BaseMenuSystem::getQueryConfirmationChoiceIndex() const {
+	return _queryConfirmationChoiceIndex;
+}
+
+void BaseMenuSystem::setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex) {
+	_queryConfirmationChoiceIndex = queryConfirmationChoiceIndex;
+}
+
+void BaseMenuSystem::setMouseCursorToMenuItem(int menuItemIndex) {
+	Common::Point mousePos;
+	if (calcMenuItemMousePos(menuItemIndex, mousePos))
+		setMousePos(mousePos);
+}
+
+void BaseMenuSystem::calcMenuItemRect(uint menuItemIndex, WRect &rect) {
+	FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
+	int charHeight = font->getCharHeight() + font->getLineIncr();
+	
+	_vm->_screenText->getTextInfoPosition(rect._topLeft);
+	/* TODO
+	if (_activeMenu->_field8) {
+		rect._topLeft.y += 4;
+		rect._topLeft.x += 4;
+	}
+	*/	
+	rect._topLeft.y += charHeight * (menuItemIndex + _menuLinesCount - 1);
+
+	WidthHeight textInfoDimensions;
+	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
+	rect._bottomRight.x = rect._topLeft.x + textInfoDimensions._width;
+	rect._bottomRight.y = rect._topLeft.y + charHeight;
+}
+
+bool BaseMenuSystem::calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt) {
+	if (menuItemIndex < _hoveredMenuItemIndex3 || menuItemIndex >= _hoveredMenuItemIndex3 + _menuItemCount)
+		return false;
+
+	WRect rect;
+	calcMenuItemRect(menuItemIndex - _hoveredMenuItemIndex3 + 1, rect);
+	pt.x = rect._topLeft.x;
+	pt.y = rect._topLeft.y + (rect._bottomRight.y - rect._topLeft.y) / 2;
+	return true;
+}
+
+bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex) {
+	WRect rect;
+	calcMenuItemRect(1, rect);
+	
+	uint index = _hoveredMenuItemIndex3 + (pt.y - rect._topLeft.y) / (rect._bottomRight.y - rect._topLeft.y);
+
+	if (pt.y < rect._topLeft.y || pt.x < rect._topLeft.x || pt.x > rect._bottomRight.x ||
+		index > _field54 || index > _hoveredMenuItemIndex3 + _menuItemCount - 1)
+		return false;
+
+	menuItemIndex = index;
+	return true;
+}
+
+void BaseMenuSystem::setMousePos(Common::Point &mousePos) {
+	_vm->_input->setCursorPosition(mousePos);
+	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	mouseCursor->_actor->_position = mousePos;
+}
+
+void BaseMenuSystem::activateMenu(BaseMenu *menu) {
+	_activeMenu = menu;
+	// TODO Run menu enter callback if neccessary
+	_menuLinesCount = menu->getHeaderLinesCount();
+	menu->_field2C18 = menu->getMenuItemsCount();
+	_hoveredMenuItemIndex3 = 1;
+	_field54 = menu->_field2C18;
+
+	uint v2 = drawMenuText(menu);
+	if (menu->_field2C18 <= v2)
+		_menuItemCount = menu->_field2C18;
+	else
+		_menuItemCount = v2;
+
+}
+
+void BaseMenuSystem::initActor318() {
+	Control *v0 = _vm->getObjectControl(0x4013E);
+	if (!v0) {
+		WidthHeight dimensions;
+		dimensions._width = 300;
+		dimensions._height = 15;
+		_vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 18);
+		v0 = _vm->getObjectControl(0x4013E);
+		v0->_flags |= 8;
+	}
+	placeActor318();
+	v0->appearActor();
+}	
+
+void BaseMenuSystem::placeActor318() {
+	Control *v0 = _vm->getObjectControl(0x4013E);
+	v0->fillActor(0);
+	
+	WidthHeight textInfoDimensions;
+	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
+
+	/* TODO	
+	if ( _activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8)
+		textInfoDimensions._width -= 6;
+		*/
+		
+	WidthHeight frameDimensions;
+	v0->getActorFrameDimensions(frameDimensions);
+	
+	FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
+	int charHeight = font->getCharHeight() + font->getLineIncr();
+	if (frameDimensions._height < charHeight)
+		charHeight = frameDimensions._height;
+		
+	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight - 1), _activeMenu->_fieldE);
+	
+	updateActor318();
+}
+
+void BaseMenuSystem::updateActor318() {
+	Control *v0 = _vm->getObjectControl(0x4013E);
+	WRect rect;
+	calcMenuItemRect(_hoveredMenuItemIndex2 - _hoveredMenuItemIndex3 + 1, rect);
+  	v0->setActorPosition(rect._topLeft);
+}
+
+void BaseMenuSystem::hideActor318() {
+	Control *v0 = _vm->getObjectControl(0x4013E);
+	if (v0)
+		v0->disappearActor();
+}
+
+void BaseMenuSystem::initActor323() {
+	Control *v0 = _vm->getObjectControl(0x40143);
+	if (!v0) {
+		WidthHeight dimensions;
+		dimensions._width = 300;
+		dimensions._height = 180;
+		_vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 17);
+		v0 = _vm->getObjectControl(0x40143);
+		v0->_flags |= 8;
+	}
+	placeActor323();
+	v0->appearActor();
+}
+
+void BaseMenuSystem::placeActor323() {
+	Control *v0 = _vm->getObjectControl(0x40143);
+	v0->fillActor(0);
+	
+	Common::Point textInfoPosition;
+	WidthHeight textInfoDimensions;
+	_vm->_screenText->getTextInfoPosition(textInfoPosition);
+	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
+	
+	/* TODO	
+	if (_activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) {
+		textInfoDimensions._width -= 2;
+		textInfoDimensions._height -= 6;
+	}
+	*/	
+
+	v0->setActorPosition(textInfoPosition);
+	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_fieldC);
+	
+}
+
+void BaseMenuSystem::hideActor323() {
+	Control *v0 = _vm->getObjectControl(0x40143);
+	if (v0)
+		v0->disappearActor();
+}
+
+void BaseMenuSystem::openMenu(BaseMenu *menu) {
+	
+	_isActive = true;
+	_menuStack.clear();
+	
+	_cursorInitialVisibleFlag = initMenuCursor();
+	_savedCursorPos = _vm->_input->getCursorPosition();
+	_savedGameState = getGameState();
+	Control *cursorControl = _vm->getObjectControl(0x40004);
+	_savedCursorActorIndex = cursorControl->_actor->_actorIndex;
+	_savedCursorSequenceId = cursorControl->_actor->_sequenceId;
+	
+	setMenuCursorNum(1);
+	
+	setGameState(4);
+	
+	activateMenu(menu);
+	
+	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
+	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
+	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+	initActor318();
+	initActor323();
+	_vm->_input->discardAllEvents();
+}
+
+void BaseMenuSystem::closeMenu() {
+	while (!_menuStack.empty()) {
+		_vm->_screenText->removeText();
+		_menuStack.pop();
+	}
+	_vm->_screenText->removeText();
+	hideActor318();
+	hideActor323();
+	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	setGameState(_savedGameState);
+	mouseCursor->_actor->_actorIndex = _savedCursorActorIndex;
+	mouseCursor->_actor->_position = _savedCursorPos;
+	setMousePos(_savedCursorPos);
+	mouseCursor->startSequenceActor(_savedCursorSequenceId, 2, 0);
+	if (_cursorInitialVisibleFlag)
+		mouseCursor->disappearActor();
+	_vm->_input->discardAllEvents();
+	_isActive = false;
+}
+
+void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) {
+
+	if (menuItemIndex == 0) {
+	    playSoundEffect14();
+	    return;
+	}
+
+	MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
+	menuItem->executeAction();
+	
+}
+
+uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
+	MenuTextBuilder *menuTextBuilder = new MenuTextBuilder();
+	uint lineCount = 0;
+	
+	for (uint i = 0; i < menu->getHeaderLinesCount(); ++i) {
+		menuTextBuilder->appendString(menu->getHeaderLine(i));
+		menuTextBuilder->appendNewLine();
+	}
+
+	for (uint i = _hoveredMenuItemIndex3; i <= _field54; ++i) {
+		menuTextBuilder->appendString(menu->getMenuItem(i - 1)->getText());
+		if (i + 1 <= menu->getMenuItemsCount())
+			menuTextBuilder->appendNewLine();
+		++lineCount;
+	}
+	
+	menuTextBuilder->finalize();
+
+	uint16 *text = menuTextBuilder->getText();
+
+	Common::Point textPt;
+	int16 v9 = 0;
+	if (menu->_field8)
+		v9 = 4;
+	textPt.x = v9;
+	textPt.y = v9;
+
+	uint flags = 1;
+	if (menu->_field8 != menu->_fieldA)
+		flags = 25;
+		
+	WidthHeight dimensions;
+	dimensions._width = 300;
+	dimensions._height = 180;
+	
+	uint16 *outTextPtr;
+	if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_field8, menu->_fieldA, 0xFF, 0xFF, 0xFF, outTextPtr)) {
+		--lineCount;
+		for ( ; *outTextPtr; ++outTextPtr) {
+			if (*outTextPtr == 13)
+				--lineCount;
+		}
+	}
+
+	delete menuTextBuilder;
+
+	return lineCount;
+}
+
+void BaseMenuSystem::update(Control *cursorControl) {
+    Common::Point mousePos = _vm->_input->getCursorPosition();
+	setMousePos(mousePos);
+	
+	uint newHoveredMenuItemIndex;
+	bool resetTimeOut = false;
+	
+	if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex) && newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
+		if (_hoveredMenuItemIndex == 0)
+			initActor318();
+		_hoveredMenuItemIndex = newHoveredMenuItemIndex;
+		_hoveredMenuItemIndex2 = newHoveredMenuItemIndex;
+		setMenuCursorNum(2);
+		updateActor318();
+		resetTimeOut = true;
+	} else if (_hoveredMenuItemIndex == 0) {
+		setMenuCursorNum(1);
+		hideActor318();
+		_hoveredMenuItemIndex = 0;
+		resetTimeOut = true;
+	}
+
+	if (_vm->_input->hasNewEvents())
+		resetTimeOut = true;
+
+	if (_vm->_input->pollEvent(kEventLeftClick)) {
+		handleClick(_hoveredMenuItemIndex, mousePos);
+	} else if (_vm->_input->pollEvent(kEventAbort) && _activeMenu->_defaultMenuItemIndex) {
+		handleClick(_activeMenu->_defaultMenuItemIndex, mousePos);
+	} else if (_vm->_input->pollEvent(kEventUp)) {
+		// TODO handleUpKey();
+	} else if (_vm->_input->pollEvent(kEventDown)) {
+		// TODO handleDownKey();
+	}
+	
+	updateTimeOut(resetTimeOut);
+}
+
+void BaseMenuSystem::setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex) {
+	if (duration > 0) {
+		_isTimeOutEnabled = true;
+		_isTimeOutReached = false;
+		_timeOutDuration = duration;
+		_timeOutMenuChoiceIndex = timeOutMenuChoiceIndex;
+		_timeOutStartTime = getCurrentTime();
+		_timeOutEndTime = duration + _timeOutStartTime;
+	} else {
+		_isTimeOutEnabled = false;
+	}
+}
+
+void BaseMenuSystem::setMenuCallerThreadId(uint32 menuCallerThreadId) {
+	_menuCallerThreadId = menuCallerThreadId;
+}
+
+void BaseMenuSystem::setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset) {
+	_menuChoiceOffsets = menuChoiceOffsets;
+	_menuChoiceOffset = menuChoiceOffset;
+}
+
+void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
+
+	if (!_isTimeOutEnabled)
+		return;
+
+	if (_menuStack.empty()) {
+		if (_isTimeOutReached) {
+			resetTimeOut = true;
+			_isTimeOutReached = false;
+		}
+	} else if (!_isTimeOutReached) {
+		_isTimeOutReached = true;
+	}
+	
+	if (!_isTimeOutReached) {
+		if (resetTimeOut) {
+			_timeOutStartTime = getCurrentTime();
+			_timeOutEndTime = _timeOutDuration + _timeOutStartTime;
+		} else if (isTimerExpired(_timeOutStartTime, _timeOutEndTime)) {
+			_isTimeOutEnabled = false;
+			selectMenuChoiceIndex(_timeOutMenuChoiceIndex);
+			// TODO *_menuChoiceOffset = *((_WORD *)&_menuCallerThreadId + _timeOutMenuChoiceIndex + 1);
+			//_vm->_threads->notifyId(_menuCallerThreadId);
+			//_menuCallerThreadId = 0;
+			//closeMenu();
+		}
+	}
+
+}
+
+// MenuTextBuilder
+
+MenuTextBuilder::MenuTextBuilder() : _pos(0) {
+}
+
+void MenuTextBuilder::appendString(const Common::String &value) {
+	for (uint i = 0; i < value.size(); ++i)
+		_text[_pos++] = value[i];
+}
+
+void MenuTextBuilder::appendNewLine() {
+	_text[_pos++] = '\r';
+}
+
+void MenuTextBuilder::finalize() {
+	_text[_pos] = '\0';
+}
+
+// BaseMenuAction
+
+BaseMenuAction::BaseMenuAction(BaseMenuSystem *menuSystem)
+	: _menuSystem(menuSystem) {
+}
+
+// MenuActionEnterMenu
+
+MenuActionEnterMenu::MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId)
+	: BaseMenuAction(menuSystem), _menuId(menuId) {
+}
+
+void MenuActionEnterMenu::execute() {
+	_menuSystem->enterSubMenuById(_menuId);
+}
+
+// MenuActionLeaveMenu
+
+MenuActionLeaveMenu::MenuActionLeaveMenu(BaseMenuSystem *menuSystem)
+	: BaseMenuAction(menuSystem) {
+}
+
+void MenuActionLeaveMenu::execute() {
+	_menuSystem->leaveMenu();
+}
+
+// MenuActionReturnChoice
+
+MenuActionReturnChoice::MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex)
+	: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
+}
+
+void MenuActionReturnChoice::execute() {
+	_menuSystem->playSoundEffect13();
+	_menuSystem->selectMenuChoiceIndex(_choiceIndex);
+}
+
+// MenuActionEnterQueryMenu
+
+MenuActionEnterQueryMenu::MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex)
+	: BaseMenuAction(menuSystem), _menuId(menuId), _confirmationChoiceIndex(confirmationChoiceIndex) {
+}
+
+void MenuActionEnterQueryMenu::execute() {
+	_menuSystem->setQueryConfirmationChoiceIndex(_confirmationChoiceIndex);
+	_menuSystem->enterSubMenuById(_menuId);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
new file mode 100644
index 0000000..98ef924
--- /dev/null
+++ b/engines/illusions/menusystem.h
@@ -0,0 +1,235 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_MENUSYSTEM_H
+#define ILLUSIONS_MENUSYSTEM_H
+
+#include "illusions/actor.h"
+#include "illusions/graphics.h"
+#include "illusions/resources/fontresource.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/stack.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class BaseMenuSystem;
+class BaseMenuAction;
+
+const uint kMenuTextSize = 4096;
+
+class MenuItem {
+public:
+	MenuItem(const Common::String text, BaseMenuAction *action);
+	~MenuItem();
+	void executeAction();
+	const Common::String& getText() const { return _text; }
+protected:
+	Common::String _text;
+	BaseMenuAction *_action;
+};
+
+class BaseMenu {
+public:
+	BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE,
+		uint defaultMenuItemIndex);
+	virtual ~BaseMenu();
+	void addText(const Common::String text);
+	void addMenuItem(MenuItem *menuItem);
+	uint getHeaderLinesCount();
+	const Common::String& getHeaderLine(uint index);
+	uint getMenuItemsCount();
+	MenuItem *getMenuItem(uint index);
+	virtual void enterMenu();
+public://protected://TODO
+	typedef Common::Array<MenuItem*> MenuItems;
+	BaseMenuSystem *_menuSystem;
+	uint32 _fontId;
+	byte _field8, _fieldA, _fieldC, _fieldE;
+	uint _field2C18;
+	uint _defaultMenuItemIndex;
+	Common::Array<Common::String> _text;
+	MenuItems _menuItems;
+};
+
+class MenuStack : public Common::Stack<BaseMenu*> {
+};
+
+typedef Common::Array<int16> MenuChoiceOffsets;
+
+class BaseMenuSystem {
+public:
+	BaseMenuSystem(IllusionsEngine *vm);
+	virtual ~BaseMenuSystem();
+	void playSoundEffect13();
+	void playSoundEffect14();
+	void selectMenuChoiceIndex(uint choiceIndex);
+	void leaveMenu();
+	void enterSubMenu(BaseMenu *menu);
+	void leaveSubMenu();
+	void enterSubMenuById(int menuId);
+	uint getQueryConfirmationChoiceIndex() const;
+	void setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex);
+	bool isActive() const { return _isActive; }
+	void openMenu(BaseMenu *menu);
+	void closeMenu();
+	void handleClick(uint menuItemIndex, const Common::Point &mousePos);
+	uint drawMenuText(BaseMenu *menu);
+	void update(Control *cursorControl);
+	void setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex);
+	void setMenuCallerThreadId(uint32 menuCallerThreadId);
+	void setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset);
+	virtual bool initMenuCursor() = 0;
+	virtual int getGameState() = 0;
+	virtual void setGameState(int gameState) = 0;
+	virtual void setMenuCursorNum(int cursorNum) = 0;
+protected:
+	IllusionsEngine *_vm;
+	MenuStack _menuStack;
+	
+	uint32 _menuCallerThreadId;
+    bool _isTimeOutEnabled;
+    bool _isTimeOutReached;
+    uint32 _timeOutDuration;
+    uint _timeOutMenuChoiceIndex;
+    uint32 _timeOutStartTime;
+    uint32 _timeOutEndTime;
+	
+	Common::Point _savedCursorPos;
+	bool _cursorInitialVisibleFlag;
+	int _savedGameState;
+	int _savedCursorActorIndex;
+	int _savedCursorSequenceId;
+	
+	bool _isActive;
+	
+	MenuChoiceOffsets _menuChoiceOffsets;
+	int16 *_menuChoiceOffset;
+	
+	uint _queryConfirmationChoiceIndex;
+	
+	uint _field54;
+	uint _menuLinesCount;
+	uint _menuItemCount;
+	
+	uint _hoveredMenuItemIndex;
+	uint _hoveredMenuItemIndex2;
+	uint _hoveredMenuItemIndex3;
+
+	BaseMenu *_activeMenu;
+	void setMouseCursorToMenuItem(int menuItemIndex);
+	
+	void calcMenuItemRect(uint menuItemIndex, WRect &rect);
+	bool calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt);
+	bool calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex);
+	void setMousePos(Common::Point &mousePos);
+	
+	void activateMenu(BaseMenu *menu);
+	
+	void updateTimeOut(bool resetTimeOut);
+	
+	void initActor318();
+	void placeActor318();
+	void updateActor318();
+	void hideActor318();
+	
+	void initActor323();
+	void placeActor323();
+	void hideActor323();
+	
+	virtual BaseMenu *getMenuById(int menuId) = 0;
+};
+
+/*
+
+
+*/
+
+class MenuTextBuilder {	
+public:
+	MenuTextBuilder();
+	void appendString(const Common::String &value);
+	void appendNewLine();
+	void finalize();
+	uint16 *getText() { return _text; }
+protected:
+	uint16 _text[kMenuTextSize];
+	uint _pos;
+};
+
+// Menu actions
+
+class BaseMenuAction {
+public:
+	BaseMenuAction(BaseMenuSystem *menuSystem);
+	virtual ~BaseMenuAction() {}
+	virtual void execute() = 0;
+protected:
+	BaseMenuSystem *_menuSystem;
+};
+
+// Type 1: Enter a submenu
+
+class MenuActionEnterMenu : public BaseMenuAction {
+public:
+	MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId);
+	virtual void execute();
+protected:
+	int _menuId;
+};
+
+// Type 4: Leave a submenu or the whole menu if on the main menu level
+
+class MenuActionLeaveMenu : public BaseMenuAction {
+public:
+	MenuActionLeaveMenu(BaseMenuSystem *menuSystem);
+	virtual void execute();
+};
+
+// Type 5: Return a menu choice index and exit the menu
+
+class MenuActionReturnChoice : public BaseMenuAction {
+public:
+	MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex);
+	virtual void execute();
+protected:
+	int _choiceIndex;
+};
+
+// Type 8: Return a menu choice index and exit the menu after displaying a query message
+
+class MenuActionEnterQueryMenu : public BaseMenuAction {
+public:
+	MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex);
+	virtual void execute();
+protected:
+	int _menuId;
+	uint _confirmationChoiceIndex;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_MENUSYSTEM_H
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index ee40fb3..d4bc393 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -18,6 +18,7 @@ MODULE_OBJS := \
 	duckman/duckman_screenshakereffects.o \
 	duckman/duckman_specialcode.o \
 	duckman/illusions_duckman.o \
+	duckman/menusystem_duckman.o \
 	duckman/propertytimers.o \
 	duckman/scriptopcodes_duckman.o \
 	fileresourcereader.o \
@@ -27,6 +28,7 @@ MODULE_OBJS := \
 	graphics.o \
 	illusions.o \
 	input.o \
+	menusystem.o \
 	pathfinder.o \
 	resources/actorresource.o \
 	resources/backgroundresource.o \
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
index e029145..fa758bc 100644
--- a/engines/illusions/resources/fontresource.h
+++ b/engines/illusions/resources/fontresource.h
@@ -65,6 +65,8 @@ public:
 	void load(Resource *resource);
 	CharInfo *getCharInfo(uint16 c);
 	int16 getColorIndex() const { return _colorIndex; }
+	int16 getCharHeight() const { return _charHeight; }
+	int16 getLineIncr() const { return _lineIncr; }
 public:
 	uint32 _totalSize;
 	int16 _charHeight;
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 8c31705..7c39823 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -42,6 +42,10 @@ void ScreenText::getTextInfoDimensions(WidthHeight &textInfoDimensions) {
 	textInfoDimensions = _dimensions;
 }
 
+void ScreenText::getTextInfoPosition(Common::Point &position) {
+	position = _position;
+}
+
 void ScreenText::setTextInfoPosition(Common::Point position) {
 	_position = position;
 	clipTextInfoPosition(_position);
@@ -86,6 +90,7 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 	bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr);
 	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
 	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1());
+	debug("ScreenText dimensions (%d, %d)", dimensions._width, dimensions._height);
 	_dimensions = dimensions;
 	textDrawer.drawText(_vm->_screen, _surface, color2, color1);
 	return done;
@@ -121,6 +126,7 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	_screenTexts.push_back(screenText);
 
 	FontResource *font = _vm->_dict->findFont(screenText->_info._fontId);
+	debug("font: %p", font);
 	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
 		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
 		outTextPtr);
diff --git a/engines/illusions/screentext.h b/engines/illusions/screentext.h
index 4340224..6b365e5 100644
--- a/engines/illusions/screentext.h
+++ b/engines/illusions/screentext.h
@@ -54,6 +54,7 @@ public:
 	ScreenText(IllusionsEngine *vm);
 	~ScreenText();
 	void getTextInfoDimensions(WidthHeight &textInfoDimensions);
+	void getTextInfoPosition(Common::Point &position);
 	void setTextInfoPosition(Common::Point position);
 	void updateTextInfoPosition(Common::Point position);
 	void clipTextInfoPosition(Common::Point &position);
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 7d515a8..38fdbec 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -62,7 +62,7 @@ ScriptOpcodes::~ScriptOpcodes() {
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug(0, "\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
+	debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 5eebf00..5b1eea8 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -37,7 +37,7 @@ MusicPlayer::~MusicPlayer() {
 }
 
 void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
-	debug("MusicPlayer::play(%08X)", musicId);
+	debug(1, "MusicPlayer::play(%08X)", musicId);
 	if (_flags & 1) {
 		stop();
 		_musicId = musicId;
@@ -57,7 +57,7 @@ void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) {
 }
 
 void MusicPlayer::stop() {
-	debug("MusicPlayer::stop()");
+	debug(1, "MusicPlayer::stop()");
 	if ((_flags & 1) && (_flags & 2)) {
 		if (g_system->getMixer()->isSoundHandleActive(_soundHandle))
 			g_system->getMixer()->stopHandle(_soundHandle);
@@ -82,7 +82,7 @@ VoicePlayer::~VoicePlayer() {
 }
 
 bool VoicePlayer::cue(const char *voiceName) {
-	debug("VoicePlayer::cue(%s)", voiceName);
+	debug(1, "VoicePlayer::cue(%s)", voiceName);
 	_voiceName = voiceName;
 	_voiceStatus = 2;
 	if (!isEnabled()) {
@@ -147,7 +147,7 @@ void Sound::load() {
 }
 
 void Sound::unload() {
-	debug("Sound::unload() %08X", _soundEffectId);
+	debug(1, "Sound::unload() %08X", _soundEffectId);
 	stop();
 	delete _stream;
 	_stream = 0;
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index 61c4455..f1c93c7 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -161,7 +161,7 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 		}
 
 	}
-
+	
 	_dimensions->_width = maxLineWidth;
 	_dimensions->_height = textPosY - _font->_lineIncr;
 


Commit: 2b9af91d8fbda6f47d16d3fb0e7e6bb6206b016e
    https://github.com/scummvm/scummvm/commit/2b9af91d8fbda6f47d16d3fb0e7e6bb6206b016e
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcode 8

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/thread.cpp


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 0fa7361..618a6e4 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -67,13 +67,16 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(5, opJump);
 	OPCODE(6, opStartScriptThread);
 	OPCODE(7, opStartTimerThread);
+	OPCODE(8, opRerunThreads);
 	OPCODE(9, opNotifyThread);
 	OPCODE(10, opSuspendThread);
+	// 11-15 unused
 	OPCODE(16, opLoadResource);
 	OPCODE(17, opUnloadResource);
 	OPCODE(18, opEnterScene18);
 	OPCODE(19, opUnloadResourcesBySceneId);
 	OPCODE(20, opChangeScene);
+	// TODO OPCODE(21, );
 	OPCODE(22, opStartModalScene);
 	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
@@ -81,6 +84,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(26, opEnterDebugger);
 	OPCODE(27, opLeaveDebugger);
 	OPCODE(28, opDumpCurrentSceneFiles);
+	// 29-31 unused
 	OPCODE(32, opPanCenterObject);
 	OPCODE(33, opPanTrackObject);
 	OPCODE(34, opPanToObject);
@@ -90,6 +94,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(38, opStartFade);
 	OPCODE(39, opSetDisplay);
 	OPCODE(40, opSetCameraBounds);
+	// 41-47 unused
 	OPCODE(48, opSetProperty);
 	OPCODE(49, opPlaceActor);
 	OPCODE(50, opFaceActor);
@@ -104,25 +109,38 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(59, opActivateObject);
 	OPCODE(60, opDeactivateObject);
 	OPCODE(61, opSetDefaultSequence);
+	// 62-63 unused
 	OPCODE(64, opStopCursorHoldingObject);
 	OPCODE(65, opStartCursorHoldingObject);
 	OPCODE(66, opPlayVideo);
+	// 67-68 unused
 	OPCODE(69, opRunSpecialCode);
 	OPCODE(70, opPause);
 	OPCODE(71, opUnpause);
 	OPCODE(72, opStartSound);
+	// TODO OPCODE(73, );
+	// 74 unused
 	OPCODE(75, opStopSound);
 	OPCODE(76, opStartMidiMusic);
 	OPCODE(77, opStopMidiMusic);
 	OPCODE(78, opFadeMidiMusic);
+	// 79 unused
 	OPCODE(80, opAddMenuChoice);
 	OPCODE(81, opDisplayMenu);
 	OPCODE(82, opSwitchMenuChoice);
+	// TODO OPCODE(83, );
 	OPCODE(84, opResetGame);
+	// TODO OPCODE(85, );
+	// TODO OPCODE(86, );
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
+	// 89-95 unused
 	OPCODE(96, opIncBlockCounter);
 	OPCODE(97, opClearBlockCounter);
+	// 98-99 unused
+	// TODO OPCODE(100, );
+	// TODO OPCODE(101, );
+	// 102-103 unused
 	OPCODE(104, opJumpIf);
 	OPCODE(105, opIsPrevSceneId);
 	OPCODE(106, opNot);
@@ -130,6 +148,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(108, opOr);
 	OPCODE(109, opGetProperty);
 	OPCODE(110, opCompareBlockCounter);
+	// 111 unused
 	OPCODE(112, opAddDialogItem);
 	OPCODE(113, opStartDialog);
 	OPCODE(114, opJumpToDialogChoice);
@@ -137,6 +156,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(116, opSetBlockCounter116);
 	OPCODE(117, opSetBlockCounter117);
 	OPCODE(118, opSetBlockCounter118);
+	// 119-125 unused
 	OPCODE(126, opDebug126);
 	OPCODE(127, opDebug127);
 #if 0		
@@ -222,6 +242,10 @@ duration = 5;
 		_vm->startTimerThread(duration, opCall._threadId);
 }
 
+void ScriptOpcodes_Duckman::opRerunThreads(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_rerunThreads = true;
+}
+
 void ScriptOpcodes_Duckman::opNotifyThread(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(threadId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index d50f967..6b3b2ec 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -53,6 +53,7 @@ protected:
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opRerunThreads(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 28c678c..dbc0772 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -63,6 +63,8 @@ IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *
 	Engine(syst), _gameDescription(gd) {
 	
 	_random = new Common::RandomSource("illusions");
+
+	_rerunThreads = false;
 	
 	Engine::syncSoundSettings();
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index b3e8141..097a2c7 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -122,6 +122,7 @@ public:
 	bool _doScriptThreadInit;
 	ScriptStack *_stack;
 	ScriptResource *_scriptResource;
+	bool _rerunThreads;
 
 	Fader *_fader;
 
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index cea0b97..b7ea41b 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -157,11 +157,9 @@ void ThreadList::updateThreads() {
 				++it;
 			}
 		}
-		/* TODO
-		if (script->threadUpdateContinueFlag)
-			script->_threadUpdateContinueFlag = false;
+		if (_vm->_rerunThreads)
+			_vm->_rerunThreads = false;
 		else
-		*/
 			break;		
 	}
 }


Commit: 15498a231aca26c701fa5f123fbe2e0f1bd9cf67
    https://github.com/scummvm/scummvm/commit/15498a231aca26c701fa5f123fbe2e0f1bd9cf67
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcode 21

Changed paths:
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h
    engines/illusions/illusions.h


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 6ce367f..95be82f 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -463,6 +463,10 @@ uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, u
 	return tempThreadId;
 }
 
+void IllusionsEngine_BBDOU::resumeFromSavegame(uint32 callingThreadId) {
+	// TODO
+}
+
 uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
 	uint32 tempThreadId = newTempThreadId();
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index f2dbd1a..3b190ea 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -114,6 +114,8 @@ public:
 		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
+	void resumeFromSavegame(uint32 callingThreadId);
+	
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
 	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index db8f83c..272a7d6 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -694,6 +694,14 @@ uint32 IllusionsEngine_Duckman::startTempScriptThread(byte *scriptCodeIp, uint32
 	return tempThreadId;
 }
 
+void IllusionsEngine_Duckman::resumeFromSavegame(uint32 callingThreadId) {
+	_input->discardAllEvents();
+	if (changeScene(_savegameSceneId, _savegameThreadId, callingThreadId)) {
+		_savegameSceneId = 0;
+		_savegameThreadId = 0;
+	}
+}
+
 void IllusionsEngine_Duckman::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	byte *scriptCodeIp) {
 	ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index e2b3223..c946736 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -150,6 +150,8 @@ public:
 		uint32 sequenceId2, uint32 callingThreadId);
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
+	void resumeFromSavegame(uint32 callingThreadId);
+	
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp);
 	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 618a6e4..5abef68 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -76,7 +76,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(18, opEnterScene18);
 	OPCODE(19, opUnloadResourcesBySceneId);
 	OPCODE(20, opChangeScene);
-	// TODO OPCODE(21, );
+	OPCODE(21, opResumeFromSavegame);
 	OPCODE(22, opStartModalScene);
 	OPCODE(23, opExitModalScene);
 	OPCODE(24, opEnterScene24);
@@ -328,6 +328,10 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	}
 }
 
+void ScriptOpcodes_Duckman::opResumeFromSavegame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->resumeFromSavegame(opCall._callerThreadId);
+}
+
 void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index 6b3b2ec..1dbe6e6 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -57,6 +57,7 @@ protected:
 	void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opResumeFromSavegame(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 097a2c7..f188d5c 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -132,6 +132,9 @@ public:
 	uint32 _resGetTime;
 	bool _unpauseControlActorFlag;
 	uint32 _lastUpdateTime;
+	
+	uint32 _savegameSceneId;
+	uint32 _savegameThreadId;
 
 	uint32 _fontId;
 	int _field8;
@@ -195,6 +198,7 @@ public:
 	virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
 	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
+	virtual void resumeFromSavegame(uint32 callingThreadId) = 0;
 		
 #if 0
 


Commit: 5a69f7356130c08f6d0835e0ca6228d542d9fb73
    https://github.com/scummvm/scummvm/commit/5a69f7356130c08f6d0835e0ca6228d542d9fb73
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcode 73

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 5abef68..07f8381 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -118,7 +118,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(70, opPause);
 	OPCODE(71, opUnpause);
 	OPCODE(72, opStartSound);
-	// TODO OPCODE(73, );
+	OPCODE(73, opStartSoundAtPosition);
 	// 74 unused
 	OPCODE(75, opStopSound);
 	OPCODE(76, opStartMidiMusic);
@@ -631,6 +631,15 @@ void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opC
 	_vm->_soundMan->playSound(soundEffectId, volume, 0);
 }
 
+void ScriptOpcodes_Duckman::opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(volume);
+	ARG_UINT32(soundEffectId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	int16 pan = _vm->convertPanXCoord(pos.x);
+	_vm->_soundMan->playSound(soundEffectId, volume, pan);
+}
+
 void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(soundEffectId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index 1dbe6e6..63536b0 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -95,6 +95,7 @@ protected:
 	void opPause(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnpause(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall);


Commit: bbbb0053e03706bcdd75c9df2e290985730bfde8
    https://github.com/scummvm/scummvm/commit/bbbb0053e03706bcdd75c9df2e290985730bfde8
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcode 83

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 07f8381..24d923e 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -128,7 +128,7 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(80, opAddMenuChoice);
 	OPCODE(81, opDisplayMenu);
 	OPCODE(82, opSwitchMenuChoice);
-	// TODO OPCODE(83, );
+	OPCODE(83, opQuitGame);
 	OPCODE(84, opResetGame);
 	// TODO OPCODE(85, );
 	// TODO OPCODE(86, );
@@ -700,6 +700,10 @@ void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCal
 	opCall._deltaOfs += _vm->_menuChoiceOfs;
 }
 
+void ScriptOpcodes_Duckman::opQuitGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->quitGame();
+}
+
 void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->reset();
 	_vm->_input->activateButton(0xFFFF);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index 63536b0..f2a05e6 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -103,6 +103,7 @@ protected:
 	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
 	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opQuitGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);


Commit: 9a84c0a62e0f47e5c01b3ed0e21f34107fadc9eb
    https://github.com/scummvm/scummvm/commit/9a84c0a62e0f47e5c01b3ed0e21f34107fadc9eb
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcode 85 and 86, actual save/load not implemented yet

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 272a7d6..f760441 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -1107,4 +1107,34 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	return tempThreadId;
 }
 
+bool IllusionsEngine_Duckman::loadSavegame(int16 slotNum, uint32 callingThreadId) {
+#if 0	
+	// TODO
+	bool success = _gameStates->load(slotNum);
+	if (success) {
+		_vm->_screen->setDisplayOn(false);
+		uint32 currSceneId = getCurrentScene();
+		if (currSceneId != 0x10003)
+			dumpCurrSceneFiles(currSceneId, callerThreadId);
+		reset();
+		stopMidi();
+		clearMidiPlayList();
+		_gameStates->readStates();
+		pushActiveScene(0x10000);
+	}
+	_gameStates->freeGameStateReadBuffer();
+	return success;
+#endif	
+	return true;
+}
+
+bool IllusionsEngine_Duckman::saveSavegame(int16 slotNum, uint32 callingThreadId) {
+#if 0
+	// TODO
+	bool success = _gameStates->save(slotNum);
+	return success;
+#endif	
+	return true;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index c946736..129069a 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -185,6 +185,9 @@ public:
 	void playSoundEffect(int index);
 	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
 	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
+	
+	bool loadSavegame(int16 slotNum, uint32 callingThreadId);
+	bool saveSavegame(int16 slotNum, uint32 callingThreadId);
 
 };
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 24d923e..f16998f 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -130,8 +130,8 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(82, opSwitchMenuChoice);
 	OPCODE(83, opQuitGame);
 	OPCODE(84, opResetGame);
-	// TODO OPCODE(85, );
-	// TODO OPCODE(86, );
+	OPCODE(85, opLoadGame);
+	OPCODE(86, opSaveGame);
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	// 89-95 unused
@@ -711,6 +711,22 @@ void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCa
 	// TODO _vm->_gameStates->clear();
 }
 
+void ScriptOpcodes_Duckman::opLoadGame(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(bankNum)
+	ARG_INT16(slotNum)
+	bool success = _vm->loadSavegame(slotNum, opCall._callerThreadId);
+	_vm->_stack->push(success ? 1 : 0);
+}
+
+void ScriptOpcodes_Duckman::opSaveGame(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(bankNum)
+	ARG_INT16(slotNum)
+	bool success = _vm->saveSavegame(slotNum, opCall._callerThreadId);
+	_vm->_stack->push(success ? 1 : 0);
+}
+
 void ScriptOpcodes_Duckman::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(button)
 	_vm->_input->deactivateButton(button);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index f2a05e6..29728e3 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -105,6 +105,8 @@ protected:
 	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
 	void opQuitGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opSaveGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);


Commit: 449d243c8fec19fe7186941bda2c4076730190ff
    https://github.com/scummvm/scummvm/commit/449d243c8fec19fe7186941bda2c4076730190ff
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement opcode 100 and 101

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index f16998f..128264b 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -138,8 +138,8 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(96, opIncBlockCounter);
 	OPCODE(97, opClearBlockCounter);
 	// 98-99 unused
-	// TODO OPCODE(100, );
-	// TODO OPCODE(101, );
+	OPCODE(100, opStackPushRandom);
+	OPCODE(101, opStackSwitchRandom);
 	// 102-103 unused
 	OPCODE(104, opJumpIf);
 	OPCODE(105, opIsPrevSceneId);
@@ -172,8 +172,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	OPCODE(65, opSetDenySfx);
 	OPCODE(66, opSetAdjustUpSfx);
 	OPCODE(67, opSetAdjustDnSfx);
-	OPCODE(78, opStackPushRandom);
-	OPCODE(79, opIfLte);
 	OPCODE(105, opIsCurrentSceneId);
 	OPCODE(106, opIsActiveSceneId);
 	OPCODE(146, opStackPop);
@@ -749,6 +747,22 @@ void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCa
 	_vm->_scriptResource->_blockCounters.set(index, 0);
 }
 
+void ScriptOpcodes_Duckman::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(maxValue);
+	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
+}
+
+void ScriptOpcodes_Duckman::opStackSwitchRandom(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(rvalue);
+	ARG_INT16(jumpOffs);
+	int16 lvalue = _vm->_stack->peek();
+	if (lvalue < rvalue) {
+		_vm->_stack->pop();
+		opCall._deltaOfs += jumpOffs;
+	}
+}
+
 void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(jumpOffs);
 	int16 value = _vm->_stack->pop();
@@ -952,20 +966,6 @@ void ScriptOpcodes_Duckman::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall
 	// TODO _vm->setAdjustDnSfx(soundEffectId);
 }
 
-void ScriptOpcodes_Duckman::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(maxValue);
-	_vm->_stack->push(_vm->getRandom(maxValue) + 1);
-}
-
-void ScriptOpcodes_Duckman::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(rvalue);
-	ARG_INT16(elseJumpOffs);
-	int16 lvalue = _vm->_stack->pop();
-	if (!(lvalue <= rvalue))
-		opCall._deltaOfs += elseJumpOffs;
-}
-
 void ScriptOpcodes_Duckman::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index 29728e3..0fae4b7 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -111,6 +111,8 @@ protected:
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
+	void opStackSwitchRandom(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opNot(ScriptThread *scriptThread, OpCall &opCall);
@@ -141,8 +143,6 @@ protected:
 	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
-	void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
-	void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);


Commit: 9c0ef1bb11508f15bd4e5b710ccbbba6dfa5341e
    https://github.com/scummvm/scummvm/commit/9c0ef1bb11508f15bd4e5b710ccbbba6dfa5341e
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Remove if'd out opcode code (used by me as scratch pad); now all opcodes should be callable, though not all are completely implemented yet

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 128264b..ffd1645 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -159,34 +159,6 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 	// 119-125 unused
 	OPCODE(126, opDebug126);
 	OPCODE(127, opDebug127);
-#if 0		
-	OPCODE(8, opStartTempScriptThread);
-	OPCODE(14, opSetThreadSceneId);
-	OPCODE(15, opEndTalkThreads);
-	OPCODE(20, opEnterScene);
-	OPCODE(30, opEnterCloseUpScene);
-	OPCODE(31, opExitCloseUpScene);
-	OPCODE(53, opSetActorToNamedPoint);
-	OPCODE(63, opSetSelectSfx);
-	OPCODE(64, opSetMoveSfx);
-	OPCODE(65, opSetDenySfx);
-	OPCODE(66, opSetAdjustUpSfx);
-	OPCODE(67, opSetAdjustDnSfx);
-	OPCODE(105, opIsCurrentSceneId);
-	OPCODE(106, opIsActiveSceneId);
-	OPCODE(146, opStackPop);
-	OPCODE(147, opStackDup);
-	OPCODE(148, opLoadSpecialCodeModule);
-	OPCODE(160, opStopActor);
-	OPCODE(161, opSetActorUsePan);
-	OPCODE(168, opStartAbortableThread);
-	OPCODE(169, opKillThread);
-	OPCODE(175, opSetSceneIdThreadId);
-	OPCODE(176, opStackPush0);
-	OPCODE(177, opSetFontId);
-	OPCODE(178, opAddMenuKey);
-	OPCODE(179, opChangeSceneAll);
-#endif	
 }
 
 #undef OPCODE
@@ -881,183 +853,4 @@ void ScriptOpcodes_Duckman::opDebug127(ScriptThread *scriptThread, OpCall &opCal
 	debug(1, "[DBG127] %s", (char*)opCall._code);
 }
 
-#if 0
-
-void ScriptOpcodes_Duckman::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(codeOffs);
-	_vm->startTempScriptThread(opCall._code + codeOffs,
-		opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-void ScriptOpcodes_Duckman::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
-}
-
-void ScriptOpcodes_Duckman::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_threads->endTalkThreads();
-}
-
-void ScriptOpcodes_Duckman::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
-	if (scenesCount > 0) {
-		uint32 currSceneId;
-		_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, 0);
-		// TODO krnfileDump(currSceneId);
-	}
-	if (!_vm->enterScene(sceneId, opCall._callerThreadId))
-		opCall._result = kTSTerminate;
-}
-
-void ScriptOpcodes_Duckman::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_input->discardAllEvents();
-	_vm->enterPause(opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-}
-
-void ScriptOpcodes_Duckman::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->exitScene(opCall._callerThreadId);
-	_vm->leavePause(opCall._callerThreadId);
-	opCall._result = kTSYield;
-}
-
-void ScriptOpcodes_Duckman::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	ARG_UINT32(namedPointId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
-	control->stopActor();
-	control->setActorPosition(pos);
-}
-
-void ScriptOpcodes_Duckman::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setSelectSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setMoveSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setDenySfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustUpSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(soundEffectId);
-	// TODO _vm->setAdjustDnSfx(soundEffectId);
-}
-
-void ScriptOpcodes_Duckman::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
-}
-
-void ScriptOpcodes_Duckman::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->pop(); 
-}
-
-void ScriptOpcodes_Duckman::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
-	int16 value = _vm->_stack->peek();
-	_vm->_stack->push(value); 
-}
-
-void ScriptOpcodes_Duckman::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(specialCodeModuleId);
-	_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
-}
-
-void ScriptOpcodes_Duckman::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->stopActor();
-}
-
-void ScriptOpcodes_Duckman::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(usePan)
-	ARG_UINT32(objectId);
-	Control *control = _vm->_dict->getObjectControl(objectId);
-	control->setActorUsePan(usePan);
-}
-
-void ScriptOpcodes_Duckman::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_INT16(codeOffs);
-	ARG_INT16(skipOffs);
-	_vm->startAbortableThread(opCall._code + codeOffs,
-		opCall._code + skipOffs, opCall._threadId);
-}
-
-void ScriptOpcodes_Duckman::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(threadId);
-	_vm->_threads->killThread(threadId);
-}
-
-void ScriptOpcodes_Duckman::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->setSceneIdThreadId(sceneId, threadId);
-}
-
-void ScriptOpcodes_Duckman::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->push(0);
-}
-
-void ScriptOpcodes_Duckman::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(fontId);
-	_vm->setCurrFontId(fontId);
-}
-
-void ScriptOpcodes_Duckman::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(key);
-	ARG_UINT32(threadId);
-	// TODO _vm->addMenuKey(key, threadId);
-}
-
-void ScriptOpcodes_Duckman::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_SKIP(2);
-	ARG_UINT32(sceneId);
-	ARG_UINT32(threadId);
-	_vm->_input->discardAllEvents();
-	_vm->_prevSceneId = _vm->getCurrentScene();
-	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
-	_vm->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
-	_vm->startAnonScriptThread(threadId, 0,
-		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
-}
-
-#endif
-
 } // End of namespace Illusions


Commit: f692e0acfbe1e0a2266502348da7576a0c4f89a1
    https://github.com/scummvm/scummvm/commit/f692e0acfbe1e0a2266502348da7576a0c4f89a1
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement special opcodes 16001D, 16001E, 16001F and related code

Changed paths:
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index d0d8a5c..4cb20fc 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -3,7 +3,7 @@
  * ScummVM is the legal property of its developers, whose names
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
- *
+ *                                                                                                                                  
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
@@ -66,6 +66,9 @@ void DuckmanSpecialCode::init() {
 	SPECIAL(0x00160010, spcCenterNewspaper);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
+	SPECIAL(0x0016001D, spcCenterCurrentScreenText);
+	SPECIAL(0x0016001E, spcSetDefaultTextCoords);
+	SPECIAL(0x0016001F, spcSetTextDuration);
 }
 
 #undef SPECIAL
@@ -157,13 +160,6 @@ void DuckmanSpecialCode::spcCenterNewspaper(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
-void DuckmanSpecialCode::spcSetCursorInventoryMode(OpCall &opCall) {
-	ARG_BYTE(mode);
-	ARG_BYTE(value);
-	_vm->setCursorInventoryMode(mode, value);
-	_vm->notifyThreadId(opCall._threadId);
-}
-
 void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
 	byte flags = 0;
 	uint32 sequenceId;
@@ -206,4 +202,31 @@ void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcSetCursorInventoryMode(OpCall &opCall) {
+	ARG_BYTE(mode);
+	ARG_BYTE(value);
+	_vm->setCursorInventoryMode(mode, value);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcCenterCurrentScreenText(OpCall &opCall) {
+	WidthHeight dimensions;
+	_vm->getDefaultTextDimensions(dimensions);
+	Common::Point pt(160, dimensions._height / 2 + 8);
+	_vm->setDefaultTextPosition(pt);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcSetDefaultTextCoords(OpCall &opCall) {
+	_vm->setDefaultTextCoords();
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcSetTextDuration(OpCall &opCall) {
+	ARG_INT16(kind);
+	ARG_INT16(duration);
+	_vm->setTextDuration(kind, duration);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index 53012d6..c4dfc36 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -67,8 +67,11 @@ public:
 	void spcSetPropertyTimer(OpCall &opCall);
 	void spcRemovePropertyTimer(OpCall &opCall);
 	void spcCenterNewspaper(OpCall &opCall);
-	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
+	void spcSetCursorInventoryMode(OpCall &opCall);
+	void spcCenterCurrentScreenText(OpCall &opCall);
+	void spcSetDefaultTextCoords(OpCall &opCall);
+	void spcSetTextDuration(OpCall &opCall);
 
 };
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index dbc0772..c4af5b5 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -303,6 +303,22 @@ bool IllusionsEngine::checkActiveTalkThreads() {
 	return _threads->isActiveThread(kMsgQueryTalkThreadActive);
 }
 
+void IllusionsEngine::setTextDuration(int kind, uint32 duration) {
+	_field8 = kind;
+	switch (_field8) {
+	case 1:
+	case 2:
+		_fieldA = 0;
+		break;
+	case 3:
+	case 4:
+		_fieldA = duration;
+		break;
+	default:
+		break;
+	}
+}
+
 uint32 IllusionsEngine::clipTextDuration(uint32 duration) {
 	switch (_field8) {
 	case 2:
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index f188d5c..9914936 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -170,6 +170,7 @@ public:
 
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
+	void setTextDuration(int kind, uint32 duration);
 	uint32 clipTextDuration(uint32 duration);
 	void getDefaultTextDimensions(WidthHeight &dimensions);
 	void setDefaultTextDimensions(WidthHeight &dimensions);


Commit: d16ebff3901b3731ec48d6dfda6b91acbdb11ff5
    https://github.com/scummvm/scummvm/commit/d16ebff3901b3731ec48d6dfda6b91acbdb11ff5
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement special opcode 160012 and related code

Changed paths:
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/menusystem.cpp
    engines/illusions/scriptopcodes.cpp


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index 4cb20fc..b23cc44 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -63,7 +63,10 @@ void DuckmanSpecialCode::init() {
 	SPECIAL(0x0016000A, spcAddPropertyTimer);
 	SPECIAL(0x0016000B, spcSetPropertyTimer);
 	SPECIAL(0x0016000C, spcRemovePropertyTimer);
+	SPECIAL(0x0016000E, spcInitTeleporterPosition);
+	SPECIAL(0x0016000F, spcUpdateTeleporterPosition);
 	SPECIAL(0x00160010, spcCenterNewspaper);
+	SPECIAL(0x00160012, spcStopScreenShaker);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 	SPECIAL(0x0016001D, spcCenterCurrentScreenText);
@@ -152,6 +155,64 @@ void DuckmanSpecialCode::spcRemovePropertyTimer(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcInitTeleporterPosition(OpCall &opCall) {
+	_teleporterPosition.x = 4;
+	_teleporterPosition.y = 3;
+	updateTeleporterProperties();
+	_vm->_scriptResource->_properties.set(0x000E007A, false);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcUpdateTeleporterPosition(OpCall &opCall) {
+	// TODO
+	ARG_BYTE(direction);
+	int16 deltaX = 0;
+	int16 deltaY = 0;
+	uint32 sequenceId = 0;
+	
+	Control *control = _vm->getObjectControl(0x400C0);
+	switch (direction) {
+	case 1:
+		if (_teleporterPosition.y > 1) {
+			deltaY = -1;
+			sequenceId = 0x60386;
+		}
+		break;
+	case 4:
+		if (_teleporterPosition.x < 4) {
+			deltaX = 1;
+			sequenceId = 0x60387;
+		}
+		break;
+	case 0x10:
+		if (_teleporterPosition.y < 3) {
+			deltaY = 1;
+			sequenceId = 0x60385;
+		}
+		break;
+	case 0x40:
+		if (_teleporterPosition.x > 1) {
+			deltaX = -1;
+			sequenceId = 0x60388;
+		}
+		break;
+	default:
+		break;
+	}
+	
+	if (sequenceId) {
+		control->startSequenceActor(sequenceId, 2, opCall._threadId);
+		_teleporterPosition.x += deltaX;
+		_teleporterPosition.y += deltaY;
+		updateTeleporterProperties();
+		_vm->_scriptResource->_properties.set(0x000E007A, false);
+	} else {
+		_vm->notifyThreadId(opCall._threadId);
+	}
+
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::spcCenterNewspaper(OpCall &opCall) {
 	Control *control = _vm->getObjectControl(0x40017);
 	control->_flags |= 8;
@@ -160,6 +221,11 @@ void DuckmanSpecialCode::spcCenterNewspaper(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcStopScreenShaker(OpCall &opCall) {
+	_vm->stopScreenShaker();
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
 	byte flags = 0;
 	uint32 sequenceId;
@@ -229,4 +295,12 @@ void DuckmanSpecialCode::spcSetTextDuration(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void DuckmanSpecialCode::updateTeleporterProperties() {
+	_vm->_scriptResource->_properties.set(0x000E0074, _teleporterPosition.x == 4 && _teleporterPosition.y == 2);
+	_vm->_scriptResource->_properties.set(0x000E0075, _teleporterPosition.x == 4 && _teleporterPosition.y == 3);
+	_vm->_scriptResource->_properties.set(0x000E0076, _teleporterPosition.x == 3 && _teleporterPosition.y == 3);
+	_vm->_scriptResource->_properties.set(0x000E0077, _teleporterPosition.x == 2 && _teleporterPosition.y == 2);
+	_vm->_scriptResource->_properties.set(0x000E0078, _teleporterPosition.x == 1 && _teleporterPosition.y == 1);	
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index c4dfc36..2d05da4 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -51,6 +51,8 @@ public:
 	uint _chinesePuzzleIndex;
 	byte _chinesePuzzleAnswers[3];
 	
+	Common::Point _teleporterPosition;
+	
 	PropertyTimers *_propertyTimers;
 	DuckmanInventory *_inventory;
 
@@ -66,13 +68,18 @@ public:
 	void spcAddPropertyTimer(OpCall &opCall);
 	void spcSetPropertyTimer(OpCall &opCall);
 	void spcRemovePropertyTimer(OpCall &opCall);
+	void spcInitTeleporterPosition(OpCall &opCall);
+	void spcUpdateTeleporterPosition(OpCall &opCall);
 	void spcCenterNewspaper(OpCall &opCall);
+	void spcStopScreenShaker(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcCenterCurrentScreenText(OpCall &opCall);
 	void spcSetDefaultTextCoords(OpCall &opCall);
 	void spcSetTextDuration(OpCall &opCall);
 
+	void updateTeleporterProperties();
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index f760441..e4c11bc 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -167,6 +167,11 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_scriptResource->_properties.set(0x000E0024, true);
 #endif
 
+#if 1
+	// DEBUG Enterprise
+	_scriptResource->_blockCounters.set(238, 1);
+#endif
+
 	while (!shouldQuit()) {
 		runUpdateFunctions();
 		_system->updateScreen();
@@ -261,12 +266,6 @@ int IllusionsEngine_Duckman::updateScript(uint flags) {
 	if (_screen->isDisplayOn() && !_screen->isFaderActive() && _pauseCtr == 0) {
 		if (_input->pollEvent(kEventAbort)) {
 			startScriptThread(0x00020342, 0);
-			//testMenu(this);//TODO DEBUG
-			
-			//BaseMenu *me = _menuSystem->getMenuById(kDuckmanPauseMenu);
-			//_menuSystem->openMenu(me);
-			//_menuSystem->runMenu(0x180002);
-			
 		} else if (_input->pollEvent(kEventF1)) {
 			startScriptThread(0x0002033F, 0);
 		}
@@ -289,6 +288,10 @@ void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duratio
 		(this, &IllusionsEngine_Duckman::updateScreenShaker));
 }
 
+void IllusionsEngine_Duckman::stopScreenShaker() {
+	_screenShaker->_finished = true;
+}
+
 int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
 	if (_pauseCtr > 0 || getCurrentScene() == 0x10038) {
 		_screenShaker->_nextTime = getCurrentTime();
@@ -846,7 +849,8 @@ bool IllusionsEngine_Duckman::findTriggerCause(uint32 sceneId, uint32 verbId, ui
 void IllusionsEngine_Duckman::reset() {
 	_scriptResource->_blockCounters.clear();
 	_scriptResource->_properties.clear();
-	// TODO script_sub_417FF0(1, 0);
+	setTextDuration(1, 0);
+	// TODO resetCursor();
 }
 
 uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
@@ -887,6 +891,8 @@ void IllusionsEngine_Duckman::updateGameState2() {
 	Control *overlappedControl;
 
 	_cursor._control->_actor->_position = cursorPos;
+	
+	debug("IllusionsEngine_Duckman::updateGameState2() #1");
 
 	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
 
@@ -907,6 +913,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		startCursorSequence();
 	}
 
+	debug("IllusionsEngine_Duckman::updateGameState2() #2");
 	if (trackCursorIndex >= 0) {
 		if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
 			_cursor._savedActorIndex = _cursor._actorIndex;
@@ -919,6 +926,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		foundOverlapped = false;
 	}
 
+	debug("IllusionsEngine_Duckman::updateGameState2() #3");
 	if (foundOverlapped) {
 		if (_cursor._currOverlappedControl != overlappedControl) {
 			int cursorValue2 = 0;
@@ -944,7 +952,9 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		_cursor._currOverlappedControl = 0;
 	}
 
+	debug("IllusionsEngine_Duckman::updateGameState2() #4");
 	if (_input->pollEvent(kEventLeftClick)) {
+	debug("IllusionsEngine_Duckman::updateGameState2() #5");
 		if (_cursor._currOverlappedControl) {
 			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
 		} else {
@@ -956,6 +966,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
 		}
 	} else if (_input->pollEvent(kEventRightClick)) {
+	debug("IllusionsEngine_Duckman::updateGameState2() #6");
 		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
 			int newActorIndex = getCursorActorIndex();
 			if (newActorIndex != _cursor._actorIndex) {
@@ -968,12 +979,14 @@ void IllusionsEngine_Duckman::updateGameState2() {
 			}
 		}
 	} else if (_input->pollEvent(kEventInventory)) {
+	debug("IllusionsEngine_Duckman::updateGameState2() #7");
 		if (_cursor._field14[0] == 1) {
 			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
 		} else if (_cursor._field14[1] == 1) {
 			runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
 		}
 	}
+	debug("IllusionsEngine_Duckman::updateGameState2() #XXX");
 
 }
 
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 129069a..87520d3 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -108,6 +108,7 @@ public:
 	int updateScript(uint flags);
 
 	void startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId);
+	void stopScreenShaker();
 	int updateScreenShaker(uint flags);
 
 	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 96d350a..666005d 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -40,6 +40,8 @@ DuckmanMenuSystem::~DuckmanMenuSystem() {
 
 void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
 	uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
+	
+	debug("DuckmanMenuSystem::runMenu(%08X)", menuId);
 
 	setTimeOutDuration(duration, timeOutMenuChoiceIndex);
 	setMenuCallerThreadId(menuCallerThreadId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index ffd1645..382a128 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -204,7 +204,8 @@ void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCal
 		duration += _vm->getRandom(maxDuration);
 		
 //duration = 1;//DEBUG Speeds up things
-duration = 5;
+//duration = 5;
+debug("duration: %d", duration);
 		
 	if (isAbortable)
 		_vm->startAbortableTimerThread(duration, opCall._threadId);
@@ -258,7 +259,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
@@ -273,6 +274,7 @@ static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
 //static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
+static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -283,13 +285,13 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	debug(1, "changeScene(%08X, %08X)", sceneId, threadId);
 	
 	//DEBUG
-	/*
+#if 1
 	if (dsceneId) {
 		sceneId = dsceneId;
 		threadId = dthreadId;
 		dsceneId = 0;
 	}
-	*/
+#endif
 	
 	if (_vm->_scriptResource->_properties.get(31)) {
 		_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
@@ -645,6 +647,7 @@ void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &op
 	ARG_UINT32(menuId);
 	ARG_UINT32(timeOutMenuChoiceIndex);
 	
+	debug("menuId: %08X", menuId);
 	debug("timeOutMenuChoiceIndex: %d", timeOutMenuChoiceIndex);
 	
 	MenuChoiceOffsets menuChoiceOffsets;
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index f26a4612..01b9220 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -105,6 +105,8 @@ void BaseMenuSystem::playSoundEffect14() {
 }
 
 void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) {
+	debug("choiceIndex: %d", choiceIndex);
+	debug("_menuChoiceOffset: %p", (void*)_menuChoiceOffset);
 	if (choiceIndex > 0 && _menuChoiceOffset) {
 		*_menuChoiceOffset = _menuChoiceOffsets[choiceIndex - 1];
 		debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset);		
@@ -211,7 +213,7 @@ bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIn
 }
 
 void BaseMenuSystem::setMousePos(Common::Point &mousePos) {
-	_vm->_input->setCursorPosition(mousePos);
+	//TODO Strange behavior _vm->_input->setCursorPosition(mousePos);
 	Control *mouseCursor = _vm->getObjectControl(0x40004);
 	mouseCursor->_actor->_position = mousePos;
 }
@@ -372,6 +374,7 @@ void BaseMenuSystem::closeMenu() {
 }
 
 void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) {
+	debug("BaseMenuSystem::handleClick() menuItemIndex: %d", menuItemIndex);
 
 	if (menuItemIndex == 0) {
 	    playSoundEffect14();
@@ -439,15 +442,17 @@ void BaseMenuSystem::update(Control *cursorControl) {
 	uint newHoveredMenuItemIndex;
 	bool resetTimeOut = false;
 	
-	if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex) && newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
-		if (_hoveredMenuItemIndex == 0)
-			initActor318();
-		_hoveredMenuItemIndex = newHoveredMenuItemIndex;
-		_hoveredMenuItemIndex2 = newHoveredMenuItemIndex;
-		setMenuCursorNum(2);
-		updateActor318();
-		resetTimeOut = true;
-	} else if (_hoveredMenuItemIndex == 0) {
+	if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex)) {
+		if (newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
+			if (_hoveredMenuItemIndex == 0)
+				initActor318();
+			_hoveredMenuItemIndex = newHoveredMenuItemIndex;
+			_hoveredMenuItemIndex2 = newHoveredMenuItemIndex;
+			setMenuCursorNum(2);
+			updateActor318();
+			resetTimeOut = true;
+		}
+	} else if (_hoveredMenuItemIndex != 0) {
 		setMenuCursorNum(1);
 		hideActor318();
 		_hoveredMenuItemIndex = 0;
@@ -511,12 +516,9 @@ void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
 			_timeOutStartTime = getCurrentTime();
 			_timeOutEndTime = _timeOutDuration + _timeOutStartTime;
 		} else if (isTimerExpired(_timeOutStartTime, _timeOutEndTime)) {
+			debug("timeout reached");
 			_isTimeOutEnabled = false;
 			selectMenuChoiceIndex(_timeOutMenuChoiceIndex);
-			// TODO *_menuChoiceOffset = *((_WORD *)&_menuCallerThreadId + _timeOutMenuChoiceIndex + 1);
-			//_vm->_threads->notifyId(_menuCallerThreadId);
-			//_menuCallerThreadId = 0;
-			//closeMenu();
 		}
 	}
 
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 38fdbec..7d515a8 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -62,7 +62,7 @@ ScriptOpcodes::~ScriptOpcodes() {
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
+	debug(0, "\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 


Commit: 11ec6d2258353cca43ae8a43085a1565e896e1e9
    https://github.com/scummvm/scummvm/commit/11ec6d2258353cca43ae8a43085a1565e896e1e9
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement special opcode 160013, also fix Input and reduce debug output

Changed paths:
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/input.cpp
    engines/illusions/input.h
    engines/illusions/screentext.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index b23cc44..6b67f12 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -67,6 +67,7 @@ void DuckmanSpecialCode::init() {
 	SPECIAL(0x0016000F, spcUpdateTeleporterPosition);
 	SPECIAL(0x00160010, spcCenterNewspaper);
 	SPECIAL(0x00160012, spcStopScreenShaker);
+	SPECIAL(0x00160013, spcIncrCounter);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 	SPECIAL(0x0016001D, spcCenterCurrentScreenText);
@@ -226,6 +227,20 @@ void DuckmanSpecialCode::spcStopScreenShaker(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcIncrCounter(OpCall &opCall) {
+	ARG_BYTE(maxCount);
+	ARG_BYTE(incr);
+	_vm->_scriptResource->_properties.set(0x000E0088, false);
+	if (incr) {
+		_counter += incr;
+		if (_counter >= maxCount)
+			_vm->_scriptResource->_properties.set(0x000E0088, true);
+	} else {
+		_counter = 0;
+	}
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
 	byte flags = 0;
 	uint32 sequenceId;
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index 2d05da4..b8bf79c 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -55,6 +55,8 @@ public:
 	
 	PropertyTimers *_propertyTimers;
 	DuckmanInventory *_inventory;
+	
+	int16 _counter;
 
 	// Special code interface functions
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
@@ -72,6 +74,7 @@ public:
 	void spcUpdateTeleporterPosition(OpCall &opCall);
 	void spcCenterNewspaper(OpCall &opCall);
 	void spcStopScreenShaker(OpCall &opCall);
+	void spcIncrCounter(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcCenterCurrentScreenText(OpCall &opCall);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index e4c11bc..4f21e6b 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -289,7 +289,8 @@ void IllusionsEngine_Duckman::startScreenShaker(uint pointsCount, uint32 duratio
 }
 
 void IllusionsEngine_Duckman::stopScreenShaker() {
-	_screenShaker->_finished = true;
+	if (_screenShaker)
+		_screenShaker->_finished = true;
 }
 
 int IllusionsEngine_Duckman::updateScreenShaker(uint flags) {
@@ -892,8 +893,6 @@ void IllusionsEngine_Duckman::updateGameState2() {
 
 	_cursor._control->_actor->_position = cursorPos;
 	
-	debug("IllusionsEngine_Duckman::updateGameState2() #1");
-
 	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
 
 	if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
@@ -913,7 +912,6 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		startCursorSequence();
 	}
 
-	debug("IllusionsEngine_Duckman::updateGameState2() #2");
 	if (trackCursorIndex >= 0) {
 		if (_cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13 && _cursor._actorIndex != 3)
 			_cursor._savedActorIndex = _cursor._actorIndex;
@@ -926,7 +924,6 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		foundOverlapped = false;
 	}
 
-	debug("IllusionsEngine_Duckman::updateGameState2() #3");
 	if (foundOverlapped) {
 		if (_cursor._currOverlappedControl != overlappedControl) {
 			int cursorValue2 = 0;
@@ -952,9 +949,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 		_cursor._currOverlappedControl = 0;
 	}
 
-	debug("IllusionsEngine_Duckman::updateGameState2() #4");
 	if (_input->pollEvent(kEventLeftClick)) {
-	debug("IllusionsEngine_Duckman::updateGameState2() #5");
 		if (_cursor._currOverlappedControl) {
 			runTriggerCause(_cursor._actorIndex, _cursor._objectId, _cursor._currOverlappedControl->_objectId);
 		} else {
@@ -966,7 +961,6 @@ void IllusionsEngine_Duckman::updateGameState2() {
 				runTriggerCause(_cursor._actorIndex, _cursor._objectId, 0x40003);
 		}
 	} else if (_input->pollEvent(kEventRightClick)) {
-	debug("IllusionsEngine_Duckman::updateGameState2() #6");
 		if (_cursor._actorIndex != 3 && _cursor._actorIndex != 10 && _cursor._actorIndex != 11 && _cursor._actorIndex != 12 && _cursor._actorIndex != 13) {
 			int newActorIndex = getCursorActorIndex();
 			if (newActorIndex != _cursor._actorIndex) {
@@ -979,14 +973,12 @@ void IllusionsEngine_Duckman::updateGameState2() {
 			}
 		}
 	} else if (_input->pollEvent(kEventInventory)) {
-	debug("IllusionsEngine_Duckman::updateGameState2() #7");
 		if (_cursor._field14[0] == 1) {
 			runTriggerCause(1, 0, _scriptResource->getMainActorObjectId());
 		} else if (_cursor._field14[1] == 1) {
 			runTriggerCause(2, 0, _scriptResource->getMainActorObjectId());
 		}
 	}
-	debug("IllusionsEngine_Duckman::updateGameState2() #XXX");
 
 }
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 382a128..60256b9 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -205,7 +205,7 @@ void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCal
 		
 //duration = 1;//DEBUG Speeds up things
 //duration = 5;
-debug("duration: %d", duration);
+//debug("duration: %d", duration);
 		
 	if (isAbortable)
 		_vm->startAbortableTimerThread(duration, opCall._threadId);
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 37da087..56ce27a 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -44,6 +44,9 @@ void KeyMap::add(Common::KeyCode key, int mouseButton) {
 
 // InputEvent
 
+InputEvent::InputEvent() : _bitMask(0) {
+}
+
 InputEvent& InputEvent::setBitMask(uint bitMask) {
 	_bitMask = bitMask;
 	return *this;
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index 7d01ea6..9956039 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -64,6 +64,7 @@ protected:
 
 class InputEvent {
 public:
+	InputEvent();
 	InputEvent& setBitMask(uint bitMask);
 	InputEvent& addKey(Common::KeyCode key);
 	InputEvent& addMouseButton(int mouseButton);
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 7c39823..9fa3d40 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -90,7 +90,6 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 	bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr);
 	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
 	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1());
-	debug("ScreenText dimensions (%d, %d)", dimensions._width, dimensions._height);
 	_dimensions = dimensions;
 	textDrawer.drawText(_vm->_screen, _surface, color2, color1);
 	return done;
@@ -126,11 +125,9 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	_screenTexts.push_back(screenText);
 
 	FontResource *font = _vm->_dict->findFont(screenText->_info._fontId);
-	debug("font: %p", font);
 	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
 		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
 		outTextPtr);
-	debug("font->getColorIndex(): %d", font->getColorIndex());
 	_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 
 	uint16 *textPart = screenText->_text;
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index fe08850..8d678eb 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -69,6 +69,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(10, opStartForeignSequence);
 	OPCODE(11, opBeginLoop);
 	OPCODE(12, opNextLoop);
+	OPCODE(13, opSetActorIndex);
 	OPCODE(14, opSwitchActorIndex);
 	OPCODE(15, opSwitchFacing);
 	OPCODE(16, opAppearActor);
@@ -77,9 +78,13 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(19, opDisappearForeignActor);
 	OPCODE(20, opSetNamedPointPosition);
 	OPCODE(21, opMoveDelta);
+	// 22-24 unused in Duckman, CHECKME BBDOU
 	OPCODE(25, opFaceActor);
+	// 26-27 unused in Duckman, CHECKME BBDOU
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
+	// 30-31 unused in Duckman, CHECKME BBDOU
+	// TODO OPCODE(32, );
 	OPCODE(33, opSetPathWalkPoints);
 	OPCODE(34, opDisableAutoScale);
 	OPCODE(35, opSetScale);
@@ -90,6 +95,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(40, opSetPriorityLayer);
 	OPCODE(41, opDisableAutoRegionLayer);
 	OPCODE(42, opSetRegionLayer);
+	// 43-47 unused in Duckman, CHECKME BBDOU
 	OPCODE(48, opSetPalette);
 	OPCODE(49, opShiftPalette);
 	OPCODE(50, opPlaySound);
@@ -213,6 +219,11 @@ void SequenceOpcodes::opNextLoop(Control *control, OpCall &opCall) {
 	}
 }
 
+void SequenceOpcodes::opSetActorIndex(Control *control, OpCall &opCall) {
+	ARG_BYTE(actorIndex);
+	control->setActorIndex(actorIndex);
+}
+
 void SequenceOpcodes::opSwitchActorIndex(Control *control, OpCall &opCall) {
 	ARG_INT16(actorIndex);
 	ARG_INT16(jumpOffs);
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 4d5cb7a..85e5df6 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -57,6 +57,7 @@ protected:
 	void opStartForeignSequence(Control *control, OpCall &opCall);
 	void opBeginLoop(Control *control, OpCall &opCall);
 	void opNextLoop(Control *control, OpCall &opCall);
+	void opSetActorIndex(Control *control, OpCall &opCall);
 	void opSwitchActorIndex(Control *control, OpCall &opCall);
 	void opSwitchFacing(Control *control, OpCall &opCall);
 	void opAppearActor(Control *control, OpCall &opCall);
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 5511fc5..0476c8f 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -311,7 +311,7 @@ static char *debugW2I(byte *wstr) {
 }
 
 int TalkThread_Duckman::insertText() {
-	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
+	debug(0, "%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
 	WidthHeight dimensions;
 	_vm->getDefaultTextDimensions(dimensions);
 	uint16 *outTextPtr;


Commit: d8e86249c7fef61e42dab54df3570b4c8d49c557
    https://github.com/scummvm/scummvm/commit/d8e86249c7fef61e42dab54df3570b4c8d49c557
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement special opcode 16001A

Changed paths:
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index 6b67f12..b0b859c 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -41,6 +41,8 @@ DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 
 	_propertyTimers = new PropertyTimers(_vm);
 	_inventory = new DuckmanInventory(_vm);
+	_wasCursorHoldingElvisPoster = false;
+	_counter = 0;
 }
 
 DuckmanSpecialCode::~DuckmanSpecialCode() {
@@ -69,6 +71,7 @@ void DuckmanSpecialCode::init() {
 	SPECIAL(0x00160012, spcStopScreenShaker);
 	SPECIAL(0x00160013, spcIncrCounter);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
+	SPECIAL(0x0016001A, spcHoldGlowingElvisPoster);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 	SPECIAL(0x0016001D, spcCenterCurrentScreenText);
 	SPECIAL(0x0016001E, spcSetDefaultTextCoords);
@@ -283,6 +286,32 @@ void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcHoldGlowingElvisPoster(OpCall &opCall) {
+	const uint32 kPosterObjectId = 0x40072;
+	const uint32 kPosterSequenceId = 0x60034;
+	ARG_BYTE(mode);
+	switch (mode) {
+	case 0:
+		if (_vm->_cursor._objectId == kPosterObjectId) {
+			_wasCursorHoldingElvisPoster = true;
+			_inventory->addInventoryItem(_vm->_cursor._objectId);
+			_vm->stopCursorHoldingObject();
+		} else {
+			_wasCursorHoldingElvisPoster = false;
+		}
+		break;
+	case 1:
+		if (_wasCursorHoldingElvisPoster) {
+			_inventory->clearInventorySlot(kPosterObjectId);
+			_vm->_cursor._objectId = kPosterObjectId;
+			_vm->_cursor._sequenceId2 = kPosterSequenceId;
+			_vm->_cursor._field14[_vm->_cursor._actorIndex - 1] = true;
+		}
+		break;
+	}
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::spcSetCursorInventoryMode(OpCall &opCall) {
 	ARG_BYTE(mode);
 	ARG_BYTE(value);
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index b8bf79c..83430a4 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -48,15 +48,14 @@ public:
 	IllusionsEngine_Duckman *_vm;
 	SpecialCodeMap _specialCodeMap;
 
+	PropertyTimers *_propertyTimers;
+	DuckmanInventory *_inventory;
+
 	uint _chinesePuzzleIndex;
 	byte _chinesePuzzleAnswers[3];
-	
 	Common::Point _teleporterPosition;
-	
-	PropertyTimers *_propertyTimers;
-	DuckmanInventory *_inventory;
-	
 	int16 _counter;
+	bool _wasCursorHoldingElvisPoster;
 
 	// Special code interface functions
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
@@ -76,6 +75,7 @@ public:
 	void spcStopScreenShaker(OpCall &opCall);
 	void spcIncrCounter(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
+	void spcHoldGlowingElvisPoster(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcCenterCurrentScreenText(OpCall &opCall);
 	void spcSetDefaultTextCoords(OpCall &opCall);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 4f21e6b..8df01a8 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -167,11 +167,17 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_scriptResource->_properties.set(0x000E0024, true);
 #endif
 
-#if 1
+#if 0
 	// DEBUG Enterprise
 	_scriptResource->_blockCounters.set(238, 1);
 #endif
 
+#if 1
+	// DEBUG Map / special code 0016001A
+	_scriptResource->_properties.set(0x000E0017, true);
+	_scriptResource->_properties.set(0x000E0022, false);
+#endif
+
 	while (!shouldQuit()) {
 		runUpdateFunctions();
 		_system->updateScreen();
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 60256b9..263c27f 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -274,7 +274,8 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 //static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
 //static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
-static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
+//static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
+static uint dsceneId = 0x00010039, dthreadId = 0x00020089;
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);


Commit: 6b36b750c2567c32d20f8f7868a8b664baac5cde
    https://github.com/scummvm/scummvm/commit/6b36b750c2567c32d20f8f7868a8b664baac5cde
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement special opcodes 160017, 16001B, 160020 and 160021

Changed paths:
  A engines/illusions/resources/genericresource.cpp
  A engines/illusions/resources/genericresource.h
    engines/illusions/actor.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/module.mk
    engines/illusions/resourcesystem.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 9192946..29a1e68 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -940,7 +940,6 @@ void Control::drawActorRect(const Common::Rect r, byte color) {
 }
 
 void Control::fillActor(byte color) {
-	debug("FILL %d, %d", _actor->_surface->w, _actor->_surface->h);
 	Common::Rect r = Common::Rect(_actor->_surface->w, _actor->_surface->h);
 	_actor->_surface->fillRect(r, color);
 	_actor->_flags |= 0x4000;
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index b0b859c..f0c3698 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -27,8 +27,14 @@
 #include "illusions/duckman/propertytimers.h"
 #include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/resources/fontresource.h"
 #include "illusions/resources/scriptresource.h"
+#include "illusions/sound.h"
 #include "illusions/specialcode.h"
+#include "illusions/textdrawer.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
 
 #include "engines/util.h"
 
@@ -43,6 +49,8 @@ DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 	_inventory = new DuckmanInventory(_vm);
 	_wasCursorHoldingElvisPoster = false;
 	_counter = 0;
+	_savedTempMasterSfxVolume = 16;
+	_lastRandomSoundIndex = 6;
 }
 
 DuckmanSpecialCode::~DuckmanSpecialCode() {
@@ -71,11 +79,15 @@ void DuckmanSpecialCode::init() {
 	SPECIAL(0x00160012, spcStopScreenShaker);
 	SPECIAL(0x00160013, spcIncrCounter);
 	SPECIAL(0x00160014, spcUpdateObject272Sequence);
+	SPECIAL(0x00160017, spcPlayRandomSound);
 	SPECIAL(0x0016001A, spcHoldGlowingElvisPoster);
+	SPECIAL(0x0016001B, spcStartCredits);
 	SPECIAL(0x0016001C, spcSetCursorInventoryMode);
 	SPECIAL(0x0016001D, spcCenterCurrentScreenText);
 	SPECIAL(0x0016001E, spcSetDefaultTextCoords);
 	SPECIAL(0x0016001F, spcSetTextDuration);
+	SPECIAL(0x00160020, spcSetTempMasterSfxVolume);
+	SPECIAL(0x00160021, spcRestoreTempMasterSfxVolume);
 }
 
 #undef SPECIAL
@@ -168,7 +180,6 @@ void DuckmanSpecialCode::spcInitTeleporterPosition(OpCall &opCall) {
 }
 
 void DuckmanSpecialCode::spcUpdateTeleporterPosition(OpCall &opCall) {
-	// TODO
 	ARG_BYTE(direction);
 	int16 deltaX = 0;
 	int16 deltaY = 0;
@@ -286,6 +297,19 @@ void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
 	control->startSequenceActor(sequenceId, 2, opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcPlayRandomSound(OpCall &opCall) {
+	static const uint32 kRandomSoundIds[] = {
+		0x00090084, 0x00090085, 0x00090086, 0x00090087, 0x00090088, 0x00090089
+	};
+	int16 soundIndex;
+	do {
+		soundIndex = _vm->getRandom(ARRAYSIZE(kRandomSoundIds));
+	} while (soundIndex == _lastRandomSoundIndex);
+	_vm->_soundMan->playSound(kRandomSoundIds[soundIndex], 255, 0);
+	_lastRandomSoundIndex = soundIndex;
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::spcHoldGlowingElvisPoster(OpCall &opCall) {
 	const uint32 kPosterObjectId = 0x40072;
 	const uint32 kPosterSequenceId = 0x60034;
@@ -312,6 +336,13 @@ void DuckmanSpecialCode::spcHoldGlowingElvisPoster(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcStartCredits(OpCall &opCall) {
+	ARG_BYTE(mode);
+	if (mode == 0)
+		startCredits();
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::spcSetCursorInventoryMode(OpCall &opCall) {
 	ARG_BYTE(mode);
 	ARG_BYTE(value);
@@ -339,6 +370,19 @@ void DuckmanSpecialCode::spcSetTextDuration(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void DuckmanSpecialCode::spcSetTempMasterSfxVolume(OpCall &opCall) {
+	ARG_INT16(sfxVolume);
+	// TODO _savedTempMasterSfxVolume = _vm->getMasterSfxVolume();
+	// TODO _vm->setMasterSfxVolume(sfxVolume);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
+void DuckmanSpecialCode::spcRestoreTempMasterSfxVolume(OpCall &opCall) {
+	// TODO _vm->setMasterSfxVolume(_savedTempMasterSfxVolume);
+	_savedTempMasterSfxVolume = 16;
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void DuckmanSpecialCode::updateTeleporterProperties() {
 	_vm->_scriptResource->_properties.set(0x000E0074, _teleporterPosition.x == 4 && _teleporterPosition.y == 2);
 	_vm->_scriptResource->_properties.set(0x000E0075, _teleporterPosition.x == 4 && _teleporterPosition.y == 3);
@@ -347,4 +391,155 @@ void DuckmanSpecialCode::updateTeleporterProperties() {
 	_vm->_scriptResource->_properties.set(0x000E0078, _teleporterPosition.x == 1 && _teleporterPosition.y == 1);	
 }
 
+void DuckmanSpecialCode::startCredits() {
+	static const struct { uint32 objectId; int scrollPosY; } kCreditsItems[] = {
+		{0x40136,   0}, {0x40137,  16}, {0x40138,  32}, {0x40139,  48},
+		{0x4013A,  64}, {0x4013B,  80}, {0x4013C,  96}, {0x4013D, 112}
+	};
+	_creditsCurrText = (char*)_vm->_resSys->getResource(0x190052)->_data;
+	_creditsItems.clear();
+	for (uint i = 0; i < ARRAYSIZE(kCreditsItems);  ++i) {
+		CreditsItem creditsItem;
+		creditsItem.objectId = kCreditsItems[i].objectId;
+		creditsItem.scrollPosY = kCreditsItems[i].scrollPosY;
+		creditsItem.scrollPosIndex = 0;
+		creditsItem.active = false;
+		_creditsItems.push_back(creditsItem);
+	}
+	uint32 currSceneId = _vm->getCurrentScene();
+	_vm->_updateFunctions->add(0, currSceneId, new Common::Functor1Mem<uint, int, DuckmanSpecialCode>(this, &DuckmanSpecialCode::updateCredits));
+	_creditsNextUpdateTicks = getCurrentTime();
+	_creditsLastUpdateTicks = _creditsNextUpdateTicks - 4;
+}
+
+int DuckmanSpecialCode::updateCredits(uint flags) {
+
+	if (_vm->_pauseCtr > 0) {
+		_creditsNextUpdateTicks = getCurrentTime() + 4;
+		return 1;
+	}
+
+	if (flags & 1) {
+		_vm->_scriptResource->_properties.set(0x000E0096, true);
+		_lastCreditsItemIndex = -1;
+		_creditsEndReached = false;
+		return 2;
+	}
+
+	if (!isTimerExpired(_creditsLastUpdateTicks, _creditsNextUpdateTicks)) {
+		return 1;
+	}
+
+	bool creditsRunning = false;
+	int index = 0;
+	for (CreditsItems::iterator it = _creditsItems.begin(); it != _creditsItems.end(); ++it, ++index) {
+		CreditsItem &creditsItem = *it;
+		Control *control = _vm->getObjectControl(creditsItem.objectId);
+		if (!creditsItem.active && creditsItem.scrollPosY == 0 && !_creditsEndReached) {
+			creditsItem.active = true;
+			creditsItem.scrollPosIndex = 0;
+			control->fillActor(0);
+			char *text = readNextCreditsLine();
+			if (!strncmp(text, "&&&END", 6)) {
+				creditsItem.active = false;
+				_creditsEndReached = true;
+			} else {
+				uint16 wtext[128];
+				charToWChar(text, wtext, ARRAYSIZE(wtext));
+
+				FontResource *font = _vm->_dict->findFont(0x120001); 
+				TextDrawer textDrawer;
+				WidthHeight dimensions;
+				uint16 *outText;
+				control->getActorFrameDimensions(dimensions);
+				textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), 2, outText);
+				textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
+				control->_actor->_flags |= 0x4000;
+
+				_lastCreditsItemIndex = index;
+			}
+		}
+		if (creditsItem.active) {
+			if (_creditsEndReached && _creditsItems[_lastCreditsItemIndex].scrollPosIndex > 53) {
+				creditsItem.active = false;
+				creditsItem.scrollPosY = -1;
+			} else {
+				creditsRunning = true;
+				control->_actor->_position = getCreditsItemPosition(creditsItem.scrollPosIndex);
+				++creditsItem.scrollPosIndex;
+				if (getCreditsItemPosition(creditsItem.scrollPosIndex).x < 0)
+					creditsItem.active = false;
+			}
+		}
+		if (creditsItem.scrollPosY > 0)
+			--creditsItem.scrollPosY;
+	}
+	_creditsLastUpdateTicks = _creditsNextUpdateTicks;
+	_creditsNextUpdateTicks = getCurrentTime() + 4;
+
+	if (!creditsRunning) {
+		_vm->_scriptResource->_properties.set(0x000E0096, true);
+		_lastCreditsItemIndex = -1;
+		_creditsEndReached = false;
+		return 2;
+	}
+
+	return 1;
+}
+
+char *DuckmanSpecialCode::readNextCreditsLine() {
+	static char line[256];
+	char *dest = line;
+	char *src = _creditsCurrText;
+	do {
+		if (*src == 10 || *src == 13) {
+			src += 2;
+			*dest = 0;
+			break;
+		}
+		*dest++ = *src++;
+	} while (1);
+	_creditsCurrText = src;
+	return line;
+}
+
+Common::Point DuckmanSpecialCode::getCreditsItemPosition(int index) {
+	static const struct { int16 x, y; } kCreditsItemsPoints[] = {
+		{159, 200}, {158, 195}, {157, 190}, {156, 185}, {156, 180}, {157, 176}, 
+		{158, 172}, {159, 168}, {161, 164}, {162, 161}, {163, 158}, {163, 155}, 
+		{162, 152}, {161, 149}, {159, 147}, {158, 144}, {157, 142}, {156, 140}, 
+		{156, 138}, {157, 136}, {158, 134}, {159, 132}, {161, 130}, {162, 128}, 
+		{163, 127}, {163, 126}, {162, 125}, {161, 124}, {159, 123}, {158, 122}, 
+		{157, 121}, {156, 120}, {156, 119}, {157, 118}, {158, 117}, {159, 116}, 
+		{161, 115}, {162, 114}, {163, 113}, {163, 112}, {162, 111}, {161, 110}, 
+		{159, 109}, {158, 108}, {157, 107}, {156, 106}, {156, 105}, {157, 104}, 
+		{158, 103}, {159, 102}, {161, 101}, {162, 100}, {163,  99}, {163,  98}, 
+		{162,  97}, {161,  96}, {159,  95}, {158,  94}, {157,  93}, {156,  92}, 
+		{156,  91}, {157,  90}, {158,  89}, {159,  88}, {161,  87}, {162,  86}, 
+		{163,  85}, {163,  84}, {162,  83}, {161,  82}, {159,  81}, {158,  80}, 
+		{157,  79}, {156,  78}, {156,  77}, {157,  76}, {158,  75}, {159,  74},
+		{161,  73}, {162,  72}, {163,  71}, {163,  70}, {162,  69}, {161,  68}, 
+		{159,  67}, {158,  66}, {157,  64}, {156,  62}, {156,  60}, {157,  58}, 
+		{158,  56}, {159,  54}, {161,  52}, {162,  50}, {163,  40}, {163,  40}, 
+		{162,  40}, {161,  40}, {159,  40}, {158,  40}, {157,  40}, {156,  40}, 
+		{156,  40}, {157,  40}, {158,  40}, {159,  40}, {161,  40}, {162,  40}, 
+		{163,  40}, {163,  40}, {162,  40}, {161,  40}, {159,  40}, {158,  40}, 
+		{157,  40}, {156,  40}, {156,  40}, {157,  40}, {158,  40}, {159,  40}, 
+		{161,  40}, {162,  40}, {163,  40}, {163,  40}, {162,  40}, {161,  40}, 
+		{159,  40}, {158,  40}, { -1,  -1} 
+	};
+
+	if (index < 0 || index >= ARRAYSIZE(kCreditsItemsPoints))
+		return Common::Point(-1, -1);
+	return Common::Point(kCreditsItemsPoints[index].x, kCreditsItemsPoints[index].y);
+}
+
+void DuckmanSpecialCode::charToWChar(char *text, uint16 *wtext, uint size) {
+	while (*text != 0 && size > 1) {
+		*wtext++ = (byte)*text++;
+		--size;
+	}
+	*wtext++ = 0;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index 83430a4..9176a64 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -35,6 +35,13 @@ class PropertyTimers;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
+struct CreditsItem {
+	uint32 objectId;
+	bool active;
+	int16 scrollPosIndex;
+	int16 scrollPosY;
+};
+
 class DuckmanSpecialCode : public SpecialCode {
 public:
 	DuckmanSpecialCode(IllusionsEngine_Duckman *vm);
@@ -44,6 +51,7 @@ public:
 public:	
 	typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
 	typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
+	typedef Common::Array<CreditsItem> CreditsItems;
 
 	IllusionsEngine_Duckman *_vm;
 	SpecialCodeMap _specialCodeMap;
@@ -56,6 +64,15 @@ public:
 	Common::Point _teleporterPosition;
 	int16 _counter;
 	bool _wasCursorHoldingElvisPoster;
+	int16 _savedTempMasterSfxVolume;
+	int16 _lastRandomSoundIndex;
+
+	uint32 _creditsLastUpdateTicks;
+	uint32 _creditsNextUpdateTicks;
+	int _lastCreditsItemIndex;
+	bool _creditsEndReached;
+	CreditsItems _creditsItems;
+	char *_creditsCurrText;
 
 	// Special code interface functions
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
@@ -75,14 +92,24 @@ public:
 	void spcStopScreenShaker(OpCall &opCall);
 	void spcIncrCounter(OpCall &opCall);
 	void spcUpdateObject272Sequence(OpCall &opCall);
+	void spcPlayRandomSound(OpCall &opCall);
 	void spcHoldGlowingElvisPoster(OpCall &opCall);
+	void spcStartCredits(OpCall &opCall);
 	void spcSetCursorInventoryMode(OpCall &opCall);
 	void spcCenterCurrentScreenText(OpCall &opCall);
 	void spcSetDefaultTextCoords(OpCall &opCall);
 	void spcSetTextDuration(OpCall &opCall);
+	void spcSetTempMasterSfxVolume(OpCall &opCall);
+	void spcRestoreTempMasterSfxVolume(OpCall &opCall);
 
 	void updateTeleporterProperties();
 
+	void startCredits();
+	int updateCredits(uint flags);
+	char *readNextCreditsLine();
+	Common::Point getCreditsItemPosition(int index);
+	void charToWChar(char *text, uint16 *wtext, uint size);
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 8df01a8..ebfcdca 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -35,6 +35,7 @@
 #include "illusions/resources/actorresource.h"
 #include "illusions/resources/backgroundresource.h"
 #include "illusions/resources/fontresource.h"
+#include "illusions/resources/genericresource.h"
 #include "illusions/resources/midiresource.h"
 #include "illusions/resources/scriptresource.h"
 #include "illusions/resources/soundresource.h"
@@ -99,6 +100,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
 	_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
+	_resSys->addResourceLoader(0x00190000, new GenericResourceLoader(this));
 
 	_screen = new Screen(this, 320, 200, 8);
 	_screenText = new ScreenText(this);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 263c27f..a62a78b 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -275,7 +275,8 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 //static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
 //static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
-static uint dsceneId = 0x00010039, dthreadId = 0x00020089;
+//static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map
+static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index d4bc393..a5aa86d 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -33,6 +33,7 @@ MODULE_OBJS := \
 	resources/actorresource.o \
 	resources/backgroundresource.o \
 	resources/fontresource.o \
+	resources/genericresource.o \
 	resources/midiresource.o \
 	resources/scriptresource.o \
 	resources/soundresource.o \
diff --git a/engines/illusions/resources/genericresource.cpp b/engines/illusions/resources/genericresource.cpp
new file mode 100644
index 0000000..8d83026
--- /dev/null
+++ b/engines/illusions/resources/genericresource.cpp
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/resources/genericresource.h"
+#include "illusions/dictionary.h"
+
+namespace Illusions {
+
+// GenericResourceLoader
+
+void GenericResourceLoader::load(Resource *resource) {
+	resource->_instance = 0;
+}
+
+bool GenericResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/resources/genericresource.h b/engines/illusions/resources/genericresource.h
new file mode 100644
index 0000000..e875b01
--- /dev/null
+++ b/engines/illusions/resources/genericresource.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_GENERICRESOURCE_H
+#define ILLUSIONS_GENERICRESOURCE_H
+
+#include "illusions/graphics.h"
+#include "illusions/resourcesystem.h"
+
+namespace Illusions {
+
+class IllusionsEngine;
+
+class GenericResourceLoader : public BaseResourceLoader {
+public:
+	GenericResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
+	virtual ~GenericResourceLoader() {}
+	virtual void load(Resource *resource);
+	virtual bool isFlag(int flag);
+protected:
+	IllusionsEngine *_vm;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_GENERICRESOURCE_H
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index b761b94..a54cda6 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -97,6 +97,7 @@ public:
 	void unloadResourceById(uint32 resId);
 	void unloadResourcesBySceneId(uint32 sceneId);
 	void unloadSceneResources(uint32 sceneId1, uint32 sceneId2);
+	Resource *getResource(uint32 resId);
 	
 protected:
 	typedef Common::HashMap<uint32, BaseResourceLoader*> ResourceLoadersMap;
@@ -141,7 +142,6 @@ protected:
 		}
 	};
 
-	Resource *getResource(uint32 resId);
 	void unloadResource(Resource *resource);
 	
 };


Commit: aed38527010e7b155af469d0521aa07e7fd7d12e
    https://github.com/scummvm/scummvm/commit/aed38527010e7b155af469d0521aa07e7fd7d12e
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement save/load functionality

- Only works in Duckman at the moment
- Only via the ScummVM main menu for now, save/load via the game's menu to be implemented

Changed paths:
  A engines/illusions/duckman/gamestate_duckman.cpp
  A engines/illusions/duckman/gamestate_duckman.h
  A engines/illusions/gamestate.cpp
  A engines/illusions/gamestate.h
  A engines/illusions/saveload.cpp
    engines/illusions/detection.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/resources/scriptresource.cpp
    engines/illusions/resources/scriptresource.h
    engines/illusions/screen.cpp


diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index 53e7a4b..a0e2488 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -95,29 +95,22 @@ public:
 
 	virtual bool hasFeature(MetaEngineFeature f) const;
 	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
-#if 0
 	virtual int getMaximumSaveSlot() const;
 	virtual SaveStateList listSaves(const char *target) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 	virtual void removeSaveState(const char *target, int slot) const;
-#endif
 };
 
 bool IllusionsMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
-		false;
-		/*
 		(f == kSupportsListSaves) ||
 		(f == kSupportsDeleteSave) ||
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSavesSupportMetaInfo) ||
 		(f == kSavesSupportThumbnail) ||
 		(f == kSavesSupportCreationDate);
-		*/
 }
 
-#if 0
-
 void IllusionsMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String fileName = Common::String::format("%s.%03d", target, slot);
 	g_system->getSavefileManager()->removeSavefile(fileName);
@@ -175,8 +168,6 @@ SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target,
 	return SaveStateDescriptor();
 }
 
-#endif
-
 bool IllusionsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
 	const Illusions::IllusionsGameDescription *gd = (const Illusions::IllusionsGameDescription *)desc;
 	if (gd) {
diff --git a/engines/illusions/duckman/gamestate_duckman.cpp b/engines/illusions/duckman/gamestate_duckman.cpp
new file mode 100644
index 0000000..8f4939f
--- /dev/null
+++ b/engines/illusions/duckman/gamestate_duckman.cpp
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/gamestate_duckman.h"
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/resources/scriptresource.h"
+
+namespace Illusions {
+
+Duckman_GameState::Duckman_GameState(IllusionsEngine_Duckman *vm)
+	: _vm(vm) {
+}
+
+uint32 Duckman_GameState::calcWriteBufferSizeInternal() {
+	return
+		_vm->_scriptResource->_properties.getSize() +
+		_vm->_scriptResource->_blockCounters.getSize();
+}
+
+bool Duckman_GameState::readStateInternal(Common::ReadStream *in) {
+	return
+		_vm->_scriptResource->_properties.readFromStream(in) &&
+		_vm->_scriptResource->_blockCounters.readFromStream(in);
+}
+
+void Duckman_GameState::writeStateInternal(Common::WriteStream *out) {
+	_vm->_scriptResource->_properties.writeToStream(out);
+	_vm->_scriptResource->_blockCounters.writeToStream(out);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/gamestate_duckman.h b/engines/illusions/duckman/gamestate_duckman.h
new file mode 100644
index 0000000..36024c8
--- /dev/null
+++ b/engines/illusions/duckman/gamestate_duckman.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H
+#define ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H
+
+#include "illusions/gamestate.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+class Duckman_GameState : public GameState {
+public:
+	Duckman_GameState(IllusionsEngine_Duckman *vm);
+protected:
+	IllusionsEngine_Duckman *_vm;
+	uint32 calcWriteBufferSizeInternal();
+	bool readStateInternal(Common::ReadStream *in);
+	void writeStateInternal(Common::WriteStream *out);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index ebfcdca..c9233ae 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -23,6 +23,7 @@
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/duckman_dialog.h"
 #include "illusions/duckman/duckman_specialcode.h"
+#include "illusions/duckman/gamestate_duckman.h"
 #include "illusions/duckman/menusystem_duckman.h"
 #include "illusions/duckman/scriptopcodes_duckman.h"
 #include "illusions/actor.h"
@@ -114,6 +115,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
 	_menuSystem = new DuckmanMenuSystem(this);
+	_gameState = new Duckman_GameState(this);
 
 	_fader = new Fader();
 	
@@ -153,10 +155,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_resSys->loadResource(0x120001, 0x00010001, 0);
 	_resSys->loadResource(0x120002, 0x00010001, 0);
 	_resSys->loadResource(0x120003, 0x00010001, 0);
-	
 	_resSys->loadResource(0x000D0001, 0x00010001, 0);
-	startScriptThread(0x00020004, 0);
-	_doScriptThreadInit = true;
 
 #if 0
 	//DEBUG
@@ -174,13 +173,28 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_scriptResource->_blockCounters.set(238, 1);
 #endif
 
-#if 1
+#if 0
 	// DEBUG Map / special code 0016001A
 	_scriptResource->_properties.set(0x000E0017, true);
 	_scriptResource->_properties.set(0x000E0022, false);
 #endif
 
+	if (ConfMan.hasKey("save_slot")) {
+		_doScriptThreadInit = true;
+		// Load global resources manually, usually done by the game script
+		enterScene(0x00010003, 0);
+		loadGameState(ConfMan.getInt("save_slot"));
+	} else {
+		startScriptThread(0x00020004, 0);
+		_doScriptThreadInit = true;
+	}
+
 	while (!shouldQuit()) {
+		if (_resumeFromSavegameRequested) {
+			activateSavegame(0);
+			resumeFromSavegame(0);
+			_resumeFromSavegameRequested = false;
+		}
 		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
@@ -195,6 +209,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _fader;
 
+    delete _gameState;
 	delete _menuSystem;
 	delete _soundMan;
 	delete _updateFunctions;
@@ -218,12 +233,9 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
 	return
-		false;
-		/*
 		(f == kSupportsRTL) ||
 		(f == kSupportsLoadingDuringRuntime) ||
 		(f == kSupportsSavingDuringRuntime);
-		*/
 }
 
 void IllusionsEngine_Duckman::initInput() {
@@ -793,7 +805,7 @@ bool IllusionsEngine_Duckman::changeScene(uint32 sceneId, uint32 threadId, uint3
 	_controls->destroyControls();
 	_resSys->unloadSceneResources(0x10003, 0x10001);
 	if (enterScene(sceneId, threadId)) {
-		// TODO GameStates_writeStates(sceneId, threadId);
+		_gameState->writeState(sceneId, threadId);
 		return true;
 	}
 	return false;
@@ -1075,23 +1087,24 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
 		return 0;
 
-	bool flag = false;
+	// TODO Extract sound effect playing to method
+	bool soundWasPlayed = false;
 	if (_scriptResource->_properties.get(0x000E003C)) {
 		if (verbId == 7 && objectId == 0x40003) {
 			playSoundEffect(7);
-			flag = true;
+			soundWasPlayed = true;
 		} else if (objectId == 0x40003) {
 			playSoundEffect(14);
-			flag = true;
+			soundWasPlayed = true;
 		} else if (verbId == 3) {
 			playSoundEffect(16);
-			flag = true;
+			soundWasPlayed = true;
 		} else if (verbId == 2) {
-			flag = true;
+			soundWasPlayed = true;
 		}
 	}
 
-	if (!flag) {
+	if (!soundWasPlayed) {
 		if (objectId == 0x40003) {
 			playSoundEffect(14);
 		} else if ((verbId == 1 || verbId == 2) && _scriptResource->getMainActorObjectId() == objectId) {
@@ -1120,34 +1133,33 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 	return tempThreadId;
 }
 
-bool IllusionsEngine_Duckman::loadSavegame(int16 slotNum, uint32 callingThreadId) {
-#if 0	
-	// TODO
-	bool success = _gameStates->load(slotNum);
-	if (success) {
-		_vm->_screen->setDisplayOn(false);
-		uint32 currSceneId = getCurrentScene();
-		if (currSceneId != 0x10003)
-			dumpCurrSceneFiles(currSceneId, callerThreadId);
-		reset();
-		stopMidi();
-		clearMidiPlayList();
-		_gameStates->readStates();
-		pushActiveScene(0x10000);
-	}
-	_gameStates->freeGameStateReadBuffer();
+bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
+	const char *fileName = getSavegameFilename(slotNum);
+	bool success = loadgame(fileName);
+	if (success)
+		activateSavegame(callingThreadId);
+	_gameState->deleteReadStream();
 	return success;
-#endif	
-	return true;
 }
 
-bool IllusionsEngine_Duckman::saveSavegame(int16 slotNum, uint32 callingThreadId) {
-#if 0
+bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
 	// TODO
-	bool success = _gameStates->save(slotNum);
+	const char *fileName = getSavegameFilename(slotNum);
+	bool success = false;//savegame(fileName, _savegameDescription.c_str());
 	return success;
-#endif	
-	return true;
+}
+
+void IllusionsEngine_Duckman::activateSavegame(uint32 callingThreadId) {
+	// TODO _screen->setDisplayOn(false);
+	uint32 currSceneId = getCurrentScene();
+	if (currSceneId != 0x10003)
+		dumpCurrSceneFiles(currSceneId, callingThreadId);
+	reset();
+	// TODO stopMidi();
+	// TODO clearMidiPlayList();
+	_gameState->readState(_savegameSceneId, _savegameThreadId);
+	pushActiveScene(0x10000);
+	_gameState->deleteReadStream();
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 87520d3..1825123 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -187,8 +187,9 @@ public:
 	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
 	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
 	
-	bool loadSavegame(int16 slotNum, uint32 callingThreadId);
-	bool saveSavegame(int16 slotNum, uint32 callingThreadId);
+	bool loadSavegameFromScript(int16 slotNum, uint32 callingThreadId);
+	bool saveSavegameFromScript(int16 slotNum, uint32 callingThreadId);
+	void activateSavegame(uint32 callingThreadId);
 
 };
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index a62a78b..1456cfc 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -259,7 +259,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
@@ -276,7 +276,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
 //static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map
-static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
+//static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -348,12 +348,12 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o
 void ScriptOpcodes_Duckman::opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall) {
 	// Used for debugging purposes in the original engine
 	// This is not supported and only reachable by code not implemented here!
-	error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called");
+	//error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called");
 }
 
 void ScriptOpcodes_Duckman::opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall) {
 	// See opEnterDebugger
-	error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called");
+	//error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called");
 }
 
 void ScriptOpcodes_Duckman::opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall) {
@@ -690,7 +690,7 @@ void ScriptOpcodes_Duckman::opLoadGame(ScriptThread *scriptThread, OpCall &opCal
 	ARG_SKIP(2);
 	ARG_INT16(bankNum)
 	ARG_INT16(slotNum)
-	bool success = _vm->loadSavegame(slotNum, opCall._callerThreadId);
+	bool success = _vm->loadSavegameFromScript(slotNum, opCall._callerThreadId);
 	_vm->_stack->push(success ? 1 : 0);
 }
 
@@ -698,7 +698,7 @@ void ScriptOpcodes_Duckman::opSaveGame(ScriptThread *scriptThread, OpCall &opCal
 	ARG_SKIP(2);
 	ARG_INT16(bankNum)
 	ARG_INT16(slotNum)
-	bool success = _vm->saveSavegame(slotNum, opCall._callerThreadId);
+	bool success = _vm->saveSavegameFromScript(slotNum, opCall._callerThreadId);
 	_vm->_stack->push(success ? 1 : 0);
 }
 
diff --git a/engines/illusions/gamestate.cpp b/engines/illusions/gamestate.cpp
new file mode 100644
index 0000000..5f6e17c
--- /dev/null
+++ b/engines/illusions/gamestate.cpp
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/gamestate.h"
+
+namespace Illusions {
+
+GameState::GameState()
+	: _writeBufferSize(0), _writeBuffer(0), _readStream(0) {
+}
+
+GameState::~GameState() {
+	free(_writeBuffer);
+}
+
+bool GameState::readState(uint32 &sceneId, uint32 &threadId) {
+	sceneId = _readStream->readUint32LE();
+	threadId = _readStream->readUint32LE();
+	return readStateInternal(_readStream);
+}
+
+void GameState::writeState(uint32 sceneId, uint32 threadId) {
+	Common::WriteStream *writeStream = newWriteStream();
+	writeStream->writeUint32LE(sceneId);
+	writeStream->writeUint32LE(threadId);
+	writeStateInternal(writeStream);
+}
+
+void GameState::read(Common::ReadStream *in) {
+	uint32 size = in->readUint32LE();
+	_readStream = in->readStream(size);
+}
+
+void GameState::write(Common::WriteStream *out) {
+	out->writeUint32LE(_writeBufferSize);
+	out->write(_writeBuffer, _writeBufferSize);
+}
+
+void GameState::deleteReadStream() {
+	delete _readStream;
+	_readStream = 0;
+}
+
+Common::WriteStream *GameState::newWriteStream() {
+	if (!_writeBufferSize == 0 || !_writeBuffer) {
+		_writeBufferSize = calcWriteBufferSize();
+		_writeBuffer = (byte*)malloc(_writeBufferSize);
+	}
+	return new Common::MemoryWriteStream(_writeBuffer, _writeBufferSize);
+}
+
+uint32 GameState::calcWriteBufferSize() {
+	return calcWriteBufferSizeInternal() + 4 + 4;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/gamestate.h b/engines/illusions/gamestate.h
new file mode 100644
index 0000000..5f279ff
--- /dev/null
+++ b/engines/illusions/gamestate.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_GAMESTATE_H
+#define ILLUSIONS_GAMESTATE_H
+
+#include "common/file.h"
+#include "common/memstream.h"
+
+namespace Illusions {
+
+class GameState {
+public:
+	GameState();
+	virtual ~GameState();
+	bool readState(uint32 &sceneId, uint32 &threadId);
+	void writeState(uint32 sceneId, uint32 threadId);
+	void read(Common::ReadStream *in);
+	void write(Common::WriteStream *out);
+	void deleteReadStream();
+protected:
+	uint32 _writeBufferSize;
+	byte *_writeBuffer;
+	Common::SeekableReadStream *_readStream;
+	Common::WriteStream *newWriteStream();
+	uint32 calcWriteBufferSize();
+	virtual uint32 calcWriteBufferSizeInternal() = 0;
+	virtual bool readStateInternal(Common::ReadStream *in) = 0;
+	virtual void writeStateInternal(Common::WriteStream *out) = 0;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_GAMESTATE_H
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index c4af5b5..e22ef0c 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -66,6 +66,11 @@ IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *
 
 	_rerunThreads = false;
 	
+	_isSaveAllowed = true; // TODO
+	_resumeFromSavegameRequested = false;
+	_savegameSceneId = 0;
+	_savegameThreadId = 0;
+
 	Engine::syncSoundSettings();
 
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 9914936..784277f 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -76,6 +76,7 @@ class SpecialCode;
 class TalkInstanceList;
 class ThreadList;
 class UpdateFunctions;
+class GameState;
 
 enum {
 	kGameIdBBDOU   = 1,
@@ -102,6 +103,7 @@ public:
 	ResourceSystem *_resSys;
 	BaseResourceReader *_resReader;
 	UpdateFunctions *_updateFunctions;
+	GameState *_gameState;
 	
 	void updateEvents();
 
@@ -132,7 +134,8 @@ public:
 	uint32 _resGetTime;
 	bool _unpauseControlActorFlag;
 	uint32 _lastUpdateTime;
-	
+
+	int _resumeFromSavegameRequested;
 	uint32 _savegameSceneId;
 	uint32 _savegameThreadId;
 
@@ -201,8 +204,6 @@ public:
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
 	virtual void resumeFromSavegame(uint32 callingThreadId) = 0;
 		
-#if 0
-
 	// Savegame API
 
 	enum kReadSaveHeaderError {
@@ -229,15 +230,14 @@ public:
 	bool canSaveGameStateCurrently() { return _isSaveAllowed; }
 	Common::Error loadGameState(int slot);
 	Common::Error saveGameState(int slot, const Common::String &description);
-	void savegame(const char *filename, const char *description);
-	void loadgame(const char *filename);
+	Common::Error removeGameState(int slot);
+	bool savegame(const char *filename, const char *description);
+	bool loadgame(const char *filename);
 	const char *getSavegameFilename(int num);
 	bool existsSavegame(int num);
 	static Common::String getSavegameFilename(const Common::String &target, int num);
 	static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header);
 
-#endif
-	
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index a5aa86d..ad61eee 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -17,6 +17,7 @@ MODULE_OBJS := \
 	duckman/duckman_inventory.o \
 	duckman/duckman_screenshakereffects.o \
 	duckman/duckman_specialcode.o \
+	duckman/gamestate_duckman.o \
 	duckman/illusions_duckman.o \
 	duckman/menusystem_duckman.o \
 	duckman/propertytimers.o \
@@ -24,6 +25,7 @@ MODULE_OBJS := \
 	fileresourcereader.o \
 	fixedpoint.o \
 	gamarchive.o \
+	gamestate.o \
 	gamresourcereader.o \
 	graphics.o \
 	illusions.o \
@@ -39,6 +41,7 @@ MODULE_OBJS := \
 	resources/soundresource.o \
 	resources/talkresource.o \
 	resourcesystem.o \
+	saveload.o \
 	screen.o \
 	screentext.o \
 	scriptstack.o \
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index 3038a86..5472d28 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -50,8 +50,8 @@ void Properties::init(uint count, byte *properties) {
 }
 
 void Properties::clear() {
-	uint size = (_count >> 3) + 1;
-	for (uint i = 0; i < size; ++i)
+	uint32 size = getSize();
+	for (uint32 i = 0; i < size; ++i)
 		_properties[i] = 0;
 }
 
@@ -72,6 +72,24 @@ void Properties::set(uint32 propertyId, bool value) {
 		_properties[index] &= ~mask;
 }
 
+uint32 Properties::getSize() {
+	return (_count >> 3) + 1;
+}
+
+void Properties::writeToStream(Common::WriteStream *out) {
+	const uint32 size = getSize();
+	out->writeUint32LE(size);
+	out->write(_properties, size);
+}
+
+bool Properties::readFromStream(Common::ReadStream *in) {
+	uint32 size = in->readUint32LE();
+	if (size != getSize())
+		return false;
+	in->read(_properties, size);
+	return true;
+}
+
 void Properties::getProperyPos(uint32 propertyId, uint &index, byte &mask) {
 	propertyId &= 0xFFFF;
 	index = propertyId >> 3;
@@ -113,6 +131,24 @@ void BlockCounters::setC0(uint index, byte value) {
 	_blockCounters[index - 1] = oldValue | (value & 0xC0);
 }
 
+uint32 BlockCounters::getSize() {
+	return _count;
+}
+
+void BlockCounters::writeToStream(Common::WriteStream *out) {
+	const uint32 size = getSize();
+	out->writeUint32LE(size);
+	out->write(_blockCounters, size);
+}
+
+bool BlockCounters::readFromStream(Common::ReadStream *in) {
+	uint32 size = in->readUint32LE();
+	if (size != getSize())
+		return false;
+	in->read(_blockCounters, size);
+	return true;
+}
+
 // TriggerCause
 
 void TriggerCause::load(Common::SeekableReadStream &stream) {
diff --git a/engines/illusions/resources/scriptresource.h b/engines/illusions/resources/scriptresource.h
index 6debcb2..f185319 100644
--- a/engines/illusions/resources/scriptresource.h
+++ b/engines/illusions/resources/scriptresource.h
@@ -24,6 +24,7 @@
 #define ILLUSIONS_SCRIPTRESOURCE_H
 
 #include "illusions/resourcesystem.h"
+#include "common/file.h"
 
 namespace Illusions {
 
@@ -46,6 +47,9 @@ public:
 	void clear();
 	bool get(uint32 propertyId);
 	void set(uint32 propertyId, bool value);
+	uint32 getSize();
+	void writeToStream(Common::WriteStream *out);
+	bool readFromStream(Common::ReadStream *in);
 public:
 	uint _count;
 	byte *_properties;
@@ -61,6 +65,9 @@ public:
 	void set(uint index, byte value);
 	byte getC0(uint index);
 	void setC0(uint index, byte value);
+	uint32 getSize();
+	void writeToStream(Common::WriteStream *out);
+	bool readFromStream(Common::ReadStream *in);
 public:
 	uint _count;
 	byte *_blockCounters;
diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp
new file mode 100644
index 0000000..751f6a6
--- /dev/null
+++ b/engines/illusions/saveload.cpp
@@ -0,0 +1,162 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/savefile.h"
+
+#include "graphics/thumbnail.h"
+
+#include "illusions/illusions.h"
+#include "illusions/gamestate.h"
+
+namespace Illusions {
+
+#define ILLUSIONS_SAVEGAME_VERSION 0
+
+IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) {
+
+	header.version = in->readUint32LE();
+	if (header.version > ILLUSIONS_SAVEGAME_VERSION)
+		return kRSHEInvalidVersion;
+
+	byte descriptionLen = in->readByte();
+	header.description = "";
+	while (descriptionLen--)
+		header.description += (char)in->readByte();
+
+	if (loadThumbnail) {
+		header.thumbnail = Graphics::loadThumbnail(*in);
+	} else {
+		Graphics::skipThumbnail(*in);
+	}
+
+	// Not used yet, reserved for future usage
+	header.gameID = in->readByte();
+	header.flags = in->readUint32LE();
+
+	header.saveDate = in->readUint32LE();
+	header.saveTime = in->readUint32LE();
+	header.playTime = in->readUint32LE();
+
+	return ((in->eos() || in->err()) ? kRSHEIoError : kRSHENoError);
+}
+
+bool IllusionsEngine::savegame(const char *filename, const char *description) {
+
+	Common::OutSaveFile *out;
+	if (!(out = g_system->getSavefileManager()->openForSaving(filename))) {
+		warning("Can't create file '%s', game not saved", filename);
+		return false;
+	}
+
+	TimeDate curTime;
+	g_system->getTimeAndDate(curTime);
+
+	// Header start
+	out->writeUint32LE(ILLUSIONS_SAVEGAME_VERSION);
+
+	byte descriptionLen = strlen(description);
+	out->writeByte(descriptionLen);
+	out->write(description, descriptionLen);
+
+	// TODO Probably pre-generate the thumbnail before the internal menu system is
+	// called to have a thumbnail without the menu system itself on it.
+	// Use the automatic thumbnail generation only when the ScummVM save dialog is used.
+	Graphics::saveThumbnail(*out);
+
+	// Not used yet, reserved for future usage
+	out->writeByte(0);
+	out->writeUint32LE(0);
+	uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+	uint32 saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+	uint32 playTime = g_engine->getTotalPlayTime() / 1000;
+	out->writeUint32LE(saveDate);
+	out->writeUint32LE(saveTime);
+	out->writeUint32LE(playTime);
+	// Header end
+	
+	_gameState->write(out);
+
+	out->finalize();
+	delete out;
+	return true;
+}
+
+bool IllusionsEngine::loadgame(const char *filename) {
+	Common::InSaveFile *in;
+	if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
+		warning("Can't open file '%s', game not loaded", filename);
+		return false;
+	}
+
+	SaveHeader header;
+
+	kReadSaveHeaderError errorCode = readSaveHeader(in, false, header);
+
+	if (errorCode != kRSHENoError) {
+		warning("Error loading savegame '%s'", filename);
+		delete in;
+		return false;
+	}
+
+	g_engine->setTotalPlayTime(header.playTime * 1000);
+
+	_gameState->read(in);
+
+	delete in;
+	return true;
+}
+
+Common::Error IllusionsEngine::loadGameState(int slot) {
+	_resumeFromSavegameRequested = false;
+	const char *fileName = getSavegameFilename(slot);
+	if (!loadgame(fileName))
+		return Common::kReadingFailed;
+	_resumeFromSavegameRequested = true;
+	return Common::kNoError;
+}
+
+Common::Error IllusionsEngine::saveGameState(int slot, const Common::String &description) {
+	const char *fileName = getSavegameFilename(slot);
+	if (!savegame(fileName, description.c_str()))
+		return Common::kWritingFailed;
+	return Common::kNoError;
+}
+
+Common::Error IllusionsEngine::removeGameState(int slot) {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Common::String filename = Illusions::IllusionsEngine::getSavegameFilename(_targetName, slot);
+	saveFileMan->removeSavefile(filename.c_str());
+	return Common::kNoError;
+}
+
+const char *IllusionsEngine::getSavegameFilename(int num) {
+	static Common::String filename;
+	filename = getSavegameFilename(_targetName, num);
+	return filename.c_str();
+}
+
+Common::String IllusionsEngine::getSavegameFilename(const Common::String &target, int num) {
+	assert(num >= 0 && num <= 999);
+	return Common::String::format("%s.%03d", target.c_str(), num);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index b9506f6..a4a3d27 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -635,10 +635,10 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
 	const int errYStart = srcHeight / dstHeight;
 	const int errYIncr = srcHeight % dstHeight;
-	const int midY = dstHeight / 2;
+//	const int midY = dstHeight / 2;
 	const int errXStart = srcWidth / dstWidth;
 	const int errXIncr = srcWidth % dstWidth;
-	const int midX = dstWidth / 2;
+//	const int midX = dstWidth / 2;
 	int h = dstHeight, errY = 0, skipY, srcY = srcRect.top;
 	byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top);
 	skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1;
@@ -807,6 +807,8 @@ void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	//debug("Screen::drawSurface20");
 }
 
+//#define TEST_SMOOTHING
+#ifdef TEST_SMOOTHING
 static uint16 average(const uint16 a, const uint16 b) {
 	byte r1, g1, b1, r2, g2, b2;
 	g_system->getScreenFormat().colorToRGB(a, r1, g1, b1);
@@ -814,6 +816,7 @@ static uint16 average(const uint16 a, const uint16 b) {
 //	return g_system->getScreenFormat().RGBToColor((r1 + r1 + r2) / 3, (g1 + g1 + g2) / 3, (b1 + b1 + b2) / 3);
 	return g_system->getScreenFormat().RGBToColor((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2);
 }
+#endif
 
 void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
 	// Scaled
@@ -821,10 +824,10 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
 	const int errYStart = srcHeight / dstHeight;
 	const int errYIncr = srcHeight % dstHeight;
-	const int midY = dstHeight / 2;
+//	const int midY = dstHeight / 2;
 	const int errXStart = srcWidth / dstWidth;
 	const int errXIncr = srcWidth % dstWidth;
-	const int midX = dstWidth / 2;
+//	const int midX = dstWidth / 2;
 	int h = dstHeight, errY = 0, skipY, srcY = srcRect.top;
 	byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top);
 	skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1;
@@ -838,12 +841,14 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co
 		while (w-- > 0) {
 			uint16 pixel = READ_LE_UINT16(src);
 			if (pixel != _colorKey1) {
+#ifdef TEST_SMOOTHING
 				if (errX >= midX) {
 					uint16 npixel = READ_LE_UINT16(src + 2);
 					if (npixel == _colorKey1)
 						npixel = READ_LE_UINT16(dstRow);
 					pixel = average(pixel, npixel);
 				}
+#endif
 				WRITE_LE_UINT16(dstRow, pixel);
 			}
 			dstRow += 2;


Commit: 2de38e3469e85e56b6401a9c3cd97ead2249f67e
    https://github.com/scummvm/scummvm/commit/2de38e3469e85e56b6401a9c3cd97ead2249f67e
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Extract trigger cause sound playing to method

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index c9233ae..289a3d8 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -1086,8 +1086,19 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 
 	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
 		return 0;
+		
+	playTriggerCauseSound(verbId, objectId2, objectId);
 
-	// TODO Extract sound effect playing to method
+	uint32 tempThreadId = newTempThreadId();
+	debug(1, "Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
+	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
+		triggerThreadId);
+	_threads->startThread(causeThread);
+
+	return tempThreadId;
+}
+
+void IllusionsEngine_Duckman::playTriggerCauseSound(uint32 verbId, uint32 objectId2, uint32 objectId) {
 	bool soundWasPlayed = false;
 	if (_scriptResource->_properties.get(0x000E003C)) {
 		if (verbId == 7 && objectId == 0x40003) {
@@ -1103,7 +1114,6 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 			soundWasPlayed = true;
 		}
 	}
-
 	if (!soundWasPlayed) {
 		if (objectId == 0x40003) {
 			playSoundEffect(14);
@@ -1123,14 +1133,6 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 			playSoundEffect(5);
 		}
 	}
-
-	uint32 tempThreadId = newTempThreadId();
-	debug(1, "Starting cause thread %08X with triggerThreadId %08X", tempThreadId, triggerThreadId);
-	CauseThread_Duckman *causeThread = new CauseThread_Duckman(this, tempThreadId, 0, 0,
-		triggerThreadId);
-	_threads->startThread(causeThread);
-
-	return tempThreadId;
 }
 
 bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 1825123..f19a659 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -186,6 +186,7 @@ public:
 	void playSoundEffect(int index);
 	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
 	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
+	void playTriggerCauseSound(uint32 verbId, uint32 objectId2, uint32 objectId);
 	
 	bool loadSavegameFromScript(int16 slotNum, uint32 callingThreadId);
 	bool saveSavegameFromScript(int16 slotNum, uint32 callingThreadId);


Commit: 823ba2f462ff75370d1091b5c59dc950b815eed9
    https://github.com/scummvm/scummvm/commit/823ba2f462ff75370d1091b5c59dc950b815eed9
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement load game from the game's menu system

- Fix pause/unpause opcodes
- Reduce debug output

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h
    engines/illusions/scriptopcodes.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 29a1e68..33610ca 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -623,7 +623,7 @@ void Control::sequenceActor() {
 	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
 		bool breakInner = false;
 		while (!breakInner) {
-			debug(1, "[%08X] SEQ[%08X] op: %08X", _objectId, _actor->_sequenceId, _actor->_seqCodeIp[0]);
+			//debug(1, "[%08X] SEQ[%08X] op: %08X", _objectId, _actor->_sequenceId, _actor->_seqCodeIp[0]);
 			opCall._op = _actor->_seqCodeIp[0] & 0x7F;
 			opCall._opSize = _actor->_seqCodeIp[1];
 			opCall._code = _actor->_seqCodeIp + 2;
@@ -643,18 +643,18 @@ void Control::sequenceActor() {
 	}
 
 	if (_actor->_newFrameIndex != 0) {
-		debug(1, "New frame %d", _actor->_newFrameIndex);
+		//debug(1, "New frame %d", _actor->_newFrameIndex);
 		setActorFrameIndex(_actor->_newFrameIndex);
 		if (_vm->getGameId() == kGameIdBBDOU &&
 			!(_actor->_flags & 1) && (_actor->_flags & 0x1000) && (_objectId != 0x40004)) {
 			appearActor();
 			_actor->_flags &= ~0x1000;
 		}
-		debug(1, "New frame OK");
+		//debug(1, "New frame OK");
 	}
 	
 	if (sequenceFinished) {
-		debug(1, "Sequence has finished");
+		//debug(1, "Sequence has finished");
 		_actor->_seqCodeIp = 0;
 	}
 	
@@ -772,7 +772,7 @@ PointArray *Control::createPath(Common::Point destPt) {
 	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
 	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
 	for (uint i = 0; i < path->size(); ++i) {
-		debug(0, "Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
+		//debug(0, "Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
 	}
 	return path;
 }
@@ -963,7 +963,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 
 	if (!sequence && _vm->getGameId() == kGameIdDuckman) {
-		debug(1, "Load external sequence...");
+		//debug(1, "Load external sequence...");
 		_vm->_resSys->loadResource(0x00060000 | (sequenceId & 0xFFFF), _vm->getCurrentScene(), 0);
 		sequence = _vm->_dict->findSequence(sequenceId);
 		_actor->_flags |= 0x800;
@@ -1394,7 +1394,7 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 				int savedSeqCodeValue1 = actor->_seqCodeValue1;
 				int savedSeqCodeValue3 = actor->_seqCodeValue3;
 				uint32 regionSequenceId = actor->_regionLayer->getRegionSequenceId(regionIndex);
-				debug(1, "Running transition sequence %08X", regionSequenceId);
+				//debug(1, "Running transition sequence %08X", regionSequenceId);
 				Sequence *sequence = _vm->_dict->findSequence(regionSequenceId);
 				actor->_sequenceId = regionSequenceId;
 				actor->_seqCodeIp = sequence->_sequenceCode;
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 289a3d8..8f28a99 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -1080,7 +1080,6 @@ bool IllusionsEngine_Duckman::getTriggerCause(uint32 verbId, uint32 objectId2, u
 }
 
 uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId) {
-	// TODO
 	debug(1, "runTriggerCause(%08X, %08X, %08X)", verbId, objectId2, objectId);
 	uint32 triggerThreadId;
 
@@ -1136,7 +1135,7 @@ void IllusionsEngine_Duckman::playTriggerCauseSound(uint32 verbId, uint32 object
 }
 
 bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
-	const char *fileName = getSavegameFilename(slotNum);
+	const char *fileName = getSavegameFilename(_savegameSlotNum);
 	bool success = loadgame(fileName);
 	if (success)
 		activateSavegame(callingThreadId);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 666005d..7b11916 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -41,7 +41,7 @@ DuckmanMenuSystem::~DuckmanMenuSystem() {
 void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
 	uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
 	
-	debug("DuckmanMenuSystem::runMenu(%08X)", menuId);
+	debug(0, "DuckmanMenuSystem::runMenu(%08X)", menuId);
 
 	setTimeOutDuration(duration, timeOutMenuChoiceIndex);
 	setMenuCallerThreadId(menuCallerThreadId);
@@ -87,11 +87,7 @@ BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
 BaseMenu *DuckmanMenuSystem::createMainMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
 	menu->addMenuItem(new MenuItem("Start New Game", new MenuActionReturnChoice(this, 11)));
-
-	menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionReturnChoice(this, 0)));
-	menu->addMenuItem(new MenuItem("Options", new MenuActionReturnChoice(this, 0)));
-
-	// TODO menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionEnterMenu(this, kDuckmanLoadGameMenu)));
+	menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionLoadGame(this, 1)));
 	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
 	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 12)));
 	return menu;
@@ -109,10 +105,12 @@ BaseMenu *DuckmanMenuSystem::createPauseMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
 	menu->addText("   Game Paused");
 	menu->addText("-------------------");
-	menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 1)));
-	//menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
+	menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Load Game", new MenuActionLoadGame(this, 1)));
+	// TODO menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
+	// TODO menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
 	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
-	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 3)));
+	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
 	return menu;
 }
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 1456cfc..759b4bc 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -203,14 +203,14 @@ void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCal
 	if (maxDuration)
 		duration += _vm->getRandom(maxDuration);
 		
-//duration = 1;//DEBUG Speeds up things
-//duration = 5;
-//debug("duration: %d", duration);
+	//duration = 1;//DEBUG Speeds up things
+	//duration = 5;
+	//debug("duration: %d", duration);
 		
 	if (isAbortable)
-		_vm->startAbortableTimerThread(duration, opCall._threadId);
+		_vm->startAbortableTimerThread(duration, opCall._callerThreadId);
 	else
-		_vm->startTimerThread(duration, opCall._threadId);
+		_vm->startTimerThread(duration, opCall._callerThreadId);
 }
 
 void ScriptOpcodes_Duckman::opRerunThreads(ScriptThread *scriptThread, OpCall &opCall) {
@@ -592,11 +592,11 @@ void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall
 }
 
 void ScriptOpcodes_Duckman::opPause(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->pause(opCall._threadId);
+	_vm->pause(opCall._callerThreadId);
 }
 
 void ScriptOpcodes_Duckman::opUnpause(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->unpause(opCall._threadId);
+	_vm->unpause(opCall._callerThreadId);
 }
 
 void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
@@ -649,21 +649,17 @@ void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &op
 	ARG_UINT32(menuId);
 	ARG_UINT32(timeOutMenuChoiceIndex);
 	
-	debug("menuId: %08X", menuId);
-	debug("timeOutMenuChoiceIndex: %d", timeOutMenuChoiceIndex);
-	
 	MenuChoiceOffsets menuChoiceOffsets;
 
 	// Load menu choices from the stack
 	do {
 		int16 choiceOffs = _vm->_stack->pop();
-		debug("choiceOffs: %04X", choiceOffs);
 		menuChoiceOffsets.push_back(choiceOffs);
 	} while (_vm->_stack->pop() == 0);
 	
 	_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs, 
 		menuId, timeOutDuration, timeOutMenuChoiceIndex,
-		opCall._threadId);
+		opCall._callerThreadId);
 	
 	//DEBUG Resume calling thread, later done by the video player
 	//_vm->notifyThreadId(opCall._callerThreadId);
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 784277f..836282d 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -136,6 +136,7 @@ public:
 	uint32 _lastUpdateTime;
 
 	int _resumeFromSavegameRequested;
+	int _savegameSlotNum;
 	uint32 _savegameSceneId;
 	uint32 _savegameThreadId;
 
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 01b9220..cb82e33 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -28,6 +28,9 @@
 #include "illusions/screentext.h"
 #include "illusions/thread.h"
 #include "illusions/time.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+#include "gui/saveload.h"
 
 namespace Illusions {
 
@@ -105,11 +108,11 @@ void BaseMenuSystem::playSoundEffect14() {
 }
 
 void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) {
-	debug("choiceIndex: %d", choiceIndex);
-	debug("_menuChoiceOffset: %p", (void*)_menuChoiceOffset);
+	debug(0, "choiceIndex: %d", choiceIndex);
+	debug(0, "_menuChoiceOffset: %p", (void*)_menuChoiceOffset);
 	if (choiceIndex > 0 && _menuChoiceOffset) {
 		*_menuChoiceOffset = _menuChoiceOffsets[choiceIndex - 1];
-		debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset);		
+		debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset);
 	}
 	_vm->_threads->notifyId(_menuCallerThreadId);
 	_menuCallerThreadId = 0;
@@ -374,7 +377,7 @@ void BaseMenuSystem::closeMenu() {
 }
 
 void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) {
-	debug("BaseMenuSystem::handleClick() menuItemIndex: %d", menuItemIndex);
+	debug(0, "BaseMenuSystem::handleClick() menuItemIndex: %d", menuItemIndex);
 
 	if (menuItemIndex == 0) {
 	    playSoundEffect14();
@@ -497,6 +500,10 @@ void BaseMenuSystem::setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, i
 	_menuChoiceOffset = menuChoiceOffset;
 }
 
+void BaseMenuSystem::setSavegameSlotNum(int slotNum) {
+	_vm->_savegameSlotNum = slotNum;
+}
+
 void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
 
 	if (!_isTimeOutEnabled)
@@ -516,7 +523,6 @@ void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
 			_timeOutStartTime = getCurrentTime();
 			_timeOutEndTime = _timeOutDuration + _timeOutStartTime;
 		} else if (isTimerExpired(_timeOutStartTime, _timeOutEndTime)) {
-			debug("timeout reached");
 			_isTimeOutEnabled = false;
 			selectMenuChoiceIndex(_timeOutMenuChoiceIndex);
 		}
@@ -590,4 +596,29 @@ void MenuActionEnterQueryMenu::execute() {
 	_menuSystem->enterSubMenuById(_menuId);
 }
 
+// MenuActionLoadGame
+
+MenuActionLoadGame::MenuActionLoadGame(BaseMenuSystem *menuSystem, uint choiceIndex)
+	: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
+}
+
+void MenuActionLoadGame::execute() {
+	const EnginePlugin *plugin = NULL;
+	EngineMan.findGame(ConfMan.get("gameid"), &plugin);
+	GUI::SaveLoadChooser *dialog;
+	Common::String desc;
+	int slot;
+
+	dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+	slot = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+	delete dialog;
+
+	if (slot >= 0) {
+		_menuSystem->setSavegameSlotNum(slot);
+		_menuSystem->selectMenuChoiceIndex(_choiceIndex);
+	}
+
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index 98ef924..26a5931 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -102,6 +102,7 @@ public:
 	void setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex);
 	void setMenuCallerThreadId(uint32 menuCallerThreadId);
 	void setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset);
+	void setSavegameSlotNum(int slotNum);
 	virtual bool initMenuCursor() = 0;
 	virtual int getGameState() = 0;
 	virtual void setGameState(int gameState) = 0;
@@ -230,6 +231,14 @@ protected:
 	uint _confirmationChoiceIndex;
 };
 
+class MenuActionLoadGame : public BaseMenuAction {
+public:
+	MenuActionLoadGame(BaseMenuSystem *menuSystem, uint choiceIndex);
+	virtual void execute();
+protected:
+	uint _choiceIndex;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_MENUSYSTEM_H
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 7d515a8..38fdbec 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -62,7 +62,7 @@ ScriptOpcodes::~ScriptOpcodes() {
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug(0, "\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
+	debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index cf20380..8ba2586 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -61,9 +61,9 @@ protected:
 
 // Convenience macros
 #define	ARG_SKIP(x) opCall.skip(x); 
-#define ARG_BYTE(name) byte name = opCall.readByte(); debug(0, "ARG_BYTE(" #name " = %d)", name);
-#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(0, "ARG_INT16(" #name " = %d)", name);
-#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(0, "ARG_UINT32(" #name " = %08X)", name);
+#define ARG_BYTE(name) byte name = opCall.readByte(); debug(5, "ARG_BYTE(" #name " = %d)", name);
+#define ARG_INT16(name) int16 name = opCall.readSint16(); debug(5, "ARG_INT16(" #name " = %d)", name);
+#define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(5, "ARG_UINT32(" #name " = %08X)", name);
 
 } // End of namespace Illusions
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 8d678eb..5ce25ca 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -45,7 +45,7 @@ SequenceOpcodes::~SequenceOpcodes() {
 void SequenceOpcodes::execOpcode(Control *control, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("SequenceOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug(1, "execOpcode(%d)", opCall._op);
+	debug(3, "execOpcode(%d)", opCall._op);
 	(*_opcodes[opCall._op])(control, opCall);
 }
 


Commit: 88e2b3fd9ee11ac724a676e8496e36691551e1c8
    https://github.com/scummvm/scummvm/commit/88e2b3fd9ee11ac724a676e8496e36691551e1c8
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement special code 160030

Changed paths:
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 4b9230b..61422a8 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -96,6 +96,39 @@ void BbdouCursor::disable(uint32 objectId) {
 	hide(objectId);
 }
 
+void BbdouCursor::reset(uint32 objectId) {
+	Control *control = _vm->_dict->getObjectControl(objectId);
+
+	_data._item10._field0 = 1;
+	_data._mode = 1;
+	_data._mode2 = 0;
+	_data._verbId1 = 0x1B0000;
+	_data._currOverlappedObjectId = 0;
+	_data._overlappedObjectId = 0;
+	_data._sequenceId = 0x6000F;
+	_data._holdingObjectId = 0;
+	_data._holdingObjectId2 = 0;
+	_data._visibleCtr = 0;
+	_data._causeThreadId1 = 0;
+	_data._flags = 0;
+	_data._item10._field58 = 1;
+	_data._sequenceId98 = 0;
+	_data._idleCtr = 0;
+	_data._item10._verbId = 0x1B0000;
+	_data._item10._playSound48 = 0;
+	_data._item10._objectIds[0] = 0;
+	_data._item10._objectIds[1] = 0;
+	_data._item10._index = 0;
+	_data._item10._flag56 = 0;
+	clearCursorDataField14();
+	control->setActorIndexTo1();
+	control->startSequenceActor(0x60029, 2, 0);
+
+	_bbdou->resetItem10(control->_objectId, &_data._item10);
+	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::cursorInteractControlRoutine));
+	
+}
+
 void BbdouCursor::addCursorSequence(uint32 objectId, uint32 sequenceId) {
 	for (uint i = 0; i < kMaxCursorSequences; ++i)
 		if (_cursorSequences[i]._objectId == 0) {
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index 7b42e4f..8f454b7 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -90,6 +90,7 @@ public:
 	void init(uint32 objectId, uint32 progResKeywordId);
 	void enable(uint32 objectId);
 	void disable(uint32 objectId);
+	void reset(uint32 objectId);
 	void addCursorSequence(uint32 objectId, uint32 sequenceId);
 	uint32 findCursorSequenceId(uint32 objectId);
 	void setStruct8bsValue(uint32 objectId, int value);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 5257a48..b10b8d2 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -124,6 +124,7 @@ typedef Common::Functor1Mem<OpCall&, void, BbdouSpecialCode> SpecialCodeFunction
 
 void BbdouSpecialCode::init() {
 	// TODO
+	// 0x00160001 only used for original debugging purposes
 	SPECIAL(0x00160006, spcInitCursor);
 	SPECIAL(0x00160008, spcEnableCursor);
 	SPECIAL(0x00160009, spcDisableCursor);
@@ -141,6 +142,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001E, spcRemoveInventoryItem);
 	SPECIAL(0x0016001F, spcHasInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
+	SPECIAL(0x00160030, spcResetCursor);
 	SPECIAL(0x00160032, spcSetCursorField90);
 	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
 	SPECIAL(0x00160038, spcInitRadarMicrophone);
@@ -266,6 +268,12 @@ void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 	_inventory->close();
 }
 
+void BbdouSpecialCode::spcResetCursor(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	_cursor->reset(objectId);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void BbdouSpecialCode::spcSetCursorField90(OpCall &opCall) {
 	ARG_SKIP(4); // objectId unused
 	_cursor->_data._field90 = 1;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 2462c2b..6ffde37 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -119,6 +119,7 @@ public:
 	void spcRemoveInventoryItem(OpCall &opCall);
 	void spcHasInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
+	void spcResetCursor(OpCall &opCall);
 	void spcSetCursorField90(OpCall &opCall);
 	void spcIsCursorHoldingObjectId(OpCall &opCall);
 	void spcInitRadarMicrophone(OpCall &opCall);


Commit: c6f2c6ba4eb013119597399e04b58435fe70bfab
    https://github.com/scummvm/scummvm/commit/c6f2c6ba4eb013119597399e04b58435fe70bfab
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement special code 16000F

Changed paths:
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 61422a8..090c809 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -46,7 +46,7 @@ void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
 	_vm->_controls->placeActor(0x50001, pos, 0x6000C, objectId, 0);
 
 	Control *control = _vm->_dict->getObjectControl(objectId);
-	//control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::actorControlRoutine1));
+	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::actorControlRoutine1));
 	control->_flags |= 8;
 	
 	_data._mode = 1;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index b10b8d2..db03cb8 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -131,6 +131,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016000A, spcAddCursorSequence);
 	SPECIAL(0x0016000B, spcCursorStartHoldingObjectId);
 	SPECIAL(0x0016000C, spcCursorStopHoldingObjectId);
+	SPECIAL(0x0016000F, spcSetCursorState);
 	SPECIAL(0x00160013, spcInitBubble);
 	SPECIAL(0x00160014, spcSetupBubble);
 	SPECIAL(0x00160015, spcSetObjectInteractMode);
@@ -205,6 +206,18 @@ void BbdouSpecialCode::spcCursorStopHoldingObjectId(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void BbdouSpecialCode::spcSetCursorState(OpCall &opCall) {
+	ARG_UINT32(objectId);
+	ARG_UINT32(newState);
+	_cursor->_data._item10._field0 = newState;
+	_cursor->clearCursorDataField14();
+	if (newState == 5)
+		setCursorControlRoutine(objectId, 1);
+	else
+		setCursorControlRoutine(objectId, 0);
+	_vm->notifyThreadId(opCall._threadId);
+}
+
 void BbdouSpecialCode::spcInitBubble(OpCall &opCall) {
 	_bubble->init();
 	_vm->notifyThreadId(opCall._threadId);
@@ -271,6 +284,7 @@ void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 void BbdouSpecialCode::spcResetCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_cursor->reset(objectId);
+	setCursorControlRoutine(objectId, 0);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 6ffde37..762aff1 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -108,6 +108,7 @@ public:
 	void spcAddCursorSequence(OpCall &opCall);
 	void spcCursorStartHoldingObjectId(OpCall &opCall);
 	void spcCursorStopHoldingObjectId(OpCall &opCall);
+	void spcSetCursorState(OpCall &opCall);
 	void spcInitBubble(OpCall &opCall);
 	void spcSetupBubble(OpCall &opCall);
 	void spcSetObjectInteractMode(OpCall &opCall);


Commit: c0c25691e0d388e30d4ca1a5acdb31ef0aabfb8d
    https://github.com/scummvm/scummvm/commit/c0c25691e0d388e30d4ca1a5acdb31ef0aabfb8d
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement missing script opcodes and special opcodes; fix ActorType bugs and more

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h
    engines/illusions/camera.cpp
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/scriptopcodes.cpp
    engines/illusions/threads/talkthread.cpp


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index db03cb8..d7ba8ce 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -148,6 +148,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
 	SPECIAL(0x00160038, spcInitRadarMicrophone);
 	SPECIAL(0x0016003A, spcSaladCtl);
+	SPECIAL(0x0016003B, spcRunCause);
 }
 
 void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
@@ -329,6 +330,16 @@ void BbdouSpecialCode::spcSaladCtl(OpCall &opCall) {
 	}
 }
 
+void BbdouSpecialCode::spcRunCause(OpCall &opCall) {
+	ARG_UINT32(cursorObjectId);
+	ARG_UINT32(verbId);
+	ARG_UINT32(objectId1);
+	ARG_UINT32(objectId2);
+	Control *cursorControl = _vm->getObjectControl(cursorObjectId);
+	debug("runCause(%08X, %08X, %08X)", verbId, objectId1, objectId2);
+	runCause(cursorControl, _cursor->_data, verbId, objectId1, objectId2, 0);
+}
+
 void BbdouSpecialCode::playSoundEffect(int soundIndex) {
 	static const uint32 kSoundEffectIds[] = {
 		      0, 1,
@@ -595,7 +606,60 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 }
 
 void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 deltaTime) {
+
+	static const struct ShooterAnim {
+		uint32 objectId;
+		uint32 sequenceIds1[8];
+		uint32 sequenceIds2[8];
+	} kShooterAnims[] = {
+		{0x401C4,
+		{0x60637, 0x60638, 0x60639, 0x6063A, 0x6063B, 0x6063C, 0x6063D, 0x6063E},
+		{0x6063F, 0x60640, 0x60641, 0x60642, 0x60643, 0x60644, 0x60645, 0x60646}},
+		{0x401C3,
+		{0x6064A, 0x6064B, 0x6064C, 0x6064D, 0x6064E, 0x6064F, 0x60650, 0x60651},
+		{0x60652, 0x60653, 0x60654, 0x60655, 0x60656, 0x60657, 0x60658, 0x60659}}
+	};
+
+	Actor *actor = cursorControl->_actor;
+	CursorData &cursorData = _cursor->_data;
+
 	// TODO
+	//debug("BbdouSpecialCode::cursorControlRoutine2()");
+
+	if (cursorData._visibleCtr <= 0) {
+		if (cursorData._currOverlappedObjectId || cursorData._mode == 3) {
+			if (cursorData._mode == 3)
+				_cursor->restoreInfo();
+			cursorControl->setActorIndexTo1();
+		}
+		cursorData._currOverlappedObjectId = 0;
+		return;
+	}
+
+	Common::Point cursorPos = _vm->_input->getCursorPosition();
+
+	if (cursorPos != actor->_position) {
+		actor->_position = cursorPos;
+		int16 gridX = 8 * cursorPos.x / 640;
+		if (gridX >= 8)
+			gridX = 4;
+
+		for (uint i = 0; i < 2; ++i) {
+			const ShooterAnim &anim = kShooterAnims[i];
+			Control *control2 = _vm->getObjectControl(anim.objectId);
+			if (control2 && control2->_actor) {
+				if (_shooterStatus[i].gridX != gridX && (!_shooterStatus[i].flag || !control2->_actor->_seqCodeIp)) {
+					_shooterStatus[i].gridX = gridX;
+					control2->_actor->_seqCodeIp = 0;
+					control2->startSequenceActor(anim.sequenceIds1[gridX], 2, 0);
+				}
+			}
+		}
+
+	}
+
+	// TODO A lot of stuff
+
 }
 
 bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId) {
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 762aff1..22fdd12 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -82,6 +82,11 @@ public:
 	RadarMicrophoneZone _zones[8];
 };
 
+struct ShooterStatus {
+	int gridX;
+	bool flag;
+};
+
 class BbdouSpecialCode : public SpecialCode {
 public:
 	BbdouSpecialCode(IllusionsEngine_BBDOU *vm);
@@ -101,6 +106,9 @@ public:
 	uint _saladCount;
 	uint32 _saladObjectIds[12];
 
+	// Shooter
+	ShooterStatus _shooterStatus[2];
+
 	// Special code interface functions
 	void spcInitCursor(OpCall &opCall);
 	void spcEnableCursor(OpCall &opCall);
@@ -125,6 +133,7 @@ public:
 	void spcIsCursorHoldingObjectId(OpCall &opCall);
 	void spcInitRadarMicrophone(OpCall &opCall);
 	void spcSaladCtl(OpCall &opCall);
+	void spcRunCause(OpCall &opCall);
 
 	void playSoundEffect(int soundIndex);
 	void resetItem10(uint32 objectId, Item10 *item10);
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 95be82f..8466780 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -196,7 +196,14 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	startScriptThread(0x00020004, 0, 0, 0, 0);
 	_doScriptThreadInit = true;
 
+	_walkthroughStarted = false;
+
 	while (!shouldQuit()) {
+		if (_walkthroughStarted) {
+			//enterScene(0x10003, 0);
+			startScriptThread(0x00020404, 0, 0, 0, 0);
+			_walkthroughStarted = false;
+		}
 		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index 3b190ea..65d9e26 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -72,6 +72,8 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
+	bool _walkthroughStarted;
+
 	void initInput();
 
 	void initUpdateFunctions();
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 945892f..6004f44 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -62,37 +62,56 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(4, opTerminate);
 	OPCODE(5, opJump);
 	OPCODE(6, opStartScriptThread);
+	// 7 unused
 	OPCODE(8, opStartTempScriptThread);
 	OPCODE(9, opStartTimerThread);
+	// 10-11 unused
 	OPCODE(12, opNotifyThreadId);
+	// 13 unused
 	OPCODE(14, opSetThreadSceneId);
 	OPCODE(15, opEndTalkThreads);
 	OPCODE(16, opLoadResource);
 	OPCODE(17, opUnloadResource);
+	// TODO OPCODE(18, opPauseText);
+	// TODO OPCODE(19, opResumeText);
 	OPCODE(20, opEnterScene);
+	OPCODE(21, opLeaveScene);
+	// TODO OPCODE(22, opEnterPause);
+	// TODO OPCODE(23, opLeavePause);
+	OPCODE(24, opUnloadActiveScenes);
 	OPCODE(25, opChangeScene);
 	OPCODE(26, opStartModalScene);
 	OPCODE(27, opExitModalScene);
+	// 28-29 unused
 	OPCODE(30, opEnterCloseUpScene);
 	OPCODE(31, opExitCloseUpScene);
 	OPCODE(32, opPanCenterObject);
+	// 33 unused
 	OPCODE(34, opPanToObject);
 	OPCODE(35, opPanToNamedPoint);
 	OPCODE(36, opPanToPoint);
 	OPCODE(37, opPanStop);
 	OPCODE(39, opSetDisplay);
+	OPCODE(40, opSetCameraBounds);
+	OPCODE(41, opSetCameraBoundsToMasterBg);
 	OPCODE(42, opIncBlockCounter);
 	OPCODE(43, opClearBlockCounter);
+	// 44 unused
 	OPCODE(45, opSetProperty);
 	OPCODE(46, opPlaceActor);
 	OPCODE(47, opFaceActor);
 	OPCODE(48, opFaceActorToObject);	
 	OPCODE(49, opStartSequenceActor);
+	// 50 unused
 	OPCODE(51, opStartMoveActor);
+	// 52 unused
 	OPCODE(53, opSetActorToNamedPoint);
+	// TODO OPCODE(54, opSetActorPosition);
+	// 55 unused
 	OPCODE(56, opStartTalkThread);
 	OPCODE(57, opAppearActor);
 	OPCODE(58, opDisappearActor);
+	OPCODE(59, opIsActorVisible);
 	OPCODE(60, opActivateObject);
 	OPCODE(61, opDeactivateObject);
 	OPCODE(62, opSetDefaultSequence);
@@ -101,18 +120,31 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(65, opSetDenySfx);
 	OPCODE(66, opSetAdjustUpSfx);
 	OPCODE(67, opSetAdjustDnSfx);
+	// 68 unused
+	// TODO OPCODE(69, opPause);
+	// TODO OPCODE(70, opResume);
 	OPCODE(71, opStartSound);
+	// TODO OPCODE(72, opStartSoundAtNamedPoint);
+	// TODO OPCODE(73, opStartSoundAtActor);
 	OPCODE(74, opStopSound);
 	OPCODE(75, opStartMusic);
 	OPCODE(76, opStopMusic);
+	// 77 unused
 	OPCODE(78, opStackPushRandom);
 	OPCODE(79, opIfLte);
 	OPCODE(80, opAddMenuChoice);
 	OPCODE(81, opDisplayMenu);
 	OPCODE(82, opSwitchMenuChoice);
+	// TODO OPCODE(83, opQuitGame);
 	OPCODE(84, opResetGame);
+	// TODO OPCODE(85, opSaveGame);
+	// TODO OPCODE(86, opRestoreGame);
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
+	// TODO 89 NOP
+	// 90 unused
+	// TODO 91 NOP
+	// 92-102 unused
 	OPCODE(103, opJumpIf);
 	OPCODE(104, opIsPrevSceneId);
 	OPCODE(105, opIsCurrentSceneId);
@@ -123,11 +155,14 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(110, opGetProperty);
 	OPCODE(111, opCompareBlockCounter);
 	OPCODE(126, opDebug126);
+	OPCODE(127, opDebug127);
 	OPCODE(144, opPlayVideo);
 	OPCODE(146, opStackPop);
 	OPCODE(147, opStackDup);
 	OPCODE(148, opLoadSpecialCodeModule);
 	OPCODE(150, opRunSpecialCode);
+	OPCODE(152, opLinkObjectToObject);
+	OPCODE(153, opUnlinkObject);
 	OPCODE(160, opStopActor);
 	OPCODE(161, opSetActorUsePan);
 	OPCODE(168, opStartAbortableThread);
@@ -185,7 +220,7 @@ void ScriptOpcodes_BBDOU::opStartTimerThread(ScriptThread *scriptThread, OpCall
 	if (maxDuration)
 		duration += _vm->getRandom(maxDuration);
 		
-duration = 1;//DEBUG Speeds up things		
+//duration = 1;//DEBUG Speeds up things
 		
 	if (isAbortable)
 		_vm->startAbortableTimerThread(duration, opCall._threadId);
@@ -237,6 +272,16 @@ void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCal
 		opCall._result = kTSTerminate;
 }
 
+void ScriptOpcodes_BBDOU::opLeaveScene(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->exitScene(opCall._callerThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(sceneId);
+	_vm->dumpActiveScenes(sceneId, opCall._callerThreadId);
+}
+
 //DEBUG Scenes
 //uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
 //uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
@@ -249,7 +294,8 @@ void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCal
 //uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
 //uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
-uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
+//uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
+uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
 
 void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -257,6 +303,12 @@ void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCa
 	ARG_UINT32(threadId);
 
 	if (dsceneId) {
+//#define RUN_WALKTHROUGH
+#ifdef RUN_WALKTHROUGH
+		_vm->_walkthroughStarted = true;
+		dsceneId = 0;
+		return;
+#endif
 		sceneId = dsceneId;
 		threadId = dthreadId;
 		dsceneId = 0;
@@ -346,6 +398,20 @@ void ScriptOpcodes_BBDOU::opSetDisplay(ScriptThread *scriptThread, OpCall &opCal
 	_vm->_screen->setDisplayOn(flag != 0);
 }
 
+void ScriptOpcodes_BBDOU::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(x1);
+	ARG_INT16(y1);
+	ARG_INT16(x2);
+	ARG_INT16(y2);
+	_vm->_camera->setBounds(Common::Point(x1, y1), Common::Point(x2, y2));
+}
+
+void ScriptOpcodes_BBDOU::opSetCameraBoundsToMasterBg(ScriptThread *scriptThread, OpCall &opCall) {
+	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
+	_vm->_camera->setBoundsToDimensions(bgDimensions);
+}
+
 void ScriptOpcodes_BBDOU::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(index);	
 	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
@@ -410,6 +476,8 @@ void ScriptOpcodes_BBDOU::opStartMoveActor(ScriptThread *scriptThread, OpCall &o
 	ARG_UINT32(namedPointId);
 	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
+	//if (!control) { opCall._deltaOfs = 0; return; }// TODO CHECKME
+	if (!control) { return; }// TODO CHECKME
 	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
 	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
 }
@@ -454,6 +522,14 @@ void ScriptOpcodes_BBDOU::opDisappearActor(ScriptThread *scriptThread, OpCall &o
 	control->disappearActor();
 }
 
+void ScriptOpcodes_BBDOU::opIsActorVisible(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	const bool visible = control && control->isActorVisible();
+	_vm->_stack->push(visible ? 1 : 0);
+}
+
 void ScriptOpcodes_BBDOU::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
@@ -675,7 +751,12 @@ void ScriptOpcodes_BBDOU::opCompareBlockCounter(ScriptThread *scriptThread, OpCa
 
 void ScriptOpcodes_BBDOU::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
 	// NOTE Prints some debug text
-	debug(1, "[DBG] %s", (char*)opCall._code);
+	debug(1, "[DBG126] %s", (char*)opCall._code);
+}
+
+void ScriptOpcodes_BBDOU::opDebug127(ScriptThread *scriptThread, OpCall &opCall) {
+	// NOTE Prints some debug text
+	debug(1, "[DBG127] %s", (char*)opCall._code);
 }
 
 void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
@@ -711,6 +792,22 @@ void ScriptOpcodes_BBDOU::opRunSpecialCode(ScriptThread *scriptThread, OpCall &o
 	_vm->_specialCode->run(specialCodeId, opCall);
 }
 
+void ScriptOpcodes_BBDOU::opLinkObjectToObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	ARG_UINT32(parentObjectId);
+	ARG_UINT32(linkedObjectValue);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->linkToObject(parentObjectId, linkedObjectValue);
+}
+
+void ScriptOpcodes_BBDOU::opUnlinkObject(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_UINT32(objectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->unlinkObject();
+}
+
 void ScriptOpcodes_BBDOU::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index 89f73dc..20b6aff 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -54,6 +54,8 @@ protected:
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeaveScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
@@ -64,7 +66,9 @@ protected:
 	void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);	
+	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);	
+	void opSetCameraBoundsToMasterBg(ScriptThread *scriptThread, OpCall &opCall);
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
@@ -77,6 +81,7 @@ protected:
 	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
+	void opIsActorVisible(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
@@ -107,11 +112,14 @@ protected:
 	void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
 	void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
+	void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
 	void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
 	void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
+	void opLinkObjectToObject(ScriptThread *scriptThread, OpCall &opCall);
+	void opUnlinkObject(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index ff774b6..9e18ff3 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -211,6 +211,9 @@ void Camera::pushCameraMode() {
 }
 
 void Camera::popCameraMode() {
+	if (_stack.empty())
+		return;
+
 	CameraModeStackItem item = _stack.pop();
 
 	if (item._panObjectId && !_vm->getObjectActorPositionPtr(item._panObjectId)) {
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index 8c65626..2152b77 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -183,7 +183,6 @@ void ActorInstance::load(Resource *resource) {
 	_sceneId = resource->_sceneId;
 	_pauseCtr = 0;
 	initActorTypes();
-	registerResources();
 }
 
 void ActorInstance::unload() {
@@ -219,6 +218,11 @@ void ActorInstance::initActorTypes() {
 			if (actorType->_value1E == 0)
 				actorType->_value1E = actorType2->_value1E;
 		}
+		_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
+	}
+	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
+		Sequence *sequence = &_actorResource->_sequences[i];
+		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
 	}
 }
 
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index d18d46b..a959196 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -202,11 +202,22 @@ void PathWalkRects::load(byte *dataStart, Common::SeekableReadStream &stream) {
 
 // BackgroundResource
 
-BackgroundResource::BackgroundResource() {
+BackgroundResource::BackgroundResource()
+	: _bgInfos(0), _scaleLayers(0), _priorityLayers(0), _regionLayers(0),
+	_regionSequences(0), _backgroundObjects(0), _pathWalkPoints(0),
+	_pathWalkRects(0), _palettes(0) {
 }
 
 BackgroundResource::~BackgroundResource() {
-	// TODO Free stuff
+	delete[] _bgInfos;
+	delete[] _scaleLayers;
+	delete[] _priorityLayers;
+	delete[] _regionLayers;
+	delete[] _regionSequences;
+	delete[] _backgroundObjects;
+	delete[] _pathWalkPoints;
+	delete[] _pathWalkRects;
+	delete[] _palettes;
 }
 
 void BackgroundResource::load(byte *data, uint32 dataSize) {
diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp
index 38fdbec..7d515a8 100644
--- a/engines/illusions/scriptopcodes.cpp
+++ b/engines/illusions/scriptopcodes.cpp
@@ -62,7 +62,7 @@ ScriptOpcodes::~ScriptOpcodes() {
 void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
+	debug(0, "\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str());
 	(*_opcodes[opCall._op])(scriptThread, opCall);
 }
 
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 930b83c..c60bc5d 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -179,7 +179,12 @@ int TalkThread::onUpdate() {
 			}
 			_flags |= 2;
 		}
+//#define DEBUG_SPEEDUP_TALK
+#ifdef DEBUG_SPEEDUP_TALK
+if (true) {
+#else
 		if (_objectId && _vm->_input->pollEvent(kEventSkip)) {
+#endif
 			if (!(_flags & 8)) {
 				_vm->_screenText->removeText();
 				if (_entryText && *_entryText)


Commit: 27a5e93268310062767c52e6fd1effa2e39920e4
    https://github.com/scummvm/scummvm/commit/27a5e93268310062767c52e6fd1effa2e39920e4
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Start implementing crosshair cursor (used in shooting minigame)

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index d7ba8ce..9278a41 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -377,7 +377,7 @@ void BbdouSpecialCode::setCursorControlRoutine(uint32 objectId, int num) {
 			new Common::Functor2Mem<Control*, uint32, void, BbdouSpecialCode>(this, &BbdouSpecialCode::cursorInteractControlRoutine));
 	else
 		control->_actor->setControlRoutine(
-			new Common::Functor2Mem<Control*, uint32, void, BbdouSpecialCode>(this, &BbdouSpecialCode::cursorControlRoutine2));
+			new Common::Functor2Mem<Control*, uint32, void, BbdouSpecialCode>(this, &BbdouSpecialCode::cursorCrosshairControlRoutine));
 }
 
 Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos) {
@@ -605,7 +605,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 
 }
 
-void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 deltaTime) {
+void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uint32 deltaTime) {
 
 	static const struct ShooterAnim {
 		uint32 objectId;
@@ -620,12 +620,14 @@ void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 delt
 		{0x60652, 0x60653, 0x60654, 0x60655, 0x60656, 0x60657, 0x60658, 0x60659}}
 	};
 
+	static const uint32 kShooterObjectIds[] = {
+		0x401DF, 0x401E0, 0x401E1, 0x401E2,
+		0x401E3, 0x401E4, 0x401E5, 0x401E6,
+	};
+
 	Actor *actor = cursorControl->_actor;
 	CursorData &cursorData = _cursor->_data;
 
-	// TODO
-	//debug("BbdouSpecialCode::cursorControlRoutine2()");
-
 	if (cursorData._visibleCtr <= 0) {
 		if (cursorData._currOverlappedObjectId || cursorData._mode == 3) {
 			if (cursorData._mode == 3)
@@ -636,11 +638,11 @@ void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 delt
 		return;
 	}
 
-	Common::Point cursorPos = _vm->_input->getCursorPosition();
+	Common::Point screenCursorPos = _vm->_input->getCursorPosition();
 
-	if (cursorPos != actor->_position) {
-		actor->_position = cursorPos;
-		int16 gridX = 8 * cursorPos.x / 640;
+	if (screenCursorPos != actor->_position) {
+		actor->_position = screenCursorPos;
+		int16 gridX = 8 * screenCursorPos.x / 640;
 		if (gridX >= 8)
 			gridX = 4;
 
@@ -658,7 +660,146 @@ void BbdouSpecialCode::cursorControlRoutine2(Control *cursorControl, uint32 delt
 
 	}
 
-	// TODO A lot of stuff
+	Common::Point cursorPos = getBackgroundCursorPos(cursorPos);
+	bool foundOverlapped = false;
+	Control *overlappedControl = 0;
+
+	if (cursorData._flags & 1)
+		foundOverlapped = false;
+    else {
+		/* TODO Implement getOverlappedObjectAccurate
+		foundOverlapped = _vm->_controls->getOverlappedObjectAccurate(cursorControl, cursorPos,
+			&overlappedControl, cursorData._item10._field58);
+		*/
+	}
+
+	if (foundOverlapped) {
+		if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
+			resetItem10(cursorControl->_objectId, &cursorData._item10);
+			int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+			if (value == 2 || value == 3 || value == 4 || value == 5 || value == 6 || value == 7) {
+				if (cursorData._mode != 3) {
+					_cursor->saveInfo();
+					cursorData._mode = 3;
+					cursorData._item10._verbId = 0x1B0006;
+					cursorData._holdingObjectId = 0;
+				}
+				switch (value) {
+				case 2:
+					cursorData._sequenceId = 0x60010;
+					break;
+				case 3:
+					cursorData._sequenceId = 0x60011;
+					break;
+				case 4:
+					cursorData._sequenceId = 0x60012;
+					break;
+				case 5:
+					cursorData._sequenceId = 0x60013;
+					break;
+				case 6:
+					cursorData._sequenceId = 0x60015;
+					break;
+				case 7:
+					cursorData._sequenceId = 0x60014;
+					break;
+				}
+				_cursor->show(cursorControl);
+				cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+			} else {
+				if (cursorData._mode == 3)
+					_cursor->restoreInfo();
+				_cursor->show(cursorControl);
+				cursorControl->setActorIndexTo2();
+				if (overlappedControl->_objectId) {
+					cursorData._item10._verbId = 0x1B0003;
+					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+				} else {
+					cursorData._item10._verbId = 0x1B0002;
+					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+				}
+			}
+		}
+	} else {
+		if (cursorData._currOverlappedObjectId || cursorData._mode == 3) {
+			if (cursorData._mode == 3)
+				_cursor->restoreInfo();
+			_cursor->show(cursorControl);
+			cursorControl->setActorIndexTo1();
+			resetItem10(cursorControl->_objectId, &cursorData._item10);
+		}
+		cursorData._currOverlappedObjectId = 0;
+	}
+
+	actor->_seqCodeValue1 = 100 * deltaTime;
+
+	if (cursorData._currOverlappedObjectId) {
+
+		if (_vm->_input->pollEvent(kEventLeftClick)) {
+
+			uint32 outSceneId, outVerbId, outObjectId2, outObjectId;
+			bool success = getShooterCause(_vm->getCurrentScene(),
+				cursorData._item10._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId,
+				outSceneId, outVerbId, outObjectId2, outObjectId);
+
+			uint index = (uint)_vm->getRandom(2);
+			const ShooterAnim &anim = kShooterAnims[index];
+			uint32 objectId = anim.objectId;
+			int gridX = _shooterStatus[index].gridX;
+			Control *gunControl = _vm->getObjectControl(objectId);
+			if (gunControl) {
+				_shooterStatus[index].flag = true;
+				gunControl->startSequenceActor(anim.sequenceIds2[gridX], 2, 0);
+			}
+			Control *hitControl = _vm->getObjectControl(kShooterObjectIds[_shooterObjectIdIndex]);
+			if (hitControl) {
+				hitControl->setActorPosition(actor->_position);
+				hitControl->startSequenceActor(0x6068D, 2, 0);
+			}
+			++_shooterObjectIdIndex;
+			if (_shooterObjectIdIndex >= ARRAYSIZE(kShooterObjectIds))
+				_shooterObjectIdIndex = 0;
+
+			if (success) {
+				_cursor->hide(cursorControl->_objectId);
+				uint32 threadId = startCauseThread(cursorControl->_objectId, _vm->getCurrentScene(), outVerbId, outObjectId2, outObjectId);
+				if (cursorData._field90) {
+					_vm->_threads->killThread(cursorData._causeThreadId2);
+					cursorData._field90 = 0;
+				}
+				cursorData._causeThreadId1 = _vm->causeTrigger(outSceneId, outVerbId, outObjectId2, outObjectId, threadId);
+				cursorData._causeThreadId2 = cursorData._causeThreadId1;
+				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				cursorData._currOverlappedObjectId = 0;
+				cursorControl->setActorIndexTo1();
+			}
+
+		} else if (_vm->_input->pollEvent(kEventRightClick) && cursorData._item10._playSound48 == 1 && !cursorData._item10._flag56) {
+			// TODO I don't think this is used; _playSound48 seems to be always 0 here
+			debug("Cursor_sub_10004DD0 TODO");
+			// TODO Cursor_sub_10004DD0(controla->objectId, cursorData->currOverlappedObjectId, cursorData->holdingObjectId, &cursorData->item10);
+		}
+
+	} else if (_vm->_input->pollEvent(kEventLeftClick)) {
+	    uint index = (uint)_vm->getRandom(2);
+		const ShooterAnim &anim = kShooterAnims[index];
+		uint32 objectId = anim.objectId;
+		int gridX = _shooterStatus[index].gridX;
+		Control *gunControl = _vm->getObjectControl(objectId);
+		if (gunControl) {
+			_shooterStatus[index].flag = true;
+			gunControl->startSequenceActor(anim.sequenceIds2[gridX], 2, 0);
+		}
+		Control *hitControl = _vm->getObjectControl(kShooterObjectIds[_shooterObjectIdIndex]);
+		if (hitControl) {
+			hitControl->setActorPosition(actor->_position);
+			hitControl->startSequenceActor(0x6068D, 2, 0);
+		}
+		++_shooterObjectIdIndex;
+		if (_shooterObjectIdIndex >= ARRAYSIZE(kShooterObjectIds))
+			_shooterObjectIdIndex = 0;
+
+	}
 
 }
 
@@ -832,4 +973,60 @@ void BbdouSpecialCode::addSalad(uint32 sequenceId) {
 	control->deactivateObject();
 }
 
+bool BbdouSpecialCode::getShooterCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
+	uint32 &outSceneId, uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId) {
+	bool success = false;
+	objectId2 = verbId != 0x1B0003 ? 0 : objectId2;
+	if (_vm->causeIsDeclared(sceneId, verbId, objectId2, objectId)) {
+		outSceneId = sceneId;
+		outVerbId = verbId;
+		outObjectId2 = objectId2;
+		outObjectId = objectId;
+		success = true;
+	} else if (verbId == 0x1B0003 && _vm->causeIsDeclared(sceneId, 0x1B0008, 0, objectId)) {
+		outSceneId = sceneId;
+		outVerbId = 0x1B0003;
+		outObjectId2 = 0;
+		outObjectId = objectId;
+		success = true;
+	} else if (_vm->causeIsDeclared(sceneId, verbId, objectId2, 0x40001)) {
+		outSceneId = sceneId;
+		outVerbId = verbId;
+		outObjectId2 = objectId2;
+		outObjectId = 0x40001;
+		success = true;
+	} else if (verbId == 0x1B0003 && _vm->causeIsDeclared(sceneId, 0x1B0008, 0, 0x40001)) {
+		outSceneId = sceneId;
+		outVerbId = 0x1B0008;
+		outObjectId2 = 0;
+		outObjectId = 0x40001;
+		success = true;
+	} else if (_vm->causeIsDeclared(0x10003, verbId, objectId2, objectId)) {
+		outSceneId = 0x10003;
+		outVerbId = verbId;
+		outObjectId2 = objectId2;
+		outObjectId = objectId;
+		success = true;
+	} else if (verbId == 0x1B0003 && _vm->causeIsDeclared(0x10003, 0x1B0008, 0, objectId)) {
+		outSceneId = 0x10003;
+		outVerbId = verbId;
+		outObjectId2 = 0;
+		outObjectId = objectId;
+		success = true;
+	} else if (_vm->causeIsDeclared(0x10003, verbId, objectId2, 0x40001)) {
+		outSceneId = 0x10003;
+		outVerbId = verbId;
+		outObjectId2 = objectId2;
+		outObjectId = 0x40001;
+		success = true;
+	} else if (verbId == 0x1B0003 && _vm->causeIsDeclared(0x10003, 0x1B0008, 0, 0x40001)) {
+		outSceneId = 0x10003;
+		outVerbId = verbId;
+		outObjectId2 = 0;
+		outObjectId = 0x40001;
+		success = true;
+	}
+	return success;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 22fdd12..0ee5fae 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -108,6 +108,7 @@ public:
 
 	// Shooter
 	ShooterStatus _shooterStatus[2];
+	uint _shooterObjectIdIndex;
 
 	// Special code interface functions
 	void spcInitCursor(OpCall &opCall);
@@ -150,7 +151,7 @@ protected:
 		Item10 *item10, uint32 progResKeywordId);
 	bool findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
 	void cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime);
-	void cursorControlRoutine2(Control *cursorControl, uint32 deltaTime);
+	void cursorCrosshairControlRoutine(Control *cursorControl, uint32 deltaTime);
 	bool testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId);
 	bool getCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
 		uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId);
@@ -160,6 +161,9 @@ protected:
 	// Salad
 	void initSalad();
 	void addSalad(uint32 sequenceId);
+	// Shooter
+	bool getShooterCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
+		uint32 &outSceneId, uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId);
 };
 
 } // End of namespace Illusions


Commit: 869d342e9f110374683002d405803d01d365119b
    https://github.com/scummvm/scummvm/commit/869d342e9f110374683002d405803d01d365119b
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement getOverlappedObjectAccurate and related functions

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/screen.cpp
    engines/illusions/screen.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 33610ca..a273b52 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -945,6 +945,12 @@ void Control::fillActor(byte color) {
 	_actor->_flags |= 0x4000;
 }
 
+bool Control::isPixelCollision(Common::Point &pt) {
+	Frame *frame = &(*_actor->_frames)[_actor->_frameIndex - 1];
+	return _vm->_screen->isSpritePixelSolid16(pt, _position, _actor->_position,
+		_actor->_surfInfo, _actor->_scale, frame->_flags, frame->_compressedPixels);
+}
+
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 	stopActor();
 
@@ -1311,6 +1317,41 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 	return foundControl != 0;
 }
 
+bool Controls::getOverlappedObjectAccurate(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority) {
+	Control *foundControl = 0;
+	uint32 foundPriority = 0;
+	uint32 minPriorityExt = _vm->getPriorityFromBase(minPriority);
+
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *testControl = *it;
+		if (testControl != control && testControl->_pauseCtr == 0 &&
+			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
+			(!testControl->_actor || (testControl->_actor->_flags & 1))) {
+			Common::Rect collisionRect;
+			testControl->getCollisionRectAccurate(collisionRect);
+			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
+				(!testControl->_actor || testControl->isPixelCollision(pt))) {
+				uint32 testPriority = testControl->getOverlapPriority();
+				if ((!foundControl || foundPriority < testPriority) &&
+					testPriority >= minPriorityExt) {
+					foundControl = testControl;
+					foundPriority = testPriority;
+				}
+			}
+		}
+	}
+
+	if (foundControl) {
+		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & 0x40)) {
+			uint32 parentObjectId = foundControl->getSubActorParent();
+			foundControl = _vm->_dict->getObjectControl(parentObjectId);
+		}
+		*outOverlappedControl = foundControl;
+	}
+
+	return foundControl != 0;
+}
+
 bool Controls::getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl) {
 	Control *foundControl = 0;
 	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index add7519..d82ca02 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -204,6 +204,7 @@ public:
 	void getActorFrameDimensions(WidthHeight &dimensions);
 	void drawActorRect(const Common::Rect r, byte color);
 	void fillActor(byte color);
+	bool isPixelCollision(Common::Point &pt);
 public:
 	IllusionsEngine *_vm;
 	uint _flags;
@@ -245,6 +246,7 @@ public:
 	void pauseControlsBySceneId(uint32 sceneId);
 	void unpauseControlsBySceneId(uint32 sceneId);
 	bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
+	bool getOverlappedObjectAccurate(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
 	bool getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl);
 	bool getOverlappedWalkObject(Control *control, Common::Point pt, Control **outOverlappedControl);
 	void destroyControl(Control *control);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 9278a41..497bc56 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -489,10 +489,10 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 		Control *overlappedControl = 0;
 		
 		if (cursorData._flags & 1) {
-			foundOverlapped = 0;
+			foundOverlapped = false;
 		} else if (_vm->getCurrentScene() == 0x1000D) {
-			/* TODO foundOverlapped = artcntrlGetOverlappedObjectAccurate(cursorControl, cursorPos,
-			&overlappedControl, cursorData._item10._field58);*/
+			foundOverlapped = _vm->_controls->getOverlappedObjectAccurate(cursorControl, cursorPos,
+				&overlappedControl, cursorData._item10._field58);
 		} else {
 			foundOverlapped = _vm->_controls->getOverlappedObject(cursorControl, cursorPos,
 				&overlappedControl, cursorData._item10._field58);
@@ -660,17 +660,15 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 
 	}
 
-	Common::Point cursorPos = getBackgroundCursorPos(cursorPos);
+	Common::Point cursorPos = getBackgroundCursorPos(screenCursorPos);
 	bool foundOverlapped = false;
 	Control *overlappedControl = 0;
 
 	if (cursorData._flags & 1)
 		foundOverlapped = false;
     else {
-		/* TODO Implement getOverlappedObjectAccurate
 		foundOverlapped = _vm->_controls->getOverlappedObjectAccurate(cursorControl, cursorPos,
 			&overlappedControl, cursorData._item10._field58);
-		*/
 	}
 
 	if (foundOverlapped) {
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index a4a3d27..52627c8 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -877,6 +877,62 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co
 
 }
 
+bool Screen::isSpritePixelSolid16(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+	const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) {
+
+	int ptX = scale * drawPosition.x / 100 + testPt.x - drawOffset.x;
+	int ptY = scale * drawPosition.y / 100 + testPt.y - drawOffset.y;
+
+	if (flags & 1) {
+		const int scaledWidth = scale * surfInfo._dimensions._width / 100;
+		ptX += 2 * (scaledWidth - scaledWidth / 2 - ptX);
+	}
+
+	if (flags & 2) {
+		const int scaledHeight = scale * surfInfo._dimensions._height / 100;
+		ptY += 2 * (scaledHeight - scaledHeight / 2 - ptY);
+	}
+
+	const int pixelLookX = 100 * ptX / scale;
+	const int pixelLookY = 100 * ptY / scale;
+	const int lookOffset = pixelLookX + surfInfo._dimensions._width * pixelLookY;
+	const int dstSize = surfInfo._dimensions._width * surfInfo._dimensions._height;
+
+	if (pixelLookX < 0 || pixelLookX >= surfInfo._dimensions._width ||
+		pixelLookY < 0 || pixelLookY >= surfInfo._dimensions._height ||
+		lookOffset < 0 || lookOffset >= dstSize)
+		return false;
+
+	byte *src = compressedPixels;
+	int processedSize = 0;
+
+	while (processedSize < dstSize) {
+		int16 op = READ_LE_UINT16(src);
+		src += 2;
+		if (op & 0x8000) {
+			int runCount = (op & 0x7FFF) + 1;
+			uint16 runColor = READ_LE_UINT16(src);
+			src += 2;
+			while (runCount--) {
+				if (processedSize == lookOffset)
+					return runColor != _colorKey1;
+				++processedSize;
+			}
+		} else {
+			int copyCount = op + 1;
+			while (copyCount--) {
+				uint16 color = READ_LE_UINT16(src);
+				src += 2;
+				if (processedSize == lookOffset)
+					return color != _colorKey1;
+				++processedSize;
+			}
+		}
+	}
+
+	return false;
+}
+
 uint16 Screen::convertFontColor(byte color) {
 	if (color) {
 		byte r, g, b;
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 6c3e83b..e22e9b1 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -181,6 +181,9 @@ public:
 	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
 	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
 	
+	bool isSpritePixelSolid16(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels);
+
 	uint16 convertFontColor(byte color);
 };
 


Commit: 6ca6fb605d96e0088f4883879e790515d40a4245
    https://github.com/scummvm/scummvm/commit/6ca6fb605d96e0088f4883879e790515d40a4245
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement more special opcodes and finish putBackInventoryItem

Changed paths:
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 064be4e..fbe10a5 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -78,6 +78,17 @@ void InventoryBag::removeInventoryItem(InventoryItem *inventoryItem) {
 			(*it)->_inventoryItem = 0;
 }
 
+bool InventoryBag::hasInventoryItem(uint32 objectId) {
+	for (InventorySlotsIterator it = _inventorySlots.begin();
+		it != _inventorySlots.end(); ++it) {
+		InventorySlot *inventorySlot = *it;
+		InventoryItem *inventoryItem = inventorySlot->_inventoryItem;
+		if (inventoryItem && inventoryItem->_objectId == objectId)
+			return true;
+	}
+	return false;
+}
+
 void InventoryBag::buildItems() {
 	for (InventorySlotsIterator it = _inventorySlots.begin();
 		it != _inventorySlots.end(); ++it) {
@@ -333,11 +344,13 @@ void BbdouInventory::putBackInventoryItem(uint32 objectId, Common::Point cursorP
 	if (!flag && !inventoryItem->_assigned)
 		return;
 	for (uint i = 0; i < _inventoryBags.size(); ++i) {
-		if (_inventoryBags[i]->_sceneId == _activeInventorySceneId) {
-			InventorySlot *inventorySlot = _inventoryBags[i]->findClosestSlot(cursorPosition, _index);
-			_inventoryBags[i]->addInventoryItem(inventoryItem, inventorySlot);
+		InventoryBag *inventoryBag = _inventoryBags[i];
+		if (inventoryBag->_sceneId == _activeInventorySceneId) {
+			InventorySlot *inventorySlot = inventoryBag->findClosestSlot(cursorPosition, _index);
+			inventoryBag->addInventoryItem(inventoryItem, inventorySlot);
 		} else {
-			debug("putBackInventoryItem OTHER STUFF TODO");
+			if (!inventoryBag->hasInventoryItem(objectId))
+				inventoryBag->addInventoryItem(inventoryItem, 0);
 		}
 	}
 	refresh();
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index 2784347..2a275d4 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -57,6 +57,7 @@ public:
 	void registerInventorySlot(uint32 namedPointId);
 	bool addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot);
 	void removeInventoryItem(InventoryItem *inventoryItem);
+	bool hasInventoryItem(uint32 objectId);
 	void buildItems();
 	InventorySlot *getInventorySlot(uint32 objectId);
 	InventorySlot *findClosestSlot(Common::Point putPos, int index);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 497bc56..f230a7c 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -135,6 +135,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160013, spcInitBubble);
 	SPECIAL(0x00160014, spcSetupBubble);
 	SPECIAL(0x00160015, spcSetObjectInteractMode);
+	SPECIAL(0x00160017, spcInitInventory);
 	SPECIAL(0x00160019, spcRegisterInventoryBag);
 	SPECIAL(0x0016001A, spcRegisterInventorySlot);
 	SPECIAL(0x0016001B, spcRegisterInventoryItem);
@@ -143,8 +144,10 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001E, spcRemoveInventoryItem);
 	SPECIAL(0x0016001F, spcHasInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
+	SPECIAL(0x00160027, spcInitConversation);
 	SPECIAL(0x00160030, spcResetCursor);
 	SPECIAL(0x00160032, spcSetCursorField90);
+	SPECIAL(0x00160036, spcInitMenu);
 	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
 	SPECIAL(0x00160038, spcInitRadarMicrophone);
 	SPECIAL(0x0016003A, spcSaladCtl);
@@ -243,6 +246,11 @@ void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void BbdouSpecialCode::spcInitInventory(OpCall &opCall) {
+	// Called but not used in the reimplementation since the
+	// inventory is initialized in this class' constructor
+}
+
 void BbdouSpecialCode::spcRegisterInventoryBag(OpCall &opCall) {
 	ARG_UINT32(sceneId);
 	_inventory->registerInventoryBag(sceneId);
@@ -282,6 +290,10 @@ void BbdouSpecialCode::spcCloseInventory(OpCall &opCall) {
 	_inventory->close();
 }
 
+void BbdouSpecialCode::spcInitConversation(OpCall &opCall) {
+	// Conversations seem unused but this is still called
+}
+
 void BbdouSpecialCode::spcResetCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_cursor->reset(objectId);
@@ -295,6 +307,10 @@ void BbdouSpecialCode::spcSetCursorField90(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void BbdouSpecialCode::spcInitMenu(OpCall &opCall) {
+	// Called but not used in the reimplementation
+}
+
 void BbdouSpecialCode::spcIsCursorHoldingObjectId(OpCall &opCall) {
 	ARG_UINT32(cursorObjectId);
 	ARG_UINT32(objectId);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 0ee5fae..d117cd7 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -121,6 +121,7 @@ public:
 	void spcInitBubble(OpCall &opCall);
 	void spcSetupBubble(OpCall &opCall);
 	void spcSetObjectInteractMode(OpCall &opCall);
+	void spcInitInventory(OpCall &opCall);
 	void spcRegisterInventoryBag(OpCall &opCall);
 	void spcRegisterInventorySlot(OpCall &opCall);
 	void spcRegisterInventoryItem(OpCall &opCall);
@@ -129,8 +130,10 @@ public:
 	void spcRemoveInventoryItem(OpCall &opCall);
 	void spcHasInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
+	void spcInitConversation(OpCall &opCall);
 	void spcResetCursor(OpCall &opCall);
 	void spcSetCursorField90(OpCall &opCall);
+	void spcInitMenu(OpCall &opCall);
 	void spcIsCursorHoldingObjectId(OpCall &opCall);
 	void spcInitRadarMicrophone(OpCall &opCall);
 	void spcSaladCtl(OpCall &opCall);


Commit: 0589588b7bb117e5a6aa29f66b12507430a9054c
    https://github.com/scummvm/scummvm/commit/0589588b7bb117e5a6aa29f66b12507430a9054c
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement credits code and adjust existing code

Changed paths:
  A engines/illusions/bbdou/bbdou_credits.cpp
  A engines/illusions/bbdou/bbdou_credits.h
  A engines/illusions/bbdou/bbdou_credits_staticdata.cpp
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/textdrawer.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index a273b52..591ad18 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -940,8 +940,7 @@ void Control::drawActorRect(const Common::Rect r, byte color) {
 }
 
 void Control::fillActor(byte color) {
-	Common::Rect r = Common::Rect(_actor->_surface->w, _actor->_surface->h);
-	_actor->_surface->fillRect(r, color);
+	_vm->_screen->fillSurface(_actor->_surface, color);
 	_actor->_flags |= 0x4000;
 }
 
diff --git a/engines/illusions/bbdou/bbdou_credits.cpp b/engines/illusions/bbdou/bbdou_credits.cpp
new file mode 100644
index 0000000..3b65129
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_credits.cpp
@@ -0,0 +1,246 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_credits.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/textdrawer.h"
+#include "illusions/time.h"
+#include "illusions/resources/scriptresource.h"
+
+namespace Illusions {
+
+BbdouCredits::BbdouCredits(IllusionsEngine_BBDOU *vm)
+	: _vm(vm) {
+}
+
+BbdouCredits::~BbdouCredits() {
+}
+
+void BbdouCredits::start(uint32 endSignalPropertyId, float speedModifier) {
+	_endSignalPropertyId = endSignalPropertyId;
+	_currFontId = 0x120004;
+	_currLineIndex = 1;
+	_split = false;
+	// convertTextData();
+	initCreditsItems();
+	createCreditsThread(speedModifier);
+}
+
+void BbdouCredits::stop() {
+	freeCreditsItems();
+}
+
+void BbdouCredits::drawNextLine() {
+	uint leftIndex, rightIndex;
+	
+	if (!readNextLine(leftIndex, rightIndex)) {
+		_vm->_scriptResource->_properties.set(_endSignalPropertyId, true);
+		return;
+	}
+
+	if (leftIndex) {
+		const char *leftText = getText(leftIndex);
+		if (leftText && strlen(leftText) != 0) {
+			uint32 objectId = getNextFreeObjectId();
+			int alignment = rightIndex ? 1 : 2;
+			drawTextToControl(objectId, leftText, alignment);
+		}
+	}
+
+	if (rightIndex) {
+		const char *rightText = getText(rightIndex);
+		if (rightText && strlen(rightText) != 0) {
+			uint32 objectId = getNextFreeObjectId();
+			drawTextToControl(objectId, rightText, 4);
+		}
+	}
+
+}
+
+void charToWChar(const char *text, uint16 *wtext, uint size) {
+	while (*text != 0 && size > 1) {
+		*wtext++ = (byte)*text++;
+		/*		
+		byte c = (byte)*text++;
+		if (c > 127) c = 32;
+		*wtext = c;
+		debug("%04X", *wtext);		
+		++wtext;
+		*/		
+		--size;
+	}
+	*wtext++ = 0;
+}
+
+void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint alignment) {
+	uint16 wtext[128];
+	charToWChar(text, wtext, ARRAYSIZE(wtext));
+
+	// TODO Extract to Actor class
+    Control *control = _vm->getObjectControl(objectId);
+	FontResource *font = _vm->_dict->findFont(_currFontId); 
+	TextDrawer textDrawer;
+	WidthHeight dimensions;
+	uint16 *outText;
+	control->getActorFrameDimensions(dimensions);
+	control->fillActor(0);
+	textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), alignment, outText);
+	textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
+	control->_actor->_flags |= 0x4000;
+
+}
+
+bool BbdouCredits::readNextLine(uint &leftIndex, uint &rightIndex) {
+	bool done = false;
+	int textLines = 0;
+	leftIndex = 0;
+	rightIndex = 0;
+	
+	do {
+		uint lineIndex = _currLineIndex++;
+		const char *text = getText(lineIndex);
+		if (text[0] == '@') {
+			const char *command = text + 1;
+			if (!strcmp(command, "end"))
+				done = true;
+			if (!strcmp(command, "bold"))
+				_currFontId = 0x120005;
+			else if (!strcmp(command, "normal"))
+				_currFontId = 0x120004;
+			else if (!strcmp(command, "center"))
+				_split = false;
+			else if (!strcmp(command, "split"))
+				_split = true;
+			else
+				done = true;
+		} else {
+			++textLines;
+			if (!_split) {
+				leftIndex = lineIndex;
+				done = true;
+			} else if (textLines > 1) {
+				rightIndex = lineIndex;
+				done = true;
+			} else {
+				leftIndex = lineIndex;
+			}
+		}
+	} while (!done);
+	
+	return textLines > 0;
+}
+
+void BbdouCredits::initCreditsItems() {
+	for (uint i = 0; i < kCreditsItemsCount; ++i) {
+		uint32 objectId = _vm->_controls->newTempObjectId();
+		_vm->_controls->placeActor(0x00050188, Common::Point(320, 480), 0x00060BE1, objectId, 0);
+		Control *control = _vm->_dict->getObjectControl(objectId);
+		control->startSequenceActor(0x60BE2, 2, 0);
+		_items[i].isUsed = false;
+		_items[i].objectId = objectId;
+	}
+}
+
+void BbdouCredits::freeCreditsItems() {
+	for (uint i = 0; i < kCreditsItemsCount; ++i) {
+		Control *control = _vm->_dict->getObjectControl(_items[i].objectId);
+		control->disappearActor();
+	}
+}
+
+uint32 BbdouCredits::getNextFreeObjectId() {
+	for (uint i = 0; i < kCreditsItemsCount; ++i) {
+		if (!_items[i].isUsed) {
+			_items[i].isUsed = true;
+			return _items[i].objectId;
+		}
+	}
+	return 0;
+}
+
+void BbdouCredits::removeText(uint32 objectId) {
+	for (uint i = 0; i < kCreditsItemsCount; ++i) {
+		if (_items[i].objectId == objectId) {
+			_items[i].isUsed = false;
+			resetObjectPos(objectId);
+		}
+	}
+}
+
+void BbdouCredits::resetObjectPos(uint32 objectId) {
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	control->setActorPosition(Common::Point(320, 480));
+}
+
+void BbdouCredits::createCreditsThread(float speedModifier) {
+	uint32 tempThreadId = _vm->newTempThreadId();
+	CreditsThread *creditsThread = new CreditsThread(_vm, this, tempThreadId, speedModifier);
+	_vm->_threads->startThread(creditsThread);
+}
+
+void BbdouCredits::updateTexts(int yIncr) {
+	for (uint i = 0; i < kCreditsItemsCount; ++i) {
+		if (_items[i].isUsed) {
+			Control *control = _vm->_dict->getObjectControl(_items[i].objectId);
+			Common::Point pt = control->getActorPosition();
+			pt.y += yIncr;
+			control->setActorPosition(pt);
+			if (pt.y <= 0)
+				removeText(_items[i].objectId);
+		}
+	}
+}
+
+// CreditsThread
+
+CreditsThread::CreditsThread(IllusionsEngine_BBDOU *vm, BbdouCredits *credits, uint32 threadId, float speedModifier)
+	: Thread(vm, threadId, 0, 0), _speedModifier(speedModifier), _lastFraction(0.0), _credits(credits) {
+	_type = kTTSpecialThread;
+	_lastUpdateTime = getCurrentTime();
+}
+
+int CreditsThread::onUpdate() {
+	uint32 currTime = getCurrentTime();
+	float fltDelta = (currTime - _lastUpdateTime) * _speedModifier + _lastFraction;
+	int delta = (int)fltDelta;
+	_lastFraction = fltDelta - delta;
+	if (delta != 0)
+		_credits->updateTexts(-delta);
+	_lastUpdateTime = currTime;
+	return 2;
+}
+
+void CreditsThread::onNotify() {
+	_lastUpdateTime = getCurrentTime();
+}
+
+void CreditsThread::onResume() {
+	onNotify();
+}
+
+void CreditsThread::onTerminated() {
+	_credits->stop();
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_credits.h b/engines/illusions/bbdou/bbdou_credits.h
new file mode 100644
index 0000000..ebf4e5f
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_credits.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_CREDITS_H
+#define ILLUSIONS_BBDOU_BBDOU_CREDITS_H
+
+#include "illusions/specialcode.h"
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+class BbdouSpecialCode;
+class Control;
+
+struct CreditsItem {
+	bool isUsed;
+	uint32 objectId;
+};
+
+const uint kCreditsItemsCount = 64;
+
+class BbdouCredits {
+public:
+	BbdouCredits(IllusionsEngine_BBDOU *vm);
+	~BbdouCredits();
+	void start(uint32 endSignalPropertyId, float speedModifier);
+	void stop();
+	void drawNextLine();
+	void updateTexts(int yIncr);
+protected:
+	IllusionsEngine_BBDOU *_vm;
+	uint32 _endSignalPropertyId;
+	uint32 _currFontId;
+	uint _currLineIndex;
+	bool _split;
+	CreditsItem _items[kCreditsItemsCount];
+	const char *getText(uint index);
+	void drawTextToControl(uint32 objectId, const char *text, uint alignment);
+	bool readNextLine(uint &leftIndex, uint &rightIndex);
+	void initCreditsItems();
+	void freeCreditsItems();
+	uint32 getNextFreeObjectId();
+	void removeText(uint32 objectId);
+	void resetObjectPos(uint32 objectId);
+	void createCreditsThread(float speedModifier);
+};
+
+class CreditsThread : public Thread {
+public:
+	CreditsThread(IllusionsEngine_BBDOU *vm, BbdouCredits *credits, uint32 threadId, float speedModifier);
+	virtual int onUpdate();
+	virtual void onNotify();
+	virtual void onResume();
+	virtual void onTerminated();
+public:
+	BbdouCredits *_credits;
+	float _speedModifier;
+	float _lastFraction;
+	uint32 _lastUpdateTime;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_CREDITS_H
diff --git a/engines/illusions/bbdou/bbdou_credits_staticdata.cpp b/engines/illusions/bbdou/bbdou_credits_staticdata.cpp
new file mode 100644
index 0000000..a00a868
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_credits_staticdata.cpp
@@ -0,0 +1,299 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/bbdou_credits.h"
+
+namespace Illusions {
+
+const char *kCreditsText[] = {
+	"@center",
+	"@normal",
+	"Directed by",
+	"@bold",
+	"Darren Bartlett",
+	"@normal",
+	"",
+	"Produced by",
+	"@bold",
+	"James C\xF6liz, Jr.",
+	"@normal",
+	"",
+	"Developed by",
+	"@bold",
+	"The Illusions Gaming Company",
+	"@normal",
+	"",
+	"@split",
+	"Creative Director",
+	"Darren Bartlett",
+	"Designer",
+	"Ryan Modjeski",
+	"Associate Designer",
+	"David Sirlin",
+	"",
+	"",
+	"Technical Director",
+	"James C\xF6liz, Jr.",
+	"Lead Programmer",
+	"Bill Fowler",
+	"Programmer",
+	"Chuck Woo",
+	"",
+	"",
+	"3D Artist",
+	"Eric Chyn",
+	"",
+	"Bill Eral",
+	"Production Artist",
+	"Jim Eral",
+	"Asst. Production Artist",
+	"Eli Remus",
+	"@center",
+	"",
+	"2D Animation by",
+	"@bold",
+	"LA West Productions",
+	"@normal",
+	"",
+	"@split",
+	"Director of Animation",
+	"Ivan Tomicic",
+	"Production Manager",
+	"Susan McGirr",
+	"Studio Supervisor",
+	"Danijel Tomicic",
+	"",
+	"",
+	"Lead Animator",
+	"Dario Pustaj",
+	"",
+	"Ivica Horvat",
+	"Animator",
+	"Kristijan Dulic",
+	"",
+	"Elvis Popovic",
+	"In-Between",
+	"Maja Surijak",
+	"",
+	"Zlatko Zlatunic",
+	"",
+	"",
+	"Lead Ink & Paint",
+	"Sasa Zec",
+	"Ink & Paint",
+	"Darko Dukaric",
+	"",
+	"Marcela Kumparic",
+	"",
+	"Vlado Lencur",
+	"",
+	"Jura Milinkovic",
+	"",
+	"Bernard Ojdanic",
+	"",
+	"Peggy Skrlec",
+	"@center",
+	"",
+	"3D Backgrounds by",
+	"@bold",
+	"LA West Productions",
+	"@normal",
+	"",
+	"@split",
+	"3D Artist",
+	"Daniela Tomicic",
+	"",
+	"Diana-Barbara Stepanic",
+	"@center",
+	"",
+	"2D Animation by",
+	"@bold",
+	"Six Foot Two Productions",
+	"@normal",
+	"",
+	"@split",
+	"Director of Animation",
+	"Tom Arndt",
+	"Producer",
+	"Suzanne D. Atherly",
+	"",
+	"",
+	"Character Animator",
+	"Robbin Atherly",
+	"",
+	"Alan Lau",
+	"",
+	"David Ball",
+	"",
+	"Jeff Nevins",
+	"",
+	"",
+	"Ink & Paint",
+	"Steve Bellin",
+	"",
+	"Corrine Wong",
+	"",
+	"Jeff Nevins",
+	"@center",
+	"",
+	"Written by",
+	"@bold",
+	"Bo Weinberg",
+	"@normal",
+	"",
+	"Principal Voice by",
+	"@bold",
+	"Mike Judge",
+	"@normal",
+	"",
+	"Secondary Voice Recorded at",
+	"@bold",
+	"Private Island Trax",
+	"@normal",
+	"",
+	"Secondary Voices by",
+	"Dean Julian",
+	"Mia Altieri",
+	"Nicole Schallig",
+	"Rick Calvert",
+	"John Campana",
+	"Alex Mebane",
+	"Denise Askew",
+	"Michael Jamal",
+	"",
+	"Studio Engineered by",
+	"Mark V",
+	"",
+	"Sound and Music by",
+	"@bold",
+	"Tommy Tallarico Studios",
+	"@normal",
+	"",
+	"@split",
+	"Sound Designer",
+	"Joey Kuras",
+	"Foley",
+	"Scott Barrett",
+	"@center",
+	"",
+	"Illusions is represented by",
+	"@bold",
+	"Interactive Studio Management",
+	"@normal",
+	"",
+	"Published by",
+	"@bold",
+	"GT Interactive Software",
+	"@normal",
+	"",
+	"@split",
+	"Producer",
+	"Nathan Rose",
+	"Assistant Producer",
+	"Jamal Jennings",
+	"Group Product Manager",
+	"Evan Stein",
+	"Product Manager",
+	"Robert J. Ricci",
+	"Senior Communications Manager",
+	"Alan Lewis",
+	"Director, Product Development Services"
+	"Mary Steer",
+	"Director, Creative Services",
+	"Leslie Mills",
+	"Creative Director",
+	"Vic Merritt",
+	"Art/Traffic Manager",
+	"Liz Fierro",
+	"Manual Editor",
+	"Peter Witcher",
+	"",
+	"",
+	"@center",
+	"",
+	"Licensed by",
+	"@bold",
+	"MTV Networks",
+	"@normal",
+	"",
+	"@split",
+	"MTV Executive Producer",
+	"Allie Eberhardt",
+	"MTV Producer",
+	"Tony Calandra",
+	"MTV Creative Consultants",
+	"Kristofor Brown",
+	"",
+	"David Felton",
+	"",
+	"Mike Judge",
+	"",
+	"Nick Litwinko",
+	"MTV Standards and Practices",
+	"Dr. Thomas Shea",
+	"MTV Legal Affairs",
+	"Beth Matthews",
+	"@center",
+	"",
+	"MTV would like to thank",
+	"Mary Frances Budig",
+	"George Eichen",
+	"Matt Farber",
+	"Rick Holzman",
+	"Jessica Jarrett",
+	"Mike Judge",
+	"Judith McGrath",
+	"David Milch",
+	"Abby Terkuhle",
+	"Van Toffler",
+	"Paige Wolfson",
+	"Marcia Zellers",
+	"",
+	"@bold",
+	"Special Thanks",
+	"@normal",
+	"Clyde Grossman",
+	"Hiromi Nobata",
+	"John Durentas",
+	"Jeff Teachworth",
+	"John Lawrence",
+	"Bill Hendrickson",
+	"Fred Schiller",
+	"Sam Fletcher",
+	"Elizabeth, Stephanie & Hannah",
+	"Sheila Mendoza",
+	"Yukari Yamano",
+	"Hang Yim, King Yip & Wayne",
+	"Li-Ming, Der-Lin & Fansy",
+	"Bobbi Eral",
+	"Miss Melissa",
+	"Yasmin, Aparna & Jenny",
+	"Tony the Cat",
+	"Sammy the Cat",
+	"@end"
+};
+
+const char *BbdouCredits::getText(uint index) {
+	return kCreditsText[index - 1];
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index f230a7c..437c902 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -24,6 +24,7 @@
 #include "illusions/bbdou/bbdou_specialcode.h"
 #include "illusions/bbdou/bbdou_bubble.h"
 #include "illusions/bbdou/bbdou_inventory.h"
+#include "illusions/bbdou/bbdou_credits.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -107,7 +108,7 @@ void RadarMicrophoneThread::initZones() {
 // BbdouSpecialCode
 
 BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine_BBDOU *vm)
-	: _vm(vm) {
+	: _vm(vm), _credits(0) {
 	_bubble = new BbdouBubble(_vm, this);
 	_cursor = new BbdouCursor(_vm, this);
 	_inventory = new BbdouInventory(_vm, this);
@@ -150,6 +151,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160036, spcInitMenu);
 	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
 	SPECIAL(0x00160038, spcInitRadarMicrophone);
+	SPECIAL(0x00160039, spcCreditsCtl);
 	SPECIAL(0x0016003A, spcSaladCtl);
 	SPECIAL(0x0016003B, spcRunCause);
 }
@@ -333,6 +335,27 @@ void BbdouSpecialCode::spcInitRadarMicrophone(OpCall &opCall) {
 	_vm->_threads->startThread(radarMicrophoneThread);
 }
 
+void BbdouSpecialCode::spcCreditsCtl(OpCall &opCall) {
+	ARG_UINT32(cmd);
+	switch (cmd) {
+	case 1:
+		{
+			ARG_UINT32(endSignalPropertyId);
+			_credits = new BbdouCredits(_vm);
+			_credits->start(endSignalPropertyId, 0.5);
+		}
+		break;
+	case 2:
+		_credits->drawNextLine();
+		break;
+	case 3:
+		_credits->stop();
+		delete _credits;
+	default:
+		break;
+	}
+}
+
 void BbdouSpecialCode::spcSaladCtl(OpCall &opCall) {
 	ARG_UINT32(cmd);
 	ARG_UINT32(sequenceId);
@@ -352,7 +375,7 @@ void BbdouSpecialCode::spcRunCause(OpCall &opCall) {
 	ARG_UINT32(objectId1);
 	ARG_UINT32(objectId2);
 	Control *cursorControl = _vm->getObjectControl(cursorObjectId);
-	debug("runCause(%08X, %08X, %08X)", verbId, objectId1, objectId2);
+	debug(0, "runCause(%08X, %08X, %08X)", verbId, objectId1, objectId2);
 	runCause(cursorControl, _cursor->_data, verbId, objectId1, objectId2, 0);
 }
 
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index d117cd7..973d4b4 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -31,6 +31,7 @@ namespace Illusions {
 
 class IllusionsEngine_BBDOU;
 class BbdouBubble;
+class BbdouCredits;
 class BbdouCursor;
 class BbdouInventory;
 struct CursorData;
@@ -101,6 +102,8 @@ public:
 	BbdouCursor *_cursor;
 	BbdouBubble *_bubble;
 	BbdouInventory *_inventory;
+	
+	BbdouCredits *_credits;
 
 	// Salad
 	uint _saladCount;
@@ -136,6 +139,7 @@ public:
 	void spcInitMenu(OpCall &opCall);
 	void spcIsCursorHoldingObjectId(OpCall &opCall);
 	void spcInitRadarMicrophone(OpCall &opCall);
+	void spcCreditsCtl(OpCall &opCall);
 	void spcSaladCtl(OpCall &opCall);
 	void spcRunCause(OpCall &opCall);
 
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 6004f44..1abcb69 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -295,7 +295,8 @@ void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCal
 //uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 //uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
-uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
+//uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
+uint32 dsceneId = 0x0001007D, dthreadId = 0x000203B9;
 
 void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index ad61eee..7cbbd7f 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -4,6 +4,8 @@ MODULE_OBJS := \
 	actor.o \
 	bbdou/bbdou_bubble.o \
 	bbdou/bbdou_cursor.o \
+	bbdou/bbdou_credits.o \
+	bbdou/bbdou_credits_staticdata.o \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
 	bbdou/bbdou_triggerfunctions.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 52627c8..d8dd004 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -482,6 +482,30 @@ void Screen::drawText(FontResource *font, Graphics::Surface *surface, int16 x, i
 	}
 }
 
+void Screen::fillSurface(Graphics::Surface *surface, byte color) {
+	Common::Rect r = Common::Rect(surface->w, surface->h);
+	switch (_backSurface->format.bytesPerPixel) {
+	case 1:
+		surface->fillRect(r, color);
+		break;
+	case 2:
+		surface->fillRect(r, convertColor(color));
+		break;
+	default:
+		break;
+	}
+}
+
+uint16 Screen::convertColor(byte color) {
+	if (color == 0)
+		return _colorKey1;
+	if (color == 20)
+		return g_system->getScreenFormat().RGBToColor(255, 255, 255);
+	if (color == 80)
+		return g_system->getScreenFormat().RGBToColor(176, 176, 176);
+	return g_system->getScreenFormat().RGBToColor(16, 16, 16);
+}
+
 void Screen::drawText8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
 	for (uint i = 0; i < count; ++i)
 		x += font->_widthC + drawChar8(font, surface, x, y, *text++);
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index e22e9b1..b801458 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -134,6 +134,8 @@ public:
 	void updateFaderPalette();
 	void setFader(int newValue, int firstIndex, int lastIndex);
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
+	void fillSurface(Graphics::Surface *surface, byte color);
+	uint16 convertColor(byte color);
 	uint16 getColorKey1() const { return _colorKey1; }
 	void setColorKey1(uint16 colorKey) { _colorKey1 = colorKey; }
 	uint16 getColorKey2() const { return _colorKey2; }
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index f1c93c7..2456fe4 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -114,6 +114,8 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 				if (_textFlags & 2) {
 					textPosX = (_dimensions->_width - currLineWidth) / 2;
 					maxLineWidth = _dimensions->_width;
+				} else if (_textFlags & 4) {
+					textPosX = _dimensions->_width - currLineWidth;
 				} else {
 					textPosX = x;
 				}


Commit: 4aed366334c4ca2651eef87734f1c4d1d41a1ba2
    https://github.com/scummvm/scummvm/commit/4aed366334c4ca2651eef87734f1c4d1d41a1ba2
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement BbdouBubble::calcBubbles

Changed paths:
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_bubble.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp


diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index d24e16e..62e7959 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -109,7 +109,7 @@ void BbdouBubble::show() {
 	_prevItem0 = _currItem0;
 	_currItem0 = 0;
 
-	// TODO calcBubbles(_pt1, _pt2);
+	calcBubbles(_pt1, _pt2);
 	
 	Control *control = _vm->_dict->getObjectControl(_prevItem0->_objectId);
 	control->setActorPosition(_pt2);
@@ -183,4 +183,108 @@ uint32 BbdouBubble::addItem(uint positionIndex, uint32 sequenceId) {
 	return 0;
 }
 
+void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
+	const int kSequenceIdsCount = 10;
+	const float kDistanceBetweenPoints = 30.0;
+	static const uint32 kSequenceIds[] = {
+		0x00060042, 0x00060043, 0x00060044, 0x00060045, 0x00060046,
+		0x00060047, 0x00060048, 0x00060049, 0x0006004A, 0x0006004B
+	};
+	static const int kIndexTbl[kSequenceIdsCount] = {4, 0, 8, 2, 6, 5, 1, 9, 3, 7};
+
+	int sequenceCounters[kSequenceIdsCount];
+	bool swapY;
+	int centerX, centerY;
+	float currentAngle, radius;
+
+	for (int i = 0; i < 32; ++i) {
+		Control *control = _vm->_dict->getObjectControl(_objectIds[i]);
+		control->startSequenceActor(0x00060056, 2, 0);
+	}
+
+	for (int i = 0; i < kSequenceIdsCount; ++i)
+		sequenceCounters[i] = 0;
+	
+	if (pt2.y >= pt1.y) {
+		swapY = true;
+		if (pt1.x == pt2.x)
+			pt2.x = pt2.x + 20;
+	} else {
+		swapY = false;
+		if (pt1.y == pt2.y)
+			pt2.y = pt2.y + 20;
+	}
+
+	if (swapY) {
+		centerX = (pt2.x * pt2.x - (pt2.y - pt1.y) * (pt2.y - pt1.y) - pt1.x * pt1.x) / (2 * (pt2.x - pt1.x));
+		centerY = pt2.y;
+		radius = ABS(pt2.x - centerX);
+	} else {
+		centerX = pt2.x;
+		centerY = (pt2.y * pt2.y - (pt2.x - pt1.x) * (pt2.x - pt1.x) - pt1.y * pt1.y) / (2 * (pt2.y - pt1.y));
+		radius = ABS(pt2.y - centerY);
+	}
+
+	const float fullDistance = sqrt((pt2.y - pt1.y) * (pt2.y - pt1.y) + (pt2.x - pt1.x) * (pt2.x - pt1.x));
+	const float arcAngle = 2 * asin(CLIP(0.5 * fullDistance / radius, -1.0, 1.0));
+	const float arcLength = arcAngle * radius;
+	int pointsCount = (int)(arcLength / kDistanceBetweenPoints);
+	float partAngle = ABS(kDistanceBetweenPoints / radius);
+
+	for (int i = 0; i < pointsCount; ++i)
+		++sequenceCounters[kIndexTbl[i % kSequenceIdsCount]];
+
+	if (!swapY) {
+		if (pt2.y < pt1.y) {
+			currentAngle = M_PI * 0.5;
+		} else {
+			currentAngle = M_PI * 1.5;
+			partAngle = -partAngle;
+		}
+		if (pt2.x < pt1.x)
+			partAngle = -partAngle;
+	} else {
+		if (pt2.x <= pt1.x) {
+			currentAngle = M_PI;
+		} else {
+			currentAngle = 0.0;
+			partAngle = -partAngle;
+		}
+		if (pt2.y > pt1.y)
+			partAngle = -partAngle;
+	}
+
+	int index = kSequenceIdsCount - 1;
+	float angleStep = partAngle / (float)pointsCount * 0.5;
+	float angleIncr = (float)(pointsCount / 2) * angleStep + partAngle;
+
+	if (pointsCount > 32)
+		pointsCount = 32;
+
+	for (int i = 0; i < pointsCount; ++i) {
+
+		currentAngle += angleIncr;
+		angleIncr -= angleStep;
+
+		Common::Point newPoint(
+			centerX + _vm->getRandom(8) - 2 + (int)(cos(currentAngle) * radius),
+			centerY + _vm->getRandom(8) - 2 - (int)(sin(currentAngle) * radius));
+
+		Control *control = _vm->_dict->getObjectControl(_objectIds[i]);
+
+		for (; index >= 0; --index) {
+			if (sequenceCounters[index] > 0) {
+				--sequenceCounters[index];
+				control->setActorPosition(newPoint);
+				control->startSequenceActor(kSequenceIds[index], 2, 0);
+				control->appearActor();
+				control->deactivateObject();
+				break;
+			}
+		}
+
+	}
+
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_bubble.h b/engines/illusions/bbdou/bbdou_bubble.h
index fcf5f51..f42ff6e 100644
--- a/engines/illusions/bbdou/bbdou_bubble.h
+++ b/engines/illusions/bbdou/bbdou_bubble.h
@@ -63,6 +63,7 @@ public:
 	void hide();
 	void setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId);
 	uint32 addItem(uint positionIndex, uint32 sequenceId);
+	void calcBubbles(Common::Point &pt1, Common::Point &pt2);
 protected:
 	IllusionsEngine_BBDOU *_vm;
 	BbdouSpecialCode *_bbdou;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 437c902..8bc09ff 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -431,28 +431,28 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 	
 	Common::Rect collisionRect;
 	Control *overlappedControl, *control2, *control3;
-	Common::Point pt1(320, 240), pt2, currPan;
+	Common::Point bubbleSourcePt(320, 240), bubbleDestPt, currPan;
 	
 	overlappedControl = _vm->_dict->getObjectControl(overlappedObjectId);
 	overlappedControl->getCollisionRect(collisionRect);
 
 	currPan = _vm->_camera->getCurrentPan();
-	pt2.x = CLIP((collisionRect.right + collisionRect.left) / 2, currPan.x - 274, currPan.x + 274);
-	pt2.y = CLIP(collisionRect.top - (collisionRect.bottom - collisionRect.top) / 8, currPan.y - 204, currPan.y + 204);
+	bubbleDestPt.x = CLIP((collisionRect.right + collisionRect.left) / 2, currPan.x - 274, currPan.x + 274);
+	bubbleDestPt.y = CLIP(collisionRect.top - (collisionRect.bottom - collisionRect.top) / 8, currPan.y - 204, currPan.y + 204);
 
 	control2 = _vm->_dict->getObjectControl(0x4000F);
 	if (!control2 || (control2->_actor && control2->_actor->_frameIndex == 0))
 		control2 = _vm->_dict->getObjectControl(0x4000E);
 
 	if (control2 && control2->_actor && control2->_actor->_frameIndex) {
-		pt1.x = control2->_actor->_surfInfo._dimensions._width / 2 + pt1.x - control2->_position.x;
-		pt1.y = control2->_actor->_position.y - control2->_position.y;
-		pt1.y = pt1.y >= 500 ? 500 : pt1.y + 32;
-		if (ABS(pt1.x - pt2.x) < ABS(pt1.y - pt2.y) / 2)
-			pt1.y += 80;
+		bubbleSourcePt.x = control2->_actor->_position.x - control2->_position.x + control2->_actor->_surfInfo._dimensions._width / 2;
+		bubbleSourcePt.y = control2->_actor->_position.y - control2->_position.y;
+		bubbleSourcePt.y = bubbleSourcePt.y >= 500 ? 500 : bubbleSourcePt.y + 32;
+		if (ABS(bubbleSourcePt.x - bubbleDestPt.x) < ABS(bubbleSourcePt.y - bubbleDestPt.y) / 2)
+			bubbleSourcePt.y += 80;
 	}
 
-	_bubble->setup(1, pt1, pt2, progResKeywordId);
+	_bubble->setup(1, bubbleSourcePt, bubbleDestPt, progResKeywordId);
 
 	item10->_objectIds[0] = _bubble->addItem(0, 0x6005A);
 	item10->_objectIds[1] = _bubble->addItem(0, 0x6005A);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 1abcb69..d887edd 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -288,7 +288,7 @@ void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCal
 //uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 //uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
-//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
+uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
 //uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
 //uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
 //uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
@@ -296,7 +296,7 @@ void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCal
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 //uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
 //uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
-uint32 dsceneId = 0x0001007D, dthreadId = 0x000203B9;
+//uint32 dsceneId = 0x0001007D, dthreadId = 0x000203B9;
 
 void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);


Commit: 48011948d60f700676629e157de75475d52c8abc
    https://github.com/scummvm/scummvm/commit/48011948d60f700676629e157de75475d52c8abc
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement food/cafeteria minigame

Changed paths:
  A engines/illusions/bbdou/bbdou_foodctl.cpp
  A engines/illusions/bbdou/bbdou_foodctl.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/bbdou/bbdou_foodctl.cpp b/engines/illusions/bbdou/bbdou_foodctl.cpp
new file mode 100644
index 0000000..89c22d3
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_foodctl.cpp
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_foodctl.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/textdrawer.h"
+#include "illusions/time.h"
+#include "illusions/resources/scriptresource.h"
+
+namespace Illusions {
+
+BbdouFoodCtl::BbdouFoodCtl(IllusionsEngine_BBDOU *vm)
+	: _vm(vm) {
+}
+
+BbdouFoodCtl::~BbdouFoodCtl() {
+}
+
+void BbdouFoodCtl::placeFood(uint totalRoundsCount, uint maxRequestedFoodCount) {
+	_totalRoundsCount = totalRoundsCount;
+	_maxRequestedFoodCount = maxRequestedFoodCount;
+	_requestedFoodCount = 0;
+	_requestedFoodIndex = 0;
+	placeActors();
+}
+
+void BbdouFoodCtl::addFood(uint32 propertyId) {
+	_foodPropertyIds[_requestedFoodCount++] = propertyId;
+}
+
+void BbdouFoodCtl::requestFirstFood() {
+	_requestedFoodIndex = 1;
+	_vm->_scriptResource->_properties.set(_foodPropertyIds[0], true);
+}
+
+void BbdouFoodCtl::requestNextFood() {
+	uint32 propertyId = _foodPropertyIds[_requestedFoodIndex++];
+	_vm->_scriptResource->_properties.set(propertyId, true);
+}
+
+void BbdouFoodCtl::nextRound() {
+	--_totalRoundsCount;
+}
+
+bool BbdouFoodCtl::hasReachedRequestedFoodCount() {
+	return _requestedFoodIndex > _requestedFoodCount;
+}
+
+bool BbdouFoodCtl::hasRoundFinished() {
+	return _totalRoundsCount == 0 || _requestedFoodCount > _maxRequestedFoodCount;
+}
+
+void BbdouFoodCtl::serveFood() {
+	uint32 foodSequenceId = getFoodSequenceId();
+	uint32 studentObjectId = getCurrentStudentObjectId();
+	uint32 foodObjectId = _foodItems[_servedFoodCount++].objectId;
+	Control *foodControl = _vm->_dict->getObjectControl(foodObjectId);
+	foodControl->startSequenceActor(foodSequenceId, 2, 0);
+	foodControl->linkToObject(studentObjectId, _servedFoodCount);
+}
+
+void BbdouFoodCtl::resetFood() {
+	for (uint i = 0; i < _servedFoodCount; ++i) {
+		Control *control = _vm->_dict->getObjectControl(_foodItems[i].objectId);
+		control->unlinkObject();
+		_foodItems[i].value = 0;
+	}
+	_servedFoodCount = 0;
+	resetFoodControls();
+}
+
+void BbdouFoodCtl::placeActors() {
+	static const uint32 kFoodSequenceIds[] = {
+		0x00060932, 0x00060933, 0x00060934,
+		0x00060935, 0x00060936, 0x00060937
+	};
+	for (uint i = 0; i < kFoodCount; ++i) {
+		uint32 objectId = _vm->_controls->newTempObjectId();
+		_vm->_controls->placeActor(0x00050119, Common::Point(0, 0), 0x00060931, objectId, 0);
+		Control *control = _vm->_dict->getObjectControl(objectId);
+		control->deactivateObject();
+		control->setPriority(i + 10);
+		control->startSequenceActor(kFoodSequenceIds[(i + 1) % 6], 2, 0);
+		_foodItems[i].objectId = objectId;
+		_foodItems[i].value = 0;
+	}
+	_servedFoodCount = 0;
+	resetFoodControls();
+}
+
+void BbdouFoodCtl::resetFoodControls() {
+	Common::Point pos(-100, 32);
+	for (uint i = 0; i < kFoodCount; ++i) {
+		Control *control = _vm->_dict->getObjectControl(_foodItems[i].objectId);
+		control->setActorPosition(pos);
+		pos.y += 20;
+	}
+}
+
+uint32 BbdouFoodCtl::getFoodSequenceId() {
+	if (_vm->_scriptResource->_properties.get(0x000E014A))
+		return 0x60932;
+	if (_vm->_scriptResource->_properties.get(0x000E014B))
+		return 0x60933;
+	if (_vm->_scriptResource->_properties.get(0x000E014C))
+		return 0x60934;
+	if (_vm->_scriptResource->_properties.get(0x000E014D))
+		return 0x60935;
+	if (_vm->_scriptResource->_properties.get(0x000E014E))
+		return 0x60936;
+	if (_vm->_scriptResource->_properties.get(0x000E014F))
+		return 0x60937;
+	return 0;
+}
+
+uint32 BbdouFoodCtl::getCurrentStudentObjectId() {
+	if (_vm->_scriptResource->_properties.get(0x000E0146))
+		return 0x40077;
+	if (_vm->_scriptResource->_properties.get(0x000E0147))
+		return 0x40255;
+	if (_vm->_scriptResource->_properties.get(0x000E0148))
+		return 0x40256;
+	if (_vm->_scriptResource->_properties.get(0x000E0149))
+		return 0x40257;
+	return 0;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_foodctl.h b/engines/illusions/bbdou/bbdou_foodctl.h
new file mode 100644
index 0000000..5a31777
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_foodctl.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_FOODCTL_H
+#define ILLUSIONS_BBDOU_BBDOU_FOODCTL_H
+
+#include "illusions/specialcode.h"
+#include "illusions/thread.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+
+// TODO Merge counts?
+const uint kFoodMaxPropertyIdsCount = 15;
+const uint kFoodCount = 16;
+
+struct FoodItem {
+	uint32 objectId;
+	int value;
+};
+
+class BbdouFoodCtl {
+public:
+	BbdouFoodCtl(IllusionsEngine_BBDOU *vm);
+	~BbdouFoodCtl();
+	void placeFood(uint totalRoundsCount, uint maxRequestedFoodCount);
+	void addFood(uint32 propertyId);
+	void requestFirstFood();
+	void requestNextFood();
+	void nextRound();
+	bool hasReachedRequestedFoodCount();
+	bool hasRoundFinished();
+	void serveFood();
+	void resetFood();
+protected:
+	IllusionsEngine_BBDOU *_vm;
+	uint _totalRoundsCount, _maxRequestedFoodCount;
+	uint32 _foodPropertyIds[kFoodMaxPropertyIdsCount];
+	uint _requestedFoodCount;
+	uint _requestedFoodIndex;
+	FoodItem _foodItems[kFoodCount];
+	uint _servedFoodCount;
+	void placeActors();
+	void resetFoodControls();
+	uint32 getFoodSequenceId();
+	uint32 getCurrentStudentObjectId();
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_FOODCTL_H
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 8bc09ff..8839e2f8 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -26,6 +26,7 @@
 #include "illusions/bbdou/bbdou_inventory.h"
 #include "illusions/bbdou/bbdou_credits.h"
 #include "illusions/bbdou/bbdou_cursor.h"
+#include "illusions/bbdou/bbdou_foodctl.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
@@ -112,9 +113,11 @@ BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine_BBDOU *vm)
 	_bubble = new BbdouBubble(_vm, this);
 	_cursor = new BbdouCursor(_vm, this);
 	_inventory = new BbdouInventory(_vm, this);
+	_foodCtl = new BbdouFoodCtl(_vm);
 }
 
 BbdouSpecialCode::~BbdouSpecialCode() {
+	delete _foodCtl;
 	delete _inventory;
 	delete _cursor;
 	delete _bubble;
@@ -148,6 +151,8 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160027, spcInitConversation);
 	SPECIAL(0x00160030, spcResetCursor);
 	SPECIAL(0x00160032, spcSetCursorField90);
+	SPECIAL(0x00160034, spcFoodCtl);
+	SPECIAL(0x00160035, spcTestFoodCtl);
 	SPECIAL(0x00160036, spcInitMenu);
 	SPECIAL(0x00160037, spcIsCursorHoldingObjectId);
 	SPECIAL(0x00160038, spcInitRadarMicrophone);
@@ -309,6 +314,56 @@ void BbdouSpecialCode::spcSetCursorField90(OpCall &opCall) {
 	_vm->notifyThreadId(opCall._threadId);
 }
 
+void BbdouSpecialCode::spcFoodCtl(OpCall &opCall) {
+	ARG_UINT32(cmd);
+	switch (cmd) {
+	case 1:
+		{
+			ARG_UINT32(minCount);
+			ARG_UINT32(maxCount);
+			_foodCtl->placeFood(minCount, maxCount);
+		}
+		break;
+	case 2:
+		{
+			ARG_UINT32(propertyId);
+			_foodCtl->addFood(propertyId);
+		}
+		break;
+	case 3:
+		_foodCtl->requestFirstFood();
+		break;
+	case 4:
+		_foodCtl->requestNextFood();
+		break;
+	case 5:
+		_foodCtl->serveFood();
+		break;
+	case 6:
+		_foodCtl->resetFood();
+		break;
+	case 8:
+		_foodCtl->nextRound();
+		break;
+	default:
+		break;
+	}
+}
+
+void BbdouSpecialCode::spcTestFoodCtl(OpCall &opCall) {
+	ARG_UINT32(cmd);
+	switch (cmd) {
+	case 7:
+		_vm->_stack->push(_foodCtl->hasReachedRequestedFoodCount() ? 1 : 0);
+		break;
+	case 9:
+		_vm->_stack->push(_foodCtl->hasRoundFinished() ? 1 : 0);
+		break;
+	default:
+		break;
+	}
+}
+
 void BbdouSpecialCode::spcInitMenu(OpCall &opCall) {
 	// Called but not used in the reimplementation
 }
@@ -705,7 +760,7 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 
 	if (cursorData._flags & 1)
 		foundOverlapped = false;
-    else {
+	else {
 		foundOverlapped = _vm->_controls->getOverlappedObjectAccurate(cursorControl, cursorPos,
 			&overlappedControl, cursorData._item10._field58);
 	}
@@ -818,7 +873,7 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 		}
 
 	} else if (_vm->_input->pollEvent(kEventLeftClick)) {
-	    uint index = (uint)_vm->getRandom(2);
+		uint index = (uint)_vm->getRandom(2);
 		const ShooterAnim &anim = kShooterAnims[index];
 		uint32 objectId = anim.objectId;
 		int gridX = _shooterStatus[index].gridX;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 973d4b4..f483307 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -33,6 +33,7 @@ class IllusionsEngine_BBDOU;
 class BbdouBubble;
 class BbdouCredits;
 class BbdouCursor;
+class BbdouFoodCtl;
 class BbdouInventory;
 struct CursorData;
 struct Item10;
@@ -113,6 +114,8 @@ public:
 	ShooterStatus _shooterStatus[2];
 	uint _shooterObjectIdIndex;
 
+	BbdouFoodCtl *_foodCtl;
+
 	// Special code interface functions
 	void spcInitCursor(OpCall &opCall);
 	void spcEnableCursor(OpCall &opCall);
@@ -136,6 +139,8 @@ public:
 	void spcInitConversation(OpCall &opCall);
 	void spcResetCursor(OpCall &opCall);
 	void spcSetCursorField90(OpCall &opCall);
+	void spcFoodCtl(OpCall &opCall);
+	void spcTestFoodCtl(OpCall &opCall);
 	void spcInitMenu(OpCall &opCall);
 	void spcIsCursorHoldingObjectId(OpCall &opCall);
 	void spcInitRadarMicrophone(OpCall &opCall);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index d887edd..e023fb3 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -288,7 +288,7 @@ void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCal
 //uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 //uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
-uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
+//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
 //uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
 //uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
 //uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
@@ -297,6 +297,7 @@ uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
 //uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
 //uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
 //uint32 dsceneId = 0x0001007D, dthreadId = 0x000203B9;
+uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;
 
 void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 7cbbd7f..8211696 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	bbdou/bbdou_cursor.o \
 	bbdou/bbdou_credits.o \
 	bbdou/bbdou_credits_staticdata.o \
+	bbdou/bbdou_foodctl.o \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
 	bbdou/bbdou_triggerfunctions.o \


Commit: c9b0a8452a3c5ef64da2955ef58412efa32e70c4
    https://github.com/scummvm/scummvm/commit/c9b0a8452a3c5ef64da2955ef58412efa32e70c4
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement remaining special opcodes 160018, 160028, 16002B

Changed paths:
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index fbe10a5..1c4c2c8 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -102,6 +102,14 @@ void InventoryBag::buildItems() {
 	}
 }
 
+void InventoryBag::clear() {
+	for (InventorySlotsIterator it = _inventorySlots.begin();
+		it != _inventorySlots.end(); ++it) {
+		InventorySlot *inventorySlot = *it;
+		inventorySlot->_inventoryItem = 0;
+	}
+}
+
 InventorySlot *InventoryBag::getInventorySlot(uint32 objectId) {
 	for (uint i = 0; i < _inventorySlots.size(); ++i)
 		if (_inventorySlots[i]->_objectId == objectId)
@@ -270,6 +278,16 @@ void BbdouInventory::buildItems(InventoryBag *inventoryBag) {
 	}
 }
 
+void BbdouInventory::clear() {
+	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it) {
+		InventoryItem *inventoryItem = *it;
+		inventoryItem->_assigned = false;
+		inventoryItem->_flag = false;
+	}
+	for (uint i = 0; i < _inventoryBags.size(); ++i)
+		_inventoryBags[i]->clear();
+}
+
 void BbdouInventory::cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId) {
 	// TODO
 	debug("cause0x1B0001");
diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index 2a275d4..a5b55f4 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -59,6 +59,7 @@ public:
 	void removeInventoryItem(InventoryItem *inventoryItem);
 	bool hasInventoryItem(uint32 objectId);
 	void buildItems();
+	void clear();
 	InventorySlot *getInventorySlot(uint32 objectId);
 	InventorySlot *findClosestSlot(Common::Point putPos, int index);
 protected:
@@ -87,6 +88,7 @@ public:
 	InventoryItem *getInventoryItem(uint32 objectId);
 	void refresh();
 	void buildItems(InventoryBag *inventoryBag);
+	void clear();
 	void cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId);
 	void cause0x1B0002(TriggerFunction *triggerFunction, uint32 callingThreadId);
 	void putBackInventoryItem(uint32 objectId, Common::Point cursorPosition);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 8839e2f8..727be8e 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -27,6 +27,7 @@
 #include "illusions/bbdou/bbdou_credits.h"
 #include "illusions/bbdou/bbdou_cursor.h"
 #include "illusions/bbdou/bbdou_foodctl.h"
+#include "illusions/resources/scriptresource.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
@@ -127,7 +128,6 @@ typedef Common::Functor1Mem<OpCall&, void, BbdouSpecialCode> SpecialCodeFunction
 #define SPECIAL(id, func) _map[id] = new SpecialCodeFunctionI(this, &BbdouSpecialCode::func);
 
 void BbdouSpecialCode::init() {
-	// TODO
 	// 0x00160001 only used for original debugging purposes
 	SPECIAL(0x00160006, spcInitCursor);
 	SPECIAL(0x00160008, spcEnableCursor);
@@ -140,6 +140,7 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x00160014, spcSetupBubble);
 	SPECIAL(0x00160015, spcSetObjectInteractMode);
 	SPECIAL(0x00160017, spcInitInventory);
+	SPECIAL(0x00160018, spcClearInventory);
 	SPECIAL(0x00160019, spcRegisterInventoryBag);
 	SPECIAL(0x0016001A, spcRegisterInventorySlot);
 	SPECIAL(0x0016001B, spcRegisterInventoryItem);
@@ -149,6 +150,8 @@ void BbdouSpecialCode::init() {
 	SPECIAL(0x0016001F, spcHasInventoryItem);
 	SPECIAL(0x00160025, spcCloseInventory);
 	SPECIAL(0x00160027, spcInitConversation);
+	SPECIAL(0x00160028, spcClearConversation);
+	SPECIAL(0x0016002B, spcClearBlockCounter);
 	SPECIAL(0x00160030, spcResetCursor);
 	SPECIAL(0x00160032, spcSetCursorField90);
 	SPECIAL(0x00160034, spcFoodCtl);
@@ -258,6 +261,10 @@ void BbdouSpecialCode::spcInitInventory(OpCall &opCall) {
 	// inventory is initialized in this class' constructor
 }
 
+void BbdouSpecialCode::spcClearInventory(OpCall &opCall) {
+	_inventory->clear();
+}
+
 void BbdouSpecialCode::spcRegisterInventoryBag(OpCall &opCall) {
 	ARG_UINT32(sceneId);
 	_inventory->registerInventoryBag(sceneId);
@@ -301,6 +308,16 @@ void BbdouSpecialCode::spcInitConversation(OpCall &opCall) {
 	// Conversations seem unused but this is still called
 }
 
+void BbdouSpecialCode::spcClearConversation(OpCall &opCall) {
+	// Conversations seem unused but this is still called
+}
+
+void BbdouSpecialCode::spcClearBlockCounter(OpCall &opCall) {
+	// Conversations seem unused but this is still called
+	ARG_UINT32(index);
+	_vm->_scriptResource->_blockCounters.set(index, 0);
+}
+
 void BbdouSpecialCode::spcResetCursor(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	_cursor->reset(objectId);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index f483307..40ed53d 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -128,6 +128,7 @@ public:
 	void spcSetupBubble(OpCall &opCall);
 	void spcSetObjectInteractMode(OpCall &opCall);
 	void spcInitInventory(OpCall &opCall);
+	void spcClearInventory(OpCall &opCall);
 	void spcRegisterInventoryBag(OpCall &opCall);
 	void spcRegisterInventorySlot(OpCall &opCall);
 	void spcRegisterInventoryItem(OpCall &opCall);
@@ -137,6 +138,8 @@ public:
 	void spcHasInventoryItem(OpCall &opCall);
 	void spcCloseInventory(OpCall &opCall);
 	void spcInitConversation(OpCall &opCall);
+	void spcClearConversation(OpCall &opCall);
+	void spcClearBlockCounter(OpCall &opCall);
 	void spcResetCursor(OpCall &opCall);
 	void spcSetCursorField90(OpCall &opCall);
 	void spcFoodCtl(OpCall &opCall);


Commit: 6b80f5b85349fc2bd645da52c4e68a337894f38a
    https://github.com/scummvm/scummvm/commit/6b80f5b85349fc2bd645da52c4e68a337894f38a
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Move game credits into own file and class

Changed paths:
  A engines/illusions/duckman/duckman_credits.cpp
  A engines/illusions/duckman/duckman_credits.h
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/duckman/duckman_credits.cpp b/engines/illusions/duckman/duckman_credits.cpp
new file mode 100644
index 0000000..18a0482
--- /dev/null
+++ b/engines/illusions/duckman/duckman_credits.cpp
@@ -0,0 +1,198 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *                                                                                                                                  
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_credits.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/resources/fontresource.h"
+#include "illusions/resources/scriptresource.h"
+#include "illusions/textdrawer.h"
+#include "illusions/time.h"
+#include "illusions/updatefunctions.h"
+
+#include "engines/util.h"
+
+namespace Illusions {
+
+// Duckman_SpecialCode
+
+DuckmanCredits::DuckmanCredits(IllusionsEngine_Duckman *vm)
+	: _vm(vm) {
+
+}
+
+DuckmanCredits::~DuckmanCredits() {
+}
+
+void DuckmanCredits::start() {
+	static const struct { uint32 objectId; int scrollPosY; } kCreditsItems[] = {
+		{0x40136,   0}, {0x40137,  16}, {0x40138,  32}, {0x40139,  48},
+		{0x4013A,  64}, {0x4013B,  80}, {0x4013C,  96}, {0x4013D, 112}
+	};
+	_currText = (char*)_vm->_resSys->getResource(0x190052)->_data;
+	_creditsItems.clear();
+	for (uint i = 0; i < ARRAYSIZE(kCreditsItems);  ++i) {
+		CreditsItem creditsItem;
+		creditsItem.objectId = kCreditsItems[i].objectId;
+		creditsItem.scrollPosY = kCreditsItems[i].scrollPosY;
+		creditsItem.scrollPosIndex = 0;
+		creditsItem.active = false;
+		_creditsItems.push_back(creditsItem);
+	}
+	uint32 currSceneId = _vm->getCurrentScene();
+	_vm->_updateFunctions->add(0, currSceneId, new Common::Functor1Mem<uint, int, DuckmanCredits>(this, &DuckmanCredits::update));
+	_nextUpdateTicks = getCurrentTime();
+	_lastUpdateTicks = _nextUpdateTicks - 4;
+}
+
+int DuckmanCredits::update(uint flags) {
+
+	if (_vm->_pauseCtr > 0) {
+		_nextUpdateTicks = getCurrentTime() + 4;
+		return 1;
+	}
+
+	if (flags & 1) {
+		_vm->_scriptResource->_properties.set(0x000E0096, true);
+		_lastItemIndex = -1;
+		_endReached = false;
+		return 2;
+	}
+
+	if (!isTimerExpired(_lastUpdateTicks, _nextUpdateTicks)) {
+		return 1;
+	}
+
+	bool creditsRunning = false;
+	int index = 0;
+	for (CreditsItems::iterator it = _creditsItems.begin(); it != _creditsItems.end(); ++it, ++index) {
+		CreditsItem &creditsItem = *it;
+		Control *control = _vm->getObjectControl(creditsItem.objectId);
+		if (!creditsItem.active && creditsItem.scrollPosY == 0 && !_endReached) {
+			creditsItem.active = true;
+			creditsItem.scrollPosIndex = 0;
+			control->fillActor(0);
+			char *text = readNextLine();
+			if (!strncmp(text, "&&&END", 6)) {
+				creditsItem.active = false;
+				_endReached = true;
+			} else {
+				uint16 wtext[128];
+				charToWChar(text, wtext, ARRAYSIZE(wtext));
+
+				FontResource *font = _vm->_dict->findFont(0x120001); 
+				TextDrawer textDrawer;
+				WidthHeight dimensions;
+				uint16 *outText;
+				control->getActorFrameDimensions(dimensions);
+				textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), 2, outText);
+				textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
+				control->_actor->_flags |= 0x4000;
+
+				_lastItemIndex = index;
+			}
+		}
+		if (creditsItem.active) {
+			if (_endReached && _creditsItems[_lastItemIndex].scrollPosIndex > 53) {
+				creditsItem.active = false;
+				creditsItem.scrollPosY = -1;
+			} else {
+				creditsRunning = true;
+				control->_actor->_position = getItemPosition(creditsItem.scrollPosIndex);
+				++creditsItem.scrollPosIndex;
+				if (getItemPosition(creditsItem.scrollPosIndex).x < 0)
+					creditsItem.active = false;
+			}
+		}
+		if (creditsItem.scrollPosY > 0)
+			--creditsItem.scrollPosY;
+	}
+	_lastUpdateTicks = _nextUpdateTicks;
+	_nextUpdateTicks = getCurrentTime() + 4;
+
+	if (!creditsRunning) {
+		_vm->_scriptResource->_properties.set(0x000E0096, true);
+		_lastItemIndex = -1;
+		_endReached = false;
+		return 2;
+	}
+
+	return 1;
+}
+
+char *DuckmanCredits::readNextLine() {
+	static char line[256];
+	char *dest = line;
+	char *src = _currText;
+	do {
+		if (*src == 10 || *src == 13) {
+			src += 2;
+			*dest = 0;
+			break;
+		}
+		*dest++ = *src++;
+	} while (1);
+	_currText = src;
+	return line;
+}
+
+Common::Point DuckmanCredits::getItemPosition(int index) {
+	static const struct { int16 x, y; } kCreditsItemsPoints[] = {
+		{159, 200}, {158, 195}, {157, 190}, {156, 185}, {156, 180}, {157, 176}, 
+		{158, 172}, {159, 168}, {161, 164}, {162, 161}, {163, 158}, {163, 155}, 
+		{162, 152}, {161, 149}, {159, 147}, {158, 144}, {157, 142}, {156, 140}, 
+		{156, 138}, {157, 136}, {158, 134}, {159, 132}, {161, 130}, {162, 128}, 
+		{163, 127}, {163, 126}, {162, 125}, {161, 124}, {159, 123}, {158, 122}, 
+		{157, 121}, {156, 120}, {156, 119}, {157, 118}, {158, 117}, {159, 116}, 
+		{161, 115}, {162, 114}, {163, 113}, {163, 112}, {162, 111}, {161, 110}, 
+		{159, 109}, {158, 108}, {157, 107}, {156, 106}, {156, 105}, {157, 104}, 
+		{158, 103}, {159, 102}, {161, 101}, {162, 100}, {163,  99}, {163,  98}, 
+		{162,  97}, {161,  96}, {159,  95}, {158,  94}, {157,  93}, {156,  92}, 
+		{156,  91}, {157,  90}, {158,  89}, {159,  88}, {161,  87}, {162,  86}, 
+		{163,  85}, {163,  84}, {162,  83}, {161,  82}, {159,  81}, {158,  80}, 
+		{157,  79}, {156,  78}, {156,  77}, {157,  76}, {158,  75}, {159,  74},
+		{161,  73}, {162,  72}, {163,  71}, {163,  70}, {162,  69}, {161,  68}, 
+		{159,  67}, {158,  66}, {157,  64}, {156,  62}, {156,  60}, {157,  58}, 
+		{158,  56}, {159,  54}, {161,  52}, {162,  50}, {163,  40}, {163,  40}, 
+		{162,  40}, {161,  40}, {159,  40}, {158,  40}, {157,  40}, {156,  40}, 
+		{156,  40}, {157,  40}, {158,  40}, {159,  40}, {161,  40}, {162,  40}, 
+		{163,  40}, {163,  40}, {162,  40}, {161,  40}, {159,  40}, {158,  40}, 
+		{157,  40}, {156,  40}, {156,  40}, {157,  40}, {158,  40}, {159,  40}, 
+		{161,  40}, {162,  40}, {163,  40}, {163,  40}, {162,  40}, {161,  40}, 
+		{159,  40}, {158,  40}, { -1,  -1} 
+	};
+
+	if (index < 0 || index >= ARRAYSIZE(kCreditsItemsPoints))
+		return Common::Point(-1, -1);
+	return Common::Point(kCreditsItemsPoints[index].x, kCreditsItemsPoints[index].y);
+}
+
+void DuckmanCredits::charToWChar(char *text, uint16 *wtext, uint size) {
+	while (*text != 0 && size > 1) {
+		*wtext++ = (byte)*text++;
+		--size;
+	}
+	*wtext++ = 0;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_credits.h b/engines/illusions/duckman/duckman_credits.h
new file mode 100644
index 0000000..039575c
--- /dev/null
+++ b/engines/illusions/duckman/duckman_credits.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_CREDITS_H
+#define ILLUSIONS_DUCKMAN_CREDITS_H
+
+#include "illusions/illusions.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+struct CreditsItem {
+	uint32 objectId;
+	bool active;
+	int16 scrollPosIndex;
+	int16 scrollPosY;
+};
+
+class DuckmanCredits {
+public:
+	DuckmanCredits(IllusionsEngine_Duckman *vm);
+	~DuckmanCredits();
+	void start();
+public:	
+	typedef Common::Array<CreditsItem> CreditsItems;
+	IllusionsEngine_Duckman *_vm;
+	uint32 _lastUpdateTicks;
+	uint32 _nextUpdateTicks;
+	int _lastItemIndex;
+	bool _endReached;
+	CreditsItems _creditsItems;
+	char *_currText;
+	int update(uint flags);
+	char *readNextLine();
+	Common::Point getItemPosition(int index);
+	void charToWChar(char *text, uint16 *wtext, uint size);
+
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_ILLUSIONS_H
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index f0c3698..d53896d 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_credits.h"
 #include "illusions/duckman/duckman_screenshakereffects.h"
 #include "illusions/duckman/duckman_specialcode.h"
 #include "illusions/duckman/duckman_inventory.h"
@@ -47,6 +48,8 @@ DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 
 	_propertyTimers = new PropertyTimers(_vm);
 	_inventory = new DuckmanInventory(_vm);
+	_credits = new DuckmanCredits(_vm);
+	
 	_wasCursorHoldingElvisPoster = false;
 	_counter = 0;
 	_savedTempMasterSfxVolume = 16;
@@ -56,6 +59,7 @@ DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 DuckmanSpecialCode::~DuckmanSpecialCode() {
 	delete _propertyTimers;
 	delete _inventory;
+	delete _credits;
 }
 
 typedef Common::Functor1Mem<OpCall&, void, DuckmanSpecialCode> SpecialCodeFunctionDM;
@@ -339,7 +343,7 @@ void DuckmanSpecialCode::spcHoldGlowingElvisPoster(OpCall &opCall) {
 void DuckmanSpecialCode::spcStartCredits(OpCall &opCall) {
 	ARG_BYTE(mode);
 	if (mode == 0)
-		startCredits();
+		_credits->start();
 	_vm->notifyThreadId(opCall._threadId);
 }
 
@@ -391,155 +395,4 @@ void DuckmanSpecialCode::updateTeleporterProperties() {
 	_vm->_scriptResource->_properties.set(0x000E0078, _teleporterPosition.x == 1 && _teleporterPosition.y == 1);	
 }
 
-void DuckmanSpecialCode::startCredits() {
-	static const struct { uint32 objectId; int scrollPosY; } kCreditsItems[] = {
-		{0x40136,   0}, {0x40137,  16}, {0x40138,  32}, {0x40139,  48},
-		{0x4013A,  64}, {0x4013B,  80}, {0x4013C,  96}, {0x4013D, 112}
-	};
-	_creditsCurrText = (char*)_vm->_resSys->getResource(0x190052)->_data;
-	_creditsItems.clear();
-	for (uint i = 0; i < ARRAYSIZE(kCreditsItems);  ++i) {
-		CreditsItem creditsItem;
-		creditsItem.objectId = kCreditsItems[i].objectId;
-		creditsItem.scrollPosY = kCreditsItems[i].scrollPosY;
-		creditsItem.scrollPosIndex = 0;
-		creditsItem.active = false;
-		_creditsItems.push_back(creditsItem);
-	}
-	uint32 currSceneId = _vm->getCurrentScene();
-	_vm->_updateFunctions->add(0, currSceneId, new Common::Functor1Mem<uint, int, DuckmanSpecialCode>(this, &DuckmanSpecialCode::updateCredits));
-	_creditsNextUpdateTicks = getCurrentTime();
-	_creditsLastUpdateTicks = _creditsNextUpdateTicks - 4;
-}
-
-int DuckmanSpecialCode::updateCredits(uint flags) {
-
-	if (_vm->_pauseCtr > 0) {
-		_creditsNextUpdateTicks = getCurrentTime() + 4;
-		return 1;
-	}
-
-	if (flags & 1) {
-		_vm->_scriptResource->_properties.set(0x000E0096, true);
-		_lastCreditsItemIndex = -1;
-		_creditsEndReached = false;
-		return 2;
-	}
-
-	if (!isTimerExpired(_creditsLastUpdateTicks, _creditsNextUpdateTicks)) {
-		return 1;
-	}
-
-	bool creditsRunning = false;
-	int index = 0;
-	for (CreditsItems::iterator it = _creditsItems.begin(); it != _creditsItems.end(); ++it, ++index) {
-		CreditsItem &creditsItem = *it;
-		Control *control = _vm->getObjectControl(creditsItem.objectId);
-		if (!creditsItem.active && creditsItem.scrollPosY == 0 && !_creditsEndReached) {
-			creditsItem.active = true;
-			creditsItem.scrollPosIndex = 0;
-			control->fillActor(0);
-			char *text = readNextCreditsLine();
-			if (!strncmp(text, "&&&END", 6)) {
-				creditsItem.active = false;
-				_creditsEndReached = true;
-			} else {
-				uint16 wtext[128];
-				charToWChar(text, wtext, ARRAYSIZE(wtext));
-
-				FontResource *font = _vm->_dict->findFont(0x120001); 
-				TextDrawer textDrawer;
-				WidthHeight dimensions;
-				uint16 *outText;
-				control->getActorFrameDimensions(dimensions);
-				textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), 2, outText);
-				textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
-				control->_actor->_flags |= 0x4000;
-
-				_lastCreditsItemIndex = index;
-			}
-		}
-		if (creditsItem.active) {
-			if (_creditsEndReached && _creditsItems[_lastCreditsItemIndex].scrollPosIndex > 53) {
-				creditsItem.active = false;
-				creditsItem.scrollPosY = -1;
-			} else {
-				creditsRunning = true;
-				control->_actor->_position = getCreditsItemPosition(creditsItem.scrollPosIndex);
-				++creditsItem.scrollPosIndex;
-				if (getCreditsItemPosition(creditsItem.scrollPosIndex).x < 0)
-					creditsItem.active = false;
-			}
-		}
-		if (creditsItem.scrollPosY > 0)
-			--creditsItem.scrollPosY;
-	}
-	_creditsLastUpdateTicks = _creditsNextUpdateTicks;
-	_creditsNextUpdateTicks = getCurrentTime() + 4;
-
-	if (!creditsRunning) {
-		_vm->_scriptResource->_properties.set(0x000E0096, true);
-		_lastCreditsItemIndex = -1;
-		_creditsEndReached = false;
-		return 2;
-	}
-
-	return 1;
-}
-
-char *DuckmanSpecialCode::readNextCreditsLine() {
-	static char line[256];
-	char *dest = line;
-	char *src = _creditsCurrText;
-	do {
-		if (*src == 10 || *src == 13) {
-			src += 2;
-			*dest = 0;
-			break;
-		}
-		*dest++ = *src++;
-	} while (1);
-	_creditsCurrText = src;
-	return line;
-}
-
-Common::Point DuckmanSpecialCode::getCreditsItemPosition(int index) {
-	static const struct { int16 x, y; } kCreditsItemsPoints[] = {
-		{159, 200}, {158, 195}, {157, 190}, {156, 185}, {156, 180}, {157, 176}, 
-		{158, 172}, {159, 168}, {161, 164}, {162, 161}, {163, 158}, {163, 155}, 
-		{162, 152}, {161, 149}, {159, 147}, {158, 144}, {157, 142}, {156, 140}, 
-		{156, 138}, {157, 136}, {158, 134}, {159, 132}, {161, 130}, {162, 128}, 
-		{163, 127}, {163, 126}, {162, 125}, {161, 124}, {159, 123}, {158, 122}, 
-		{157, 121}, {156, 120}, {156, 119}, {157, 118}, {158, 117}, {159, 116}, 
-		{161, 115}, {162, 114}, {163, 113}, {163, 112}, {162, 111}, {161, 110}, 
-		{159, 109}, {158, 108}, {157, 107}, {156, 106}, {156, 105}, {157, 104}, 
-		{158, 103}, {159, 102}, {161, 101}, {162, 100}, {163,  99}, {163,  98}, 
-		{162,  97}, {161,  96}, {159,  95}, {158,  94}, {157,  93}, {156,  92}, 
-		{156,  91}, {157,  90}, {158,  89}, {159,  88}, {161,  87}, {162,  86}, 
-		{163,  85}, {163,  84}, {162,  83}, {161,  82}, {159,  81}, {158,  80}, 
-		{157,  79}, {156,  78}, {156,  77}, {157,  76}, {158,  75}, {159,  74},
-		{161,  73}, {162,  72}, {163,  71}, {163,  70}, {162,  69}, {161,  68}, 
-		{159,  67}, {158,  66}, {157,  64}, {156,  62}, {156,  60}, {157,  58}, 
-		{158,  56}, {159,  54}, {161,  52}, {162,  50}, {163,  40}, {163,  40}, 
-		{162,  40}, {161,  40}, {159,  40}, {158,  40}, {157,  40}, {156,  40}, 
-		{156,  40}, {157,  40}, {158,  40}, {159,  40}, {161,  40}, {162,  40}, 
-		{163,  40}, {163,  40}, {162,  40}, {161,  40}, {159,  40}, {158,  40}, 
-		{157,  40}, {156,  40}, {156,  40}, {157,  40}, {158,  40}, {159,  40}, 
-		{161,  40}, {162,  40}, {163,  40}, {163,  40}, {162,  40}, {161,  40}, 
-		{159,  40}, {158,  40}, { -1,  -1} 
-	};
-
-	if (index < 0 || index >= ARRAYSIZE(kCreditsItemsPoints))
-		return Common::Point(-1, -1);
-	return Common::Point(kCreditsItemsPoints[index].x, kCreditsItemsPoints[index].y);
-}
-
-void DuckmanSpecialCode::charToWChar(char *text, uint16 *wtext, uint size) {
-	while (*text != 0 && size > 1) {
-		*wtext++ = (byte)*text++;
-		--size;
-	}
-	*wtext++ = 0;
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index 9176a64..a9fb7b9 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -30,18 +30,12 @@
 namespace Illusions {
 
 class IllusionsEngine_Duckman;
+class DuckmanCredits;
 class DuckmanInventory;
 class PropertyTimers;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
-struct CreditsItem {
-	uint32 objectId;
-	bool active;
-	int16 scrollPosIndex;
-	int16 scrollPosY;
-};
-
 class DuckmanSpecialCode : public SpecialCode {
 public:
 	DuckmanSpecialCode(IllusionsEngine_Duckman *vm);
@@ -51,7 +45,6 @@ public:
 public:	
 	typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
 	typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
-	typedef Common::Array<CreditsItem> CreditsItems;
 
 	IllusionsEngine_Duckman *_vm;
 	SpecialCodeMap _specialCodeMap;
@@ -67,12 +60,7 @@ public:
 	int16 _savedTempMasterSfxVolume;
 	int16 _lastRandomSoundIndex;
 
-	uint32 _creditsLastUpdateTicks;
-	uint32 _creditsNextUpdateTicks;
-	int _lastCreditsItemIndex;
-	bool _creditsEndReached;
-	CreditsItems _creditsItems;
-	char *_creditsCurrText;
+	DuckmanCredits *_credits;
 
 	// Special code interface functions
 	void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 759b4bc..2518002 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -259,7 +259,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 
 //static uint dsceneId = 0, dthreadId = 0;
 //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
-static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
+//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
 //static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
 //static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
@@ -276,7 +276,7 @@ static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
 //static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map
-//static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
+static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 8211696..1f164cd 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS := \
 	cursor.o \
 	detection.o \
 	dictionary.o \
+	duckman/duckman_credits.o \
 	duckman/duckman_dialog.o \
 	duckman/duckman_inventory.o \
 	duckman/duckman_screenshakereffects.o \


Commit: 2f80bd0e2b4ab747792d5cdd3c548ba1963a6e00
    https://github.com/scummvm/scummvm/commit/2f80bd0e2b4ab747792d5cdd3c548ba1963a6e00
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement opcodes 18, 19

Changed paths:
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 8466780..bacd53e 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -567,6 +567,16 @@ void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
 	_camera->clearCameraModeStack();
 }
 
+void IllusionsEngine_BBDOU::enterMenuPause() {
+      // TODO suspendAudio();
+      _screenText->clearText();
+}
+
+void IllusionsEngine_BBDOU::leaveMenuPause() {
+	_screenText->removeText();
+	// TODO unsuspendAudio();
+}
+
 void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
 	_theSceneId = theSceneId;
 	_theThreadId = theThreadId;
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index 65d9e26..00ce617 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -129,6 +129,9 @@ public:
 	void leavePause(uint32 threadId);
 	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
 
+	void enterMenuPause();
+	void leaveMenuPause();
+
 	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
 	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void reset();
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index e023fb3..93b6f8f 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -72,8 +72,8 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(15, opEndTalkThreads);
 	OPCODE(16, opLoadResource);
 	OPCODE(17, opUnloadResource);
-	// TODO OPCODE(18, opPauseText);
-	// TODO OPCODE(19, opResumeText);
+	OPCODE(18, opEnterMenuPause);
+	OPCODE(19, opLeaveMenuPause);
 	OPCODE(20, opEnterScene);
 	OPCODE(21, opLeaveScene);
 	// TODO OPCODE(22, opEnterPause);
@@ -259,6 +259,14 @@ void ScriptOpcodes_BBDOU::opUnloadResource(ScriptThread *scriptThread, OpCall &o
 	_vm->_resSys->unloadResourceById(resourceId);
 }
 
+void ScriptOpcodes_BBDOU::opEnterMenuPause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->enterMenuPause();
+}
+
+void ScriptOpcodes_BBDOU::opLeaveMenuPause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->leaveMenuPause();
+}
+
 void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index 20b6aff..7a73ebd 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -53,6 +53,8 @@ protected:
 	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
 	void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterMenuPause(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeaveMenuPause(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall);


Commit: 3abe3d759c6e16ef1e0e3cea7a8eb5f766a56851
    https://github.com/scummvm/scummvm/commit/3abe3d759c6e16ef1e0e3cea7a8eb5f766a56851
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement opcodes 22, 23

Changed paths:
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h


diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 93b6f8f..1c3a8ea 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -76,8 +76,8 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(19, opLeaveMenuPause);
 	OPCODE(20, opEnterScene);
 	OPCODE(21, opLeaveScene);
-	// TODO OPCODE(22, opEnterPause);
-	// TODO OPCODE(23, opLeavePause);
+	OPCODE(22, opEnterPause);
+	OPCODE(23, opLeavePause);
 	OPCODE(24, opUnloadActiveScenes);
 	OPCODE(25, opChangeScene);
 	OPCODE(26, opStartModalScene);
@@ -284,6 +284,15 @@ void ScriptOpcodes_BBDOU::opLeaveScene(ScriptThread *scriptThread, OpCall &opCal
 	_vm->exitScene(opCall._callerThreadId);
 }
 
+void ScriptOpcodes_BBDOU::opEnterPause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->enterPause(opCall._callerThreadId);
+	_vm->_talkItems->pauseBySceneId(_vm->getCurrentScene());
+}
+
+void ScriptOpcodes_BBDOU::opLeavePause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->leavePause(opCall._callerThreadId);
+}
+
 void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index 7a73ebd..a51b685 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -57,6 +57,8 @@ protected:
 	void opLeaveMenuPause(ScriptThread *scriptThread, OpCall &opCall);
 	void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opLeaveScene(ScriptThread *scriptThread, OpCall &opCall);
+	void opEnterPause(ScriptThread *scriptThread, OpCall &opCall);
+	void opLeavePause(ScriptThread *scriptThread, OpCall &opCall);
 	void opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);


Commit: 1e2e5d636cdde3db2c30598b43281183b03234c3
    https://github.com/scummvm/scummvm/commit/1e2e5d636cdde3db2c30598b43281183b03234c3
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement opcode 54

Changed paths:
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h


diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 1c3a8ea..eb37025 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -106,7 +106,7 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(51, opStartMoveActor);
 	// 52 unused
 	OPCODE(53, opSetActorToNamedPoint);
-	// TODO OPCODE(54, opSetActorPosition);
+	OPCODE(54, opSetActorPosition);
 	// 55 unused
 	OPCODE(56, opStartTalkThread);
 	OPCODE(57, opAppearActor);
@@ -511,6 +511,15 @@ void ScriptOpcodes_BBDOU::opSetActorToNamedPoint(ScriptThread *scriptThread, OpC
 	control->setActorPosition(pos);
 }
 
+void ScriptOpcodes_BBDOU::opSetActorPosition(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_UINT32(objectId);
+	ARG_INT16(x);
+	ARG_INT16(y);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos(x, y);
+	control->setActorPosition(pos);
+}
+
 void ScriptOpcodes_BBDOU::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(duration);	
 	ARG_UINT32(objectId);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index a51b685..d8e0e39 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -82,6 +82,7 @@ protected:
 	void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
+	void opSetActorPosition(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);


Commit: 47b94b1396cdc3529c137ac1dfe105b5ebdef79d
    https://github.com/scummvm/scummvm/commit/47b94b1396cdc3529c137ac1dfe105b5ebdef79d
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Minor cleanup: Remove empty methods in thread classes

Changed paths:
    engines/illusions/threads/abortablethread.cpp
    engines/illusions/threads/abortablethread.h
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread.h
    engines/illusions/threads/talkthread_duckman.cpp
    engines/illusions/threads/talkthread_duckman.h
    engines/illusions/threads/timerthread.cpp
    engines/illusions/threads/timerthread.h


diff --git a/engines/illusions/threads/abortablethread.cpp b/engines/illusions/threads/abortablethread.cpp
index acbc1ad..d2fc807 100644
--- a/engines/illusions/threads/abortablethread.cpp
+++ b/engines/illusions/threads/abortablethread.cpp
@@ -51,19 +51,4 @@ int AbortableThread::onUpdate() {
 	return kTSYield;
 }
 
-void AbortableThread::onSuspend() {
-}
-
-void AbortableThread::onNotify() {
-}
-
-void AbortableThread::onPause() {
-}
-
-void AbortableThread::onResume() {
-}
-
-void AbortableThread::onTerminated() {
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/threads/abortablethread.h b/engines/illusions/threads/abortablethread.h
index 8fc2a47..96ec9dc 100644
--- a/engines/illusions/threads/abortablethread.h
+++ b/engines/illusions/threads/abortablethread.h
@@ -34,11 +34,6 @@ public:
 	AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		uint32 scriptThreadId, byte *scriptCodeIp);
 	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
 public:
 	int _status;
 	byte *_scriptCodeIp;
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index c60bc5d..e9c3e36 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -80,7 +80,6 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 			_sceneId = callingThread->_sceneId;
 	}
 
-	//debug("New talk thread: %08X %08X", _threadId, _talkId);
 }
 
 int TalkThread::onUpdate() {
@@ -245,21 +244,6 @@ if (true) {
 
 }
 
-void TalkThread::onSuspend() {
-}
-
-void TalkThread::onNotify() {
-}
-
-void TalkThread::onPause() {
-}
-
-void TalkThread::onResume() {
-}
-
-void TalkThread::onTerminated() {
-}
-
 void TalkThread::onKill() {
 	_callingThreadId = 0;
 	sendMessage(kMsgClearSequenceId1, 0);
@@ -313,30 +297,16 @@ static char *debugW2I(byte *wstr) {
 }
 
 int TalkThread::insertText() {
-/*
-	int charCount = 100;
-	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
-	_entryText = 0;
-	// TODO _vm->getDimensions1(&dimensions);
-	// TODO _vm->insertText(_currEntryText, _vm->_currFontId, dimensions, 0, 2, 0, 0, 0, 0, 0, 0, &outTextPtr);
-	// TODO _vm->charCount = (char *)outTextPtr - (char *)text;
-	// TODO _entryText = outTextPtr;
-	// TODO _vm->getPoint1(&pt);
-	// TODO _vm->updateTextInfoPosition(pt);
-	return charCount >> 1;
-*/	
 	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
 	WidthHeight dimensions;
 	_vm->getDefaultTextDimensions(dimensions);
 	uint16 *outTextPtr;
 	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
-		//Common::Point(0, 0), 2, 0, 0, _color.r, _color.g, _color.b, outTextPtr);
 		Common::Point(0, 0), 2, 0, 0, 0, 0, 0, outTextPtr);
 	_entryText = (byte*)outTextPtr;
 	Common::Point pt;
 	_vm->getDefaultTextPosition(pt);
 	_vm->_screenText->updateTextInfoPosition(pt);
-	//_vm->_screenText->updateTextInfoPosition(Common::Point(320, 200));
 	int charCount = (_entryText - _currEntryText) / 2;
 	return charCount;
 }
diff --git a/engines/illusions/threads/talkthread.h b/engines/illusions/threads/talkthread.h
index ada593b..1d6573c 100644
--- a/engines/illusions/threads/talkthread.h
+++ b/engines/illusions/threads/talkthread.h
@@ -42,11 +42,6 @@ public:
 		int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
 		uint32 namedPointId);
 	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
-	virtual void onPause();
-	virtual void onResume();
-	virtual void onTerminated();
 	virtual void onKill();
 	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
 public:
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 0476c8f..f891301 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -203,12 +203,6 @@ int TalkThread_Duckman::onUpdate() {
 
 }
 
-void TalkThread_Duckman::onSuspend() {
-}
-
-void TalkThread_Duckman::onNotify() {
-}
-
 void TalkThread_Duckman::onPause() {
 	if (_status == 5) {
 		if (!(_flags & 4)) {
diff --git a/engines/illusions/threads/talkthread_duckman.h b/engines/illusions/threads/talkthread_duckman.h
index 6f4758d..9c710e3 100644
--- a/engines/illusions/threads/talkthread_duckman.h
+++ b/engines/illusions/threads/talkthread_duckman.h
@@ -41,8 +41,6 @@ public:
 	TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2);
 	virtual int onUpdate();
-	virtual void onSuspend();
-	virtual void onNotify();
 	virtual void onPause();
 	virtual void onUnpause();
 	virtual void onResume();
diff --git a/engines/illusions/threads/timerthread.cpp b/engines/illusions/threads/timerthread.cpp
index de1502d..1f9a63b 100644
--- a/engines/illusions/threads/timerthread.cpp
+++ b/engines/illusions/threads/timerthread.cpp
@@ -77,8 +77,4 @@ void TimerThread::onResume() {
 	onNotify();
 }
 
-void TimerThread::onTerminated() {
-	// Empty
-}
-
 } // End of namespace Illusions
diff --git a/engines/illusions/threads/timerthread.h b/engines/illusions/threads/timerthread.h
index 7e1b613..6f4aba9 100644
--- a/engines/illusions/threads/timerthread.h
+++ b/engines/illusions/threads/timerthread.h
@@ -39,7 +39,6 @@ public:
 	virtual void onPause();
 	virtual void onUnpause();
 	virtual void onResume();
-	virtual void onTerminated();
 public:
 	uint32 _startTime, _endTime;
 	uint32 _duration, _durationElapsed;


Commit: e31c454cfa2d536bda56e791e65048f0bf854dd0
    https://github.com/scummvm/scummvm/commit/e31c454cfa2d536bda56e791e65048f0bf854dd0
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Rename stuff

Changed paths:
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 090c809..a6bc4a9 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -63,18 +63,18 @@ void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
 	_data._causeThreadId2 = 0;
 	_data._field90 = 0;
 	_data._flags = 0;
-	_data._item10._field58 = 1;
-	_data._sequenceId98 = 0;
+	_data._verbState._minPriority = 1;
+	_data._currCursorTrackingSequenceId = 0;
 	_data._idleCtr = 0;
-	_data._item10._verbId = 0x1B0000;
-	_data._item10._field0 = 1;
-	_data._item10._playSound48 = 0;
-	_data._item10._objectIds[0] = 0;
-	_data._item10._objectIds[1] = 0;
-	_data._item10._index = 0;
-	_data._item10._flag56 = 0;
+	_data._verbState._verbId = 0x1B0000;
+	_data._verbState._cursorState = 1;
+	_data._verbState._isBubbleVisible = 0;
+	_data._verbState._objectIds[0] = 0;
+	_data._verbState._objectIds[1] = 0;
+	_data._verbState._index = 0;
+	_data._verbState._flag56 = false;
 	
-	clearCursorDataField14();
+	resetActiveVerbs();
 
 	control->setActorIndexTo1();
 
@@ -99,7 +99,7 @@ void BbdouCursor::disable(uint32 objectId) {
 void BbdouCursor::reset(uint32 objectId) {
 	Control *control = _vm->_dict->getObjectControl(objectId);
 
-	_data._item10._field0 = 1;
+	_data._verbState._cursorState = 1;
 	_data._mode = 1;
 	_data._mode2 = 0;
 	_data._verbId1 = 0x1B0000;
@@ -111,25 +111,25 @@ void BbdouCursor::reset(uint32 objectId) {
 	_data._visibleCtr = 0;
 	_data._causeThreadId1 = 0;
 	_data._flags = 0;
-	_data._item10._field58 = 1;
-	_data._sequenceId98 = 0;
+	_data._verbState._minPriority = 1;
+	_data._currCursorTrackingSequenceId = 0;
 	_data._idleCtr = 0;
-	_data._item10._verbId = 0x1B0000;
-	_data._item10._playSound48 = 0;
-	_data._item10._objectIds[0] = 0;
-	_data._item10._objectIds[1] = 0;
-	_data._item10._index = 0;
-	_data._item10._flag56 = 0;
-	clearCursorDataField14();
+	_data._verbState._verbId = 0x1B0000;
+	_data._verbState._isBubbleVisible = 0;
+	_data._verbState._objectIds[0] = 0;
+	_data._verbState._objectIds[1] = 0;
+	_data._verbState._index = 0;
+	_data._verbState._flag56 = false;
+	resetActiveVerbs();
 	control->setActorIndexTo1();
 	control->startSequenceActor(0x60029, 2, 0);
 
-	_bbdou->resetItem10(control->_objectId, &_data._item10);
+	_bbdou->hideVerbBubble(control->_objectId, &_data._verbState);
 	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::cursorInteractControlRoutine));
 	
 }
 
-void BbdouCursor::addCursorSequence(uint32 objectId, uint32 sequenceId) {
+void BbdouCursor::addCursorSequenceId(uint32 objectId, uint32 sequenceId) {
 	for (uint i = 0; i < kMaxCursorSequences; ++i)
 		if (_cursorSequences[i]._objectId == 0) {
 			_cursorSequences[i]._objectId = objectId;
@@ -145,49 +145,48 @@ uint32 BbdouCursor::findCursorSequenceId(uint32 objectId) {
 	return 0;
 }
 
-void BbdouCursor::setStruct8bsValue(uint32 objectId, int value) {
-	// TODO Clean this up, preliminary
-	Struct8b *struct8b = 0;
-	for (uint i = 0; i < 512; ++i)
-		if (_cursorStruct8bs[i]._objectId == objectId) {
-			struct8b = &_cursorStruct8bs[i];
+void BbdouCursor::setObjectInteractMode(uint32 objectId, int value) {
+	ObjectInteractMode *objectInteractMode = 0;
+	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+		if (_objectVerbs[i]._objectId == objectId) {
+			objectInteractMode = &_objectVerbs[i];
 			break;
 		}
-	if (!struct8b) {
-		for (uint i = 0; i < 512; ++i)
-			if (_cursorStruct8bs[i]._objectId == 0) {
-				struct8b = &_cursorStruct8bs[i];
+	if (!objectInteractMode) {
+		for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+			if (_objectVerbs[i]._objectId == 0) {
+				objectInteractMode = &_objectVerbs[i];
 				break;
 			}
 	}
 	if (value != 11) {
-		struct8b->_objectId = objectId;
-		struct8b->_value = value;
-	} else if (struct8b->_objectId == objectId) {
-		struct8b->_objectId = 0;
-		struct8b->_value = 0;
+		objectInteractMode->_objectId = objectId;
+		objectInteractMode->_interactMode = value;
+	} else if (objectInteractMode->_objectId == objectId) {
+		objectInteractMode->_objectId = 0;
+		objectInteractMode->_interactMode = 0;
 	}
 }
 
-int BbdouCursor::findStruct8bsValue(uint32 objectId) {
-	for (uint i = 0; i < 512; ++i)
-		if (_cursorStruct8bs[i]._objectId == objectId)
-			return _cursorStruct8bs[i]._value;
+int BbdouCursor::getObjectInteractMode(uint32 objectId) {
+	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+		if (_objectVerbs[i]._objectId == objectId)
+			return _objectVerbs[i]._interactMode;
 	return 11;
 }
 
 bool BbdouCursor::updateTrackingCursor(Control *control) {
 	uint32 sequenceId;
 	if (getTrackingCursorSequenceId(control, sequenceId)) {
-		if (_data._sequenceId98 != sequenceId) {
+		if (_data._currCursorTrackingSequenceId != sequenceId) {
 			saveBeforeTrackingCursor(control, sequenceId);
 			show(control);
-			_data._sequenceId98 = sequenceId;
+			_data._currCursorTrackingSequenceId = sequenceId;
 		}
 		return true;
 	} else {
-		if (_data._sequenceId98) {
-			_data._sequenceId98 = 0;
+		if (_data._currCursorTrackingSequenceId) {
+			_data._currCursorTrackingSequenceId = 0;
 			restoreAfterTrackingCursor();
 			show(control);
 		}
@@ -215,9 +214,9 @@ void BbdouCursor::saveBeforeTrackingCursor(Control *control, uint32 sequenceId)
 		if (_data._mode == 3)
 			restoreInfo();
 		control->setActorIndexTo1();
-		if (_data._item10._playSound48)
+		if (_data._verbState._isBubbleVisible)
 			_bbdou->playSoundEffect(4);
-		_bbdou->resetItem10(control->_objectId, &_data._item10);
+		_bbdou->hideVerbBubble(control->_objectId, &_data._verbState);
 	}
 	_data._currOverlappedObjectId = 0;
 	if (_data._mode != 4) {
@@ -240,7 +239,7 @@ void BbdouCursor::restoreAfterTrackingCursor() {
 	_data._mode2 = 0;
 	_data._sequenceId2 = 0;
 	_data._holdingObjectId2 = 0;
-	_data._sequenceId98 = 0;
+	_data._currCursorTrackingSequenceId = 0;
 }
 
 uint32 BbdouCursor::getSequenceId1(int sequenceIndex) {
@@ -360,17 +359,17 @@ bool BbdouCursor::getTrackingCursorSequenceId(Control *control, uint32 &outSeque
 	return outSequenceId != 0;
 }
 
-void BbdouCursor::clearCursorDataField14() {
+void BbdouCursor::resetActiveVerbs() {
 	for (uint i = 0; i < 32; ++i)
-		_data._item10._verbActive[i] = 0;
-	if (_data._item10._field0 == 1) {
-		_data._item10._verbActive[1] = 1;
-		_data._item10._verbActive[2] = 1;
-		_data._item10._verbActive[3] = 1;
-		_data._item10._verbActive[5] = 1;
-	} else if (_data._item10._field0 == 3) {
-		_data._item10._verbActive[1] = 1;
-		_data._item10._verbActive[2] = 1;
+		_data._verbState._verbActive[i] = false;
+	if (_data._verbState._cursorState == 1) {
+		_data._verbState._verbActive[1] = true;
+		_data._verbState._verbActive[2] = true;
+		_data._verbState._verbActive[3] = true;
+		_data._verbState._verbActive[5] = true;
+	} else if (_data._verbState._cursorState == 3) {
+		_data._verbState._verbActive[1] = true;
+		_data._verbState._verbActive[2] = true;
 	}
 }
 
@@ -384,7 +383,7 @@ void BbdouCursor::hide(uint32 objectId) {
 	if (_data._visibleCtr == 0) {
 		Control *control = _vm->_dict->getObjectControl(objectId);
 		control->startSequenceActor(0x60029, 2, 0);
-		_bbdou->resetItem10(objectId, &_data._item10);
+		_bbdou->hideVerbBubble(objectId, &_data._verbState);
 		_vm->_camera->popCameraMode();
 	}
 	_vm->_input->discardAllEvents();
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index 8f454b7..c3f023a 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -30,18 +30,16 @@ namespace Illusions {
 class IllusionsEngine_BBDOU;
 class BbdouSpecialCode;
 class Control;
-struct Item10;
 
-struct Item10 {
-	int _field0;
-	int16 _verbActive[32];
+struct VerbState {
+	int _cursorState;
+	bool _verbActive[32];
 	uint32 _verbId;
-	int16 _playSound48;
-	//field_4A dw
+	bool _isBubbleVisible;
 	uint32 _objectIds[2];
 	int16 _index;
-	int16 _flag56;
-	int _field58;
+	bool _flag56;
+	int _minPriority;
 };
 
 struct CursorData {
@@ -49,7 +47,7 @@ struct CursorData {
 	int _mode2;
 	uint32 _verbId1;
 	uint32 _progResKeywordId;
-	Item10 _item10;
+	VerbState _verbState;
 	uint32 _currOverlappedObjectId;
 	uint32 _overlappedObjectId;
 	uint32 _sequenceId;
@@ -57,16 +55,12 @@ struct CursorData {
 	uint32 _holdingObjectId;
 	uint32 _holdingObjectId2;
 	int _visibleCtr;
-	//field_86 dw
 	uint32 _causeThreadId1;
 	uint32 _causeThreadId2;
 	int16 _field90;
-	//field_92 dw
 	uint _flags;
-	uint32 _sequenceId98;
+	uint32 _currCursorTrackingSequenceId;
 	int16 _idleCtr;
-	//field_9E db
-	//field_9F db
 };
 
 struct CursorSequence {
@@ -75,10 +69,10 @@ struct CursorSequence {
 	CursorSequence() : _objectId(0), _sequenceId(0) {}
 };
 
-struct Struct8b {
+struct ObjectInteractMode {
 	uint32 _objectId;
-	int _value;
-	Struct8b() : _objectId(0), _value(0) {}
+	int _interactMode;
+	ObjectInteractMode() : _objectId(0), _interactMode(0) {}
 };
 
 const uint kMaxCursorSequences = 100;
@@ -91,10 +85,10 @@ public:
 	void enable(uint32 objectId);
 	void disable(uint32 objectId);
 	void reset(uint32 objectId);
-	void addCursorSequence(uint32 objectId, uint32 sequenceId);
+	void addCursorSequenceId(uint32 objectId, uint32 sequenceId);
 	uint32 findCursorSequenceId(uint32 objectId);
-	void setStruct8bsValue(uint32 objectId, int value);
-	int findStruct8bsValue(uint32 objectId);
+	void setObjectInteractMode(uint32 objectId, int value);
+	int getObjectInteractMode(uint32 objectId);
 	bool updateTrackingCursor(Control *control);
 	void saveInfo();
 	void restoreInfo();
@@ -110,8 +104,8 @@ public:
 	Control *_control;
 	CursorData _data;
 	CursorSequence _cursorSequences[kMaxCursorSequences];
-	Struct8b _cursorStruct8bs[512];
-	void clearCursorDataField14();
+	ObjectInteractMode _objectVerbs[512];
+	void resetActiveVerbs();
 	void show(Control *control);
 	void hide(uint32 objectId);
 };
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 727be8e..540a31e 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -200,7 +200,7 @@ void BbdouSpecialCode::spcAddCursorSequence(OpCall &opCall) {
 	ARG_SKIP(4);
 	ARG_UINT32(objectId);
 	ARG_UINT32(sequenceId);
-	_cursor->addCursorSequence(objectId, sequenceId);
+	_cursor->addCursorSequenceId(objectId, sequenceId);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
@@ -223,8 +223,8 @@ void BbdouSpecialCode::spcCursorStopHoldingObjectId(OpCall &opCall) {
 void BbdouSpecialCode::spcSetCursorState(OpCall &opCall) {
 	ARG_UINT32(objectId);
 	ARG_UINT32(newState);
-	_cursor->_data._item10._field0 = newState;
-	_cursor->clearCursorDataField14();
+	_cursor->_data._verbState._cursorState = newState;
+	_cursor->resetActiveVerbs();
 	if (newState == 5)
 		setCursorControlRoutine(objectId, 1);
 	else
@@ -252,7 +252,7 @@ void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
 	ARG_SKIP(4);
 	ARG_UINT32(objectId);
 	ARG_INT16(value);
-	_cursor->setStruct8bsValue(objectId, value);
+	_cursor->setObjectInteractMode(objectId, value);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
@@ -466,13 +466,13 @@ void BbdouSpecialCode::playSoundEffect(int soundIndex) {
 	}
 }
 
-void BbdouSpecialCode::resetItem10(uint32 objectId, Item10 *item10) {
-	if (item10->_playSound48 == 1) {
+void BbdouSpecialCode::hideVerbBubble(uint32 objectId, VerbState *verbState) {
+	if (verbState->_isBubbleVisible) {
 		_bubble->hide();
-		item10->_verbId = 0x1B0000;
-		item10->_playSound48 = 0;
-		item10->_objectIds[0] = 0;
-		item10->_objectIds[1] = 0;
+		verbState->_verbId = 0x1B0000;
+		verbState->_isBubbleVisible = false;
+		verbState->_objectIds[0] = 0;
+		verbState->_objectIds[1] = 0;
 	}
 	_vm->_input->discardAllEvents();
 }
@@ -499,7 +499,7 @@ Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos)
 }
 
 void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
-	Item10 *item10, uint32 progResKeywordId) {
+	VerbState *verbState, uint32 progResKeywordId) {
 	
 	Common::Rect collisionRect;
 	Control *overlappedControl, *control2, *control3;
@@ -526,44 +526,44 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 
 	_bubble->setup(1, bubbleSourcePt, bubbleDestPt, progResKeywordId);
 
-	item10->_objectIds[0] = _bubble->addItem(0, 0x6005A);
-	item10->_objectIds[1] = _bubble->addItem(0, 0x6005A);
-	item10->_index = 0;
+	verbState->_objectIds[0] = _bubble->addItem(0, 0x6005A);
+	verbState->_objectIds[1] = _bubble->addItem(0, 0x6005A);
+	verbState->_index = 0;
 	
-	int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+	int value = _cursor->getObjectInteractMode(overlappedControl->_objectId);
 	if (holdingObjectId) {
-		item10->_verbId = 0x1B0003;
+		verbState->_verbId = 0x1B0003;
 	} else if (value == 9) {
-		item10->_verbId = 0x1B0005;
+		verbState->_verbId = 0x1B0005;
 	} else if (value == 8) {
-		item10->_verbId = 0x1B0005;
+		verbState->_verbId = 0x1B0005;
 	} else {
-		item10->_verbId = 0x1B0002;
+		verbState->_verbId = 0x1B0002;
 	}
 	
-	uint32 sequenceId = kStruct10s[item10->_verbId & 0xFFFF]._sequenceId2;
+	uint32 sequenceId = kStruct10s[verbState->_verbId & 0xFFFF]._sequenceId2;
 	_bubble->show();
 	
-	control3 = _vm->_dict->getObjectControl(item10->_objectIds[0]);
+	control3 = _vm->_dict->getObjectControl(verbState->_objectIds[0]);
 	control3->startSequenceActor(sequenceId, 2, 0);
 	control3->appearActor();
 	control3->deactivateObject();
 	
-	item10->_playSound48 = 1;
+	verbState->_isBubbleVisible = true;
 	_vm->_input->discardAllEvents();
 
 }
 
-bool BbdouSpecialCode::findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId) {
-	if (item10->_playSound48) {
-		int verbNum = item10->_verbId & 0xFFFF;
+bool BbdouSpecialCode::findVerbId(VerbState *verbState, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId) {
+	if (verbState->_isBubbleVisible) {
+		int verbNum = verbState->_verbId & 0xFFFF;
 		int verbNumI = verbNum + 1;
 		while (1) {
 			if (verbNumI >= 32)
 				verbNumI = 0;
 			if (verbNumI++ == verbNum)
 				break;
-			if (item10->_verbActive[verbNumI] && testVerbId(verbNumI | 0x1B0000, always0, currOverlappedObjectId)) {
+			if (verbState->_verbActive[verbNumI] && testVerbId(verbNumI | 0x1B0000, always0, currOverlappedObjectId)) {
 				outVerbId = verbNumI | 0x1B0000;
 				return true;
 			}
@@ -603,18 +603,18 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 			foundOverlapped = false;
 		} else if (_vm->getCurrentScene() == 0x1000D) {
 			foundOverlapped = _vm->_controls->getOverlappedObjectAccurate(cursorControl, cursorPos,
-				&overlappedControl, cursorData._item10._field58);
+				&overlappedControl, cursorData._verbState._minPriority);
 		} else {
 			foundOverlapped = _vm->_controls->getOverlappedObject(cursorControl, cursorPos,
-				&overlappedControl, cursorData._item10._field58);
+				&overlappedControl, cursorData._verbState._minPriority);
 		}
 		
 		if (foundOverlapped) {
 			if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
-				if (cursorData._item10._playSound48)
+				if (cursorData._verbState._isBubbleVisible)
 					playSoundEffect(4);
-				resetItem10(cursorControl->_objectId, &cursorData._item10);
-				int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
+				int value = _cursor->getObjectInteractMode(overlappedControl->_objectId);
 				if (!testValueRange(value)) {
 					if (cursorData._mode == 3)
 						_cursor->restoreInfo();
@@ -626,17 +626,17 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 					}
 					if (value == 10) {
 						if (cursorData._holdingObjectId) {
-							cursorData._item10._verbId = 0x1B0003;
+							cursorData._verbState._verbId = 0x1B0003;
 							cursorData._currOverlappedObjectId = overlappedControl->_objectId;
 						}
 						else {
-							cursorData._item10._verbId = 0x1B0002;
+							cursorData._verbState._verbId = 0x1B0002;
 							cursorData._currOverlappedObjectId = overlappedControl->_objectId;
 						}
 					} else {
 						playSoundEffect(3);
 						showBubble(cursorControl->_objectId, overlappedControl->_objectId,
-							cursorData._holdingObjectId, &cursorData._item10,
+							cursorData._holdingObjectId, &cursorData._verbState,
 							cursorData._progResKeywordId);
 						cursorData._currOverlappedObjectId = overlappedControl->_objectId;
 					}
@@ -644,7 +644,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 					if (cursorData._mode != 3) {
 						_cursor->saveInfo();
 						cursorData._mode = 3;
-						cursorData._item10._verbId = 0x1B0006;
+						cursorData._verbState._verbId = 0x1B0006;
 						cursorData._holdingObjectId = 0;
 					}
 					cursorData._sequenceId = _cursor->getSequenceId1(value);
@@ -662,9 +662,9 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 					_cursor->restoreInfo();
 				_cursor->show(cursorControl);
 				cursorControl->setActorIndexTo1();
-				if (cursorData._item10._playSound48)
+				if (cursorData._verbState._isBubbleVisible)
 					playSoundEffect(4);
-				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
 			}
 			cursorData._currOverlappedObjectId = 0;
 		}
@@ -685,8 +685,8 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 	} else if (cursorData._currOverlappedObjectId) {
 		if (_vm->_input->pollEvent(kEventLeftClick)) {
 			cursorData._idleCtr = 0;
-			if (runCause(cursorControl, cursorData, cursorData._item10._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId, 1)) {
-				resetItem10(cursorControl->_objectId, &cursorData._item10);
+			if (runCause(cursorControl, cursorData, cursorData._verbState._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId, 1)) {
+				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
 				cursorData._currOverlappedObjectId = 0;
 				cursorControl->setActorIndexTo1();
 			}
@@ -696,9 +696,9 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 			if (cursorData._holdingObjectId) {
 				runCause(cursorControl, cursorData, 0x1B000B, 0, 0x40003, 0);
 				cursorData._currOverlappedObjectId = 0;
-			} else if (findVerbId(&cursorData._item10, cursorData._currOverlappedObjectId, 0, verbId) &&
+			} else if (findVerbId(&cursorData._verbState, cursorData._currOverlappedObjectId, 0, verbId) &&
 				runCause(cursorControl, cursorData, verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId, 1)) {
-				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
 				cursorData._currOverlappedObjectId = 0;
 				cursorControl->setActorIndexTo1();
 			}
@@ -709,7 +709,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 			runCause(cursorControl, cursorData, 0x1B0002, 0, 0x40003, 0);
 		} else if (_vm->_input->pollEvent(kEventInventory)) {
 			cursorData._idleCtr = 0;
-			if (cursorData._item10._field58 <= 1)
+			if (cursorData._verbState._minPriority <= 1)
 				runCause(cursorControl, cursorData, cursorData._holdingObjectId != 0 ? 0x1B000B : 0x1B0004, 0, 0x40003, 0);
 		}
 	}
@@ -779,18 +779,18 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 		foundOverlapped = false;
 	else {
 		foundOverlapped = _vm->_controls->getOverlappedObjectAccurate(cursorControl, cursorPos,
-			&overlappedControl, cursorData._item10._field58);
+			&overlappedControl, cursorData._verbState._minPriority);
 	}
 
 	if (foundOverlapped) {
 		if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
-			resetItem10(cursorControl->_objectId, &cursorData._item10);
-			int value = _cursor->findStruct8bsValue(overlappedControl->_objectId);
+			hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
+			int value = _cursor->getObjectInteractMode(overlappedControl->_objectId);
 			if (value == 2 || value == 3 || value == 4 || value == 5 || value == 6 || value == 7) {
 				if (cursorData._mode != 3) {
 					_cursor->saveInfo();
 					cursorData._mode = 3;
-					cursorData._item10._verbId = 0x1B0006;
+					cursorData._verbState._verbId = 0x1B0006;
 					cursorData._holdingObjectId = 0;
 				}
 				switch (value) {
@@ -821,10 +821,10 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 				_cursor->show(cursorControl);
 				cursorControl->setActorIndexTo2();
 				if (overlappedControl->_objectId) {
-					cursorData._item10._verbId = 0x1B0003;
+					cursorData._verbState._verbId = 0x1B0003;
 					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
 				} else {
-					cursorData._item10._verbId = 0x1B0002;
+					cursorData._verbState._verbId = 0x1B0002;
 					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
 				}
 			}
@@ -835,7 +835,7 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 				_cursor->restoreInfo();
 			_cursor->show(cursorControl);
 			cursorControl->setActorIndexTo1();
-			resetItem10(cursorControl->_objectId, &cursorData._item10);
+			hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
 		}
 		cursorData._currOverlappedObjectId = 0;
 	}
@@ -848,7 +848,7 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 
 			uint32 outSceneId, outVerbId, outObjectId2, outObjectId;
 			bool success = getShooterCause(_vm->getCurrentScene(),
-				cursorData._item10._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId,
+				cursorData._verbState._verbId, cursorData._holdingObjectId, cursorData._currOverlappedObjectId,
 				outSceneId, outVerbId, outObjectId2, outObjectId);
 
 			uint index = (uint)_vm->getRandom(2);
@@ -878,15 +878,15 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 				}
 				cursorData._causeThreadId1 = _vm->causeTrigger(outSceneId, outVerbId, outObjectId2, outObjectId, threadId);
 				cursorData._causeThreadId2 = cursorData._causeThreadId1;
-				resetItem10(cursorControl->_objectId, &cursorData._item10);
+				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
 				cursorData._currOverlappedObjectId = 0;
 				cursorControl->setActorIndexTo1();
 			}
 
-		} else if (_vm->_input->pollEvent(kEventRightClick) && cursorData._item10._playSound48 == 1 && !cursorData._item10._flag56) {
-			// TODO I don't think this is used; _playSound48 seems to be always 0 here
+		} else if (_vm->_input->pollEvent(kEventRightClick) && cursorData._verbState._isBubbleVisible && !cursorData._verbState._flag56) {
+			// TODO I don't think this is used; _isBubbleVisible seems to be always 0 here
 			debug("Cursor_sub_10004DD0 TODO");
-			// TODO Cursor_sub_10004DD0(controla->objectId, cursorData->currOverlappedObjectId, cursorData->holdingObjectId, &cursorData->item10);
+			// TODO Cursor_sub_10004DD0(controla->objectId, cursorData->currOverlappedObjectId, cursorData->holdingObjectId, &cursorData->verbState);
 		}
 
 	} else if (_vm->_input->pollEvent(kEventLeftClick)) {
@@ -921,7 +921,7 @@ bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32
 	static const uint32 kVerbIdsH8[] = {0x001B0003, 0x001B0001, 0};
 	
 	const uint32 *verbIds;
-	int value = _cursor->findStruct8bsValue(overlappedObjectId);
+	int value = _cursor->getObjectInteractMode(overlappedObjectId);
   
 	if (holdingObjectId) {
 		if (value == 9)
@@ -1034,7 +1034,7 @@ void BbdouSpecialCode::startHoldingObjectId(uint32 objectId1, uint32 holdingObje
 	if (_cursor->_data._visibleCtr > 0)
 		_cursor->show(control);
 	_cursor->_data._mode = 2;
-	_cursor->_data._item10._verbId = 0x1B0003;
+	_cursor->_data._verbState._verbId = 0x1B0003;
 	if (!doPlaySound)
 		playSoundEffect(5);
 	_inventory->removeInventoryItem(holdingObjectId);
@@ -1049,7 +1049,7 @@ void BbdouSpecialCode::stopHoldingObjectId(uint32 objectId1, bool doPlaySound) {
 		playSoundEffect(6);
 	if (_cursor->_data._visibleCtr > 0)
 		_cursor->show(control);
-	_cursor->_data._item10._verbId = 0x1B0001;
+	_cursor->_data._verbState._verbId = 0x1B0001;
 	if (_cursor->_data._mode == 3)
 		holdingObjectId = _cursor->_data._holdingObjectId2;
 	if (holdingObjectId)
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 40ed53d..567c956 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -36,7 +36,7 @@ class BbdouCursor;
 class BbdouFoodCtl;
 class BbdouInventory;
 struct CursorData;
-struct Item10;
+struct VerbState;
 
 typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
 
@@ -152,7 +152,7 @@ public:
 	void spcRunCause(OpCall &opCall);
 
 	void playSoundEffect(int soundIndex);
-	void resetItem10(uint32 objectId, Item10 *item10);
+	void hideVerbBubble(uint32 objectId, VerbState *verbState);
 	void startHoldingObjectId(uint32 objectId1, uint32 holdingObjectId, bool doPlaySound);
 	void stopHoldingObjectId(uint32 objectId1, bool doPlaySound);
 	bool isHoldingObjectId(uint32 objectId);
@@ -163,8 +163,8 @@ protected:
 	void setCursorControlRoutine(uint32 objectId, int num);
 	Common::Point getBackgroundCursorPos(Common::Point cursorPos);
 	void showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
-		Item10 *item10, uint32 progResKeywordId);
-	bool findVerbId(Item10 *item10, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
+		VerbState *verbState, uint32 progResKeywordId);
+	bool findVerbId(VerbState *verbState, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
 	void cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime);
 	void cursorCrosshairControlRoutine(Control *cursorControl, uint32 deltaTime);
 	bool testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId);


Commit: 41c7a99262d73658f86dec5fadbd01de7a4fa9bf
    https://github.com/scummvm/scummvm/commit/41c7a99262d73658f86dec5fadbd01de7a4fa9bf
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Extract code to new class ObjectInteractModeMap

Changed paths:
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_cursor.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index a6bc4a9..605919a 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -33,6 +33,8 @@ namespace Illusions {
 // NOTE It's assumed there's only one game cursor object
 // The original stores the _data inside the actor, here it's inside the Cursor class.
 
+// BbdouCursor
+
 BbdouCursor::BbdouCursor(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
 	: _vm(vm), _bbdou(bbdou) {
 }
@@ -145,36 +147,6 @@ uint32 BbdouCursor::findCursorSequenceId(uint32 objectId) {
 	return 0;
 }
 
-void BbdouCursor::setObjectInteractMode(uint32 objectId, int value) {
-	ObjectInteractMode *objectInteractMode = 0;
-	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
-		if (_objectVerbs[i]._objectId == objectId) {
-			objectInteractMode = &_objectVerbs[i];
-			break;
-		}
-	if (!objectInteractMode) {
-		for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
-			if (_objectVerbs[i]._objectId == 0) {
-				objectInteractMode = &_objectVerbs[i];
-				break;
-			}
-	}
-	if (value != 11) {
-		objectInteractMode->_objectId = objectId;
-		objectInteractMode->_interactMode = value;
-	} else if (objectInteractMode->_objectId == objectId) {
-		objectInteractMode->_objectId = 0;
-		objectInteractMode->_interactMode = 0;
-	}
-}
-
-int BbdouCursor::getObjectInteractMode(uint32 objectId) {
-	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
-		if (_objectVerbs[i]._objectId == objectId)
-			return _objectVerbs[i]._interactMode;
-	return 11;
-}
-
 bool BbdouCursor::updateTrackingCursor(Control *control) {
 	uint32 sequenceId;
 	if (getTrackingCursorSequenceId(control, sequenceId)) {
diff --git a/engines/illusions/bbdou/bbdou_cursor.h b/engines/illusions/bbdou/bbdou_cursor.h
index c3f023a..49082a0 100644
--- a/engines/illusions/bbdou/bbdou_cursor.h
+++ b/engines/illusions/bbdou/bbdou_cursor.h
@@ -69,12 +69,6 @@ struct CursorSequence {
 	CursorSequence() : _objectId(0), _sequenceId(0) {}
 };
 
-struct ObjectInteractMode {
-	uint32 _objectId;
-	int _interactMode;
-	ObjectInteractMode() : _objectId(0), _interactMode(0) {}
-};
-
 const uint kMaxCursorSequences = 100;
 
 class BbdouCursor {
@@ -87,8 +81,6 @@ public:
 	void reset(uint32 objectId);
 	void addCursorSequenceId(uint32 objectId, uint32 sequenceId);
 	uint32 findCursorSequenceId(uint32 objectId);
-	void setObjectInteractMode(uint32 objectId, int value);
-	int getObjectInteractMode(uint32 objectId);
 	bool updateTrackingCursor(Control *control);
 	void saveInfo();
 	void restoreInfo();
@@ -104,7 +96,6 @@ public:
 	Control *_control;
 	CursorData _data;
 	CursorSequence _cursorSequences[kMaxCursorSequences];
-	ObjectInteractMode _objectVerbs[512];
 	void resetActiveVerbs();
 	void show(Control *control);
 	void hide(uint32 objectId);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 540a31e..ed1ce5c 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -107,6 +107,41 @@ void RadarMicrophoneThread::initZones() {
 	_currZoneIndex = 0;
 }
 
+// ObjectInteractModeMap
+
+ObjectInteractModeMap::ObjectInteractModeMap() {
+}
+
+void ObjectInteractModeMap::setObjectInteractMode(uint32 objectId, int value) {
+	ObjectInteractMode *objectInteractMode = 0;
+	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+		if (_objectVerbs[i]._objectId == objectId) {
+			objectInteractMode = &_objectVerbs[i];
+			break;
+		}
+	if (!objectInteractMode) {
+		for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+			if (_objectVerbs[i]._objectId == 0) {
+				objectInteractMode = &_objectVerbs[i];
+				break;
+			}
+	}
+	if (value != 11) {
+		objectInteractMode->_objectId = objectId;
+		objectInteractMode->_interactMode = value;
+	} else if (objectInteractMode->_objectId == objectId) {
+		objectInteractMode->_objectId = 0;
+		objectInteractMode->_interactMode = 0;
+	}
+}
+
+int ObjectInteractModeMap::getObjectInteractMode(uint32 objectId) {
+	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+		if (_objectVerbs[i]._objectId == objectId)
+			return _objectVerbs[i]._interactMode;
+	return 11;
+}
+
 // BbdouSpecialCode
 
 BbdouSpecialCode::BbdouSpecialCode(IllusionsEngine_BBDOU *vm)
@@ -252,7 +287,7 @@ void BbdouSpecialCode::spcSetObjectInteractMode(OpCall &opCall) {
 	ARG_SKIP(4);
 	ARG_UINT32(objectId);
 	ARG_INT16(value);
-	_cursor->setObjectInteractMode(objectId, value);
+	_objectInteractModeMap.setObjectInteractMode(objectId, value);
 	_vm->notifyThreadId(opCall._threadId);
 }
 
@@ -530,7 +565,7 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 	verbState->_objectIds[1] = _bubble->addItem(0, 0x6005A);
 	verbState->_index = 0;
 	
-	int value = _cursor->getObjectInteractMode(overlappedControl->_objectId);
+	int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
 	if (holdingObjectId) {
 		verbState->_verbId = 0x1B0003;
 	} else if (value == 9) {
@@ -614,7 +649,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 				if (cursorData._verbState._isBubbleVisible)
 					playSoundEffect(4);
 				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
-				int value = _cursor->getObjectInteractMode(overlappedControl->_objectId);
+				int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
 				if (!testValueRange(value)) {
 					if (cursorData._mode == 3)
 						_cursor->restoreInfo();
@@ -785,7 +820,7 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 	if (foundOverlapped) {
 		if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
 			hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
-			int value = _cursor->getObjectInteractMode(overlappedControl->_objectId);
+			int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
 			if (value == 2 || value == 3 || value == 4 || value == 5 || value == 6 || value == 7) {
 				if (cursorData._mode != 3) {
 					_cursor->saveInfo();
@@ -921,7 +956,7 @@ bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32
 	static const uint32 kVerbIdsH8[] = {0x001B0003, 0x001B0001, 0};
 	
 	const uint32 *verbIds;
-	int value = _cursor->getObjectInteractMode(overlappedObjectId);
+	int value = _objectInteractModeMap.getObjectInteractMode(overlappedObjectId);
   
 	if (holdingObjectId) {
 		if (value == 9)
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 567c956..ad9d3b7 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -89,6 +89,21 @@ struct ShooterStatus {
 	bool flag;
 };
 
+struct ObjectInteractMode {
+	uint32 _objectId;
+	int _interactMode;
+	ObjectInteractMode() : _objectId(0), _interactMode(0) {}
+};
+
+class ObjectInteractModeMap {
+public:
+	ObjectInteractModeMap();
+	void setObjectInteractMode(uint32 objectId, int value);
+	int getObjectInteractMode(uint32 objectId);
+protected:
+	ObjectInteractMode _objectVerbs[512];
+};
+
 class BbdouSpecialCode : public SpecialCode {
 public:
 	BbdouSpecialCode(IllusionsEngine_BBDOU *vm);
@@ -115,6 +130,8 @@ public:
 	uint _shooterObjectIdIndex;
 
 	BbdouFoodCtl *_foodCtl;
+	
+	ObjectInteractModeMap _objectInteractModeMap;
 
 	// Special code interface functions
 	void spcInitCursor(OpCall &opCall);


Commit: ad558f8c684662e8dbfdf96dcf1e244d5a0b2451
    https://github.com/scummvm/scummvm/commit/ad558f8c684662e8dbfdf96dcf1e244d5a0b2451
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Rename more

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index ed1ce5c..bc6ed03 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -512,7 +512,7 @@ void BbdouSpecialCode::hideVerbBubble(uint32 objectId, VerbState *verbState) {
 	_vm->_input->discardAllEvents();
 }
 
-bool BbdouSpecialCode::testValueRange(int value) {
+bool BbdouSpecialCode::testInteractModeRange(int value) {
 	return value >= 2 && value <= 7;
 }
 
@@ -649,8 +649,8 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 				if (cursorData._verbState._isBubbleVisible)
 					playSoundEffect(4);
 				hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
-				int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
-				if (!testValueRange(value)) {
+				int interactMode = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
+				if (!testInteractModeRange(interactMode)) {
 					if (cursorData._mode == 3)
 						_cursor->restoreInfo();
 					_cursor->show(cursorControl);
@@ -659,7 +659,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 						cursorData._overlappedObjectId = overlappedControl->_objectId;
 						runCause(cursorControl, cursorData, 0x1B0009, 0, overlappedControl->_objectId, 0);
 					}
-					if (value == 10) {
+					if (interactMode == 10) {
 						if (cursorData._holdingObjectId) {
 							cursorData._verbState._verbId = 0x1B0003;
 							cursorData._currOverlappedObjectId = overlappedControl->_objectId;
@@ -682,7 +682,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 						cursorData._verbState._verbId = 0x1B0006;
 						cursorData._holdingObjectId = 0;
 					}
-					cursorData._sequenceId = _cursor->getSequenceId1(value);
+					cursorData._sequenceId = _cursor->getSequenceId1(interactMode);
 					_cursor->show(cursorControl);
 					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
 				}
@@ -820,15 +820,27 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 	if (foundOverlapped) {
 		if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
 			hideVerbBubble(cursorControl->_objectId, &cursorData._verbState);
-			int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
-			if (value == 2 || value == 3 || value == 4 || value == 5 || value == 6 || value == 7) {
+			int interactMode = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
+			if (!testInteractModeRange(interactMode)) {
+				if (cursorData._mode == 3)
+					_cursor->restoreInfo();
+				_cursor->show(cursorControl);
+				cursorControl->setActorIndexTo2();
+				if (overlappedControl->_objectId) {
+					cursorData._verbState._verbId = 0x1B0003;
+					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+				} else {
+					cursorData._verbState._verbId = 0x1B0002;
+					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
+				}
+			} else {
 				if (cursorData._mode != 3) {
 					_cursor->saveInfo();
 					cursorData._mode = 3;
 					cursorData._verbState._verbId = 0x1B0006;
 					cursorData._holdingObjectId = 0;
 				}
-				switch (value) {
+				switch (interactMode) {
 				case 2:
 					cursorData._sequenceId = 0x60010;
 					break;
@@ -850,18 +862,6 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 				}
 				_cursor->show(cursorControl);
 				cursorData._currOverlappedObjectId = overlappedControl->_objectId;
-			} else {
-				if (cursorData._mode == 3)
-					_cursor->restoreInfo();
-				_cursor->show(cursorControl);
-				cursorControl->setActorIndexTo2();
-				if (overlappedControl->_objectId) {
-					cursorData._verbState._verbId = 0x1B0003;
-					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
-				} else {
-					cursorData._verbState._verbId = 0x1B0002;
-					cursorData._currOverlappedObjectId = overlappedControl->_objectId;
-				}
 			}
 		}
 	} else {
@@ -956,19 +956,19 @@ bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32
 	static const uint32 kVerbIdsH8[] = {0x001B0003, 0x001B0001, 0};
 	
 	const uint32 *verbIds;
-	int value = _objectInteractModeMap.getObjectInteractMode(overlappedObjectId);
+	int interactMode = _objectInteractModeMap.getObjectInteractMode(overlappedObjectId);
   
 	if (holdingObjectId) {
-		if (value == 9)
+		if (interactMode == 9)
 			verbIds = kVerbIdsH9;
-		else if (value == 9)
+		else if (interactMode == 9)
 			verbIds = kVerbIdsH8;
 		else
 			verbIds = kVerbIdsHE;
 	} else {
-		if (value == 9)
+		if (interactMode == 9)
 			verbIds = kVerbIdsE9;
-		else if (value == 8)
+		else if (interactMode == 8)
 			verbIds = kVerbIdsE8;
 		else
 			verbIds = kVerbIdsEE;
@@ -1030,7 +1030,6 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 	
 	if (!success)
 		return false;
-	
 
 	_cursor->hide(cursorControl->_objectId);
 
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index ad9d3b7..5d11e3b 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -176,7 +176,7 @@ public:
 
 protected:
 	// Internal functions
-	bool testValueRange(int value);
+	bool testInteractModeRange(int value);
 	void setCursorControlRoutine(uint32 objectId, int num);
 	Common::Point getBackgroundCursorPos(Common::Point cursorPos);
 	void showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,


Commit: 9447d42fd71ce092d70fad416caf2babacf00dcd
    https://github.com/scummvm/scummvm/commit/9447d42fd71ce092d70fad416caf2babacf00dcd
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Remove experimental sprite smoothing code

Changed paths:
    engines/illusions/screen.cpp


diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index d8dd004..8add55e 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -831,27 +831,14 @@ void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	//debug("Screen::drawSurface20");
 }
 
-//#define TEST_SMOOTHING
-#ifdef TEST_SMOOTHING
-static uint16 average(const uint16 a, const uint16 b) {
-	byte r1, g1, b1, r2, g2, b2;
-	g_system->getScreenFormat().colorToRGB(a, r1, g1, b1);
-	g_system->getScreenFormat().colorToRGB(b, r2, g2, b2);
-//	return g_system->getScreenFormat().RGBToColor((r1 + r1 + r2) / 3, (g1 + g1 + g2) / 3, (b1 + b1 + b2) / 3);
-	return g_system->getScreenFormat().RGBToColor((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2);
-}
-#endif
-
 void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
 	// Scaled
 	const int dstWidth = dstRect.width(), dstHeight = dstRect.height();
 	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
 	const int errYStart = srcHeight / dstHeight;
 	const int errYIncr = srcHeight % dstHeight;
-//	const int midY = dstHeight / 2;
 	const int errXStart = srcWidth / dstWidth;
 	const int errXIncr = srcWidth % dstWidth;
-//	const int midX = dstWidth / 2;
 	int h = dstHeight, errY = 0, skipY, srcY = srcRect.top;
 	byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top);
 	skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1;
@@ -864,17 +851,8 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co
 		byte *dstRow = dst; 
 		while (w-- > 0) {
 			uint16 pixel = READ_LE_UINT16(src);
-			if (pixel != _colorKey1) {
-#ifdef TEST_SMOOTHING
-				if (errX >= midX) {
-					uint16 npixel = READ_LE_UINT16(src + 2);
-					if (npixel == _colorKey1)
-						npixel = READ_LE_UINT16(dstRow);
-					pixel = average(pixel, npixel);
-				}
-#endif
+			if (pixel != _colorKey1)
 				WRITE_LE_UINT16(dstRow, pixel);
-			}
 			dstRow += 2;
 			src += 2 * errXStart;
 			errX += errXIncr;


Commit: 2bd13865285058d72e13150ab64462dbbe23163c
    https://github.com/scummvm/scummvm/commit/2bd13865285058d72e13150ab64462dbbe23163c
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move palette code from Screen to new ScreenPalette class

Changed paths:
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/screentext.cpp
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index bacd53e..15b1031 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -152,6 +152,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
 
 	_screen = new Screen(this, 640, 480, 16);
+	_screenPalette = new NullScreenPalette();
 	_screenText = new ScreenText(this);
 	_input = new Input();	
 	_actorInstances = new ActorInstanceList(this);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 8f28a99..a9f6b6e 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -104,6 +104,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_resSys->addResourceLoader(0x00190000, new GenericResourceLoader(this));
 
 	_screen = new Screen(this, 320, 200, 8);
+	_screenPalette = new ScreenPalette(this);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
 	_actorInstances = new ActorInstanceList(this);
@@ -221,6 +222,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _actorInstances;
 	delete _input;
 	delete _screenText;
+	delete _screenPalette;
 	delete _screen;
 	delete _resSys;
 	delete _resReader;
@@ -283,7 +285,7 @@ void IllusionsEngine_Duckman::initUpdateFunctions() {
 int IllusionsEngine_Duckman::updateScript(uint flags) {
 	// TODO Some more stuff
 
-	if (_screen->isDisplayOn() && !_screen->isFaderActive() && _pauseCtr == 0) {
+	if (_screen->isDisplayOn() && !_screenPalette->isFaderActive() && _pauseCtr == 0) {
 		if (_input->pollEvent(kEventAbort)) {
 			startScriptThread(0x00020342, 0);
 		} else if (_input->pollEvent(kEventF1)) {
@@ -362,6 +364,39 @@ void IllusionsEngine_Duckman::startFader(int duration, int minValue, int maxValu
 	_fader->_notifyThreadId = threadId;
 }
 
+void IllusionsEngine_Duckman::updateFader() {
+	if (_fader && !_fader->_paused && _fader->_active) {
+		int32 currTime = getCurrentTime();
+		int32 currDuration = currTime - _fader->_startTime;
+		if (currDuration) {
+			int newValue;
+			if (currDuration >= _fader->_duration) {
+				newValue = _fader->_maxValue;
+			} else {
+				newValue = (currDuration * (_fader->_maxValue - _fader->_minValue) / _fader->_duration) + _fader->_minValue;
+			}
+			if (_fader->_currValue != newValue) {
+				_fader->_currValue = newValue;
+				_screenPalette->setFader(newValue, _fader->_firstIndex, _fader->_lastIndex);
+			}
+			if (_fader->_currValue == _fader->_maxValue) {
+				_fader->_active = false;
+				notifyThreadId(_fader->_notifyThreadId);
+			}
+		}
+	}
+}
+
+void IllusionsEngine_Duckman::pauseFader() {
+	_fader->_paused = true;
+	_fader->_startTime = getCurrentTime() - _fader->_startTime;
+}
+
+void IllusionsEngine_Duckman::unpauseFader() {
+	_fader->_startTime = getCurrentTime() - _fader->_startTime;
+	_fader->_paused = false;
+}
+
 void IllusionsEngine_Duckman::setDefaultTextCoords() {
 	WidthHeight dimensions;
 	dimensions._width = 300;
@@ -1145,7 +1180,7 @@ bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 calli
 
 bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
 	// TODO
-	const char *fileName = getSavegameFilename(slotNum);
+	// const char *fileName = getSavegameFilename(slotNum);
 	bool success = false;//savegame(fileName, _savegameDescription.c_str());
 	return success;
 }
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index f19a659..daa1b06 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -81,7 +81,7 @@ public:
 protected:
 	virtual Common::Error run();
 	virtual bool hasFeature(EngineFeature f) const;
-public:	
+public:
 
 	// TODO ActiveScenes _activeScenes;
 	uint32 _prevSceneId;
@@ -112,6 +112,9 @@ public:
 	int updateScreenShaker(uint flags);
 
 	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
+	void updateFader();
+	void pauseFader();
+	void unpauseFader();
 
 	void setDefaultTextCoords();
 
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index e22ef0c..9bb40c3 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -206,14 +206,14 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	return kUFNext;
 }
 
-int IllusionsEngine::updateSprites(uint flags) {
-	_screen->updateSprites();
-	_screen->updatePalette();
+int IllusionsEngine::updateSoundMan(uint flags) {
+	_soundMan->update();
 	return kUFNext;
 }
 
-int IllusionsEngine::updateSoundMan(uint flags) {
-	_soundMan->update();
+int IllusionsEngine::updateSprites(uint flags) {
+	_screen->updateSprites();
+	_screenPalette->updatePalette();
 	return kUFNext;
 }
 
@@ -267,39 +267,6 @@ bool IllusionsEngine::isSoundActive() {
 	return true;
 }
 
-void IllusionsEngine::updateFader() {
-	if (_fader && !_fader->_paused && _fader->_active) {
-		int32 currTime = getCurrentTime();
-		int32 currDuration = currTime - _fader->_startTime;
-		if (currDuration) {
-			int newValue;
-			if (currDuration >= _fader->_duration) {
-				newValue = _fader->_maxValue;
-			} else {
-				newValue = (currDuration * (_fader->_maxValue - _fader->_minValue) / _fader->_duration) + _fader->_minValue;
-			}
-			if (_fader->_currValue != newValue) {
-				_fader->_currValue = newValue;
-				_screen->setFader(newValue, _fader->_firstIndex, _fader->_lastIndex);
-			}
-			if (_fader->_currValue == _fader->_maxValue) {
-				_fader->_active = false;
-				notifyThreadId(_fader->_notifyThreadId);
-			}
-		}
-	}
-}
-
-void IllusionsEngine::pauseFader() {
-	_fader->_paused = true;
-	_fader->_startTime = getCurrentTime() - _fader->_startTime;
-}
-
-void IllusionsEngine::unpauseFader() {
-	_fader->_startTime = getCurrentTime() - _fader->_startTime;
-	_fader->_paused = false;
-}
-
 void IllusionsEngine::setCurrFontId(uint32 fontId) {
 	_fontId = fontId;
 }
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 836282d..40ed327 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -77,6 +77,7 @@ class TalkInstanceList;
 class ThreadList;
 class UpdateFunctions;
 class GameState;
+class ScreenPaletteBase;
 
 enum {
 	kGameIdBBDOU   = 1,
@@ -108,6 +109,7 @@ public:
 	void updateEvents();
 
 	Screen *_screen;
+	ScreenPaletteBase *_screenPalette;	
 	ScreenText *_screenText;
 	Input *_input;
 	ActorInstanceList *_actorInstances;
@@ -157,8 +159,8 @@ public:
 	int updateActors(uint flags);
 	int updateSequences(uint flags);
 	int updateGraphics(uint flags);
-	int updateSprites(uint flags);
 	int updateSoundMan(uint flags);
+	int updateSprites(uint flags);
 
 	uint32 getElapsedUpdateTime();
 	Common::Point *getObjectActorPositionPtr(uint32 objectId);
@@ -168,9 +170,9 @@ public:
 	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
 	bool isSoundActive();
 
-	void updateFader();
-	void pauseFader();
-	void unpauseFader();
+	virtual void updateFader() {};
+	virtual void pauseFader() {};
+	virtual void unpauseFader() {};
 
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index a959196..4df66e1 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -404,7 +404,7 @@ void BackgroundInstance::load(Resource *resource) {
 
 	if (_bgRes->_palettesCount > 0) {
 		Palette *palette = _bgRes->getPalette(_bgRes->_paletteIndex - 1);
-		_vm->_screen->setPalette(palette->_palette, 1, palette->_count);
+		_vm->_screenPalette->setPalette(palette->_palette, 1, palette->_count);
 	}
 
 }
@@ -425,7 +425,7 @@ void BackgroundInstance::pause() {
 		_vm->setDefaultTextCoords();
 		_vm->_camera->getActiveState(_savedCameraState);
 		_savedPalette = new byte[1024];
-		_vm->_screen->getPalette(_savedPalette);
+		_vm->_screenPalette->getPalette(_savedPalette);
 		freeSurface();
 	}
 }
@@ -435,7 +435,7 @@ void BackgroundInstance::unpause() {
 	if (_pauseCtr <= 0) {
 		registerResources();
 		initSurface();
-		_vm->_screen->setPalette(_savedPalette, 1, 256);
+		_vm->_screenPalette->setPalette(_savedPalette, 1, 256);
 		delete[] _savedPalette;
 		_savedPalette = 0;
 		// TODO _vm->_screen->_fadeClear();
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 8add55e..3cbdd91 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -214,137 +214,27 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	return true;
 }
 
-// Screen
+// Palette
 
-Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
-	: _vm(vm), _colorKey1(0), _colorKey2(0) {
-	_displayOn = true;
-	_decompressQueue = new SpriteDecompressQueue(this);
-	_drawQueue = new SpriteDrawQueue(this);
-	if (bpp == 8) {
-		initGraphics(width, height, false);
-	} else {
-		Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
-		initGraphics(width, height, true, &pixelFormat16);
-	}
+ScreenPalette::ScreenPalette(IllusionsEngine *vm)
+	: _vm(vm), _needRefreshPalette(false), _isFaderActive(false) {
 
-	_backSurface = allocSurface(width, height);
-
-	_needRefreshPalette = false;
 	memset(_mainPalette, 0, sizeof(_mainPalette));
-
-	_isFaderActive = false;
-	_isScreenOffsetActive = false;
-
-}
-
-Screen::~Screen() {
-	delete _drawQueue;
-	delete _decompressQueue;
-	_backSurface->free();
-	delete _backSurface;
-}
-
-Graphics::Surface *Screen::allocSurface(int16 width, int16 height) {
-	Graphics::Surface *surface = new Graphics::Surface();
-	surface->create(width, height, _vm->_system->getScreenFormat());
-	return surface; 
-}
-
-Graphics::Surface *Screen::allocSurface(SurfInfo &surfInfo) {
-	return allocSurface(surfInfo._dimensions._width, surfInfo._dimensions._height);
-}
-
-bool Screen::isDisplayOn() {
-	return _displayOn;
-}
-
-void Screen::setDisplayOn(bool isOn) {
-	_displayOn = isOn;
-	// TODO Clear screen when off
-}
-
-void Screen::setScreenOffset(Common::Point offsPt) {
-	if (offsPt.x != 0 || offsPt.y != 0) {
-		_isScreenOffsetActive = true;
-		_screenOffsetPt = offsPt;
-	} else {
-		_isScreenOffsetActive = false;
-	}
-}
-
-void Screen::updateSprites() {
-	_decompressQueue->decompressAll();
-	// NOTE Skipped doShiftBrightness and related as it seems to be unused
-	_drawQueue->drawAll();
-	if (_isScreenOffsetActive)
-		clearScreenOffsetAreas();
-	if (!_displayOn) // TODO Check if a video is playing then don't do it
-		_backSurface->fillRect(Common::Rect(_backSurface->w, _backSurface->h), 0);
-	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
-}
-
-void Screen::clearScreenOffsetAreas() {
-	int16 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
-	if (_screenOffsetPt.x < 0) {
-		x1 = _backSurface->w + _screenOffsetPt.x;
-		x2 = _backSurface->w;
-	} else if (_screenOffsetPt.x > 0) {
-		x1 = 0;
-		x2 = _screenOffsetPt.x;
-	}
-	if (_screenOffsetPt.y < 0) {
-		y1 = _backSurface->h + _screenOffsetPt.y;
-		y2 = _backSurface->h;
-	} else if (_screenOffsetPt.y > 0) {
-		y1 = 0;
-		y2 = _screenOffsetPt.y;
-	}
-	_backSurface->fillRect(Common::Rect(0, y1, _backSurface->w, y2), 0);
-	_backSurface->fillRect(Common::Rect(x1, 0, x2, _backSurface->h), 0);
-}
-
-void Screen::decompressSprite(SpriteDecompressQueueItem *item) {
-	switch (_backSurface->format.bytesPerPixel) {
-	case 1:
-		decompressSprite8(item);
-		break;
-	case 2:
-		decompressSprite16(item);
-		break;
-	default:
-		break;
-	}
 }
 
-void Screen::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
-	switch (_backSurface->format.bytesPerPixel) {
-	case 1:
-		drawSurface8(dstRect, surface, srcRect, scale, flags);
-		break;
-	case 2:
-		drawSurface16(dstRect, surface, srcRect, scale, flags);
-		break;
-	default:
-		break;
-	}
-}
-
-void Screen::setPalette(byte *colors, uint start, uint count) {
-	if (_backSurface->format.bytesPerPixel == 1) {
-		byte *dstPal = &_mainPalette[3 * (start - 1)];
-		for (uint i = 0; i < count; ++i) {
-			*dstPal++ = *colors++;
-			*dstPal++ = *colors++;
-			*dstPal++ = *colors++;
-			++colors;
-		}
-		buildColorTransTbl();
-		_needRefreshPalette = true;
+void ScreenPalette::setPalette(byte *colors, uint start, uint count) {
+	byte *dstPal = &_mainPalette[3 * (start - 1)];
+	for (uint i = 0; i < count; ++i) {
+		*dstPal++ = *colors++;
+		*dstPal++ = *colors++;
+		*dstPal++ = *colors++;
+		++colors;
 	}
+	buildColorTransTbl();
+	_needRefreshPalette = true;
 }
 
-void Screen::setPaletteEntry(int16 index, byte r, byte g, byte b) {
+void ScreenPalette::setPaletteEntry(int16 index, byte r, byte g, byte b) {
 	byte colors[4];
 	colors[0] = r;
 	colors[1] = g;
@@ -352,7 +242,7 @@ void Screen::setPaletteEntry(int16 index, byte r, byte g, byte b) {
 	setPalette(colors, index, 1);
 }
 
-void Screen::getPalette(byte *colors) {
+void ScreenPalette::getPalette(byte *colors) {
 	byte *srcPal = _mainPalette;
 	for (uint i = 0; i < 256; ++i) {
 		*colors++ = *srcPal++;
@@ -362,7 +252,7 @@ void Screen::getPalette(byte *colors) {
 	}
 }
 
-void Screen::shiftPalette(int16 fromIndex, int16 toIndex) {
+void ScreenPalette::shiftPalette(int16 fromIndex, int16 toIndex) {
 	byte r, g, b;
 	if (toIndex > fromIndex) {
 		r = _mainPalette[3 * toIndex + 0];
@@ -397,7 +287,7 @@ void Screen::shiftPalette(int16 fromIndex, int16 toIndex) {
 	_needRefreshPalette = true;
 }
 
-void Screen::updatePalette() {
+void ScreenPalette::updatePalette() {
 	if (_needRefreshPalette) {
 		if (_isFaderActive) {
 			updateFaderPalette();
@@ -409,7 +299,7 @@ void Screen::updatePalette() {
 	}
 }
 
-void Screen::updateFaderPalette() {
+void ScreenPalette::updateFaderPalette() {
 	if (_newFaderValue >= 255) {
 		_newFaderValue -= 256;
 		for (int i = _firstFaderIndex; i <= _lastFaderIndex; ++i) {
@@ -432,7 +322,7 @@ void Screen::updateFaderPalette() {
 	}
 }
 
-void Screen::setFader(int newValue, int firstIndex, int lastIndex) {
+void ScreenPalette::setFader(int newValue, int firstIndex, int lastIndex) {
 	if (newValue == 255) {
 		_isFaderActive = false;
 		_needRefreshPalette = true;
@@ -445,7 +335,11 @@ void Screen::setFader(int newValue, int firstIndex, int lastIndex) {
 	}
 }
 
-void Screen::buildColorTransTbl() {
+void ScreenPalette::setSystemPalette(byte *palette) {
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+}
+
+void ScreenPalette::buildColorTransTbl() {
 	const int cr = _mainPalette[3 * 1 + 0];
 	const int cg = _mainPalette[3 * 1 + 1];
 	const int cb = _mainPalette[3 * 1 + 2];
@@ -469,6 +363,118 @@ void Screen::buildColorTransTbl() {
 	}
 }
 
+// Screen
+
+Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
+	: _vm(vm), _colorKey1(0), _colorKey2(0) {
+	_displayOn = true;
+	_decompressQueue = new SpriteDecompressQueue(this);
+	_drawQueue = new SpriteDrawQueue(this);
+	if (bpp == 8) {
+		initGraphics(width, height, false);
+	} else {
+		Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
+		initGraphics(width, height, true, &pixelFormat16);
+	}
+
+	_backSurface = allocSurface(width, height);
+
+	_isScreenOffsetActive = false;
+
+}
+
+Screen::~Screen() {
+	delete _drawQueue;
+	delete _decompressQueue;
+	_backSurface->free();
+	delete _backSurface;
+}
+
+Graphics::Surface *Screen::allocSurface(int16 width, int16 height) {
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->create(width, height, _vm->_system->getScreenFormat());
+	return surface; 
+}
+
+Graphics::Surface *Screen::allocSurface(SurfInfo &surfInfo) {
+	return allocSurface(surfInfo._dimensions._width, surfInfo._dimensions._height);
+}
+
+bool Screen::isDisplayOn() {
+	return _displayOn;
+}
+
+void Screen::setDisplayOn(bool isOn) {
+	_displayOn = isOn;
+	// TODO Clear screen when off
+}
+
+void Screen::setScreenOffset(Common::Point offsPt) {
+	if (offsPt.x != 0 || offsPt.y != 0) {
+		_isScreenOffsetActive = true;
+		_screenOffsetPt = offsPt;
+	} else {
+		_isScreenOffsetActive = false;
+	}
+}
+
+void Screen::updateSprites() {
+	_decompressQueue->decompressAll();
+	// NOTE Skipped doShiftBrightness and related as it seems to be unused
+	_drawQueue->drawAll();
+	if (_isScreenOffsetActive)
+		clearScreenOffsetAreas();
+	if (!_displayOn) // TODO Check if a video is playing then don't do it
+		_backSurface->fillRect(Common::Rect(_backSurface->w, _backSurface->h), 0);
+	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
+}
+
+void Screen::clearScreenOffsetAreas() {
+	int16 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+	if (_screenOffsetPt.x < 0) {
+		x1 = _backSurface->w + _screenOffsetPt.x;
+		x2 = _backSurface->w;
+	} else if (_screenOffsetPt.x > 0) {
+		x1 = 0;
+		x2 = _screenOffsetPt.x;
+	}
+	if (_screenOffsetPt.y < 0) {
+		y1 = _backSurface->h + _screenOffsetPt.y;
+		y2 = _backSurface->h;
+	} else if (_screenOffsetPt.y > 0) {
+		y1 = 0;
+		y2 = _screenOffsetPt.y;
+	}
+	_backSurface->fillRect(Common::Rect(0, y1, _backSurface->w, y2), 0);
+	_backSurface->fillRect(Common::Rect(x1, 0, x2, _backSurface->h), 0);
+}
+
+void Screen::decompressSprite(SpriteDecompressQueueItem *item) {
+	switch (_backSurface->format.bytesPerPixel) {
+	case 1:
+		decompressSprite8(item);
+		break;
+	case 2:
+		decompressSprite16(item);
+		break;
+	default:
+		break;
+	}
+}
+
+void Screen::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
+	switch (_backSurface->format.bytesPerPixel) {
+	case 1:
+		drawSurface8(dstRect, surface, srcRect, scale, flags);
+		break;
+	case 2:
+		drawSurface16(dstRect, surface, srcRect, scale, flags);
+		break;
+	default:
+		break;
+	}
+}
+
 void Screen::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
 	switch (_backSurface->format.bytesPerPixel) {
 	case 1:
@@ -547,10 +553,6 @@ int16 Screen::drawChar16(FontResource *font, Graphics::Surface *surface, int16 x
 	return charWidth;
 }
 
-void Screen::setSystemPalette(byte *palette) {
-	g_system->getPaletteManager()->setPalette(palette, 0, 256);
-}
-
 void Screen::decompressSprite8(SpriteDecompressQueueItem *item) {
 	byte *src = item->_compressedPixels;
 	Graphics::Surface *dstSurface = item->_surface;
@@ -637,6 +639,7 @@ void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface,
 	// Unscaled
 	const int16 w = srcRect.width();
 	const int16 h = srcRect.height();
+	const byte* colorTransTbl = _vm->_screenPalette->getColorTransTbl();
 	for (int16 yc = 0; yc < h; ++yc) {
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcRect.top + yc);
 		byte *dst = (byte*)_backSurface->getBasePtr(destX, destY + yc);
@@ -644,7 +647,7 @@ void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface,
 			const byte pixel = *src++;
 			if (pixel != 0) {
 				if (pixel == 1)
-					*dst = _colorTransTbl[*dst];
+					*dst = colorTransTbl[*dst];
 				else
 					*dst = pixel;
 			}
@@ -659,10 +662,9 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
 	const int errYStart = srcHeight / dstHeight;
 	const int errYIncr = srcHeight % dstHeight;
-//	const int midY = dstHeight / 2;
 	const int errXStart = srcWidth / dstWidth;
 	const int errXIncr = srcWidth % dstWidth;
-//	const int midX = dstWidth / 2;
+	const byte* colorTransTbl = _vm->_screenPalette->getColorTransTbl();
 	int h = dstHeight, errY = 0, skipY, srcY = srcRect.top;
 	byte *dst = (byte*)_backSurface->getBasePtr(dstRect.left, dstRect.top);
 	skipY = (dstHeight < srcHeight) ? 0 : dstHeight / (2*srcHeight) + 1;
@@ -677,7 +679,7 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 			const byte pixel = *src;
 			if (pixel != 0) {
 				if (pixel == 1)
-					*dstRow = _colorTransTbl[*dstRow];
+					*dstRow = colorTransTbl[*dstRow];
 				else
 					*dstRow = pixel;
 			}
@@ -693,7 +695,7 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 			const byte pixel = *src;
 			if (pixel != 0) {
 				if (pixel == 1)
-					*dstRow = _colorTransTbl[*dstRow];
+					*dstRow = colorTransTbl[*dstRow];
 				else
 					*dstRow = pixel;
 			}
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index b801458..7715058 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -111,6 +111,47 @@ struct Fader {
 	Fader() : _active(false), _paused(false) {}
 };
 
+class ScreenPaletteBase {
+public:
+	virtual ~ScreenPaletteBase() {};
+	virtual void setPalette(byte *colors, uint start, uint count) {};
+	virtual void setPaletteEntry(int16 index, byte r, byte g, byte b) {};
+	virtual void getPalette(byte *colors) {};
+	virtual void shiftPalette(int16 fromIndex, int16 toIndex) {};
+	virtual void updatePalette() {};
+	virtual void updateFaderPalette() {};
+	virtual void setFader(int newValue, int firstIndex, int lastIndex) {};
+	virtual bool isFaderActive() const { return false; }
+	virtual const byte* getColorTransTbl() const { return 0; }
+};
+
+class ScreenPalette : public ScreenPaletteBase {
+public:
+	ScreenPalette(IllusionsEngine *vm);
+	void setPalette(byte *colors, uint start, uint count);
+	void setPaletteEntry(int16 index, byte r, byte g, byte b);
+	void getPalette(byte *colors);
+	void shiftPalette(int16 fromIndex, int16 toIndex);
+	void updatePalette();
+	void updateFaderPalette();
+	void setFader(int newValue, int firstIndex, int lastIndex);
+	bool isFaderActive() const { return _isFaderActive; }
+	const byte* getColorTransTbl() const { return _colorTransTbl; }
+protected:
+	IllusionsEngine *_vm;
+	bool _needRefreshPalette;
+	byte _mainPalette[768];
+	byte _colorTransTbl[256];
+	bool _isFaderActive;
+	byte _faderPalette[768];
+	int _newFaderValue, _firstFaderIndex, _lastFaderIndex;
+	void setSystemPalette(byte *palette);
+	void buildColorTransTbl();
+};
+
+class NullScreenPalette : public ScreenPaletteBase {
+};
+
 // TODO Split into two classes (8bit and 16bit)?
 
 class Screen {
@@ -126,13 +167,6 @@ public:
 	void clearScreenOffsetAreas();
 	void decompressSprite(SpriteDecompressQueueItem *item);
 	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
-	void setPalette(byte *colors, uint start, uint count);
-	void setPaletteEntry(int16 index, byte r, byte g, byte b);
-	void getPalette(byte *colors);
-	void shiftPalette(int16 fromIndex, int16 toIndex);
-	void updatePalette();
-	void updateFaderPalette();
-	void setFader(int newValue, int firstIndex, int lastIndex);
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
 	void fillSurface(Graphics::Surface *surface, byte color);
 	uint16 convertColor(byte color);
@@ -141,7 +175,6 @@ public:
 	uint16 getColorKey2() const { return _colorKey2; }
 	int16 getScreenWidth() const { return _backSurface->w; }
 	int16 getScreenHeight() const { return _backSurface->h; }
-	bool isFaderActive() const { return _isFaderActive; }
 public:
 	IllusionsEngine *_vm;
 	bool _displayOn;
@@ -151,20 +184,9 @@ public:
 	SpriteDrawQueue *_drawQueue;
 	Graphics::Surface *_backSurface;
 	
-	bool _needRefreshPalette;
-	byte _mainPalette[768];
-	byte _colorTransTbl[256];
-	
-	bool _isFaderActive;
-	byte _faderPalette[768];
-	int _newFaderValue, _firstFaderIndex, _lastFaderIndex;
-
 	bool _isScreenOffsetActive;
 	Common::Point _screenOffsetPt;
 
-	void setSystemPalette(byte *palette);
-	void buildColorTransTbl();
-
 	void drawText8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
 	int16 drawChar8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
 
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 9fa3d40..0a91570 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -128,7 +128,7 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
 		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
 		outTextPtr);
-	_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
+	_vm->_screenPalette->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 
 	uint16 *textPart = screenText->_text;
 	while (text != outTextPtr)
@@ -157,7 +157,7 @@ void ScreenText::removeText() {
 			refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
 				screenText->_text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
 				outTextPtr);
-			_vm->_screen->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
+			_vm->_screenPalette->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 			setTextInfoPosition(screenText->_info._position);
 		}
 	}
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 5ce25ca..81f881b 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -361,13 +361,13 @@ void SequenceOpcodes::opSetPalette(Control *control, OpCall &opCall) {
 	ARG_BYTE(fromIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	Palette *palette = bgRes->getPalette(paletteIndex - 1);
-	_vm->_screen->setPalette(palette->_palette, fromIndex, palette->_count);
+	_vm->_screenPalette->setPalette(palette->_palette, fromIndex, palette->_count);
 }
 
 void SequenceOpcodes::opShiftPalette(Control *control, OpCall &opCall) {
 	ARG_INT16(fromIndex);
 	ARG_INT16(toIndex);
-	_vm->_screen->shiftPalette(fromIndex, toIndex);
+	_vm->_screenPalette->shiftPalette(fromIndex, toIndex);
 }
 
 void SequenceOpcodes::opPlaySound(Control *control, OpCall &opCall) {


Commit: 5e919fd3084d12f1d9ff9c9921e5993b8ae318eb
    https://github.com/scummvm/scummvm/commit/5e919fd3084d12f1d9ff9c9921e5993b8ae318eb
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Split Screen class into variants for 8bit and 16bit (i.e. Duckman and BBDOU)

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/screen.cpp
    engines/illusions/screen.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 591ad18..a99a124 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -946,7 +946,7 @@ void Control::fillActor(byte color) {
 
 bool Control::isPixelCollision(Common::Point &pt) {
 	Frame *frame = &(*_actor->_frames)[_actor->_frameIndex - 1];
-	return _vm->_screen->isSpritePixelSolid16(pt, _position, _actor->_position,
+	return _vm->_screen->isSpritePixelSolid(pt, _position, _actor->_position,
 		_actor->_surfInfo, _actor->_scale, frame->_flags, frame->_compressedPixels);
 }
 
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 15b1031..e3b0df2 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -151,7 +151,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
 	_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
 
-	_screen = new Screen(this, 640, 480, 16);
+	_screen = new Screen16Bit(this, 640, 480);
 	_screenPalette = new NullScreenPalette();
 	_screenText = new ScreenText(this);
 	_input = new Input();	
@@ -569,8 +569,8 @@ void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
 }
 
 void IllusionsEngine_BBDOU::enterMenuPause() {
-      // TODO suspendAudio();
-      _screenText->clearText();
+	// TODO suspendAudio();
+	_screenText->clearText();
 }
 
 void IllusionsEngine_BBDOU::leaveMenuPause() {
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index a9f6b6e..2cd338b 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -103,7 +103,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
 	_resSys->addResourceLoader(0x00190000, new GenericResourceLoader(this));
 
-	_screen = new Screen(this, 320, 200, 8);
+	_screen = new Screen8Bit(this, 320, 200);
 	_screenPalette = new ScreenPalette(this);
 	_screenText = new ScreenText(this);
 	_input = new Input();	
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 3cbdd91..0b04c2a 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -449,111 +449,9 @@ void Screen::clearScreenOffsetAreas() {
 	_backSurface->fillRect(Common::Rect(x1, 0, x2, _backSurface->h), 0);
 }
 
-void Screen::decompressSprite(SpriteDecompressQueueItem *item) {
-	switch (_backSurface->format.bytesPerPixel) {
-	case 1:
-		decompressSprite8(item);
-		break;
-	case 2:
-		decompressSprite16(item);
-		break;
-	default:
-		break;
-	}
-}
-
-void Screen::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
-	switch (_backSurface->format.bytesPerPixel) {
-	case 1:
-		drawSurface8(dstRect, surface, srcRect, scale, flags);
-		break;
-	case 2:
-		drawSurface16(dstRect, surface, srcRect, scale, flags);
-		break;
-	default:
-		break;
-	}
-}
-
-void Screen::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
-	switch (_backSurface->format.bytesPerPixel) {
-	case 1:
-		drawText8(font, surface, x, y, text, count);
-		break;
-	case 2:
-		drawText16(font, surface, x, y, text, count);
-		break;
-	default:
-		break;
-	}
-}
-
-void Screen::fillSurface(Graphics::Surface *surface, byte color) {
-	Common::Rect r = Common::Rect(surface->w, surface->h);
-	switch (_backSurface->format.bytesPerPixel) {
-	case 1:
-		surface->fillRect(r, color);
-		break;
-	case 2:
-		surface->fillRect(r, convertColor(color));
-		break;
-	default:
-		break;
-	}
-}
-
-uint16 Screen::convertColor(byte color) {
-	if (color == 0)
-		return _colorKey1;
-	if (color == 20)
-		return g_system->getScreenFormat().RGBToColor(255, 255, 255);
-	if (color == 80)
-		return g_system->getScreenFormat().RGBToColor(176, 176, 176);
-	return g_system->getScreenFormat().RGBToColor(16, 16, 16);
-}
-
-void Screen::drawText8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
-	for (uint i = 0; i < count; ++i)
-		x += font->_widthC + drawChar8(font, surface, x, y, *text++);
-}
+// Screen8Bit
 
-int16 Screen::drawChar8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
-	const CharInfo *charInfo = font->getCharInfo(c);
-	const int16 charWidth = charInfo->_width;
-	byte *dst = (byte*)surface->getBasePtr(x, y);
-	byte *pixels = charInfo->_pixels;
-	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
-		for (int16 xc = 0; xc < charWidth; ++xc)
-			if (pixels[xc])
-				dst[xc] = pixels[xc];
-		dst += surface->pitch;
-		pixels += charWidth;
-	}
-	return charWidth;
-}
-
-void Screen::drawText16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
-	for (uint i = 0; i < count; ++i)
-		x += font->_widthC + drawChar16(font, surface, x, y, *text++);
-}
-
-int16 Screen::drawChar16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
-	const CharInfo *charInfo = font->getCharInfo(c);
-	const int16 charWidth = charInfo->_width;
-	byte *pixels = charInfo->_pixels;
-	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
-		byte *dst = (byte*)surface->getBasePtr(x, y + yc);
-		for (int16 xc = 0; xc < charWidth; ++xc) {
-			if (pixels[xc])
-				WRITE_LE_UINT16(dst, convertFontColor(pixels[xc]));
-			dst += 2;
-		}
-		pixels += charWidth;
-	}
-	return charWidth;
-}
-
-void Screen::decompressSprite8(SpriteDecompressQueueItem *item) {
+void Screen8Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 	byte *src = item->_compressedPixels;
 	Graphics::Surface *dstSurface = item->_surface;
 	int dstSize = item->_dimensions._width * item->_dimensions._height;
@@ -587,9 +485,9 @@ void Screen::decompressSprite8(SpriteDecompressQueueItem *item) {
 		y = 0;
 		yincr = 1;
 	}
-	
+
 	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
-	
+
 	while (processedSize < dstSize) {
 		byte op = *src++;
 		if (op & 0x80) {
@@ -627,16 +525,45 @@ void Screen::decompressSprite8(SpriteDecompressQueueItem *item) {
 
 }
 
-void Screen::drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
+void Screen8Bit::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
 	if (scale == 100) {
-		drawSurface81(dstRect.left, dstRect.top, surface, srcRect);
+		drawSurfaceUnscaled(dstRect.left, dstRect.top, surface, srcRect);
 	} else {
-		drawSurface82(dstRect, surface, srcRect);
+		drawSurfaceScaled(dstRect, surface, srcRect);
 	}
 }
 
-void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// Unscaled
+void Screen8Bit::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
+	for (uint i = 0; i < count; ++i)
+		x += font->_widthC + drawChar(font, surface, x, y, *text++);
+}
+
+void Screen8Bit::fillSurface(Graphics::Surface *surface, byte color) {
+	surface->fillRect(Common::Rect(surface->w, surface->h), color);
+}
+
+bool Screen8Bit::isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+	const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) {
+	// Unused in Duckman
+	return false;
+}
+
+int16 Screen8Bit::drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+	const CharInfo *charInfo = font->getCharInfo(c);
+	const int16 charWidth = charInfo->_width;
+	byte *dst = (byte*)surface->getBasePtr(x, y);
+	byte *pixels = charInfo->_pixels;
+	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
+		for (int16 xc = 0; xc < charWidth; ++xc)
+			if (pixels[xc])
+				dst[xc] = pixels[xc];
+		dst += surface->pitch;
+		pixels += charWidth;
+	}
+	return charWidth;
+}
+
+void Screen8Bit::drawSurfaceUnscaled(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
 	const int16 w = srcRect.width();
 	const int16 h = srcRect.height();
 	const byte* colorTransTbl = _vm->_screenPalette->getColorTransTbl();
@@ -656,8 +583,7 @@ void Screen::drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface,
 	}
 }
 
-void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
-	// Scaled
+void Screen8Bit::drawSurfaceScaled(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
 	const int dstWidth = dstRect.width(), dstHeight = dstRect.height();
 	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
 	const int errYStart = srcHeight / dstHeight;
@@ -712,7 +638,9 @@ void Screen::drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	}
 }
 
-void Screen::decompressSprite16(SpriteDecompressQueueItem *item) {
+// Screen16Bit
+
+void Screen16Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 	byte *src = item->_compressedPixels;
 	Graphics::Surface *dstSurface = item->_surface;
 	int dstSize = item->_dimensions._width * item->_dimensions._height;
@@ -789,7 +717,7 @@ void Screen::decompressSprite16(SpriteDecompressQueueItem *item) {
 
 }
 
-void Screen::drawSurface16(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
+void Screen16Bit::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) {
 	if (scale == 100) {
 		if (flags & 1)
 			drawSurface10(dstRect.left, dstRect.top, surface, srcRect, _colorKey2);
@@ -803,13 +731,94 @@ void Screen::drawSurface16(Common::Rect &dstRect, Graphics::Surface *surface, Co
 	}
 }
 
-void Screen::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+void Screen16Bit::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
+	for (uint i = 0; i < count; ++i)
+		x += font->_widthC + drawChar(font, surface, x, y, *text++);
+}
+
+void Screen16Bit::fillSurface(Graphics::Surface *surface, byte color) {
+	surface->fillRect(Common::Rect(surface->w, surface->h), convertColor(color));
+}
+
+bool Screen16Bit::isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+	const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) {
+
+	int ptX = scale * drawPosition.x / 100 + testPt.x - drawOffset.x;
+	int ptY = scale * drawPosition.y / 100 + testPt.y - drawOffset.y;
+
+	if (flags & 1) {
+		const int scaledWidth = scale * surfInfo._dimensions._width / 100;
+		ptX += 2 * (scaledWidth - scaledWidth / 2 - ptX);
+	}
+
+	if (flags & 2) {
+		const int scaledHeight = scale * surfInfo._dimensions._height / 100;
+		ptY += 2 * (scaledHeight - scaledHeight / 2 - ptY);
+	}
+
+	const int pixelLookX = 100 * ptX / scale;
+	const int pixelLookY = 100 * ptY / scale;
+	const int lookOffset = pixelLookX + surfInfo._dimensions._width * pixelLookY;
+	const int dstSize = surfInfo._dimensions._width * surfInfo._dimensions._height;
+
+	if (pixelLookX < 0 || pixelLookX >= surfInfo._dimensions._width ||
+		pixelLookY < 0 || pixelLookY >= surfInfo._dimensions._height ||
+		lookOffset < 0 || lookOffset >= dstSize)
+		return false;
+
+	byte *src = compressedPixels;
+	int processedSize = 0;
+
+	while (processedSize < dstSize) {
+		int16 op = READ_LE_UINT16(src);
+		src += 2;
+		if (op & 0x8000) {
+			int runCount = (op & 0x7FFF) + 1;
+			uint16 runColor = READ_LE_UINT16(src);
+			src += 2;
+			while (runCount--) {
+				if (processedSize == lookOffset)
+					return runColor != _colorKey1;
+				++processedSize;
+			}
+		} else {
+			int copyCount = op + 1;
+			while (copyCount--) {
+				uint16 color = READ_LE_UINT16(src);
+				src += 2;
+				if (processedSize == lookOffset)
+					return color != _colorKey1;
+				++processedSize;
+			}
+		}
+	}
+
+	return false;
+}
+
+int16 Screen16Bit::drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+	const CharInfo *charInfo = font->getCharInfo(c);
+	const int16 charWidth = charInfo->_width;
+	byte *pixels = charInfo->_pixels;
+	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
+		byte *dst = (byte*)surface->getBasePtr(x, y + yc);
+		for (int16 xc = 0; xc < charWidth; ++xc) {
+			if (pixels[xc])
+				WRITE_LE_UINT16(dst, convertFontColor(pixels[xc]));
+			dst += 2;
+		}
+		pixels += charWidth;
+	}
+	return charWidth;
+}
+
+void Screen16Bit::drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
 	// Unscaled
 	// TODO
 	//debug("Screen::drawSurface10");
 }
 
-void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
+void Screen16Bit::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect) {
 	// Unscaled
 	//debug("Screen::drawSurface11() destX: %d; destY: %d; srcRect: (%d, %d, %d, %d)", destX, destY, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom);
 	const int16 w = srcRect.width();
@@ -827,13 +836,13 @@ void Screen::drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface,
 	}
 }
 
-void Screen::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
+void Screen16Bit::drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey) {
 	// Scaled
 	// TODO
 	//debug("Screen::drawSurface20");
 }
 
-void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
+void Screen16Bit::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect) {
 	// Scaled
 	const int dstWidth = dstRect.width(), dstHeight = dstRect.height();
 	const int srcWidth = srcRect.width(), srcHeight = srcRect.height();
@@ -878,66 +887,19 @@ void Screen::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Co
 			++srcY;
 		}
 	}
-
 }
 
-bool Screen::isSpritePixelSolid16(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
-	const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) {
-
-	int ptX = scale * drawPosition.x / 100 + testPt.x - drawOffset.x;
-	int ptY = scale * drawPosition.y / 100 + testPt.y - drawOffset.y;
-
-	if (flags & 1) {
-		const int scaledWidth = scale * surfInfo._dimensions._width / 100;
-		ptX += 2 * (scaledWidth - scaledWidth / 2 - ptX);
-	}
-
-	if (flags & 2) {
-		const int scaledHeight = scale * surfInfo._dimensions._height / 100;
-		ptY += 2 * (scaledHeight - scaledHeight / 2 - ptY);
-	}
-
-	const int pixelLookX = 100 * ptX / scale;
-	const int pixelLookY = 100 * ptY / scale;
-	const int lookOffset = pixelLookX + surfInfo._dimensions._width * pixelLookY;
-	const int dstSize = surfInfo._dimensions._width * surfInfo._dimensions._height;
-
-	if (pixelLookX < 0 || pixelLookX >= surfInfo._dimensions._width ||
-		pixelLookY < 0 || pixelLookY >= surfInfo._dimensions._height ||
-		lookOffset < 0 || lookOffset >= dstSize)
-		return false;
-
-	byte *src = compressedPixels;
-	int processedSize = 0;
-
-	while (processedSize < dstSize) {
-		int16 op = READ_LE_UINT16(src);
-		src += 2;
-		if (op & 0x8000) {
-			int runCount = (op & 0x7FFF) + 1;
-			uint16 runColor = READ_LE_UINT16(src);
-			src += 2;
-			while (runCount--) {
-				if (processedSize == lookOffset)
-					return runColor != _colorKey1;
-				++processedSize;
-			}
-		} else {
-			int copyCount = op + 1;
-			while (copyCount--) {
-				uint16 color = READ_LE_UINT16(src);
-				src += 2;
-				if (processedSize == lookOffset)
-					return color != _colorKey1;
-				++processedSize;
-			}
-		}
-	}
-
-	return false;
+uint16 Screen16Bit::convertColor(byte color) {
+	if (color == 0)
+		return _colorKey1;
+	if (color == 20)
+		return g_system->getScreenFormat().RGBToColor(255, 255, 255);
+	if (color == 80)
+		return g_system->getScreenFormat().RGBToColor(176, 176, 176);
+	return g_system->getScreenFormat().RGBToColor(16, 16, 16);
 }
 
-uint16 Screen::convertFontColor(byte color) {
+uint16 Screen16Bit::convertFontColor(byte color) {
 	if (color) {
 		byte r, g, b;
 		if (color == 204) {
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 7715058..1da1d5b 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -152,12 +152,10 @@ protected:
 class NullScreenPalette : public ScreenPaletteBase {
 };
 
-// TODO Split into two classes (8bit and 16bit)?
-
 class Screen {
 public:
 	Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp);
-	~Screen();
+	virtual ~Screen();
 	Graphics::Surface *allocSurface(int16 width, int16 height);
 	Graphics::Surface *allocSurface(SurfInfo &surfInfo);
 	bool isDisplayOn();
@@ -165,16 +163,17 @@ public:
 	void setScreenOffset(Common::Point offsPt);
 	void updateSprites();
 	void clearScreenOffsetAreas();
-	void decompressSprite(SpriteDecompressQueueItem *item);
-	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
-	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
-	void fillSurface(Graphics::Surface *surface, byte color);
-	uint16 convertColor(byte color);
 	uint16 getColorKey1() const { return _colorKey1; }
 	void setColorKey1(uint16 colorKey) { _colorKey1 = colorKey; }
 	uint16 getColorKey2() const { return _colorKey2; }
 	int16 getScreenWidth() const { return _backSurface->w; }
 	int16 getScreenHeight() const { return _backSurface->h; }
+	virtual void decompressSprite(SpriteDecompressQueueItem *item) = 0;
+	virtual void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) = 0;
+	virtual void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) = 0;
+	virtual void fillSurface(Graphics::Surface *surface, byte color) = 0;
+	virtual bool isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) = 0;
 public:
 	IllusionsEngine *_vm;
 	bool _displayOn;
@@ -183,31 +182,41 @@ public:
 	SpriteDecompressQueue *_decompressQueue;
 	SpriteDrawQueue *_drawQueue;
 	Graphics::Surface *_backSurface;
-	
 	bool _isScreenOffsetActive;
 	Common::Point _screenOffsetPt;
+};
 
-	void drawText8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
-	int16 drawChar8(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
-
-	void drawText16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
-	int16 drawChar16(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
-
-	void decompressSprite8(SpriteDecompressQueueItem *item);
-	void drawSurface8(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
-	void drawSurface81(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
-	void drawSurface82(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+class Screen8Bit : public Screen {
+public:
+	Screen8Bit(IllusionsEngine *vm, int16 width, int16 height) : Screen(vm, width, height, 8) {}
+	void decompressSprite(SpriteDecompressQueueItem *item);
+	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
+	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
+	void fillSurface(Graphics::Surface *surface, byte color);
+	bool isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels);
+public:
+	int16 drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+	void drawSurfaceUnscaled(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
+	void drawSurfaceScaled(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
+};
 
-	void decompressSprite16(SpriteDecompressQueueItem *item);
-	void drawSurface16(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
+class Screen16Bit : public Screen {
+public:
+	Screen16Bit(IllusionsEngine *vm, int16 width, int16 height) : Screen(vm, width, height, 16) {}
+	void decompressSprite(SpriteDecompressQueueItem *item);
+	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
+	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
+	void fillSurface(Graphics::Surface *surface, byte color);
+	bool isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
+		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels);
+public:
+	int16 drawChar(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 c);
 	void drawSurface10(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
 	void drawSurface11(int16 destX, int16 destY, Graphics::Surface *surface, Common::Rect &srcRect);
 	void drawSurface20(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, uint16 colorKey);
 	void drawSurface21(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect);
-	
-	bool isSpritePixelSolid16(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
-		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels);
-
+	uint16 convertColor(byte color);
 	uint16 convertFontColor(byte color);
 };
 


Commit: 02063f60d0eed3ba4131643adad3058f760542ff
    https://github.com/scummvm/scummvm/commit/02063f60d0eed3ba4131643adad3058f760542ff
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
Getting it to compile after merging master

Changed paths:
    engines/illusions/detection.cpp
    engines/illusions/menusystem.cpp
    engines/illusions/saveload.cpp
    engines/illusions/screen.cpp


diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index a0e2488..db6090b 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -80,13 +80,13 @@ static const char * const directoryGlobs[] = {
 class IllusionsMetaEngine : public AdvancedMetaEngine {
 public:
 	IllusionsMetaEngine() : AdvancedMetaEngine(Illusions::gameDescriptions, sizeof(Illusions::IllusionsGameDescription), illusionsGames) {
-		_singleid = "illusions";
+		_singleId = "illusions";
 		_maxScanDepth = 2;
 		_directoryGlobs = directoryGlobs;
 	}
 
 	virtual const char *getName() const {
-		return "Illusions Engine";
+		return "Illusions";
 	}
 
 	virtual const char *getOriginalCopyright() const {
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index cb82e33..829756c 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -603,7 +603,7 @@ MenuActionLoadGame::MenuActionLoadGame(BaseMenuSystem *menuSystem, uint choiceIn
 }
 
 void MenuActionLoadGame::execute() {
-	const EnginePlugin *plugin = NULL;
+	const Plugin *plugin = NULL;
 	EngineMan.findGame(ConfMan.get("gameid"), &plugin);
 	GUI::SaveLoadChooser *dialog;
 	Common::String desc;
diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp
index 751f6a6..f3fae99 100644
--- a/engines/illusions/saveload.cpp
+++ b/engines/illusions/saveload.cpp
@@ -43,7 +43,7 @@ IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::Se
 		header.description += (char)in->readByte();
 
 	if (loadThumbnail) {
-		header.thumbnail = Graphics::loadThumbnail(*in);
+		Graphics::loadThumbnail(*in, header.thumbnail);
 	} else {
 		Graphics::skipThumbnail(*in);
 	}
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 0b04c2a..c234cde 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -272,7 +272,7 @@ void ScreenPalette::shiftPalette(int16 fromIndex, int16 toIndex) {
 		r = _mainPalette[3 * toIndex + 0];
 		g = _mainPalette[3 * toIndex + 1];
 		b = _mainPalette[3 * toIndex + 2];
-		for (int16 i = toIndex + 1; i < fromIndex; +i) {
+		for (int16 i = toIndex + 1; i < fromIndex; +i) { //TODO fix this. +i
 			byte *dst = &_mainPalette[3 * i];
 			byte *src = &_mainPalette[3 * (i + 1)];
 			dst[0] = src[0];
@@ -371,16 +371,15 @@ Screen::Screen(IllusionsEngine *vm, int16 width, int16 height, int bpp)
 	_decompressQueue = new SpriteDecompressQueue(this);
 	_drawQueue = new SpriteDrawQueue(this);
 	if (bpp == 8) {
-		initGraphics(width, height, false);
+		initGraphics(width, height);
 	} else {
 		Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
-		initGraphics(width, height, true, &pixelFormat16);
+		initGraphics(width, height, &pixelFormat16);
 	}
 
 	_backSurface = allocSurface(width, height);
 
 	_isScreenOffsetActive = false;
-
 }
 
 Screen::~Screen() {


Commit: 95d6171b56eba42a86dad2a7e65341760cf10001
    https://github.com/scummvm/scummvm/commit/95d6171b56eba42a86dad2a7e65341760cf10001
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
Disable debug scene override

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 2518002..ad6ec9d 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -287,7 +287,7 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	debug(1, "changeScene(%08X, %08X)", sceneId, threadId);
 	
 	//DEBUG
-#if 1
+#if 0
 	if (dsceneId) {
 		sceneId = dsceneId;
 		threadId = dthreadId;


Commit: 783fd028937d1b5c16d945db4dbed3ba8ca307b2
    https://github.com/scummvm/scummvm/commit/783fd028937d1b5c16d945db4dbed3ba8ca307b2
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
Work on save support

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 2cd338b..199bae7 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -1179,9 +1179,8 @@ bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 calli
 }
 
 bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
-	// TODO
-	// const char *fileName = getSavegameFilename(slotNum);
-	bool success = false;//savegame(fileName, _savegameDescription.c_str());
+	const char *fileName = getSavegameFilename(_savegameSlotNum);
+	bool success = savegame(fileName, "");//_savegameDescription.c_str()); //TODO
 	return success;
 }
 
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 7b11916..a1e500b 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -79,6 +79,8 @@ BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
 		return createQueryRestartMenu();
 	case kDuckmanQueryQuitMenu:
 		return createQueryQuitMenu();
+	case kDuckmanSaveCompleteMenu:
+		return createSaveCompleteMenu();
 	default:
 		error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId);
 	}
@@ -107,7 +109,7 @@ BaseMenu *DuckmanMenuSystem::createPauseMenu() {
 	menu->addText("-------------------");
 	menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 21)));
 	menu->addMenuItem(new MenuItem("Load Game", new MenuActionLoadGame(this, 1)));
-	// TODO menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
+	menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
 	// TODO menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
 	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
 	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
@@ -127,14 +129,23 @@ BaseMenu *DuckmanMenuSystem::createQueryQuitMenu() {
 	return menu;
 }
 
+BaseMenu *DuckmanMenuSystem::createSaveCompleteMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
+	menu->addText("Game Saved");
+	menu->addText("-------------");
+	menu->addMenuItem(new MenuItem("Continue", new MenuActionLeaveMenu(this)));
+	return menu;
+}
+
 int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
 	switch (menuId) {
 	case 0x180001:
 		return kDuckmanMainMenu;
 	case 0x180002:
 		return kDuckmanPauseMenu;
-	/* Debug menus, not implemented
 	case 0x180005:
+		return kDuckmanSaveCompleteMenu;
+	/* Debug menus, not implemented
 	case 0x180006:
 	case 0x180007:
 	*/
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index bb43619..10a8a6e 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -34,6 +34,7 @@ enum {
 	kDuckmanPauseMenu,
 	kDuckmanQueryQuitMenu,
 	kDuckmanQueryRestartMenu,
+	kDuckmanSaveCompleteMenu,
 	kDuckmanLastMenuIndex
 };
 
@@ -56,8 +57,9 @@ public://protected:
 	BaseMenu *createLoadGameMenu();
 	BaseMenu *createOptionsMenu();
 	BaseMenu *createPauseMenu();
-	BaseMenu *createQueryRestartMenu();		
+	BaseMenu *createQueryRestartMenu();
 	BaseMenu *createQueryQuitMenu();
+	BaseMenu *createSaveCompleteMenu();
 	int convertRootMenuId(uint32 menuId);
 	virtual bool initMenuCursor();
 	virtual int getGameState();
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 829756c..4fb0f18 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -621,4 +621,28 @@ void MenuActionLoadGame::execute() {
 
 }
 
+// MenuActionSaveGame
+
+	MenuActionSaveGame::MenuActionSaveGame(BaseMenuSystem *menuSystem, uint choiceIndex)
+			: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
+	}
+
+	void MenuActionSaveGame::execute() {
+		const Plugin *plugin = NULL;
+		EngineMan.findGame(ConfMan.get("gameid"), &plugin);
+		GUI::SaveLoadChooser *dialog;
+		Common::String desc;
+		int slot;
+
+		dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+		slot = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+		delete dialog;
+
+		if (slot >= 0) {
+			_menuSystem->setSavegameSlotNum(slot);
+			_menuSystem->selectMenuChoiceIndex(_choiceIndex);
+		}
+		_menuSystem->closeMenu();
+	}
 } // End of namespace Illusions
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index 26a5931..26324c6 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -239,6 +239,14 @@ protected:
 	uint _choiceIndex;
 };
 
+class MenuActionSaveGame : public BaseMenuAction {
+public:
+	MenuActionSaveGame(BaseMenuSystem *menuSystem, uint choiceIndex);
+	virtual void execute();
+protected:
+	uint _choiceIndex;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_MENUSYSTEM_H


Commit: 92c979e99ab6edf85d20fe5ba693f5a7d14fb2e1
    https://github.com/scummvm/scummvm/commit/92c979e99ab6edf85d20fe5ba693f5a7d14fb2e1
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
Fixed save continue dialog menu

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/menusystem.cpp


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index a1e500b..e16e0a2 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -133,7 +133,7 @@ BaseMenu *DuckmanMenuSystem::createSaveCompleteMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
 	menu->addText("Game Saved");
 	menu->addText("-------------");
-	menu->addMenuItem(new MenuItem("Continue", new MenuActionLeaveMenu(this)));
+	menu->addMenuItem(new MenuItem("Continue", new MenuActionReturnChoice(this, 1)));
 	return menu;
 }
 
@@ -145,9 +145,9 @@ int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
 		return kDuckmanPauseMenu;
 	case 0x180005:
 		return kDuckmanSaveCompleteMenu;
-	/* Debug menus, not implemented
-	case 0x180006:
-	case 0x180007:
+	/*
+	case 0x180006: // save game failed menu
+	case 0x180007: // load game failed menu
 	*/
 	/* TODO CHECKME Another pause menu?
 	case 0x180008:
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 4fb0f18..c8bf36e 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -643,6 +643,5 @@ void MenuActionLoadGame::execute() {
 			_menuSystem->setSavegameSlotNum(slot);
 			_menuSystem->selectMenuChoiceIndex(_choiceIndex);
 		}
-		_menuSystem->closeMenu();
 	}
 } // End of namespace Illusions


Commit: 13d5c2fa2629a9409f385ae8054fada0fc030eda
    https://github.com/scummvm/scummvm/commit/13d5c2fa2629a9409f385ae8054fada0fc030eda
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
Save description with savegame

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/illusions.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 199bae7..955aa8d 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -1180,8 +1180,7 @@ bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 calli
 
 bool IllusionsEngine_Duckman::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
 	const char *fileName = getSavegameFilename(_savegameSlotNum);
-	bool success = savegame(fileName, "");//_savegameDescription.c_str()); //TODO
-	return success;
+	return savegame(fileName, _savegameDescription.c_str());
 }
 
 void IllusionsEngine_Duckman::activateSavegame(uint32 callingThreadId) {
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 40ed327..7154467 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -139,6 +139,7 @@ public:
 
 	int _resumeFromSavegameRequested;
 	int _savegameSlotNum;
+	Common::String _savegameDescription;
 	uint32 _savegameSceneId;
 	uint32 _savegameThreadId;
 
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index c8bf36e..f32bdc6 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -504,6 +504,10 @@ void BaseMenuSystem::setSavegameSlotNum(int slotNum) {
 	_vm->_savegameSlotNum = slotNum;
 }
 
+void BaseMenuSystem::setSavegameDescription(Common::String desc) {
+    _vm->_savegameDescription = desc;
+}
+
 void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
 
 	if (!_isTimeOutEnabled)
@@ -636,11 +640,13 @@ void MenuActionLoadGame::execute() {
 
 		dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
 		slot = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+        desc = dialog->getResultString().c_str();
 
-		delete dialog;
+        delete dialog;
 
-		if (slot >= 0) {
-			_menuSystem->setSavegameSlotNum(slot);
+        if (slot >= 0) {
+            _menuSystem->setSavegameSlotNum(slot);
+            _menuSystem->setSavegameDescription(desc);
 			_menuSystem->selectMenuChoiceIndex(_choiceIndex);
 		}
 	}
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index 26324c6..6262cef 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -103,6 +103,7 @@ public:
 	void setMenuCallerThreadId(uint32 menuCallerThreadId);
 	void setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset);
 	void setSavegameSlotNum(int slotNum);
+	void setSavegameDescription(Common::String desc);
 	virtual bool initMenuCursor() = 0;
 	virtual int getGameState() = 0;
 	virtual void setGameState(int gameState) = 0;


Commit: 23ae4685425eaf4a46e85daf9480023f7d04add0
    https://github.com/scummvm/scummvm/commit/23ae4685425eaf4a46e85daf9480023f7d04add0
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
Work on options menu

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/textdrawer.cpp


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index e16e0a2..5ac5e29 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -81,6 +81,8 @@ BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
 		return createQueryQuitMenu();
 	case kDuckmanSaveCompleteMenu:
 		return createSaveCompleteMenu();
+	case kDuckmanOptionsMenu:
+		return createOptionsMenu();
 	default:
 		error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId);
 	}
@@ -100,18 +102,27 @@ BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
 }
 
 BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
-	return 0; // TODO
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
+	menu->addText("              GAME OPTIONS");
+	menu->addText("--------------------------------------");
+	menu->addMenuItem(new MenuItem("SFX Volume     @@{~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Music Volume  @@@{~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Speech Volume {~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Text Duration @@@{~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
+	return menu;
 }
 
 BaseMenu *DuckmanMenuSystem::createPauseMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
 	menu->addText("   Game Paused");
-	menu->addText("-------------------");
+	menu->addText("--------------------");
 	menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 21)));
 	menu->addMenuItem(new MenuItem("Load Game", new MenuActionLoadGame(this, 1)));
 	menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
-	// TODO menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
-	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
+	menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
+	menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
 	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
 	return menu;
 }
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index 2456fe4..e562732 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -28,7 +28,7 @@ namespace Illusions {
 
 bool TextDrawer::wrapText(FontResource *font, uint16 *text, WidthHeight *dimensions, Common::Point offsPt,
 	uint textFlags, uint16 *&outTextPtr) {
-	_font = font;	
+	_font = font;
 	_text = text;
 	_dimensions = dimensions;
 	_offsPt = offsPt;
@@ -52,7 +52,7 @@ void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 col
 			debugN("%c", c);
 		}
 		debug(" ");
-#endif		
+#endif
 	}
 }
 	


Commit: b3e9c972f1f43b6f571acbc3e00bcbcb23bce891
    https://github.com/scummvm/scummvm/commit/b3e9c972f1f43b6f571acbc3e00bcbcb23bce891
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Improve naming of variables and methods in menu system.

Start work on keyboard menu support and menu background fill.

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 5ac5e29..621a49c 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -103,14 +103,14 @@ BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
 
 BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
-	menu->addText("              GAME OPTIONS");
+	menu->addText("              GAME OPTIONS             @@@@");
 	menu->addText("--------------------------------------");
-	menu->addMenuItem(new MenuItem("SFX Volume     @@{~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Music Volume  @@@{~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Speech Volume {~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Text Duration @@@{~~~~~~~~~~~~|~~~}", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
+	menu->addMenuItem(new MenuItem("SFX Volume     @@{~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Music Volume  @@@{~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Speech Volume {~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Text Duration @@@{~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Restore Defaults                      @@", new MenuActionReturnChoice(this, 21)));
+	menu->addMenuItem(new MenuItem("Back                                       @@@", new MenuActionLeaveMenu(this)));
 	return menu;
 }
 
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index f32bdc6..fe82a7f 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -50,9 +50,9 @@ void MenuItem::executeAction() {
 
 // BaseMenu
 
-BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE,
+BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte textColor, byte fieldE,
 	uint defaultMenuItemIndex)
-	: _menuSystem(menuSystem), _fontId(fontId), _field8(field8), _fieldA(fieldA), _fieldC(fieldC), _fieldE(fieldE),
+	: _menuSystem(menuSystem), _fontId(fontId), _field8(field8), _fieldA(fieldA), _textColor(textColor), _fieldE(fieldE),
 	_defaultMenuItemIndex(defaultMenuItemIndex)
 {
 }
@@ -133,8 +133,8 @@ void BaseMenuSystem::enterSubMenu(BaseMenu *menu) {
 	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
 	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
 	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
-	placeActor318();
-	placeActor323();
+	placeActorHoverBackground();
+	placeActorTextColorRect();
 }
 
 void BaseMenuSystem::leaveSubMenu() {
@@ -148,8 +148,8 @@ void BaseMenuSystem::leaveSubMenu() {
 	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
 	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
 	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
-	initActor318();
-	placeActor323();
+	initActorHoverBackground();
+	placeActorTextColorRect();
 }
 
 void BaseMenuSystem::enterSubMenuById(int menuId) {
@@ -216,7 +216,7 @@ bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIn
 }
 
 void BaseMenuSystem::setMousePos(Common::Point &mousePos) {
-	//TODO Strange behavior _vm->_input->setCursorPosition(mousePos);
+	_vm->_input->setCursorPosition(mousePos);
 	Control *mouseCursor = _vm->getObjectControl(0x40004);
 	mouseCursor->_actor->_position = mousePos;
 }
@@ -237,7 +237,7 @@ void BaseMenuSystem::activateMenu(BaseMenu *menu) {
 
 }
 
-void BaseMenuSystem::initActor318() {
+void BaseMenuSystem::initActorHoverBackground() {
 	Control *v0 = _vm->getObjectControl(0x4013E);
 	if (!v0) {
 		WidthHeight dimensions;
@@ -247,11 +247,11 @@ void BaseMenuSystem::initActor318() {
 		v0 = _vm->getObjectControl(0x4013E);
 		v0->_flags |= 8;
 	}
-	placeActor318();
+	placeActorHoverBackground();
 	v0->appearActor();
 }	
 
-void BaseMenuSystem::placeActor318() {
+void BaseMenuSystem::placeActorHoverBackground() {
 	Control *v0 = _vm->getObjectControl(0x4013E);
 	v0->fillActor(0);
 	
@@ -272,24 +272,24 @@ void BaseMenuSystem::placeActor318() {
 		charHeight = frameDimensions._height;
 		
 	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight - 1), _activeMenu->_fieldE);
-	
-	updateActor318();
+
+	updateActorHoverBackground();
 }
 
-void BaseMenuSystem::updateActor318() {
+void BaseMenuSystem::updateActorHoverBackground() {
 	Control *v0 = _vm->getObjectControl(0x4013E);
 	WRect rect;
 	calcMenuItemRect(_hoveredMenuItemIndex2 - _hoveredMenuItemIndex3 + 1, rect);
   	v0->setActorPosition(rect._topLeft);
 }
 
-void BaseMenuSystem::hideActor318() {
+void BaseMenuSystem::hideActorHoverBackground() {
 	Control *v0 = _vm->getObjectControl(0x4013E);
 	if (v0)
 		v0->disappearActor();
 }
 
-void BaseMenuSystem::initActor323() {
+void BaseMenuSystem::initActorTextColorRect() {
 	Control *v0 = _vm->getObjectControl(0x40143);
 	if (!v0) {
 		WidthHeight dimensions;
@@ -299,11 +299,11 @@ void BaseMenuSystem::initActor323() {
 		v0 = _vm->getObjectControl(0x40143);
 		v0->_flags |= 8;
 	}
-	placeActor323();
+	placeActorTextColorRect();
 	v0->appearActor();
 }
 
-void BaseMenuSystem::placeActor323() {
+void BaseMenuSystem::placeActorTextColorRect() {
 	Control *v0 = _vm->getObjectControl(0x40143);
 	v0->fillActor(0);
 	
@@ -312,19 +312,19 @@ void BaseMenuSystem::placeActor323() {
 	_vm->_screenText->getTextInfoPosition(textInfoPosition);
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 	
-	/* TODO	
+	/* TODO
 	if (_activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) {
 		textInfoDimensions._width -= 2;
 		textInfoDimensions._height -= 6;
 	}
-	*/	
+	*/
 
 	v0->setActorPosition(textInfoPosition);
-	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_fieldC);
+	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_textColor);
 	
 }
 
-void BaseMenuSystem::hideActor323() {
+void BaseMenuSystem::hideActorTextColorRect() {
 	Control *v0 = _vm->getObjectControl(0x40143);
 	if (v0)
 		v0->disappearActor();
@@ -351,8 +351,8 @@ void BaseMenuSystem::openMenu(BaseMenu *menu) {
 	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
 	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
 	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
-	initActor318();
-	initActor323();
+	initActorHoverBackground();
+	initActorTextColorRect();
 	_vm->_input->discardAllEvents();
 }
 
@@ -362,8 +362,8 @@ void BaseMenuSystem::closeMenu() {
 		_menuStack.pop();
 	}
 	_vm->_screenText->removeText();
-	hideActor318();
-	hideActor323();
+	hideActorHoverBackground();
+	hideActorTextColorRect();
 	Control *mouseCursor = _vm->getObjectControl(0x40004);
 	setGameState(_savedGameState);
 	mouseCursor->_actor->_actorIndex = _savedCursorActorIndex;
@@ -448,16 +448,16 @@ void BaseMenuSystem::update(Control *cursorControl) {
 	if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex)) {
 		if (newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
 			if (_hoveredMenuItemIndex == 0)
-				initActor318();
+				initActorHoverBackground();
 			_hoveredMenuItemIndex = newHoveredMenuItemIndex;
 			_hoveredMenuItemIndex2 = newHoveredMenuItemIndex;
 			setMenuCursorNum(2);
-			updateActor318();
+			updateActorHoverBackground();
 			resetTimeOut = true;
 		}
 	} else if (_hoveredMenuItemIndex != 0) {
 		setMenuCursorNum(1);
-		hideActor318();
+		hideActorHoverBackground();
 		_hoveredMenuItemIndex = 0;
 		resetTimeOut = true;
 	}
@@ -471,8 +471,17 @@ void BaseMenuSystem::update(Control *cursorControl) {
 		handleClick(_activeMenu->_defaultMenuItemIndex, mousePos);
 	} else if (_vm->_input->pollEvent(kEventUp)) {
 		// TODO handleUpKey();
+		if (_hoveredMenuItemIndex > 0) {
+			setMouseCursorToMenuItem(--_hoveredMenuItemIndex);
+			_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
+			updateActorHoverBackground();
+		}
+
 	} else if (_vm->_input->pollEvent(kEventDown)) {
 		// TODO handleDownKey();
+		setMouseCursorToMenuItem(++_hoveredMenuItemIndex);
+		_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
+		updateActorHoverBackground();
 	}
 	
 	updateTimeOut(resetTimeOut);
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index 6262cef..a7ef083 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -54,7 +54,7 @@ protected:
 
 class BaseMenu {
 public:
-	BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE,
+	BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte textColor, byte fieldE,
 		uint defaultMenuItemIndex);
 	virtual ~BaseMenu();
 	void addText(const Common::String text);
@@ -68,7 +68,7 @@ public://protected://TODO
 	typedef Common::Array<MenuItem*> MenuItems;
 	BaseMenuSystem *_menuSystem;
 	uint32 _fontId;
-	byte _field8, _fieldA, _fieldC, _fieldE;
+	byte _field8, _fieldA, _textColor, _fieldE;
 	uint _field2C18;
 	uint _defaultMenuItemIndex;
 	Common::Array<Common::String> _text;
@@ -153,14 +153,14 @@ protected:
 	
 	void updateTimeOut(bool resetTimeOut);
 	
-	void initActor318();
-	void placeActor318();
-	void updateActor318();
-	void hideActor318();
+	void initActorHoverBackground();
+	void placeActorHoverBackground();
+	void updateActorHoverBackground();
+	void hideActorHoverBackground();
 	
-	void initActor323();
-	void placeActor323();
-	void hideActor323();
+	void initActorTextColorRect();
+	void placeActorTextColorRect();
+	void hideActorTextColorRect();
 	
 	virtual BaseMenu *getMenuById(int menuId) = 0;
 };


Commit: a6cd908e0efd17fe0835f929953c4e1260ac297b
    https://github.com/scummvm/scummvm/commit/a6cd908e0efd17fe0835f929953c4e1260ac297b
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Replace text flag magic numbers with defines

Changed paths:
    engines/illusions/menusystem.cpp
    engines/illusions/screentext.h
    engines/illusions/textdrawer.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index fe82a7f..266d0b9 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -416,9 +416,9 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 	textPt.x = v9;
 	textPt.y = v9;
 
-	uint flags = 1;
+	uint flags = TEXT_FLAG_LEFT_ALIGN;
 	if (menu->_field8 != menu->_fieldA)
-		flags = 25;
+		flags |= TEXT_FLAG_BORDER_DECORATION;
 		
 	WidthHeight dimensions;
 	dimensions._width = 300;
diff --git a/engines/illusions/screentext.h b/engines/illusions/screentext.h
index 6b365e5..e71b6cc 100644
--- a/engines/illusions/screentext.h
+++ b/engines/illusions/screentext.h
@@ -30,6 +30,11 @@
 
 namespace Illusions {
 
+#define TEXT_FLAG_LEFT_ALIGN 1
+#define TEXT_FLAG_CENTER_ALIGN 2
+#define TEXT_FLAG_RIGHT_ALIGN 4
+#define TEXT_FLAG_BORDER_DECORATION 24
+
 class IllusionsEngine;
 class FontResource;
 
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index e562732..1d1ac57 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -23,6 +23,7 @@
 #include "illusions/illusions.h"
 #include "illusions/textdrawer.h"
 #include "illusions/screen.h"
+#include "illusions/screentext.h"
 
 namespace Illusions {
 
@@ -111,10 +112,10 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 			if (textPosY + _font->_charHeight <= maxHeight) {
 				int16 textPosX;
 
-				if (_textFlags & 2) {
+				if (_textFlags & TEXT_FLAG_CENTER_ALIGN) {
 					textPosX = (_dimensions->_width - currLineWidth) / 2;
 					maxLineWidth = _dimensions->_width;
-				} else if (_textFlags & 4) {
+				} else if (_textFlags & TEXT_FLAG_RIGHT_ALIGN) {
 					textPosX = _dimensions->_width - currLineWidth;
 				} else {
 					textPosX = x;
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index e9c3e36..f8b4c15 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -302,7 +302,7 @@ int TalkThread::insertText() {
 	_vm->getDefaultTextDimensions(dimensions);
 	uint16 *outTextPtr;
 	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
-		Common::Point(0, 0), 2, 0, 0, 0, 0, 0, outTextPtr);
+		Common::Point(0, 0), TEXT_FLAG_CENTER_ALIGN, 0, 0, 0, 0, 0, outTextPtr);
 	_entryText = (byte*)outTextPtr;
 	Common::Point pt;
 	_vm->getDefaultTextPosition(pt);
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index f891301..07fe1bb 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -310,7 +310,7 @@ int TalkThread_Duckman::insertText() {
 	_vm->getDefaultTextDimensions(dimensions);
 	uint16 *outTextPtr;
 	_vm->_screenText->insertText((uint16*)_currEntryText, 0x120001, dimensions,
-		Common::Point(0, 0), 2, 0, 0, _color.r, _color.g, _color.b, outTextPtr);
+		Common::Point(0, 0), TEXT_FLAG_CENTER_ALIGN, 0, 0, _color.r, _color.g, _color.b, outTextPtr);
 	_entryText = (byte*)outTextPtr;
 	Common::Point pt;
 	_vm->getDefaultTextPosition(pt);


Commit: 8e462f313fa195ce532a7a684d17a487cbdd4613
    https://github.com/scummvm/scummvm/commit/8e462f313fa195ce532a7a684d17a487cbdd4613
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Work on menu border decoration

Changed paths:
    engines/illusions/menusystem.cpp
    engines/illusions/resources/fontresource.h
    engines/illusions/screentext.cpp
    engines/illusions/textdrawer.cpp


diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 266d0b9..5002484 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -258,11 +258,9 @@ void BaseMenuSystem::placeActorHoverBackground() {
 	WidthHeight textInfoDimensions;
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 
-	/* TODO	
 	if ( _activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8)
 		textInfoDimensions._width -= 6;
-		*/
-		
+
 	WidthHeight frameDimensions;
 	v0->getActorFrameDimensions(frameDimensions);
 	
@@ -312,12 +310,10 @@ void BaseMenuSystem::placeActorTextColorRect() {
 	_vm->_screenText->getTextInfoPosition(textInfoPosition);
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 	
-	/* TODO
 	if (_activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) {
 		textInfoDimensions._width -= 2;
 		textInfoDimensions._height -= 6;
 	}
-	*/
 
 	v0->setActorPosition(textInfoPosition);
 	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_textColor);
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
index fa758bc..6204c9d 100644
--- a/engines/illusions/resources/fontresource.h
+++ b/engines/illusions/resources/fontresource.h
@@ -81,11 +81,11 @@ public:
 
 class FontInstance : public ResourceInstance {
 public:
-	FontInstance(IllusionsEngine *vm);              
+	FontInstance(IllusionsEngine *vm);
 	virtual void load(Resource *resource);
 	virtual void unload();
 public:
-	IllusionsEngine *_vm;	
+	IllusionsEngine *_vm;
 	FontResource *_fontResource;
 	uint32 _resId;
 };
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 0a91570..0894330 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -88,6 +88,10 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 	uint16 *text, uint textFlags, uint16 color2, uint16 color1, uint16 *&outTextPtr) {
 	TextDrawer textDrawer;
 	bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr);
+	if (textFlags & TEXT_FLAG_BORDER_DECORATION) {
+		dimensions._width += 11;
+		dimensions._height += 14;
+	}
 	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
 	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1());
 	_dimensions = dimensions;
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index 1d1ac57..e743330 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -43,10 +43,27 @@ bool TextDrawer::wrapText(FontResource *font, uint16 *text, WidthHeight *dimensi
 
 void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 color2, uint16 color1) {
 	// TODO Fill box, draw borders and shadow if flags are set
+	uint16 x = 0;
+	uint16 y = 0;
+
+	if (_textFlags & TEXT_FLAG_BORDER_DECORATION) {
+		surface->frameRect(Common::Rect(0, 0, surface->w - 3, surface->h - 6), color1);
+
+		surface->fillRect(Common::Rect(1, 1, surface->w - 4, 4), color2);
+		surface->fillRect(Common::Rect(1, surface->h - 10, surface->w - 4, surface->h - 7), color2);
+		surface->fillRect(Common::Rect(1, 4, 4, surface->h - 10), color2);
+		surface->fillRect(Common::Rect(surface->w - 7, 4, surface->w - 4, surface->h - 10), color2);
+
+		surface->fillRect(Common::Rect(3, surface->h - 7, surface->w, surface->h), color1);
+		surface->fillRect(Common::Rect(surface->w - 3, 6, surface->w, surface->h), color1);
+		x = 4;
+		y = 4;
+	}
+
 	for (Common::Array<TextLine>::iterator it = _textLines.begin(); it != _textLines.end(); ++it) {
 		const TextLine &textLine = *it;
 		if (textLine._text)
-			screen->drawText(_font, surface, textLine._x, textLine._y, textLine._text, textLine._length);
+			screen->drawText(_font, surface, textLine._x + x, textLine._y + y, textLine._text, textLine._length);
 #if 0
 		for (int16 linePos = 0; linePos < textLine._length; ++linePos) {
 			const uint16 c = textLine._text[linePos];


Commit: a36cd2e39fce85e5cc1024322767284ba9b8e3b4
    https://github.com/scummvm/scummvm/commit/a36cd2e39fce85e5cc1024322767284ba9b8e3b4
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Name menu border color variables to add readability

Fix menu hover color layer position.

Changed paths:
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h
    engines/illusions/screentext.cpp
    engines/illusions/screentext.h
    engines/illusions/textdrawer.cpp
    engines/illusions/textdrawer.h


diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 5002484..391cded 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -50,9 +50,9 @@ void MenuItem::executeAction() {
 
 // BaseMenu
 
-BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte textColor, byte fieldE,
+BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte backgroundColor, byte borderColor, byte textColor, byte fieldE,
 	uint defaultMenuItemIndex)
-	: _menuSystem(menuSystem), _fontId(fontId), _field8(field8), _fieldA(fieldA), _textColor(textColor), _fieldE(fieldE),
+	: _menuSystem(menuSystem), _fontId(fontId), _backgroundColor(backgroundColor), _borderColor(borderColor), _textColor(textColor), _fieldE(fieldE),
 	_defaultMenuItemIndex(defaultMenuItemIndex)
 {
 }
@@ -176,12 +176,10 @@ void BaseMenuSystem::calcMenuItemRect(uint menuItemIndex, WRect &rect) {
 	int charHeight = font->getCharHeight() + font->getLineIncr();
 	
 	_vm->_screenText->getTextInfoPosition(rect._topLeft);
-	/* TODO
-	if (_activeMenu->_field8) {
+	if (_activeMenu->_backgroundColor) {
 		rect._topLeft.y += 4;
 		rect._topLeft.x += 4;
 	}
-	*/	
 	rect._topLeft.y += charHeight * (menuItemIndex + _menuLinesCount - 1);
 
 	WidthHeight textInfoDimensions;
@@ -258,7 +256,7 @@ void BaseMenuSystem::placeActorHoverBackground() {
 	WidthHeight textInfoDimensions;
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 
-	if ( _activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8)
+	if ( _activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor)
 		textInfoDimensions._width -= 6;
 
 	WidthHeight frameDimensions;
@@ -269,7 +267,7 @@ void BaseMenuSystem::placeActorHoverBackground() {
 	if (frameDimensions._height < charHeight)
 		charHeight = frameDimensions._height;
 		
-	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight - 1), _activeMenu->_fieldE);
+	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight), _activeMenu->_fieldE);
 
 	updateActorHoverBackground();
 }
@@ -310,7 +308,7 @@ void BaseMenuSystem::placeActorTextColorRect() {
 	_vm->_screenText->getTextInfoPosition(textInfoPosition);
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 	
-	if (_activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) {
+	if (_activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor) {
 		textInfoDimensions._width -= 2;
 		textInfoDimensions._height -= 6;
 	}
@@ -407,13 +405,13 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 
 	Common::Point textPt;
 	int16 v9 = 0;
-	if (menu->_field8)
+	if (menu->_backgroundColor)
 		v9 = 4;
 	textPt.x = v9;
 	textPt.y = v9;
 
 	uint flags = TEXT_FLAG_LEFT_ALIGN;
-	if (menu->_field8 != menu->_fieldA)
+	if (menu->_backgroundColor != menu->_borderColor)
 		flags |= TEXT_FLAG_BORDER_DECORATION;
 		
 	WidthHeight dimensions;
@@ -421,7 +419,7 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 	dimensions._height = 180;
 	
 	uint16 *outTextPtr;
-	if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_field8, menu->_fieldA, 0xFF, 0xFF, 0xFF, outTextPtr)) {
+	if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_backgroundColor, menu->_borderColor, 0xFF, 0xFF, 0xFF, outTextPtr)) {
 		--lineCount;
 		for ( ; *outTextPtr; ++outTextPtr) {
 			if (*outTextPtr == 13)
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index a7ef083..d9961f8 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -54,7 +54,7 @@ protected:
 
 class BaseMenu {
 public:
-	BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte textColor, byte fieldE,
+	BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte backgroundColor, byte borderColor, byte textColor, byte fieldE,
 		uint defaultMenuItemIndex);
 	virtual ~BaseMenu();
 	void addText(const Common::String text);
@@ -68,7 +68,7 @@ public://protected://TODO
 	typedef Common::Array<MenuItem*> MenuItems;
 	BaseMenuSystem *_menuSystem;
 	uint32 _fontId;
-	byte _field8, _fieldA, _textColor, _fieldE;
+	byte _backgroundColor, _borderColor, _textColor, _fieldE;
 	uint _field2C18;
 	uint _defaultMenuItemIndex;
 	Common::Array<Common::String> _text;
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 0894330..de664d6 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -85,7 +85,7 @@ void ScreenText::clipTextInfoPosition(Common::Point &position) {
 }
 
 bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, Common::Point offsPt,
-	uint16 *text, uint textFlags, uint16 color2, uint16 color1, uint16 *&outTextPtr) {
+	uint16 *text, uint textFlags, uint16 backgroundColor, uint16 borderColor, uint16 *&outTextPtr) {
 	TextDrawer textDrawer;
 	bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr);
 	if (textFlags & TEXT_FLAG_BORDER_DECORATION) {
@@ -95,12 +95,12 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
 	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1());
 	_dimensions = dimensions;
-	textDrawer.drawText(_vm->_screen, _surface, color2, color1);
+	textDrawer.drawText(_vm->_screen, _surface, backgroundColor, borderColor);
 	return done;
 }
 
 bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions, Common::Point offsPt, uint flags,
-	uint16 color2, uint16 color1, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr) {
+	uint16 backgroundColor, uint16 borderColor, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr) {
 	
 	if (!_screenTexts.empty()) {
 		ScreenTextEntry *screenText = _screenTexts.back();
@@ -121,8 +121,8 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 		screenText->_info._flags |= 1;
 	else
 		screenText->_info._flags |= 2;
-	screenText->_info._color2 = color2;
-	screenText->_info._color1 = color1;
+	screenText->_info._backgroundColor = backgroundColor;
+	screenText->_info._borderColor = borderColor;
 	screenText->_info._colorR = colorR;
 	screenText->_info._colorG = colorG;
 	screenText->_info._colorB = colorB;
@@ -130,7 +130,7 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 
 	FontResource *font = _vm->_dict->findFont(screenText->_info._fontId);
 	bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
-		text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
+		text, screenText->_info._flags, screenText->_info._backgroundColor, screenText->_info._borderColor,
 		outTextPtr);
 	_vm->_screenPalette->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 
@@ -159,7 +159,7 @@ void ScreenText::removeText() {
 			uint16 *outTextPtr;
 			FontResource *font = _vm->_dict->findFont(screenText->_info._fontId);
 			refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt,
-				screenText->_text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1,
+				screenText->_text, screenText->_info._flags, screenText->_info._backgroundColor, screenText->_info._borderColor,
 				outTextPtr);
 			_vm->_screenPalette->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 			setTextInfoPosition(screenText->_info._position);
diff --git a/engines/illusions/screentext.h b/engines/illusions/screentext.h
index e71b6cc..f8953e3 100644
--- a/engines/illusions/screentext.h
+++ b/engines/illusions/screentext.h
@@ -43,8 +43,8 @@ struct ScreenTextInfo {
 	WidthHeight _dimensions;
 	Common::Point _offsPt;
 	uint32 _fontId;
-	uint16 _color2;
-	uint16 _color1;
+	uint16 _backgroundColor;
+	uint16 _borderColor;
 	byte _colorR, _colorG, _colorB;
 	uint _flags;
 };
@@ -64,9 +64,9 @@ public:
 	void updateTextInfoPosition(Common::Point position);
 	void clipTextInfoPosition(Common::Point &position);
 	bool refreshScreenText(FontResource *font, WidthHeight dimensions, Common::Point offsPt,
-		uint16 *text, uint textFlags, uint16 color2, uint16 color1, uint16 *&outTextPtr);
+		uint16 *text, uint textFlags, uint16 backgroundColor, uint16 borderColor, uint16 *&outTextPtr);
 	bool insertText(uint16 *text, uint32 fontId, WidthHeight dimensions, Common::Point offsPt, uint flags,
-		uint16 color2, uint16 color1, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr);
+		uint16 backgroundColor, uint16 borderColor, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr);
 	void removeText();
 	void clearText();
 public:
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index e743330..b1c9658 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -41,21 +41,21 @@ bool TextDrawer::wrapText(FontResource *font, uint16 *text, WidthHeight *dimensi
 	return done;
 }
 
-void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 color2, uint16 color1) {
+void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 backgroundColor, uint16 borderColor) {
 	// TODO Fill box, draw borders and shadow if flags are set
 	uint16 x = 0;
 	uint16 y = 0;
 
 	if (_textFlags & TEXT_FLAG_BORDER_DECORATION) {
-		surface->frameRect(Common::Rect(0, 0, surface->w - 3, surface->h - 6), color1);
+		surface->frameRect(Common::Rect(0, 0, surface->w - 3, surface->h - 6), borderColor);
 
-		surface->fillRect(Common::Rect(1, 1, surface->w - 4, 4), color2);
-		surface->fillRect(Common::Rect(1, surface->h - 10, surface->w - 4, surface->h - 7), color2);
-		surface->fillRect(Common::Rect(1, 4, 4, surface->h - 10), color2);
-		surface->fillRect(Common::Rect(surface->w - 7, 4, surface->w - 4, surface->h - 10), color2);
+		surface->fillRect(Common::Rect(1, 1, surface->w - 4, 4), backgroundColor);
+		surface->fillRect(Common::Rect(1, surface->h - 10, surface->w - 4, surface->h - 7), backgroundColor);
+		surface->fillRect(Common::Rect(1, 4, 4, surface->h - 10), backgroundColor);
+		surface->fillRect(Common::Rect(surface->w - 7, 4, surface->w - 4, surface->h - 10), backgroundColor);
 
-		surface->fillRect(Common::Rect(3, surface->h - 7, surface->w, surface->h), color1);
-		surface->fillRect(Common::Rect(surface->w - 3, 6, surface->w, surface->h), color1);
+		surface->fillRect(Common::Rect(3, surface->h - 7, surface->w, surface->h), borderColor);
+		surface->fillRect(Common::Rect(surface->w - 3, 6, surface->w, surface->h), borderColor);
 		x = 4;
 		y = 4;
 	}
diff --git a/engines/illusions/textdrawer.h b/engines/illusions/textdrawer.h
index 290d6c7..68b28e0 100644
--- a/engines/illusions/textdrawer.h
+++ b/engines/illusions/textdrawer.h
@@ -46,7 +46,7 @@ class TextDrawer {
 public:
 	bool wrapText(FontResource *font, uint16 *text, WidthHeight *dimensions, Common::Point offsPt,
 		uint textFlags, uint16 *&outTextPtr);
-	void drawText(Screen *screen, Graphics::Surface *surface, uint16 color2, uint16 color1);
+	void drawText(Screen *screen, Graphics::Surface *surface, uint16 backgroundColor, uint16 borderColor);
 protected:
 	FontResource *_font;
 	uint16 *_text;


Commit: 43ba5f6327c21e60c9c73b2f2f93e71dbde7bf08
    https://github.com/scummvm/scummvm/commit/43ba5f6327c21e60c9c73b2f2f93e71dbde7bf08
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fill in background for text in menus

Changed paths:
    engines/illusions/resources/fontresource.cpp
    engines/illusions/resources/fontresource.h
    engines/illusions/textdrawer.cpp


diff --git a/engines/illusions/resources/fontresource.cpp b/engines/illusions/resources/fontresource.cpp
index 29aaf9b..4c89708 100644
--- a/engines/illusions/resources/fontresource.cpp
+++ b/engines/illusions/resources/fontresource.cpp
@@ -110,6 +110,15 @@ CharInfo *FontResource::getCharInfo(uint16 c) {
 	return 0;
 }
 
+const Common::Rect FontResource::calculateRectForText(uint16 *text, uint textLength) {
+	int16 width = 0;
+	for (uint i = 0; i < textLength && *text; i++) {
+		width += getCharInfo(*text)->_width;
+		text++;
+	}
+	return Common::Rect(width, getCharHeight() + getLineIncr());
+}
+
 // FontInstance
 
 FontInstance::FontInstance(IllusionsEngine *vm) : _vm(vm) {
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
index 6204c9d..1a433a2 100644
--- a/engines/illusions/resources/fontresource.h
+++ b/engines/illusions/resources/fontresource.h
@@ -67,6 +67,7 @@ public:
 	int16 getColorIndex() const { return _colorIndex; }
 	int16 getCharHeight() const { return _charHeight; }
 	int16 getLineIncr() const { return _lineIncr; }
+	const Common::Rect calculateRectForText(uint16 *text, uint textLength);
 public:
 	uint32 _totalSize;
 	int16 _charHeight;
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index b1c9658..a5679b0 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -42,7 +42,6 @@ bool TextDrawer::wrapText(FontResource *font, uint16 *text, WidthHeight *dimensi
 }
 
 void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 backgroundColor, uint16 borderColor) {
-	// TODO Fill box, draw borders and shadow if flags are set
 	uint16 x = 0;
 	uint16 y = 0;
 
@@ -62,8 +61,15 @@ void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 bac
 
 	for (Common::Array<TextLine>::iterator it = _textLines.begin(); it != _textLines.end(); ++it) {
 		const TextLine &textLine = *it;
-		if (textLine._text)
+		if (textLine._text) {
 			screen->drawText(_font, surface, textLine._x + x, textLine._y + y, textLine._text, textLine._length);
+			if (_textFlags & TEXT_FLAG_BORDER_DECORATION) {
+				Common::Rect textRect = _font->calculateRectForText(textLine._text, textLine._length);
+				// Fill in remainder of text line with background color.
+				surface->fillRect(Common::Rect(textLine._x + x + textRect.right, textLine._y + y,
+											   surface->w - 4, textLine._y + y + textRect.bottom), backgroundColor);
+			}
+		}
 #if 0
 		for (int16 linePos = 0; linePos < textLine._length; ++linePos) {
 			const uint16 c = textLine._text[linePos];
@@ -73,7 +79,7 @@ void TextDrawer::drawText(Screen *screen, Graphics::Surface *surface, uint16 bac
 #endif
 	}
 }
-	
+
 bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeight, uint16 *&outTextPtr) {
 
 	bool lineBreak = false;


Commit: 28b0acc6c22efcb7c7cad6514c706e6b81e979d9
    https://github.com/scummvm/scummvm/commit/28b0acc6c22efcb7c7cad6514c706e6b81e979d9
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix bug when setting property timers.

Illusions engine variable wasn't wired up

Changed paths:
    engines/illusions/duckman/propertytimers.cpp
    engines/illusions/duckman/propertytimers.h


diff --git a/engines/illusions/duckman/propertytimers.cpp b/engines/illusions/duckman/propertytimers.cpp
index 47d8cb4..cdfdd92 100644
--- a/engines/illusions/duckman/propertytimers.cpp
+++ b/engines/illusions/duckman/propertytimers.cpp
@@ -32,6 +32,7 @@ namespace Illusions {
 // PropertyTimers
 
 PropertyTimers::PropertyTimers(IllusionsEngine_Duckman *vm) {
+	_vm = vm;
 	_propertyTimersActive = false;
 	_propertyTimersPaused = false;
 }
diff --git a/engines/illusions/duckman/propertytimers.h b/engines/illusions/duckman/propertytimers.h
index d4b7306..0c4d378 100644
--- a/engines/illusions/duckman/propertytimers.h
+++ b/engines/illusions/duckman/propertytimers.h
@@ -45,11 +45,12 @@ class PropertyTimers {
 public:
 	PropertyTimers(IllusionsEngine_Duckman *vm);
 	~PropertyTimers();
-public:
-	IllusionsEngine_Duckman *_vm;	
+private:
+	IllusionsEngine_Duckman *_vm;
 	PropertyTimer _propertyTimers[kPropertyTimersCount];
 	bool _propertyTimersActive;
 	bool _propertyTimersPaused;
+public:
 	void addPropertyTimer(uint32 propertyId);
 	void setPropertyTimer(uint32 propertyId, uint32 duration);
 	void removePropertyTimer(uint32 propertyId);


Commit: 6ae95550864e126f3e8634183f32ac831ace5d5f
    https://github.com/scummvm/scummvm/commit/6ae95550864e126f3e8634183f32ac831ace5d5f
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix valgrind warnings about usage of uninitialised variables

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/updatefunctions.cpp


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 9bb40c3..7957001 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -70,6 +70,7 @@ IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *
 	_resumeFromSavegameRequested = false;
 	_savegameSceneId = 0;
 	_savegameThreadId = 0;
+	_nextTempThreadId = 0;
 
 	Engine::syncSoundSettings();
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 7154467..ac6ff3b 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -109,7 +109,7 @@ public:
 	void updateEvents();
 
 	Screen *_screen;
-	ScreenPaletteBase *_screenPalette;	
+	ScreenPaletteBase *_screenPalette;
 	ScreenText *_screenText;
 	Input *_input;
 	ActorInstanceList *_actorInstances;
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index 86aaf55..7317508 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -30,6 +30,7 @@ namespace Illusions {
 // UpdateFunctions
 
 UpdateFunctions::UpdateFunctions() {
+	_lastTimerUpdateTime = 0;
 }
 
 UpdateFunctions::~UpdateFunctions() {


Commit: 2d836d4ec0f0b13c340e48aeafb651008c6771c3
    https://github.com/scummvm/scummvm/commit/2d836d4ec0f0b13c340e48aeafb651008c6771c3
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
VIDEO: Warn instead of error when unhandled TXTS stream found in AVI

Changed paths:
    video/avi_decoder.cpp


diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index 7d18293..a42c8e2 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -266,9 +266,12 @@ void AVIDecoder::handleStreamHeader(uint32 size) {
 	sHeader.size = size;
 	sHeader.streamType = _fileStream->readUint32BE();
 
-	if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS)
+	if (sHeader.streamType == ID_MIDS)
 		error("Unhandled MIDI/Text stream");
 
+	if (sHeader.streamType == ID_TXTS)
+		warning("Unsupported Text stream detected");
+
 	sHeader.streamHandler = _fileStream->readUint32BE();
 	sHeader.flags = _fileStream->readUint32LE();
 	sHeader.priority = _fileStream->readUint16LE();


Commit: 9be0a7b08589163d9c0ff151d3fcd15e0e766bba
    https://github.com/scummvm/scummvm/commit/9be0a7b08589163d9c0ff151d3fcd15e0e766bba
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Play video files

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.cpp


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index ad6ec9d..c864728 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -578,7 +578,7 @@ void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCa
 	ARG_SKIP(2);
 	ARG_UINT32(objectId);
 	// NOTE This has no attached objectId or priority
-	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
+	_vm->playVideo(0, objectId, 0, opCall._threadId);
 	
 	//DEBUG Resume calling thread, later done by the video player
 	_vm->notifyThreadId(opCall._threadId);
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 7957001..9224ba9 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -45,6 +45,8 @@
 #include "illusions/threads/talkthread.h"
 
 #include "audio/audiostream.h"
+#include "video/video_decoder.h"
+#include "video/avi_decoder.h"
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
 #include "common/error.h"
@@ -260,7 +262,42 @@ bool IllusionsEngine::calcPointDirection(Common::Point &srcPt, Common::Point &ds
 }
 
 void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
-	// TODO
+	Video::VideoDecoder *videoDecoder = new Video::AVIDecoder();
+	Common::String filename = Common::String::format("%08X.AVI", objectId);
+	if (!videoDecoder->loadFile(filename)) {
+		delete videoDecoder;
+		warning("Unable to open video %s", filename.c_str());
+		return;
+	}
+
+	videoDecoder->start();
+
+	bool skipVideo = false;
+
+	while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
+		if (videoDecoder->needsUpdate()) {
+			const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
+			if (videoDecoder->hasDirtyPalette()) {
+				const byte *palette = videoDecoder->getPalette();
+				_system->getPaletteManager()->setPalette(palette, 0, 256);
+			}
+
+			if (frame) {
+				_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
+				_system->updateScreen();
+			}
+		}
+
+		Common::Event event;
+		while (_eventMan->pollEvent(event)) {
+			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) ||
+				event.type == Common::EVENT_LBUTTONUP)
+				skipVideo = true;
+		}
+	}
+
+	videoDecoder->close();
+	delete videoDecoder;
 }
 
 bool IllusionsEngine::isSoundActive() {


Commit: 39798c63d1d6a7153527a726acfa3c1cf837760f
    https://github.com/scummvm/scummvm/commit/39798c63d1d6a7153527a726acfa3c1cf837760f
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix static buffer overrun in debug log function.

This was causing the game to crash when loading the main scumm menu.
It was corrupting another static string in the about dialog.

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/resources/scriptresource.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 9224ba9..7b2f5f9 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -61,6 +61,19 @@
 
 namespace Illusions {
 
+char *debugW2I(byte *wstr) {
+	static char buf[65];
+	char *p = buf;
+	int i = 0;
+	while (*wstr != 0 && i < sizeof(buf) - 1) {
+		*p++ = *wstr;
+		wstr += 2;
+		i++;
+	}
+	*p = 0;
+	return buf;
+}
+
 IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *gd) :
 	Engine(syst), _gameDescription(gd) {
 	
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index ac6ff3b..4b3efa2 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -44,6 +44,8 @@ struct ADGameDescription;
 
 namespace Illusions {
 
+char *debugW2I(byte *wstr);
+
 #define ILLUSIONS_SAVEGAME_VERSION 0
 
 class ResourceSystem;
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index 5472d28..53de6b4 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -217,17 +217,6 @@ SceneInfo::~SceneInfo() {
 	delete[] _resources;
 }
 
-char *debugW2I(byte *wstr) {
-	static char buf[65];
-	char *p = buf;
-	while (*wstr != 0) {
-		*p++ = *wstr;
-		wstr += 2;
-	}
-	*p = 0;
-	return buf;
-}
-
 void SceneInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_id = stream.readUint16LE();
 	_unk = stream.readUint16LE();
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index f8b4c15..5b54abc 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -285,17 +285,6 @@ void TalkThread::refreshText() {
 	_textEndTime = _textStartTime + _textDuration;
 }
 
-static char *debugW2I(byte *wstr) {
-	static char buf[65];
-	char *p = buf;
-	while (*wstr != 0) {
-		*p++ = *wstr;
-		wstr += 2;
-	}
-	*p = 0;
-	return buf;
-}
-
 int TalkThread::insertText() {
 	debug("%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
 	WidthHeight dimensions;
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 07fe1bb..2d6c455 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -293,17 +293,6 @@ void TalkThread_Duckman::refreshText() {
 	_textEndTime = _textStartTime + _textDuration;
 }
 
-static char *debugW2I(byte *wstr) {
-	static char buf[65];
-	char *p = buf;
-	while (*wstr != 0) {
-		*p++ = *wstr;
-		wstr += 2;
-	}
-	*p = 0;
-	return buf;
-}
-
 int TalkThread_Duckman::insertText() {
 	debug(0, "%08X %08X [%s]", _threadId, _talkId, debugW2I(_currEntryText));
 	WidthHeight dimensions;


Commit: a7d78df98cdd0399d338c0077efc474d02678643
    https://github.com/scummvm/scummvm/commit/a7d78df98cdd0399d338c0077efc474d02678643
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix clang warnings. Work on menu keyboard control

Fix some warnings while compiling with clang.
Add up/down key support in game menus.

Changed paths:
    engines/illusions/bbdou/bbdou_inventory.h
    engines/illusions/dictionary.h
    engines/illusions/gamestate.cpp
    engines/illusions/illusions.h
    engines/illusions/menusystem.cpp
    engines/illusions/pathfinder.cpp
    engines/illusions/resources/scriptresource.cpp


diff --git a/engines/illusions/bbdou/bbdou_inventory.h b/engines/illusions/bbdou/bbdou_inventory.h
index a5b55f4..b8fb8f0 100644
--- a/engines/illusions/bbdou/bbdou_inventory.h
+++ b/engines/illusions/bbdou/bbdou_inventory.h
@@ -32,7 +32,7 @@ namespace Illusions {
 class IllusionsEngine_BBDOU;
 class BbdouSpecialCode;
 class Control;
-class TriggerFunction;
+struct TriggerFunction;
 
 struct InventoryItem {
 	uint32 _objectId;
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index 63cb9b1..8a5476c 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -27,11 +27,11 @@
 
 namespace Illusions {
 
-class ActorType;
+struct ActorType;
 class Control;
 class FontResource;
-class Sequence;
-class TalkEntry;
+struct Sequence;
+struct TalkEntry;
 
 template<class T>
 class DictionaryHashMap {
diff --git a/engines/illusions/gamestate.cpp b/engines/illusions/gamestate.cpp
index 5f6e17c..caf8584 100644
--- a/engines/illusions/gamestate.cpp
+++ b/engines/illusions/gamestate.cpp
@@ -61,7 +61,7 @@ void GameState::deleteReadStream() {
 }
 
 Common::WriteStream *GameState::newWriteStream() {
-	if (!_writeBufferSize == 0 || !_writeBuffer) {
+	if (_writeBufferSize == 0 || !_writeBuffer) {
 		_writeBufferSize = calcWriteBufferSize();
 		_writeBuffer = (byte*)malloc(_writeBufferSize);
 	}
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 4b3efa2..12bd284 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -53,10 +53,8 @@ class BaseResourceReader;
 
 struct SurfInfo;
 
-class ActorItem;
 class ActorInstanceList;
-class ActorType;
-class BackgroundItem;
+struct ActorType;
 class BackgroundInstanceList;
 class BackgroundResource;
 class Camera;
@@ -72,7 +70,7 @@ class ScreenText;
 class ScriptOpcodes;
 class ScriptResource;
 class ScriptStack;
-class Sequence;
+struct Sequence;
 class SoundMan;
 class SpecialCode;
 class TalkInstanceList;
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 391cded..878dacf 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -465,15 +465,22 @@ void BaseMenuSystem::update(Control *cursorControl) {
 		handleClick(_activeMenu->_defaultMenuItemIndex, mousePos);
 	} else if (_vm->_input->pollEvent(kEventUp)) {
 		// TODO handleUpKey();
-		if (_hoveredMenuItemIndex > 0) {
-			setMouseCursorToMenuItem(--_hoveredMenuItemIndex);
-			_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
-			updateActorHoverBackground();
+		if (_hoveredMenuItemIndex == 1) {
+			_hoveredMenuItemIndex = _activeMenu->getMenuItemsCount();
+		} else {
+			_hoveredMenuItemIndex--;
 		}
-
+		setMouseCursorToMenuItem(_hoveredMenuItemIndex);
+		_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
+		updateActorHoverBackground();
 	} else if (_vm->_input->pollEvent(kEventDown)) {
 		// TODO handleDownKey();
-		setMouseCursorToMenuItem(++_hoveredMenuItemIndex);
+		if (_hoveredMenuItemIndex == _activeMenu->getMenuItemsCount()) {
+			_hoveredMenuItemIndex = 1;
+		} else {
+			_hoveredMenuItemIndex++;
+		}
+		setMouseCursorToMenuItem(_hoveredMenuItemIndex);
 		_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
 		updateActorHoverBackground();
 	}
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index a9a76e3..ac4f53d 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -315,7 +315,8 @@ int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common:
 		return 2;
 
 	int v15 = sourceDeltaX * delta1, v18 = sourceDeltaY * delta1;
-	int v16, v17;
+	int v16 = 0;
+	int v17 = 0;
 	
 	if ((v15 >= 0 && delta2 >= 0) || (v15 < 0 && delta2 < 0)) {
 		v16 = delta2 / 2;
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index 53de6b4..e2108a6 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -284,7 +284,7 @@ void ScriptResource::load(Resource *resource) {
 
 	Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
 	
-	uint32 objectMapOffs, sceneInfosOffs;
+	uint32 objectMapOffs = 0, sceneInfosOffs = 0;
 	_objectMapCount = 0;
 	
 	if (resource->_gameId == kGameIdBBDOU) {
@@ -311,7 +311,7 @@ void ScriptResource::load(Resource *resource) {
 	uint32 propertiesOffs = stream.readUint32LE();
 	uint32 blockCountersOffs = stream.readUint32LE();
 	if (resource->_gameId == kGameIdDuckman)
-		objectMapOffs = stream.readUint32LE();
+		objectMapOffs = stream.readUint32LE(); //TODO Is this needed for BBDOU?
 	uint32 codeTblOffs = stream.readUint32LE();
 	
 	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _sceneInfosCount: %d; _objectMapCount: %d",


Commit: 5dd96b6fbe574d8ea2ec3710b46fe94779c3d3ce
    https://github.com/scummvm/scummvm/commit/5dd96b6fbe574d8ea2ec3710b46fe94779c3d3ce
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add midi music player

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/sound.cpp
    engines/illusions/sound.h


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index c864728..c453aeb 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -623,11 +623,11 @@ void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCa
 void ScriptOpcodes_Duckman::opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(musicId);
-	// TODO _vm->playMidiMusic(musicId);
+	_vm->_soundMan->playMidiMusic(musicId);
 }
 
 void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
-	// TODO _vm->stopMidiMusic();
+	_vm->_soundMan->stopMidiMusic();
 }
 
 void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index 4df66e1..68bccf7 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -343,7 +343,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 
 int BackgroundResource::findMasterBgIndex() {
 	int index = 1;
-	while (!_bgInfos[index - 1]._flags & 1)
+	while (!_bgInfos[index - 1]._flags & 1) //TODO check if this is a typo
 		++index;
 	return index;
 }
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 5b1eea8..9379bf1 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/sound.h"
+#include "audio/midiparser.h"
 
 namespace Illusions {
 
@@ -72,6 +73,85 @@ bool MusicPlayer::isPlaying() {
 	return (_flags & 1) && (_flags & 2) && g_system->getMixer()->isSoundHandleActive(_soundHandle);
 }
 
+// MidiPlayer
+
+MidiPlayer::MidiPlayer() {
+	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+	_driver = MidiDriver::createMidi(dev);
+	assert(_driver);
+	_paused = false;
+
+
+	int ret = _driver->open();
+	if (ret == 0) {
+		_driver->sendGMReset();
+
+		_driver->setTimerCallback(this, &timerCallback);
+	}
+}
+
+void MidiPlayer::play(const Common::String &filename) {
+	Common::StackLock lock(_mutex);
+
+	stop();
+
+	Common::File *fd = new Common::File();
+	if (!fd->open(filename)) {
+		delete fd;
+		error("MidiPlayer::play() Could not load %s", filename.c_str());
+	}
+
+	uint32 size = (uint32)fd->size();
+	_midiData = (uint8 *)malloc(size);
+
+	if (_midiData) {
+		fd->read(_midiData, size);
+
+		syncVolume();	// FIXME: syncVolume calls setVolume which in turn also locks the mutex! ugh
+
+		_parser = MidiParser::createParser_SMF();
+		_parser->loadMusic(_midiData, size);
+		_parser->setTrack(0);
+		_parser->setMidiDriver(this);
+		_parser->setTimerRate(_driver->getBaseTempo());
+		_isLooping = false;
+		_isPlaying = true;
+	}
+	fd->close();
+	delete fd;
+}
+
+void MidiPlayer::pause(bool p) {
+	_paused = p;
+
+	for (int i = 0; i < kNumChannels; ++i) {
+		if (_channelsTable[i]) {
+			_channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
+		}
+	}
+}
+
+void MidiPlayer::onTimer() {
+	Common::StackLock lock(_mutex);
+
+	if (!_paused && _isPlaying && _parser) {
+		_parser->onTimer();
+	}
+}
+
+void MidiPlayer::sendToChannel(byte channel, uint32 b) {
+	if (!_channelsTable[channel]) {
+		_channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+		// If a new channel is allocated during the playback, make sure
+		// its volume is correctly initialized.
+		if (_channelsTable[channel])
+			_channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
+	}
+
+	if (_channelsTable[channel])
+		_channelsTable[channel]->send(b);
+}
+
 // VoicePlayer
 
 VoicePlayer::VoicePlayer() {
@@ -175,11 +255,13 @@ bool Sound::isPlaying() {
 SoundMan::SoundMan(IllusionsEngine *vm)
 	: _vm(vm), _musicNotifyThreadId(0) {
 	_musicPlayer = new MusicPlayer();
+	_midiPlayer = new MidiPlayer();
 	_voicePlayer = new VoicePlayer();
 }
 
 SoundMan::~SoundMan() {
 	delete _musicPlayer;
+	delete _midiPlayer;
 	delete _voicePlayer;
 	unloadSounds(0);
 }
@@ -264,4 +346,13 @@ Sound *SoundMan::getSound(uint32 soundEffectId) {
 	return 0;
 }
 
+void SoundMan::playMidiMusic(uint32 musicId) {
+	Common::String filename = Common::String::format("%08x.MID", musicId);
+	_midiPlayer->play(filename);
+}
+
+void SoundMan::stopMidiMusic() {
+	_midiPlayer->stop();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index d2d91ac..7979978 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -25,6 +25,7 @@
 
 #include "illusions/graphics.h"
 #include "audio/audiostream.h"
+#include "audio/midiplayer.h"
 #include "audio/mixer.h"
 #include "audio/decoders/wave.h"
 #include "common/list.h"
@@ -46,6 +47,27 @@ protected:
 	uint _flags;
 };
 
+class MidiPlayer : public Audio::MidiPlayer {
+public:
+	MidiPlayer();
+
+	void pause(bool p);
+	void play(const Common::String &filename);
+
+	// The following line prevents compiler warnings about hiding the pause()
+	// method from the parent class.
+	// FIXME: Maybe the pause(bool p) method should be removed and the
+	// pause/resume methods of the parent class be used instead?
+	virtual void pause() { Audio::MidiPlayer::pause(); }
+
+	// Overload Audio::MidiPlayer method
+	virtual void sendToChannel(byte channel, uint32 b);
+	virtual void onTimer();
+
+private:
+	bool _paused;
+};
+
 class VoicePlayer {
 public:
 	VoicePlayer();
@@ -90,6 +112,9 @@ public:
 	void playMusic(uint32 musicId, int16 type, int16 volume, int16 pan, uint32 notifyThreadId);
 	void stopMusic();
 
+	void playMidiMusic(uint32 musicId);
+	void stopMidiMusic();
+
 	bool cueVoice(const char *voiceName);
 	void stopCueingVoice();
 	void startVoice(int16 volume, int16 pan);
@@ -109,6 +134,7 @@ protected:
 	IllusionsEngine *_vm;
 	uint32 _musicNotifyThreadId;
 	MusicPlayer *_musicPlayer;
+	MidiPlayer *_midiPlayer;
 	VoicePlayer *_voicePlayer;
 	SoundList _sounds;
 	Sound *getSound(uint32 soundEffectId);


Commit: 2f551cabbacf64d336a855ec087fb6e31c484161
    https://github.com/scummvm/scummvm/commit/2f551cabbacf64d336a855ec087fb6e31c484161
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Change video skip key from escape to space

Add debug logging on unknown background info field
Removed unused debug variables

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.cpp
    engines/illusions/resources/backgroundresource.cpp


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index c453aeb..db8a7a8 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -276,7 +276,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
 //static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map
-static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
+//static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
 
 void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 7b2f5f9..cc8e769 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -303,7 +303,7 @@ void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority
 
 		Common::Event event;
 		while (_eventMan->pollEvent(event)) {
-			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) ||
+			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_SPACE) ||
 				event.type == Common::EVENT_LBUTTONUP)
 				skipVideo = true;
 		}
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index 68bccf7..1eff8e0 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -58,7 +58,7 @@ void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
 
 void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	_flags = stream.readUint32LE();
-	stream.skip(2); // Unknown
+	uint16 unknown = stream.readUint16LE(); //	TODO Unknown
 	_priorityBase = stream.readSint16LE();
 	_surfInfo.load(stream);
 	loadPoint(stream, _panPoint);
@@ -67,8 +67,8 @@ void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	stream.seek(tileMapOffs);
 	_tileMap.load(dataStart, stream);
 	_tilePixels = dataStart + tilePixelsOffs;
-	debug(0, "BgInfo::load() _flags: %08X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
-		_flags, _priorityBase, tileMapOffs, tilePixelsOffs);
+	debug(0, "BgInfo::load() _flags: %08X; unknown: %04X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
+		_flags, unknown, _priorityBase, tileMapOffs, tilePixelsOffs);
 }
 
 // PriorityLayer


Commit: d3fbb0e8b97fbefa8a4ca019ea8689a98fcdc62b
    https://github.com/scummvm/scummvm/commit/d3fbb0e8b97fbefa8a4ca019ea8689a98fcdc62b
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Replace actor flag magic values with enum definitions

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_credits.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/cursor.cpp
    engines/illusions/duckman/duckman_credits.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/illusions.cpp
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index a99a124..d24ec7b 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -146,11 +146,11 @@ void Actor::createSurface(SurfInfo &surfInfo) {
 			_surface->fillRect(Common::Rect(surfInfo._dimensions._width, surfInfo._dimensions._height), 0);
 			gfx_sub_40CA70(_surface, font, _field18C, _surfInfo._dimensions, _field198);
 			*/
-			_flags |= 0x4000;
+			_flags |= Illusions::ACTOR_FLAG_4000;
 		}
 		else {
-			_flags |= 0x2000;
-			_flags |= 0x4000;
+			_flags |= Illusions::ACTOR_FLAG_2000;
+			_flags |= Illusions::ACTOR_FLAG_4000;
 		}
 	}
 }
@@ -225,7 +225,7 @@ void Control::pause() {
 	if (_objectId == 0x40004)
 		_vm->setCursorControl(0);
 
-	if (_actor && !(_actor->_flags & 0x0200))
+	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200))
 		_actor->destroySurface();
 
 }
@@ -237,7 +237,7 @@ void Control::unpause() {
 	if (_objectId == 0x40004)
 		_vm->setCursorControl(this);
   
-	if (_actor && !(_actor->_flags & 0x0200)) {
+	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200)) {
 		SurfInfo surfInfo;
 		ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
 		if (actorType)
@@ -252,11 +252,11 @@ void Control::unpause() {
 void Control::appearActor() {
 	if (_vm->getGameId() == kGameIdDuckman) {
 		_flags |= 1;
-		_actor->_flags |= 1;
+		_actor->_flags |= Illusions::ACTOR_FLAG_1;
 		if (_objectId == 0x40004) {
 			if (_actor->_frameIndex) {
-				_actor->_flags |= 0x2000;
-				_actor->_flags |= 0x4000;
+				_actor->_flags |= Illusions::ACTOR_FLAG_2000;
+				_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 			}
 			_vm->_input->discardAllEvents();
 		}
@@ -265,9 +265,9 @@ void Control::appearActor() {
 			_vm->showCursor();
 		} else {
 			if (_actor->_frameIndex || _actorTypeId == 0x50004)
-				_actor->_flags |= 1;
+				_actor->_flags |= Illusions::ACTOR_FLAG_1;
 			else
-				_actor->_flags |= 0x1000;
+				_actor->_flags |= Illusions::ACTOR_FLAG_1000;
 			for (uint i = 0; i < kSubObjectsCount; ++i)
 				if (_actor->_subobjects[i]) {
 					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
@@ -280,13 +280,13 @@ void Control::appearActor() {
 void Control::disappearActor() {
 	if (_vm->getGameId() == kGameIdDuckman) {
 		_flags &= ~1;
-		_actor->_flags &= ~1;
+		_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
 	} else {
 		if (_objectId == 0x40004) {
 			_vm->hideCursor();
 		} else {
-			_actor->_flags &= ~1;
-			_actor->_flags &= ~0x1000;
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
 			for (uint i = 0; i < kSubObjectsCount; ++i)
 				if (_actor->_subobjects[i]) {
 					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
@@ -297,7 +297,7 @@ void Control::disappearActor() {
 }
 
 bool Control::isActorVisible() {
-	return (_actor->_flags & 1) != 0;
+	return (_actor->_flags & Illusions::ACTOR_FLAG_1) != 0;
 }
 
 void Control::activateObject() {
@@ -388,11 +388,11 @@ void Control::clearNotifyThreadId2() {
 	for (uint i = 0; i < kSubObjectsCount; ++i)
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
-			subControl->_actor->_flags &= ~0x80;
+			subControl->_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
 			subControl->_actor->_entryTblPtr = 0;
 			subControl->_actor->_notifyThreadId2 = 0;
 		}
-	_actor->_flags &= ~0x80;
+	_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
 	_actor->_entryTblPtr = 0;
 	_actor->_notifyThreadId2 = 0;
 }
@@ -405,7 +405,7 @@ uint32 Control::getPriority() {
 	uint32 objectId;
 	int16 positionY, priority, priority1;
 	if (_actor) {
-		if (_actor->_parentObjectId && (_actor->_flags & 0x40)) {
+		if (_actor->_parentObjectId && (_actor->_flags & Illusions::ACTOR_FLAG_40)) {
 			uint32 parentObjectId = getSubActorParent();
 			Control *parentControl = _vm->_dict->getObjectControl(parentObjectId);
 			objectId = parentControl->_objectId;
@@ -480,7 +480,7 @@ uint32 Control::getSubActorParent() {
 	uint32 parentObjectId = _objectId;
 	while (1) {
 		Actor *actor = _vm->_dict->getObjectControl(parentObjectId)->_actor;
-		if (actor->_parentObjectId && (actor->_flags & 0x40))
+		if (actor->_parentObjectId && (actor->_flags & Illusions::ACTOR_FLAG_40))
 			parentObjectId = actor->_parentObjectId;
 		else
 			break;
@@ -545,8 +545,8 @@ void Control::setActorFrameIndex(int16 frameIndex) {
 		const Frame &frame = (*_actor->_frames)[frameIndex - 1];
 		_actor->_surfInfo = frame._surfInfo;
 		readPointsConfig(frame._pointsConfig);
-		_actor->_flags |= 0x2000;
-		_actor->_flags |= 0x4000;
+		_actor->_flags |= Illusions::ACTOR_FLAG_2000;
+		_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 		_actor->_newFrameIndex = 0;
 	}
 }
@@ -554,9 +554,9 @@ void Control::setActorFrameIndex(int16 frameIndex) {
 void Control::stopActor() {
 	_actor->_seqCodeIp = 0;
 	if (_actor->_pathNode) {
-		if (_actor->_flags & 0x0400) {
+		if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
 			delete _actor->_pathNode;
-			_actor->_flags &= ~0x0400;
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
 		}
 		_actor->_pathNode = 0;
 		_actor->_pathPoints = 0;
@@ -575,12 +575,12 @@ void Control::startSequenceActor(uint32 sequenceId, int value, uint32 notifyThre
 }
 
 void Control::stopSequenceActor() {
-	if (_actor->_flags & 0x40) {
+	if (_actor->_flags & Illusions::ACTOR_FLAG_40) {
 		stopActor();
 		_actor->_frameIndex = 0;
-		if ((_actor->_flags & 1) || (_actor->_flags & 0x1000)) {
-			_actor->_flags &= ~1;
-			_actor->_flags |= 0x1000;
+		if ((_actor->_flags & Illusions::ACTOR_FLAG_1) || (_actor->_flags & Illusions::ACTOR_FLAG_1000)) {
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
+			_actor->_flags |= Illusions::ACTOR_FLAG_1000;
 		}
 	}
 	for (uint i = 0; i < kSubObjectsCount; ++i)
@@ -594,12 +594,12 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 	bool doSeq = true;
 	if (_actor->_linkIndex2) {
 		Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[_actor->_linkIndex2 - 1]);
-		if (subControl->_actor->_flags & 1) {
+		if (subControl->_actor->_flags & Illusions::ACTOR_FLAG_1) {
 			if (_actor->_pathNode) {
 				doSeq = false;
 				subControl->_actor->_notifyThreadId2 = threadId;
 				subControl->_actor->_entryTblPtr = entryTblPtr;
-				subControl->_actor->_flags |= 0x80;
+				subControl->_actor->_flags |= Illusions::ACTOR_FLAG_80;
 				Thread *thread = _vm->_threads->findThread(threadId);
 				thread->sendMessage(kMsgClearSequenceId2, 0);
 			}
@@ -646,9 +646,9 @@ void Control::sequenceActor() {
 		//debug(1, "New frame %d", _actor->_newFrameIndex);
 		setActorFrameIndex(_actor->_newFrameIndex);
 		if (_vm->getGameId() == kGameIdBBDOU &&
-			!(_actor->_flags & 1) && (_actor->_flags & 0x1000) && (_objectId != 0x40004)) {
+			!(_actor->_flags & Illusions::ACTOR_FLAG_1) && (_actor->_flags & Illusions::ACTOR_FLAG_1000) && (_objectId != 0x40004)) {
 			appearActor();
-			_actor->_flags &= ~0x1000;
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
 		}
 		//debug(1, "New frame OK");
 	}
@@ -676,9 +676,9 @@ void Control::startSubSequence(int linkIndex, uint32 sequenceId) {
 	Control *linkedControl = _vm->_dict->getObjectControl(_actor->_subobjects[linkIndex - 1]);
 	Actor *linkedActor = linkedControl->_actor;
 	if (!linkedActor->_entryTblPtr)
-		linkedActor->_flags &= ~0x80;
-	linkedActor->_flags &= ~0x400;
-	linkedActor->_flags |= 0x100;
+		linkedActor->_flags &= ~Illusions::ACTOR_FLAG_80;
+	linkedActor->_flags &= ~Illusions::ACTOR_FLAG_400;
+	linkedActor->_flags |= Illusions::ACTOR_FLAG_100;
 	linkedActor->_sequenceId = sequenceId;
 	linkedActor->_notifyThreadId1 = 0;
 	linkedActor->_notifyId3C = 0;
@@ -701,12 +701,12 @@ void Control::stopSubSequence(int linkIndex) {
 	uint32 notifyThreadId2 = _actor->_notifyThreadId2;
 	_actor->_linkIndex2 = linkIndex;
 	if (_actor->_entryTblPtr) {
-		linkedActor->_flags |= 0x80;
+		linkedActor->_flags |= Illusions::ACTOR_FLAG_80;
 		linkedActor->_entryTblPtr = _actor->_entryTblPtr;
 		linkedActor->_notifyThreadId2 = _actor->_notifyThreadId2;
 		linkedActor->_seqCodeValue1 = _actor->_seqCodeValue1;
 		linkedActor->_seqCodeValue3 = _actor->_seqCodeValue3;
-		_actor->_flags &= ~0x80;
+		_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
 		_actor->_entryTblPtr = 0;
 		_actor->_notifyThreadId1 = 0;
 		_actor->_notifyThreadId2 = 0;
@@ -755,7 +755,7 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 		_actor->_pathNode = pathNode;
 		_actor->_pathPointsCount = pathNode->size();
 		_actor->_pathPoints = pathNode->size();
-		_actor->_flags |= 0x0400;
+		_actor->_flags |= Illusions::ACTOR_FLAG_400;
 		_actor->_walkCallerThreadId1 = callerThreadId1;
 		_vm->notifyThreadId(_actor->_notifyId3C);
 		_actor->_notifyId3C = callerThreadId2;
@@ -766,8 +766,8 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 }
 
 PointArray *Control::createPath(Common::Point destPt) {
-	PointArray *walkPoints = (_actor->_flags & 2) ? _actor->_pathWalkPoints->_points : 0;
-	PathLines *walkRects = (_actor->_flags & 0x10) ? _actor->_pathWalkRects->_rects : 0;
+	PointArray *walkPoints = (_actor->_flags & Illusions::ACTOR_FLAG_2) ? _actor->_pathWalkPoints->_points : 0;
+	PathLines *walkRects = (_actor->_flags & Illusions::ACTOR_FLAG_10) ? _actor->_pathWalkRects->_rects : 0;
 	PathFinder pathFinder;
 	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
 	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
@@ -789,7 +789,7 @@ void Control::updateActorMovement(uint32 deltaTime) {
 	if (!fastWalked && _vm->testMainActorFastWalk(this)) {
 		fastWalked = true;
 		disappearActor();
-		_actor->_flags |= 0x8000;
+		_actor->_flags |= Illusions::ACTOR_FLAG_8000;
 		_actor->_seqCodeIp = 0;
 		deltaTime = 2;
 	}
@@ -853,7 +853,7 @@ void Control::updateActorMovement(uint32 deltaTime) {
 
 	FP16 deltaX24, deltaY24;
 
-	if (_actor->_flags & 0x0400) {
+	if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
 
 		FP16 v20 = fixedMul((deltaTime + _actor->_pathCtrX) << 16, _actor->_pathCtrY << 16);
 		FP16 v21 = fixedDiv(v20, 100 << 16);
@@ -900,9 +900,9 @@ void Control::updateActorMovement(uint32 deltaTime) {
 		++_actor->_pathPoints;
 		_actor->_pathInitialPosFlag = true;
 		if (_actor->_pathPointsCount == 0) {
-			if (_actor->_flags & 0x0400) {
+			if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
 				delete _actor->_pathNode;
-				_actor->_flags &= ~0x0400;
+				_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
 			}
 			_actor->_pathNode = 0;
 			_actor->_pathPoints = 0;
@@ -936,12 +936,12 @@ void Control::getActorFrameDimensions(WidthHeight &dimensions) {
 
 void Control::drawActorRect(const Common::Rect r, byte color) {
 	_actor->_surface->fillRect(r, color);
-	_actor->_flags |= 0x4000;
+	_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 }
 
 void Control::fillActor(byte color) {
 	_vm->_screen->fillSurface(_actor->_surface, color);
-	_actor->_flags |= 0x4000;
+	_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 }
 
 bool Control::isPixelCollision(Common::Point &pt) {
@@ -953,9 +953,9 @@ bool Control::isPixelCollision(Common::Point &pt) {
 void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) {
 	stopActor();
 
-	_actor->_flags &= ~0x80;
-	_actor->_flags &= ~0x0400;
-	_actor->_flags |= 0x0100;
+	_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
+	_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
+	_actor->_flags |= Illusions::ACTOR_FLAG_100;
 
 	sequenceId = _actor->_defaultSequences.use(sequenceId);
 
@@ -971,7 +971,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 		//debug(1, "Load external sequence...");
 		_vm->_resSys->loadResource(0x00060000 | (sequenceId & 0xFFFF), _vm->getCurrentScene(), 0);
 		sequence = _vm->_dict->findSequence(sequenceId);
-		_actor->_flags |= 0x800;
+		_actor->_flags |= Illusions::ACTOR_FLAG_800;
 	}
 
 	_actor->_seqCodeIp = sequence->_sequenceCode;
@@ -994,7 +994,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_linkIndex2 = 0;
 	
 	if (entryTblPtr) {
-		_actor->_flags |= 0x80;
+		_actor->_flags |= Illusions::ACTOR_FLAG_80;
 		_actor->_entryTblPtr = entryTblPtr;
 		if (_vm->getGameId() == kGameIdBBDOU) {
 			_actor->_notifyThreadId1 = 0;
@@ -1053,7 +1053,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
 		actor->createSurface(actorType->_surfInfo);
 	} else {
-		actor->_flags |= 0x0200;
+		actor->_flags |= Illusions::ACTOR_FLAG_200;
 	}
 
 	actor->_position = placePt;
@@ -1067,27 +1067,27 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	if (actorType->_pathWalkPointsIndex) {
 		actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
-		actor->_flags |= 0x02;
+		actor->_flags |= Illusions::ACTOR_FLAG_2;
 	}
 
 	if (actorType->_scaleLayerIndex) {
 		actor->_scaleLayer = bgRes->getScaleLayer(actorType->_scaleLayerIndex - 1);
-		actor->_flags |= 0x04;
+		actor->_flags |= Illusions::ACTOR_FLAG_4;
 	}
 
 	if (actorType->_pathWalkRectIndex) {
 		actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
-		actor->_flags |= 0x10;
+		actor->_flags |= Illusions::ACTOR_FLAG_10;
 	}
 	
 	if (actorType->_priorityLayerIndex) {
 		actor->_priorityLayer = bgRes->getPriorityLayer(actorType->_priorityLayerIndex - 1);
-		actor->_flags |= 0x08;
+		actor->_flags |= Illusions::ACTOR_FLAG_8;
 	}
 	
 	if (actorType->_regionLayerIndex) {
 		actor->_regionLayer = bgRes->getRegionLayer(actorType->_regionLayerIndex - 1);
-		actor->_flags |= 0x20;
+		actor->_flags |= Illusions::ACTOR_FLAG_20;
 	}
 	
 	actor->_pathCtrY = 140;
@@ -1099,7 +1099,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 		control->appearActor();
 	} else if (_vm->getGameId() == kGameIdBBDOU) {
 		control->_flags |= 0x01;
-		actor->_flags |= 0x1000;
+		actor->_flags |= Illusions::ACTOR_FLAG_1000;
 	}
 
 	if (_vm->isCursorObject(actorTypeId, objectId))
@@ -1162,7 +1162,7 @@ void Controls::placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId,
 	placeActor(actorTypeId, Common::Point(0, 0), sequenceId, tempObjectId, 0);
 	parentControl->_actor->_subobjects[linkIndex - 1] = tempObjectId;
 	Actor *subActor = _vm->_dict->getObjectControl(tempObjectId)->_actor;
-	subActor->_flags |= 0x40;
+	subActor->_flags |= Illusions::ACTOR_FLAG_40;
 	subActor->_parentObjectId = parentControl->_objectId;
 	subActor->_linkIndex = linkIndex;
 }
@@ -1291,7 +1291,7 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 		Control *testControl = *it;
 		if (testControl != control && testControl->_pauseCtr == 0 &&
 			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
-			(!testControl->_actor || (testControl->_actor->_flags & 1))) {
+			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_1))) {
 			Common::Rect collisionRect;
 			testControl->getCollisionRect(collisionRect);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
@@ -1306,7 +1306,7 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 	}
 
 	if (foundControl) {
-		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & 0x40)) {
+		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & Illusions::ACTOR_FLAG_40)) {
 			uint32 parentObjectId = foundControl->getSubActorParent();
 			foundControl = _vm->_dict->getObjectControl(parentObjectId);
 		}
@@ -1325,7 +1325,7 @@ bool Controls::getOverlappedObjectAccurate(Control *control, Common::Point pt, C
 		Control *testControl = *it;
 		if (testControl != control && testControl->_pauseCtr == 0 &&
 			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
-			(!testControl->_actor || (testControl->_actor->_flags & 1))) {
+			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_1))) {
 			Common::Rect collisionRect;
 			testControl->getCollisionRectAccurate(collisionRect);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
@@ -1341,7 +1341,7 @@ bool Controls::getOverlappedObjectAccurate(Control *control, Common::Point pt, C
 	}
 
 	if (foundControl) {
-		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & 0x40)) {
+		if (foundControl->_actor && foundControl->_actor->_parentObjectId && (foundControl->_actor->_flags & Illusions::ACTOR_FLAG_40)) {
 			uint32 parentObjectId = foundControl->getSubActorParent();
 			foundControl = _vm->_dict->getObjectControl(parentObjectId);
 		}
@@ -1413,18 +1413,18 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 		actor->_seqCodeValue1 = 100 * deltaTime;
 	}
 
-	if (actor->_flags & 4) {
+	if (actor->_flags & Illusions::ACTOR_FLAG_4) {
 		int scale = actor->_scaleLayer->getScale(actor->_position);
 		control->setActorScale(scale);
 	}
 
-	if (actor->_flags & 8) {
+	if (actor->_flags & Illusions::ACTOR_FLAG_8) {
 		int16 priority = actor->_priorityLayer->getPriority(actor->_position);
 		if (priority)
 			control->setPriority(priority + 1);
 	}
 
-	if (actor->_flags & 0x20) {
+	if (actor->_flags & Illusions::ACTOR_FLAG_20) {
 		// Update transition sequence
 		int regionIndex = actor->_regionLayer->getRegionIndex(actor->_position);
 		if (actor->_regionIndex != regionIndex) {
@@ -1485,9 +1485,9 @@ void Controls::destroyControlInternal(Control *control) {
 		_vm->setCursorControl(0);
 
 	if (control->_actor) {
-		if (control->_actor->_pathNode && (control->_actor->_flags & 0x400))
+		if (control->_actor->_pathNode && (control->_actor->_flags & Illusions::ACTOR_FLAG_400))
 			delete control->_actor->_pathNode;
-		if (!(control->_actor->_flags & 0x200))
+		if (!(control->_actor->_flags & Illusions::ACTOR_FLAG_200))
 			control->_actor->destroySurface();
 		/* TODO
 		if (control->_actor->_field2)
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index d82ca02..e590cac 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -39,6 +39,25 @@ class IllusionsEngine;
 class SequenceOpcodes;
 struct OpCall;
 
+enum ActorFlags {
+	ACTOR_FLAG_1 = 1,
+	ACTOR_FLAG_2 = 2,
+	ACTOR_FLAG_4 = 4,
+	ACTOR_FLAG_8 = 8,
+	ACTOR_FLAG_10 = 0x10,
+	ACTOR_FLAG_20 = 0x20,
+	ACTOR_FLAG_40 = 0x40,
+	ACTOR_FLAG_80 = 0x80,
+	ACTOR_FLAG_100 = 0x100,
+	ACTOR_FLAG_200 = 0x200,
+	ACTOR_FLAG_400 = 0x400,
+	ACTOR_FLAG_800 = 0x800,
+	ACTOR_FLAG_1000 = 0x1000,
+	ACTOR_FLAG_2000 = 0x2000,
+	ACTOR_FLAG_4000 = 0x4000,
+	ACTOR_FLAG_8000 = 0x8000
+};
+
 const uint kSubObjectsCount = 15;
 
 struct DefaultSequence {
diff --git a/engines/illusions/bbdou/bbdou_credits.cpp b/engines/illusions/bbdou/bbdou_credits.cpp
index 3b65129..eb23779 100644
--- a/engines/illusions/bbdou/bbdou_credits.cpp
+++ b/engines/illusions/bbdou/bbdou_credits.cpp
@@ -107,7 +107,7 @@ void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint ali
 	control->fillActor(0);
 	textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), alignment, outText);
 	textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
-	control->_actor->_flags |= 0x4000;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 
 }
 
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index e3b0df2..4d54eb4 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -410,7 +410,7 @@ void IllusionsEngine_BBDOU::hideCursor() {
 
 void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (control->_actor->_flags & 1) {
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_1) {
 		switch (_cursor->_status) {
 		case 2:
 			// Unused nullsub_1(control);
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
index 962ea93..0cb6ff7 100644
--- a/engines/illusions/cursor.cpp
+++ b/engines/illusions/cursor.cpp
@@ -62,10 +62,10 @@ void Cursor::show() {
 	++_visibleCtr;
 	if (_visibleCtr > 0) {
 		_control->_flags |= 1;
-		_control->_actor->_flags |= 1;
+		_control->_actor->_flags |= Illusions::ACTOR_FLAG_1;
 		if (_control->_actor->_frameIndex) {
-			_control->_actor->_flags |= 0x2000;
-			_control->_actor->_flags |= 0x4000;
+			_control->_actor->_flags |= Illusions::ACTOR_FLAG_2000;
+			_control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 		}
 		_vm->_input->discardAllEvents();
 	}
@@ -75,7 +75,7 @@ void Cursor::hide() {
 	--_visibleCtr;
 	if (_visibleCtr <= 0) {
 		_control->_flags &= ~1;
-		_control->_actor->_flags &= ~1;
+		_control->_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
 	}
 }
 
diff --git a/engines/illusions/duckman/duckman_credits.cpp b/engines/illusions/duckman/duckman_credits.cpp
index 18a0482..2798efe 100644
--- a/engines/illusions/duckman/duckman_credits.cpp
+++ b/engines/illusions/duckman/duckman_credits.cpp
@@ -107,7 +107,7 @@ int DuckmanCredits::update(uint flags) {
 				control->getActorFrameDimensions(dimensions);
 				textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), 2, outText);
 				textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
-				control->_actor->_flags |= 0x4000;
+				control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 
 				_lastItemIndex = index;
 			}
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 955aa8d..d917a35 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -437,7 +437,7 @@ bool IllusionsEngine_Duckman::testMainActorCollision(Control *control) {
 			_currWalkOverlappedControl = overlappedControl;
 			if (runTriggerCause(9, 0, overlappedControl->_objectId)) {
 				delete control->_actor->_pathNode;
-				control->_actor->_flags &= ~0x0400;
+				control->_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
 				control->_actor->_pathNode = 0;
 				control->_actor->_pathPoints = 0;
 				control->_actor->_pathPointsCount = 0;
@@ -692,7 +692,7 @@ void IllusionsEngine_Duckman::stopCursorHoldingObject() {
 
 void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (control->_actor->_flags & 1) {
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_1) {
 		switch (_cursor._gameState) {
 		case 2:
 			updateGameState2();
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index cc8e769..2317bd3 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -188,14 +188,14 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
 		Actor *actor = control->_actor;
-		if (control->_pauseCtr == 0 && actor && (actor->_flags & 1) && !(actor->_flags & 0x0200)) {
+		if (control->_pauseCtr == 0 && actor && (actor->_flags & Illusions::ACTOR_FLAG_1) && !(actor->_flags & Illusions::ACTOR_FLAG_200)) {
 			Common::Point drawPosition = control->calcPosition(panPoint);
-			if (actor->_flags & 0x2000) {
+			if (actor->_flags & Illusions::ACTOR_FLAG_2000) {
 				Frame *frame = &(*actor->_frames)[actor->_frameIndex - 1];
 				_screen->_decompressQueue->insert(&actor->_drawFlags, frame->_flags,
 					frame->_surfInfo._pixelSize, frame->_surfInfo._dimensions,
 					frame->_compressedPixels, actor->_surface);
-				actor->_flags &= ~0x2000;
+				actor->_flags &= ~Illusions::ACTOR_FLAG_2000;
 			}
 			/* Unused
 			if (actor->_flags & 0x4000) {
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 81f881b..c394537 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -121,31 +121,31 @@ void SequenceOpcodes::opYield(Control *control, OpCall &opCall) {
 
 void SequenceOpcodes::opSetFrameIndex(Control *control, OpCall &opCall) {
 	ARG_INT16(frameIndex);
-	if (control->_actor->_flags & 0x80) {
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_80) {
 		int16 frameIncr = READ_LE_UINT16(control->_actor->_entryTblPtr);
 		if (frameIncr) {
 			frameIndex += frameIncr - 1;
 			control->_actor->_entryTblPtr += 2;
 		} else {
-			control->_actor->_flags &= ~0x80;
+			control->_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
 			control->_actor->_entryTblPtr = 0;
 			control->_actor->_notifyThreadId2 = 0;
 			_vm->notifyThreadId(control->_actor->_notifyThreadId1);
 			opCall._result = 1;
 		}
 	}
-	control->_actor->_flags &= ~0x0100;
-	if (control->_actor->_flags & 0x8000) {
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_100;
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_8000) {
 		control->appearActor();
-		control->_actor->_flags &= ~0x8000;
+		control->_actor->_flags &= ~Illusions::ACTOR_FLAG_8000;
 	}
 	control->_actor->_newFrameIndex = frameIndex;
 }
 
 void SequenceOpcodes::opEndSequence(Control *control, OpCall &opCall) {
 	control->_actor->_seqCodeIp = 0;
-	if (control->_actor->_flags & 0x0800) {
-		control->_actor->_flags &= ~0x0800;
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_800) {
+		control->_actor->_flags &= ~Illusions::ACTOR_FLAG_800;
 		control->_actor->_frames = 0;
 		control->_actor->_frameIndex = 0;
 		control->_actor->_newFrameIndex = 0;
@@ -295,64 +295,64 @@ void SequenceOpcodes::opSetPathCtrY(Control *control, OpCall &opCall) {
 void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkPointsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= 2;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_2;
 	control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
 }
 
 void SequenceOpcodes::opDisableAutoScale(Control *control, OpCall &opCall) {
 	// Keep current scale but don't autoscale
-	control->_actor->_flags &= ~4;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_4;
 }
 
 void SequenceOpcodes::opSetScale(Control *control, OpCall &opCall) {
 	ARG_INT16(scale);
-	control->_actor->_flags &= ~4;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_4;
 	control->setActorScale(scale);
 }
 
 void SequenceOpcodes::opSetScaleLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(scaleLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= 4;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_4;
 	control->_actor->_scaleLayer = bgRes->getScaleLayer(scaleLayerIndex - 1);
 	int scale = control->_actor->_scaleLayer->getScale(control->_actor->_position);
 	control->setActorScale(scale);
 }
 
 void SequenceOpcodes::opDeactivatePathWalkRects(Control *control, OpCall &opCall) {
-	control->_actor->_flags &= ~0x0010;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_10;
 }
 
 void SequenceOpcodes::opSetPathWalkRects(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkRectsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= 0x0010;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_10;
 	control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
 }
 
 void SequenceOpcodes::opSetPriority(Control *control, OpCall &opCall) {
 	ARG_INT16(priority);
-	control->_actor->_flags &= ~8;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_8;
 	control->setPriority(priority);
 }
 
 void SequenceOpcodes::opSetPriorityLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(priorityLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= 8;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_8;
 	control->_actor->_priorityLayer = bgRes->getPriorityLayer(priorityLayerIndex - 1);
 	int priority = control->_actor->_priorityLayer->getPriority(control->_actor->_position);
 	control->setPriority(priority);
 }
 
 void SequenceOpcodes::opDisableAutoRegionLayer(Control *control, OpCall &opCall) {
-	control->_actor->_flags &= ~0x20;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_20;
 }
 
 void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(regionLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= 0x20;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_20;
 	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
 }
 


Commit: 2be02752218955374b5de881fc6f310097c09cbb
    https://github.com/scummvm/scummvm/commit/2be02752218955374b5de881fc6f310097c09cbb
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Connect up midi fade to MidiPlayer object

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/sound.cpp
    engines/illusions/sound.h


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index db8a7a8..a2303b0 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -633,7 +633,7 @@ void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &
 void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(duration);
 	ARG_INT16(finalVolume);
-	// TODO _vm->fadeMidiMusic(2, finalVolume, duration, opCall._threadId);
+	_vm->_soundMan->fadeMidiMusic(finalVolume, duration);
 }
 
 void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 9379bf1..307d40a 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -152,6 +152,11 @@ void MidiPlayer::sendToChannel(byte channel, uint32 b) {
 		_channelsTable[channel]->send(b);
 }
 
+void MidiPlayer::fade(int16 finalVolume, int16 duration) {
+	//TODO fade here.
+	debug(0, "Fade midi. finalVolume: %d, duration: %d", finalVolume, duration);
+}
+
 // VoicePlayer
 
 VoicePlayer::VoicePlayer() {
@@ -355,4 +360,8 @@ void SoundMan::stopMidiMusic() {
 	_midiPlayer->stop();
 }
 
+void SoundMan::fadeMidiMusic(int16 finalVolume, int16 duration) {
+	_midiPlayer->fade(finalVolume, duration);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index 7979978..d2da8ed 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -53,6 +53,7 @@ public:
 
 	void pause(bool p);
 	void play(const Common::String &filename);
+	void fade(int16 finalVolume, int16 duration);
 
 	// The following line prevents compiler warnings about hiding the pause()
 	// method from the parent class.
@@ -114,6 +115,7 @@ public:
 
 	void playMidiMusic(uint32 musicId);
 	void stopMidiMusic();
+	void fadeMidiMusic(int16 finalVolume, int16 duration);
 
 	bool cueVoice(const char *voiceName);
 	void stopCueingVoice();


Commit: c66b2208e0f8aa35288da64ce3903edc5a8205cd
    https://github.com/scummvm/scummvm/commit/c66b2208e0f8aa35288da64ce3903edc5a8205cd
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: silence clang warning about not operation in expression

Changed paths:
    engines/illusions/resources/backgroundresource.cpp


diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index 1eff8e0..ec2a08a 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -343,7 +343,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 
 int BackgroundResource::findMasterBgIndex() {
 	int index = 1;
-	while (!_bgInfos[index - 1]._flags & 1) //TODO check if this is a typo
+	while (!(_bgInfos[index - 1]._flags & 1)) //TODO check if this is correct
 		++index;
 	return index;
 }


Commit: 6e09cd7e0899052a927bb65ba13219a4cd53c814
    https://github.com/scummvm/scummvm/commit/6e09cd7e0899052a927bb65ba13219a4cd53c814
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Rename some actor flags

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/cursor.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/illusions.cpp
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index d24ec7b..4ebce41 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -252,7 +252,7 @@ void Control::unpause() {
 void Control::appearActor() {
 	if (_vm->getGameId() == kGameIdDuckman) {
 		_flags |= 1;
-		_actor->_flags |= Illusions::ACTOR_FLAG_1;
+		_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
 		if (_objectId == 0x40004) {
 			if (_actor->_frameIndex) {
 				_actor->_flags |= Illusions::ACTOR_FLAG_2000;
@@ -265,7 +265,7 @@ void Control::appearActor() {
 			_vm->showCursor();
 		} else {
 			if (_actor->_frameIndex || _actorTypeId == 0x50004)
-				_actor->_flags |= Illusions::ACTOR_FLAG_1;
+				_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
 			else
 				_actor->_flags |= Illusions::ACTOR_FLAG_1000;
 			for (uint i = 0; i < kSubObjectsCount; ++i)
@@ -280,12 +280,12 @@ void Control::appearActor() {
 void Control::disappearActor() {
 	if (_vm->getGameId() == kGameIdDuckman) {
 		_flags &= ~1;
-		_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
+		_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
 	} else {
 		if (_objectId == 0x40004) {
 			_vm->hideCursor();
 		} else {
-			_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
 			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
 			for (uint i = 0; i < kSubObjectsCount; ++i)
 				if (_actor->_subobjects[i]) {
@@ -297,7 +297,7 @@ void Control::disappearActor() {
 }
 
 bool Control::isActorVisible() {
-	return (_actor->_flags & Illusions::ACTOR_FLAG_1) != 0;
+	return (_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) != 0;
 }
 
 void Control::activateObject() {
@@ -578,8 +578,8 @@ void Control::stopSequenceActor() {
 	if (_actor->_flags & Illusions::ACTOR_FLAG_40) {
 		stopActor();
 		_actor->_frameIndex = 0;
-		if ((_actor->_flags & Illusions::ACTOR_FLAG_1) || (_actor->_flags & Illusions::ACTOR_FLAG_1000)) {
-			_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
+		if ((_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) || (_actor->_flags & Illusions::ACTOR_FLAG_1000)) {
+			_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
 			_actor->_flags |= Illusions::ACTOR_FLAG_1000;
 		}
 	}
@@ -594,7 +594,7 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 	bool doSeq = true;
 	if (_actor->_linkIndex2) {
 		Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[_actor->_linkIndex2 - 1]);
-		if (subControl->_actor->_flags & Illusions::ACTOR_FLAG_1) {
+		if (subControl->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
 			if (_actor->_pathNode) {
 				doSeq = false;
 				subControl->_actor->_notifyThreadId2 = threadId;
@@ -646,7 +646,7 @@ void Control::sequenceActor() {
 		//debug(1, "New frame %d", _actor->_newFrameIndex);
 		setActorFrameIndex(_actor->_newFrameIndex);
 		if (_vm->getGameId() == kGameIdBBDOU &&
-			!(_actor->_flags & Illusions::ACTOR_FLAG_1) && (_actor->_flags & Illusions::ACTOR_FLAG_1000) && (_objectId != 0x40004)) {
+			!(_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) && (_actor->_flags & Illusions::ACTOR_FLAG_1000) && (_objectId != 0x40004)) {
 			appearActor();
 			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
 		}
@@ -766,8 +766,8 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 }
 
 PointArray *Control::createPath(Common::Point destPt) {
-	PointArray *walkPoints = (_actor->_flags & Illusions::ACTOR_FLAG_2) ? _actor->_pathWalkPoints->_points : 0;
-	PathLines *walkRects = (_actor->_flags & Illusions::ACTOR_FLAG_10) ? _actor->_pathWalkRects->_rects : 0;
+	PointArray *walkPoints = (_actor->_flags & Illusions::ACTOR_FLAG_HAS_WALK_POINTS) ? _actor->_pathWalkPoints->_points : 0;
+	PathLines *walkRects = (_actor->_flags & Illusions::ACTOR_FLAG_HAS_WALK_RECTS) ? _actor->_pathWalkRects->_rects : 0;
 	PathFinder pathFinder;
 	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
 	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
@@ -1067,27 +1067,27 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	if (actorType->_pathWalkPointsIndex) {
 		actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
-		actor->_flags |= Illusions::ACTOR_FLAG_2;
+		actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_POINTS;
 	}
 
 	if (actorType->_scaleLayerIndex) {
 		actor->_scaleLayer = bgRes->getScaleLayer(actorType->_scaleLayerIndex - 1);
-		actor->_flags |= Illusions::ACTOR_FLAG_4;
+		actor->_flags |= Illusions::ACTOR_FLAG_SCALED;
 	}
 
 	if (actorType->_pathWalkRectIndex) {
 		actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
-		actor->_flags |= Illusions::ACTOR_FLAG_10;
+		actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_RECTS;
 	}
 	
 	if (actorType->_priorityLayerIndex) {
 		actor->_priorityLayer = bgRes->getPriorityLayer(actorType->_priorityLayerIndex - 1);
-		actor->_flags |= Illusions::ACTOR_FLAG_8;
+		actor->_flags |= Illusions::ACTOR_FLAG_PRIORITY;
 	}
 	
 	if (actorType->_regionLayerIndex) {
 		actor->_regionLayer = bgRes->getRegionLayer(actorType->_regionLayerIndex - 1);
-		actor->_flags |= Illusions::ACTOR_FLAG_20;
+		actor->_flags |= Illusions::ACTOR_FLAG_REGION;
 	}
 	
 	actor->_pathCtrY = 140;
@@ -1291,7 +1291,7 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 		Control *testControl = *it;
 		if (testControl != control && testControl->_pauseCtr == 0 &&
 			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
-			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_1))) {
+			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE))) {
 			Common::Rect collisionRect;
 			testControl->getCollisionRect(collisionRect);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt)) {
@@ -1325,7 +1325,7 @@ bool Controls::getOverlappedObjectAccurate(Control *control, Common::Point pt, C
 		Control *testControl = *it;
 		if (testControl != control && testControl->_pauseCtr == 0 &&
 			(testControl->_flags & 1) && !(testControl->_flags & 0x10) &&
-			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_1))) {
+			(!testControl->_actor || (testControl->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE))) {
 			Common::Rect collisionRect;
 			testControl->getCollisionRectAccurate(collisionRect);
 			if (!collisionRect.isEmpty() && collisionRect.contains(pt) &&
@@ -1413,18 +1413,18 @@ void Controls::actorControlRoutine(Control *control, uint32 deltaTime) {
 		actor->_seqCodeValue1 = 100 * deltaTime;
 	}
 
-	if (actor->_flags & Illusions::ACTOR_FLAG_4) {
+	if (actor->_flags & Illusions::ACTOR_FLAG_SCALED) {
 		int scale = actor->_scaleLayer->getScale(actor->_position);
 		control->setActorScale(scale);
 	}
 
-	if (actor->_flags & Illusions::ACTOR_FLAG_8) {
+	if (actor->_flags & Illusions::ACTOR_FLAG_PRIORITY) {
 		int16 priority = actor->_priorityLayer->getPriority(actor->_position);
 		if (priority)
 			control->setPriority(priority + 1);
 	}
 
-	if (actor->_flags & Illusions::ACTOR_FLAG_20) {
+	if (actor->_flags & Illusions::ACTOR_FLAG_REGION) {
 		// Update transition sequence
 		int regionIndex = actor->_regionLayer->getRegionIndex(actor->_position);
 		if (actor->_regionIndex != regionIndex) {
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index e590cac..7fb99ce 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -40,12 +40,12 @@ class SequenceOpcodes;
 struct OpCall;
 
 enum ActorFlags {
-	ACTOR_FLAG_1 = 1,
-	ACTOR_FLAG_2 = 2,
-	ACTOR_FLAG_4 = 4,
-	ACTOR_FLAG_8 = 8,
-	ACTOR_FLAG_10 = 0x10,
-	ACTOR_FLAG_20 = 0x20,
+	ACTOR_FLAG_IS_VISIBLE = 1,
+	ACTOR_FLAG_HAS_WALK_POINTS = 2,
+	ACTOR_FLAG_SCALED = 4,
+	ACTOR_FLAG_PRIORITY = 8,
+	ACTOR_FLAG_HAS_WALK_RECTS = 0x10,
+	ACTOR_FLAG_REGION = 0x20,
 	ACTOR_FLAG_40 = 0x40,
 	ACTOR_FLAG_80 = 0x80,
 	ACTOR_FLAG_100 = 0x100,
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 4d54eb4..b0353c2 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -410,7 +410,7 @@ void IllusionsEngine_BBDOU::hideCursor() {
 
 void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (control->_actor->_flags & Illusions::ACTOR_FLAG_1) {
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
 		switch (_cursor->_status) {
 		case 2:
 			// Unused nullsub_1(control);
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
index 0cb6ff7..da8a83b 100644
--- a/engines/illusions/cursor.cpp
+++ b/engines/illusions/cursor.cpp
@@ -62,7 +62,7 @@ void Cursor::show() {
 	++_visibleCtr;
 	if (_visibleCtr > 0) {
 		_control->_flags |= 1;
-		_control->_actor->_flags |= Illusions::ACTOR_FLAG_1;
+		_control->_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
 		if (_control->_actor->_frameIndex) {
 			_control->_actor->_flags |= Illusions::ACTOR_FLAG_2000;
 			_control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
@@ -75,7 +75,7 @@ void Cursor::hide() {
 	--_visibleCtr;
 	if (_visibleCtr <= 0) {
 		_control->_flags &= ~1;
-		_control->_actor->_flags &= ~Illusions::ACTOR_FLAG_1;
+		_control->_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
 	}
 }
 
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index d917a35..9407812 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -692,7 +692,7 @@ void IllusionsEngine_Duckman::stopCursorHoldingObject() {
 
 void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 deltaTime) {
 	control->_actor->_seqCodeValue1 = 100 * deltaTime;
-	if (control->_actor->_flags & Illusions::ACTOR_FLAG_1) {
+	if (control->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
 		switch (_cursor._gameState) {
 		case 2:
 			updateGameState2();
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 2317bd3..11ef6d0 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -188,7 +188,7 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
 		Actor *actor = control->_actor;
-		if (control->_pauseCtr == 0 && actor && (actor->_flags & Illusions::ACTOR_FLAG_1) && !(actor->_flags & Illusions::ACTOR_FLAG_200)) {
+		if (control->_pauseCtr == 0 && actor && (actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) && !(actor->_flags & Illusions::ACTOR_FLAG_200)) {
 			Common::Point drawPosition = control->calcPosition(panPoint);
 			if (actor->_flags & Illusions::ACTOR_FLAG_2000) {
 				Frame *frame = &(*actor->_frames)[actor->_frameIndex - 1];
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index c394537..2115209 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -295,64 +295,64 @@ void SequenceOpcodes::opSetPathCtrY(Control *control, OpCall &opCall) {
 void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkPointsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= Illusions::ACTOR_FLAG_2;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_POINTS;
 	control->_actor->_pathWalkPoints = bgRes->getPathWalkPoints(pathWalkPointsIndex - 1);
 }
 
 void SequenceOpcodes::opDisableAutoScale(Control *control, OpCall &opCall) {
 	// Keep current scale but don't autoscale
-	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_4;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_SCALED;
 }
 
 void SequenceOpcodes::opSetScale(Control *control, OpCall &opCall) {
 	ARG_INT16(scale);
-	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_4;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_SCALED;
 	control->setActorScale(scale);
 }
 
 void SequenceOpcodes::opSetScaleLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(scaleLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= Illusions::ACTOR_FLAG_4;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_SCALED;
 	control->_actor->_scaleLayer = bgRes->getScaleLayer(scaleLayerIndex - 1);
 	int scale = control->_actor->_scaleLayer->getScale(control->_actor->_position);
 	control->setActorScale(scale);
 }
 
 void SequenceOpcodes::opDeactivatePathWalkRects(Control *control, OpCall &opCall) {
-	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_10;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_HAS_WALK_RECTS;
 }
 
 void SequenceOpcodes::opSetPathWalkRects(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkRectsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= Illusions::ACTOR_FLAG_10;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_RECTS;
 	control->_actor->_pathWalkRects = bgRes->getPathWalkRects(pathWalkRectsIndex - 1);
 }
 
 void SequenceOpcodes::opSetPriority(Control *control, OpCall &opCall) {
 	ARG_INT16(priority);
-	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_8;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_PRIORITY;
 	control->setPriority(priority);
 }
 
 void SequenceOpcodes::opSetPriorityLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(priorityLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= Illusions::ACTOR_FLAG_8;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_PRIORITY;
 	control->_actor->_priorityLayer = bgRes->getPriorityLayer(priorityLayerIndex - 1);
 	int priority = control->_actor->_priorityLayer->getPriority(control->_actor->_position);
 	control->setPriority(priority);
 }
 
 void SequenceOpcodes::opDisableAutoRegionLayer(Control *control, OpCall &opCall) {
-	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_20;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_REGION;
 }
 
 void SequenceOpcodes::opSetRegionLayer(Control *control, OpCall &opCall) {
 	ARG_INT16(regionLayerIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
-	control->_actor->_flags |= Illusions::ACTOR_FLAG_20;
+	control->_actor->_flags |= Illusions::ACTOR_FLAG_REGION;
 	control->_actor->_regionLayer = bgRes->getRegionLayer(regionLayerIndex - 1);
 }
 


Commit: 7c46d891c6acc414e69c5cf624f45b7b297fe584
    https://github.com/scummvm/scummvm/commit/7c46d891c6acc414e69c5cf624f45b7b297fe584
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Print walk rectangle point on background to aid in debugging
Comment and rename some code in pathfinder to aid in understanding

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/pathfinder.cpp
    engines/illusions/pathfinder.h
    engines/illusions/resources/backgroundresource.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 4ebce41..e9aad26 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -1063,7 +1063,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 		actor->_facing = 64;
 	actor->_scale = actorType->_scale;
 	actor->_namedPoints = &actorType->_namedPoints;
-	
+
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
 	if (actorType->_pathWalkPointsIndex) {
 		actor->_pathWalkPoints = bgRes->getPathWalkPoints(actorType->_pathWalkPointsIndex - 1);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index a2303b0..10efcea 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -274,6 +274,7 @@ void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThrea
 //static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
 //static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
 //static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
+//static uint dsceneId = 0x10035, dthreadId = 0x000201B4; // Starship Enterprise (outside)
 //static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
 //static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map
 //static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index ac4f53d..1e781e6 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -261,12 +261,17 @@ void PathFinder::findDeltaPt(Common::Point pt, Common::Point &outDeltaPt) {
 		}
 	}
 }
-
-bool PathFinder::testRect(PathLine &line, PathLine &rect) {
+/**
+ * returns true if line is contained within rect.
+ */
+bool PathFinder::isLineWithinRectangle(PathLine &line, PathLine &rect) {
 	return line.p0.x <= rect.p1.x && line.p1.x >= rect.p0.x &&
 		line.p0.y <= rect.p1.y && line.p1.y >= rect.p0.y;
 }
 
+/**
+ * flip line coordinates so it starts top left and finishes bottom right
+ */
 void PathFinder::swapLine(PathLine &line, PathLine &outLine) {
 	if (line.p1.x <= line.p0.x) {
 		outLine.p1.x = line.p0.x;
@@ -289,7 +294,7 @@ int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common:
 	swapLine(sourceLine, sourceLine1);
 	swapLine(destRect, destRect1);
 
-	if (!testRect(sourceLine1, destRect1))
+	if (!isLineWithinRectangle(sourceLine1, destRect1))
 		return 3;
 
 	int sourceDeltaX = sourceLine.p1.x - sourceLine.p0.x;
diff --git a/engines/illusions/pathfinder.h b/engines/illusions/pathfinder.h
index ca402bb..f337a25 100644
--- a/engines/illusions/pathfinder.h
+++ b/engines/illusions/pathfinder.h
@@ -57,7 +57,7 @@ protected:
 	void swapDimensions(WidthHeight &dimensions);
 	void clipLineToBg(Common::Point &destPt, WidthHeight &rectDimensions, PathLine &outDestLine);
 	void findDeltaPt(Common::Point pt, Common::Point &outDeltaPt);
-	bool testRect(PathLine &line, PathLine &rect);
+	bool isLineWithinRectangle(PathLine &line, PathLine &rect);
 	void swapLine(PathLine &line, PathLine &outLine);
 	int calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common::Point *outPoint);
 };
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index ec2a08a..fc13326 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -466,6 +466,16 @@ void BackgroundInstance::initSurface() {
 		_panPoints[i] = bgInfo->_panPoint;
 		_surfaces[i] = _vm->_screen->allocSurface(bgInfo->_surfInfo);
 		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
+#if 0
+		if(_bgRes->_pathWalkRectsCount > 0) {
+			PathLines *pl = _bgRes->_pathWalkRects->_rects;
+			for(int j=0;j < pl->size(); j++) {
+				PathLine pathLine = (*pl)[j];
+				debug(0, "walk path rect line[%d]. (%d,%d)->(%d,%d)", j, pathLine.p0.x, pathLine.p0.y, pathLine.p1.x, pathLine.p1.y);
+				_surfaces[i]->drawLine(pathLine.p0.x, pathLine.p0.y, pathLine.p1.x, pathLine.p1.y, 5);
+			}
+		}
+#endif
 	}
 }
 


Commit: bdc477bef9d4ec9691e77c4de867a30d08865c46
    https://github.com/scummvm/scummvm/commit/bdc477bef9d4ec9691e77c4de867a30d08865c46
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement convertPanXCoord() to support audio panning.
Wire up startScriptThread2() calls.
Add namedpoint logic.

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.cpp
    engines/illusions/pathfinder.cpp
    engines/illusions/pathfinder.h
    engines/illusions/thread.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index e9aad26..cb9f3a9 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -770,7 +770,7 @@ PointArray *Control::createPath(Common::Point destPt) {
 	PathLines *walkRects = (_actor->_flags & Illusions::ACTOR_FLAG_HAS_WALK_RECTS) ? _actor->_pathWalkRects->_rects : 0;
 	PathFinder pathFinder;
 	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
-	PointArray *path = pathFinder.findPath(_actor->_position, destPt, walkPoints, walkRects, bgDimensions);
+	PointArray *path = pathFinder.findPath(_vm->_camera, _actor->_position, destPt, walkPoints, walkRects, bgDimensions);
 	for (uint i = 0; i < path->size(); ++i) {
 		//debug(0, "Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
 	}
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index d53896d..60e0505 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -103,6 +103,7 @@ void DuckmanSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
 	} else {
 		debug("DuckmanSpecialCode::run() Unimplemented special code %08X", specialCodeId);
 		_vm->notifyThreadId(opCall._threadId);
+		error("DuckmanSpecialCode::run() Unimplemented special code");
 	}
 }
 
@@ -392,7 +393,7 @@ void DuckmanSpecialCode::updateTeleporterProperties() {
 	_vm->_scriptResource->_properties.set(0x000E0075, _teleporterPosition.x == 4 && _teleporterPosition.y == 3);
 	_vm->_scriptResource->_properties.set(0x000E0076, _teleporterPosition.x == 3 && _teleporterPosition.y == 3);
 	_vm->_scriptResource->_properties.set(0x000E0077, _teleporterPosition.x == 2 && _teleporterPosition.y == 2);
-	_vm->_scriptResource->_properties.set(0x000E0078, _teleporterPosition.x == 1 && _teleporterPosition.y == 1);	
+	_vm->_scriptResource->_properties.set(0x000E0078, _teleporterPosition.x == 1 && _teleporterPosition.y == 1);
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 9407812..325b6dc 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -261,10 +261,10 @@ void IllusionsEngine_Duckman::initInput() {
 	_input->setInputEvent(kEventDown, 0x80)
 		.addMouseButton(MOUSE_RIGHT_BUTTON)
 		.addKey(Common::KEYCODE_DOWN);
-	/* Not implemented, used for original debugging purposes
+#if 1 //TODO hide behind "gosanta" code
 	_input->setInputEvent(kEventF1, 0x100)
 		.addKey(Common::KEYCODE_F1);
-	*/
+#endif
 }
 
 #define UPDATEFUNCTION(priority, sceneId, callback) \
@@ -294,6 +294,8 @@ int IllusionsEngine_Duckman::updateScript(uint flags) {
 	}
 
 	_threads->updateThreads();
+
+	//TODO need to call startScriptThread2(0x10002, 0x20001, 0);
 	return kUFNext;
 }
 
@@ -459,6 +461,22 @@ Control *IllusionsEngine_Duckman::getObjectControl(uint32 objectId) {
 	return _dict->getObjectControl(objectId);
 }
 
+static const uint8 kPointConversionTable[] = {
+		0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 1, 2, 3,
+		4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+		35, 35, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+		33, 34
+};
 Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId) {
 	Common::Point pt;
 	Common::Point currPan = _camera->getCurrentPan();
@@ -471,8 +489,50 @@ Common::Point IllusionsEngine_Duckman::getNamedPointPosition(uint32 namedPointId
 			return currPan;
 		}
 	} else {
-		// TODO
-		debug(1, "getNamedPointPosition(%08X) UNKNOWN", namedPointId);
+		debug(1, "getNamedPointPosition(%02d)", kPointConversionTable[namedPointId - 0x00070001]);
+		switch(kPointConversionTable[namedPointId - 0x00070001]) {
+			case 0 : return Common::Point(160, 100);
+			case 1 : return currPan;
+			case 2 : return Common::Point(currPan.x - 160, currPan.y);
+			case 3 : return Common::Point(currPan.x + 160, currPan.y);
+			case 4 : return Common::Point(currPan.x, currPan.y - 100);
+			case 5 : return Common::Point(currPan.x, currPan.y + 100);
+			case 6 : return Common::Point(currPan.x - 160, currPan.y - 100);
+			case 7 : return Common::Point((uint16)(currPan.x + 160), currPan.y - 100);
+			case 8 : return Common::Point(currPan.x - 160, currPan.y + 100);
+			case 9 : return Common::Point(currPan.x + 160, currPan.y + 100);
+			/* TODO implement these
+			case 10 : return Common::Point(0, 0);
+			case 11 : return Common::Point(0, 0);
+			case 12 : return Common::Point(0, 0);
+			case 13 : return Common::Point(0, 0);
+			case 14 : return Common::Point(0, 0);
+			*/
+			case 15 : return Common::Point(0, 0);
+			/* TODO implement these
+			case 16 : return Common::Point(0, 0);
+			case 17 : return Common::Point(0, 0);
+			case 18 : return Common::Point(0, 0);
+			 */
+			case 19 : return Common::Point(0, 0);
+			case 20 : return Common::Point(320, 0);
+			case 21 : return Common::Point(640, 0);
+			case 22 : return Common::Point(960, 0);
+			case 23 : return Common::Point(0, 200);
+			case 24 : return Common::Point(320, 200);
+			case 25 : return Common::Point(640, 200);
+			case 26 : return Common::Point(960, 200);
+			case 27 : return Common::Point(0, 400);
+			case 28 : return Common::Point(320, 400);
+			case 29 : return Common::Point(640, 400);
+			case 30 : return Common::Point(960, 400);
+			case 31 : return Common::Point(0, 600);
+			case 32 : return Common::Point(320, 0);
+			case 33 : return Common::Point(640, 0);
+			case 34 : return Common::Point(960, 0);
+			default : break;
+		}
+		error("getNamedPointPosition(%02d) UNKNOWN", kPointConversionTable[namedPointId - 0x00070001]);
 		return Common::Point(0, 0);
 	}
 }
@@ -717,6 +777,13 @@ void IllusionsEngine_Duckman::startScriptThread(uint32 threadId, uint32 callingT
 	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp);
 }
 
+void IllusionsEngine_Duckman::startScriptThread2(uint32 sceneId, uint32 threadId, uint32 callingThreadId) {
+	debug(2, "Starting script thread2");
+	_savegameSceneId = sceneId;
+	_savegameThreadId = threadId;
+	startScriptThread(0x20002, callingThreadId);
+}
+
 uint32 IllusionsEngine_Duckman::startAbortableTimerThread(uint32 duration, uint32 threadId) {
 	return newTimerThread(duration, threadId, true);
 }
@@ -824,7 +891,7 @@ bool IllusionsEngine_Duckman::enterScene(uint32 sceneId, uint32 threadId) {
 			startScriptThread(threadId, 0);
 		return true;
 	}
-	// TODO startScriptThread2(0x10002, 0x20001, 0);
+	startScriptThread2(0x10002, 0x20001, 0);
 	return false;
 }
 
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index daa1b06..87ca932 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -147,6 +147,7 @@ public:
 
 	void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId);
 	void startScriptThread(uint32 threadId, uint32 callingThreadId);
+	void startScriptThread2(uint32 threadId, uint32 callingThreadId, uint32 unk);
 	uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
 	uint32 startTimerThread(uint32 duration, uint32 threadId);
 	uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 10efcea..9ac0b9a 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -321,7 +321,7 @@ void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall
 void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_input->discardAllEvents();
 	if (_vm->_scriptResource->_properties.get(0x000E0027)) {
-		// TODO _vm->startScriptThread2(0x10002, 0x20001, 0);
+		_vm->startScriptThread2(0x10002, 0x20001, 0);
 		opCall._result = kTSTerminate;
 	} else {
 		_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
@@ -521,7 +521,7 @@ void ScriptOpcodes_Duckman::opAppearActor(ScriptThread *scriptThread, OpCall &op
 	ARG_UINT32(objectId);
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	if (!control) {
-		Common::Point pos = _vm->getNamedPointPosition(0x70001);		
+		Common::Point pos = _vm->getNamedPointPosition(0x70001);
 		_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
 		control = _vm->_dict->getObjectControl(objectId);
 	}
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 11ef6d0..8388110 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -238,8 +238,19 @@ int IllusionsEngine::getRandom(int max) {
 }
 
 int IllusionsEngine::convertPanXCoord(int16 x) {
-	// TODO
-	return 0;
+	int16 diff = x - _camera->getCurrentPan().x;
+	int16 absX = ABS(diff);
+	int newX = 0;
+	if ( absX < 160) {
+		newX = (diff << 7) / 320;
+	} else if (diff < 0) {
+		newX = -64;
+	} else {
+		newX = 64;
+	}
+	debug(1, "convertPanXCoord %d %d -> %d", diff, x, newX);
+
+	return newX;
 }
 
 bool IllusionsEngine::calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing) {
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index 1e781e6..b0b43a0 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -22,11 +22,16 @@
 
 #include "illusions/illusions.h"
 #include "illusions/pathfinder.h"
+#include "camera.h"
 
 namespace Illusions {
 
-PointArray *PathFinder::findPath(Common::Point sourcePt, Common::Point destPt,
+PointArray *PathFinder::findPath(Camera *camera, Common::Point sourcePt, Common::Point destPt,
 	PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions) {
+	Common::Point cameraPt = camera->getScreenOffset();
+	_screenRect.p0 = cameraPt;
+	_screenRect.p1.x = cameraPt.x + 320; //TODO fix me get screen dimentions here.
+	_screenRect.p1.y = cameraPt.y + 200;
 	_walkPoints = walkPoints;
 	_walkRects = walkRects;
 	_bgDimensions = bgDimensions;
@@ -132,8 +137,16 @@ void PathFinder::findValidDestPt(Common::Point &destPt) {
 	int minDistance = 0xFFFF, currDistance;
 	PathLine destLine;
 	for (uint i = 0; i < _walkRects->size(); ++i) {
-		PathLine &currRect = (*_walkRects)[i];
+		PathLine currRect = (*_walkRects)[i];
+		//TODO fix this hack. Used here to get xmas tree scene to work.
+		if(currRect.p1.x > _screenRect.p1.x) {
+			currRect.p1.x = _screenRect.p1.x;
+		}
+		if(currRect.p0.x < _screenRect.p0.x) {
+			currRect.p0.x = _screenRect.p0.x;
+		}
 		WidthHeight rectDimensions = calcRectDimensions(currRect);
+
 		adjustRectDimensions(rectDimensions);
 		clipLineToBg(destPt, rectDimensions, destLine);
 		if (calcLineStatus(destLine, currRect, &outPt) == 3) {
diff --git a/engines/illusions/pathfinder.h b/engines/illusions/pathfinder.h
index f337a25..47cc057 100644
--- a/engines/illusions/pathfinder.h
+++ b/engines/illusions/pathfinder.h
@@ -39,9 +39,10 @@ typedef Common::Array<Common::Point> PointArray;
 
 class PathFinder {
 public:
-	PointArray *findPath(Common::Point sourcePt, Common::Point destPt,
+	PointArray *findPath(Camera *camera, Common::Point sourcePt, Common::Point destPt,
 		PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions);
 protected:
+	PathLine _screenRect;
 	PointArray *_walkPoints;
 	PathLines *_walkRects;
 	WidthHeight _bgDimensions;
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index b7ea41b..d984af2 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -160,7 +160,7 @@ void ThreadList::updateThreads() {
 		if (_vm->_rerunThreads)
 			_vm->_rerunThreads = false;
 		else
-			break;		
+			break;
 	}
 }
 


Commit: 423a8ec43351d1cbba84b67ad1c038ea4ca4e108
    https://github.com/scummvm/scummvm/commit/423a8ec43351d1cbba84b67ad1c038ea4ca4e108
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Replace 0x40004 with CURSOR_OBJECT_ID constant
Fix pan bug in starship enterprise scene
Add sequence opcode name to debug log
Hack to fix endless loop bug outside the starship enterprise

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/camera.cpp
    engines/illusions/duckman/duckman_dialog.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/menusystem.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h
    engines/illusions/threads/causethread_duckman.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index cb9f3a9..dba1195 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -222,7 +222,7 @@ void Control::pause() {
 
 	_vm->_dict->setObjectControl(_objectId, 0);
 
-	if (_objectId == 0x40004)
+	if (_objectId == Illusions::CURSOR_OBJECT_ID)
 		_vm->setCursorControl(0);
 
 	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200))
@@ -234,7 +234,7 @@ void Control::unpause() {
 
 	_vm->_dict->setObjectControl(_objectId, this);
 
-	if (_objectId == 0x40004)
+	if (_objectId == Illusions::CURSOR_OBJECT_ID)
 		_vm->setCursorControl(this);
   
 	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200)) {
@@ -253,7 +253,7 @@ void Control::appearActor() {
 	if (_vm->getGameId() == kGameIdDuckman) {
 		_flags |= 1;
 		_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
-		if (_objectId == 0x40004) {
+		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
 			if (_actor->_frameIndex) {
 				_actor->_flags |= Illusions::ACTOR_FLAG_2000;
 				_actor->_flags |= Illusions::ACTOR_FLAG_4000;
@@ -261,7 +261,7 @@ void Control::appearActor() {
 			_vm->_input->discardAllEvents();
 		}
 	} else {
-		if (_objectId == 0x40004) {
+		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
 			_vm->showCursor();
 		} else {
 			if (_actor->_frameIndex || _actorTypeId == 0x50004)
@@ -282,7 +282,7 @@ void Control::disappearActor() {
 		_flags &= ~1;
 		_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
 	} else {
-		if (_objectId == 0x40004) {
+		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
 			_vm->hideCursor();
 		} else {
 			_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
@@ -646,7 +646,7 @@ void Control::sequenceActor() {
 		//debug(1, "New frame %d", _actor->_newFrameIndex);
 		setActorFrameIndex(_actor->_newFrameIndex);
 		if (_vm->getGameId() == kGameIdBBDOU &&
-			!(_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) && (_actor->_flags & Illusions::ACTOR_FLAG_1000) && (_objectId != 0x40004)) {
+			!(_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) && (_actor->_flags & Illusions::ACTOR_FLAG_1000) && (_objectId != Illusions::CURSOR_OBJECT_ID)) {
 			appearActor();
 			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
 		}
@@ -1105,6 +1105,9 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	if (_vm->isCursorObject(actorTypeId, objectId))
 		_vm->placeCursorControl(control, sequenceId);
 
+	// TODO HACK at least we should restrict this to the sequenceId
+	control->setActorIndex(1);
+
 	control->startSequenceActor(sequenceId, 2, notifyThreadId);
 }
 
@@ -1205,7 +1208,7 @@ void Controls::destroyActiveControls() {
 			destroyControlInternal(*it);
 			it = _controls.erase(it);
 		} else
-			++it;			
+			++it;
 	}
 }
 
@@ -1216,7 +1219,7 @@ void Controls::destroyControlsBySceneId(uint32 sceneId) {
 			destroyControlInternal(*it);
 			it = _controls.erase(it);
 		} else
-			++it;			
+			++it;
 	}
 }
 
@@ -1481,7 +1484,7 @@ void Controls::destroyControlInternal(Control *control) {
 	if (!(control->_flags & 4) && control->_pauseCtr <= 0)
 		_vm->_dict->setObjectControl(control->_objectId, 0);
 
-	if (!(control->_flags & 4) && control->_objectId == 0x40004 && control->_pauseCtr <= 0)
+	if (!(control->_flags & 4) && control->_objectId == Illusions::CURSOR_OBJECT_ID && control->_pauseCtr <= 0)
 		_vm->setCursorControl(0);
 
 	if (control->_actor) {
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 7fb99ce..51f930b 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -58,6 +58,10 @@ enum ActorFlags {
 	ACTOR_FLAG_8000 = 0x8000
 };
 
+enum ControlObjectID {
+	CURSOR_OBJECT_ID = 0x40004
+};
+
 const uint kSubObjectsCount = 15;
 
 struct DefaultSequence {
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index b0353c2..a642835 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -384,7 +384,7 @@ uint32 IllusionsEngine_BBDOU::getPrevScene() {
 }
 
 bool IllusionsEngine_BBDOU::isCursorObject(uint32 actorTypeId, uint32 objectId) {
-	return actorTypeId == 0x50001 && objectId == 0x40004;
+	return actorTypeId == 0x50001 && objectId == Illusions::CURSOR_OBJECT_ID;
 }
 
 void IllusionsEngine_BBDOU::setCursorControlRoutine(Control *control) {
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 9e18ff3..390dff8 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -25,6 +25,7 @@
 #include "illusions/fixedpoint.h"
 #include "illusions/resources/backgroundresource.h"
 #include "illusions/time.h"
+#include "illusions/actor.h"
 
 namespace Illusions {
 
@@ -85,10 +86,16 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 
 void Camera::panCenterObject(uint32 objectId, int16 panSpeed) {
 	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
-	if (_vm->getGameId() == kGameIdDuckman && objectId == 0x40004) {
-		_activeState._cameraMode = 2;
-		_activeState._trackingLimits.x = 156;
-		_activeState._trackingLimits.y = 96;
+	if (_vm->getGameId() == kGameIdDuckman) {
+		if(objectId == Illusions::CURSOR_OBJECT_ID) {
+			_activeState._cameraMode = 2;
+			_activeState._trackingLimits.x = 156;
+			_activeState._trackingLimits.y = 96;
+		} else {
+			_activeState._cameraMode = 1;
+			_activeState._trackingLimits.x = 4;
+			_activeState._trackingLimits.y = 4;
+		}
 	} else if (_vm->getGameId() == kGameIdBBDOU) {
 		_activeState._cameraMode = 1;
 		_activeState._trackingLimits = _centerObjectTrackingLimits;
diff --git a/engines/illusions/duckman/duckman_dialog.cpp b/engines/illusions/duckman/duckman_dialog.cpp
index 3e42955..f624ed8 100644
--- a/engines/illusions/duckman/duckman_dialog.cpp
+++ b/engines/illusions/duckman/duckman_dialog.cpp
@@ -85,8 +85,8 @@ void DuckmanDialogSystem::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, u
 	} else {
 		if (!_vm->_cursor._control) {
 			Common::Point pos = _vm->getNamedPointPosition(0x70001);
-			_vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
-			_vm->_cursor._control = _vm->_dict->getObjectControl(0x40004);
+			_vm->_controls->placeActor(0x50001, pos, 0x60001, Illusions::CURSOR_OBJECT_ID, 0);
+			_vm->_cursor._control = _vm->_dict->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 		}
 		_vm->_cursor._control->appearActor();
 		_vm->setCursorActorIndex(6, 1, 0);
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 325b6dc..0b2bcca 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -944,13 +944,13 @@ void IllusionsEngine_Duckman::pause(uint32 callerThreadId) {
 		_threads->pauseThreads(callerThreadId);
 		_camera->pause();
 		pauseFader();
-		// TODO largeObj_pauseControlActor(0x40004);
+		// TODO largeObj_pauseControlActor(Illusions::CURSOR_OBJECT_ID);
 	}
 }
 
 void IllusionsEngine_Duckman::unpause(uint32 callerThreadId) {
 	if (--_pauseCtr == 0) {
-		// TODO largeObj_unpauseControlActor(0x40004);
+		// TODO largeObj_unpauseControlActor(Illusions::CURSOR_OBJECT_ID);
 		unpauseFader();
 		_camera->unpause();
 		_threads->unpauseThreads(callerThreadId);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 621a49c..263cda8 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -171,15 +171,15 @@ int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
 
 bool DuckmanMenuSystem::initMenuCursor() {
 	bool cursorInitialVisibleFlag = false;
-	Control *cursorControl = _vm->getObjectControl(0x40004);
+	Control *cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	if (cursorControl) {
 		if (cursorControl->_flags & 1)
 			cursorInitialVisibleFlag = false;
 		cursorControl->appearActor();
 	} else {
 		Common::Point pos = _vm->getNamedPointPosition(0x70001);
-		_vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
-		cursorControl = _vm->getObjectControl(0x40004);
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, Illusions::CURSOR_OBJECT_ID, 0);
+		cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	}
 	return cursorInitialVisibleFlag;
 }
@@ -189,7 +189,7 @@ int DuckmanMenuSystem::getGameState() {
 }
 
 void DuckmanMenuSystem::setMenuCursorNum(int cursorNum) {
-	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	Control *mouseCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	_vm->setCursorActorIndex(5, cursorNum, 0);
 	mouseCursor->startSequenceActor(0x60001, 2, 0);
 }
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 878dacf..3330cb7 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -215,7 +215,7 @@ bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIn
 
 void BaseMenuSystem::setMousePos(Common::Point &mousePos) {
 	_vm->_input->setCursorPosition(mousePos);
-	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	Control *mouseCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	mouseCursor->_actor->_position = mousePos;
 }
 
@@ -332,7 +332,7 @@ void BaseMenuSystem::openMenu(BaseMenu *menu) {
 	_cursorInitialVisibleFlag = initMenuCursor();
 	_savedCursorPos = _vm->_input->getCursorPosition();
 	_savedGameState = getGameState();
-	Control *cursorControl = _vm->getObjectControl(0x40004);
+	Control *cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	_savedCursorActorIndex = cursorControl->_actor->_actorIndex;
 	_savedCursorSequenceId = cursorControl->_actor->_sequenceId;
 	
@@ -358,7 +358,7 @@ void BaseMenuSystem::closeMenu() {
 	_vm->_screenText->removeText();
 	hideActorHoverBackground();
 	hideActorTextColorRect();
-	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	Control *mouseCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	setGameState(_savedGameState);
 	mouseCursor->_actor->_actorIndex = _savedCursorActorIndex;
 	mouseCursor->_actor->_position = _savedCursorPos;
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 2115209..b07e9ec 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -45,12 +45,14 @@ SequenceOpcodes::~SequenceOpcodes() {
 void SequenceOpcodes::execOpcode(Control *control, OpCall &opCall) {
 	if (!_opcodes[opCall._op])
 		error("SequenceOpcodes::execOpcode() Unimplemented opcode %d", opCall._op);
-	debug(3, "execOpcode(%d)", opCall._op);
+	debug(3, "execSequenceOpcode(%d) %s objectID: %08X", opCall._op, _opcodeNames[opCall._op].c_str(), control->_objectId);
 	(*_opcodes[opCall._op])(control, opCall);
 }
 
 typedef Common::Functor2Mem<Control*, OpCall&, void, SequenceOpcodes> SequenceOpcodeI;
-#define OPCODE(op, func) _opcodes[op] = new SequenceOpcodeI(this, &SequenceOpcodes::func);
+#define OPCODE(op, func) \
+	_opcodes[op] = new SequenceOpcodeI(this, &SequenceOpcodes::func); \
+	_opcodeNames[op] = #func;
 
 void SequenceOpcodes::initOpcodes() {
 	// First clear everything
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 85e5df6..8432512 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -41,6 +41,7 @@ public:
 protected:
 	IllusionsEngine *_vm;
 	SequenceOpcode *_opcodes[256];
+	Common::String _opcodeNames[256];
 	void initOpcodes();
 	void freeOpcodes();
 
diff --git a/engines/illusions/threads/causethread_duckman.cpp b/engines/illusions/threads/causethread_duckman.cpp
index 8fb8364..09f3fa9 100644
--- a/engines/illusions/threads/causethread_duckman.cpp
+++ b/engines/illusions/threads/causethread_duckman.cpp
@@ -39,14 +39,14 @@ CauseThread_Duckman::CauseThread_Duckman(IllusionsEngine_Duckman *vm, uint32 thr
 int CauseThread_Duckman::onUpdate() {
 	if (_flag) {
 		if (_vm->getCurrentScene() == _sceneId) {
-			Control *cursorCursor = _vm->getObjectControl(0x40004);
+			Control *cursorCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 			cursorCursor->appearActor();
 			_vm->_input->discardEvent(kEventLeftClick);
 		}
 		return kTSTerminate;
 	} else {
 		_sceneId = _vm->getCurrentScene();
-		Control *cursorCursor = _vm->getObjectControl(0x40004);
+		Control *cursorCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 		cursorCursor->disappearActor();
 		_vm->_input->discardEvent(kEventLeftClick);
 		_vm->startScriptThread(_triggerThreadId, _threadId);


Commit: 7b88bd8efe25fe3092e190d5fed16990c69e2849
    https://github.com/scummvm/scummvm/commit/7b88bd8efe25fe3092e190d5fed16990c69e2849
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add more logging
Add todo to check named point on opAppearForeignActor

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/resources/actorresource.cpp
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index dba1195..74d57d3 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -1033,6 +1033,7 @@ void Controls::placeBackgroundObject(BackgroundObject *backgroundObject) {
 	control->activateObject();
 	_controls.push_front(control);
 	_vm->_dict->setObjectControl(control->_objectId, control);
+	debug(0, "Added background control. objectId: %08X", control->_objectId);
 }
 
 void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId) {
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index 2152b77..59e2294 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -123,7 +123,7 @@ void ActorResource::load(Resource *resource) {
 		_actorTypes.push_back(actorType);
 	}
 
-	// Load sequences	
+	// Load sequences
 	stream.seek(0x08);
 	uint sequencesCount = stream.readUint16LE();
 	stream.seek(0x14);
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index b07e9ec..53c72b9 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -253,7 +253,7 @@ void SequenceOpcodes::opAppearForeignActor(Control *control, OpCall &opCall) {
 	ARG_INT16(foreignObjectNum);
 	Control *foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
 	if (!foreignControl) {
-		Common::Point pos = _vm->getNamedPointPosition(0x00070023);
+		Common::Point pos = _vm->getNamedPointPosition(0x00070023); // TODO Eric check this. duckman looks to be using 0x70001
 		_vm->_controls->placeActor(0x00050001, pos, 0x00060001, foreignObjectNum | 0x40000, 0);
 		foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
 	}


Commit: 88ef19056915b30d5e4b5d5e0e59ec6c17053d59
    https://github.com/scummvm/scummvm/commit/88ef19056915b30d5e4b5d5e0e59ec6c17053d59
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add logic to enable palette cycling sequence

Changed paths:
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h


diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index 59e2294..c38a467 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -20,6 +20,7 @@
  *
  */
 
+#include "illusions/actor.h"
 #include "illusions/illusions.h"
 #include "illusions/resources/actorresource.h"
 #include "illusions/dictionary.h"
@@ -223,6 +224,9 @@ void ActorInstance::initActorTypes() {
 	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
 		Sequence *sequence = &_actorResource->_sequences[i];
 		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
+		if (sequence->_sequenceId == 0x60101) {
+			_vm->_controls->placeActor(0x50023, Common::Point(0,0), sequence->_sequenceId, 0x400d7, 0);
+		}
 	}
 }
 
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index fc13326..3ad7085 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -27,6 +27,7 @@
 #include "illusions/dictionary.h"
 #include "illusions/resources/actorresource.h"
 #include "illusions/screen.h"
+#include "illusions/sequenceopcodes.h"
 #include "common/str.h"
 
 namespace Illusions {
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 53c72b9..f2de9f2 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -253,7 +253,7 @@ void SequenceOpcodes::opAppearForeignActor(Control *control, OpCall &opCall) {
 	ARG_INT16(foreignObjectNum);
 	Control *foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
 	if (!foreignControl) {
-		Common::Point pos = _vm->getNamedPointPosition(0x00070023); // TODO Eric check this. duckman looks to be using 0x70001
+		Common::Point pos = _vm->getNamedPointPosition(_vm->getGameId() == kGameIdDuckman ? 0x00070001 : 0x00070023);
 		_vm->_controls->placeActor(0x00050001, pos, 0x00060001, foreignObjectNum | 0x40000, 0);
 		foreignControl = _vm->_dict->getObjectControl(foreignObjectNum | 0x40000);
 	}
@@ -396,20 +396,20 @@ void SequenceOpcodes::opStartScriptThread(Control *control, OpCall &opCall) {
 }
 
 void SequenceOpcodes::opPlaceSubActor(Control *control, OpCall &opCall) {
- 	ARG_INT16(linkIndex);
- 	ARG_UINT32(actorTypeId);
- 	ARG_UINT32(sequenceId);
- 	_vm->_controls->placeSubActor(control->_objectId, linkIndex, actorTypeId, sequenceId);
+	ARG_INT16(linkIndex);
+	ARG_UINT32(actorTypeId);
+	ARG_UINT32(sequenceId);
+	_vm->_controls->placeSubActor(control->_objectId, linkIndex, actorTypeId, sequenceId);
 }
 
 void SequenceOpcodes::opStartSubSequence(Control *control, OpCall &opCall) {
- 	ARG_INT16(linkIndex);
- 	ARG_UINT32(sequenceId);
+	ARG_INT16(linkIndex);
+	ARG_UINT32(sequenceId);
 	control->startSubSequence(linkIndex, sequenceId);
 }
 
 void SequenceOpcodes::opStopSubSequence(Control *control, OpCall &opCall) {
- 	ARG_INT16(linkIndex);
+	ARG_INT16(linkIndex);
 	control->stopSubSequence(linkIndex);
 }
 
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 8432512..6c5b3ca 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -87,8 +87,8 @@ protected:
 	void opStartScriptThread(Control *control, OpCall &opCall);
 	void opPlaceSubActor(Control *control, OpCall &opCall);
 	void opStartSubSequence(Control *control, OpCall &opCall);
-	void opStopSubSequence(Control *control, OpCall &opCall);	
-	
+	void opStopSubSequence(Control *control, OpCall &opCall);
+
 };
 
 } // End of namespace Illusions


Commit: 83994972de35193e10ad01acac3369f1b00c992b
    https://github.com/scummvm/scummvm/commit/83994972de35193e10ad01acac3369f1b00c992b
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add debug cheat code support
Work on inventory add/remove debug menu.

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/input.cpp
    engines/illusions/input.h
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/actorresource.h


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 0b2bcca..c427293 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -261,10 +261,8 @@ void IllusionsEngine_Duckman::initInput() {
 	_input->setInputEvent(kEventDown, 0x80)
 		.addMouseButton(MOUSE_RIGHT_BUTTON)
 		.addKey(Common::KEYCODE_DOWN);
-#if 1 //TODO hide behind "gosanta" code
 	_input->setInputEvent(kEventF1, 0x100)
 		.addKey(Common::KEYCODE_F1);
-#endif
 }
 
 #define UPDATEFUNCTION(priority, sceneId, callback) \
@@ -288,7 +286,7 @@ int IllusionsEngine_Duckman::updateScript(uint flags) {
 	if (_screen->isDisplayOn() && !_screenPalette->isFaderActive() && _pauseCtr == 0) {
 		if (_input->pollEvent(kEventAbort)) {
 			startScriptThread(0x00020342, 0);
-		} else if (_input->pollEvent(kEventF1)) {
+		} else if (_input->isCheatModeActive() && _input->pollEvent(kEventF1)) {
 			startScriptThread(0x0002033F, 0);
 		}
 	}
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 263cda8..79734f1 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -24,6 +24,7 @@
 #include "illusions/actor.h"
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/menusystem_duckman.h"
+#include "illusions/resources/scriptresource.h"
 
 namespace Illusions {
 
@@ -83,6 +84,10 @@ BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
 		return createSaveCompleteMenu();
 	case kDuckmanOptionsMenu:
 		return createOptionsMenu();
+	case kDuckmanDebugMenu:
+		return createDebugMenu();
+		case kDuckmanAddRemoveInventoryMenu:
+			return createAddRemoveInventoryMenu();
 	default:
 		error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId);
 	}
@@ -148,12 +153,64 @@ BaseMenu *DuckmanMenuSystem::createSaveCompleteMenu() {
 	return menu;
 }
 
+BaseMenu *DuckmanMenuSystem::createDebugMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
+	menu->addText("Debug Pause Menu");
+	menu->addText("-----------------");
+	menu->addMenuItem(new MenuItem("Return to Game", new MenuActionReturnChoice(this, 1)));
+	menu->addMenuItem(new MenuItem("Add/Remove Inventory", new MenuActionEnterMenu(this, kDuckmanAddRemoveInventoryMenu)));
+	return menu;
+}
+typedef struct InventoryMenuItem {
+	const char *name;
+	uint32 objectId;
+	uint32 sequenceId;
+	uint32 propertyId;
+} InventoryMenuItem;
+
+static const InventoryMenuItem kDebugInventoryItems[21] =
+{
+	{ "Pick-up Book", 262212, 393231, 917519 },
+	{ "Bucket and Squeegee", 262314, 393233, 917599 },
+	{ "Cardboard Cut Out", 262219, 393264, 917573 },
+	{ "Talking Doll", 262209, 393943, 917587 },
+	{ "Cookie Fortunes", 262263, 393266, 917520 },
+	{ "Garbage Can Lid", 262311, 393259, 917597 },
+	{ "Chewing Gum", 262210, 393267, 917522 },
+	{ "Ladder", 262155, 393258, 917598 },
+	{ "Disco Light", 262342, 393260, 917594 },
+	{ "Magazine Cover", 262185, 393261, 917517 },
+	{ "Matches", 262159, 393232, 917516 },
+	{ "Opera Lessons", 262293, 393731, 917600 },
+	{ "Pizza Card", 262239, 393262, 917526 },
+	{ "Toilet Plunger", 262282, 393257, 917555 },
+	{ "Black Velvet Poster", 262258, 393269, 917527 },
+	{ "Red Spray Paint", 262297, 393254, 917531 },
+	{ "Remote Control", 262161, 393255, 917595 },
+	{ "Sparkplug", 262294, 393256, 917532 },
+	{ "Tape Recorder", 262328, 393827, 917584 },
+	{ "Wacky Putty", 262228, 393559, 917537 },
+	{ "Wrench", 262175, 393422, 917530 }
+};
+
+BaseMenu *DuckmanMenuSystem::createAddRemoveInventoryMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
+	menu->addText("Add/Remove Inventory");
+	menu->addText("-----------------");
+	for(int i=0;i < 21;i++) {
+		menu->addMenuItem(new MenuItem(kDebugInventoryItems[i].name, new MenuActionInventoryAddRemove(this, _vm, i)));
+	}
+	return menu;
+}
+
 int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
 	switch (menuId) {
 	case 0x180001:
 		return kDuckmanMainMenu;
 	case 0x180002:
 		return kDuckmanPauseMenu;
+	case 0x180004:
+		return kDuckmanDebugMenu;
 	case 0x180005:
 		return kDuckmanSaveCompleteMenu;
 	/*
@@ -198,4 +255,20 @@ void DuckmanMenuSystem::setGameState(int gameState) {
 	_vm->_cursor._gameState = gameState;
 }
 
+MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
+		: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex), _vm(vm) {
+}
+
+void MenuActionInventoryAddRemove::execute() {
+	if (_vm->_scriptResource->_properties.get(kDebugInventoryItems[_choiceIndex].propertyId)) {
+		//TODO stop holding object in cursor.
+		_vm->_scriptResource->_properties.set(kDebugInventoryItems[_choiceIndex].propertyId, false);
+	} else {
+		_vm->startCursorHoldingObject(kDebugInventoryItems[_choiceIndex].objectId,
+									  kDebugInventoryItems[_choiceIndex].sequenceId);
+		_vm->_scriptResource->_properties.set(kDebugInventoryItems[_choiceIndex].propertyId, true);
+	}
+	_menuSystem->leaveMenu();
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 10a8a6e..7bcf780 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -32,6 +32,8 @@ enum {
 	kDuckmanLoadGameMenu,
 	kDuckmanOptionsMenu,
 	kDuckmanPauseMenu,
+	kDuckmanDebugMenu,
+	kDuckmanAddRemoveInventoryMenu,
 	kDuckmanQueryQuitMenu,
 	kDuckmanQueryRestartMenu,
 	kDuckmanSaveCompleteMenu,
@@ -60,6 +62,8 @@ public://protected:
 	BaseMenu *createQueryRestartMenu();
 	BaseMenu *createQueryQuitMenu();
 	BaseMenu *createSaveCompleteMenu();
+	BaseMenu *createDebugMenu();
+	BaseMenu *createAddRemoveInventoryMenu();
 	int convertRootMenuId(uint32 menuId);
 	virtual bool initMenuCursor();
 	virtual int getGameState();
@@ -67,6 +71,15 @@ public://protected:
 	virtual void setMenuCursorNum(int cursorNum);
 };
 
+class MenuActionInventoryAddRemove : public BaseMenuAction {
+public:
+	MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex);
+	virtual void execute();
+protected:
+	IllusionsEngine_Duckman *_vm;
+	int _choiceIndex;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 56ce27a..21042dc 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -81,6 +81,7 @@ uint InputEvent::handle(Common::KeyCode key, int mouseButton, bool down) {
 // Input
 
 const uint kAllButtons = 0xFFFF;
+static const char kCheatCode[] = "gosanta";
 
 Input::Input() {
 	_buttonStates = 0;
@@ -92,6 +93,7 @@ Input::Input() {
 	_cursorPos.y = 0;
 	_prevCursorPos.x = 0;
 	_prevCursorPos.y = 0;
+	_cheatCodeIndex = 0;
 }
 
 void Input::processEvent(Common::Event event) {
@@ -177,6 +179,14 @@ void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
 	_buttonStates |= _newKeys;
 	_newKeys = 0;
 	_newButtons = ~prevButtonStates & _buttonStates;
+
+	if ( !down && !isCheatModeActive() ) {
+		if( _cheatCodeIndex < 7 && key == kCheatCode[_cheatCodeIndex] ) {
+			_cheatCodeIndex++;
+		} else {
+			_cheatCodeIndex = 0;
+		}
+	}
 }
 
 void Input::handleMouseButton(int mouseButton, bool down) {
@@ -211,4 +221,8 @@ void Input::discardButtons(uint bitMask) {
 	_buttonStates &= ~bitMask;
 }
 
+bool Input::isCheatModeActive() {
+	return _cheatCodeIndex == 7;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index 9956039..77a1bda 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -89,7 +89,9 @@ public:
 	void setCursorPosition(Common::Point mousePos);
 	Common::Point getCursorDelta();
 	InputEvent& setInputEvent(uint evt, uint bitMask);
+	bool isCheatModeActive();
 protected:
+	uint _cheatCodeIndex;
 	uint _buttonStates, _newButtons, _buttonsDown;
 	uint _enabledButtons;
 	uint _newKeys;
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index c38a467..4212457 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -183,7 +183,7 @@ void ActorInstance::load(Resource *resource) {
 	_actorResource->load(resource);
 	_sceneId = resource->_sceneId;
 	_pauseCtr = 0;
-	initActorTypes();
+	initActorTypes(resource->_gameId);
 }
 
 void ActorInstance::unload() {
@@ -205,7 +205,7 @@ void ActorInstance::unpause() {
 		registerResources();
 }
 
-void ActorInstance::initActorTypes() {
+void ActorInstance::initActorTypes(int gameId) {
 	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
 		ActorType *actorType = &_actorResource->_actorTypes[i];
 		ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
@@ -224,7 +224,8 @@ void ActorInstance::initActorTypes() {
 	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
 		Sequence *sequence = &_actorResource->_sequences[i];
 		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
-		if (sequence->_sequenceId == 0x60101) {
+		if (gameId == kGameIdDuckman && sequence->_sequenceId == 0x60101) {
+			// TODO check that this is the correct location for this logic.
 			_vm->_controls->placeActor(0x50023, Common::Point(0,0), sequence->_sequenceId, 0x400d7, 0);
 		}
 	}
diff --git a/engines/illusions/resources/actorresource.h b/engines/illusions/resources/actorresource.h
index c24213d..ea356a9 100644
--- a/engines/illusions/resources/actorresource.h
+++ b/engines/illusions/resources/actorresource.h
@@ -105,8 +105,8 @@ public:
 	int _pauseCtr;
 	ActorResource *_actorResource;
 protected:
-	void initActorTypes();
-	void registerResources();	
+	void initActorTypes(int gameId);
+	void registerResources();
 	void unregisterResources();	
 };
 


Commit: 4a42aba1fdf35515d253e588e98a0223bec1d78d
    https://github.com/scummvm/scummvm/commit/4a42aba1fdf35515d253e588e98a0223bec1d78d
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Start on debug menu

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 79734f1..e756518 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -84,8 +84,8 @@ BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
 		return createSaveCompleteMenu();
 	case kDuckmanOptionsMenu:
 		return createOptionsMenu();
-	case kDuckmanDebugMenu:
-		return createDebugMenu();
+	case kDuckmanDebugPauseMenu:
+		return createDebugPauseMenu();
 		case kDuckmanAddRemoveInventoryMenu:
 			return createAddRemoveInventoryMenu();
 	default:
@@ -154,6 +154,14 @@ BaseMenu *DuckmanMenuSystem::createSaveCompleteMenu() {
 }
 
 BaseMenu *DuckmanMenuSystem::createDebugMenu() {
+	// TODO
+	BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
+	menu->addText("Debug Pause Menu");
+	menu->addText("-----------------");
+	return menu;
+}
+
+BaseMenu *DuckmanMenuSystem::createDebugPauseMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
 	menu->addText("Debug Pause Menu");
 	menu->addText("-----------------");
@@ -161,6 +169,7 @@ BaseMenu *DuckmanMenuSystem::createDebugMenu() {
 	menu->addMenuItem(new MenuItem("Add/Remove Inventory", new MenuActionEnterMenu(this, kDuckmanAddRemoveInventoryMenu)));
 	return menu;
 }
+
 typedef struct InventoryMenuItem {
 	const char *name;
 	uint32 objectId;
@@ -209,8 +218,10 @@ int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
 		return kDuckmanMainMenu;
 	case 0x180002:
 		return kDuckmanPauseMenu;
-	case 0x180004:
+	case 0x180003:
 		return kDuckmanDebugMenu;
+	case 0x180004:
+		return kDuckmanDebugPauseMenu;
 	case 0x180005:
 		return kDuckmanSaveCompleteMenu;
 	/*
@@ -261,7 +272,9 @@ MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuS
 
 void MenuActionInventoryAddRemove::execute() {
 	if (_vm->_scriptResource->_properties.get(kDebugInventoryItems[_choiceIndex].propertyId)) {
-		//TODO stop holding object in cursor.
+		if(_vm->_cursor._objectId == kDebugInventoryItems[_choiceIndex].objectId) {
+			_vm->stopCursorHoldingObject();
+		}
 		_vm->_scriptResource->_properties.set(kDebugInventoryItems[_choiceIndex].propertyId, false);
 	} else {
 		_vm->startCursorHoldingObject(kDebugInventoryItems[_choiceIndex].objectId,
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 7bcf780..787ced3 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -33,6 +33,7 @@ enum {
 	kDuckmanOptionsMenu,
 	kDuckmanPauseMenu,
 	kDuckmanDebugMenu,
+	kDuckmanDebugPauseMenu,
 	kDuckmanAddRemoveInventoryMenu,
 	kDuckmanQueryQuitMenu,
 	kDuckmanQueryRestartMenu,
@@ -63,6 +64,7 @@ public://protected:
 	BaseMenu *createQueryQuitMenu();
 	BaseMenu *createSaveCompleteMenu();
 	BaseMenu *createDebugMenu();
+	BaseMenu *createDebugPauseMenu();
 	BaseMenu *createAddRemoveInventoryMenu();
 	int convertRootMenuId(uint32 menuId);
 	virtual bool initMenuCursor();


Commit: 0793272dfa574d48c28e94181993c3c7ab5afb60
    https://github.com/scummvm/scummvm/commit/0793272dfa574d48c28e94181993c3c7ab5afb60
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Wire up sfx to menus

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index e756518..8a9e1cc 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -97,7 +97,7 @@ BaseMenu *DuckmanMenuSystem::createMainMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
 	menu->addMenuItem(new MenuItem("Start New Game", new MenuActionReturnChoice(this, 11)));
 	menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionLoadGame(this, 1)));
-	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
+	menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
 	menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 12)));
 	return menu;
 }
@@ -156,7 +156,7 @@ BaseMenu *DuckmanMenuSystem::createSaveCompleteMenu() {
 BaseMenu *DuckmanMenuSystem::createDebugMenu() {
 	// TODO
 	BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
-	menu->addText("Debug Pause Menu");
+	menu->addText("Debug Menu");
 	menu->addText("-----------------");
 	return menu;
 }
@@ -167,6 +167,7 @@ BaseMenu *DuckmanMenuSystem::createDebugPauseMenu() {
 	menu->addText("-----------------");
 	menu->addMenuItem(new MenuItem("Return to Game", new MenuActionReturnChoice(this, 1)));
 	menu->addMenuItem(new MenuItem("Add/Remove Inventory", new MenuActionEnterMenu(this, kDuckmanAddRemoveInventoryMenu)));
+	// TODO quit to debug menu.
 	return menu;
 }
 
@@ -209,6 +210,7 @@ BaseMenu *DuckmanMenuSystem::createAddRemoveInventoryMenu() {
 	for(int i=0;i < 21;i++) {
 		menu->addMenuItem(new MenuItem(kDebugInventoryItems[i].name, new MenuActionInventoryAddRemove(this, _vm, i)));
 	}
+	menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
 	return menu;
 }
 
@@ -266,6 +268,10 @@ void DuckmanMenuSystem::setGameState(int gameState) {
 	_vm->_cursor._gameState = gameState;
 }
 
+void DuckmanMenuSystem::playSoundEffect(int sfxId) {
+	_vm->playSoundEffect(sfxId);
+}
+
 MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
 		: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex), _vm(vm) {
 }
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 787ced3..2921277 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -71,6 +71,7 @@ public://protected:
 	virtual int getGameState();
 	virtual void setGameState(int gameState);
 	virtual void setMenuCursorNum(int cursorNum);
+	virtual void playSoundEffect(int sfxId);
 };
 
 class MenuActionInventoryAddRemove : public BaseMenuAction {
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 3330cb7..e1aacf7 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -100,11 +100,11 @@ BaseMenuSystem::~BaseMenuSystem() {
 }
 
 void BaseMenuSystem::playSoundEffect13() {
-	// TODO
+	playSoundEffect(13);
 }
 
 void BaseMenuSystem::playSoundEffect14() {
-	// TODO
+	playSoundEffect(14);
 }
 
 void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) {
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index d9961f8..0a7f432 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -163,6 +163,7 @@ protected:
 	void hideActorTextColorRect();
 	
 	virtual BaseMenu *getMenuById(int menuId) = 0;
+	virtual void playSoundEffect(int sfxId) = 0;
 };
 
 /*


Commit: ced1ff2356391f24c70f9bb4c514733aeadfc38b
    https://github.com/scummvm/scummvm/commit/ced1ff2356391f24c70f9bb4c514733aeadfc38b
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Move sliders in option menu

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 8a9e1cc..554be9a 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -110,12 +110,26 @@ BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
 	menu->addText("              GAME OPTIONS             @@@@");
 	menu->addText("--------------------------------------");
-	menu->addMenuItem(new MenuItem("SFX Volume     @@{~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Music Volume  @@@{~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Speech Volume {~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Text Duration @@@{~~~~~~~~~~~~|~~~} @@@", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Restore Defaults                      @@", new MenuActionReturnChoice(this, 21)));
-	menu->addMenuItem(new MenuItem("Back                                       @@@", new MenuActionLeaveMenu(this)));
+	MenuActionUpdateSlider *action = new MenuActionUpdateSlider(this, menu);
+	MenuItem *menuItem = new MenuItem("SFX Volume     @@{~~~~~~~~~~~~|~~~} @@@", action);
+	action->setMenuItem(menuItem);
+	menu->addMenuItem(menuItem);
+
+	action = new MenuActionUpdateSlider(this, menu);
+	menuItem = new MenuItem("Music Volume  @@@{~~~~~~~~~~~~|~~~} @@@", action);
+	action->setMenuItem(menuItem);
+	menu->addMenuItem(menuItem);
+
+	action = new MenuActionUpdateSlider(this, menu);
+	menuItem = new MenuItem("Speech Volume {~~~~~~~~~~~~|~~~} @@@", action);
+	action->setMenuItem(menuItem);
+	menu->addMenuItem(menuItem);
+
+	action = new MenuActionUpdateSlider(this, menu);
+	menuItem = new MenuItem("Text Duration @@@{~~~~~~~~~~~~|~~~} @@@", action);
+	action->setMenuItem(menuItem);
+	menu->addMenuItem(menuItem);
+
 	return menu;
 }
 
@@ -290,4 +304,55 @@ void MenuActionInventoryAddRemove::execute() {
 	_menuSystem->leaveMenu();
 }
 
+MenuActionUpdateSlider::MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *menu)
+			: BaseMenuAction(menuSystem), menu(menu) {
+	menuItem = NULL;
+}
+
+void MenuActionUpdateSlider::execute() {
+	assert(menuItem);
+	Common::String text = menuItem->getText();
+	Common::Point point = menuItem->getMouseClickPoint();
+	int offset = 0;
+	_menuSystem->calcMenuItemTextPositionAtPoint(point, offset);
+	int newSliderValue = calcNewSliderValue(text, offset);
+
+	debug(0, "item text: %s, (%d, %d), New slider value: %d", text.c_str(), point.x, point.y, newSliderValue);
+
+	menuItem->setText(text);
+	_menuSystem->redrawMenuText(menu);
+	// TODO update slider here set value callback.
+}
+
+int MenuActionUpdateSlider::calcNewSliderValue(Common::String &text, int newOffset) {
+	int newSliderValue = 0;
+	int start = 0;
+	int end = 0;
+	int currentPosition = 0;
+	for(int i = 0; i < text.size(); i++) {
+		switch (text[i]) {
+			case '{' : start = i; break;
+			case '}' : end = i; break;
+			case '|' : currentPosition = i; break;
+			default: break;
+		}
+	}
+
+	if (newOffset >= start && newOffset <= end) {
+		if (newOffset == start) {
+			newSliderValue = 0;
+		} else if (newOffset == end) {
+			newSliderValue = 15;
+		} else {
+			newSliderValue = newOffset - (start + 1);
+		}
+
+		text.setChar('~', currentPosition);
+		text.setChar('|', start + 1 + newSliderValue);
+
+		return newSliderValue;
+	}
+	return currentPosition - start - 1;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 2921277..22bfefa 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -83,6 +83,20 @@ protected:
 	int _choiceIndex;
 };
 
+class MenuActionUpdateSlider : public BaseMenuAction {
+public:
+	MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu);
+	void setMenuItem(MenuItem *newMmenuItem) {
+		menuItem = newMmenuItem;
+	}
+
+	virtual void execute();
+protected:
+	MenuItem *menuItem;
+	BaseMenu *menu;
+	int calcNewSliderValue(Common::String &text, int newOffset);
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index e1aacf7..fbb52ee 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -44,7 +44,8 @@ MenuItem::~MenuItem() {
 	delete _action;
 }
 
-void MenuItem::executeAction() {
+void MenuItem::executeAction(const Common::Point &point) {
+	_mouseClickPoint = point;
 	_action->execute();
 }
 
@@ -371,7 +372,7 @@ void BaseMenuSystem::closeMenu() {
 }
 
 void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) {
-	debug(0, "BaseMenuSystem::handleClick() menuItemIndex: %d", menuItemIndex);
+	debug(0, "BaseMenuSystem::handleClick() menuItemIndex: %d click point: (%d, %d)", menuItemIndex, mousePos.x, mousePos.y);
 
 	if (menuItemIndex == 0) {
 	    playSoundEffect14();
@@ -379,7 +380,7 @@ void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mouseP
 	}
 
 	MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
-	menuItem->executeAction();
+	menuItem->executeAction(mousePos);
 	
 }
 
@@ -544,6 +545,38 @@ void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
 
 }
 
+void BaseMenuSystem::redrawMenuText(BaseMenu *menu) {
+	_vm->_screenText->removeText();
+	drawMenuText(menu);
+}
+
+bool BaseMenuSystem::calcMenuItemTextPositionAtPoint(Common::Point pt, int &offset) {
+	uint menuItemIndex;
+	if(!calcMenuItemIndexAtPoint(pt, menuItemIndex)) {
+		return false;
+	}
+
+	WRect rect;
+
+	MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
+	calcMenuItemRect(menuItemIndex, rect);
+	int x = pt.x - rect._topLeft.x;
+	Common::String text = menuItem->getText();
+	FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
+
+	uint curX = 0;
+	for (int i=0; i < text.size(); i++) {
+		int16 w = font->getCharInfo(text[i])->_width;
+		if (x >= curX && x <= curX + w) {
+			offset = i;
+			return true;
+		}
+		curX = curX + w;
+	}
+
+	return false;
+}
+
 // MenuTextBuilder
 
 MenuTextBuilder::MenuTextBuilder() : _pos(0) {
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index 0a7f432..cc76116 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -45,11 +45,14 @@ class MenuItem {
 public:
 	MenuItem(const Common::String text, BaseMenuAction *action);
 	~MenuItem();
-	void executeAction();
+	void executeAction(const Common::Point &point);
 	const Common::String& getText() const { return _text; }
+	void setText(const Common::String &text) { _text = text; }
+	const Common::Point& getMouseClickPoint() { return _mouseClickPoint; };
 protected:
 	Common::String _text;
 	BaseMenuAction *_action;
+	Common::Point _mouseClickPoint;
 };
 
 class BaseMenu {
@@ -98,12 +101,14 @@ public:
 	void closeMenu();
 	void handleClick(uint menuItemIndex, const Common::Point &mousePos);
 	uint drawMenuText(BaseMenu *menu);
+	void redrawMenuText(BaseMenu *menu);
 	void update(Control *cursorControl);
 	void setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex);
 	void setMenuCallerThreadId(uint32 menuCallerThreadId);
 	void setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset);
 	void setSavegameSlotNum(int slotNum);
 	void setSavegameDescription(Common::String desc);
+	bool calcMenuItemTextPositionAtPoint(Common::Point pt, int &offset);
 	virtual bool initMenuCursor() = 0;
 	virtual int getGameState() = 0;
 	virtual void setGameState(int gameState) = 0;


Commit: 0303b83ead46346cd74a9649384d8a2dde0e145b
    https://github.com/scummvm/scummvm/commit/0303b83ead46346cd74a9649384d8a2dde0e145b
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Pause actors when entering menu

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/menusystem.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 74d57d3..10f5744 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -1305,7 +1305,7 @@ bool Controls::getOverlappedObject(Control *control, Common::Point pt, Control *
 					foundControl = testControl;
 					foundPriority = testPriority;
 				}
-			}		
+			}
 		}
 	}
 
@@ -1504,4 +1504,49 @@ void Controls::destroyControlInternal(Control *control) {
 	delete control;
 }
 
+void Controls::disappearActors() {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_flags & 4 && control->_pauseCtr == 0) {
+			control->disappearActor();
+		}
+	}
+	Control *control = _vm->_dict->getObjectControl(0x40148);
+	if (control) {
+		control->disappearActor();
+	}
+}
+
+void Controls::appearActors() {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_flags & 4 && control->_pauseCtr == 0) {
+			control->appearActor();
+		}
+	}
+	Control *control = _vm->_dict->getObjectControl(0x40148);
+	if (control) {
+		control->appearActor();
+	}
+}
+
+void Controls::pauseActors(uint32 objectId) {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_actor && control->_objectId != objectId) {
+			control->_actor->pause();
+		}
+	}
+}
+
+void Controls::unpauseActors(uint32 objectId) {
+	for (ItemsIterator it = _controls.begin(); it != _controls.end(); ++it) {
+		Control *control = *it;
+		if (control->_actor && control->_objectId != objectId) {
+			control->_actor->unpause();
+		}
+	}
+}
+
+
 } // End of namespace Illusions
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 51f930b..5a4dd4f 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -276,6 +276,10 @@ public:
 	bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
 	void actorControlRoutine(Control *control, uint32 deltaTime);
 	void dialogItemControlRoutine(Control *control, uint32 deltaTime);
+	void disappearActors();
+	void appearActors();
+	void pauseActors(uint32 objectId);
+	void unpauseActors(uint32 objectId);
 public:
 	typedef Common::List<Control*> Items;
 	typedef Items::iterator ItemsIterator;
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index c427293..a93ec27 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -942,13 +942,13 @@ void IllusionsEngine_Duckman::pause(uint32 callerThreadId) {
 		_threads->pauseThreads(callerThreadId);
 		_camera->pause();
 		pauseFader();
-		// TODO largeObj_pauseControlActor(Illusions::CURSOR_OBJECT_ID);
+		_controls->pauseActors(Illusions::CURSOR_OBJECT_ID);
 	}
 }
 
 void IllusionsEngine_Duckman::unpause(uint32 callerThreadId) {
 	if (--_pauseCtr == 0) {
-		// TODO largeObj_unpauseControlActor(Illusions::CURSOR_OBJECT_ID);
+		_controls->unpauseActors(Illusions::CURSOR_OBJECT_ID);
 		unpauseFader();
 		_camera->unpause();
 		_threads->unpauseThreads(callerThreadId);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 554be9a..7cadd7e 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -107,7 +107,7 @@ BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
 }
 
 BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
-	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 6);
 	menu->addText("              GAME OPTIONS             @@@@");
 	menu->addText("--------------------------------------");
 	MenuActionUpdateSlider *action = new MenuActionUpdateSlider(this, menu);
@@ -130,6 +130,9 @@ BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
 	action->setMenuItem(menuItem);
 	menu->addMenuItem(menuItem);
 
+	menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionLeaveMenu(this)));
+
+	menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
 	return menu;
 }
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 9ac0b9a..66b45ad 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -350,11 +350,15 @@ void ScriptOpcodes_Duckman::opEnterDebugger(ScriptThread *scriptThread, OpCall &
 	// Used for debugging purposes in the original engine
 	// This is not supported and only reachable by code not implemented here!
 	//error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called");
+	_vm->_controls->disappearActors();
+	// TODO more logic needed here
 }
 
 void ScriptOpcodes_Duckman::opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall) {
 	// See opEnterDebugger
 	//error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called");
+	_vm->_controls->appearActors();
+	// TODO more logic needed here
 }
 
 void ScriptOpcodes_Duckman::opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index fbb52ee..9131d58 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -474,6 +474,7 @@ void BaseMenuSystem::update(Control *cursorControl) {
 		setMouseCursorToMenuItem(_hoveredMenuItemIndex);
 		_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
 		updateActorHoverBackground();
+		playSoundEffect(0xC);
 	} else if (_vm->_input->pollEvent(kEventDown)) {
 		// TODO handleDownKey();
 		if (_hoveredMenuItemIndex == _activeMenu->getMenuItemsCount()) {
@@ -484,6 +485,7 @@ void BaseMenuSystem::update(Control *cursorControl) {
 		setMouseCursorToMenuItem(_hoveredMenuItemIndex);
 		_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
 		updateActorHoverBackground();
+		playSoundEffect(0xC);
 	}
 	
 	updateTimeOut(resetTimeOut);


Commit: 09281b85f507a8859de3b03ac512e871d849611e
    https://github.com/scummvm/scummvm/commit/09281b85f507a8859de3b03ac512e871d849611e
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: loop music tracks

Changed paths:
    engines/illusions/sound.cpp


diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 307d40a..cf6e4f8 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -114,7 +114,7 @@ void MidiPlayer::play(const Common::String &filename) {
 		_parser->setTrack(0);
 		_parser->setMidiDriver(this);
 		_parser->setTimerRate(_driver->getBaseTempo());
-		_isLooping = false;
+		_isLooping = true;
 		_isPlaying = true;
 	}
 	fd->close();


Commit: 102dd462734f0baa67ae4f8439094f6aa50b42fa
    https://github.com/scummvm/scummvm/commit/102dd462734f0baa67ae4f8439094f6aa50b42fa
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Wire up audio sliders on option menu.
Set volumes based on scummvm config

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/sound.cpp
    engines/illusions/sound.h


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 7cadd7e..502a0ac 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -22,6 +22,7 @@
 
 #include "illusions/illusions.h"
 #include "illusions/actor.h"
+#include "illusions/sound.h"
 #include "illusions/duckman/illusions_duckman.h"
 #include "illusions/duckman/menusystem_duckman.h"
 #include "illusions/resources/scriptresource.h"
@@ -106,29 +107,34 @@ BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
 	return 0; // TODO
 }
 
+MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(const Common::String &text, SliderActionType type, BaseMenu *baseMenu) {
+	int sliderValue = 0;
+	Common::String sliderText = "{~~~~~~~~~~~~~~~~}";
+	switch (type) {
+		case SFX : sliderValue = _vm->_soundMan->getSfxVolume()/(255/15); break;
+		case MUSIC : sliderValue = _vm->_soundMan->getMusicVolume()/(255/15); break;
+		case VOICE : sliderValue = _vm->_soundMan->getSpeechVolume()/(255/15); break;
+		case TEXT_DURATION : sliderValue = 128/(255/15); break; // TODO wire up text duration config
+		default: break;
+	}
+
+	sliderText.setChar('|', sliderValue + 1);
+
+	MenuActionUpdateSlider *action = new MenuActionUpdateSlider(this, baseMenu, type, _vm);
+	MenuItem *menuItem = new MenuItem(text + sliderText, action);
+	action->setMenuItem(menuItem);
+	return menuItem;
+}
+
 BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 6);
 	menu->addText("              GAME OPTIONS             @@@@");
 	menu->addText("--------------------------------------");
-	MenuActionUpdateSlider *action = new MenuActionUpdateSlider(this, menu);
-	MenuItem *menuItem = new MenuItem("SFX Volume     @@{~~~~~~~~~~~~|~~~} @@@", action);
-	action->setMenuItem(menuItem);
-	menu->addMenuItem(menuItem);
 
-	action = new MenuActionUpdateSlider(this, menu);
-	menuItem = new MenuItem("Music Volume  @@@{~~~~~~~~~~~~|~~~} @@@", action);
-	action->setMenuItem(menuItem);
-	menu->addMenuItem(menuItem);
-
-	action = new MenuActionUpdateSlider(this, menu);
-	menuItem = new MenuItem("Speech Volume {~~~~~~~~~~~~|~~~} @@@", action);
-	action->setMenuItem(menuItem);
-	menu->addMenuItem(menuItem);
-
-	action = new MenuActionUpdateSlider(this, menu);
-	menuItem = new MenuItem("Text Duration @@@{~~~~~~~~~~~~|~~~} @@@", action);
-	action->setMenuItem(menuItem);
-	menu->addMenuItem(menuItem);
+	menu->addMenuItem(createOptionsSliderMenuItem("SFX Volume     @@", SFX, menu));
+	menu->addMenuItem(createOptionsSliderMenuItem("Music Volume  @@@", MUSIC, menu));
+	menu->addMenuItem(createOptionsSliderMenuItem("Speech Volume ", VOICE, menu));
+	menu->addMenuItem(createOptionsSliderMenuItem("Text Duration @@@", TEXT_DURATION, menu));
 
 	menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionLeaveMenu(this)));
 
@@ -289,7 +295,7 @@ void DuckmanMenuSystem::playSoundEffect(int sfxId) {
 	_vm->playSoundEffect(sfxId);
 }
 
-MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
+	MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
 		: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex), _vm(vm) {
 }
 
@@ -307,8 +313,8 @@ void MenuActionInventoryAddRemove::execute() {
 	_menuSystem->leaveMenu();
 }
 
-MenuActionUpdateSlider::MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *menu)
-			: BaseMenuAction(menuSystem), menu(menu) {
+MenuActionUpdateSlider::MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu, SliderActionType type, IllusionsEngine_Duckman *vm)
+			: BaseMenuAction(menuSystem), menu(baseMenu), _type(type), _vm(vm) {
 	menuItem = NULL;
 }
 
@@ -324,7 +330,14 @@ void MenuActionUpdateSlider::execute() {
 
 	menuItem->setText(text);
 	_menuSystem->redrawMenuText(menu);
-	// TODO update slider here set value callback.
+
+	switch(_type) {
+		case SFX : _vm->_soundMan->setSfxVolume(newSliderValue * (255/15)); break;
+		case MUSIC : _vm->_soundMan->setMusicVolume(newSliderValue * (255/15)); break;
+		case VOICE : _vm->_soundMan->setSpeechVolume(newSliderValue * (255/15)); break;
+		case TEXT_DURATION : break; // TODO
+		default: break;
+	}
 }
 
 int MenuActionUpdateSlider::calcNewSliderValue(Common::String &text, int newOffset) {
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 22bfefa..3ebab4d 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -27,7 +27,14 @@
 
 namespace Illusions {
 
-enum {
+	enum SliderActionType {
+		SFX,
+		MUSIC,
+		VOICE,
+		TEXT_DURATION
+	};
+
+	enum {
 	kDuckmanMainMenu,
 	kDuckmanLoadGameMenu,
 	kDuckmanOptionsMenu,
@@ -59,6 +66,7 @@ public://protected:
 	BaseMenu *createMainMenu();
 	BaseMenu *createLoadGameMenu();
 	BaseMenu *createOptionsMenu();
+	MenuItem *createOptionsSliderMenuItem(const Common::String &text, SliderActionType type, BaseMenu *baseMenu);
 	BaseMenu *createPauseMenu();
 	BaseMenu *createQueryRestartMenu();
 	BaseMenu *createQueryQuitMenu();
@@ -85,13 +93,15 @@ protected:
 
 class MenuActionUpdateSlider : public BaseMenuAction {
 public:
-	MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu);
+	MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu, SliderActionType type, IllusionsEngine_Duckman *vm);
 	void setMenuItem(MenuItem *newMmenuItem) {
 		menuItem = newMmenuItem;
 	}
 
 	virtual void execute();
 protected:
+	IllusionsEngine_Duckman *_vm;
+	SliderActionType _type;
 	MenuItem *menuItem;
 	BaseMenu *menu;
 	int calcNewSliderValue(Common::String &text, int newOffset);
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index cf6e4f8..bbcf8ad 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -20,6 +20,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "illusions/illusions.h"
 #include "illusions/sound.h"
 #include "audio/midiparser.h"
@@ -296,7 +297,7 @@ void SoundMan::stopCueingVoice() {
 }
 
 void SoundMan::startVoice(int16 volume, int16 pan) {
-	_voicePlayer->start(volume, pan);
+	_voicePlayer->start(calcAdjustedVolume("speech_volume", (uint8)volume), pan);
 }
 
 void SoundMan::stopVoice() {
@@ -323,7 +324,7 @@ void SoundMan::loadSound(uint32 soundEffectId, uint32 soundGroupId, bool looping
 void SoundMan::playSound(uint32 soundEffectId, int16 volume, int16 pan) {
 	Sound *sound = getSound(soundEffectId);
 	if (sound)
-		sound->play(volume, pan);
+		sound->play(calcAdjustedVolume("sfx_volume", (uint8)volume), pan);
 }
 
 void SoundMan::stopSound(uint32 soundEffectId) {
@@ -364,4 +365,34 @@ void SoundMan::fadeMidiMusic(int16 finalVolume, int16 duration) {
 	_midiPlayer->fade(finalVolume, duration);
 }
 
+void SoundMan::setMusicVolume(uint8 volume) {
+	ConfMan.setInt("music_volume", volume);
+	_midiPlayer->syncVolume();
+}
+
+void SoundMan::setSfxVolume(uint8 volume) {
+	ConfMan.setInt("sfx_volume", volume);
+}
+
+void SoundMan::setSpeechVolume(uint8 volume) {
+	ConfMan.setInt("speech_volume", volume);
+}
+
+uint8 SoundMan::calcAdjustedVolume(const Common::String &volumeConfigKey, uint8 volume) {
+	uint8 masterVolume = (uint8)ConfMan.getInt(volumeConfigKey);
+	return (uint8)(((float)masterVolume/255) * (float)volume);
+}
+
+uint8 SoundMan::getMusicVolume() {
+	return (uint8)ConfMan.getInt("music_volume");
+}
+
+uint8 SoundMan::getSfxVolume() {
+	return (uint8)ConfMan.getInt("sfx_volume");
+}
+
+uint8 SoundMan::getSpeechVolume() {
+	return (uint8)ConfMan.getInt("speech_volume");
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index d2da8ed..f0786b2 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -117,6 +117,14 @@ public:
 	void stopMidiMusic();
 	void fadeMidiMusic(int16 finalVolume, int16 duration);
 
+	uint8 getMusicVolume();
+	uint8 getSfxVolume();
+	uint8 getSpeechVolume();
+
+	void setMusicVolume(uint8 volume);
+	void setSfxVolume(uint8 volume);
+	void setSpeechVolume(uint8 volume);
+
 	bool cueVoice(const char *voiceName);
 	void stopCueingVoice();
 	void startVoice(int16 volume, int16 pan);
@@ -140,6 +148,7 @@ protected:
 	VoicePlayer *_voicePlayer;
 	SoundList _sounds;
 	Sound *getSound(uint32 soundEffectId);
+	uint8 calcAdjustedVolume(const Common::String &volumeConfigKey, uint8 volume);
 };
 
 } // End of namespace Illusions


Commit: 7e4a1c2bf11d18907d4a0a52a0f144eca6ee2bee
    https://github.com/scummvm/scummvm/commit/7e4a1c2bf11d18907d4a0a52a0f144eca6ee2bee
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add ability to restore options sliders to defaults

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/sound.cpp


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 502a0ac..b5af102 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -107,7 +107,7 @@ BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
 	return 0; // TODO
 }
 
-MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(const Common::String &text, SliderActionType type, BaseMenu *baseMenu) {
+MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(MenuActionUpdateSlider **action, const Common::String &text, SliderActionType type, BaseMenu *baseMenu) {
 	int sliderValue = 0;
 	Common::String sliderText = "{~~~~~~~~~~~~~~~~}";
 	switch (type) {
@@ -120,9 +120,9 @@ MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(const Common::String &t
 
 	sliderText.setChar('|', sliderValue + 1);
 
-	MenuActionUpdateSlider *action = new MenuActionUpdateSlider(this, baseMenu, type, _vm);
-	MenuItem *menuItem = new MenuItem(text + sliderText, action);
-	action->setMenuItem(menuItem);
+	*action = new MenuActionUpdateSlider(this, baseMenu, type, _vm);
+	MenuItem *menuItem = new MenuItem(text + sliderText, *action);
+	(*action)->setMenuItem(menuItem);
 	return menuItem;
 }
 
@@ -131,12 +131,17 @@ BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
 	menu->addText("              GAME OPTIONS             @@@@");
 	menu->addText("--------------------------------------");
 
-	menu->addMenuItem(createOptionsSliderMenuItem("SFX Volume     @@", SFX, menu));
-	menu->addMenuItem(createOptionsSliderMenuItem("Music Volume  @@@", MUSIC, menu));
-	menu->addMenuItem(createOptionsSliderMenuItem("Speech Volume ", VOICE, menu));
-	menu->addMenuItem(createOptionsSliderMenuItem("Text Duration @@@", TEXT_DURATION, menu));
+	MenuActionUpdateSlider *sfxSlider;
+	MenuActionUpdateSlider *musicSlider;
+	MenuActionUpdateSlider *speechSlider;
+	MenuActionUpdateSlider *textDurationSlider;
 
-	menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionLeaveMenu(this)));
+	menu->addMenuItem(createOptionsSliderMenuItem(&sfxSlider, "SFX Volume     @@", SFX, menu));
+	menu->addMenuItem(createOptionsSliderMenuItem(&musicSlider, "Music Volume  @@@", MUSIC, menu));
+	menu->addMenuItem(createOptionsSliderMenuItem(&speechSlider, "Speech Volume ", VOICE, menu));
+	menu->addMenuItem(createOptionsSliderMenuItem(&textDurationSlider, "Text Duration @@@", TEXT_DURATION, menu));
+
+	menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionResetOptionSliders(this, sfxSlider, musicSlider, speechSlider, textDurationSlider)));
 
 	menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
 	return menu;
@@ -315,32 +320,24 @@ void MenuActionInventoryAddRemove::execute() {
 
 MenuActionUpdateSlider::MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu, SliderActionType type, IllusionsEngine_Duckman *vm)
 			: BaseMenuAction(menuSystem), menu(baseMenu), _type(type), _vm(vm) {
-	menuItem = NULL;
+	_menuItem = NULL;
 }
 
 void MenuActionUpdateSlider::execute() {
-	assert(menuItem);
-	Common::String text = menuItem->getText();
-	Common::Point point = menuItem->getMouseClickPoint();
+	assert(_menuItem);
+	Common::String text = _menuItem->getText();
+	Common::Point point = _menuItem->getMouseClickPoint();
 	int offset = 0;
 	_menuSystem->calcMenuItemTextPositionAtPoint(point, offset);
-	int newSliderValue = calcNewSliderValue(text, offset);
+	int newSliderValue = calcNewSliderValue(offset);
 
 	debug(0, "item text: %s, (%d, %d), New slider value: %d", text.c_str(), point.x, point.y, newSliderValue);
 
-	menuItem->setText(text);
-	_menuSystem->redrawMenuText(menu);
-
-	switch(_type) {
-		case SFX : _vm->_soundMan->setSfxVolume(newSliderValue * (255/15)); break;
-		case MUSIC : _vm->_soundMan->setMusicVolume(newSliderValue * (255/15)); break;
-		case VOICE : _vm->_soundMan->setSpeechVolume(newSliderValue * (255/15)); break;
-		case TEXT_DURATION : break; // TODO
-		default: break;
-	}
+	setSliderValue(newSliderValue);
 }
 
-int MenuActionUpdateSlider::calcNewSliderValue(Common::String &text, int newOffset) {
+int MenuActionUpdateSlider::calcNewSliderValue(int newOffset) {
+	Common::String text = _menuItem->getText();
 	int newSliderValue = 0;
 	int start = 0;
 	int end = 0;
@@ -362,13 +359,51 @@ int MenuActionUpdateSlider::calcNewSliderValue(Common::String &text, int newOffs
 		} else {
 			newSliderValue = newOffset - (start + 1);
 		}
-
-		text.setChar('~', currentPosition);
-		text.setChar('|', start + 1 + newSliderValue);
-
 		return newSliderValue;
 	}
 	return currentPosition - start - 1;
 }
 
+void MenuActionUpdateSlider::setSliderValue(uint8 newValue) {
+	int start = 0;
+	Common::String text = _menuItem->getText();
+	for(int i = 0; i < text.size(); i++) {
+		switch (text[i]) {
+			case '{' : start = i; break;
+			case '|' : text.setChar('~', i); break;
+			default: break;
+		}
+	}
+
+	text.setChar('|', start + newValue + 1);
+
+	_menuItem->setText(text);
+	_menuSystem->redrawMenuText(menu);
+
+	switch(_type) {
+		case SFX : _vm->_soundMan->setSfxVolume(newValue * (255/15)); break;
+		case MUSIC : _vm->_soundMan->setMusicVolume(newValue * (255/15)); break;
+		case VOICE : _vm->_soundMan->setSpeechVolume(newValue * (255/15)); break;
+		case TEXT_DURATION : break; // TODO
+		default: break;
+	}
+}
+
+MenuActionResetOptionSliders::MenuActionResetOptionSliders(BaseMenuSystem *menuSystem,
+														   MenuActionUpdateSlider *sfxSlider,
+														   MenuActionUpdateSlider *musiclider,
+														   MenuActionUpdateSlider *speechSlider,
+														   MenuActionUpdateSlider *textDurationSlider)
+: BaseMenuAction(menuSystem), _sfxSlider(sfxSlider), _musiclider(musiclider),
+  _speechSlider(speechSlider), _textDurationSlider(textDurationSlider) {
+
+}
+
+void MenuActionResetOptionSliders::execute() {
+	_sfxSlider->setSliderValue(11);
+	_musiclider->setSliderValue(11);
+	_speechSlider->setSliderValue(15);
+	_textDurationSlider->setSliderValue(0);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 3ebab4d..5b3cd55 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -49,8 +49,9 @@ namespace Illusions {
 };
 
 class IllusionsEngine_Duckman;
+class MenuActionUpdateSlider;
 
-class DuckmanMenuSystem : public BaseMenuSystem {
+	class DuckmanMenuSystem : public BaseMenuSystem {
 public:
 	DuckmanMenuSystem(IllusionsEngine_Duckman *vm);
 	~DuckmanMenuSystem();
@@ -66,7 +67,6 @@ public://protected:
 	BaseMenu *createMainMenu();
 	BaseMenu *createLoadGameMenu();
 	BaseMenu *createOptionsMenu();
-	MenuItem *createOptionsSliderMenuItem(const Common::String &text, SliderActionType type, BaseMenu *baseMenu);
 	BaseMenu *createPauseMenu();
 	BaseMenu *createQueryRestartMenu();
 	BaseMenu *createQueryQuitMenu();
@@ -80,7 +80,10 @@ public://protected:
 	virtual void setGameState(int gameState);
 	virtual void setMenuCursorNum(int cursorNum);
 	virtual void playSoundEffect(int sfxId);
-};
+private:
+	MenuItem *createOptionsSliderMenuItem(MenuActionUpdateSlider **action, const Common::String &text,
+										  SliderActionType type, BaseMenu *baseMenu);
+	};
 
 class MenuActionInventoryAddRemove : public BaseMenuAction {
 public:
@@ -94,17 +97,34 @@ protected:
 class MenuActionUpdateSlider : public BaseMenuAction {
 public:
 	MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu, SliderActionType type, IllusionsEngine_Duckman *vm);
-	void setMenuItem(MenuItem *newMmenuItem) {
-		menuItem = newMmenuItem;
+	void setMenuItem(MenuItem *menuItem) {
+		_menuItem = menuItem;
 	}
 
 	virtual void execute();
+	void setSliderValue(uint8 newValue);
 protected:
 	IllusionsEngine_Duckman *_vm;
 	SliderActionType _type;
-	MenuItem *menuItem;
+	MenuItem *_menuItem;
 	BaseMenu *menu;
-	int calcNewSliderValue(Common::String &text, int newOffset);
+	int calcNewSliderValue(int newOffset);
+};
+
+class MenuActionResetOptionSliders : public BaseMenuAction {
+public:
+	MenuActionResetOptionSliders(BaseMenuSystem *menuSystem,
+								 MenuActionUpdateSlider *sfxSlider,
+								 MenuActionUpdateSlider *musiclider,
+								 MenuActionUpdateSlider *speechSlider,
+								 MenuActionUpdateSlider *textDurationSlider
+	);
+	virtual void execute();
+protected:
+	MenuActionUpdateSlider *_sfxSlider;
+	MenuActionUpdateSlider *_musiclider;
+	MenuActionUpdateSlider *_speechSlider;
+	MenuActionUpdateSlider *_textDurationSlider;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index bbcf8ad..55bbf86 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -368,14 +368,17 @@ void SoundMan::fadeMidiMusic(int16 finalVolume, int16 duration) {
 void SoundMan::setMusicVolume(uint8 volume) {
 	ConfMan.setInt("music_volume", volume);
 	_midiPlayer->syncVolume();
+	ConfMan.flushToDisk();
 }
 
 void SoundMan::setSfxVolume(uint8 volume) {
 	ConfMan.setInt("sfx_volume", volume);
+	ConfMan.flushToDisk();
 }
 
 void SoundMan::setSpeechVolume(uint8 volume) {
 	ConfMan.setInt("speech_volume", volume);
+	ConfMan.flushToDisk();
 }
 
 uint8 SoundMan::calcAdjustedVolume(const Common::String &volumeConfigKey, uint8 volume) {


Commit: dc9dc0324e2e009092f6c042cd7351362f076c1d
    https://github.com/scummvm/scummvm/commit/dc9dc0324e2e009092f6c042cd7351362f076c1d
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Wire up subtitle text duration config slider

Changed paths:
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/sound.cpp
    engines/illusions/sound.h
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index a642835..f3845a6 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -185,8 +185,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_pauseCtr = 0;
 	_field8 = 1;
 	_fieldA = 0;
-	_fieldE = 240;
-	
+	ConfMan.registerDefault("talkspeed", 240);
+	_subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
+
 	_globalSceneId = 0x00010003;	
 	
 	setDefaultTextCoords();
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index a93ec27..6ce29de 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -142,8 +142,11 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_doScriptThreadInit = false;
 	_field8 = 1;
 	_fieldA = 0;
-	_fieldE = 240;
-	
+
+	ConfMan.registerDefault("talkspeed", 240);
+	_subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
+	debug(0, "talkspeed: %d", _subtitleDuration);
+
 	_globalSceneId = 0x00010003;
 
 	_savedInventoryActorIndex = 0;
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index b5af102..f7f93ce 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -111,10 +111,10 @@ MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(MenuActionUpdateSlider
 	int sliderValue = 0;
 	Common::String sliderText = "{~~~~~~~~~~~~~~~~}";
 	switch (type) {
-		case SFX : sliderValue = _vm->_soundMan->getSfxVolume()/(255/15); break;
-		case MUSIC : sliderValue = _vm->_soundMan->getMusicVolume()/(255/15); break;
-		case VOICE : sliderValue = _vm->_soundMan->getSpeechVolume()/(255/15); break;
-		case TEXT_DURATION : sliderValue = 128/(255/15); break; // TODO wire up text duration config
+		case SFX : sliderValue = _vm->_soundMan->getSfxVolume()/(256/15); break;
+		case MUSIC : sliderValue = _vm->_soundMan->getMusicVolume()/(256/15); break;
+		case VOICE : sliderValue = _vm->_soundMan->getSpeechVolume()/(256/15); break;
+		case TEXT_DURATION : sliderValue = _vm->getSubtitleDuration()/(256/15); break;
 		default: break;
 	}
 
@@ -381,10 +381,10 @@ void MenuActionUpdateSlider::setSliderValue(uint8 newValue) {
 	_menuSystem->redrawMenuText(menu);
 
 	switch(_type) {
-		case SFX : _vm->_soundMan->setSfxVolume(newValue * (255/15)); break;
-		case MUSIC : _vm->_soundMan->setMusicVolume(newValue * (255/15)); break;
-		case VOICE : _vm->_soundMan->setSpeechVolume(newValue * (255/15)); break;
-		case TEXT_DURATION : break; // TODO
+		case SFX : _vm->_soundMan->setSfxVolume(newValue * (256/15)); break;
+		case MUSIC : _vm->_soundMan->setMusicVolume(newValue * (256/15)); break;
+		case VOICE : _vm->_soundMan->setSpeechVolume(newValue * (256/15)); break;
+		case TEXT_DURATION : _vm->setSubtitleDuration(newValue * (256/15)); break;
 		default: break;
 	}
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 8388110..7c3e62b 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -391,4 +391,14 @@ FramesList *IllusionsEngine::findActorSequenceFrames(Sequence *sequence) {
 	return _actorInstances->findSequenceFrames(sequence);
 }
 
+void IllusionsEngine::setSubtitleDuration(uint16 duration) {
+	_subtitleDuration = duration;
+	ConfMan.setInt("talkspeed", _subtitleDuration);
+	ConfMan.flushToDisk();
+}
+
+uint16 IllusionsEngine::getSubtitleDuration() {
+	return (uint16)_subtitleDuration;
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 12bd284..63983e1 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -145,7 +145,8 @@ public:
 
 	uint32 _fontId;
 	int _field8;
-	uint32 _fieldA, _fieldE;
+	uint32 _fieldA;
+	uint32 _subtitleDuration;
 
 	WidthHeight _defaultTextDimensions;
 	Common::Point _defaultTextPosition;
@@ -184,6 +185,9 @@ public:
 	void getDefaultTextPosition(Common::Point &position);
 	void setDefaultTextPosition(Common::Point &position);
 
+	uint16 getSubtitleDuration();
+	void setSubtitleDuration(uint16 duration);
+
 	FramesList *findActorSequenceFrames(Sequence *sequence);
 
 	virtual void setDefaultTextCoords() = 0;
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 55bbf86..a40c43f 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -365,37 +365,37 @@ void SoundMan::fadeMidiMusic(int16 finalVolume, int16 duration) {
 	_midiPlayer->fade(finalVolume, duration);
 }
 
-void SoundMan::setMusicVolume(uint8 volume) {
+void SoundMan::setMusicVolume(uint16 volume) {
 	ConfMan.setInt("music_volume", volume);
 	_midiPlayer->syncVolume();
 	ConfMan.flushToDisk();
 }
 
-void SoundMan::setSfxVolume(uint8 volume) {
+void SoundMan::setSfxVolume(uint16 volume) {
 	ConfMan.setInt("sfx_volume", volume);
 	ConfMan.flushToDisk();
 }
 
-void SoundMan::setSpeechVolume(uint8 volume) {
+void SoundMan::setSpeechVolume(uint16 volume) {
 	ConfMan.setInt("speech_volume", volume);
 	ConfMan.flushToDisk();
 }
 
-uint8 SoundMan::calcAdjustedVolume(const Common::String &volumeConfigKey, uint8 volume) {
-	uint8 masterVolume = (uint8)ConfMan.getInt(volumeConfigKey);
-	return (uint8)(((float)masterVolume/255) * (float)volume);
+uint16 SoundMan::calcAdjustedVolume(const Common::String &volumeConfigKey, uint16 volume) {
+	uint16 masterVolume = (uint16)ConfMan.getInt(volumeConfigKey);
+	return (uint16)(((float)masterVolume/256) * (float)volume);
 }
 
-uint8 SoundMan::getMusicVolume() {
-	return (uint8)ConfMan.getInt("music_volume");
+uint16 SoundMan::getMusicVolume() {
+	return (uint16)ConfMan.getInt("music_volume");
 }
 
-uint8 SoundMan::getSfxVolume() {
-	return (uint8)ConfMan.getInt("sfx_volume");
+uint16 SoundMan::getSfxVolume() {
+	return (uint16)ConfMan.getInt("sfx_volume");
 }
 
-uint8 SoundMan::getSpeechVolume() {
-	return (uint8)ConfMan.getInt("speech_volume");
+uint16 SoundMan::getSpeechVolume() {
+	return (uint16)ConfMan.getInt("speech_volume");
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index f0786b2..75464e8 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -117,13 +117,13 @@ public:
 	void stopMidiMusic();
 	void fadeMidiMusic(int16 finalVolume, int16 duration);
 
-	uint8 getMusicVolume();
-	uint8 getSfxVolume();
-	uint8 getSpeechVolume();
+	uint16 getMusicVolume();
+	uint16 getSfxVolume();
+	uint16 getSpeechVolume();
 
-	void setMusicVolume(uint8 volume);
-	void setSfxVolume(uint8 volume);
-	void setSpeechVolume(uint8 volume);
+	void setMusicVolume(uint16 volume);
+	void setSfxVolume(uint16 volume);
+	void setSpeechVolume(uint16 volume);
 
 	bool cueVoice(const char *voiceName);
 	void stopCueingVoice();
@@ -148,7 +148,7 @@ protected:
 	VoicePlayer *_voicePlayer;
 	SoundList _sounds;
 	Sound *getSound(uint32 soundEffectId);
-	uint8 calcAdjustedVolume(const Common::String &volumeConfigKey, uint8 volume);
+	uint16 calcAdjustedVolume(const Common::String &volumeConfigKey, uint16 volume);
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 5b54abc..9e94ef6 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -60,7 +60,7 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 	
 	_flags = 0x0E;
 	
-	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
+	_durationMult = _vm->clipTextDuration(_vm->getSubtitleDuration());
 	_textDuration = _durationMult;
 	_defDurationMult = _vm->clipTextDuration(240);
 	_textStartTime = 0;
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 2d6c455..42d9747 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -56,7 +56,7 @@ TalkThread_Duckman::TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threa
 	else
 		_status = 2;
 		
-	_durationMult = _vm->clipTextDuration(_vm->_fieldE);
+	_durationMult = _vm->clipTextDuration(_vm->_subtitleDuration);
 	_textDuration = _durationMult;
 	_defDurationMult = _vm->clipTextDuration(240);
 	


Commit: dc79026a31bde8b46463d0c5a1d62496fc0963ed
    https://github.com/scummvm/scummvm/commit/dc79026a31bde8b46463d0c5a1d62496fc0963ed
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Enable illusions engine by default
Add credits info for illusions engine

Changed paths:
    devtools/credits.pl
    engines/illusions/configure.engine
    gui/credits.h


diff --git a/devtools/credits.pl b/devtools/credits.pl
index 8acf625..484c7be 100755
--- a/devtools/credits.pl
+++ b/devtools/credits.pl
@@ -625,6 +625,11 @@ begin_credits("Credits");
 				add_person("Eugene Sandulenko", "sev", "");
 			end_section();
 
+			begin_section("Illusions");
+				add_person("Benjamin Haisch", "john_doe", "");
+				add_person("Eric Fry", "yuv422", "");
+			end_section();
+
 			begin_section("Kyra");
 				add_person("Torbjörn Andersson", "eriktorbjorn", "VQA Player");
 				add_person("Oystein Eftevaag", "vinterstum", "");
diff --git a/engines/illusions/configure.engine b/engines/illusions/configure.engine
index c037c53..2b262a0 100644
--- a/engines/illusions/configure.engine
+++ b/engines/illusions/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine illusions "Illusions Engine" no
+add_engine illusions "Illusions Engine" yes
diff --git a/gui/credits.h b/gui/credits.h
index 649e73d..5281726 100644
--- a/gui/credits.h
+++ b/gui/credits.h
@@ -173,6 +173,10 @@ static const char *credits[] = {
 "C0""Oystein Eftevaag",
 "C0""Eugene Sandulenko",
 "",
+"C1""Illusions",
+"C0""Benjamin Haisch",
+"C0""Eric Fry",
+"",
 "C1""Kyra",
 "A0""Torbjorn Andersson",
 "C0""Torbj\366rn Andersson",


Commit: a5319cbce6669fbadae98f81142b8189ecd4bb27
    https://github.com/scummvm/scummvm/commit/a5319cbce6669fbadae98f81142b8189ecd4bb27
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Wire up restart savegame menu item.

Changed paths:
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/saveload.cpp


diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index f7f93ce..50ce932 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -161,7 +161,12 @@ BaseMenu *DuckmanMenuSystem::createPauseMenu() {
 }
 
 BaseMenu *DuckmanMenuSystem::createQueryRestartMenu() {
-	return 0; // TODO
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 2);
+	menu->addText("Do you really want to restart?");
+	menu->addText("-----------------------------------");
+	menu->addMenuItem(new MenuItem("Yes, let's try again", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex())));
+	menu->addMenuItem(new MenuItem("No, just kidding", new MenuActionLeaveMenu(this)));
+	return menu;
 }
 
 BaseMenu *DuckmanMenuSystem::createQueryQuitMenu() {
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 66b45ad..81c0c41 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -683,7 +683,7 @@ void ScriptOpcodes_Duckman::opQuitGame(ScriptThread *scriptThread, OpCall &opCal
 void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->reset();
 	_vm->_input->activateButton(0xFFFF);
-	// TODO _vm->stopMusic();
+	_vm->_soundMan->stopMidiMusic();
 	// TODO _vm->_gameStates->clear();
 }
 
diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp
index f3fae99..50a728a 100644
--- a/engines/illusions/saveload.cpp
+++ b/engines/illusions/saveload.cpp
@@ -131,6 +131,7 @@ Common::Error IllusionsEngine::loadGameState(int slot) {
 	if (!loadgame(fileName))
 		return Common::kReadingFailed;
 	_resumeFromSavegameRequested = true;
+	_savegameSlotNum = slot;
 	return Common::kNoError;
 }
 


Commit: 284631140211c27f15557e74fb96c85e19d96278
    https://github.com/scummvm/scummvm/commit/284631140211c27f15557e74fb96c85e19d96278
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Add support for German version.

Changed paths:
    engines/illusions/detection.cpp


diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index db6090b..ca002ae 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -67,6 +67,19 @@ static const IllusionsGameDescription gameDescriptions[] = {
 		kGameIdDuckman
 	},
 
+	{
+		{
+			"duckman",
+			0,
+			AD_ENTRY1s("duckman.gam", "64d16922ffb46b746fc2c12a14d75bcc", 29779968),
+			Common::DE_DEU,
+			Common::kPlatformWindows,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+		kGameIdDuckman
+	},
+
 	{AD_TABLE_END_MARKER, 0}
 };
 


Commit: 92e74327e6cd92b9764c851f99f02a9fa238051f
    https://github.com/scummvm/scummvm/commit/92e74327e6cd92b9764c851f99f02a9fa238051f
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Initialise save slot variable

Changed paths:
    engines/illusions/illusions.cpp


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 7c3e62b..0178740 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -83,6 +83,7 @@ IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *
 	
 	_isSaveAllowed = true; // TODO
 	_resumeFromSavegameRequested = false;
+	_savegameSlotNum = -1;
 	_savegameSceneId = 0;
 	_savegameThreadId = 0;
 	_nextTempThreadId = 0;


Commit: d77dd6c14ab6c41513ed282597a6da82289061e8
    https://github.com/scummvm/scummvm/commit/d77dd6c14ab6c41513ed282597a6da82289061e8
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Pause voice when entering in-game menu
Wire up load fail dialog when trying to restart from unsaved new game.

Changed paths:
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/sound.cpp
    engines/illusions/sound.h
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 6ce29de..38d3bab 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -1238,6 +1238,10 @@ void IllusionsEngine_Duckman::playTriggerCauseSound(uint32 verbId, uint32 object
 }
 
 bool IllusionsEngine_Duckman::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
+	if (_savegameSlotNum < 0) {
+		return false; // TODO need to handle reset from new game (without exising savegame).
+	}
+
 	const char *fileName = getSavegameFilename(_savegameSlotNum);
 	bool success = loadgame(fileName);
 	if (success)
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 50ce932..c773afc 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -87,8 +87,10 @@ BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
 		return createOptionsMenu();
 	case kDuckmanDebugPauseMenu:
 		return createDebugPauseMenu();
-		case kDuckmanAddRemoveInventoryMenu:
-			return createAddRemoveInventoryMenu();
+	case kDuckmanAddRemoveInventoryMenu:
+		return createAddRemoveInventoryMenu();
+	case kDuckmanLoadGameFailedMenu:
+		return createLoadGameFailedMenu();
 	default:
 		error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId);
 	}
@@ -107,6 +109,15 @@ BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
 	return 0; // TODO
 }
 
+BaseMenu *DuckmanMenuSystem::createLoadGameFailedMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
+	menu->addText("Load Game Failed");
+	menu->addText("-------------------");
+	menu->addMenuItem(new MenuItem("Continue", new MenuActionReturnChoice(this, 1)));
+	return menu;
+}
+
+
 MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(MenuActionUpdateSlider **action, const Common::String &text, SliderActionType type, BaseMenu *baseMenu) {
 	int sliderValue = 0;
 	Common::String sliderText = "{~~~~~~~~~~~~~~~~}";
@@ -261,8 +272,10 @@ int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
 		return kDuckmanSaveCompleteMenu;
 	/*
 	case 0x180006: // save game failed menu
-	case 0x180007: // load game failed menu
 	*/
+	case 0x180007: // load game failed menu
+		return kDuckmanLoadGameFailedMenu;
+
 	/* TODO CHECKME Another pause menu?
 	case 0x180008:
 		menuData = &g_menuDataPause;
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index 5b3cd55..edf8620 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -37,6 +37,7 @@ namespace Illusions {
 	enum {
 	kDuckmanMainMenu,
 	kDuckmanLoadGameMenu,
+	kDuckmanLoadGameFailedMenu,
 	kDuckmanOptionsMenu,
 	kDuckmanPauseMenu,
 	kDuckmanDebugMenu,
@@ -66,6 +67,7 @@ public://protected:
 	BaseMenu *createMenuById(int menuId);
 	BaseMenu *createMainMenu();
 	BaseMenu *createLoadGameMenu();
+	BaseMenu *createLoadGameFailedMenu();
 	BaseMenu *createOptionsMenu();
 	BaseMenu *createPauseMenu();
 	BaseMenu *createQueryRestartMenu();
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 81c0c41..1876b13 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -672,7 +672,6 @@ void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &op
 }
 
 void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-	//_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game"
 	opCall._deltaOfs += _vm->_menuChoiceOfs;
 }
 
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index a40c43f..bc65e37 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -211,6 +211,12 @@ bool VoicePlayer::isCued() {
 	return _voiceStatus == 2;
 }
 
+void VoicePlayer::pause(bool paused) {
+	if(isPlaying()) {
+		g_system->getMixer()->pauseHandle(_soundHandle, paused);
+	}
+}
+
 // Sound
 
 Sound::Sound(uint32 soundEffectId, uint32 soundGroupId, bool looping)
@@ -304,6 +310,10 @@ void SoundMan::stopVoice() {
 	_voicePlayer->stop();
 }
 
+void SoundMan::pauseVoice(bool paused) {
+	_voicePlayer->pause(paused);
+}
+
 bool SoundMan::isVoicePlaying() {
 	return _voicePlayer->isPlaying();
 }
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index 75464e8..f6a96d5 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -76,6 +76,7 @@ public:
 	bool cue(const char *voiceName);
 	void stopCueing();
 	void start(int16 volume, int16 pan);
+	void pause(bool paused);
 	void stop();
 	bool isPlaying();
 	bool isEnabled();
@@ -129,6 +130,7 @@ public:
 	void stopCueingVoice();
 	void startVoice(int16 volume, int16 pan);
 	void stopVoice();
+	void pauseVoice(bool paused);
 	bool isVoicePlaying();
 	bool isVoiceEnabled();
 	bool isVoiceCued();
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 42d9747..fdfcc0a 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -206,7 +206,7 @@ int TalkThread_Duckman::onUpdate() {
 void TalkThread_Duckman::onPause() {
 	if (_status == 5) {
 		if (!(_flags & 4)) {
-			// TODO audvocPauseVoice();
+			_vm->_soundMan->pauseVoice(true);
 		}
 		if (!(_flags & 8))
 			_textDurationElapsed = getDurationElapsed(_textStartTime, _textEndTime);
@@ -220,7 +220,7 @@ void TalkThread_Duckman::onUnpause() {
 			_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName);
 	} else if (_status == 5) {
 		if (!(_flags & 4)) {
-			// TODO audvocUnpauseVoice();
+			_vm->_soundMan->pauseVoice(false);
 		}
 		if (!(_flags & 8)) {
 			_textStartTime = getCurrentTime();


Commit: 3d9f5ed20ff182ee0cbb621f2bcf0b98a44e384d
    https://github.com/scummvm/scummvm/commit/3d9f5ed20ff182ee0cbb621f2bcf0b98a44e384d
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Replace spaces with tabs
Replace while(1) with do..while loop
Removed dead code
Renamed FP16 to FixedPoint16 to improve readability

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_credits.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/camera.cpp
    engines/illusions/dictionary.h
    engines/illusions/duckman/duckman_credits.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/fixedpoint.cpp
    engines/illusions/fixedpoint.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h
    engines/illusions/resources/actorresource.h
    engines/illusions/resources/midiresource.cpp
    engines/illusions/resources/scriptresource.h
    engines/illusions/resources/soundresource.h


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 10f5744..7cbada1 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -771,9 +771,6 @@ PointArray *Control::createPath(Common::Point destPt) {
 	PathFinder pathFinder;
 	WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
 	PointArray *path = pathFinder.findPath(_vm->_camera, _actor->_position, destPt, walkPoints, walkRects, bgDimensions);
-	for (uint i = 0; i < path->size(); ++i) {
-		//debug(0, "Path(%d) (%d, %d)", i, (*path)[i].x, (*path)[i].y);
-	}
 	return path;
 }
 
@@ -784,143 +781,140 @@ void Control::updateActorMovement(uint32 deltaTime) {
 	static const int16 kAngleTbl[] = {60, 0, 120, 0, 60, 0, 120, 0};
 	bool fastWalked = false;
 
-	while (1) {
+	do {
 
-	if (!fastWalked && _vm->testMainActorFastWalk(this)) {
-		fastWalked = true;
-		disappearActor();
-		_actor->_flags |= Illusions::ACTOR_FLAG_8000;
-		_actor->_seqCodeIp = 0;
-		deltaTime = 2;
-	}
+		if (!fastWalked && _vm->testMainActorFastWalk(this)) {
+			fastWalked = true;
+			disappearActor();
+			_actor->_flags |= Illusions::ACTOR_FLAG_8000;
+			_actor->_seqCodeIp = 0;
+			deltaTime = 2;
+		}
 
-	if (_vm->testMainActorCollision(this))
-		break;
+		if (_vm->testMainActorCollision(this))
+			break;
 
-	Common::Point prevPt;
-	if (_actor->_pathPointIndex == 0) {
-		if (_actor->_pathInitialPosFlag) {
-			_actor->_pathCtrX = 0;
-			_actor->_pathInitialPos = _actor->_position;
-			_actor->_pathInitialPosFlag = false;
+		Common::Point prevPt;
+		if (_actor->_pathPointIndex == 0) {
+			if (_actor->_pathInitialPosFlag) {
+				_actor->_pathCtrX = 0;
+				_actor->_pathInitialPos = _actor->_position;
+				_actor->_pathInitialPosFlag = false;
+			}
+			prevPt = _actor->_pathInitialPos;
+		} else {
+			prevPt = (*_actor->_pathNode)[_actor->_pathPointIndex - 1];
 		}
-		prevPt = _actor->_pathInitialPos;
-	} else {
-		prevPt = (*_actor->_pathNode)[_actor->_pathPointIndex - 1];
-	}
 
-	Common::Point currPt = (*_actor->_pathNode)[_actor->_pathPointIndex];
+		Common::Point currPt = (*_actor->_pathNode)[_actor->_pathPointIndex];
 
-	int16 deltaX = currPt.x - prevPt.x;
-	int16 deltaY = currPt.y - prevPt.y;
+		int16 deltaX = currPt.x - prevPt.x;
+		int16 deltaY = currPt.y - prevPt.y;
 
-	if (!_actor->_pathFlag50) {
+		if (!_actor->_pathFlag50) {
 
-		// TODO Move to own function
-		FP16 angle;
-		if (currPt.x == prevPt.x) {
-			if (prevPt.y >= currPt.y)
-				angle = fixedMul(-0x5A0000, 0x478);
-			else
-				angle = fixedMul(0x5A0000, 0x478);
-		} else {
-			angle = fixedAtan(fixedDiv(deltaY << 16, deltaX << 16));
-		}
-		_actor->_pathAngle = angle;
-
-		// TODO Move to own function
-		int16 v13 = (fixedTrunc(fixedMul(angle, 0x394BB8)) + 360) % 360;
-		if (deltaX >= 0)
-			v13 += 180;
-		v13 = (v13 + 90) % 360;
-		int16 v15 = kAngleTbl[0] / -2;
-		uint newFacing = 1;
-		for (uint i = 0; i < 8; ++i) {
-			v15 += kAngleTbl[i];
-			if (v13 < v15) {
-				newFacing = 1 << i;
-				break;
+			// TODO Move to own function
+			FixedPoint16 angle;
+			if (currPt.x == prevPt.x) {
+				if (prevPt.y >= currPt.y)
+					angle = fixedMul(-0x5A0000, 0x478);
+				else
+					angle = fixedMul(0x5A0000, 0x478);
+			} else {
+				angle = fixedAtan(fixedDiv(deltaY << 16, deltaX << 16));
+			}
+			_actor->_pathAngle = angle;
+
+			// TODO Move to own function
+			int16 v13 = (fixedTrunc(fixedMul(angle, 0x394BB8)) + 360) % 360; // 0x394BB8 is 180 / pi
+			if (deltaX >= 0)
+				v13 += 180;
+			v13 = (v13 + 90) % 360;
+			int16 v15 = kAngleTbl[0] / -2;
+			uint newFacing = 1;
+			for (uint i = 0; i < 8; ++i) {
+				v15 += kAngleTbl[i];
+				if (v13 < v15) {
+					newFacing = 1 << i;
+					break;
+				}
+			}
+			if (newFacing != _actor->_facing) {
+				refreshSequenceCode();
+				faceActor(newFacing);
 			}
-		}
-		if (newFacing != _actor->_facing) {
-			refreshSequenceCode();
-			faceActor(newFacing);
-		}
 
-		_actor->_pathFlag50 = true;
+			_actor->_pathFlag50 = true;
 
-	}
+		}
 
-	FP16 deltaX24, deltaY24;
+		FixedPoint16 deltaX24, deltaY24;
 
-	if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
+		if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
 
-		FP16 v20 = fixedMul((deltaTime + _actor->_pathCtrX) << 16, _actor->_pathCtrY << 16);
-		FP16 v21 = fixedDiv(v20, 100 << 16);
-		FP16 v22 = fixedMul(v21, _actor->_scale << 16);
-		FP16 v23 = fixedDiv(v22, 100 << 16);
-		_actor->_seqCodeValue1 = 100 * _actor->_pathCtrY * deltaTime / 100;
-		if (v23) {
-			FP16 prevDistance = fixedDistance(prevPt.x << 16, prevPt.y << 16, _actor->_posXShl, _actor->_posYShl);
-			FP16 distance = prevDistance + v23;
-			if (prevPt.x > currPt.x)
-				distance = -distance;
-			deltaX24 = fixedMul(fixedCos(_actor->_pathAngle), distance);
-			deltaY24 = fixedMul(fixedSin(_actor->_pathAngle), distance);
+			FixedPoint16 v20 = fixedMul((deltaTime + _actor->_pathCtrX) << 16, _actor->_pathCtrY << 16);
+			FixedPoint16 v21 = fixedDiv(v20, 100 << 16);
+			FixedPoint16 v22 = fixedMul(v21, _actor->_scale << 16);
+			FixedPoint16 v23 = fixedDiv(v22, 100 << 16);
+			_actor->_seqCodeValue1 = 100 * _actor->_pathCtrY * deltaTime / 100;
+			if (v23) {
+				FixedPoint16 prevDistance = fixedDistance(prevPt.x << 16, prevPt.y << 16, _actor->_posXShl, _actor->_posYShl);
+				FixedPoint16 distance = prevDistance + v23;
+				if (prevPt.x > currPt.x)
+					distance = -distance;
+				deltaX24 = fixedMul(fixedCos(_actor->_pathAngle), distance);
+				deltaY24 = fixedMul(fixedSin(_actor->_pathAngle), distance);
+			} else {
+				deltaX24 = _actor->_posXShl - (prevPt.x << 16);
+				deltaY24 = _actor->_posYShl - (prevPt.y << 16);
+			}
 		} else {
-			deltaX24 = _actor->_posXShl - (prevPt.x << 16);
-			deltaY24 = _actor->_posYShl - (prevPt.y << 16);
+			if (100 * (int)deltaTime <= _actor->_seqCodeValue2)
+				break;
+			deltaX24 = deltaX << 16;
+			deltaY24 = deltaY << 16;
 		}
-	} else {
-		if (100 * (int)deltaTime <= _actor->_seqCodeValue2)
-			break;
-		deltaX24 = deltaX << 16;
-		deltaY24 = deltaY << 16;
-	}
 
-	if (ABS(deltaX24) < ABS(deltaX << 16) ||
-		ABS(deltaY24) < ABS(deltaY << 16)) {
-		FP16 newX = (prevPt.x << 16) + deltaX24;
-		FP16 newY = (prevPt.y << 16) + deltaY24;
-		if (newX == _actor->_posXShl &&	newY == _actor->_posYShl) {
-			_actor->_pathCtrX += deltaTime;
-		} else {
-			_actor->_pathCtrX = 0;
-			_actor->_posXShl = newX;
-			_actor->_posYShl = newY;
-			_actor->_position.x = fixedTrunc(_actor->_posXShl);
-			_actor->_position.y = fixedTrunc(_actor->_posYShl);
-		}
-	} else {
-		_actor->_position = currPt;
-		_actor->_posXShl = _actor->_position.x << 16;
-		_actor->_posYShl = _actor->_position.y << 16;
-		--_actor->_pathPointsCount;
-		++_actor->_pathPointIndex;
-		++_actor->_pathPoints;
-		_actor->_pathInitialPosFlag = true;
-		if (_actor->_pathPointsCount == 0) {
-			if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
-				delete _actor->_pathNode;
-				_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
+		if (ABS(deltaX24) < ABS(deltaX << 16) ||
+			ABS(deltaY24) < ABS(deltaY << 16)) {
+			FixedPoint16 newX = (prevPt.x << 16) + deltaX24;
+			FixedPoint16 newY = (prevPt.y << 16) + deltaY24;
+			if (newX == _actor->_posXShl &&	newY == _actor->_posYShl) {
+				_actor->_pathCtrX += deltaTime;
+			} else {
+				_actor->_pathCtrX = 0;
+				_actor->_posXShl = newX;
+				_actor->_posYShl = newY;
+				_actor->_position.x = fixedTrunc(_actor->_posXShl);
+				_actor->_position.y = fixedTrunc(_actor->_posYShl);
 			}
-			_actor->_pathNode = 0;
-			_actor->_pathPoints = 0;
-			_actor->_pathPointsCount = 0;
-			_actor->_pathPointIndex = 0;
-			if (_actor->_notifyId3C) {
-				_vm->notifyThreadId(_actor->_notifyId3C);
-				_actor->_walkCallerThreadId1 = 0;
+		} else {
+			_actor->_position = currPt;
+			_actor->_posXShl = _actor->_position.x << 16;
+			_actor->_posYShl = _actor->_position.y << 16;
+			--_actor->_pathPointsCount;
+			++_actor->_pathPointIndex;
+			++_actor->_pathPoints;
+			_actor->_pathInitialPosFlag = true;
+			if (_actor->_pathPointsCount == 0) {
+				if (_actor->_flags & Illusions::ACTOR_FLAG_400) {
+					delete _actor->_pathNode;
+					_actor->_flags &= ~Illusions::ACTOR_FLAG_400;
+				}
+				_actor->_pathNode = 0;
+				_actor->_pathPoints = 0;
+				_actor->_pathPointsCount = 0;
+				_actor->_pathPointIndex = 0;
+				if (_actor->_notifyId3C) {
+					_vm->notifyThreadId(_actor->_notifyId3C);
+					_actor->_walkCallerThreadId1 = 0;
+				}
+				fastWalked = false;
 			}
-			fastWalked = false;
+			_actor->_pathFlag50 = false;
 		}
-		_actor->_pathFlag50 = false;
-	}
 
-	if (!fastWalked)
-		break;
-
-	}
+	} while (fastWalked);
 
 }
 
diff --git a/engines/illusions/bbdou/bbdou_credits.cpp b/engines/illusions/bbdou/bbdou_credits.cpp
index eb23779..393026e 100644
--- a/engines/illusions/bbdou/bbdou_credits.cpp
+++ b/engines/illusions/bbdou/bbdou_credits.cpp
@@ -98,7 +98,7 @@ void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint ali
 	charToWChar(text, wtext, ARRAYSIZE(wtext));
 
 	// TODO Extract to Actor class
-    Control *control = _vm->getObjectControl(objectId);
+	Control *control = _vm->getObjectControl(objectId);
 	FontResource *font = _vm->_dict->findFont(_currFontId); 
 	TextDrawer textDrawer;
 	WidthHeight dimensions;
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index bc6ed03..95e17fa 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -60,7 +60,7 @@ CauseThread_BBDOU::CauseThread_BBDOU(IllusionsEngine_BBDOU *vm, uint32 threadId,
 	_sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId) {
 	_type = kTTSpecialThread;
 }
-		
+
 void CauseThread_BBDOU::onNotify() {
 	_bbdou->_cursor->_data._causeThreadId1 = 0;
 	terminate();
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index f3845a6..06cfc28 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -168,7 +168,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 
 	_screen->setColorKey1(0xF81F);
 
-    initInput();
+	initInput();
 
 	initUpdateFunctions();
 
@@ -188,10 +188,10 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	ConfMan.registerDefault("talkspeed", 240);
 	_subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
 
-	_globalSceneId = 0x00010003;	
-	
+	_globalSceneId = 0x00010003;
+
 	setDefaultTextCoords();
-	
+
 	_resSys->loadResource(0x000D0001, 0, 0);
 
 	_doScriptThreadInit = false;
@@ -214,7 +214,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
-    delete _soundMan;
+	delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
 	delete _triggerFunctions;
@@ -230,9 +230,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _resSys;
 	delete _resReader;
 	delete _dict;
-	
+
 	debug("Ok");
-	
+
 	return Common::kNoError;
 }
 
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index 00ce617..c1bd22b 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -83,7 +83,7 @@ public:
 	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
 	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
 
-    void setDefaultTextCoords();
+	void setDefaultTextCoords();
 
 	void loadSpecialCode(uint32 resId);
 	void unloadSpecialCode(uint32 resId);
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 390dff8..2aa8e74 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -488,11 +488,11 @@ void Camera::recalcPan(uint32 currTime) {
 	if (_activeState._panSpeed == 0) {
 		_activeState._time2E = 0;
 	} else {
-		FP16 x1 = _activeState._currPan2.x << 16;
-		FP16 y1 = _activeState._currPan2.y << 16;
-		FP16 x2 = _activeState._panTargetPoint.x << 16;
-		FP16 y2 = _activeState._panTargetPoint.y << 16;
-		FP16 distance = fixedDistance(x1, y1, x2, y2);
+		FixedPoint16 x1 = _activeState._currPan2.x << 16;
+		FixedPoint16 y1 = _activeState._currPan2.y << 16;
+		FixedPoint16 x2 = _activeState._panTargetPoint.x << 16;
+		FixedPoint16 y2 = _activeState._panTargetPoint.y << 16;
+		FixedPoint16 distance = fixedDistance(x1, y1, x2, y2);
 		_activeState._time2E = 60 * fixedTrunc(distance) / _activeState._panSpeed;
 	}
 
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index 8a5476c..667042c 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -87,20 +87,20 @@ public:
 	void removeActorType(uint32 id);
 	ActorType *findActorType(uint32 id);
 
-    void addFont(uint32 id, FontResource *fontResource);
+	void addFont(uint32 id, FontResource *fontResource);
 	void removeFont(uint32 id);
 	FontResource *findFont(uint32 id);
 
-    void addSequence(uint32 id, Sequence *sequence);
+	void addSequence(uint32 id, Sequence *sequence);
 	void removeSequence(uint32 id);
 	Sequence *findSequence(uint32 id);
 
-    void addTalkEntry(uint32 id, TalkEntry *talkEntry);
+	void addTalkEntry(uint32 id, TalkEntry *talkEntry);
 	void removeTalkEntry(uint32 id);
 	TalkEntry *findTalkEntry(uint32 id);
 
-    void setObjectControl(uint32 objectId, Control *control);
-    Control *getObjectControl(uint32 objectId);
+	void setObjectControl(uint32 objectId, Control *control);
+	Control *getObjectControl(uint32 objectId);
 
 protected:
 	DictionaryHashMap<ActorType> _actorTypes;
diff --git a/engines/illusions/duckman/duckman_credits.cpp b/engines/illusions/duckman/duckman_credits.cpp
index 2798efe..ccbde8d 100644
--- a/engines/illusions/duckman/duckman_credits.cpp
+++ b/engines/illusions/duckman/duckman_credits.cpp
@@ -3,7 +3,7 @@
  * ScummVM is the legal property of its developers, whose names
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
- *                                                                                                                                  
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index 60e0505..ec85782 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -3,7 +3,7 @@
  * ScummVM is the legal property of its developers, whose names
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
- *                                                                                                                                  
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 38d3bab..7ffa312 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -213,7 +213,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _fader;
 
-    delete _gameState;
+	delete _gameState;
 	delete _menuSystem;
 	delete _soundMan;
 	delete _updateFunctions;
diff --git a/engines/illusions/fixedpoint.cpp b/engines/illusions/fixedpoint.cpp
index 54dbb13..f240b14 100644
--- a/engines/illusions/fixedpoint.cpp
+++ b/engines/illusions/fixedpoint.cpp
@@ -25,23 +25,23 @@
 
 namespace Illusions {
 
-FP16 floatToFixed(float value) {
+FixedPoint16 floatToFixed(float value) {
 	return value * 65536.0;
 }
 
-float fixedToFloat(FP16 value) {
+float fixedToFloat(FixedPoint16 value) {
 	return value / 65536.0;
 }
 
-FP16 fixedMul(FP16 a, FP16 b) {
+FixedPoint16 fixedMul(FixedPoint16 a, FixedPoint16 b) {
 	return ((float)a * b) / 65536.0;
 }
 
-FP16 fixedDiv(FP16 a, FP16 b) {
+FixedPoint16 fixedDiv(FixedPoint16 a, FixedPoint16 b) {
 	return ((float)a / b) * 65536.0;
 }
 
-int16 fixedTrunc(FP16 value) {
+int16 fixedTrunc(FixedPoint16 value) {
 	// CHECKME Not sure if this correct
 	int16 result = (value >> 16) & 0xFFFF;
 	if ((value & 0xFFFF) >= 0x8000)
@@ -49,7 +49,7 @@ int16 fixedTrunc(FP16 value) {
 	return result;
 }
 
-FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2) {
+FixedPoint16 fixedDistance(FixedPoint16 x1, FixedPoint16 y1, FixedPoint16 x2, FixedPoint16 y2) {
 	float xd = fixedToFloat(x1) - fixedToFloat(x2);
 	float yd = fixedToFloat(y1) - fixedToFloat(y2);
 	if (xd != 0.0 || yd != 0.0)
@@ -57,16 +57,16 @@ FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2) {
 	return 0;
 }
 
-FP16 fixedAtan(FP16 value) {
+FixedPoint16 fixedAtan(FixedPoint16 value) {
 	//return floatToFixed(atan2(1.0, fixedToFloat(value)));
 	return floatToFixed(atan(fixedToFloat(value)));
 }
 
-FP16 fixedCos(FP16 value) {
+FixedPoint16 fixedCos(FixedPoint16 value) {
 	return floatToFixed(cos(fixedToFloat(value)));
 }
 
-FP16 fixedSin(FP16 value) {
+FixedPoint16 fixedSin(FixedPoint16 value) {
 	return floatToFixed(sin(fixedToFloat(value)));
 }
 
diff --git a/engines/illusions/fixedpoint.h b/engines/illusions/fixedpoint.h
index 1320daa7..fa3fd22 100644
--- a/engines/illusions/fixedpoint.h
+++ b/engines/illusions/fixedpoint.h
@@ -27,17 +27,17 @@
 
 namespace Illusions {
 
-typedef int32 FP16;
-
-FP16 floatToFixed(float value);
-float fixedToFloat(FP16 value);
-FP16 fixedMul(FP16 a, FP16 b);
-FP16 fixedDiv(FP16 a, FP16 b);
-int16 fixedTrunc(FP16 value);
-FP16 fixedDistance(FP16 x1, FP16 y1, FP16 x2, FP16 y2);
-FP16 fixedAtan(FP16 value);
-FP16 fixedCos(FP16 value);
-FP16 fixedSin(FP16 value);
+typedef int32 FixedPoint16;
+
+FixedPoint16 floatToFixed(float value);
+float fixedToFloat(FixedPoint16 value);
+FixedPoint16 fixedMul(FixedPoint16 a, FixedPoint16 b);
+FixedPoint16 fixedDiv(FixedPoint16 a, FixedPoint16 b);
+int16 fixedTrunc(FixedPoint16 value);
+FixedPoint16 fixedDistance(FixedPoint16 x1, FixedPoint16 y1, FixedPoint16 x2, FixedPoint16 y2);
+FixedPoint16 fixedAtan(FixedPoint16 value);
+FixedPoint16 fixedCos(FixedPoint16 value);
+FixedPoint16 fixedSin(FixedPoint16 value);
 
 } // End of namespace Illusions
 
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 9131d58..762f504 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -262,12 +262,12 @@ void BaseMenuSystem::placeActorHoverBackground() {
 
 	WidthHeight frameDimensions;
 	v0->getActorFrameDimensions(frameDimensions);
-	
+
 	FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
 	int charHeight = font->getCharHeight() + font->getLineIncr();
 	if (frameDimensions._height < charHeight)
 		charHeight = frameDimensions._height;
-		
+
 	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight), _activeMenu->_fieldE);
 
 	updateActorHoverBackground();
@@ -375,8 +375,8 @@ void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mouseP
 	debug(0, "BaseMenuSystem::handleClick() menuItemIndex: %d click point: (%d, %d)", menuItemIndex, mousePos.x, mousePos.y);
 
 	if (menuItemIndex == 0) {
-	    playSoundEffect14();
-	    return;
+		playSoundEffect14();
+		return;
 	}
 
 	MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
@@ -434,7 +434,7 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 }
 
 void BaseMenuSystem::update(Control *cursorControl) {
-    Common::Point mousePos = _vm->_input->getCursorPosition();
+	Common::Point mousePos = _vm->_input->getCursorPosition();
 	setMousePos(mousePos);
 	
 	uint newHoveredMenuItemIndex;
@@ -518,7 +518,7 @@ void BaseMenuSystem::setSavegameSlotNum(int slotNum) {
 }
 
 void BaseMenuSystem::setSavegameDescription(Common::String desc) {
-    _vm->_savegameDescription = desc;
+	_vm->_savegameDescription = desc;
 }
 
 void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
@@ -672,27 +672,28 @@ void MenuActionLoadGame::execute() {
 
 // MenuActionSaveGame
 
-	MenuActionSaveGame::MenuActionSaveGame(BaseMenuSystem *menuSystem, uint choiceIndex)
-			: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
-	}
+MenuActionSaveGame::MenuActionSaveGame(BaseMenuSystem *menuSystem, uint choiceIndex)
+		: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
+}
 
-	void MenuActionSaveGame::execute() {
-		const Plugin *plugin = NULL;
-		EngineMan.findGame(ConfMan.get("gameid"), &plugin);
-		GUI::SaveLoadChooser *dialog;
-		Common::String desc;
-		int slot;
+void MenuActionSaveGame::execute() {
+	const Plugin *plugin = NULL;
+	EngineMan.findGame(ConfMan.get("gameid"), &plugin);
+	GUI::SaveLoadChooser *dialog;
+	Common::String desc;
+	int slot;
 
-		dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
-		slot = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
-        desc = dialog->getResultString().c_str();
+	dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+	slot = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+	desc = dialog->getResultString().c_str();
 
-        delete dialog;
+	delete dialog;
 
-        if (slot >= 0) {
-            _menuSystem->setSavegameSlotNum(slot);
-            _menuSystem->setSavegameDescription(desc);
-			_menuSystem->selectMenuChoiceIndex(_choiceIndex);
-		}
+	if (slot >= 0) {
+		_menuSystem->setSavegameSlotNum(slot);
+		_menuSystem->setSavegameDescription(desc);
+		_menuSystem->selectMenuChoiceIndex(_choiceIndex);
 	}
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index cc76116..ff85ca6 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -116,57 +116,57 @@ public:
 protected:
 	IllusionsEngine *_vm;
 	MenuStack _menuStack;
-	
+
 	uint32 _menuCallerThreadId;
-    bool _isTimeOutEnabled;
-    bool _isTimeOutReached;
-    uint32 _timeOutDuration;
-    uint _timeOutMenuChoiceIndex;
-    uint32 _timeOutStartTime;
-    uint32 _timeOutEndTime;
-	
+	bool _isTimeOutEnabled;
+	bool _isTimeOutReached;
+	uint32 _timeOutDuration;
+	uint _timeOutMenuChoiceIndex;
+	uint32 _timeOutStartTime;
+	uint32 _timeOutEndTime;
+
 	Common::Point _savedCursorPos;
 	bool _cursorInitialVisibleFlag;
 	int _savedGameState;
 	int _savedCursorActorIndex;
 	int _savedCursorSequenceId;
-	
+
 	bool _isActive;
-	
+
 	MenuChoiceOffsets _menuChoiceOffsets;
 	int16 *_menuChoiceOffset;
-	
+
 	uint _queryConfirmationChoiceIndex;
-	
+
 	uint _field54;
 	uint _menuLinesCount;
 	uint _menuItemCount;
-	
+
 	uint _hoveredMenuItemIndex;
 	uint _hoveredMenuItemIndex2;
 	uint _hoveredMenuItemIndex3;
 
 	BaseMenu *_activeMenu;
 	void setMouseCursorToMenuItem(int menuItemIndex);
-	
+
 	void calcMenuItemRect(uint menuItemIndex, WRect &rect);
 	bool calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt);
 	bool calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex);
 	void setMousePos(Common::Point &mousePos);
-	
+
 	void activateMenu(BaseMenu *menu);
-	
+
 	void updateTimeOut(bool resetTimeOut);
-	
+
 	void initActorHoverBackground();
 	void placeActorHoverBackground();
 	void updateActorHoverBackground();
 	void hideActorHoverBackground();
-	
+
 	void initActorTextColorRect();
 	void placeActorTextColorRect();
 	void hideActorTextColorRect();
-	
+
 	virtual BaseMenu *getMenuById(int menuId) = 0;
 	virtual void playSoundEffect(int sfxId) = 0;
 };
diff --git a/engines/illusions/resources/actorresource.h b/engines/illusions/resources/actorresource.h
index ea356a9..34b4702 100644
--- a/engines/illusions/resources/actorresource.h
+++ b/engines/illusions/resources/actorresource.h
@@ -94,7 +94,7 @@ public:
 
 class ActorInstance : public ResourceInstance {
 public:
-	ActorInstance(IllusionsEngine *vm);              
+	ActorInstance(IllusionsEngine *vm);
 	virtual void load(Resource *resource);
 	virtual void unload();
 	virtual void pause();
diff --git a/engines/illusions/resources/midiresource.cpp b/engines/illusions/resources/midiresource.cpp
index 203ed12..5e6e85a 100644
--- a/engines/illusions/resources/midiresource.cpp
+++ b/engines/illusions/resources/midiresource.cpp
@@ -30,8 +30,8 @@ namespace Illusions {
 void MidiGroupResourceLoader::load(Resource *resource) {
 	debug(1, "MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
 
-    // TODO
-	
+	// TODO
+
 }
 
 bool MidiGroupResourceLoader::isFlag(int flag) {
diff --git a/engines/illusions/resources/scriptresource.h b/engines/illusions/resources/scriptresource.h
index f185319..6c4fddf 100644
--- a/engines/illusions/resources/scriptresource.h
+++ b/engines/illusions/resources/scriptresource.h
@@ -141,11 +141,11 @@ public:
 
 class ScriptInstance : public ResourceInstance {
 public:
-	ScriptInstance(IllusionsEngine *vm);              
+	ScriptInstance(IllusionsEngine *vm);
 	virtual void load(Resource *resource);
 	virtual void unload();
 public:
-	IllusionsEngine *_vm;	
+	IllusionsEngine *_vm;
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/resources/soundresource.h b/engines/illusions/resources/soundresource.h
index 97e6f89..b6e6382 100644
--- a/engines/illusions/resources/soundresource.h
+++ b/engines/illusions/resources/soundresource.h
@@ -61,11 +61,11 @@ public:
 
 class SoundGroupInstance : public ResourceInstance {
 public:
-	SoundGroupInstance(IllusionsEngine *vm);              
+	SoundGroupInstance(IllusionsEngine *vm);
 	virtual void load(Resource *resource);
 	virtual void unload();
 public:
-	IllusionsEngine *_vm;	
+	IllusionsEngine *_vm;
 	SoundGroupResource *_soundGroupResource;
 	uint32 _resId;
 };


Commit: aa241b8ef37a09972171159045c9f2efe0c167e9
    https://github.com/scummvm/scummvm/commit/aa241b8ef37a09972171159045c9f2efe0c167e9
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Mark bbdou as unstable
Drop platform from game id

Changed paths:
    engines/illusions/detection.cpp


diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index ca002ae..bbecdc0 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -33,7 +33,7 @@
 
 static const PlainGameDescriptor illusionsGames[] = {
 	{ "illusions", "Illusions engine game" },
-	{ "bbdou", "Beavis and Butthead Do U" },
+	{ "bbdou", "Beavis and Butt-head Do U" },
 	{ "duckman", "Duckman" },
 	{ 0, 0 }
 };
@@ -48,7 +48,7 @@ static const IllusionsGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("000D0001.scr", "d0c846d5dccc5607a482c7dcbdf06973", 601980),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_NO_FLAGS,
+			ADGF_UNSTABLE | ADGF_DROPPLATFORM,
 			GUIO0()
 		},
 		kGameIdBBDOU
@@ -61,7 +61,7 @@ static const IllusionsGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("duckman.gam", "172c0514f3793041718159cf9cf9935f", 29560832),
 			Common::EN_ANY,
 			Common::kPlatformWindows,
-			ADGF_NO_FLAGS,
+			ADGF_DROPPLATFORM,
 			GUIO0()
 		},
 		kGameIdDuckman
@@ -74,7 +74,7 @@ static const IllusionsGameDescription gameDescriptions[] = {
 			AD_ENTRY1s("duckman.gam", "64d16922ffb46b746fc2c12a14d75bcc", 29779968),
 			Common::DE_DEU,
 			Common::kPlatformWindows,
-			ADGF_NO_FLAGS,
+			ADGF_DROPPLATFORM,
 			GUIO0()
 		},
 		kGameIdDuckman


Commit: 7cadb7ad0ce7b24c28d3fa9d842f7083ae5b9056
    https://github.com/scummvm/scummvm/commit/7cadb7ad0ce7b24c28d3fa9d842f7083ae5b9056
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Formatting

Changed paths:
    engines/illusions/camera.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/input.cpp
    engines/illusions/menusystem.cpp
    engines/illusions/pathfinder.cpp
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/sound.cpp


diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 2aa8e74..1f7ea32 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -87,7 +87,7 @@ void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
 void Camera::panCenterObject(uint32 objectId, int16 panSpeed) {
 	Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
 	if (_vm->getGameId() == kGameIdDuckman) {
-		if(objectId == Illusions::CURSOR_OBJECT_ID) {
+		if (objectId == Illusions::CURSOR_OBJECT_ID) {
 			_activeState._cameraMode = 2;
 			_activeState._trackingLimits.x = 156;
 			_activeState._trackingLimits.y = 96;
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index c773afc..4d63147 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -251,7 +251,7 @@ BaseMenu *DuckmanMenuSystem::createAddRemoveInventoryMenu() {
 	BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
 	menu->addText("Add/Remove Inventory");
 	menu->addText("-----------------");
-	for(int i=0;i < 21;i++) {
+	for (int i = 0; i < 21; i++) {
 		menu->addMenuItem(new MenuItem(kDebugInventoryItems[i].name, new MenuActionInventoryAddRemove(this, _vm, i)));
 	}
 	menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
@@ -318,13 +318,13 @@ void DuckmanMenuSystem::playSoundEffect(int sfxId) {
 	_vm->playSoundEffect(sfxId);
 }
 
-	MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
-		: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex), _vm(vm) {
+MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
+	: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex), _vm(vm) {
 }
 
 void MenuActionInventoryAddRemove::execute() {
 	if (_vm->_scriptResource->_properties.get(kDebugInventoryItems[_choiceIndex].propertyId)) {
-		if(_vm->_cursor._objectId == kDebugInventoryItems[_choiceIndex].objectId) {
+		if (_vm->_cursor._objectId == kDebugInventoryItems[_choiceIndex].objectId) {
 			_vm->stopCursorHoldingObject();
 		}
 		_vm->_scriptResource->_properties.set(kDebugInventoryItems[_choiceIndex].propertyId, false);
@@ -360,7 +360,7 @@ int MenuActionUpdateSlider::calcNewSliderValue(int newOffset) {
 	int start = 0;
 	int end = 0;
 	int currentPosition = 0;
-	for(int i = 0; i < text.size(); i++) {
+	for (int i = 0; i < text.size(); i++) {
 		switch (text[i]) {
 			case '{' : start = i; break;
 			case '}' : end = i; break;
@@ -385,7 +385,7 @@ int MenuActionUpdateSlider::calcNewSliderValue(int newOffset) {
 void MenuActionUpdateSlider::setSliderValue(uint8 newValue) {
 	int start = 0;
 	Common::String text = _menuItem->getText();
-	for(int i = 0; i < text.size(); i++) {
+	for (int i = 0; i < text.size(); i++) {
 		switch (text[i]) {
 			case '{' : start = i; break;
 			case '|' : text.setChar('~', i); break;
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 21042dc..712f417 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -180,8 +180,8 @@ void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
 	_newKeys = 0;
 	_newButtons = ~prevButtonStates & _buttonStates;
 
-	if ( !down && !isCheatModeActive() ) {
-		if( _cheatCodeIndex < 7 && key == kCheatCode[_cheatCodeIndex] ) {
+	if ( !down && !isCheatModeActive()) {
+		if ( _cheatCodeIndex < 7 && key == kCheatCode[_cheatCodeIndex]) {
 			_cheatCodeIndex++;
 		} else {
 			_cheatCodeIndex = 0;
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 762f504..79cbdd4 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -554,7 +554,7 @@ void BaseMenuSystem::redrawMenuText(BaseMenu *menu) {
 
 bool BaseMenuSystem::calcMenuItemTextPositionAtPoint(Common::Point pt, int &offset) {
 	uint menuItemIndex;
-	if(!calcMenuItemIndexAtPoint(pt, menuItemIndex)) {
+	if (!calcMenuItemIndexAtPoint(pt, menuItemIndex)) {
 		return false;
 	}
 
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index b0b43a0..04ee493 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -139,10 +139,10 @@ void PathFinder::findValidDestPt(Common::Point &destPt) {
 	for (uint i = 0; i < _walkRects->size(); ++i) {
 		PathLine currRect = (*_walkRects)[i];
 		//TODO fix this hack. Used here to get xmas tree scene to work.
-		if(currRect.p1.x > _screenRect.p1.x) {
+		if (currRect.p1.x > _screenRect.p1.x) {
 			currRect.p1.x = _screenRect.p1.x;
 		}
-		if(currRect.p0.x < _screenRect.p0.x) {
+		if (currRect.p0.x < _screenRect.p0.x) {
 			currRect.p0.x = _screenRect.p0.x;
 		}
 		WidthHeight rectDimensions = calcRectDimensions(currRect);
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index 3ad7085..a35f06e 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -468,9 +468,9 @@ void BackgroundInstance::initSurface() {
 		_surfaces[i] = _vm->_screen->allocSurface(bgInfo->_surfInfo);
 		drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
 #if 0
-		if(_bgRes->_pathWalkRectsCount > 0) {
+		if (_bgRes->_pathWalkRectsCount > 0) {
 			PathLines *pl = _bgRes->_pathWalkRects->_rects;
-			for(int j=0;j < pl->size(); j++) {
+			for (int j=0; j < pl->size(); j++) {
 				PathLine pathLine = (*pl)[j];
 				debug(0, "walk path rect line[%d]. (%d,%d)->(%d,%d)", j, pathLine.p0.x, pathLine.p0.y, pathLine.p1.x, pathLine.p1.y);
 				_surfaces[i]->drawLine(pathLine.p0.x, pathLine.p0.y, pathLine.p1.x, pathLine.p1.y, 5);
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index bc65e37..aed389a 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -212,7 +212,7 @@ bool VoicePlayer::isCued() {
 }
 
 void VoicePlayer::pause(bool paused) {
-	if(isPlaying()) {
+	if (isPlaying()) {
 		g_system->getMixer()->pauseHandle(_soundHandle, paused);
 	}
 }


Commit: 989d8d9b8abecee3f0b72c92a05067e128fc6cdd
    https://github.com/scummvm/scummvm/commit/989d8d9b8abecee3f0b72c92a05067e128fc6cdd
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Formatting, remove trailing whitespace.

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_credits.cpp
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/bbdou/bbdou_triggerfunctions.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h
    engines/illusions/camera.cpp
    engines/illusions/detection.cpp
    engines/illusions/duckman/duckman_credits.cpp
    engines/illusions/duckman/duckman_credits.h
    engines/illusions/duckman/duckman_dialog.cpp
    engines/illusions/duckman/duckman_dialog.h
    engines/illusions/duckman/duckman_inventory.h
    engines/illusions/duckman/duckman_screenshakereffects.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/duckman_specialcode.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.h
    engines/illusions/fileresourcereader.cpp
    engines/illusions/gamarchive.cpp
    engines/illusions/gamresourcereader.cpp
    engines/illusions/graphics.cpp
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h
    engines/illusions/menusystem.cpp
    engines/illusions/menusystem.h
    engines/illusions/pathfinder.cpp
    engines/illusions/pathfinder.h
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/actorresource.h
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/resources/backgroundresource.h
    engines/illusions/resources/scriptresource.cpp
    engines/illusions/resourcesystem.cpp
    engines/illusions/resourcesystem.h
    engines/illusions/saveload.cpp
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/screentext.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/sequenceopcodes.h
    engines/illusions/textdrawer.cpp
    engines/illusions/textdrawer.h
    engines/illusions/thread.cpp
    engines/illusions/threads/abortablethread.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 7cbada1..eaf603f 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -95,7 +95,7 @@ Actor::Actor(IllusionsEngine *vm)
 	_seqCodeValue1 = 0;
 	_seqCodeValue2 = 600;
 	_seqCodeValue3 = 0;
-	
+
 	_notifyId3C = 0;
 
 	_controlRoutine = 0;
@@ -236,7 +236,7 @@ void Control::unpause() {
 
 	if (_objectId == Illusions::CURSOR_OBJECT_ID)
 		_vm->setCursorControl(this);
-  
+
 	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200)) {
 		SurfInfo surfInfo;
 		ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
@@ -619,7 +619,7 @@ void Control::sequenceActor() {
 
 	opCall._result = 0;
 	_actor->_seqCodeValue3 -= _actor->_seqCodeValue1;
-	
+
 	while (_actor->_seqCodeValue3 <= 0 && !sequenceFinished) {
 		bool breakInner = false;
 		while (!breakInner) {
@@ -652,12 +652,12 @@ void Control::sequenceActor() {
 		}
 		//debug(1, "New frame OK");
 	}
-	
+
 	if (sequenceFinished) {
 		//debug(1, "Sequence has finished");
 		_actor->_seqCodeIp = 0;
 	}
-	
+
 }
 
 void Control::setActorIndex(int actorIndex) {
@@ -958,7 +958,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	_actor->_notifyId3C = 0;
 	_actor->_walkCallerThreadId1 = 0;
 	_actor->_entryTblPtr = 0;
-	
+
 	Sequence *sequence = _vm->_dict->findSequence(sequenceId);
 
 	if (!sequence && _vm->getGameId() == kGameIdDuckman) {
@@ -970,7 +970,7 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 
 	_actor->_seqCodeIp = sequence->_sequenceCode;
 	_actor->_frames = _vm->findActorSequenceFrames(sequence);
-	
+
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
 
@@ -981,12 +981,12 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 	}
 
 	_actor->initSequenceStack();
-	
+
 	if (_vm->getGameId() == kGameIdBBDOU)
 		stopSequenceActor();
-	
+
 	_actor->_linkIndex2 = 0;
-	
+
 	if (entryTblPtr) {
 		_actor->_flags |= Illusions::ACTOR_FLAG_80;
 		_actor->_entryTblPtr = entryTblPtr;
@@ -1034,7 +1034,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 	Control *control = newControl();
 	Actor *actor = newActor();
 	ActorType *actorType = _vm->_dict->findActorType(actorTypeId);
-	
+
 	control->_objectId = objectId;
 	control->_flags = actorType->_flags;
 	control->_priority = actorType->_priority;
@@ -1044,7 +1044,7 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 
 	if (_vm->isCursorObject(actorTypeId, objectId))
 		_vm->setCursorControlRoutine(control);
-		
+
 	if (actorType->_surfInfo._dimensions._width > 0 || actorType->_surfInfo._dimensions._height > 0) {
 		actor->createSurface(actorType->_surfInfo);
 	} else {
@@ -1074,19 +1074,19 @@ void Controls::placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequ
 		actor->_pathWalkRects = bgRes->getPathWalkRects(actorType->_pathWalkRectIndex - 1);
 		actor->_flags |= Illusions::ACTOR_FLAG_HAS_WALK_RECTS;
 	}
-	
+
 	if (actorType->_priorityLayerIndex) {
 		actor->_priorityLayer = bgRes->getPriorityLayer(actorType->_priorityLayerIndex - 1);
 		actor->_flags |= Illusions::ACTOR_FLAG_PRIORITY;
 	}
-	
+
 	if (actorType->_regionLayerIndex) {
 		actor->_regionLayer = bgRes->getRegionLayer(actorType->_regionLayerIndex - 1);
 		actor->_flags |= Illusions::ACTOR_FLAG_REGION;
 	}
-	
+
 	actor->_pathCtrY = 140;
-	
+
 	_controls.push_front(control);
 	_vm->_dict->setObjectControl(objectId, control);
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 5a4dd4f..0f76050 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -110,7 +110,7 @@ public:
 	IllusionsEngine *_vm;
 	byte _drawFlags;
 	uint _spriteFlags;
-	
+
 	int _pauseCtr;
 	uint _flags;
 
@@ -119,50 +119,50 @@ public:
 	int16 _newFrameIndex;
 	SurfInfo _surfInfo;
 	Graphics::Surface *_surface;
-	
+
 	FramesList *_frames;
 	NamedPoints *_namedPoints;
-	
+
 	ScaleLayer *_scaleLayer;
 	PriorityLayer *_priorityLayer;
 	RegionLayer *_regionLayer;
 	PathWalkPoints *_pathWalkPoints;
 	PathWalkRects *_pathWalkRects;
-	
+
 	uint _seqStackCount;
 	int16 _seqStack[5];
-	
+
 	Common::Point _position;
 	Common::Point _position2;
 	uint _facing;
 	int _regionIndex;
-	
+
 	uint32 _fontId;
 	int16 _actorIndex;
-	
+
 	DefaultSequences _defaultSequences;
 
 	uint32 _parentObjectId;
 	int _linkIndex;
 	int _linkIndex2;
 	uint32 _subobjects[kSubObjectsCount];
-	
+
 	uint32 _notifyThreadId1;
 	uint32 _notifyId3C;
 
 	uint32 _notifyThreadId2;
 	byte *_entryTblPtr;
-	
+
 	int _surfaceTextFlag;
-	
+
 	ActorControlRoutine *_controlRoutine;
-	
+
 	uint32 _sequenceId;
 	int _seqCodeValue2;
 	byte *_seqCodeIp;
 	int _seqCodeValue1;
 	int _seqCodeValue3;
-	
+
 	int _pathCtrX, _pathCtrY;
 	int _pathAngle;
 	int32 _posXShl, _posYShl;
diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index 62e7959..c631a22 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -101,7 +101,7 @@ void BbdouBubble::addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progRe
 }
 
 void BbdouBubble::show() {
-	
+
 	if (_prevItem0) {
 		hide();
 	}
@@ -110,14 +110,14 @@ void BbdouBubble::show() {
 	_currItem0 = 0;
 
 	calcBubbles(_pt1, _pt2);
-	
+
 	Control *control = _vm->_dict->getObjectControl(_prevItem0->_objectId);
 	control->setActorPosition(_pt2);
 	control->startSequenceActor(0x60057, 2, 0);
 	control->startSequenceActor(_prevItem0->_sequenceId1, 2, 0);
 	control->appearActor();
 	control->deactivateObject();
-	
+
 	for (uint i = 0; i < 32; ++i) {
 		if (_items[i]._enabled == 1) {
 			Control *subControl = _vm->_dict->getObjectControl(_items[i]._objectId);
@@ -125,7 +125,7 @@ void BbdouBubble::show() {
 			subControl->startSequenceActor(_items[i]._sequenceId, 2, 0);
 		}
 	}
-	
+
 }
 
 void BbdouBubble::hide() {
@@ -156,7 +156,7 @@ void BbdouBubble::setup(int16 minCount, Common::Point pt1, Common::Point pt2, ui
 		if (item0->_count < maxCount && item0->_count >= minCount &&
 			(!progResKeywordId || item0->_progResKeywordId == progResKeywordId)) {
 			maxCount = item0->_count;
-			_currItem0 = item0; 
+			_currItem0 = item0;
 		}
 	}
 	_pt1 = pt1;
@@ -204,7 +204,7 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 
 	for (int i = 0; i < kSequenceIdsCount; ++i)
 		sequenceCounters[i] = 0;
-	
+
 	if (pt2.y >= pt1.y) {
 		swapY = true;
 		if (pt1.x == pt2.x)
diff --git a/engines/illusions/bbdou/bbdou_credits.cpp b/engines/illusions/bbdou/bbdou_credits.cpp
index 393026e..08f9900 100644
--- a/engines/illusions/bbdou/bbdou_credits.cpp
+++ b/engines/illusions/bbdou/bbdou_credits.cpp
@@ -53,7 +53,7 @@ void BbdouCredits::stop() {
 
 void BbdouCredits::drawNextLine() {
 	uint leftIndex, rightIndex;
-	
+
 	if (!readNextLine(leftIndex, rightIndex)) {
 		_vm->_scriptResource->_properties.set(_endSignalPropertyId, true);
 		return;
@@ -81,13 +81,13 @@ void BbdouCredits::drawNextLine() {
 void charToWChar(const char *text, uint16 *wtext, uint size) {
 	while (*text != 0 && size > 1) {
 		*wtext++ = (byte)*text++;
-		/*		
+		/*
 		byte c = (byte)*text++;
 		if (c > 127) c = 32;
 		*wtext = c;
-		debug("%04X", *wtext);		
+		debug("%04X", *wtext);
 		++wtext;
-		*/		
+		*/
 		--size;
 	}
 	*wtext++ = 0;
@@ -99,7 +99,7 @@ void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint ali
 
 	// TODO Extract to Actor class
 	Control *control = _vm->getObjectControl(objectId);
-	FontResource *font = _vm->_dict->findFont(_currFontId); 
+	FontResource *font = _vm->_dict->findFont(_currFontId);
 	TextDrawer textDrawer;
 	WidthHeight dimensions;
 	uint16 *outText;
@@ -116,7 +116,7 @@ bool BbdouCredits::readNextLine(uint &leftIndex, uint &rightIndex) {
 	int textLines = 0;
 	leftIndex = 0;
 	rightIndex = 0;
-	
+
 	do {
 		uint lineIndex = _currLineIndex++;
 		const char *text = getText(lineIndex);
@@ -147,7 +147,7 @@ bool BbdouCredits::readNextLine(uint &leftIndex, uint &rightIndex) {
 			}
 		}
 	} while (!done);
-	
+
 	return textLines > 0;
 }
 
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 605919a..3a098b9 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -50,7 +50,7 @@ void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::actorControlRoutine1));
 	control->_flags |= 8;
-	
+
 	_data._mode = 1;
 	_data._mode2 = 0;
 	_data._verbId1 = 0x1B0000;
@@ -75,7 +75,7 @@ void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
 	_data._verbState._objectIds[1] = 0;
 	_data._verbState._index = 0;
 	_data._verbState._flag56 = false;
-	
+
 	resetActiveVerbs();
 
 	control->setActorIndexTo1();
@@ -128,7 +128,7 @@ void BbdouCursor::reset(uint32 objectId) {
 
 	_bbdou->hideVerbBubble(control->_objectId, &_data._verbState);
 	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::cursorInteractControlRoutine));
-	
+
 }
 
 void BbdouCursor::addCursorSequenceId(uint32 objectId, uint32 sequenceId) {
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 95e17fa..89f9441 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -492,7 +492,7 @@ void BbdouSpecialCode::playSoundEffect(int soundIndex) {
 		0x900C1, 2,
 		      0, 3,
 		0x900C0, 4,
-		0x900C2, 5, 
+		0x900C2, 5,
 		      0, 6
 	};
 	uint32 soundEffectId = kSoundEffectIds[2 * soundIndex];
@@ -535,11 +535,11 @@ Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos)
 
 void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
 	VerbState *verbState, uint32 progResKeywordId) {
-	
+
 	Common::Rect collisionRect;
 	Control *overlappedControl, *control2, *control3;
 	Common::Point bubbleSourcePt(320, 240), bubbleDestPt, currPan;
-	
+
 	overlappedControl = _vm->_dict->getObjectControl(overlappedObjectId);
 	overlappedControl->getCollisionRect(collisionRect);
 
@@ -564,7 +564,7 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 	verbState->_objectIds[0] = _bubble->addItem(0, 0x6005A);
 	verbState->_objectIds[1] = _bubble->addItem(0, 0x6005A);
 	verbState->_index = 0;
-	
+
 	int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
 	if (holdingObjectId) {
 		verbState->_verbId = 0x1B0003;
@@ -575,15 +575,15 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 	} else {
 		verbState->_verbId = 0x1B0002;
 	}
-	
+
 	uint32 sequenceId = kStruct10s[verbState->_verbId & 0xFFFF]._sequenceId2;
 	_bubble->show();
-	
+
 	control3 = _vm->_dict->getObjectControl(verbState->_objectIds[0]);
 	control3->startSequenceActor(sequenceId, 2, 0);
 	control3->appearActor();
 	control3->deactivateObject();
-	
+
 	verbState->_isBubbleVisible = true;
 	_vm->_input->discardAllEvents();
 
@@ -633,7 +633,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 		cursorPos = getBackgroundCursorPos(cursorPos);
 		bool foundOverlapped = false;
 		Control *overlappedControl = 0;
-		
+
 		if (cursorData._flags & 1) {
 			foundOverlapped = false;
 		} else if (_vm->getCurrentScene() == 0x1000D) {
@@ -643,7 +643,7 @@ void BbdouSpecialCode::cursorInteractControlRoutine(Control *cursorControl, uint
 			foundOverlapped = _vm->_controls->getOverlappedObject(cursorControl, cursorPos,
 				&overlappedControl, cursorData._verbState._minPriority);
 		}
-		
+
 		if (foundOverlapped) {
 			if (overlappedControl->_objectId != cursorData._currOverlappedObjectId) {
 				if (cursorData._verbState._isBubbleVisible)
@@ -954,10 +954,10 @@ bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32
 	static const uint32 kVerbIdsHE[] = {0x001B0003, 0x001B0001, 0};
 	static const uint32 kVerbIdsH9[] = {0x001B0003, 0};
 	static const uint32 kVerbIdsH8[] = {0x001B0003, 0x001B0001, 0};
-	
+
 	const uint32 *verbIds;
 	int interactMode = _objectInteractModeMap.getObjectInteractMode(overlappedObjectId);
-  
+
 	if (holdingObjectId) {
 		if (interactMode == 9)
 			verbIds = kVerbIdsH9;
@@ -973,7 +973,7 @@ bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32
 		else
 			verbIds = kVerbIdsEE;
 	}
-	
+
 	for (; *verbIds; ++verbIds)
 		if (*verbIds == verbId)
 			return true;
@@ -1019,7 +1019,7 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 	uint32 sceneId = _vm->getCurrentScene();
 	uint32 outVerbId, outObjectId2, outObjectId;
 	bool success = false;
-	
+
 	if (getCause(_vm->getCurrentScene(), verbId, objectId2, objectId, outVerbId, outObjectId2, outObjectId)) {
 		sceneId = _vm->getCurrentScene();
 		success = true;
@@ -1027,7 +1027,7 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 		sceneId = 0x10003;
 		success = true;
 	}
-	
+
 	if (!success)
 		return false;
 
@@ -1042,7 +1042,7 @@ bool BbdouSpecialCode::runCause(Control *cursorControl, CursorData &cursorData,
 
 	if (soundIndex)
 		playSoundEffect(soundIndex);
-	
+
 	cursorData._causeThreadId1 = _vm->causeTrigger(sceneId, outVerbId, outObjectId2, outObjectId, threadId);
 	cursorData._causeThreadId2 = cursorData._causeThreadId1;
 
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index 5d11e3b..df9b3f7 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -118,7 +118,7 @@ public:
 	BbdouCursor *_cursor;
 	BbdouBubble *_bubble;
 	BbdouInventory *_inventory;
-	
+
 	BbdouCredits *_credits;
 
 	// Salad
@@ -130,7 +130,7 @@ public:
 	uint _shooterObjectIdIndex;
 
 	BbdouFoodCtl *_foodCtl;
-	
+
 	ObjectInteractModeMap _objectInteractModeMap;
 
 	// Special code interface functions
diff --git a/engines/illusions/bbdou/bbdou_triggerfunctions.cpp b/engines/illusions/bbdou/bbdou_triggerfunctions.cpp
index 7c1ee47..6025d44 100644
--- a/engines/illusions/bbdou/bbdou_triggerfunctions.cpp
+++ b/engines/illusions/bbdou/bbdou_triggerfunctions.cpp
@@ -76,7 +76,7 @@ TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, u
 			triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
 			break;
 	}
-	return it;		
+	return it;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 06cfc28..0074036 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -154,7 +154,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_screen = new Screen16Bit(this, 640, 480);
 	_screenPalette = new NullScreenPalette();
 	_screenText = new ScreenText(this);
-	_input = new Input();	
+	_input = new Input();
 	_actorInstances = new ActorInstanceList(this);
 	_backgroundInstances = new BackgroundInstanceList(this);
 	_camera = new Camera(this);
@@ -176,7 +176,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 
 	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
 	_stack = new ScriptStack();
-	
+
 	// TODO Move to own class
 	_resGetCtr = 0;
 	_unpauseControlActorFlag = false;
@@ -291,7 +291,7 @@ int IllusionsEngine_BBDOU::updateScript(uint flags) {
 
 bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
 	uint32 codeOffs;
-	return 
+	return
 		_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
 		findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
 }
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index c1bd22b..ddbb827 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -61,7 +61,7 @@ public:
 protected:
 	virtual Common::Error run();
 	virtual bool hasFeature(EngineFeature f) const;
-public:	
+public:
 	ScriptMan *_scriptMan;
 	TriggerFunctions *_triggerFunctions;
 	Cursor *_cursor;
@@ -94,8 +94,8 @@ public:
 	Common::Point getNamedPointPosition(uint32 namedPointId);
 	uint32 getPriorityFromBase(int16 priority);
 	uint32 getCurrentScene();
-	uint32 getPrevScene();	
-	
+	uint32 getPrevScene();
+
 	bool isCursorObject(uint32 actorTypeId, uint32 objectId);
 	void setCursorControlRoutine(Control *control);
 	void placeCursorControl(Control *control, uint32 sequenceId);
@@ -117,7 +117,7 @@ public:
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
 	void resumeFromSavegame(uint32 callingThreadId);
-	
+
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
 	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
@@ -135,7 +135,7 @@ public:
 	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
 	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void reset();
-	
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index eb37025..764dda9 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -100,7 +100,7 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(45, opSetProperty);
 	OPCODE(46, opPlaceActor);
 	OPCODE(47, opFaceActor);
-	OPCODE(48, opFaceActorToObject);	
+	OPCODE(48, opFaceActorToObject);
 	OPCODE(49, opStartSequenceActor);
 	// 50 unused
 	OPCODE(51, opStartMoveActor);
@@ -219,9 +219,9 @@ void ScriptOpcodes_BBDOU::opStartTimerThread(ScriptThread *scriptThread, OpCall
 	ARG_INT16(maxDuration);
 	if (maxDuration)
 		duration += _vm->getRandom(maxDuration);
-		
+
 //duration = 1;//DEBUG Speeds up things
-		
+
 	if (isAbortable)
 		_vm->startAbortableTimerThread(duration, opCall._threadId);
 	else
@@ -332,7 +332,7 @@ void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCa
 		threadId = dthreadId;
 		dsceneId = 0;
 	}
-	
+
 	// NOTE Skipped checking for stalled resources
 	_vm->_input->discardAllEvents();
 	_vm->_prevSceneId = _vm->getCurrentScene();
@@ -381,13 +381,13 @@ void ScriptOpcodes_BBDOU::opExitCloseUpScene(ScriptThread *scriptThread, OpCall
 }
 
 void ScriptOpcodes_BBDOU::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
+	ARG_INT16(speed);
 	ARG_UINT32(objectId);
 	_vm->_camera->panCenterObject(objectId, speed);
 }
 
 void ScriptOpcodes_BBDOU::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
+	ARG_INT16(speed);
 	ARG_UINT32(objectId);
 	Control *control = _vm->_dict->getObjectControl(objectId);
 	Common::Point pos = control->getActorPosition();
@@ -395,16 +395,16 @@ void ScriptOpcodes_BBDOU::opPanToObject(ScriptThread *scriptThread, OpCall &opCa
 }
 
 void ScriptOpcodes_BBDOU::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
+	ARG_INT16(speed);
 	ARG_UINT32(namedPointId);
 	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
 	_vm->_camera->panToPoint(pos, speed, opCall._threadId);
 }
 
 void ScriptOpcodes_BBDOU::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(speed);	
-	ARG_INT16(x);	
-	ARG_INT16(y);	
+	ARG_INT16(speed);
+	ARG_INT16(x);
+	ARG_INT16(y);
 	_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
 }
 
@@ -432,7 +432,7 @@ void ScriptOpcodes_BBDOU::opSetCameraBoundsToMasterBg(ScriptThread *scriptThread
 }
 
 void ScriptOpcodes_BBDOU::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
+	ARG_INT16(index);
 	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
 	if (value <= 63)
 		_vm->_scriptResource->_blockCounters.set(index, value);
@@ -444,8 +444,8 @@ void ScriptOpcodes_BBDOU::opClearBlockCounter(ScriptThread *scriptThread, OpCall
 }
 
 void ScriptOpcodes_BBDOU::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(value);	
-	ARG_UINT32(propertyId);	
+	ARG_INT16(value);
+	ARG_UINT32(propertyId);
 	_vm->_scriptResource->_properties.set(propertyId, value != 0);
 }
 
@@ -521,7 +521,7 @@ void ScriptOpcodes_BBDOU::opSetActorPosition(ScriptThread *scriptThread, OpCall
 }
 
 void ScriptOpcodes_BBDOU::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(duration);	
+	ARG_INT16(duration);
 	ARG_UINT32(objectId);
 	ARG_UINT32(talkId);
 	ARG_UINT32(sequenceId1);
@@ -749,8 +749,8 @@ void ScriptOpcodes_BBDOU::opGetProperty(ScriptThread *scriptThread, OpCall &opCa
 }
 
 void ScriptOpcodes_BBDOU::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	ARG_INT16(compareOp);	
+	ARG_INT16(index);
+	ARG_INT16(compareOp);
 	ARG_INT16(rvalue);
 	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
 	bool compareResult = false;
@@ -793,19 +793,19 @@ void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall
 	ARG_UINT32(videoId);
 	ARG_UINT32(priority);
 	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
-	
+
 	//DEBUG Resume calling thread, later done by the video player
 	_vm->notifyThreadId(opCall._callerThreadId);
-	
+
 }
 
 void ScriptOpcodes_BBDOU::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
-	_vm->_stack->pop(); 
+	_vm->_stack->pop();
 }
 
 void ScriptOpcodes_BBDOU::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
 	int16 value = _vm->_stack->peek();
-	_vm->_stack->push(value); 
+	_vm->_stack->push(value);
 }
 
 void ScriptOpcodes_BBDOU::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index d8e0e39..5385103 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -71,7 +71,7 @@ protected:
 	void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
 	void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
-	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);	
+	void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetCameraBoundsToMasterBg(ScriptThread *scriptThread, OpCall &opCall);
 	void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
 	void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
@@ -134,7 +134,7 @@ protected:
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
 	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
-	
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/camera.cpp b/engines/illusions/camera.cpp
index 1f7ea32..7fdb219 100644
--- a/engines/illusions/camera.cpp
+++ b/engines/illusions/camera.cpp
@@ -132,7 +132,7 @@ void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
 
 	_activeState._panTargetPoint = getPtOffset(pt);
 	clipPanTargetPoint();
-	
+
 	if (panSpeed) {
 		_activeState._cameraMode = 5;
 		_activeState._panSpeed = panSpeed;
@@ -269,7 +269,7 @@ void Camera::update(uint32 currTime) {
 		updateMode3(currTime);
 		break;
 	}
-	
+
 	if (_activeState._cameraMode != 6) {
 
 		if (!isPanFinished() &&	updatePan(currTime)) {
@@ -389,7 +389,7 @@ void Camera::updateMode1(uint32 currTime) {
 		_activeState._panStartTime = _activeState._time28;
 		recalcPan(oldPanTime);
 	}
-	
+
 }
 
 void Camera::updateMode2(uint32 currTime) {
@@ -451,7 +451,7 @@ void Camera::updateMode3(uint32 currTime) {
 		recalcPan(currTime);
 		_activeState._cameraMode = 4;
 	}
-	
+
 }
 
 bool Camera::updatePan(uint32 currTime) {
diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index bbecdc0..63aea79 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -166,7 +166,7 @@ SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target,
 		Illusions::IllusionsEngine::kReadSaveHeaderError error;
 		error = Illusions::IllusionsEngine::readSaveHeader(in, true, header);
 		delete in;
-		if (error == Illusions::IllusionsEngine::kRSHENoError) {		
+		if (error == Illusions::IllusionsEngine::kRSHENoError) {
 			SaveStateDescriptor desc(slot, header.description);
 			// Slot 0 is used for the "Continue" save
 			desc.setDeletableFlag(slot != 0);
diff --git a/engines/illusions/duckman/duckman_credits.cpp b/engines/illusions/duckman/duckman_credits.cpp
index ccbde8d..804d55e 100644
--- a/engines/illusions/duckman/duckman_credits.cpp
+++ b/engines/illusions/duckman/duckman_credits.cpp
@@ -100,7 +100,7 @@ int DuckmanCredits::update(uint flags) {
 				uint16 wtext[128];
 				charToWChar(text, wtext, ARRAYSIZE(wtext));
 
-				FontResource *font = _vm->_dict->findFont(0x120001); 
+				FontResource *font = _vm->_dict->findFont(0x120001);
 				TextDrawer textDrawer;
 				WidthHeight dimensions;
 				uint16 *outText;
@@ -158,28 +158,28 @@ char *DuckmanCredits::readNextLine() {
 
 Common::Point DuckmanCredits::getItemPosition(int index) {
 	static const struct { int16 x, y; } kCreditsItemsPoints[] = {
-		{159, 200}, {158, 195}, {157, 190}, {156, 185}, {156, 180}, {157, 176}, 
-		{158, 172}, {159, 168}, {161, 164}, {162, 161}, {163, 158}, {163, 155}, 
-		{162, 152}, {161, 149}, {159, 147}, {158, 144}, {157, 142}, {156, 140}, 
-		{156, 138}, {157, 136}, {158, 134}, {159, 132}, {161, 130}, {162, 128}, 
-		{163, 127}, {163, 126}, {162, 125}, {161, 124}, {159, 123}, {158, 122}, 
-		{157, 121}, {156, 120}, {156, 119}, {157, 118}, {158, 117}, {159, 116}, 
-		{161, 115}, {162, 114}, {163, 113}, {163, 112}, {162, 111}, {161, 110}, 
-		{159, 109}, {158, 108}, {157, 107}, {156, 106}, {156, 105}, {157, 104}, 
-		{158, 103}, {159, 102}, {161, 101}, {162, 100}, {163,  99}, {163,  98}, 
-		{162,  97}, {161,  96}, {159,  95}, {158,  94}, {157,  93}, {156,  92}, 
-		{156,  91}, {157,  90}, {158,  89}, {159,  88}, {161,  87}, {162,  86}, 
-		{163,  85}, {163,  84}, {162,  83}, {161,  82}, {159,  81}, {158,  80}, 
+		{159, 200}, {158, 195}, {157, 190}, {156, 185}, {156, 180}, {157, 176},
+		{158, 172}, {159, 168}, {161, 164}, {162, 161}, {163, 158}, {163, 155},
+		{162, 152}, {161, 149}, {159, 147}, {158, 144}, {157, 142}, {156, 140},
+		{156, 138}, {157, 136}, {158, 134}, {159, 132}, {161, 130}, {162, 128},
+		{163, 127}, {163, 126}, {162, 125}, {161, 124}, {159, 123}, {158, 122},
+		{157, 121}, {156, 120}, {156, 119}, {157, 118}, {158, 117}, {159, 116},
+		{161, 115}, {162, 114}, {163, 113}, {163, 112}, {162, 111}, {161, 110},
+		{159, 109}, {158, 108}, {157, 107}, {156, 106}, {156, 105}, {157, 104},
+		{158, 103}, {159, 102}, {161, 101}, {162, 100}, {163,  99}, {163,  98},
+		{162,  97}, {161,  96}, {159,  95}, {158,  94}, {157,  93}, {156,  92},
+		{156,  91}, {157,  90}, {158,  89}, {159,  88}, {161,  87}, {162,  86},
+		{163,  85}, {163,  84}, {162,  83}, {161,  82}, {159,  81}, {158,  80},
 		{157,  79}, {156,  78}, {156,  77}, {157,  76}, {158,  75}, {159,  74},
-		{161,  73}, {162,  72}, {163,  71}, {163,  70}, {162,  69}, {161,  68}, 
-		{159,  67}, {158,  66}, {157,  64}, {156,  62}, {156,  60}, {157,  58}, 
-		{158,  56}, {159,  54}, {161,  52}, {162,  50}, {163,  40}, {163,  40}, 
-		{162,  40}, {161,  40}, {159,  40}, {158,  40}, {157,  40}, {156,  40}, 
-		{156,  40}, {157,  40}, {158,  40}, {159,  40}, {161,  40}, {162,  40}, 
-		{163,  40}, {163,  40}, {162,  40}, {161,  40}, {159,  40}, {158,  40}, 
-		{157,  40}, {156,  40}, {156,  40}, {157,  40}, {158,  40}, {159,  40}, 
-		{161,  40}, {162,  40}, {163,  40}, {163,  40}, {162,  40}, {161,  40}, 
-		{159,  40}, {158,  40}, { -1,  -1} 
+		{161,  73}, {162,  72}, {163,  71}, {163,  70}, {162,  69}, {161,  68},
+		{159,  67}, {158,  66}, {157,  64}, {156,  62}, {156,  60}, {157,  58},
+		{158,  56}, {159,  54}, {161,  52}, {162,  50}, {163,  40}, {163,  40},
+		{162,  40}, {161,  40}, {159,  40}, {158,  40}, {157,  40}, {156,  40},
+		{156,  40}, {157,  40}, {158,  40}, {159,  40}, {161,  40}, {162,  40},
+		{163,  40}, {163,  40}, {162,  40}, {161,  40}, {159,  40}, {158,  40},
+		{157,  40}, {156,  40}, {156,  40}, {157,  40}, {158,  40}, {159,  40},
+		{161,  40}, {162,  40}, {163,  40}, {163,  40}, {162,  40}, {161,  40},
+		{159,  40}, {158,  40}, { -1,  -1}
 	};
 
 	if (index < 0 || index >= ARRAYSIZE(kCreditsItemsPoints))
diff --git a/engines/illusions/duckman/duckman_credits.h b/engines/illusions/duckman/duckman_credits.h
index 039575c..ef52553 100644
--- a/engines/illusions/duckman/duckman_credits.h
+++ b/engines/illusions/duckman/duckman_credits.h
@@ -41,7 +41,7 @@ public:
 	DuckmanCredits(IllusionsEngine_Duckman *vm);
 	~DuckmanCredits();
 	void start();
-public:	
+public:
 	typedef Common::Array<CreditsItem> CreditsItems;
 	IllusionsEngine_Duckman *_vm;
 	uint32 _lastUpdateTicks;
diff --git a/engines/illusions/duckman/duckman_dialog.cpp b/engines/illusions/duckman/duckman_dialog.cpp
index f624ed8..60da222 100644
--- a/engines/illusions/duckman/duckman_dialog.cpp
+++ b/engines/illusions/duckman/duckman_dialog.cpp
@@ -131,7 +131,7 @@ void DuckmanDialogSystem::updateDialogState() {
 
 	Control *currOverlappedControl = _vm->_cursor._currOverlappedControl;
 	Control *newOverlappedControl;
-	
+
 	if (_vm->_controls->getDialogItemAtPos(_vm->_cursor._control, mousePos, &newOverlappedControl)) {
 		if (currOverlappedControl != newOverlappedControl) {
 			newOverlappedControl->setActorIndex(2);
diff --git a/engines/illusions/duckman/duckman_dialog.h b/engines/illusions/duckman/duckman_dialog.h
index 9c7cfad..cb4b4a1 100644
--- a/engines/illusions/duckman/duckman_dialog.h
+++ b/engines/illusions/duckman/duckman_dialog.h
@@ -42,7 +42,7 @@ public:
 	void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
 	void updateDialogState();
 public:
-	IllusionsEngine_Duckman *_vm;	
+	IllusionsEngine_Duckman *_vm;
 	Common::Array<DialogItem> _dialogItems;
 };
 
diff --git a/engines/illusions/duckman/duckman_inventory.h b/engines/illusions/duckman/duckman_inventory.h
index e1abbeb..05048f4 100644
--- a/engines/illusions/duckman/duckman_inventory.h
+++ b/engines/illusions/duckman/duckman_inventory.h
@@ -47,7 +47,7 @@ public:
 	DuckmanInventory(IllusionsEngine_Duckman *vm);
 	~DuckmanInventory();
 public:
-	IllusionsEngine_Duckman *_vm;	
+	IllusionsEngine_Duckman *_vm;
 	Common::Array<DMInventorySlot> _inventorySlots;
 	Common::Array<DMInventoryItem> _inventoyItems;
 	void initInventory();
diff --git a/engines/illusions/duckman/duckman_screenshakereffects.cpp b/engines/illusions/duckman/duckman_screenshakereffects.cpp
index 66bfaea..6ccb143 100644
--- a/engines/illusions/duckman/duckman_screenshakereffects.cpp
+++ b/engines/illusions/duckman/duckman_screenshakereffects.cpp
@@ -36,8 +36,8 @@ static const ScreenShakeEffect kShakerEffect0 = {
 };
 
 static const ScreenShakerPoint kShakerPoints1[] = {
-	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2}, 
-	{ 1,  2}, {0, -1} 
+	{-4, -5}, {4,  5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2},
+	{ 1,  2}, {0, -1}
 };
 
 static const ScreenShakeEffect kShakerEffect1 = {
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index ec85782..c420bd5 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -49,7 +49,7 @@ DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
 	_propertyTimers = new PropertyTimers(_vm);
 	_inventory = new DuckmanInventory(_vm);
 	_credits = new DuckmanCredits(_vm);
-	
+
 	_wasCursorHoldingElvisPoster = false;
 	_counter = 0;
 	_savedTempMasterSfxVolume = 16;
@@ -189,7 +189,7 @@ void DuckmanSpecialCode::spcUpdateTeleporterPosition(OpCall &opCall) {
 	int16 deltaX = 0;
 	int16 deltaY = 0;
 	uint32 sequenceId = 0;
-	
+
 	Control *control = _vm->getObjectControl(0x400C0);
 	switch (direction) {
 	case 1:
@@ -219,7 +219,7 @@ void DuckmanSpecialCode::spcUpdateTeleporterPosition(OpCall &opCall) {
 	default:
 		break;
 	}
-	
+
 	if (sequenceId) {
 		control->startSequenceActor(sequenceId, 2, opCall._threadId);
 		_teleporterPosition.x += deltaX;
diff --git a/engines/illusions/duckman/duckman_specialcode.h b/engines/illusions/duckman/duckman_specialcode.h
index a9fb7b9..694086b 100644
--- a/engines/illusions/duckman/duckman_specialcode.h
+++ b/engines/illusions/duckman/duckman_specialcode.h
@@ -42,7 +42,7 @@ public:
 	~DuckmanSpecialCode();
 	virtual void init();
 	virtual void run(uint32 specialCodeId, OpCall &opCall);
-public:	
+public:
 	typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
 	typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
 
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 7ffa312..e08d1b0 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -89,7 +89,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
 
 	_dict = new Dictionary();
-	
+
 	_resReader = new ResourceReaderGamArchive("duckman.gam");
 
 	_resSys = new ResourceSystem(this);
@@ -106,7 +106,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_screen = new Screen8Bit(this, 320, 200);
 	_screenPalette = new ScreenPalette(this);
 	_screenText = new ScreenText(this);
-	_input = new Input();	
+	_input = new Input();
 	_actorInstances = new ActorInstanceList(this);
 	_backgroundInstances = new BackgroundInstanceList(this);
 	_camera = new Camera(this);
@@ -119,7 +119,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_gameState = new Duckman_GameState(this);
 
 	_fader = new Fader();
-	
+
 	_dialogSys = new DuckmanDialogSystem(this);
 
 	_screen->setColorKey1(0);
@@ -130,7 +130,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	_scriptOpcodes = new ScriptOpcodes_Duckman(this);
 	_stack = new ScriptStack();
-	
+
 	// TODO Move to own class
 	_resGetCtr = 0;
 	_unpauseControlActorFlag = false;
@@ -230,9 +230,9 @@ Common::Error IllusionsEngine_Duckman::run() {
 	delete _resSys;
 	delete _resReader;
 	delete _dict;
-	
+
 	debug("Ok");
-	
+
 	return Common::kNoError;
 }
 
@@ -1015,7 +1015,7 @@ void IllusionsEngine_Duckman::updateGameState2() {
 	Control *overlappedControl;
 
 	_cursor._control->_actor->_position = cursorPos;
-	
+
 	foundOverlapped = _controls->getOverlappedObject(_cursor._control, convMousePos, &overlappedControl, 0);
 
 	if (cursorPos.y < 8 && !_camera->isAtPanLimit(1)) {
@@ -1188,7 +1188,7 @@ uint32 IllusionsEngine_Duckman::runTriggerCause(uint32 verbId, uint32 objectId2,
 
 	if (!getTriggerCause(verbId, objectId2, objectId, triggerThreadId))
 		return 0;
-		
+
 	playTriggerCauseSound(verbId, objectId2, objectId);
 
 	uint32 tempThreadId = newTempThreadId();
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 87ca932..feacd50 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -94,16 +94,16 @@ public:
 
 	Cursor_Duckman _cursor;
 	Control *_currWalkOverlappedControl;
-	
+
 	DuckmanDialogSystem *_dialogSys;
 
 	int _savedInventoryActorIndex;
 
 	ScreenShaker *_screenShaker;
 	DuckmanMenuSystem *_menuSystem;
-	
+
 	void initInput();
-	
+
 	void initUpdateFunctions();
 	int updateScript(uint flags);
 
@@ -156,7 +156,7 @@ public:
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
 	void resumeFromSavegame(uint32 callingThreadId);
-	
+
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp);
 	uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
@@ -180,9 +180,9 @@ public:
 	void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
 	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void reset();
-	
+
 	uint32 getObjectActorTypeId(uint32 objectId);
-	
+
 	Common::Point convertMousePos(Common::Point mousePos);
 	void startCursorSequence();
 	int getCursorActorIndex();
@@ -191,7 +191,7 @@ public:
 	bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
 	uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
 	void playTriggerCauseSound(uint32 verbId, uint32 objectId2, uint32 objectId);
-	
+
 	bool loadSavegameFromScript(int16 slotNum, uint32 callingThreadId);
 	bool saveSavegameFromScript(int16 slotNum, uint32 callingThreadId);
 	void activateSavegame(uint32 callingThreadId);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 4d63147..84f7974 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -42,7 +42,7 @@ DuckmanMenuSystem::~DuckmanMenuSystem() {
 
 void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
 	uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
-	
+
 	debug(0, "DuckmanMenuSystem::runMenu(%08X)", menuId);
 
 	setTimeOutDuration(duration, timeOutMenuChoiceIndex);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 1876b13..6b76a65 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -202,11 +202,11 @@ void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCal
 	ARG_INT16(maxDuration);
 	if (maxDuration)
 		duration += _vm->getRandom(maxDuration);
-		
+
 	//duration = 1;//DEBUG Speeds up things
 	//duration = 5;
 	//debug("duration: %d", duration);
-		
+
 	if (isAbortable)
 		_vm->startAbortableTimerThread(duration, opCall._callerThreadId);
 	else
@@ -284,9 +284,9 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 	ARG_UINT32(sceneId);
 	ARG_UINT32(threadId);
 	_vm->_input->discardAllEvents();
-	
+
 	debug(1, "changeScene(%08X, %08X)", sceneId, threadId);
-	
+
 	//DEBUG
 #if 0
 	if (dsceneId) {
@@ -295,7 +295,7 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op
 		dsceneId = 0;
 	}
 #endif
-	
+
 	if (_vm->_scriptResource->_properties.get(31)) {
 		_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
 	} else {
@@ -584,10 +584,10 @@ void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCa
 	ARG_UINT32(objectId);
 	// NOTE This has no attached objectId or priority
 	_vm->playVideo(0, objectId, 0, opCall._threadId);
-	
+
 	//DEBUG Resume calling thread, later done by the video player
 	_vm->notifyThreadId(opCall._threadId);
-	
+
 }
 
 void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
@@ -653,7 +653,7 @@ void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &op
 	ARG_INT16(timeOutDuration);
 	ARG_UINT32(menuId);
 	ARG_UINT32(timeOutMenuChoiceIndex);
-	
+
 	MenuChoiceOffsets menuChoiceOffsets;
 
 	// Load menu choices from the stack
@@ -661,11 +661,11 @@ void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &op
 		int16 choiceOffs = _vm->_stack->pop();
 		menuChoiceOffsets.push_back(choiceOffs);
 	} while (_vm->_stack->pop() == 0);
-	
-	_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs, 
+
+	_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs,
 		menuId, timeOutDuration, timeOutMenuChoiceIndex,
 		opCall._callerThreadId);
-	
+
 	//DEBUG Resume calling thread, later done by the video player
 	//_vm->notifyThreadId(opCall._callerThreadId);
 
@@ -713,7 +713,7 @@ void ScriptOpcodes_Duckman::opActivateButton(ScriptThread *scriptThread, OpCall
 }
 
 void ScriptOpcodes_Duckman::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
+	ARG_INT16(index);
 	byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
 	if (value <= 63)
 		_vm->_scriptResource->_blockCounters.set(index, value);
@@ -778,8 +778,8 @@ void ScriptOpcodes_Duckman::opGetProperty(ScriptThread *scriptThread, OpCall &op
 }
 
 void ScriptOpcodes_Duckman::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(index);	
-	ARG_INT16(compareOp);	
+	ARG_INT16(index);
+	ARG_INT16(compareOp);
 	ARG_INT16(rvalue);
 	int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
 	bool compareResult = false;
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h
index 0fae4b7..521a732 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.h
+++ b/engines/illusions/duckman/scriptopcodes_duckman.h
@@ -41,7 +41,7 @@ protected:
 	IllusionsEngine_Duckman *_vm;
 
 	// Opcodes
-	
+
 	void opNop(ScriptThread *scriptThread, OpCall &opCall);
 	void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
 	void opYield(ScriptThread *scriptThread, OpCall &opCall);
@@ -129,8 +129,8 @@ protected:
 	void opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
 	void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
-	
-#if 0	
+
+#if 0
 	void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
@@ -157,7 +157,7 @@ protected:
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
 	void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
 	void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
-#endif	
+#endif
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/fileresourcereader.cpp b/engines/illusions/fileresourcereader.cpp
index c707c65..baa5e47 100644
--- a/engines/illusions/fileresourcereader.cpp
+++ b/engines/illusions/fileresourcereader.cpp
@@ -30,7 +30,7 @@ namespace Illusions {
 
 byte *ResourceReaderFileReader::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
 	debug("ResourceReaderFileReader::readResource(%08X, %08X)", sceneId, resId);
-	
+
 	Common::String filename = buildResourceFilename(resId);
 	Common::File fd;
 	if (!fd.open(filename))
diff --git a/engines/illusions/gamarchive.cpp b/engines/illusions/gamarchive.cpp
index 098ad35..5c0debb 100644
--- a/engines/illusions/gamarchive.cpp
+++ b/engines/illusions/gamarchive.cpp
@@ -49,20 +49,20 @@ void GamArchive::loadDictionary() {
 	_groupCount = _fd->readUint32LE();
 	_groups = new GamGroupEntry[_groupCount];
 	uint32 *groupOffsets = new uint32[_groupCount];
-	
+
 	for (uint i = 0; i < _groupCount; ++i) {
 		_groups[i]._id = _fd->readUint32LE();
 		groupOffsets[i] = _fd->readUint32LE();
 	}
-	
+
 	for (uint i = 0; i < _groupCount; ++i) {
 		_fd->seek(groupOffsets[i]);
 		uint32 fileCount = _fd->readUint32LE();
 		_groups[i]._fileCount = fileCount;
 		_groups[i]._files = new GamFileEntry[fileCount];
-		
+
 		debug(8, "Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount);
-		
+
 		for (uint j = 0; j < fileCount; ++j) {
 			_groups[i]._files[j]._id = _fd->readUint32LE();
 			_groups[i]._files[j]._fileOffset = _fd->readUint32LE();
diff --git a/engines/illusions/gamresourcereader.cpp b/engines/illusions/gamresourcereader.cpp
index 18d16be..55c5150 100644
--- a/engines/illusions/gamresourcereader.cpp
+++ b/engines/illusions/gamresourcereader.cpp
@@ -40,5 +40,5 @@ ResourceReaderGamArchive::~ResourceReaderGamArchive() {
 byte *ResourceReaderGamArchive::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
 	return _gamArchive->readResource(sceneId, resId, dataSize);
 }
-	
+
 } // End of namespace Illusions
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index eae6056..2472137 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -29,7 +29,7 @@ namespace Illusions {
 void WidthHeight::load(Common::SeekableReadStream &stream) {
 	_width = stream.readSint16LE();
 	_height = stream.readSint16LE();
-	
+
 	debug(5, "WidthHeight::load() _width: %d; _height: %d",
 		_width, _height);
 }
@@ -39,7 +39,7 @@ void WidthHeight::load(Common::SeekableReadStream &stream) {
 void SurfInfo::load(Common::SeekableReadStream &stream) {
 	_pixelSize = stream.readUint32LE();
 	_dimensions.load(stream);
-	
+
 	debug(5, "SurfInfo::load() _pixelSize: %d",
 		_pixelSize);
 }
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 0178740..14fddc5 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -76,11 +76,11 @@ char *debugW2I(byte *wstr) {
 
 IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *gd) :
 	Engine(syst), _gameDescription(gd) {
-	
+
 	_random = new Common::RandomSource("illusions");
 
 	_rerunThreads = false;
-	
+
 	_isSaveAllowed = true; // TODO
 	_resumeFromSavegameRequested = false;
 	_savegameSlotNum = -1;
@@ -167,7 +167,7 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	Common::Point panPoint(0, 0);
 
 	uint32 currTime = getCurrentTime();
-	
+
 	_camera->update(currTime);
 	updateFader();
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 63983e1..9cf1e0b 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -98,14 +98,14 @@ private:
 	const IllusionsGameDescription *_gameDescription;
 	Graphics::PixelFormat _pixelFormat;
 public:
-	
+
 	Common::RandomSource *_random;
 	Dictionary *_dict;
 	ResourceSystem *_resSys;
 	BaseResourceReader *_resReader;
 	UpdateFunctions *_updateFunctions;
 	GameState *_gameState;
-	
+
 	void updateEvents();
 
 	Screen *_screen;
@@ -121,7 +121,7 @@ public:
 	SpecialCode *_specialCode;
 	ThreadList *_threads;
 	SoundMan *_soundMan;
-	
+
 	uint32 _nextTempThreadId;
 	bool _doScriptThreadInit;
 	ScriptStack *_stack;
@@ -199,7 +199,7 @@ public:
 	virtual Control *getObjectControl(uint32 objectId) = 0;
 	virtual Common::Point getNamedPointPosition(uint32 namedPointId) = 0;
 	virtual uint32 getPriorityFromBase(int16 priority) = 0;
-	virtual uint32 getPrevScene() = 0;	
+	virtual uint32 getPrevScene() = 0;
 	virtual uint32 getCurrentScene() = 0;
 	virtual bool isCursorObject(uint32 actorTypeId, uint32 objectId) = 0;
 	virtual void setCursorControlRoutine(Control *control) = 0;
@@ -211,7 +211,7 @@ public:
 	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
 	virtual void resumeFromSavegame(uint32 callingThreadId) = 0;
-		
+
 	// Savegame API
 
 	enum kReadSaveHeaderError {
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 79cbdd4..816b17d 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -175,7 +175,7 @@ void BaseMenuSystem::setMouseCursorToMenuItem(int menuItemIndex) {
 void BaseMenuSystem::calcMenuItemRect(uint menuItemIndex, WRect &rect) {
 	FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
 	int charHeight = font->getCharHeight() + font->getLineIncr();
-	
+
 	_vm->_screenText->getTextInfoPosition(rect._topLeft);
 	if (_activeMenu->_backgroundColor) {
 		rect._topLeft.y += 4;
@@ -203,7 +203,7 @@ bool BaseMenuSystem::calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt)
 bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex) {
 	WRect rect;
 	calcMenuItemRect(1, rect);
-	
+
 	uint index = _hoveredMenuItemIndex3 + (pt.y - rect._topLeft.y) / (rect._bottomRight.y - rect._topLeft.y);
 
 	if (pt.y < rect._topLeft.y || pt.x < rect._topLeft.x || pt.x > rect._bottomRight.x ||
@@ -248,12 +248,12 @@ void BaseMenuSystem::initActorHoverBackground() {
 	}
 	placeActorHoverBackground();
 	v0->appearActor();
-}	
+}
 
 void BaseMenuSystem::placeActorHoverBackground() {
 	Control *v0 = _vm->getObjectControl(0x4013E);
 	v0->fillActor(0);
-	
+
 	WidthHeight textInfoDimensions;
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 
@@ -303,12 +303,12 @@ void BaseMenuSystem::initActorTextColorRect() {
 void BaseMenuSystem::placeActorTextColorRect() {
 	Control *v0 = _vm->getObjectControl(0x40143);
 	v0->fillActor(0);
-	
+
 	Common::Point textInfoPosition;
 	WidthHeight textInfoDimensions;
 	_vm->_screenText->getTextInfoPosition(textInfoPosition);
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
-	
+
 	if (_activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor) {
 		textInfoDimensions._width -= 2;
 		textInfoDimensions._height -= 6;
@@ -316,7 +316,7 @@ void BaseMenuSystem::placeActorTextColorRect() {
 
 	v0->setActorPosition(textInfoPosition);
 	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_textColor);
-	
+
 }
 
 void BaseMenuSystem::hideActorTextColorRect() {
@@ -326,23 +326,23 @@ void BaseMenuSystem::hideActorTextColorRect() {
 }
 
 void BaseMenuSystem::openMenu(BaseMenu *menu) {
-	
+
 	_isActive = true;
 	_menuStack.clear();
-	
+
 	_cursorInitialVisibleFlag = initMenuCursor();
 	_savedCursorPos = _vm->_input->getCursorPosition();
 	_savedGameState = getGameState();
 	Control *cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
 	_savedCursorActorIndex = cursorControl->_actor->_actorIndex;
 	_savedCursorSequenceId = cursorControl->_actor->_sequenceId;
-	
+
 	setMenuCursorNum(1);
-	
+
 	setGameState(4);
-	
+
 	activateMenu(menu);
-	
+
 	_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
 	_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
 	setMouseCursorToMenuItem(_hoveredMenuItemIndex);
@@ -381,13 +381,13 @@ void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mouseP
 
 	MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
 	menuItem->executeAction(mousePos);
-	
+
 }
 
 uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 	MenuTextBuilder *menuTextBuilder = new MenuTextBuilder();
 	uint lineCount = 0;
-	
+
 	for (uint i = 0; i < menu->getHeaderLinesCount(); ++i) {
 		menuTextBuilder->appendString(menu->getHeaderLine(i));
 		menuTextBuilder->appendNewLine();
@@ -399,7 +399,7 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 			menuTextBuilder->appendNewLine();
 		++lineCount;
 	}
-	
+
 	menuTextBuilder->finalize();
 
 	uint16 *text = menuTextBuilder->getText();
@@ -414,11 +414,11 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 	uint flags = TEXT_FLAG_LEFT_ALIGN;
 	if (menu->_backgroundColor != menu->_borderColor)
 		flags |= TEXT_FLAG_BORDER_DECORATION;
-		
+
 	WidthHeight dimensions;
 	dimensions._width = 300;
 	dimensions._height = 180;
-	
+
 	uint16 *outTextPtr;
 	if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_backgroundColor, menu->_borderColor, 0xFF, 0xFF, 0xFF, outTextPtr)) {
 		--lineCount;
@@ -436,10 +436,10 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 void BaseMenuSystem::update(Control *cursorControl) {
 	Common::Point mousePos = _vm->_input->getCursorPosition();
 	setMousePos(mousePos);
-	
+
 	uint newHoveredMenuItemIndex;
 	bool resetTimeOut = false;
-	
+
 	if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex)) {
 		if (newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
 			if (_hoveredMenuItemIndex == 0)
@@ -487,7 +487,7 @@ void BaseMenuSystem::update(Control *cursorControl) {
 		updateActorHoverBackground();
 		playSoundEffect(0xC);
 	}
-	
+
 	updateTimeOut(resetTimeOut);
 }
 
@@ -534,7 +534,7 @@ void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
 	} else if (!_isTimeOutReached) {
 		_isTimeOutReached = true;
 	}
-	
+
 	if (!_isTimeOutReached) {
 		if (resetTimeOut) {
 			_timeOutStartTime = getCurrentTime();
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index ff85ca6..dd45129 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -176,7 +176,7 @@ protected:
 
 */
 
-class MenuTextBuilder {	
+class MenuTextBuilder {
 public:
 	MenuTextBuilder();
 	void appendString(const Common::String &value);
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index 04ee493..e3ea07b 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -43,7 +43,7 @@ PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point d
 	PointArray *foundPath = new PointArray();
 	line.p0 = sourcePt;
 	line.p1 = destPt;
-	
+
 	if (_walkRects && _walkPoints && isLineBlocked(line)) {
 		Common::Point nextStartPt = sourcePt, outPt;
 
@@ -325,7 +325,7 @@ int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common:
 		(delta2 <= 0 && (delta3 > 0 || delta2 > delta3)) ||
 		(delta2 > 0 && (delta3 < 0 || delta2 < delta3)))
 		return 3;
-	
+
 	if (!outPoint)
 		return 1;
 
@@ -335,7 +335,7 @@ int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common:
 	int v15 = sourceDeltaX * delta1, v18 = sourceDeltaY * delta1;
 	int v16 = 0;
 	int v17 = 0;
-	
+
 	if ((v15 >= 0 && delta2 >= 0) || (v15 < 0 && delta2 < 0)) {
 		v16 = delta2 / 2;
 		v17 = delta2 / 2;
@@ -345,12 +345,12 @@ int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common:
 	}
 
 	outPoint->x = sourceLine.p0.x + (v15 + v16) / delta2;
-	
+
 	if ((v18 >= 0 && delta2 < 0) || (v18 < 0 && delta2 >= 0))
 		v17 = -v17;
 
 	outPoint->y = sourceLine.p0.y + (v18 + v17) / delta2;
-	
+
 	return 1;
 }
 
diff --git a/engines/illusions/pathfinder.h b/engines/illusions/pathfinder.h
index 47cc057..2b56b2d 100644
--- a/engines/illusions/pathfinder.h
+++ b/engines/illusions/pathfinder.h
@@ -54,7 +54,7 @@ protected:
 	bool findValidDestLine(Common::Point &destPt);
 	void findValidDestPt(Common::Point &destPt);
 	WidthHeight calcRectDimensions(PathLine &rect);
-	void adjustRectDimensions(WidthHeight &dimensions);	
+	void adjustRectDimensions(WidthHeight &dimensions);
 	void swapDimensions(WidthHeight &dimensions);
 	void clipLineToBg(Common::Point &destPt, WidthHeight &rectDimensions, PathLine &outDestLine);
 	void findDeltaPt(Common::Point pt, Common::Point &outDeltaPt);
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index 4212457..13c8abc 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -111,7 +111,7 @@ void ActorResource::load(Resource *resource) {
 
 	_totalSize = stream.readUint32LE();
 
-	// Load actor types	
+	// Load actor types
 	stream.seek(0x06);
 	uint actorTypesCount = stream.readUint16LE();
 	stream.seek(0x10);
@@ -137,7 +137,7 @@ void ActorResource::load(Resource *resource) {
 		_sequences.push_back(sequence);
 	}
 
-	// Load frames	
+	// Load frames
 	stream.seek(0x0A);
 	uint framesCount = stream.readUint16LE();
 	stream.seek(0x18);
@@ -149,7 +149,7 @@ void ActorResource::load(Resource *resource) {
 		frame.load(data, stream);
 		_frames.push_back(frame);
 	}
-	
+
 	// Load named points
 	if (resource->_gameId == kGameIdBBDOU) {
 		// The count isn't stored explicitly so calculate it
@@ -157,7 +157,7 @@ void ActorResource::load(Resource *resource) {
 		stream.seek(0x20);
 		_namedPoints.load(namedPointsCount, stream);
 	}
-	
+
 	debug(1, "ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
 }
 
@@ -240,7 +240,7 @@ void ActorInstance::registerResources() {
 		Sequence *sequence = &_actorResource->_sequences[i];
 		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
 	}
-}	
+}
 
 void ActorInstance::unregisterResources() {
 	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i)
diff --git a/engines/illusions/resources/actorresource.h b/engines/illusions/resources/actorresource.h
index 34b4702..d4f798b 100644
--- a/engines/illusions/resources/actorresource.h
+++ b/engines/illusions/resources/actorresource.h
@@ -107,7 +107,7 @@ public:
 protected:
 	void initActorTypes(int gameId);
 	void registerResources();
-	void unregisterResources();	
+	void unregisterResources();
 };
 
 class ActorInstanceList {
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index a35f06e..27d73a7 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -223,10 +223,10 @@ BackgroundResource::~BackgroundResource() {
 
 void BackgroundResource::load(byte *data, uint32 dataSize) {
 	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
-	
+
 	stream.seek(8);
 	_paletteIndex = stream.readUint16LE();
-	
+
 	// Load background pixels
 	stream.seek(0x0A);
 	_bgInfosCount = stream.readUint16LE();
@@ -295,7 +295,7 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 		stream.seek(backgroundObjectsOffs + i * 12);
 		_backgroundObjects[i].load(data, stream);
 	}
-	
+
 	// Load path walk points
 	stream.seek(0x0E);
 	_pathWalkPointsCount = stream.readUint16LE();
@@ -392,11 +392,11 @@ void BackgroundInstance::load(Resource *resource) {
 	_bgRes = backgroundResource;
 	_sceneId = resource->_sceneId;
 	initSurface();
-	
+
 	// Insert background objects
 	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
 		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
-		
+
 	registerResources();
 
 	// TODO camera_fadeClear();
@@ -451,13 +451,13 @@ void BackgroundInstance::registerResources() {
 		_vm->_dict->addSequence(sequence->_sequenceId, sequence);
 	}
 }
-	
+
 void BackgroundInstance::unregisterResources() {
 	for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
 		Sequence *sequence = &_bgRes->_regionSequences[i];
 		_vm->_dict->removeSequence(sequence->_sequenceId);
 	}
-}	
+}
 
 void BackgroundInstance::initSurface() {
 	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
diff --git a/engines/illusions/resources/backgroundresource.h b/engines/illusions/resources/backgroundresource.h
index d0c0c76..fba2e7e 100644
--- a/engines/illusions/resources/backgroundresource.h
+++ b/engines/illusions/resources/backgroundresource.h
@@ -150,7 +150,7 @@ public:
 
 	uint _bgInfosCount;
 	BgInfo *_bgInfos;
-	
+
 	uint _priorityLayersCount;
 	PriorityLayer *_priorityLayers;
 
@@ -173,7 +173,7 @@ public:
 	PathWalkRects *_pathWalkRects;
 
 	NamedPoints _namedPoints;
-	
+
 	uint _palettesCount;
 	Palette *_palettes;
 
@@ -197,8 +197,8 @@ public:
 	Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
 	CameraState _savedCameraState;
 	byte *_savedPalette;
-	void registerResources();	
-	void unregisterResources();	
+	void registerResources();
+	void unregisterResources();
 	void initSurface();
 	void freeSurface();
 	void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index e2108a6..08433fe 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -155,7 +155,7 @@ void TriggerCause::load(Common::SeekableReadStream &stream) {
 	_verbId = stream.readUint32LE();
 	_objectId2 = stream.readUint32LE();
 	_codeOffs = stream.readUint32LE();
-	
+
 	debug(2, "TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
 		_verbId, _objectId2, _codeOffs);
 }
@@ -283,10 +283,10 @@ void ScriptResource::load(Resource *resource) {
 	_dataSize = resource->_dataSize;
 
 	Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
-	
+
 	uint32 objectMapOffs = 0, sceneInfosOffs = 0;
 	_objectMapCount = 0;
-	
+
 	if (resource->_gameId == kGameIdBBDOU) {
 		sceneInfosOffs = 0x18;
 	} else if (resource->_gameId == kGameIdDuckman) {
@@ -294,7 +294,7 @@ void ScriptResource::load(Resource *resource) {
 			_soundIds[i] = stream.readUint32LE();
 		sceneInfosOffs = 0x8C;
 	}
-	
+
 	stream.skip(4); // Skip unused
 
 	// Read item counts
@@ -313,17 +313,17 @@ void ScriptResource::load(Resource *resource) {
 	if (resource->_gameId == kGameIdDuckman)
 		objectMapOffs = stream.readUint32LE(); //TODO Is this needed for BBDOU?
 	uint32 codeTblOffs = stream.readUint32LE();
-	
+
 	debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _sceneInfosCount: %d; _objectMapCount: %d",
 		propertiesCount, blockCountersCount, _codeCount, _sceneInfosCount, _objectMapCount);
 	debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X; objectMapOffs: %08X",
 		propertiesOffs, blockCountersOffs, codeTblOffs, objectMapOffs);
 	// Init properties
 	_properties.init(propertiesCount, _data + propertiesOffs);
-	
+
 	// Init blockcounters
 	_blockCounters.init(blockCountersCount, _data + blockCountersOffs);
-	
+
 	_codeOffsets = new uint32[_codeCount];
 	stream.seek(codeTblOffs);
 	for (uint i = 0; i < _codeCount; ++i)
@@ -336,7 +336,7 @@ void ScriptResource::load(Resource *resource) {
 		stream.seek(sceneInfoOffs);
 		_sceneInfos[i].load(_data, stream);
 	}
-	
+
 	if (_objectMapCount > 0) {
 		_objectMap = new uint32[_objectMapCount];
 		stream.seek(objectMapOffs);
@@ -345,7 +345,7 @@ void ScriptResource::load(Resource *resource) {
 			stream.skip(4);
 		}
 	}
-	
+
 	if (resource->_gameId == kGameIdDuckman) {
 		stream.seek(0x6C);
 		_mainActorObjectId = stream.readUint32LE();
@@ -353,7 +353,7 @@ void ScriptResource::load(Resource *resource) {
 		stream.seek(0);
 		_mainActorObjectId = stream.readUint32LE();
 	}
-	
+
 	if (resource->_gameId == kGameIdDuckman)
 		fixupSceneInfosDuckman();
 
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index 4f6644f..bf43db7 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -89,14 +89,14 @@ void ResourceSystem::loadResource(uint32 resId, uint32 sceneId, uint32 threadId)
 		debug(1, "ResourceSystem::loadResource() kRlfLoadFile");
 		resource->loadData(_vm->_resReader);
 	}
-	
+
 	resourceLoader->load(resource);
-	
+
 	if (resourceLoader->isFlag(kRlfFreeDataAfterLoad)) {
 		debug(1, "ResourceSystem::loadResource() kRlfFreeDataAfterLoad");
 		resource->unloadData();
 	}
-	
+
 	resource->_loaded = true;
 
 	_resources.push_back(resource);
@@ -105,7 +105,7 @@ void ResourceSystem::loadResource(uint32 resId, uint32 sceneId, uint32 threadId)
 
 void ResourceSystem::unloadResourceById(uint32 resId) {
 	Resource *resource = getResource(resId);
-	if (resource) 
+	if (resource)
 		unloadResource(resource);
 }
 
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index a54cda6..56e5f2d 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -91,14 +91,14 @@ public:
 	~ResourceSystem();
 
 	void addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader);
-	
+
 	// TODO Handle threadId in caller as well as pausing of timer
 	void loadResource(uint32 resId, uint32 sceneId, uint32 threadId);
 	void unloadResourceById(uint32 resId);
 	void unloadResourcesBySceneId(uint32 sceneId);
 	void unloadSceneResources(uint32 sceneId1, uint32 sceneId2);
 	Resource *getResource(uint32 resId);
-	
+
 protected:
 	typedef Common::HashMap<uint32, BaseResourceLoader*> ResourceLoadersMap;
 	typedef ResourceLoadersMap::iterator ResourceLoadersMapIterator;
@@ -143,7 +143,7 @@ protected:
 	};
 
 	void unloadResource(Resource *resource);
-	
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp
index 50a728a..cc63b18 100644
--- a/engines/illusions/saveload.cpp
+++ b/engines/illusions/saveload.cpp
@@ -92,7 +92,7 @@ bool IllusionsEngine::savegame(const char *filename, const char *description) {
 	out->writeUint32LE(saveTime);
 	out->writeUint32LE(playTime);
 	// Header end
-	
+
 	_gameState->write(out);
 
 	out->finalize();
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index c234cde..f935315 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -75,7 +75,7 @@ SpriteDrawQueue::~SpriteDrawQueue() {
 bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 
 	// Check if the sprite has finished decompressing
-	if (item->_kind != 0 && (*item->_drawFlags & 1)) {		
+	if (item->_kind != 0 && (*item->_drawFlags & 1)) {
 		insert(item, item->_priority);
 		return false;
 	}
@@ -83,11 +83,11 @@ bool SpriteDrawQueue::draw(SpriteDrawQueueItem *item) {
 	if (!_screen->isDisplayOn()) {
 		if (item->_drawFlags)
 			*item->_drawFlags &= ~4;
-		return true;			
-	}	
+		return true;
+	}
 
 	Common::Rect srcRect, dstRect;
-	
+
 	// Check if the sprite is on-screen
 	if (!calcItemRect(item, srcRect, dstRect))
 		return true;
@@ -172,12 +172,12 @@ bool SpriteDrawQueue::calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcR
 	srcRect.top = 0;
 	srcRect.right = item->_dimensions._width;
 	srcRect.bottom = item->_dimensions._height;
-	
+
 	dstRect.left = item->_drawPosition.x - item->_scale * item->_controlPosition.x / 100;
 	dstRect.top = item->_drawPosition.y - item->_scale * item->_controlPosition.y / 100;
 	dstRect.right = item->_drawPosition.x + item->_scale * (item->_dimensions._width - item->_controlPosition.x) / 100;
 	dstRect.bottom = item->_drawPosition.y + item->_scale * (item->_dimensions._height - item->_controlPosition.y) / 100;
-	
+
 	if (_screen->_isScreenOffsetActive) {
 		dstRect.left += _screen->_screenOffsetPt.x;
 		dstRect.right += _screen->_screenOffsetPt.x;
@@ -392,7 +392,7 @@ Screen::~Screen() {
 Graphics::Surface *Screen::allocSurface(int16 width, int16 height) {
 	Graphics::Surface *surface = new Graphics::Surface();
 	surface->create(width, height, _vm->_system->getScreenFormat());
-	return surface; 
+	return surface;
 }
 
 Graphics::Surface *Screen::allocSurface(SurfInfo &surfInfo) {
@@ -457,7 +457,7 @@ void Screen8Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 	int processedSize = 0;
 	int xincr, x, xstart;
 	int yincr, y;
-	
+
 	*item->_drawFlags &= ~1;
 
 	// Safeguard
@@ -468,7 +468,7 @@ void Screen8Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 			item->_surface->w, item->_surface->h);
 		return;
 	}
-	
+
 	if (item->_flags & 1) {
 		x = xstart = item->_dimensions._width - 1;
 		xincr = -1;
@@ -577,7 +577,7 @@ void Screen8Bit::drawSurfaceUnscaled(int16 destX, int16 destY, Graphics::Surface
 				else
 					*dst = pixel;
 			}
-			++dst;				
+			++dst;
 		}
 	}
 }
@@ -599,7 +599,7 @@ void Screen8Bit::drawSurfaceScaled(Common::Rect &dstRect, Graphics::Surface *sur
 		skipX = (dstWidth < srcWidth) ? 0 : dstWidth / (2*srcWidth) + 1;
 		w -= skipX;
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcY);
-		byte *dstRow = dst; 
+		byte *dstRow = dst;
 		while (w-- > 0) {
 			const byte pixel = *src;
 			if (pixel != 0) {
@@ -646,7 +646,7 @@ void Screen16Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 	int processedSize = 0;
 	int xincr, x, xstart;
 	int yincr, y;
-	
+
 	*item->_drawFlags &= ~1;
 
 	// Safeguard
@@ -657,7 +657,7 @@ void Screen16Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 			item->_surface->w, item->_surface->h);
 		return;
 	}
-	
+
 	if (item->_flags & 1) {
 		x = xstart = item->_dimensions._width - 1;
 		xincr = -1;
@@ -673,9 +673,9 @@ void Screen16Bit::decompressSprite(SpriteDecompressQueueItem *item) {
 		y = 0;
 		yincr = 1;
 	}
-	
+
 	byte *dst = (byte*)dstSurface->getBasePtr(x, y);
-	
+
 	while (processedSize < dstSize) {
 		int16 op = READ_LE_UINT16(src);
 		src += 2;
@@ -858,7 +858,7 @@ void Screen16Bit::drawSurface21(Common::Rect &dstRect, Graphics::Surface *surfac
 		skipX = (dstWidth < srcWidth) ? 0 : dstWidth / (2*srcWidth) + 1;
 		w -= skipX;
 		byte *src = (byte*)surface->getBasePtr(srcRect.left, srcY);
-		byte *dstRow = dst; 
+		byte *dstRow = dst;
 		while (w-- > 0) {
 			uint16 pixel = READ_LE_UINT16(src);
 			if (pixel != _colorKey1)
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 1da1d5b..4a89c79 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -92,7 +92,7 @@ protected:
 		}
 	};
 	Screen *_screen;
-	SpriteDrawQueueList _queue;	
+	SpriteDrawQueueList _queue;
 	void insert(SpriteDrawQueueItem *item, uint32 priority);
 	bool calcItemRect(SpriteDrawQueueItem *item, Common::Rect &srcRect, Common::Rect &dstRect);
 };
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index de664d6..0f811df 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -101,13 +101,13 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 
 bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions, Common::Point offsPt, uint flags,
 	uint16 backgroundColor, uint16 borderColor, byte colorR, byte colorG, byte colorB, uint16 *&outTextPtr) {
-	
+
 	if (!_screenTexts.empty()) {
 		ScreenTextEntry *screenText = _screenTexts.back();
 		screenText->_info._position = _position;
 		freeTextSurface();
 	}
-	
+
 	ScreenTextEntry *screenText = new ScreenTextEntry();
 	screenText->_info._fontId = fontId;
 	screenText->_info._dimensions = dimensions;
@@ -169,13 +169,13 @@ void ScreenText::removeText() {
 }
 
 void ScreenText::clearText() {
-	
+
 	if (!_screenTexts.empty()) {
 		ScreenTextEntry *screenText = _screenTexts.back();
 		screenText->_info._position = _position;
 		freeTextSurface();
 	}
-	
+
 	ScreenTextEntry *screenText = new ScreenTextEntry();
 	screenText->_info._fontId = 0;
 	_screenTexts.push_back(screenText);
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 8ba2586..9314a60 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -60,7 +60,7 @@ protected:
 };
 
 // Convenience macros
-#define	ARG_SKIP(x) opCall.skip(x); 
+#define	ARG_SKIP(x) opCall.skip(x);
 #define ARG_BYTE(name) byte name = opCall.readByte(); debug(5, "ARG_BYTE(" #name " = %d)", name);
 #define ARG_INT16(name) int16 name = opCall.readSint16(); debug(5, "ARG_INT16(" #name " = %d)", name);
 #define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(5, "ARG_UINT32(" #name " = %08X)", name);
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 6c5b3ca..5fe5281 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -51,7 +51,7 @@ protected:
 	void opEndSequence(Control *control, OpCall &opCall);
 	void opIncFrameDelay(Control *control, OpCall &opCall);
 	void opSetRandomFrameDelay(Control *control, OpCall &opCall);
-	void opSetFrameSpeed(Control *control, OpCall &opCall);	
+	void opSetFrameSpeed(Control *control, OpCall &opCall);
 	void opJump(Control *control, OpCall &opCall);
 	void opJumpRandom(Control *control, OpCall &opCall);
 	void opGotoSequence(Control *control, OpCall &opCall);
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index a5679b0..31839e0 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -127,7 +127,7 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 				currWordWidth = 0;
 			}
 		}
-		
+
 		while (lineBreak) {
 
 			currLineWidth -= _font->_widthC;
@@ -143,7 +143,7 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 				} else {
 					textPosX = x;
 				}
-				
+
 				_textLines.push_back(TextLine(lineStartText, currLineLen, textPosX, textPosY));
 
 				if (*currText == 13) {
@@ -187,7 +187,7 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 		}
 
 	}
-	
+
 	_dimensions->_width = maxLineWidth;
 	_dimensions->_height = textPosY - _font->_lineIncr;
 
diff --git a/engines/illusions/textdrawer.h b/engines/illusions/textdrawer.h
index 68b28e0..bbecd98 100644
--- a/engines/illusions/textdrawer.h
+++ b/engines/illusions/textdrawer.h
@@ -54,9 +54,9 @@ protected:
 	Common::Point _offsPt;
 	uint _textFlags;
 	Graphics::Surface *_surface;
-	
+
 	Common::Array<TextLine> _textLines;
-	
+
 	bool textHasChar(uint16 c);
 	int16 getSpaceWidth();
 	int16 getCharWidth(uint16 c);
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index d984af2..3c327ba 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -307,11 +307,11 @@ void ThreadList::killThread(uint32 threadId) {
 
 	if (!threadId)
 		return;
-	
+
 	Thread *thread = findThread(threadId);
 	if (!thread)
 		return;
-		
+
 	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		Thread *childThread = *it;
 		if (childThread->_callingThreadId == threadId)
diff --git a/engines/illusions/threads/abortablethread.cpp b/engines/illusions/threads/abortablethread.cpp
index d2fc807..84cd840 100644
--- a/engines/illusions/threads/abortablethread.cpp
+++ b/engines/illusions/threads/abortablethread.cpp
@@ -31,7 +31,7 @@ namespace Illusions {
 
 AbortableThread::AbortableThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 	uint32 scriptThreadId, byte *scriptCodeIp)
-	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId), 
+	: Thread(vm, threadId, callingThreadId, notifyFlags), _scriptThreadId(scriptThreadId),
 	_scriptCodeIp(scriptCodeIp), _status(1) {
 	_type = kTTAbortableThread;
 	_sceneId = _vm->getCurrentScene();
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 9e94ef6..d20f94a 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -57,9 +57,9 @@ TalkThread::TalkThread(IllusionsEngine *vm, uint32 threadId, uint32 callingThrea
 		_status = 2;
 	else
 		_status = 3;
-	
+
 	_flags = 0x0E;
-	
+
 	_durationMult = _vm->clipTextDuration(_vm->getSubtitleDuration());
 	_textDuration = _durationMult;
 	_defDurationMult = _vm->clipTextDuration(240);
@@ -135,7 +135,7 @@ int TalkThread::onUpdate() {
 			return kTSYield;
 		_status = 5;
 		// Fallthrough to status 5
-		
+
 	case 5:
 		if (!(_flags & 8))
 			refreshText();
@@ -239,7 +239,7 @@ if (true) {
 		return kTSTerminate;
 
 	}
-	
+
 	return kTSTerminate;
 
 }
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index fdfcc0a..a2e9eba 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -55,11 +55,11 @@ TalkThread_Duckman::TalkThread_Duckman(IllusionsEngine_Duckman *vm, uint32 threa
 		_status = 1;
 	else
 		_status = 2;
-		
+
 	_durationMult = _vm->clipTextDuration(_vm->_subtitleDuration);
 	_textDuration = _durationMult;
 	_defDurationMult = _vm->clipTextDuration(240);
-	
+
 	_sceneId = _vm->getCurrentScene();
 
 }
@@ -101,14 +101,14 @@ int TalkThread_Duckman::onUpdate() {
 		if (_objectId == 0 || _durationMult == 0)
 			_flags |= 8;
 		_status = 3;
-		// Fallthrough to status 3 
+		// Fallthrough to status 3
 
 	case 3:
 		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
 			return kTSYield;
 		_status = 4;
 		// Fallthrough to status 4
-		
+
 	case 4:
 		if (!(_flags & 8) ) {
 			uint32 actorTypeId = _vm->getObjectActorTypeId(_objectId);
@@ -196,7 +196,7 @@ int TalkThread_Duckman::onUpdate() {
 			_flags |= 2;
 		}
 		return kTSTerminate;
-		
+
 	}
 
 	return kTSTerminate;


Commit: d36eae4c17f15fe05d5a18508dd0b6fddfa1c96f
    https://github.com/scummvm/scummvm/commit/d36eae4c17f15fe05d5a18508dd0b6fddfa1c96f
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix typo in palette cycle logic.
Fix uninitialised local variable

Changed paths:
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/screen.cpp


diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 1c4c2c8..e35a2e8 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -290,9 +290,8 @@ void BbdouInventory::clear() {
 
 void BbdouInventory::cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId) {
 	// TODO
-	debug("cause0x1B0001");
 	uint32 foundSceneId, foundVerbId, foundObjectId2, foundObjectId;
-	bool found;
+	bool found = false;
 	InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
 	InventorySlot *inventorySlot = inventoryBag->getInventorySlot(triggerFunction->_objectId);
 	uint32 objectId = inventorySlot->_inventoryItem->_objectId;
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index f935315..b2a970c 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -272,7 +272,7 @@ void ScreenPalette::shiftPalette(int16 fromIndex, int16 toIndex) {
 		r = _mainPalette[3 * toIndex + 0];
 		g = _mainPalette[3 * toIndex + 1];
 		b = _mainPalette[3 * toIndex + 2];
-		for (int16 i = toIndex + 1; i < fromIndex; +i) { //TODO fix this. +i
+		for (int16 i = toIndex + 1; i < fromIndex; ++i) {
 			byte *dst = &_mainPalette[3 * i];
 			byte *src = &_mainPalette[3 * (i + 1)];
 			dst[0] = src[0];


Commit: 9ad048f8a6c6380fe8ca6534afcae30d3b1ada37
    https://github.com/scummvm/scummvm/commit/9ad048f8a6c6380fe8ca6534afcae30d3b1ada37
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix formatting
Remove debug code from bbdou menu item select callback

Changed paths:
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/menusystem.cpp


diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 764dda9..0939583 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -676,8 +676,6 @@ void ScriptOpcodes_BBDOU::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCa
 }
 
 void ScriptOpcodes_BBDOU::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
-_vm->_menuChoiceOfs = 88; // DEBUG Chose "Start game"
-
 	opCall._deltaOfs += _vm->_menuChoiceOfs;
 }
 
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index c420bd5..dd4e689 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -72,8 +72,10 @@ void DuckmanSpecialCode::init() {
 	SPECIAL(0x00160003, spcResetChinesePuzzle);
 	SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
 	SPECIAL(0x00160005, spcOpenInventory);
+
 	SPECIAL(0x00160007, spcPutBackInventoryItem);
 	SPECIAL(0x00160008, spcClearInventorySlot);
+
 	SPECIAL(0x0016000A, spcAddPropertyTimer);
 	SPECIAL(0x0016000B, spcSetPropertyTimer);
 	SPECIAL(0x0016000C, spcRemovePropertyTimer);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 84f7974..b4ccee9 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -222,8 +222,7 @@ typedef struct InventoryMenuItem {
 	uint32 propertyId;
 } InventoryMenuItem;
 
-static const InventoryMenuItem kDebugInventoryItems[21] =
-{
+static const InventoryMenuItem kDebugInventoryItems[21] = {
 	{ "Pick-up Book", 262212, 393231, 917519 },
 	{ "Bucket and Squeegee", 262314, 393233, 917599 },
 	{ "Cardboard Cut Out", 262219, 393264, 917573 },
@@ -398,7 +397,7 @@ void MenuActionUpdateSlider::setSliderValue(uint8 newValue) {
 	_menuItem->setText(text);
 	_menuSystem->redrawMenuText(menu);
 
-	switch(_type) {
+	switch (_type) {
 		case SFX : _vm->_soundMan->setSfxVolume(newValue * (256/15)); break;
 		case MUSIC : _vm->_soundMan->setMusicVolume(newValue * (256/15)); break;
 		case VOICE : _vm->_soundMan->setSpeechVolume(newValue * (256/15)); break;
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 816b17d..b33da13 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -257,7 +257,7 @@ void BaseMenuSystem::placeActorHoverBackground() {
 	WidthHeight textInfoDimensions;
 	_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
 
-	if ( _activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor)
+	if (_activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor)
 		textInfoDimensions._width -= 6;
 
 	WidthHeight frameDimensions;
@@ -567,7 +567,7 @@ bool BaseMenuSystem::calcMenuItemTextPositionAtPoint(Common::Point pt, int &offs
 	FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
 
 	uint curX = 0;
-	for (int i=0; i < text.size(); i++) {
+	for (int i = 0; i < text.size(); i++) {
 		int16 w = font->getCharInfo(text[i])->_width;
 		if (x >= curX && x <= curX + w) {
 			offset = i;


Commit: 608f2f1f1ae524fd6ffc656dfcef3985988adaa9
    https://github.com/scummvm/scummvm/commit/608f2f1f1ae524fd6ffc656dfcef3985988adaa9
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Refactor duckman inventory code to remove verbose init logic
Replaced Common::String with c string array in slider menu init logic

Changed paths:
    engines/illusions/duckman/duckman_inventory.cpp
    engines/illusions/duckman/duckman_inventory.h
    engines/illusions/duckman/menusystem_duckman.cpp


diff --git a/engines/illusions/duckman/duckman_inventory.cpp b/engines/illusions/duckman/duckman_inventory.cpp
index 2196826..1f863b5 100644
--- a/engines/illusions/duckman/duckman_inventory.cpp
+++ b/engines/illusions/duckman/duckman_inventory.cpp
@@ -40,48 +40,40 @@ DuckmanInventory::DuckmanInventory(IllusionsEngine_Duckman *vm)
 DuckmanInventory::~DuckmanInventory() {
 }
 
+static const struct DMInventoryItem kInventoryItems[21] = {
+	{0x40011, 0xE005B},
+	{0x40099, 0xE001B},
+	{0x4000F, 0xE000C},
+	{0x40042, 0xE0012},
+	{0x40044, 0xE000F},
+	{0x40029, 0xE000D},
+	{0x400A7, 0xE005D},
+	{0x40096, 0xE001C},
+	{0x40077, 0xE0010},
+	{0x4008A, 0xE0033},
+	{0x4004B, 0xE0045},
+	{0x40054, 0xE0021},
+	{0x400C6, 0xE005A},
+	{0x4000B, 0xE005E},
+	{0x4005F, 0xE0016},
+	{0x40072, 0xE0017},
+	{0x400AA, 0xE005F},
+	{0x400B8, 0xE0050},
+	{0x4001F, 0xE001A},
+	{0x40095, 0xE0060},
+	{0x40041, 0xE0053}
+};
+
 void DuckmanInventory::initInventory() {
-	_inventorySlots.push_back(DMInventorySlot( 64,  52));
-	_inventorySlots.push_back(DMInventorySlot(112,  52));
-	_inventorySlots.push_back(DMInventorySlot(160,  52));
-	_inventorySlots.push_back(DMInventorySlot(208,  52));
-	_inventorySlots.push_back(DMInventorySlot(255,  52));
-	_inventorySlots.push_back(DMInventorySlot( 64,  84));
-	_inventorySlots.push_back(DMInventorySlot(112,  84));
-	_inventorySlots.push_back(DMInventorySlot(160,  84));
-	_inventorySlots.push_back(DMInventorySlot(208,  84));
-	_inventorySlots.push_back(DMInventorySlot(255,  84));
-	_inventorySlots.push_back(DMInventorySlot( 64, 116));
-	_inventorySlots.push_back(DMInventorySlot(112, 116));
-	_inventorySlots.push_back(DMInventorySlot(160, 116));
-	_inventorySlots.push_back(DMInventorySlot(208, 116));
-	_inventorySlots.push_back(DMInventorySlot(255, 116));
-	_inventorySlots.push_back(DMInventorySlot( 64, 148));
-	_inventorySlots.push_back(DMInventorySlot(112, 148));
-	_inventorySlots.push_back(DMInventorySlot(160, 148));
-	_inventorySlots.push_back(DMInventorySlot(208, 148));
-	_inventorySlots.push_back(DMInventorySlot(255, 148));
-	_inventoyItems.push_back(DMInventoryItem(0x40011, 0xE005B));
-	_inventoyItems.push_back(DMInventoryItem(0x40099, 0xE001B));
-	_inventoyItems.push_back(DMInventoryItem(0x4000F, 0xE000C));
-	_inventoyItems.push_back(DMInventoryItem(0x40042, 0xE0012));
-	_inventoyItems.push_back(DMInventoryItem(0x40044, 0xE000F));
-	_inventoyItems.push_back(DMInventoryItem(0x40029, 0xE000D));
-	_inventoyItems.push_back(DMInventoryItem(0x400A7, 0xE005D));
-	_inventoyItems.push_back(DMInventoryItem(0x40096, 0xE001C));
-	_inventoyItems.push_back(DMInventoryItem(0x40077, 0xE0010));
-	_inventoyItems.push_back(DMInventoryItem(0x4008A, 0xE0033));
-	_inventoyItems.push_back(DMInventoryItem(0x4004B, 0xE0045));
-	_inventoyItems.push_back(DMInventoryItem(0x40054, 0xE0021));
-	_inventoyItems.push_back(DMInventoryItem(0x400C6, 0xE005A));
-	_inventoyItems.push_back(DMInventoryItem(0x4000B, 0xE005E));
-	_inventoyItems.push_back(DMInventoryItem(0x4005F, 0xE0016));
-	_inventoyItems.push_back(DMInventoryItem(0x40072, 0xE0017));
-	_inventoyItems.push_back(DMInventoryItem(0x400AA, 0xE005F));
-	_inventoyItems.push_back(DMInventoryItem(0x400B8, 0xE0050));
-	_inventoyItems.push_back(DMInventoryItem(0x4001F, 0xE001A));
-	_inventoyItems.push_back(DMInventoryItem(0x40095, 0xE0060));
-	_inventoyItems.push_back(DMInventoryItem(0x40041, 0xE0053));
+	for (int y = 0; y < 4; y++) {
+		for (int x = 0; x < 5; x++) {
+			_inventorySlots.push_back(DMInventorySlot( 64 + x * 48,  52 + y * 32));
+		}
+	}
+
+	for (int i = 0; i < 21; i++) {
+		_inventoryItems.push_back(kInventoryItems[i]);
+	}
 }
 
 void DuckmanInventory::openInventory() {
@@ -95,8 +87,8 @@ void DuckmanInventory::openInventory() {
 		}
 	}
 
-	for (uint i = 0; i < _inventoyItems.size(); ++i) {
-		DMInventoryItem *inventoryItem = &_inventoyItems[i];
+	for (uint i = 0; i < _inventoryItems.size(); ++i) {
+		DMInventoryItem *inventoryItem = &_inventoryItems[i];
 		if (_vm->_scriptResource->_properties.get(inventoryItem->_propertyId)) {
 			DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
 			if (inventorySlot) {
@@ -155,9 +147,9 @@ DMInventorySlot *DuckmanInventory::findInventorySlot(uint32 objectId) {
 }
 
 DMInventoryItem *DuckmanInventory::findInventoryItem(uint32 objectId) {
-	for (uint i = 0; i < _inventoyItems.size(); ++i)
-		if (_inventoyItems[i]._objectId == objectId)
-			return &_inventoyItems[i];
+	for (uint i = 0; i < _inventoryItems.size(); ++i)
+		if (_inventoryItems[i]._objectId == objectId)
+			return &_inventoryItems[i];
 	return 0;
 }
 
diff --git a/engines/illusions/duckman/duckman_inventory.h b/engines/illusions/duckman/duckman_inventory.h
index 05048f4..f8faf5d 100644
--- a/engines/illusions/duckman/duckman_inventory.h
+++ b/engines/illusions/duckman/duckman_inventory.h
@@ -37,9 +37,6 @@ struct DMInventorySlot {
 struct DMInventoryItem {
 	uint32 _objectId;
 	uint32 _propertyId;
-	DMInventoryItem() : _objectId(0) {}
-	DMInventoryItem(uint32 objectId, uint32 propertyId)
-		: _objectId(objectId), _propertyId(propertyId) {}
 };
 
 class DuckmanInventory {
@@ -49,7 +46,7 @@ public:
 public:
 	IllusionsEngine_Duckman *_vm;
 	Common::Array<DMInventorySlot> _inventorySlots;
-	Common::Array<DMInventoryItem> _inventoyItems;
+	Common::Array<DMInventoryItem> _inventoryItems;
 	void initInventory();
 	void openInventory();
 	void addInventoryItem(uint32 objectId);
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index b4ccee9..3a10ec3 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -120,7 +120,7 @@ BaseMenu *DuckmanMenuSystem::createLoadGameFailedMenu() {
 
 MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(MenuActionUpdateSlider **action, const Common::String &text, SliderActionType type, BaseMenu *baseMenu) {
 	int sliderValue = 0;
-	Common::String sliderText = "{~~~~~~~~~~~~~~~~}";
+	char sliderText[] = "{~~~~~~~~~~~~~~~~}";
 	switch (type) {
 		case SFX : sliderValue = _vm->_soundMan->getSfxVolume()/(256/15); break;
 		case MUSIC : sliderValue = _vm->_soundMan->getMusicVolume()/(256/15); break;
@@ -129,7 +129,7 @@ MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(MenuActionUpdateSlider
 		default: break;
 	}
 
-	sliderText.setChar('|', sliderValue + 1);
+	sliderText[sliderValue + 1] = '|';
 
 	*action = new MenuActionUpdateSlider(this, baseMenu, type, _vm);
 	MenuItem *menuItem = new MenuItem(text + sliderText, *action);


Commit: fee1f3d8cb065182322bc80aba39a66a1b4b0cfb
    https://github.com/scummvm/scummvm/commit/fee1f3d8cb065182322bc80aba39a66a1b4b0cfb
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: always use braces for loops

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/dictionary.h
    engines/illusions/duckman/duckman_inventory.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.cpp
    engines/illusions/duckman/propertytimers.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/gamarchive.cpp
    engines/illusions/graphics.cpp
    engines/illusions/input.cpp
    engines/illusions/menusystem.cpp
    engines/illusions/pathfinder.cpp
    engines/illusions/resources/actorresource.cpp
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/resources/fontresource.cpp
    engines/illusions/resources/scriptresource.cpp
    engines/illusions/resources/soundresource.cpp
    engines/illusions/resources/talkresource.cpp
    engines/illusions/resourcesystem.cpp
    engines/illusions/saveload.cpp
    engines/illusions/screen.cpp
    engines/illusions/screentext.cpp
    engines/illusions/scriptstack.cpp
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sound.cpp
    engines/illusions/thread.cpp
    engines/illusions/updatefunctions.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index eaf603f..568a1cc 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -84,8 +84,9 @@ Actor::Actor(IllusionsEngine *vm)
 	_parentObjectId = 0;
 	_linkIndex = 0;
 	_linkIndex2 = 0;
-	for (uint i = 0; i < kSubObjectsCount; ++i)
+	for (uint i = 0; i < kSubObjectsCount; ++i) {
 		_subobjects[i] = 0;
+	}
 	_notifyThreadId1 = 0;
 	_notifyThreadId2 = 0;
 	_surfaceTextFlag = 0;
@@ -231,7 +232,6 @@ void Control::pause() {
 }
 
 void Control::unpause() {
-
 	_vm->_dict->setObjectControl(_objectId, this);
 
 	if (_objectId == Illusions::CURSOR_OBJECT_ID)
@@ -246,7 +246,6 @@ void Control::unpause() {
 			surfInfo = _actor->_surfInfo;
 		_actor->createSurface(surfInfo);
 	}
-
 }
 
 void Control::appearActor() {
@@ -264,15 +263,17 @@ void Control::appearActor() {
 		if (_objectId == Illusions::CURSOR_OBJECT_ID) {
 			_vm->showCursor();
 		} else {
-			if (_actor->_frameIndex || _actorTypeId == 0x50004)
+			if (_actor->_frameIndex || _actorTypeId == 0x50004) {
 				_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
-			else
+			} else {
 				_actor->_flags |= Illusions::ACTOR_FLAG_1000;
-			for (uint i = 0; i < kSubObjectsCount; ++i)
+			}
+			for (uint i = 0; i < kSubObjectsCount; ++i) {
 				if (_actor->_subobjects[i]) {
 					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 					subControl->appearActor();
 				}
+			}
 		}
 	}
 }
@@ -287,11 +288,12 @@ void Control::disappearActor() {
 		} else {
 			_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
 			_actor->_flags &= ~Illusions::ACTOR_FLAG_1000;
-			for (uint i = 0; i < kSubObjectsCount; ++i)
+			for (uint i = 0; i < kSubObjectsCount; ++i) {
 				if (_actor->_subobjects[i]) {
 					Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 					subControl->disappearActor();
 				}
+			}
 		}
 	}
 }
@@ -303,22 +305,24 @@ bool Control::isActorVisible() {
 void Control::activateObject() {
 	_flags |= 1;
 	if (_actor) {
-		for (uint i = 0; i < kSubObjectsCount; ++i)
+		for (uint i = 0; i < kSubObjectsCount; ++i) {
 			if (_actor->_subobjects[i]) {
 				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 				subControl->activateObject();
 			}
+		}
 	}
 }
 
 void Control::deactivateObject() {
 	_flags &= ~1;
 	if (_actor) {
-		for (uint i = 0; i < kSubObjectsCount; ++i)
+		for (uint i = 0; i < kSubObjectsCount; ++i) {
 			if (_actor->_subobjects[i]) {
 				Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 				subControl->deactivateObject();
 			}
+		}
 	}
 }
 
@@ -354,20 +358,22 @@ Common::Point Control::getActorPosition() {
 
 void Control::setActorScale(int scale) {
 	_actor->_scale = scale;
-	for (uint i = 0; i < kSubObjectsCount; ++i)
+	for (uint i = 0; i < kSubObjectsCount; ++i) {
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->setActorScale(scale);
 		}
+	}
 }
 
 void Control::faceActor(uint facing) {
 	_actor->_facing = facing;
-	for (uint i = 0; i < kSubObjectsCount; ++i)
+	for (uint i = 0; i < kSubObjectsCount; ++i) {
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->faceActor(facing);
 		}
+	}
 }
 
 void Control::linkToObject(uint32 parentObjectId, uint32 linkedObjectValue) {
@@ -385,13 +391,14 @@ void Control::clearNotifyThreadId1() {
 }
 
 void Control::clearNotifyThreadId2() {
-	for (uint i = 0; i < kSubObjectsCount; ++i)
+	for (uint i = 0; i < kSubObjectsCount; ++i) {
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
 			subControl->_actor->_entryTblPtr = 0;
 			subControl->_actor->_notifyThreadId2 = 0;
 		}
+	}
 	_actor->_flags &= ~Illusions::ACTOR_FLAG_80;
 	_actor->_entryTblPtr = 0;
 	_actor->_notifyThreadId2 = 0;
@@ -583,11 +590,12 @@ void Control::stopSequenceActor() {
 			_actor->_flags |= Illusions::ACTOR_FLAG_1000;
 		}
 	}
-	for (uint i = 0; i < kSubObjectsCount; ++i)
+	for (uint i = 0; i < kSubObjectsCount; ++i) {
 		if (_actor->_subobjects[i]) {
 			Control *subControl = _vm->_dict->getObjectControl(_actor->_subobjects[i]);
 			subControl->stopSequenceActor();
 		}
+	}
 }
 
 void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId) {
diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index c631a22..cba0dcc 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -63,8 +63,9 @@ void BbdouBubble::init() {
 	_objectId1414 = 0x4005B;
 	_objectId1418 = 0x4005C;
 
-	for (uint i = 0; i < 32; ++i)
+	for (uint i = 0; i < 32; ++i) {
 		_objectIds[i] = kObjectIds3[i];
+	}
 
 	for (uint i = 0; i < 32; ++i) {
 		_items[i]._objectId = kObjectIds2[i];
@@ -92,8 +93,9 @@ void BbdouBubble::addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progRe
 	item0._progResKeywordId = progResKeywordId;
 	item0._baseNamedPointId = namedPointId;
 	item0._count = count;
-	for (int16 i = 0; i < count; ++i)
+	for (int16 i = 0; i < count; ++i) {
 		item0._namedPointIds[i] = FROM_LE_32(namedPointIds[i]);
+	}
 	item0._objectId = 0;
 	item0._pt.x = 0;
 	item0._pt.y = 0;
@@ -148,8 +150,9 @@ void BbdouBubble::hide() {
 }
 
 void BbdouBubble::setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId) {
-	for (uint i = 0; i < 32; ++i)
+	for (uint i = 0; i < 32; ++i) {
 		_items[i]._enabled = 0;
+	}
 	int16 maxCount = 32;
 	for (uint i = 0; i < _item0s.size(); ++i) {
 		Item0 *item0 = &_item0s[i];
@@ -202,8 +205,9 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 		control->startSequenceActor(0x00060056, 2, 0);
 	}
 
-	for (int i = 0; i < kSequenceIdsCount; ++i)
+	for (int i = 0; i < kSequenceIdsCount; ++i) {
 		sequenceCounters[i] = 0;
+	}
 
 	if (pt2.y >= pt1.y) {
 		swapY = true;
@@ -231,8 +235,9 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 	int pointsCount = (int)(arcLength / kDistanceBetweenPoints);
 	float partAngle = ABS(kDistanceBetweenPoints / radius);
 
-	for (int i = 0; i < pointsCount; ++i)
+	for (int i = 0; i < pointsCount; ++i) {
 		++sequenceCounters[kIndexTbl[i % kSequenceIdsCount]];
+	}
 
 	if (!swapY) {
 		if (pt2.y < pt1.y) {
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 3a098b9..2d546de 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -132,18 +132,20 @@ void BbdouCursor::reset(uint32 objectId) {
 }
 
 void BbdouCursor::addCursorSequenceId(uint32 objectId, uint32 sequenceId) {
-	for (uint i = 0; i < kMaxCursorSequences; ++i)
+	for (uint i = 0; i < kMaxCursorSequences; ++i) {
 		if (_cursorSequences[i]._objectId == 0) {
 			_cursorSequences[i]._objectId = objectId;
 			_cursorSequences[i]._sequenceId = sequenceId;
 			break;
 		}
+	}
 }
 
 uint32 BbdouCursor::findCursorSequenceId(uint32 objectId) {
-	for (uint i = 0; i < kMaxCursorSequences; ++i)
+	for (uint i = 0; i < kMaxCursorSequences; ++i) {
 		if (_cursorSequences[i]._objectId == objectId)
 			return _cursorSequences[i]._sequenceId;
+	}
 	return 0;
 }
 
@@ -332,8 +334,9 @@ bool BbdouCursor::getTrackingCursorSequenceId(Control *control, uint32 &outSeque
 }
 
 void BbdouCursor::resetActiveVerbs() {
-	for (uint i = 0; i < 32; ++i)
+	for (uint i = 0; i < 32; ++i) {
 		_data._verbState._verbActive[i] = false;
+	}
 	if (_data._verbState._cursorState == 1) {
 		_data._verbState._verbActive[1] = true;
 		_data._verbState._verbActive[2] = true;
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index e35a2e8..14c2d76 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -59,11 +59,12 @@ void InventoryBag::registerInventorySlot(uint32 namedPointId) {
 bool InventoryBag::addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot) {
 	// NOTE Skipped support for multiple items per slot, not used in BBDOU
 	if (!inventorySlot) {
-		for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it)
+		for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it) {
 			if (!(*it)->_inventoryItem) {
 				inventorySlot = *it;
 				break;
 			}
+		}
 	}
 	if (inventorySlot) {
 		inventorySlot->_inventoryItem = inventoryItem;
@@ -73,9 +74,10 @@ bool InventoryBag::addInventoryItem(InventoryItem *inventoryItem, InventorySlot
 }
 
 void InventoryBag::removeInventoryItem(InventoryItem *inventoryItem) {
-	for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it)
+	for (InventorySlotsIterator it = _inventorySlots.begin(); it != _inventorySlots.end(); ++it) {
 		if ((*it)->_inventoryItem && (*it)->_inventoryItem->_objectId == inventoryItem->_objectId)
 			(*it)->_inventoryItem = 0;
+	}
 }
 
 bool InventoryBag::hasInventoryItem(uint32 objectId) {
@@ -111,9 +113,10 @@ void InventoryBag::clear() {
 }
 
 InventorySlot *InventoryBag::getInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
 		if (_inventorySlots[i]->_objectId == objectId)
 			return _inventorySlots[i];
+	}
 	return 0;
 }
 
@@ -159,9 +162,10 @@ void BbdouInventory::addInventoryItem(uint32 objectId) {
 	bool assigned = inventoryItem->_assigned;
 	inventoryItem->_assigned = true;
 	if (!assigned && !inventoryItem->_flag) {
-		for (uint i = 0; i < _inventoryBags.size(); ++i)
+		for (uint i = 0; i < _inventoryBags.size(); ++i) {
 			if (!_inventoryBags[i]->addInventoryItem(inventoryItem, 0))
 				inventoryItem->_assigned = false;
+		}
 	}
 	if (_activeInventorySceneId)
 		refresh();
@@ -181,10 +185,11 @@ void BbdouInventory::removeInventoryItem(uint32 objectId) {
 }
 
 bool BbdouInventory::hasInventoryItem(uint32 objectId) {
-	for (uint i = 0; i < _inventoryItems.size(); ++i)
+	for (uint i = 0; i < _inventoryItems.size(); ++i) {
 		if (_inventoryItems[i]->_objectId == objectId &&
 			_inventoryItems[i]->_assigned)
 			return true;
+	}
 	return false;
 }
 
@@ -234,16 +239,18 @@ void BbdouInventory::close() {
 }
 
 InventoryBag *BbdouInventory::getInventoryBag(uint32 sceneId) {
-	for (uint i = 0; i < _inventoryBags.size(); ++i)
+	for (uint i = 0; i < _inventoryBags.size(); ++i) {
 		if (_inventoryBags[i]->_sceneId == sceneId)
 			return _inventoryBags[i];
+	}
 	return 0;
 }
 
 InventoryItem *BbdouInventory::getInventoryItem(uint32 objectId) {
-	for (uint i = 0; i < _inventoryItems.size(); ++i)
+	for (uint i = 0; i < _inventoryItems.size(); ++i) {
 		if (_inventoryItems[i]->_objectId == objectId)
 			return _inventoryItems[i];
+	}
 	return 0;
 }
 
@@ -266,8 +273,9 @@ void BbdouInventory::refresh() {
 }
 
 void BbdouInventory::buildItems(InventoryBag *inventoryBag) {
-	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it)
+	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it) {
 		(*it)->_timesPresent = 0;
+	}
 	inventoryBag->buildItems();
 	for (InventoryItemsIterator it = _inventoryItems.begin(); it != _inventoryItems.end(); ++it) {
 		InventoryItem *inventoryItem = *it;
@@ -284,8 +292,9 @@ void BbdouInventory::clear() {
 		inventoryItem->_assigned = false;
 		inventoryItem->_flag = false;
 	}
-	for (uint i = 0; i < _inventoryBags.size(); ++i)
+	for (uint i = 0; i < _inventoryBags.size(); ++i) {
 		_inventoryBags[i]->clear();
+	}
 }
 
 void BbdouInventory::cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId) {
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 89f9441..2d8df43 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -101,8 +101,9 @@ void RadarMicrophoneThread::addZone(uint32 threadId) {
 }
 
 void RadarMicrophoneThread::initZones() {
-	for (uint i = 0; i < _zonesCount; ++i)
+	for (uint i = 0; i < _zonesCount; ++i) {
 		_zones[i]._x = (i + 1) * 640 / _zonesCount;
+	}
 	_zones[_zonesCount]._x = 640;
 	_currZoneIndex = 0;
 }
@@ -114,17 +115,19 @@ ObjectInteractModeMap::ObjectInteractModeMap() {
 
 void ObjectInteractModeMap::setObjectInteractMode(uint32 objectId, int value) {
 	ObjectInteractMode *objectInteractMode = 0;
-	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i) {
 		if (_objectVerbs[i]._objectId == objectId) {
 			objectInteractMode = &_objectVerbs[i];
 			break;
 		}
+	}
 	if (!objectInteractMode) {
-		for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+		for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i) {
 			if (_objectVerbs[i]._objectId == 0) {
 				objectInteractMode = &_objectVerbs[i];
 				break;
 			}
+		}
 	}
 	if (value != 11) {
 		objectInteractMode->_objectId = objectId;
@@ -136,9 +139,10 @@ void ObjectInteractModeMap::setObjectInteractMode(uint32 objectId, int value) {
 }
 
 int ObjectInteractModeMap::getObjectInteractMode(uint32 objectId) {
-	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i)
+	for (uint i = 0; i < ARRAYSIZE(_objectVerbs); ++i) {
 		if (_objectVerbs[i]._objectId == objectId)
 			return _objectVerbs[i]._interactMode;
+	}
 	return 11;
 }
 
@@ -974,9 +978,10 @@ bool BbdouSpecialCode::testVerbId(uint32 verbId, uint32 holdingObjectId, uint32
 			verbIds = kVerbIdsEE;
 	}
 
-	for (; *verbIds; ++verbIds)
+	for (; *verbIds; ++verbIds) {
 		if (*verbIds == verbId)
 			return true;
+	}
 	return false;
 }
 
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 0074036..0c16ad5 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -113,9 +113,10 @@ uint32 ActiveScenes::getCurrentScene() {
 }
 
 bool ActiveScenes::isSceneActive(uint32 sceneId) {
-	for (uint i = 0; i < _stack.size(); ++i)
+	for (uint i = 0; i < _stack.size(); ++i) {
 		if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
 			return true;
+	}
 	return false;
 }
 
@@ -493,8 +494,9 @@ void IllusionsEngine_BBDOU::newScriptThread(uint32 threadId, uint32 callingThrea
 		scriptThread->pause();
 	if (_doScriptThreadInit) {
 		int updateResult = kTSRun;
-		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield)
+		while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield) {
 			updateResult = scriptThread->update();
+		}
 	}
 }
 
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 0939583..e6a3dd6 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -54,8 +54,9 @@ typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> S
 
 void ScriptOpcodes_BBDOU::initOpcodes() {
 	// First clear everything
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		_opcodes[i] = 0;
+	}
 	// Register opcodes
 	OPCODE(2, opSuspend);
 	OPCODE(3, opYield);
@@ -177,8 +178,9 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 #undef OPCODE
 
 void ScriptOpcodes_BBDOU::freeOpcodes() {
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		delete _opcodes[i];
+	}
 }
 
 // Opcodes
diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index 667042c..c0d60a9 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -44,8 +44,9 @@ protected:
 public:
 
 	~DictionaryHashMap() {
-		for (MapIterator it = _map.begin(); it != _map.end(); ++it)
+		for (MapIterator it = _map.begin(); it != _map.end(); ++it) {
 			delete it->_value;
+		}
 	}
 
 	void add(uint32 id, T *value) {
diff --git a/engines/illusions/duckman/duckman_inventory.cpp b/engines/illusions/duckman/duckman_inventory.cpp
index 1f863b5..e1ab1ff 100644
--- a/engines/illusions/duckman/duckman_inventory.cpp
+++ b/engines/illusions/duckman/duckman_inventory.cpp
@@ -112,9 +112,10 @@ void DuckmanInventory::addInventoryItem(uint32 objectId) {
 }
 
 void DuckmanInventory::clearInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
 		if (_inventorySlots[i]._objectId == objectId)
 			_inventorySlots[i]._objectId = 0;
+	}
 }
 
 void DuckmanInventory::putBackInventoryItem() {
@@ -140,16 +141,18 @@ void DuckmanInventory::putBackInventoryItem() {
 }
 
 DMInventorySlot *DuckmanInventory::findInventorySlot(uint32 objectId) {
-	for (uint i = 0; i < _inventorySlots.size(); ++i)
+	for (uint i = 0; i < _inventorySlots.size(); ++i) {
 		if (_inventorySlots[i]._objectId == objectId)
 			return &_inventorySlots[i];
+	}
 	return 0;
 }
 
 DMInventoryItem *DuckmanInventory::findInventoryItem(uint32 objectId) {
-	for (uint i = 0; i < _inventoryItems.size(); ++i)
+	for (uint i = 0; i < _inventoryItems.size(); ++i) {
 		if (_inventoryItems[i]._objectId == objectId)
 			return &_inventoryItems[i];
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index e08d1b0..56c997d 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -881,8 +881,9 @@ bool IllusionsEngine_Duckman::loadScene(uint32 sceneId) {
 	uint resourcesCount;
 	uint32 *resources;
 	sceneInfo->getResources(resourcesCount, resources);
-	for (uint i = 0; i < resourcesCount; ++i)
+	for (uint i = 0; i < resourcesCount; ++i) {
 		_resSys->loadResource(resources[i], sceneId, 0);
+	}
 	return true;
 }
 
diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp
index 3a10ec3..367eb28 100644
--- a/engines/illusions/duckman/menusystem_duckman.cpp
+++ b/engines/illusions/duckman/menusystem_duckman.cpp
@@ -56,13 +56,15 @@ void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menu
 }
 
 void DuckmanMenuSystem::clearMenus() {
-	for (int i = 0; i < kDuckmanLastMenuIndex; ++i)
+	for (int i = 0; i < kDuckmanLastMenuIndex; ++i) {
 		_menus[i] = 0;
+	}
 }
 
 void DuckmanMenuSystem::freeMenus() {
-	for (int i = 0; i < kDuckmanLastMenuIndex; ++i)
+	for (int i = 0; i < kDuckmanLastMenuIndex; ++i) {
 		delete _menus[i];
+	}
 }
 
 BaseMenu *DuckmanMenuSystem::getMenuById(int menuId) {
diff --git a/engines/illusions/duckman/propertytimers.cpp b/engines/illusions/duckman/propertytimers.cpp
index cdfdd92..c74b9db 100644
--- a/engines/illusions/duckman/propertytimers.cpp
+++ b/engines/illusions/duckman/propertytimers.cpp
@@ -73,11 +73,12 @@ void PropertyTimers::removePropertyTimer(uint32 propertyId) {
 }
 
 bool PropertyTimers::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
-	for (uint i = 0; i < kPropertyTimersCount; ++i)
+	for (uint i = 0; i < kPropertyTimersCount; ++i) {
 		if (_propertyTimers[i]._propertyId == propertyId) {
 			propertyTimer = &_propertyTimers[i];
 			return true;
 		}
+	}
 	return false;
 }
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 6b76a65..e6f286c 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -57,8 +57,9 @@ typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_Duckman>
 
 void ScriptOpcodes_Duckman::initOpcodes() {
 	// First clear everything
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		_opcodes[i] = 0;
+	}
 	// Register opcodes
 	OPCODE(1, opNop);
 	OPCODE(2, opSuspend);
@@ -164,8 +165,9 @@ void ScriptOpcodes_Duckman::initOpcodes() {
 #undef OPCODE
 
 void ScriptOpcodes_Duckman::freeOpcodes() {
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		delete _opcodes[i];
+	}
 }
 
 // Opcodes
diff --git a/engines/illusions/gamarchive.cpp b/engines/illusions/gamarchive.cpp
index 5c0debb..cda7859 100644
--- a/engines/illusions/gamarchive.cpp
+++ b/engines/illusions/gamarchive.cpp
@@ -75,16 +75,18 @@ void GamArchive::loadDictionary() {
 }
 
 const GamGroupEntry *GamArchive::getGroupEntry(uint32 sceneId) {
-	for (uint i = 0; i < _groupCount; ++i)
+	for (uint i = 0; i < _groupCount; ++i) {
 		if (_groups[i]._id == sceneId)
 			return &_groups[i];
+	}
 	return 0;
 }
 
 const GamFileEntry *GamArchive::getFileEntry(const GamGroupEntry *groupEntry, uint32 resId) {
-	for (uint i = 0; i < groupEntry->_fileCount; ++i)
+	for (uint i = 0; i < groupEntry->_fileCount; ++i) {
 		if (groupEntry->_files[i]._id == resId)
 			return &groupEntry->_files[i];
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index 2472137..772d627 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -54,11 +54,12 @@ void NamedPoint::load(Common::SeekableReadStream &stream) {
 // NamedPoints
 
 bool NamedPoints::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
-	for (ItemsIterator it = _namedPoints.begin(); it != _namedPoints.end(); ++it)
+	for (ItemsIterator it = _namedPoints.begin(); it != _namedPoints.end(); ++it) {
 		if ((*it)._namedPointId == namedPointId) {
 			pt = (*it)._pt;
 			return true;
 		}
+	}
 	return false;
 }
 
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 712f417..12d8436 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -173,8 +173,9 @@ InputEvent& Input::setInputEvent(uint evt, uint bitMask) {
 }
 
 void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
-	for (uint i = 0; i < kEventMax; ++i)
+	for (uint i = 0; i < kEventMax; ++i) {
 		_newKeys |= _inputEvents[i].handle(key, mouseButton, down);
+	}
 	uint prevButtonStates = _buttonStates;
 	_buttonStates |= _newKeys;
 	_newKeys = 0;
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index b33da13..6d83591 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -59,8 +59,9 @@ BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte backgroundCol
 }
 
 BaseMenu::~BaseMenu() {
-	for (MenuItems::iterator it = _menuItems.begin(); it != _menuItems.end(); ++it)
+	for (MenuItems::iterator it = _menuItems.begin(); it != _menuItems.end(); ++it) {
 		delete *it;
+	}
 }
 
 void BaseMenu::addText(const Common::String text) {
@@ -585,8 +586,9 @@ MenuTextBuilder::MenuTextBuilder() : _pos(0) {
 }
 
 void MenuTextBuilder::appendString(const Common::String &value) {
-	for (uint i = 0; i < value.size(); ++i)
+	for (uint i = 0; i < value.size(); ++i) {
 		_text[_pos++] = value[i];
+	}
 }
 
 void MenuTextBuilder::appendNewLine() {
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index e3ea07b..936e855 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -82,9 +82,10 @@ PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point d
 }
 
 bool PathFinder::isLineBlocked(PathLine &line) {
-	for (uint i = 0; i < _walkRects->size(); ++i)
+	for (uint i = 0; i < _walkRects->size(); ++i) {
 		if (calcLineStatus(line, (*_walkRects)[i], 0) != 3)
 			return true;
+	}
 	return false;
 }
 
diff --git a/engines/illusions/resources/actorresource.cpp b/engines/illusions/resources/actorresource.cpp
index 13c8abc..04e6c9d 100644
--- a/engines/illusions/resources/actorresource.cpp
+++ b/engines/illusions/resources/actorresource.cpp
@@ -162,9 +162,10 @@ void ActorResource::load(Resource *resource) {
 }
 
 bool ActorResource::containsSequence(Sequence *sequence) {
-	for (uint i = 0; i < _sequences.size(); ++i)
+	for (uint i = 0; i < _sequences.size(); ++i) {
 		if (sequence == &_sequences[i])
 			return true;
+	}
 	return false;
 }
 
@@ -243,10 +244,12 @@ void ActorInstance::registerResources() {
 }
 
 void ActorInstance::unregisterResources() {
-	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i)
+	for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
 		_vm->_dict->removeActorType(_actorResource->_actorTypes[i]._actorTypeId);
-	for (uint i = 0; i < _actorResource->_sequences.size(); ++i)
+	}
+	for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
 		_vm->_dict->removeSequence(_actorResource->_sequences[i]._sequenceId);
+	}
 }
 
 // ActorInstanceList
@@ -270,15 +273,17 @@ void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
 }
 
 void ActorInstanceList::pauseBySceneId(uint32 sceneId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_sceneId == sceneId)
 			(*it)->pause();
+	}
 }
 
 void ActorInstanceList::unpauseBySceneId(uint32 sceneId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_sceneId == sceneId)
 			(*it)->unpause();
+	}
 }
 
 FramesList *ActorInstanceList::findSequenceFrames(Sequence *sequence) {
@@ -291,9 +296,10 @@ FramesList *ActorInstanceList::findSequenceFrames(Sequence *sequence) {
 }
 
 ActorInstance *ActorInstanceList::findActorByResource(ActorResource *actorResource) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_actorResource == actorResource)
 			return (*it);
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index 27d73a7..2a37bba 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -281,8 +281,9 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 	stream.seek(0x48);
 	uint32 regionSequencesOffs = stream.readUint32LE();
 	stream.seek(regionSequencesOffs);
-	for (uint i = 0; i < _regionSequencesCount; ++i)
+	for (uint i = 0; i < _regionSequencesCount; ++i) {
 		_regionSequences[i].load(data, stream);
+	}
 
 	// Load background objects
 	stream.seek(0x1C);
@@ -344,8 +345,9 @@ void BackgroundResource::load(byte *data, uint32 dataSize) {
 
 int BackgroundResource::findMasterBgIndex() {
 	int index = 1;
-	while (!(_bgInfos[index - 1]._flags & 1)) //TODO check if this is correct
+	while (!(_bgInfos[index - 1]._flags & 1)) { // TODO check if this is correct
 		++index;
+	}
 	return index;
 }
 
@@ -394,8 +396,9 @@ void BackgroundInstance::load(Resource *resource) {
 	initSurface();
 
 	// Insert background objects
-	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i)
+	for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i) {
 		_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
+	}
 
 	registerResources();
 
@@ -460,8 +463,9 @@ void BackgroundInstance::unregisterResources() {
 }
 
 void BackgroundInstance::initSurface() {
-	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i)
+	for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i) {
 		_surfaces[i] = 0;
+	}
 	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
 		BgInfo *bgInfo = &_bgRes->_bgInfos[i];
 		_panPoints[i] = bgInfo->_panPoint;
@@ -481,12 +485,13 @@ void BackgroundInstance::initSurface() {
 }
 
 void BackgroundInstance::freeSurface() {
-	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i)
+	for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
 		if (_surfaces[i]) {
 			_surfaces[i]->free();
 			delete _surfaces[i];
 			_surfaces[i] = 0;
 		}
+	}
 }
 
 void BackgroundInstance::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
@@ -572,28 +577,32 @@ void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgr
 }
 
 void BackgroundInstanceList::pauseBySceneId(uint32 sceneId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_sceneId == sceneId)
 			(*it)->pause();
+	}
 }
 
 void BackgroundInstanceList::unpauseBySceneId(uint32 sceneId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_sceneId == sceneId)
 			(*it)->unpause();
+	}
 }
 
 BackgroundInstance *BackgroundInstanceList::findActiveBackgroundInstance() {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_pauseCtr == 0)
 			return (*it);
+	}
 	return 0;
 }
 
 BackgroundInstance *BackgroundInstanceList::findBackgroundByResource(BackgroundResource *backgroundResource) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_bgRes == backgroundResource)
 			return (*it);
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/resources/fontresource.cpp b/engines/illusions/resources/fontresource.cpp
index 4c89708..8d11e92 100644
--- a/engines/illusions/resources/fontresource.cpp
+++ b/engines/illusions/resources/fontresource.cpp
@@ -104,9 +104,10 @@ void FontResource::load(Resource *resource) {
 }
 
 CharInfo *FontResource::getCharInfo(uint16 c) {
-	for (uint i = 0; i < _charRangesCount; ++i)
+	for (uint i = 0; i < _charRangesCount; ++i) {
 		if (_charRanges[i].containsChar(c))
 			return _charRanges[i].getCharInfo(c);
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/resources/scriptresource.cpp b/engines/illusions/resources/scriptresource.cpp
index 08433fe..760273c 100644
--- a/engines/illusions/resources/scriptresource.cpp
+++ b/engines/illusions/resources/scriptresource.cpp
@@ -51,8 +51,9 @@ void Properties::init(uint count, byte *properties) {
 
 void Properties::clear() {
 	uint32 size = getSize();
-	for (uint32 i = 0; i < size; ++i)
+	for (uint32 i = 0; i < size; ++i) {
 		_properties[i] = 0;
+	}
 }
 
 bool Properties::get(uint32 propertyId) {
@@ -108,8 +109,9 @@ void BlockCounters::init(uint count, byte *blockCounters) {
 }
 
 void BlockCounters::clear() {
-	for (uint i = 0; i < _count; ++i)
+	for (uint i = 0; i < _count; ++i) {
 		_blockCounters[i] = 0;
+	}
 }
 
 byte BlockCounters::get(uint index) {
@@ -177,8 +179,9 @@ void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	debug(2, "TriggerObject::load() _objectId: %08X; _causesCount: %d",
 		_objectId, _causesCount);
 	_causes = new TriggerCause[_causesCount];
-	for (uint i = 0; i < _causesCount; ++i)
+	for (uint i = 0; i < _causesCount; ++i) {
 		_causes[i].load(stream);
+	}
 }
 
 bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
@@ -191,18 +194,20 @@ bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &co
 			}
 		}
 	} else {
-		for (uint i = 0; i < _causesCount; ++i)
+		for (uint i = 0; i < _causesCount; ++i) {
 			if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
 				codeOffs = _causes[i]._codeOffs;
 				return true;
 			}
+		}
 	}
 	return false;
 }
 
 void TriggerObject::fixupSceneInfosDuckman() {
-	for (uint i = 0; i < _causesCount; ++i)
+	for (uint i = 0; i < _causesCount; ++i) {
 		_causes[i]._verbId &= 0xFFFF;
+	}
 }
 
 // SceneInfo
@@ -229,8 +234,9 @@ void SceneInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
 	uint32 triggerObjectsListOffs = stream.readUint32LE();
 	if (_resourcesCount > 0) {
 		_resources = new uint32[_resourcesCount];
-		for (uint i = 0; i < _resourcesCount; ++i)
+		for (uint i = 0; i < _resourcesCount; ++i) {
 			_resources[i] = stream.readUint32LE();
+		}
 	}
 	if (_triggerObjectsCount > 0) {
 		_triggerObjects = new TriggerObject[_triggerObjectsCount];
@@ -256,15 +262,17 @@ void SceneInfo::getResources(uint &resourcesCount, uint32 *&resources) {
 }
 
 TriggerObject *SceneInfo::findTriggerObject(uint32 objectId) {
-	for (uint i = 0; i < _triggerObjectsCount; ++i)
+	for (uint i = 0; i < _triggerObjectsCount; ++i) {
 		if (_triggerObjects[i]._objectId == objectId)
 			return &_triggerObjects[i];
+	}
 	return 0;
 }
 
 void SceneInfo::fixupSceneInfosDuckman() {
-	for (uint i = 0; i < _triggerObjectsCount; ++i)
+	for (uint i = 0; i < _triggerObjectsCount; ++i) {
 		_triggerObjects[i].fixupSceneInfosDuckman();
+	}
 }
 
 // ScriptResource
@@ -290,8 +298,9 @@ void ScriptResource::load(Resource *resource) {
 	if (resource->_gameId == kGameIdBBDOU) {
 		sceneInfosOffs = 0x18;
 	} else if (resource->_gameId == kGameIdDuckman) {
-		for (uint i = 0; i < 27; ++i)
+		for (uint i = 0; i < 27; ++i) {
 			_soundIds[i] = stream.readUint32LE();
+		}
 		sceneInfosOffs = 0x8C;
 	}
 
@@ -326,8 +335,9 @@ void ScriptResource::load(Resource *resource) {
 
 	_codeOffsets = new uint32[_codeCount];
 	stream.seek(codeTblOffs);
-	for (uint i = 0; i < _codeCount; ++i)
+	for (uint i = 0; i < _codeCount; ++i) {
 		_codeOffsets[i] = stream.readUint32LE();
+	}
 
 	_sceneInfos = new SceneInfo[_sceneInfosCount];
 	for (uint i = 0; i < _sceneInfosCount; ++i) {
@@ -378,8 +388,9 @@ uint32 ScriptResource::getObjectActorTypeId(uint32 objectId) {
 }
 
 void ScriptResource::fixupSceneInfosDuckman() {
-	for (uint i = 0; i < _sceneInfosCount; ++i)
+	for (uint i = 0; i < _sceneInfosCount; ++i) {
 		_sceneInfos[i].fixupSceneInfosDuckman();
+	}
 }
 
 // ScriptInstance
diff --git a/engines/illusions/resources/soundresource.cpp b/engines/illusions/resources/soundresource.cpp
index 5cfa240..030a4cc 100644
--- a/engines/illusions/resources/soundresource.cpp
+++ b/engines/illusions/resources/soundresource.cpp
@@ -73,8 +73,9 @@ void SoundGroupResource::load(byte *data, uint32 dataSize) {
 	debug(1, "_soundEffectsCount: %d; soundEffectsOffs: %08X", _soundEffectsCount, soundEffectsOffs);
 	_soundEffects = new SoundEffect[_soundEffectsCount];
 	stream.seek(soundEffectsOffs);
-	for (uint i = 0; i < _soundEffectsCount; ++i)
+	for (uint i = 0; i < _soundEffectsCount; ++i) {
 		_soundEffects[i].load(stream);
+	}
 
 }
 
diff --git a/engines/illusions/resources/talkresource.cpp b/engines/illusions/resources/talkresource.cpp
index a75c370..4b2f678 100644
--- a/engines/illusions/resources/talkresource.cpp
+++ b/engines/illusions/resources/talkresource.cpp
@@ -142,16 +142,18 @@ void TalkInstanceList::removeTalkInstance(TalkInstance *talkInstance) {
 }
 
 TalkInstance *TalkInstanceList::findTalkItem(uint32 talkId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_talkId == talkId)
 			return (*it);
+	}
 	return 0;
 }
 
 TalkInstance *TalkInstanceList::findTalkItemBySceneId(uint32 sceneId) {
-	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it)
+	for (ItemsIterator it = _items.begin(); it != _items.end(); ++it) {
 		if ((*it)->_sceneId == sceneId)
 			return (*it);
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/resourcesystem.cpp b/engines/illusions/resourcesystem.cpp
index bf43db7..d09f4f1 100644
--- a/engines/illusions/resourcesystem.cpp
+++ b/engines/illusions/resourcesystem.cpp
@@ -66,8 +66,9 @@ ResourceSystem::ResourceSystem(IllusionsEngine *vm)
 
 ResourceSystem::~ResourceSystem() {
 	// Delete all registered resource loaders
-	for (ResourceLoadersMapIterator it = _resourceLoaders.begin(); it != _resourceLoaders.end(); ++it)
+	for (ResourceLoadersMapIterator it = _resourceLoaders.begin(); it != _resourceLoaders.end(); ++it) {
 		delete (*it)._value;
+	}
 }
 
 void ResourceSystem::addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader) {
diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp
index cc63b18..233771a 100644
--- a/engines/illusions/saveload.cpp
+++ b/engines/illusions/saveload.cpp
@@ -39,8 +39,9 @@ IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::Se
 
 	byte descriptionLen = in->readByte();
 	header.description = "";
-	while (descriptionLen--)
+	while (descriptionLen--) {
 		header.description += (char)in->readByte();
+	}
 
 	if (loadThumbnail) {
 		Graphics::loadThumbnail(*in, header.thumbnail);
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index b2a970c..6a991cd 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -533,8 +533,9 @@ void Screen8Bit::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface,
 }
 
 void Screen8Bit::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
-	for (uint i = 0; i < count; ++i)
+	for (uint i = 0; i < count; ++i) {
 		x += font->_widthC + drawChar(font, surface, x, y, *text++);
+	}
 }
 
 void Screen8Bit::fillSurface(Graphics::Surface *surface, byte color) {
@@ -553,9 +554,10 @@ int16 Screen8Bit::drawChar(FontResource *font, Graphics::Surface *surface, int16
 	byte *dst = (byte*)surface->getBasePtr(x, y);
 	byte *pixels = charInfo->_pixels;
 	for (int16 yc = 0; yc < font->_charHeight; ++yc) {
-		for (int16 xc = 0; xc < charWidth; ++xc)
+		for (int16 xc = 0; xc < charWidth; ++xc) {
 			if (pixels[xc])
 				dst[xc] = pixels[xc];
+		}
 		dst += surface->pitch;
 		pixels += charWidth;
 	}
@@ -731,8 +733,9 @@ void Screen16Bit::drawSurface(Common::Rect &dstRect, Graphics::Surface *surface,
 }
 
 void Screen16Bit::drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) {
-	for (uint i = 0; i < count; ++i)
+	for (uint i = 0; i < count; ++i) {
 		x += font->_widthC + drawChar(font, surface, x, y, *text++);
+	}
 }
 
 void Screen16Bit::fillSurface(Graphics::Surface *surface, byte color) {
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 0f811df..206272d 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -135,8 +135,9 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	_vm->_screenPalette->setPaletteEntry(font->getColorIndex(), screenText->_info._colorR, screenText->_info._colorG, screenText->_info._colorB);
 
 	uint16 *textPart = screenText->_text;
-	while (text != outTextPtr)
+	while (text != outTextPtr) {
 		*textPart++ = *text++;
+	}
 	*textPart = 0;
 
 	updateTextInfoPosition(Common::Point(160, 100));
diff --git a/engines/illusions/scriptstack.cpp b/engines/illusions/scriptstack.cpp
index 36cd3cb..0bad714 100644
--- a/engines/illusions/scriptstack.cpp
+++ b/engines/illusions/scriptstack.cpp
@@ -32,8 +32,9 @@ ScriptStack::ScriptStack() {
 }
 
 void ScriptStack::clear() {
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		_stack[i] = (int16)0xEEEE;
+	}
 	_stackPos = 256;
 }
 
diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index f2de9f2..7652471 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -56,8 +56,9 @@ typedef Common::Functor2Mem<Control*, OpCall&, void, SequenceOpcodes> SequenceOp
 
 void SequenceOpcodes::initOpcodes() {
 	// First clear everything
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		_opcodes[i] = 0;
+	}
 	// Register opcodes
 	OPCODE(1, opYield);
 	OPCODE(2, opSetFrameIndex);
@@ -111,8 +112,9 @@ void SequenceOpcodes::initOpcodes() {
 #undef OPCODE
 
 void SequenceOpcodes::freeOpcodes() {
-	for (uint i = 0; i < 256; ++i)
+	for (uint i = 0; i < 256; ++i) {
 		delete _opcodes[i];
+	}
 }
 
 // Opcodes
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index aed389a..d976069 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -356,9 +356,10 @@ void SoundMan::unloadSounds(uint32 soundGroupId) {
 }
 
 Sound *SoundMan::getSound(uint32 soundEffectId) {
-	for (SoundListIterator it = _sounds.begin(); it != _sounds.end(); ++it)
+	for (SoundListIterator it = _sounds.begin(); it != _sounds.end(); ++it) {
 		if ((*it)->_soundEffectId == soundEffectId)
 			return *it;
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index 3c327ba..afc3a6d 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -152,8 +152,9 @@ void ThreadList::updateThreads() {
 				it = _threads.erase(it);
 			} else {
 				int status = kTSRun;
-				while (!thread->_terminated && status != kTSTerminate && status != kTSYield)
+				while (!thread->_terminated && status != kTSTerminate && status != kTSYield) {
 					status = thread->update();
+				}
 				++it;
 			}
 		}
@@ -165,9 +166,10 @@ void ThreadList::updateThreads() {
 }
 
 Thread *ThreadList::findThread(uint32 threadId) {
-	for (Iterator it = _threads.begin(); it != _threads.end(); ++it)
+	for (Iterator it = _threads.begin(); it != _threads.end(); ++it) {
 		if ((*it)->_threadId == threadId && !(*it)->_terminated)
 			return (*it);
+	}
 	return 0;
 }
 
diff --git a/engines/illusions/updatefunctions.cpp b/engines/illusions/updatefunctions.cpp
index 7317508..ae14a0c 100644
--- a/engines/illusions/updatefunctions.cpp
+++ b/engines/illusions/updatefunctions.cpp
@@ -35,8 +35,9 @@ UpdateFunctions::UpdateFunctions() {
 
 UpdateFunctions::~UpdateFunctions() {
 	// Free update functions
-	for (UpdateFunctionListIterator it = _updateFunctions.begin(); it != _updateFunctions.end(); ++it)
+	for (UpdateFunctionListIterator it = _updateFunctions.begin(); it != _updateFunctions.end(); ++it) {
 		delete *it;
+	}
 }
 
 void UpdateFunctions::add(int priority, uint32 sceneId, UpdateFunctionCallback *callback) {
@@ -51,8 +52,9 @@ void UpdateFunctions::add(int priority, uint32 sceneId, UpdateFunctionCallback *
 
 void UpdateFunctions::update() {
 	// Avoid running updates multiple times in the current time slice
-	while (_lastTimerUpdateTime == getCurrentTime())
+	while (_lastTimerUpdateTime == getCurrentTime()) {
 		g_system->delayMillis(10); // CHECKME Timer resolution
+	}
 	_lastTimerUpdateTime = getCurrentTime();
 	UpdateFunctionListIterator it = _updateFunctions.begin();
 	while (it != _updateFunctions.end()) {
@@ -72,9 +74,10 @@ void UpdateFunctions::update() {
 }
 
 void UpdateFunctions::terminateByScene(uint32 sceneId) {
-	for (UpdateFunctionListIterator it = _updateFunctions.begin(); it != _updateFunctions.end(); ++it)
+	for (UpdateFunctionListIterator it = _updateFunctions.begin(); it != _updateFunctions.end(); ++it) {
 		if ((*it)->_sceneId == sceneId)
 			(*it)->terminate();
+	}
 }
 
 } // End of namespace Illusions


Commit: 8e43261d13a81131e0be0bfc75627f7395f78a90
    https://github.com/scummvm/scummvm/commit/8e43261d13a81131e0be0bfc75627f7395f78a90
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Formatting fixes
Simplified some point arithmetic
Lock fixedpoint calcs to float rather than double

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/detection.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/menusystem_duckman.h
    engines/illusions/fixedpoint.cpp
    engines/illusions/graphics.cpp
    engines/illusions/illusions.cpp
    engines/illusions/menusystem.h
    engines/illusions/pathfinder.cpp
    engines/illusions/scriptopcodes.h
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 568a1cc..bd0d724 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -618,7 +618,6 @@ void Control::startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 thread
 }
 
 void Control::sequenceActor() {
-
 	if (_actor->_pauseCtr > 0)
 		return;
 
@@ -665,7 +664,6 @@ void Control::sequenceActor() {
 		//debug(1, "Sequence has finished");
 		_actor->_seqCodeIp = 0;
 	}
-
 }
 
 void Control::setActorIndex(int actorIndex) {
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 2d8df43..c7e79a7 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -531,10 +531,7 @@ void BbdouSpecialCode::setCursorControlRoutine(uint32 objectId, int num) {
 }
 
 Common::Point BbdouSpecialCode::getBackgroundCursorPos(Common::Point cursorPos) {
-	Common::Point pt = _vm->_camera->getScreenOffset();
-	pt.x += cursorPos.x;
-	pt.y += cursorPos.y;
-	return pt;
+	return _vm->_camera->getScreenOffset() + cursorPos;
 }
 
 void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index 63aea79..89fa429 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -140,7 +140,6 @@ SaveStateList IllusionsMetaEngine::listSaves(const char *target) const {
 	pattern += ".???";
 	Common::StringArray filenames;
 	filenames = saveFileMan->listSavefiles(pattern.c_str());
-	Common::sort(filenames.begin(), filenames.end());	// Sort (hopefully ensuring we are sorted numerically..)
 	SaveStateList saveList;
 	for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
 		// Obtain the last 3 digits of the filename, since they correspond to the save slot
@@ -155,6 +154,7 @@ SaveStateList IllusionsMetaEngine::listSaves(const char *target) const {
 			}
 		}
 	}
+	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
 	return saveList;
 }
 
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 56c997d..c30b8ce 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -983,10 +983,7 @@ uint32 IllusionsEngine_Duckman::getObjectActorTypeId(uint32 objectId) {
 }
 
 Common::Point IllusionsEngine_Duckman::convertMousePos(Common::Point mousePos) {
-	Common::Point screenOffsPt = _camera->getScreenOffset();
-	mousePos.x += screenOffsPt.x;
-	mousePos.y += screenOffsPt.y;
-	return mousePos;
+	return mousePos + _camera->getScreenOffset();
 }
 
 void IllusionsEngine_Duckman::startCursorSequence() {
diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h
index edf8620..ee53543 100644
--- a/engines/illusions/duckman/menusystem_duckman.h
+++ b/engines/illusions/duckman/menusystem_duckman.h
@@ -27,14 +27,14 @@
 
 namespace Illusions {
 
-	enum SliderActionType {
-		SFX,
-		MUSIC,
-		VOICE,
-		TEXT_DURATION
-	};
+enum SliderActionType {
+	SFX,
+	MUSIC,
+	VOICE,
+	TEXT_DURATION
+};
 
-	enum {
+enum {
 	kDuckmanMainMenu,
 	kDuckmanLoadGameMenu,
 	kDuckmanLoadGameFailedMenu,
@@ -52,7 +52,7 @@ namespace Illusions {
 class IllusionsEngine_Duckman;
 class MenuActionUpdateSlider;
 
-	class DuckmanMenuSystem : public BaseMenuSystem {
+class DuckmanMenuSystem : public BaseMenuSystem {
 public:
 	DuckmanMenuSystem(IllusionsEngine_Duckman *vm);
 	~DuckmanMenuSystem();
diff --git a/engines/illusions/fixedpoint.cpp b/engines/illusions/fixedpoint.cpp
index f240b14..18c76df 100644
--- a/engines/illusions/fixedpoint.cpp
+++ b/engines/illusions/fixedpoint.cpp
@@ -52,7 +52,7 @@ int16 fixedTrunc(FixedPoint16 value) {
 FixedPoint16 fixedDistance(FixedPoint16 x1, FixedPoint16 y1, FixedPoint16 x2, FixedPoint16 y2) {
 	float xd = fixedToFloat(x1) - fixedToFloat(x2);
 	float yd = fixedToFloat(y1) - fixedToFloat(y2);
-	if (xd != 0.0 || yd != 0.0)
+	if (xd != 0.0f || yd != 0.0f)
 		return floatToFixed(sqrt(xd * xd + yd * yd));
 	return 0;
 }
diff --git a/engines/illusions/graphics.cpp b/engines/illusions/graphics.cpp
index 772d627..e359683 100644
--- a/engines/illusions/graphics.cpp
+++ b/engines/illusions/graphics.cpp
@@ -76,8 +76,7 @@ void NamedPoints::load(uint count, Common::SeekableReadStream &stream) {
 void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
 	pt.x = stream.readSint16LE();
 	pt.y = stream.readSint16LE();
-	debug(0, "loadPoint() x: %d; y: %d",
-		pt.x, pt.y);
+	debug(0, "loadPoint() x: %d; y: %d", pt.x, pt.y);
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 14fddc5..1108681 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -242,7 +242,7 @@ int IllusionsEngine::convertPanXCoord(int16 x) {
 	int16 diff = x - _camera->getCurrentPan().x;
 	int16 absX = ABS(diff);
 	int newX = 0;
-	if ( absX < 160) {
+	if (absX < 160) {
 		newX = (diff << 7) / 320;
 	} else if (diff < 0) {
 		newX = -64;
diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h
index dd45129..ce5b136 100644
--- a/engines/illusions/menusystem.h
+++ b/engines/illusions/menusystem.h
@@ -171,11 +171,6 @@ protected:
 	virtual void playSoundEffect(int sfxId) = 0;
 };
 
-/*
-
-
-*/
-
 class MenuTextBuilder {
 public:
 	MenuTextBuilder();
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index 936e855..f021b2d 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -30,7 +30,7 @@ PointArray *PathFinder::findPath(Camera *camera, Common::Point sourcePt, Common:
 	PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions) {
 	Common::Point cameraPt = camera->getScreenOffset();
 	_screenRect.p0 = cameraPt;
-	_screenRect.p1.x = cameraPt.x + 320; //TODO fix me get screen dimentions here.
+	_screenRect.p1.x = cameraPt.x + 320; //TODO fix me get screen dimensions here.
 	_screenRect.p1.y = cameraPt.y + 200;
 	_walkPoints = walkPoints;
 	_walkRects = walkRects;
diff --git a/engines/illusions/scriptopcodes.h b/engines/illusions/scriptopcodes.h
index 9314a60..e219210 100644
--- a/engines/illusions/scriptopcodes.h
+++ b/engines/illusions/scriptopcodes.h
@@ -60,7 +60,7 @@ protected:
 };
 
 // Convenience macros
-#define	ARG_SKIP(x) opCall.skip(x);
+#define ARG_SKIP(x) opCall.skip(x);
 #define ARG_BYTE(name) byte name = opCall.readByte(); debug(5, "ARG_BYTE(" #name " = %d)", name);
 #define ARG_INT16(name) int16 name = opCall.readSint16(); debug(5, "ARG_INT16(" #name " = %d)", name);
 #define ARG_UINT32(name) uint32 name = opCall.readUint32(); debug(5, "ARG_UINT32(" #name " = %08X)", name);
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index a2e9eba..3691159 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -74,7 +74,7 @@ int TalkThread_Duckman::onUpdate() {
 		if (_vm->checkActiveTalkThreads())
 			return kTSYield;
 		_status = 3;
-		// Fallthrough to status 2
+		// fall through
 
 	case 2:
 		talkEntry = getTalkResourceEntry(_talkId);
@@ -101,13 +101,13 @@ int TalkThread_Duckman::onUpdate() {
 		if (_objectId == 0 || _durationMult == 0)
 			_flags |= 8;
 		_status = 3;
-		// Fallthrough to status 3
+		// fall through
 
 	case 3:
 		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
 			return kTSYield;
 		_status = 4;
-		// Fallthrough to status 4
+		// fall through
 
 	case 4:
 		if (!(_flags & 8) ) {


Commit: 02eaa4c83d643d8a14a25d16a9cacbd3bfedf006
    https://github.com/scummvm/scummvm/commit/02eaa4c83d643d8a14a25d16a9cacbd3bfedf006
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Refactor savegame thumbnail logic to be inline with rest of tree

Changed paths:
    engines/illusions/detection.cpp
    engines/illusions/illusions.h
    engines/illusions/saveload.cpp


diff --git a/engines/illusions/detection.cpp b/engines/illusions/detection.cpp
index 89fa429..cf771f8 100644
--- a/engines/illusions/detection.cpp
+++ b/engines/illusions/detection.cpp
@@ -147,7 +147,7 @@ SaveStateList IllusionsMetaEngine::listSaves(const char *target) const {
 		if (slotNum >= 0 && slotNum <= 999) {
 			Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
 			if (in) {
-				if (Illusions::IllusionsEngine::readSaveHeader(in, false, header) == Illusions::IllusionsEngine::kRSHENoError) {
+				if (Illusions::IllusionsEngine::readSaveHeader(in, header) == Illusions::IllusionsEngine::kRSHENoError) {
 					saveList.push_back(SaveStateDescriptor(slotNum, header.description));
 				}
 				delete in;
@@ -164,7 +164,7 @@ SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target,
 	if (in) {
 		Illusions::IllusionsEngine::SaveHeader header;
 		Illusions::IllusionsEngine::kReadSaveHeaderError error;
-		error = Illusions::IllusionsEngine::readSaveHeader(in, true, header);
+		error = Illusions::IllusionsEngine::readSaveHeader(in, header, false);
 		delete in;
 		if (error == Illusions::IllusionsEngine::kRSHENoError) {
 			SaveStateDescriptor desc(slot, header.description);
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 9cf1e0b..6855792 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -244,7 +244,7 @@ public:
 	const char *getSavegameFilename(int num);
 	bool existsSavegame(int num);
 	static Common::String getSavegameFilename(const Common::String &target, int num);
-	static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header);
+	static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail = true);
 
 };
 
diff --git a/engines/illusions/saveload.cpp b/engines/illusions/saveload.cpp
index 233771a..9409173 100644
--- a/engines/illusions/saveload.cpp
+++ b/engines/illusions/saveload.cpp
@@ -31,7 +31,7 @@ namespace Illusions {
 
 #define ILLUSIONS_SAVEGAME_VERSION 0
 
-IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) {
+IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) {
 
 	header.version = in->readUint32LE();
 	if (header.version > ILLUSIONS_SAVEGAME_VERSION)
@@ -43,10 +43,8 @@ IllusionsEngine::kReadSaveHeaderError IllusionsEngine::readSaveHeader(Common::Se
 		header.description += (char)in->readByte();
 	}
 
-	if (loadThumbnail) {
-		Graphics::loadThumbnail(*in, header.thumbnail);
-	} else {
-		Graphics::skipThumbnail(*in);
+	if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
+		return kRSHEIoError;
 	}
 
 	// Not used yet, reserved for future usage
@@ -110,7 +108,7 @@ bool IllusionsEngine::loadgame(const char *filename) {
 
 	SaveHeader header;
 
-	kReadSaveHeaderError errorCode = readSaveHeader(in, false, header);
+	kReadSaveHeaderError errorCode = readSaveHeader(in, header);
 
 	if (errorCode != kRSHENoError) {
 		warning("Error loading savegame '%s'", filename);


Commit: c9a3377408bb47ef3fe81755e8471f746362d75f
    https://github.com/scummvm/scummvm/commit/c9a3377408bb47ef3fe81755e8471f746362d75f
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Standardized fall through comments

Changed paths:
    engines/illusions/threads/talkthread.cpp


diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index d20f94a..74fd162 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -101,7 +101,7 @@ int TalkThread::onUpdate() {
 		if (_vm->checkActiveTalkThreads())
 			return kTSYield;
 		_status = 3;
-		// Fallthrough to status 3
+		// fall through
 
 	case 3:
 		talkEntry = getTalkResourceEntry(_talkId);
@@ -128,13 +128,13 @@ int TalkThread::onUpdate() {
 		if (_objectId == 0 || _durationMult == 0)
 			_flags |= 8;
 		_status = 4;
-		// Fallthrough to status 4
+		// fall through
 
 	case 4:
 		if (!(_flags & 4) && !_vm->_soundMan->isVoiceCued())
 			return kTSYield;
 		_status = 5;
-		// Fallthrough to status 5
+		// fall through
 
 	case 5:
 		if (!(_flags & 8))


Commit: 24a4c6367ced2bfc76ae2326b077a8b324c6b6a5
    https://github.com/scummvm/scummvm/commit/24a4c6367ced2bfc76ae2326b077a8b324c6b6a5
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: update _vm->_unpauseControlActorFlag when unpausing actors

Changed paths:
    engines/illusions/actor.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index bd0d724..a5276db 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -1546,6 +1546,7 @@ void Controls::unpauseActors(uint32 objectId) {
 			control->_actor->unpause();
 		}
 	}
+	_vm->_unpauseControlActorFlag = true;
 }
 
 


Commit: c01a1269b6bd6bfbce0de201490fc2c1d61c2250
    https://github.com/scummvm/scummvm/commit/c01a1269b6bd6bfbce0de201490fc2c1d61c2250
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Clear fader when loading/unpausing backgrounds

(cherry picked from commit 3367aafae37d6c28c1ab1e8323613a56a44e3a5c)

ILLUSIONS: Delete dictionary list before it gets removed

(cherry picked from commit d9d0c11)

ILLUSIONS: Implement PathFinder::postProcess

(cherry picked from commit ee98dfc)

Changed paths:
    engines/illusions/dictionary.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.h
    engines/illusions/pathfinder.cpp
    engines/illusions/pathfinder.h
    engines/illusions/resources/backgroundresource.cpp
    engines/illusions/resources/midiresource.cpp
    engines/illusions/resources/midiresource.h
    engines/illusions/sound.cpp
    engines/illusions/sound.h


diff --git a/engines/illusions/dictionary.h b/engines/illusions/dictionary.h
index c0d60a9..d5c2d66 100644
--- a/engines/illusions/dictionary.h
+++ b/engines/illusions/dictionary.h
@@ -67,8 +67,10 @@ public:
 		if (it != _map.end()) {
 			list = it->_value;
 			list->pop_back();
-			if (list->empty())
+			if (list->empty()) {
 				_map.erase(id);
+				delete list;
+			}
 		}
 	}
 
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index c30b8ce..9eae7a5 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -390,6 +390,18 @@ void IllusionsEngine_Duckman::updateFader() {
 	}
 }
 
+void IllusionsEngine_Duckman::clearFader() {
+	_fader->_active = false;
+	_fader->_currValue = 255;
+	_fader->_minValue = 255;
+	_fader->_maxValue = 255;
+	_fader->_firstIndex = 1;
+	_fader->_lastIndex = 256;
+	_fader->_startTime = 0;
+	_fader->_duration = 0;
+	_fader->_notifyThreadId = 0;
+}
+
 void IllusionsEngine_Duckman::pauseFader() {
 	_fader->_paused = true;
 	_fader->_startTime = getCurrentTime() - _fader->_startTime;
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index feacd50..46bf15e 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -113,6 +113,7 @@ public:
 
 	void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
 	void updateFader();
+	void clearFader();
 	void pauseFader();
 	void unpauseFader();
 
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index e6f286c..8764f92 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -640,7 +640,7 @@ void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &
 void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(duration);
 	ARG_INT16(finalVolume);
-	_vm->_soundMan->fadeMidiMusic(finalVolume, duration);
+	//FIXME _vm->_soundMan->fadeMidiMusic(finalVolume, duration);
 }
 
 void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 6855792..438ab04 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -173,6 +173,7 @@ public:
 	bool isSoundActive();
 
 	virtual void updateFader() {};
+	virtual void clearFader() {};
 	virtual void pauseFader() {};
 	virtual void unpauseFader() {};
 
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index f021b2d..2f72356 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -73,7 +73,7 @@ PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point d
 		}
 
 		free(_pathBytes);
-		// TODO postProcess(sourcePt, foundPath);
+		postProcess(sourcePt, foundPath);
 
 	} else {
 		foundPath->push_back(destPt);
@@ -81,6 +81,19 @@ PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point d
 	return foundPath;
 }
 
+void PathFinder::postProcess(Common::Point sourcePt, PointArray *foundPath) {
+	// For each three points A, B and C, removes B if the line between A and C is not blocked
+    for (uint index = 0; index + 2 < foundPath->size(); ++index) {
+		PathLine line;
+		line.p0 = index == 0 ? sourcePt : (*foundPath)[index - 1];
+		line.p1 = (*foundPath)[index + 1];
+		if (!isLineBlocked(line)) {
+			debug("remove point");
+			foundPath->remove_at(index);
+		}
+    }
+}
+
 bool PathFinder::isLineBlocked(PathLine &line) {
 	for (uint i = 0; i < _walkRects->size(); ++i) {
 		if (calcLineStatus(line, (*_walkRects)[i], 0) != 3)
diff --git a/engines/illusions/pathfinder.h b/engines/illusions/pathfinder.h
index 2b56b2d..2f8d1b7 100644
--- a/engines/illusions/pathfinder.h
+++ b/engines/illusions/pathfinder.h
@@ -48,6 +48,7 @@ protected:
 	WidthHeight _bgDimensions;
 	byte *_pathBytes;
 	PointArray *findPathInternal(Common::Point sourcePt, Common::Point destPt);
+	void postProcess(Common::Point sourcePt, PointArray *foundPath);
 	bool isLineBlocked(PathLine &line);
 	int calcLineDistance(PathLine &line);
 	bool findClosestPt(Common::Point &sourcePt, Common::Point &closestPt, Common::Point &destPt);
diff --git a/engines/illusions/resources/backgroundresource.cpp b/engines/illusions/resources/backgroundresource.cpp
index 2a37bba..933ba25 100644
--- a/engines/illusions/resources/backgroundresource.cpp
+++ b/engines/illusions/resources/backgroundresource.cpp
@@ -402,7 +402,8 @@ void BackgroundInstance::load(Resource *resource) {
 
 	registerResources();
 
-	// TODO camera_fadeClear();
+	_vm->clearFader();
+
 	int index = _bgRes->findMasterBgIndex();
 	_vm->_camera->set(_bgRes->_bgInfos[index - 1]._panPoint, _bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
 
@@ -442,7 +443,7 @@ void BackgroundInstance::unpause() {
 		_vm->_screenPalette->setPalette(_savedPalette, 1, 256);
 		delete[] _savedPalette;
 		_savedPalette = 0;
-		// TODO _vm->_screen->_fadeClear();
+		_vm->clearFader();
 		_vm->_camera->setActiveState(_savedCameraState);
 		_vm->_backgroundInstances->refreshPan();
 	}
diff --git a/engines/illusions/resources/midiresource.cpp b/engines/illusions/resources/midiresource.cpp
index 5e6e85a..060565b 100644
--- a/engines/illusions/resources/midiresource.cpp
+++ b/engines/illusions/resources/midiresource.cpp
@@ -29,13 +29,71 @@ namespace Illusions {
 
 void MidiGroupResourceLoader::load(Resource *resource) {
 	debug(1, "MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
+	MidiGroupInstance *midiGroupInstance = new MidiGroupInstance(_vm);
+	midiGroupInstance->load(resource);
+	resource->_instance = midiGroupInstance;
+}
 
-	// TODO
+bool MidiGroupResourceLoader::isFlag(int flag) {
+	return
+		flag == kRlfLoadFile/* ||
+		flag == kRlfFreeDataAfterLoad*/;
+}
 
+// MidiMusic
+
+void MidiMusic::load(Common::SeekableReadStream &stream) {
+	_musicId = stream.readUint32LE();
+	_looping = stream.readUint16LE() != 0;
+	stream.skip(2 + 32 + 4); // Skip unused/unknown values
+	debug(1, "MidiMusic::load() _musicId: %08X; _looping: %d", _musicId, _looping);
 }
 
-bool MidiGroupResourceLoader::isFlag(int flag) {
-	return false;
+// MidiGroupResource
+
+MidiGroupResource::MidiGroupResource()
+	: _midiMusicCount(0), _midiMusic(0) {
+}
+
+MidiGroupResource::~MidiGroupResource() {
+	delete[] _midiMusic;
+}
+
+void MidiGroupResource::load(byte *data, uint32 dataSize) {
+	Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
+
+	stream.skip(4);
+	_midiMusicCount = stream.readUint16LE();
+	stream.skip(2);
+	uint32 midiMusicOffs = stream.readUint32LE();
+	debug("_midiMusicCount: %d; midiMusicOffs: %08X", _midiMusicCount, midiMusicOffs);
+	_midiMusic = new MidiMusic[_midiMusicCount];
+	stream.seek(midiMusicOffs);
+	for (uint i = 0; i < _midiMusicCount; ++i)
+		_midiMusic[i].load(stream);
+
+}
+
+// MidiGroupInstance
+
+MidiGroupInstance::MidiGroupInstance(IllusionsEngine *vm)
+	: _vm(vm), _midiGroupResource(0) {
+}
+
+void MidiGroupInstance::load(Resource *resource) {
+	_midiGroupResource = new MidiGroupResource();
+	_midiGroupResource->load(resource->_data, resource->_dataSize);
+	for (uint i = 0; i < _midiGroupResource->_midiMusicCount; ++i) {
+		// TODO
+		// SoundEffect *soundEffect = &_soundGroupResource->_soundEffects[i];
+		// _vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
+	}
+	_resId = resource->_resId;
+}
+
+void MidiGroupInstance::unload() {
+	// _vm->_soundMan->unloadSounds(_resId);
+	delete _midiGroupResource;
 }
 
 } // End of namespace Illusions
diff --git a/engines/illusions/resources/midiresource.h b/engines/illusions/resources/midiresource.h
index 1cd3f80..fee4486 100644
--- a/engines/illusions/resources/midiresource.h
+++ b/engines/illusions/resources/midiresource.h
@@ -40,6 +40,33 @@ protected:
 	IllusionsEngine *_vm;
 };
 
+struct MidiMusic {
+	uint32 _musicId;
+	bool _looping;
+	void load(Common::SeekableReadStream &stream);
+};
+
+class MidiGroupResource {
+public:
+	MidiGroupResource();
+	~MidiGroupResource();
+	void load(byte *data, uint32 dataSize);
+public:
+	uint _midiMusicCount;
+	MidiMusic *_midiMusic;
+};
+
+class MidiGroupInstance : public ResourceInstance {
+public:
+	MidiGroupInstance(IllusionsEngine *vm);
+	virtual void load(Resource *resource);
+	virtual void unload();
+public:
+	IllusionsEngine *_vm;	
+	MidiGroupResource *_midiGroupResource;
+	uint32 _resId;
+};
+
 } // End of namespace Illusions
 
 #endif // ILLUSIONS_SOUNDRESOURCE_H
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index d976069..1e8ca49 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -23,6 +23,7 @@
 #include "common/config-manager.h"
 #include "illusions/illusions.h"
 #include "illusions/sound.h"
+#include "audio/mididrv.h"
 #include "audio/midiparser.h"
 
 namespace Illusions {
@@ -76,68 +77,125 @@ bool MusicPlayer::isPlaying() {
 
 // MidiPlayer
 
-MidiPlayer::MidiPlayer() {
-	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
-	_driver = MidiDriver::createMidi(dev);
-	assert(_driver);
-	_paused = false;
+MidiPlayer::MidiPlayer()
+	: _isIdle(true), _isPlaying(false), _isCurrentlyPlaying(false), _isLooped(false),
+	_loopedMusicId(0), _queuedMusicId(0), _loadedMusicId(0),
+	_data(0), _dataSize(0) {
 
+	_data = 0;
+	_dataSize = 0;
+	_isGM = false;
+
+	MidiPlayer::createDriver();
 
 	int ret = _driver->open();
 	if (ret == 0) {
-		_driver->sendGMReset();
+		if (_nativeMT32)
+			_driver->sendMT32Reset();
+		else
+			_driver->sendGMReset();
 
 		_driver->setTimerCallback(this, &timerCallback);
 	}
 }
 
-void MidiPlayer::play(const Common::String &filename) {
-	Common::StackLock lock(_mutex);
+MidiPlayer::~MidiPlayer() {
+	sysMidiStop();
+}
 
-	stop();
+bool MidiPlayer::play(uint32 musicId) {
+	debug("MidiPlayer::play(%08X)", musicId);
+	bool isMusicLooping = true; // TODO Use actual flag
 
-	Common::File *fd = new Common::File();
-	if (!fd->open(filename)) {
-		delete fd;
-		error("MidiPlayer::play() Could not load %s", filename.c_str());
-	}
+	if (!_isIdle)
+		return false;
 
-	uint32 size = (uint32)fd->size();
-	_midiData = (uint8 *)malloc(size);
+	if (_isPlaying) {
+		if (isMusicLooping) {
+			_loopedMusicId = musicId;
+		} else {
+			_queuedMusicId = musicId;
+			_isIdle = false;
+		}
+		return true;
+	}
 
-	if (_midiData) {
-		fd->read(_midiData, size);
+	if (_isCurrentlyPlaying && _loopedMusicId == musicId)
+		return true;
 
-		syncVolume();	// FIXME: syncVolume calls setVolume which in turn also locks the mutex! ugh
+	sysMidiStop();
 
-		_parser = MidiParser::createParser_SMF();
-		_parser->loadMusic(_midiData, size);
-		_parser->setTrack(0);
-		_parser->setMidiDriver(this);
-		_parser->setTimerRate(_driver->getBaseTempo());
-		_isLooping = true;
+    _isLooped = isMusicLooping;
+    if (_isLooped) {
+		_loopedMusicId = musicId;
+    } else {
 		_isPlaying = true;
 	}
-	fd->close();
-	delete fd;
+
+	sysMidiPlay(musicId);
+
+	_isCurrentlyPlaying = true;
+
+	return true;
 }
 
-void MidiPlayer::pause(bool p) {
-	_paused = p;
+void MidiPlayer::stop() {
+	sysMidiStop();
+	_isIdle = true;
+	_isPlaying = false;
+	_isCurrentlyPlaying = false;
+	_loopedMusicId = 0;
+	_queuedMusicId = 0;
+}
 
-	for (int i = 0; i < kNumChannels; ++i) {
-		if (_channelsTable[i]) {
-			_channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
-		}
+void MidiPlayer::sysMidiPlay(uint32 musicId) {
+	Common::StackLock lock(_mutex);
+
+	Common::String filename = Common::String::format("%08x.mid", musicId);
+	debug(0, "MidiPlayer::sysMidiPlay() %s", filename.c_str());
+
+	Common::File fd;
+	if (!fd.open(filename)) {
+		error("MidiPlayer::sysMidiPlay() Could not open %s", filename.c_str());
+	}
+
+	_dataSize = fd.size();
+	_data = new byte[_dataSize];
+	fd.read(_data, _dataSize);
+
+	_isGM = true;
+	_loadedMusicId = musicId;
+
+	MidiParser *parser = MidiParser::createParser_SMF();
+	if (parser->loadMusic(_data, _dataSize)) {
+		parser->setTrack(0);
+		parser->setMidiDriver(this);
+		parser->setTimerRate(_driver->getBaseTempo());
+		parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+
+		_parser = parser;
+
+		syncVolume();
+
+		Audio::MidiPlayer::_isLooping = _isLooped;
+		Audio::MidiPlayer::_isPlaying = true;
 	}
 }
 
-void MidiPlayer::onTimer() {
-	Common::StackLock lock(_mutex);
+void MidiPlayer::sysMidiStop() {
+	Audio::MidiPlayer::stop();
+	delete[] _data;
+	_data = 0;
+	_dataSize = 0;
+	_loadedMusicId = 0;
+}
 
-	if (!_paused && _isPlaying && _parser) {
-		_parser->onTimer();
+void MidiPlayer::send(uint32 b) {
+	if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+		b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
 	}
+
+	Audio::MidiPlayer::send(b);
 }
 
 void MidiPlayer::sendToChannel(byte channel, uint32 b) {
@@ -153,9 +211,20 @@ void MidiPlayer::sendToChannel(byte channel, uint32 b) {
 		_channelsTable[channel]->send(b);
 }
 
-void MidiPlayer::fade(int16 finalVolume, int16 duration) {
-	//TODO fade here.
-	debug(0, "Fade midi. finalVolume: %d, duration: %d", finalVolume, duration);
+void MidiPlayer::endOfTrack() {
+	uint32 nextMusicId = _queuedMusicId;
+	if (nextMusicId == 0)
+		nextMusicId = _loopedMusicId;
+
+	if (_isLooped && _loadedMusicId == nextMusicId) {
+		Audio::MidiPlayer::endOfTrack();
+		return;
+	}
+
+	sysMidiStop();
+	_queuedMusicId = 0;
+	_isIdle = true;
+	play(nextMusicId);
 }
 
 // VoicePlayer
@@ -279,6 +348,7 @@ SoundMan::~SoundMan() {
 }
 
 void SoundMan::update() {
+	updateMidi();
 	// TODO voc_testCued();
 	if (_musicNotifyThreadId && !_musicPlayer->isPlaying())
 		_vm->notifyThreadId(_musicNotifyThreadId);
@@ -294,6 +364,20 @@ void SoundMan::stopMusic() {
 	_musicPlayer->stop();
 }
 
+void SoundMan::playMidiMusic(uint32 musicId) {
+	if (!_midiPlayer->play(musicId)) {
+		_midiMusicQueue.push_back(musicId);
+	}
+}
+
+void SoundMan::stopMidiMusic() {
+	_midiPlayer->stop();
+}
+
+void SoundMan::clearMidiMusicQueue() {
+	_midiMusicQueue.clear();
+}
+
 bool SoundMan::cueVoice(const char *voiceName) {
 	return _voicePlayer->cue(voiceName);
 }
@@ -363,17 +447,13 @@ Sound *SoundMan::getSound(uint32 soundEffectId) {
 	return 0;
 }
 
-void SoundMan::playMidiMusic(uint32 musicId) {
-	Common::String filename = Common::String::format("%08x.MID", musicId);
-	_midiPlayer->play(filename);
-}
-
-void SoundMan::stopMidiMusic() {
-	_midiPlayer->stop();
-}
-
-void SoundMan::fadeMidiMusic(int16 finalVolume, int16 duration) {
-	_midiPlayer->fade(finalVolume, duration);
+void SoundMan::updateMidi() {
+	if (_midiPlayer->isIdle() & !_midiMusicQueue.empty()) {
+		uint32 musicId = _midiMusicQueue.front();
+		_midiMusicQueue.remove_at(0);
+		_midiPlayer->play(musicId);
+	}
+	// TODO Update music volume fading
 }
 
 void SoundMan::setMusicVolume(uint16 volume) {
@@ -408,5 +488,4 @@ uint16 SoundMan::getSfxVolume() {
 uint16 SoundMan::getSpeechVolume() {
 	return (uint16)ConfMan.getInt("speech_volume");
 }
-
 } // End of namespace Illusions
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index f6a96d5..8d5f21e 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -25,9 +25,10 @@
 
 #include "illusions/graphics.h"
 #include "audio/audiostream.h"
+#include "audio/decoders/wave.h"
 #include "audio/midiplayer.h"
 #include "audio/mixer.h"
-#include "audio/decoders/wave.h"
+#include "common/array.h"
 #include "common/list.h"
 
 namespace Illusions {
@@ -50,23 +51,26 @@ protected:
 class MidiPlayer : public Audio::MidiPlayer {
 public:
 	MidiPlayer();
-
-	void pause(bool p);
-	void play(const Common::String &filename);
-	void fade(int16 finalVolume, int16 duration);
-
-	// The following line prevents compiler warnings about hiding the pause()
-	// method from the parent class.
-	// FIXME: Maybe the pause(bool p) method should be removed and the
-	// pause/resume methods of the parent class be used instead?
-	virtual void pause() { Audio::MidiPlayer::pause(); }
-
-	// Overload Audio::MidiPlayer method
+	~MidiPlayer();
+	bool play(uint32 musicId);
+	void stop();
+	bool isIdle() const { return _isIdle; }
+protected:
+	bool _isIdle;
+	bool _isPlaying;
+	bool _isCurrentlyPlaying;
+	bool _isLooped;
+	uint32 _loopedMusicId;
+	uint32 _queuedMusicId;
+	uint32 _loadedMusicId;
+	byte *_data;
+	uint _dataSize;
+	bool _isGM;
+	void sysMidiPlay(uint32 musicId);
+	void sysMidiStop();
+	virtual void send(uint32 b);
 	virtual void sendToChannel(byte channel, uint32 b);
-	virtual void onTimer();
-
-private:
-	bool _paused;
+	virtual void endOfTrack();
 };
 
 class VoicePlayer {
@@ -116,7 +120,7 @@ public:
 
 	void playMidiMusic(uint32 musicId);
 	void stopMidiMusic();
-	void fadeMidiMusic(int16 finalVolume, int16 duration);
+	void clearMidiMusicQueue();
 
 	uint16 getMusicVolume();
 	uint16 getSfxVolume();
@@ -149,7 +153,9 @@ protected:
 	MidiPlayer *_midiPlayer;
 	VoicePlayer *_voicePlayer;
 	SoundList _sounds;
+	Common::Array<uint32> _midiMusicQueue;
 	Sound *getSound(uint32 soundEffectId);
+	void updateMidi();
 	uint16 calcAdjustedVolume(const Common::String &volumeConfigKey, uint16 volume);
 };
 


Commit: 960d79ed5acde4d1dda7c123e224b551c7df7260
    https://github.com/scummvm/scummvm/commit/960d79ed5acde4d1dda7c123e224b551c7df7260
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement video player

(cherry picked from commit 62577c8)

Changed paths:
  A engines/illusions/duckman/duckman_videoplayer.cpp
  A engines/illusions/duckman/duckman_videoplayer.h
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h


diff --git a/engines/illusions/duckman/duckman_videoplayer.cpp b/engines/illusions/duckman/duckman_videoplayer.cpp
new file mode 100644
index 0000000..ae17d32
--- /dev/null
+++ b/engines/illusions/duckman/duckman_videoplayer.cpp
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *                                                                                                                                  
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/duckman/illusions_duckman.h"
+#include "illusions/duckman/duckman_videoplayer.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+#include "engines/util.h"
+
+namespace Illusions {
+
+// DuckmanVideoPlayer
+
+DuckmanVideoPlayer::DuckmanVideoPlayer(IllusionsEngine_Duckman *vm)
+	: _vm(vm), _videoDecoder(0) {
+}
+
+DuckmanVideoPlayer::~DuckmanVideoPlayer() {
+	delete _videoDecoder;
+}
+
+void DuckmanVideoPlayer::start(uint32 videoId, uint32 callingThreadId) {
+	debug(0, "DuckmanVideoPlayer::play(%08X, %08X)", videoId, callingThreadId);
+	_callingThreadId = callingThreadId;
+	_vm->_input->discardAllEvents();
+	Common::String filename = Common::String::format("%08x.avi", videoId);
+	_videoDecoder = new Video::AVIDecoder();
+	if (!_videoDecoder->loadFile(filename)) {
+		delete _videoDecoder;
+		_videoDecoder = 0;
+		warning("Unable to open video %s", filename.c_str());
+		return;
+	}
+	_videoDecoder->start();
+}
+
+void DuckmanVideoPlayer::stop() {
+	_vm->_input->discardAllEvents();
+	delete _videoDecoder;
+	_videoDecoder = 0;
+	if (_callingThreadId != 0) {
+		_vm->notifyThreadId(_callingThreadId);
+		_callingThreadId = 0;
+	}
+}
+
+void DuckmanVideoPlayer::update() {
+	if (_vm->_input->pollEvent(kEventSkip) || _videoDecoder->endOfVideo()) {
+		stop();
+	} else if (_videoDecoder->needsUpdate()) {
+		const Graphics::Surface *frame = _videoDecoder->decodeNextFrame();
+		Graphics::Surface *backSurface = _vm->_screen->getBackSurface();
+		if (frame->format.bytesPerPixel == g_system->getScreenFormat().bytesPerPixel) {
+			const int width = MIN(frame->w, backSurface->w);
+			const int height = MIN(frame->h, backSurface->h);
+			const byte *src = (const byte*)frame->getPixels();
+			byte *dest = (byte*)backSurface->getPixels();
+			for (int yc = 0; yc < height; ++yc) {
+				memcpy(dest, src, width);
+				src += frame->pitch;
+				dest += backSurface->pitch;
+			}
+		}
+		if (_videoDecoder->hasDirtyPalette()) {
+			const byte *palette = _videoDecoder->getPalette();
+			byte palette4[1024];
+			for (uint i = 0; i < 256; ++i) {
+				palette4[i * 4 + 0] = palette[i * 3 + 0];
+				palette4[i * 4 + 1] = palette[i * 3 + 1];
+				palette4[i * 4 + 2] = palette[i * 3 + 2];
+			}
+			_vm->_screenPalette->setPalette(palette4, 1, 256);
+		}
+	}
+}
+
+bool DuckmanVideoPlayer::isPlaying() const {
+	return _videoDecoder != 0;
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/duckman/duckman_videoplayer.h b/engines/illusions/duckman/duckman_videoplayer.h
new file mode 100644
index 0000000..d2d7f1f
--- /dev/null
+++ b/engines/illusions/duckman/duckman_videoplayer.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_DUCKMAN_VIDEOPLAYER_H
+#define ILLUSIONS_DUCKMAN_VIDEOPLAYER_H
+
+#include "illusions/illusions.h"
+#include "video/avi_decoder.h"
+
+namespace Illusions {
+
+class IllusionsEngine_Duckman;
+
+class DuckmanVideoPlayer {
+public:
+	DuckmanVideoPlayer(IllusionsEngine_Duckman *vm);
+	~DuckmanVideoPlayer();
+	void start(uint32 videoId, uint32 callingThreadId);
+	void stop();
+	void update();
+	bool isPlaying() const;
+public:	
+	IllusionsEngine_Duckman *_vm;
+	Video::VideoDecoder *_videoDecoder;
+	uint32 _callingThreadId;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_DUCKMAN_VIDEOPLAYER_H
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index 9eae7a5..d22b947 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -26,6 +26,7 @@
 #include "illusions/duckman/gamestate_duckman.h"
 #include "illusions/duckman/menusystem_duckman.h"
 #include "illusions/duckman/scriptopcodes_duckman.h"
+#include "illusions/duckman/duckman_videoplayer.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
@@ -116,6 +117,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
 	_menuSystem = new DuckmanMenuSystem(this);
+	_videoPlayer = new DuckmanVideoPlayer(this);
 	_gameState = new Duckman_GameState(this);
 
 	_fader = new Fader();
@@ -215,6 +217,7 @@ Common::Error IllusionsEngine_Duckman::run() {
 
 	delete _gameState;
 	delete _menuSystem;
+	delete _videoPlayer;
 	delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
@@ -273,6 +276,7 @@ void IllusionsEngine_Duckman::initInput() {
 		(this, &IllusionsEngine_Duckman::callback));
 
 void IllusionsEngine_Duckman::initUpdateFunctions() {
+	UPDATEFUNCTION(25, 0, updateVideoPlayer);
 	UPDATEFUNCTION(30, 0, updateScript);
 	UPDATEFUNCTION(50, 0, updateActors);
 	UPDATEFUNCTION(60, 0, updateSequences);
@@ -412,6 +416,20 @@ void IllusionsEngine_Duckman::unpauseFader() {
 	_fader->_paused = false;
 }
 
+int IllusionsEngine_Duckman::updateVideoPlayer(uint flags) {
+	if (_videoPlayer->isPlaying())
+		_videoPlayer->update();
+	return kUFNext;
+}
+
+void IllusionsEngine_Duckman::playVideo(uint32 videoId, uint32 callingThreadId) {
+	_videoPlayer->start(videoId, callingThreadId);
+}
+
+bool IllusionsEngine_Duckman::isVideoPlaying() {
+	return _videoPlayer->isPlaying();
+}
+
 void IllusionsEngine_Duckman::setDefaultTextCoords() {
 	WidthHeight dimensions;
 	dimensions._width = 300;
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index 46bf15e..d4583bc 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -33,6 +33,7 @@ class Dictionary;
 class ScriptStack;
 class DuckmanDialogSystem;
 class DuckmanMenuSystem;
+class DuckmanVideoPlayer;
 
 struct Cursor_Duckman {
 	int _gameState;
@@ -101,6 +102,7 @@ public:
 
 	ScreenShaker *_screenShaker;
 	DuckmanMenuSystem *_menuSystem;
+	DuckmanVideoPlayer *_videoPlayer;
 
 	void initInput();
 
@@ -117,6 +119,10 @@ public:
 	void pauseFader();
 	void unpauseFader();
 
+	int updateVideoPlayer(uint flags);
+	void playVideo(uint32 videoId, uint32 callingThreadId);
+	bool isVideoPlaying();
+
 	void setDefaultTextCoords();
 
 	void loadSpecialCode(uint32 resId);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 8764f92..01a9c96 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -583,13 +583,13 @@ void ScriptOpcodes_Duckman::opStartCursorHoldingObject(ScriptThread *scriptThrea
 
 void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
-	ARG_UINT32(objectId);
-	// NOTE This has no attached objectId or priority
-	_vm->playVideo(0, objectId, 0, opCall._threadId);
-
+	ARG_UINT32(videoId);
+#if 1 // TODO DEBUG Set to 0 to skip videos
+	_vm->playVideo(videoId, opCall._threadId);
+#else
 	//DEBUG Resume calling thread, later done by the video player
 	_vm->notifyThreadId(opCall._threadId);
-
+#endif
 }
 
 void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
@@ -640,7 +640,7 @@ void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &
 void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(duration);
 	ARG_INT16(finalVolume);
-	//FIXME _vm->_soundMan->fadeMidiMusic(finalVolume, duration);
+	// TODO _vm->fadeMidiMusic(2, finalVolume, duration, opCall._threadId);
 }
 
 void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
@@ -684,7 +684,7 @@ void ScriptOpcodes_Duckman::opQuitGame(ScriptThread *scriptThread, OpCall &opCal
 void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->reset();
 	_vm->_input->activateButton(0xFFFF);
-	_vm->_soundMan->stopMidiMusic();
+	// TODO _vm->stopMusic();
 	// TODO _vm->_gameStates->clear();
 }
 
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 438ab04..a6f9ac7 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -176,6 +176,7 @@ public:
 	virtual void clearFader() {};
 	virtual void pauseFader() {};
 	virtual void unpauseFader() {};
+	virtual bool isVideoPlaying() { return false; }
 
 	void setCurrFontId(uint32 fontId);
 	bool checkActiveTalkThreads();
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index 1f164cd..d1272f4 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS := \
 	duckman/duckman_inventory.o \
 	duckman/duckman_screenshakereffects.o \
 	duckman/duckman_specialcode.o \
+	duckman/duckman_videoplayer.o \
 	duckman/gamestate_duckman.o \
 	duckman/illusions_duckman.o \
 	duckman/menusystem_duckman.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 6a991cd..9979cd1 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -423,7 +423,7 @@ void Screen::updateSprites() {
 	_drawQueue->drawAll();
 	if (_isScreenOffsetActive)
 		clearScreenOffsetAreas();
-	if (!_displayOn) // TODO Check if a video is playing then don't do it
+	if (!_displayOn && !_vm->isVideoPlaying())
 		_backSurface->fillRect(Common::Rect(_backSurface->w, _backSurface->h), 0);
 	g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
 }
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 4a89c79..63750ce 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -168,6 +168,7 @@ public:
 	uint16 getColorKey2() const { return _colorKey2; }
 	int16 getScreenWidth() const { return _backSurface->w; }
 	int16 getScreenHeight() const { return _backSurface->h; }
+	Graphics::Surface *getBackSurface() const { return _backSurface; }
 	virtual void decompressSprite(SpriteDecompressQueueItem *item) = 0;
 	virtual void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) = 0;
 	virtual void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) = 0;


Commit: f15335db0f5a52750ee9e2b6d61e53880bad844f
    https://github.com/scummvm/scummvm/commit/f15335db0f5a52750ee9e2b6d61e53880bad844f
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Remove old video logic

Changed paths:
    engines/illusions/illusions.cpp
    engines/illusions/illusions.h


diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index 1108681..c6ae20c 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -286,45 +286,6 @@ bool IllusionsEngine::calcPointDirection(Common::Point &srcPt, Common::Point &ds
 	return facing != 0;
 }
 
-void IllusionsEngine::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 threadId) {
-	Video::VideoDecoder *videoDecoder = new Video::AVIDecoder();
-	Common::String filename = Common::String::format("%08X.AVI", objectId);
-	if (!videoDecoder->loadFile(filename)) {
-		delete videoDecoder;
-		warning("Unable to open video %s", filename.c_str());
-		return;
-	}
-
-	videoDecoder->start();
-
-	bool skipVideo = false;
-
-	while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
-		if (videoDecoder->needsUpdate()) {
-			const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
-			if (videoDecoder->hasDirtyPalette()) {
-				const byte *palette = videoDecoder->getPalette();
-				_system->getPaletteManager()->setPalette(palette, 0, 256);
-			}
-
-			if (frame) {
-				_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
-				_system->updateScreen();
-			}
-		}
-
-		Common::Event event;
-		while (_eventMan->pollEvent(event)) {
-			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_SPACE) ||
-				event.type == Common::EVENT_LBUTTONUP)
-				skipVideo = true;
-		}
-	}
-
-	videoDecoder->close();
-	delete videoDecoder;
-}
-
 bool IllusionsEngine::isSoundActive() {
 	// TODO
 	return true;
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index a6f9ac7..9c73c47 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -169,7 +169,6 @@ public:
 	int getRandom(int max);
 	int convertPanXCoord(int16 x);
 	bool calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing);
-	void playVideo(uint32 videoId, uint32 objectId, uint32 value, uint32 threadId);
 	bool isSoundActive();
 
 	virtual void updateFader() {};


Commit: d5690d60256b163036076e80ce52917dfaca6784
    https://github.com/scummvm/scummvm/commit/d5690d60256b163036076e80ce52917dfaca6784
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Fix text drawing colors

(cherry picked from commit a028c3f)

Changed paths:
    engines/illusions/screentext.cpp


diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index 206272d..a7e97ea 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -95,6 +95,17 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C
 	_surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height);
 	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1());
 	_dimensions = dimensions;
+	if (_vm->getGameId() == kGameIdBBDOU) {
+		if (backgroundColor == 0) {
+			backgroundColor = _vm->_screen->getColorKey1();
+			borderColor = g_system->getScreenFormat().RGBToColor(128, 128, 128);
+		} else if (backgroundColor == 218) {
+			backgroundColor = g_system->getScreenFormat().RGBToColor(50, 50, 180);
+			borderColor = 0;
+		} else {
+			borderColor = g_system->getScreenFormat().RGBToColor(128, 128, 128);
+		}
+	}
 	textDrawer.drawText(_vm->_screen, _surface, backgroundColor, borderColor);
 	return done;
 }


Commit: 25a303a52967220e8f838b4f185c2fc8f8092f47
    https://github.com/scummvm/scummvm/commit/25a303a52967220e8f838b4f185c2fc8f8092f47
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement video player

(cherry picked from commit aab0b29)

Changed paths:
  A engines/illusions/bbdou/bbdou_videoplayer.cpp
  A engines/illusions/bbdou/bbdou_videoplayer.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/module.mk


diff --git a/engines/illusions/bbdou/bbdou_videoplayer.cpp b/engines/illusions/bbdou/bbdou_videoplayer.cpp
new file mode 100644
index 0000000..ba57890
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_videoplayer.cpp
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *                                                                                                                                  
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_videoplayer.h"
+#include "illusions/actor.h"
+#include "illusions/dictionary.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+#include "engines/util.h"
+
+namespace Illusions {
+
+// BBDOUVideoPlayer
+
+BBDOUVideoPlayer::BBDOUVideoPlayer(IllusionsEngine_BBDOU *vm)
+	: _vm(vm), _videoDecoder(0), _callingThreadId(0), _objectId(0) {
+}
+
+BBDOUVideoPlayer::~BBDOUVideoPlayer() {
+	delete _videoDecoder;
+}
+
+void BBDOUVideoPlayer::start(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId) {
+	debug(0, "BBDOUVideoPlayer::play(%08X, %08X, %d, %08X)", videoId, objectId, priority, callingThreadId);
+	notifyCallingThread();
+	_objectId = objectId;
+	_callingThreadId = callingThreadId;
+	Control *videoControl = _vm->_dict->getObjectControl(objectId);
+	videoControl->_flags |= 0x0008;
+	_vm->_input->discardAllEvents();
+	Common::String filename = Common::String::format("%08x.avi", videoId);
+	_videoDecoder = new Video::AVIDecoder();
+	if (!_videoDecoder->loadFile(filename)) {
+		delete _videoDecoder;
+		_videoDecoder = 0;
+		warning("Unable to open video %s", filename.c_str());
+		notifyCallingThread();
+		return;
+	}
+	_videoDecoder->start();
+}
+
+void BBDOUVideoPlayer::stop() {
+	_vm->_input->discardAllEvents();
+	delete _videoDecoder;
+	_videoDecoder = 0;
+	notifyCallingThread();
+	_objectId = 0;
+}
+
+void BBDOUVideoPlayer::update() {
+	if (_vm->_input->pollEvent(kEventAbort) || _videoDecoder->endOfVideo()) {
+		stop();
+	} else if (_videoDecoder->needsUpdate()) {
+		Control *videoControl = _vm->_dict->getObjectControl(_objectId);
+		const Graphics::Surface *frame = _videoDecoder->decodeNextFrame();
+		Graphics::Surface *backSurface = videoControl->_actor->_surface;
+		if (frame->format.bytesPerPixel == g_system->getScreenFormat().bytesPerPixel) {
+			const int width = MIN(frame->w, backSurface->w) * frame->format.bytesPerPixel;
+			const int height = MIN(frame->h, backSurface->h);
+			const byte *src = (const byte*)frame->getPixels();
+			byte *dest = (byte*)backSurface->getPixels();
+			for (int yc = 0; yc < height; ++yc) {
+				memcpy(dest, src, width);
+				src += frame->pitch;
+				dest += backSurface->pitch;
+			}
+		}
+		ActorType *actorType = _vm->_dict->findActorType(videoControl->_actorTypeId);
+		videoControl->_actor->_frameIndex = 1;
+		videoControl->_actor->_surfInfo = actorType->_surfInfo;
+		videoControl->appearActor();
+		videoControl->deactivateObject();
+		videoControl->_actor->_flags &= ~0x2000;
+	}
+}
+
+bool BBDOUVideoPlayer::isPlaying() const {
+	return _videoDecoder != 0;
+}
+
+void BBDOUVideoPlayer::notifyCallingThread() {
+	if (_callingThreadId != 0) {
+		_vm->notifyThreadId(_callingThreadId);
+		_callingThreadId = 0;
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_videoplayer.h b/engines/illusions/bbdou/bbdou_videoplayer.h
new file mode 100644
index 0000000..7ad149a
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_videoplayer.h
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_VIDEOPLAYER_H
+#define ILLUSIONS_BBDOU_VIDEOPLAYER_H
+
+#include "illusions/illusions.h"
+#include "video/avi_decoder.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+
+class BBDOUVideoPlayer {
+public:
+	BBDOUVideoPlayer(IllusionsEngine_BBDOU *vm);
+	~BBDOUVideoPlayer();
+	void start(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId);
+	void stop();
+	void update();
+	bool isPlaying() const;
+public:	
+	IllusionsEngine_BBDOU *_vm;
+	Video::VideoDecoder *_videoDecoder;
+	uint32 _objectId;
+	int _priority;
+	uint32 _callingThreadId;
+	void notifyCallingThread();
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_VIDEOPLAYER_H
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 0c16ad5..52e41da 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_videoplayer.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
@@ -166,6 +167,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
+	_videoPlayer = new BBDOUVideoPlayer(this);
 
 	_screen->setColorKey1(0xF81F);
 
@@ -215,6 +217,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
+	delete _videoPlayer;
 	delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
@@ -279,6 +282,7 @@ void IllusionsEngine_BBDOU::initUpdateFunctions() {
 	UPDATEFUNCTION(50, 0, updateActors);
 	UPDATEFUNCTION(60, 0, updateSequences);
 	UPDATEFUNCTION(70, 0, updateGraphics);
+	UPDATEFUNCTION(70, 0, updateVideoPlayer);
 	UPDATEFUNCTION(90, 0, updateSprites);
 	UPDATEFUNCTION(120, 0, updateSoundMan);
 }
@@ -314,6 +318,20 @@ uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32
 	return causeThreadId;
 }
 
+int IllusionsEngine_BBDOU::updateVideoPlayer(uint flags) {
+	if (_videoPlayer->isPlaying())
+		_videoPlayer->update();
+	return kUFNext;
+}
+
+void IllusionsEngine_BBDOU::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId) {
+	_videoPlayer->start(videoId, objectId, priority, callingThreadId);
+}
+
+bool IllusionsEngine_BBDOU::isVideoPlaying() {
+	return _videoPlayer->isPlaying();
+}
+
 void IllusionsEngine_BBDOU::setDefaultTextCoords() {
 	WidthHeight dimensions;
 	dimensions._width = 480;
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index ddbb827..f1843f7 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -33,6 +33,7 @@ namespace Illusions {
 class Dictionary;
 class ScriptMan;
 class ScriptStack;
+class BBDOUVideoPlayer;
 
 struct ActiveScene {
 	uint32 _sceneId;
@@ -72,6 +73,8 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
+	BBDOUVideoPlayer *_videoPlayer;
+
 	bool _walkthroughStarted;
 
 	void initInput();
@@ -83,6 +86,10 @@ public:
 	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
 	uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
 
+	int updateVideoPlayer(uint flags);
+	void playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId);
+	bool isVideoPlaying();
+
 	void setDefaultTextCoords();
 
 	void loadSpecialCode(uint32 resId);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index e6a3dd6..3a1b248 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -792,11 +792,12 @@ void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall
 	ARG_UINT32(objectId);
 	ARG_UINT32(videoId);
 	ARG_UINT32(priority);
-	// TODO _vm->playVideo(videoId, objectId, value, opCall._threadId);
-
+#if 1 // TODO DEBUG Set to 0 to skip videos
+	_vm->playVideo(videoId, objectId, priority, opCall._threadId);
+#else
 	//DEBUG Resume calling thread, later done by the video player
 	_vm->notifyThreadId(opCall._callerThreadId);
-
+#endif
 }
 
 void ScriptOpcodes_BBDOU::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index d1272f4..dc915c3 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS := \
 	bbdou/bbdou_foodctl.o \
 	bbdou/bbdou_inventory.o \
 	bbdou/bbdou_specialcode.o \
+	bbdou/bbdou_videoplayer.o \
 	bbdou/bbdou_triggerfunctions.o \
 	bbdou/illusions_bbdou.o \
 	bbdou/scriptopcodes_bbdou.o \


Commit: b787b06156fe22261f2a80227d48fdd2da47e0a2
    https://github.com/scummvm/scummvm/commit/b787b06156fe22261f2a80227d48fdd2da47e0a2
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Formatting

Changed paths:
    engines/illusions/bbdou/bbdou_videoplayer.cpp
    engines/illusions/bbdou/bbdou_videoplayer.h


diff --git a/engines/illusions/bbdou/bbdou_videoplayer.cpp b/engines/illusions/bbdou/bbdou_videoplayer.cpp
index ba57890..f61f0d3 100644
--- a/engines/illusions/bbdou/bbdou_videoplayer.cpp
+++ b/engines/illusions/bbdou/bbdou_videoplayer.cpp
@@ -3,7 +3,7 @@
  * ScummVM is the legal property of its developers, whose names
  * are too numerous to list here. Please refer to the COPYRIGHT
  * file distributed with this source distribution.
- *                                                                                                                                  
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
diff --git a/engines/illusions/bbdou/bbdou_videoplayer.h b/engines/illusions/bbdou/bbdou_videoplayer.h
index 7ad149a..33c1093 100644
--- a/engines/illusions/bbdou/bbdou_videoplayer.h
+++ b/engines/illusions/bbdou/bbdou_videoplayer.h
@@ -38,7 +38,7 @@ public:
 	void stop();
 	void update();
 	bool isPlaying() const;
-public:	
+public:
 	IllusionsEngine_BBDOU *_vm;
 	Video::VideoDecoder *_videoDecoder;
 	uint32 _objectId;


Commit: 4637104e6d289ab42d5eb02e0f42f48133180542
    https://github.com/scummvm/scummvm/commit/4637104e6d289ab42d5eb02e0f42f48133180542
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement missing talkthread events

(cherry picked from commit 87869cb)

Changed paths:
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread.h


diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 74fd162..7c2e012 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -241,7 +241,114 @@ if (true) {
 	}
 
 	return kTSTerminate;
+}
+
+void TalkThread::onSuspend() {
+	switch (_status) {
+	case 1:
+		_voiceDurationElapsed = getDurationElapsed(_voiceStartTime, _voiceEndTime);
+		_status = 7;
+		break;
+	case 4:
+		_vm->_soundMan->stopCueingVoice();
+		_status = 7;
+		break;
+	case 6:
+	case 7:
+		if (!(_flags & 4)) {
+			_vm->_soundMan->stopVoice();
+			_flags |= 4;
+		}
+		if (!(_flags & 8)) {
+			_vm->_screenText->removeText();
+			_flags |= 8;
+		}
+		_status = 7;
+		break;
+	default:
+		_status = 7;
+		break;
+	}
+}
+
+void TalkThread::onPause() {
+	switch (_status) {
+	case 1:
+		_voiceDurationElapsed = getDurationElapsed(_voiceStartTime, _voiceEndTime);
+		break;
+	case 4:
+		_vm->_soundMan->stopCueingVoice();
+		break;
+	case 6:
+	case 7:
+		if (!(_flags & 4)) {
+			// TODO audvocPauseVoice();
+		}
+		if (!(_flags & 8)) {
+			_textDurationElapsed = getDurationElapsed(_textStartTime, _textEndTime);
+		}
+		break;
+	default:
+		break;
+	}
+}
 
+void TalkThread::onUnpause() {
+	switch (_status) {
+	case 1:
+		_voiceStartTime = getCurrentTime();
+		if (_voiceDuration <= _voiceDurationElapsed) {
+			_voiceDurationElapsed = 0;
+			_voiceEndTime = _voiceStartTime;
+		} else {
+			_voiceDurationElapsed = 0;
+			_voiceEndTime = _voiceStartTime + _voiceDuration - _voiceDurationElapsed;
+		}
+		break;
+	case 4:
+		if (_vm->isSoundActive()) {
+			TalkEntry *talkEntry = getTalkResourceEntry(_talkId);
+			_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName);
+		}
+		break;
+	case 6:
+		if (!(_flags & 4)) {
+			// TODO audvocUnpauseVoice();
+		}
+		if (!(_flags & 8)) {
+			_textStartTime = getCurrentTime();
+			if (_textDuration <= _textDurationElapsed) {
+				_textDurationElapsed = 0;
+				_textEndTime = _textStartTime;
+			} else {
+				_textDurationElapsed = 0;
+				_textEndTime = _textStartTime + _textDuration - _textDurationElapsed;
+			}
+		}
+		break;
+	}
+}
+
+void TalkThread::onTerminated() {
+	if (_status == 4) {
+		_vm->_soundMan->stopCueingVoice();
+	} else if (_status == 6) {
+		if (!(_flags & 4)) {
+			_vm->_soundMan->stopVoice();
+			_flags |= 4;
+		}
+		if (!(_flags & 8)) {
+			_vm->_screenText->removeText();
+			_flags |= 8;
+		}
+		if (!(_flags & 2)) {
+			if (_sequenceId2) {
+				Control *control = _vm->_dict->getObjectControl(_objectId);
+				control->startSequenceActor(_sequenceId2, 2, 0);
+			}
+			_flags |= 2;
+		}
+	}
 }
 
 void TalkThread::onKill() {
diff --git a/engines/illusions/threads/talkthread.h b/engines/illusions/threads/talkthread.h
index 1d6573c..7f8a197 100644
--- a/engines/illusions/threads/talkthread.h
+++ b/engines/illusions/threads/talkthread.h
@@ -42,6 +42,10 @@ public:
 		int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1, uint32 sequenceId2,
 		uint32 namedPointId);
 	virtual int onUpdate();
+	virtual void onSuspend();
+	virtual void onPause();
+	virtual void onUnpause();
+	virtual void onTerminated();
 	virtual void onKill();
 	virtual uint32 sendMessage(int msgNum, uint32 msgValue);
 public:


Commit: a76612ec5a91847e7e23652614d5e572af2c4aab
    https://github.com/scummvm/scummvm/commit/a76612ec5a91847e7e23652614d5e572af2c4aab
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement voice pausing/unpausing

(cherry picked from commit a70cf1d)

Changed paths:
    engines/illusions/sound.cpp
    engines/illusions/sound.h
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 1e8ca49..d8b0316 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -229,7 +229,7 @@ void MidiPlayer::endOfTrack() {
 
 // VoicePlayer
 
-VoicePlayer::VoicePlayer() {
+VoicePlayer::VoicePlayer() : _wasPlaying(false), _isPaused(false) {
 }
 
 VoicePlayer::~VoicePlayer() {
@@ -267,6 +267,22 @@ void VoicePlayer::stop() {
 	_voiceName.clear();
 }
 
+void VoicePlayer::pause() {
+	if (!_isPaused) {
+		_isPaused = true;
+		_wasPlaying = isPlaying();
+		g_system->getMixer()->pauseHandle(_soundHandle, true);
+	}
+}
+
+void VoicePlayer::unpause() {
+	if (_isPaused) {
+		_isPaused = false;
+		if (_wasPlaying)
+			g_system->getMixer()->pauseHandle(_soundHandle, false);
+	}
+}
+
 bool VoicePlayer::isPlaying() {
 	return g_system->getMixer()->isSoundHandleActive(_soundHandle);
 }
@@ -280,12 +296,6 @@ bool VoicePlayer::isCued() {
 	return _voiceStatus == 2;
 }
 
-void VoicePlayer::pause(bool paused) {
-	if (isPlaying()) {
-		g_system->getMixer()->pauseHandle(_soundHandle, paused);
-	}
-}
-
 // Sound
 
 Sound::Sound(uint32 soundEffectId, uint32 soundGroupId, bool looping)
@@ -394,8 +404,12 @@ void SoundMan::stopVoice() {
 	_voicePlayer->stop();
 }
 
-void SoundMan::pauseVoice(bool paused) {
-	_voicePlayer->pause(paused);
+void SoundMan::pauseVoice() {
+	_voicePlayer->pause();
+}
+
+void SoundMan::unpauseVoice() {
+	_voicePlayer->unpause();
 }
 
 bool SoundMan::isVoicePlaying() {
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index 8d5f21e..2669102 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -80,8 +80,9 @@ public:
 	bool cue(const char *voiceName);
 	void stopCueing();
 	void start(int16 volume, int16 pan);
-	void pause(bool paused);
 	void stop();
+	void pause();
+	void unpause();
 	bool isPlaying();
 	bool isEnabled();
 	bool isCued();
@@ -89,6 +90,8 @@ protected:
 	Audio::SoundHandle _soundHandle;
 	Common::String _voiceName;
 	uint _voiceStatus;
+	bool _wasPlaying;
+	bool _isPaused;
 };
 
 class Sound {
@@ -134,7 +137,8 @@ public:
 	void stopCueingVoice();
 	void startVoice(int16 volume, int16 pan);
 	void stopVoice();
-	void pauseVoice(bool paused);
+	void pauseVoice();
+	void unpauseVoice();
 	bool isVoicePlaying();
 	bool isVoiceEnabled();
 	bool isVoiceCued();
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 7c2e012..c7f7a67 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -282,7 +282,7 @@ void TalkThread::onPause() {
 	case 6:
 	case 7:
 		if (!(_flags & 4)) {
-			// TODO audvocPauseVoice();
+			_vm->_soundMan->pauseVoice();
 		}
 		if (!(_flags & 8)) {
 			_textDurationElapsed = getDurationElapsed(_textStartTime, _textEndTime);
@@ -313,7 +313,7 @@ void TalkThread::onUnpause() {
 		break;
 	case 6:
 		if (!(_flags & 4)) {
-			// TODO audvocUnpauseVoice();
+			_vm->_soundMan->unpauseVoice();
 		}
 		if (!(_flags & 8)) {
 			_textStartTime = getCurrentTime();
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 3691159..2b1294c 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -206,7 +206,7 @@ int TalkThread_Duckman::onUpdate() {
 void TalkThread_Duckman::onPause() {
 	if (_status == 5) {
 		if (!(_flags & 4)) {
-			_vm->_soundMan->pauseVoice(true);
+			_vm->_soundMan->pauseVoice();
 		}
 		if (!(_flags & 8))
 			_textDurationElapsed = getDurationElapsed(_textStartTime, _textEndTime);
@@ -220,7 +220,7 @@ void TalkThread_Duckman::onUnpause() {
 			_vm->_soundMan->cueVoice((char*)talkEntry->_voiceName);
 	} else if (_status == 5) {
 		if (!(_flags & 4)) {
-			_vm->_soundMan->pauseVoice(false);
+			_vm->_soundMan->unpauseVoice();
 		}
 		if (!(_flags & 8)) {
 			_textStartTime = getCurrentTime();


Commit: 54dd3814414d7ef5de09cda197b1065655ee9242
    https://github.com/scummvm/scummvm/commit/54dd3814414d7ef5de09cda197b1065655ee9242
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Play sound in BbdouSpecialCode::playSoundEffect

(cherry picked from commit 1e822f0)

Changed paths:
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index c7e79a7..0f3a264 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -34,6 +34,7 @@
 #include "illusions/input.h"
 #include "illusions/scriptstack.h"
 #include "illusions/scriptopcodes.h"
+#include "illusions/sound.h"
 
 namespace Illusions {
 
@@ -501,7 +502,7 @@ void BbdouSpecialCode::playSoundEffect(int soundIndex) {
 	};
 	uint32 soundEffectId = kSoundEffectIds[2 * soundIndex];
 	if (soundEffectId) {
-		// TODO _vm->startSound(soundEffectId, 255, 0);
+		_vm->_soundMan->playSound(soundEffectId, 255, 0);
 	}
 }
 
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 52e41da..247a00f 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -589,6 +589,24 @@ void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
 	_camera->clearCameraModeStack();
 }
 
+void IllusionsEngine_BBDOU::pause(uint32 callerThreadId) {
+	if (++_pauseCtr == 1) {
+		_threads->pauseThreads(callerThreadId);
+		_camera->pause();
+		pauseFader();
+		_controls->pauseActors(0x40004);
+	}
+}
+
+void IllusionsEngine_BBDOU::unpause(uint32 callerThreadId) {
+	if (--_pauseCtr == 0) {
+		_controls->unpauseActors(0x40004);
+		unpauseFader();
+		_camera->unpause();
+		_threads->unpauseThreads(callerThreadId);
+	}
+}
+
 void IllusionsEngine_BBDOU::enterMenuPause() {
 	// TODO suspendAudio();
 	_screenText->clearText();
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index f1843f7..9bdfc6d 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -136,6 +136,9 @@ public:
 	void leavePause(uint32 threadId);
 	void dumpActiveScenes(uint32 sceneId, uint32 threadId);
 
+	void pause(uint32 callerThreadId);
+	void unpause(uint32 callerThreadId);
+
 	void enterMenuPause();
 	void leaveMenuPause();
 
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 3a1b248..f0e406e 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -122,11 +122,11 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(66, opSetAdjustUpSfx);
 	OPCODE(67, opSetAdjustDnSfx);
 	// 68 unused
-	// TODO OPCODE(69, opPause);
-	// TODO OPCODE(70, opResume);
+	OPCODE(69, opPause);
+	OPCODE(70, opResume);
 	OPCODE(71, opStartSound);
-	// TODO OPCODE(72, opStartSoundAtNamedPoint);
-	// TODO OPCODE(73, opStartSoundAtActor);
+	OPCODE(72, opStartSoundAtPosition);
+	OPCODE(73, opStartSoundAtActor);
 	OPCODE(74, opStopSound);
 	OPCODE(75, opStartMusic);
 	OPCODE(76, opStopMusic);
@@ -142,9 +142,9 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	// TODO OPCODE(86, opRestoreGame);
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
-	// TODO 89 NOP
+	OPCODE(89, opNop);
 	// 90 unused
-	// TODO 91 NOP
+	OPCODE(91, opNop);
 	// 92-102 unused
 	OPCODE(103, opJumpIf);
 	OPCODE(104, opIsPrevSceneId);
@@ -614,6 +614,14 @@ void ScriptOpcodes_BBDOU::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &o
 	// TODO _vm->setAdjustDnSfx(soundEffectId);
 }
 
+void ScriptOpcodes_BBDOU::opPause(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->pause(opCall._callerThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opResume(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->unpause(opCall._callerThreadId);
+}
+
 void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_INT16(volume);
@@ -621,6 +629,26 @@ void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCal
 	ARG_UINT32(soundEffectId);
 	_vm->_soundMan->playSound(soundEffectId, volume, pan);
 }
+
+void ScriptOpcodes_BBDOU::opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(volume);
+	ARG_UINT32(soundEffectId);
+	ARG_UINT32(namedPointId);
+	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
+	int16 pan = _vm->convertPanXCoord(pos.x);
+	_vm->_soundMan->playSound(soundEffectId, volume, pan);
+}
+
+void ScriptOpcodes_BBDOU::opStartSoundAtActor(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_INT16(volume);
+	ARG_UINT32(objectId);
+	ARG_UINT32(soundEffectId);
+	Control *control = _vm->_dict->getObjectControl(objectId);
+	Common::Point pos = control->getActorPosition();
+	int16 pan = _vm->convertPanXCoord(pos.x);
+	_vm->_soundMan->playSound(soundEffectId, volume, pan);
+}
+
 void ScriptOpcodes_BBDOU::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(soundEffectId);
@@ -699,6 +727,10 @@ void ScriptOpcodes_BBDOU::opActivateButton(ScriptThread *scriptThread, OpCall &o
 	_vm->_input->activateButton(button);
 }
 
+void ScriptOpcodes_BBDOU::opNop(ScriptThread *scriptThread, OpCall &opCall) {
+	// Opcode empty but still called
+}
+
 void ScriptOpcodes_BBDOU::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(jumpOffs);
 	int16 value = _vm->_stack->pop();
@@ -792,7 +824,7 @@ void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall
 	ARG_UINT32(objectId);
 	ARG_UINT32(videoId);
 	ARG_UINT32(priority);
-#if 1 // TODO DEBUG Set to 0 to skip videos
+#if 0 // TODO DEBUG Set to 0 to skip videos
 	_vm->playVideo(videoId, objectId, priority, opCall._threadId);
 #else
 	//DEBUG Resume calling thread, later done by the video player
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index 5385103..3171382 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -95,7 +95,11 @@ protected:
 	void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
+	void opPause(ScriptThread *scriptThread, OpCall &opCall);
+	void opResume(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall);
+	void opStartSoundAtActor(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
 	void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
@@ -107,6 +111,7 @@ protected:
 	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
+	void opNop(ScriptThread *scriptThread, OpCall &opCall);
 	void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
 	void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);


Commit: 94fdd597d985cba1436aea7c6be67982de220199
    https://github.com/scummvm/scummvm/commit/94fdd597d985cba1436aea7c6be67982de220199
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Add menu system class, adjust existing code (actual menus not done yet)

(cherry picked from commit 03b0ca1)

Changed paths:
  A engines/illusions/bbdou/bbdou_menukeys.cpp
  A engines/illusions/bbdou/bbdou_menukeys.h
  A engines/illusions/bbdou/menusystem_bbdou.cpp
  A engines/illusions/bbdou/menusystem_bbdou.h
    engines/illusions/actor.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/input.h
    engines/illusions/menusystem.cpp
    engines/illusions/module.mk
    engines/illusions/screen.cpp
    engines/illusions/screen.h
    engines/illusions/screentext.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index a5276db..a38814c 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -935,7 +935,7 @@ void Control::getActorFrameDimensions(WidthHeight &dimensions) {
 }
 
 void Control::drawActorRect(const Common::Rect r, byte color) {
-	_actor->_surface->fillRect(r, color);
+	_vm->_screen->fillSurfaceRect(_actor->_surface, r, color);
 	_actor->_flags |= Illusions::ACTOR_FLAG_4000;
 }
 
diff --git a/engines/illusions/bbdou/bbdou_menukeys.cpp b/engines/illusions/bbdou/bbdou_menukeys.cpp
new file mode 100644
index 0000000..aadd9fe
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_menukeys.cpp
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_menukeys.h"
+#include "illusions/bbdou/menusystem_bbdou.h"
+#include "illusions/input.h"
+#include "illusions/screen.h"
+
+namespace Illusions {
+
+// BBDOUMenuKeys
+
+BBDOUMenuKeys::BBDOUMenuKeys(IllusionsEngine_BBDOU *vm)
+	: _vm(vm) {
+}
+
+BBDOUMenuKeys::~BBDOUMenuKeys() {
+
+}
+
+void BBDOUMenuKeys::addMenuKey(uint bitMask, uint32 threadId) {
+	MenuKey menuKey;
+	menuKey.bitMask = bitMask;
+	menuKey.threadId = threadId;
+	_menuKeys.push_back(menuKey);
+}
+
+void BBDOUMenuKeys::update() {
+	if (_vm->_screen->isDisplayOn() && !_vm->_menuSystem->isActive()) {
+		for (MenuKeys::iterator it = _menuKeys.begin(); it != _menuKeys.end(); ++it) {
+			const MenuKey &menuKey = *it;
+			if (_vm->_input->pollButton(menuKey.bitMask)) {
+				_vm->startScriptThread(menuKey.threadId, 0, 0, 0, 0);
+				break;
+			}
+		}
+	}
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_menukeys.h b/engines/illusions/bbdou/bbdou_menukeys.h
new file mode 100644
index 0000000..e26f1f0
--- /dev/null
+++ b/engines/illusions/bbdou/bbdou_menukeys.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_BBDOU_MENUKEYS_H
+#define ILLUSIONS_BBDOU_BBDOU_MENUKEYS_H
+
+#include "illusions/specialcode.h"
+#include "illusions/thread.h"
+#include "common/array.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+
+struct MenuKey {
+	uint bitMask;
+	uint32 threadId;
+};
+
+class BBDOUMenuKeys {
+public:
+	BBDOUMenuKeys(IllusionsEngine_BBDOU *vm);
+	~BBDOUMenuKeys();
+	void addMenuKey(uint bitMask, uint32 threadId);
+	void update();
+protected:
+	typedef Common::Array<MenuKey> MenuKeys;
+	IllusionsEngine_BBDOU *_vm;
+	MenuKeys _menuKeys;
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_BBDOU_MENUKEYS_H
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 247a00f..7c129f1 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -21,7 +21,9 @@
  */
 
 #include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/bbdou_menukeys.h"
 #include "illusions/bbdou/bbdou_videoplayer.h"
+#include "illusions/bbdou/menusystem_bbdou.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/cursor.h"
@@ -167,7 +169,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_threads = new ThreadList(this);
 	_updateFunctions = new UpdateFunctions();
 	_soundMan = new SoundMan(this);
+	_menuSystem = new BBDOUMenuSystem(this);
 	_videoPlayer = new BBDOUVideoPlayer(this);
+	_menuKeys = new BBDOUMenuKeys(this);
 
 	_screen->setColorKey1(0xF81F);
 
@@ -217,7 +221,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _stack;
 	delete _scriptOpcodes;
 
+	delete _menuKeys;
 	delete _videoPlayer;
+	delete _menuSystem;
 	delete _soundMan;
 	delete _updateFunctions;
 	delete _threads;
@@ -280,6 +286,7 @@ void IllusionsEngine_BBDOU::initInput() {
 void IllusionsEngine_BBDOU::initUpdateFunctions() {
 	UPDATEFUNCTION(30, 0, updateScript);
 	UPDATEFUNCTION(50, 0, updateActors);
+	UPDATEFUNCTION(60, 0, updateMenuKeys);
 	UPDATEFUNCTION(60, 0, updateSequences);
 	UPDATEFUNCTION(70, 0, updateGraphics);
 	UPDATEFUNCTION(70, 0, updateVideoPlayer);
@@ -291,7 +298,12 @@ void IllusionsEngine_BBDOU::initUpdateFunctions() {
 
 int IllusionsEngine_BBDOU::updateScript(uint flags) {
 	_threads->updateThreads();
-	return 1;
+	return kUFNext;
+}
+
+int IllusionsEngine_BBDOU::updateMenuKeys(uint flags) {
+	_menuKeys->update();
+	return kUFNext;
 }
 
 bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
@@ -436,7 +448,7 @@ void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaT
 			// Unused nullsub_1(control);
 			break;
 		case 3:
-			// TODO _vm->_shellMgr->handleMouse(control);
+			_menuSystem->update(control);
 			break;
 		}
 	}
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index 9bdfc6d..2bad993 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -34,6 +34,8 @@ class Dictionary;
 class ScriptMan;
 class ScriptStack;
 class BBDOUVideoPlayer;
+class BBDOUMenuKeys;
+class BBDOUMenuSystem;
 
 struct ActiveScene {
 	uint32 _sceneId;
@@ -73,7 +75,9 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
+	BBDOUMenuSystem *_menuSystem;
 	BBDOUVideoPlayer *_videoPlayer;
+	BBDOUMenuKeys *_menuKeys;
 
 	bool _walkthroughStarted;
 
@@ -81,6 +85,7 @@ public:
 
 	void initUpdateFunctions();
 	int updateScript(uint flags);
+	int updateMenuKeys(uint flags);
 
 	bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
 	void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
diff --git a/engines/illusions/bbdou/menusystem_bbdou.cpp b/engines/illusions/bbdou/menusystem_bbdou.cpp
new file mode 100644
index 0000000..9d1b718
--- /dev/null
+++ b/engines/illusions/bbdou/menusystem_bbdou.cpp
@@ -0,0 +1,172 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/illusions.h"
+#include "illusions/actor.h"
+#include "illusions/cursor.h"
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/bbdou/menusystem_bbdou.h"
+
+namespace Illusions {
+
+// BBDOUMenuSystem
+
+BBDOUMenuSystem::BBDOUMenuSystem(IllusionsEngine_BBDOU *vm)
+	: BaseMenuSystem(vm), _vm(vm) {
+	clearMenus();
+}
+
+BBDOUMenuSystem::~BBDOUMenuSystem() {
+	freeMenus();
+}
+
+void BBDOUMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
+	uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
+	
+	debug(0, "BBDOUMenuSystem::runMenu(%08X)", menuId);
+
+	setTimeOutDuration(duration, timeOutMenuChoiceIndex);
+	setMenuCallerThreadId(menuCallerThreadId);
+	setMenuChoiceOffsets(menuChoiceOffsets, menuChoiceOffset);
+
+	int rootMenuId = convertRootMenuId(menuId);
+	BaseMenu *rootMenu = getMenuById(rootMenuId);
+	openMenu(rootMenu);
+
+}
+
+void BBDOUMenuSystem::clearMenus() {
+	for (int i = 0; i < kBBDOULastMenuIndex; ++i)
+		_menus[i] = 0;
+}
+
+void BBDOUMenuSystem::freeMenus() {
+	for (int i = 0; i < kBBDOULastMenuIndex; ++i)
+		delete _menus[i];
+}
+
+BaseMenu *BBDOUMenuSystem::getMenuById(int menuId) {
+	if (!_menus[menuId])
+		_menus[menuId] = createMenuById(menuId);
+	return _menus[menuId];
+}
+
+BaseMenu *BBDOUMenuSystem::createMenuById(int menuId) {
+	switch (menuId) {
+	case kBBDOUMainMenu:
+		return createMainMenu();
+	case kBBDOUPauseMenu:
+		return createPauseMenu();
+	// TODO Other menus
+	default:
+		error("BBDOUMenuSystem::createMenuById() Invalid menu id %d", menuId);
+	}
+}
+
+BaseMenu *BBDOUMenuSystem::createMainMenu() {
+	return 0; // TODO
+}
+
+BaseMenu *BBDOUMenuSystem::createLoadGameMenu() {
+	return 0; // TODO
+}
+
+BaseMenu *BBDOUMenuSystem::createOptionsMenu() {
+	return 0; // TODO
+}
+
+BaseMenu *BBDOUMenuSystem::createPauseMenu() {
+	BaseMenu *menu = new BaseMenu(this, 0x00120003, 218, 150, 80, 20, 1);
+	menu->addText("   Game Paused");
+	menu->addText("-------------------");
+	menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 1)));
+	// menu->addMenuItem(new MenuItem("Load Game", new MenuActionLoadGame(this, 1)));
+	// TODO menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
+	// TODO menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
+	// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
+	// menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
+	return menu;
+}
+
+int BBDOUMenuSystem::convertRootMenuId(uint32 menuId) {
+	switch (menuId) {
+	case 0x1C0001:
+		return kBBDOUMainMenu;
+	case 0x1C0002:
+		return kBBDOUPauseMenu;
+	case 0x1C0006:
+		return kBBDOULoadGameMenu;
+	case 0x1C0007:
+		return kBBDOUSaveGameMenu;
+	case 0x1C0008:
+		return kBBDOUGameSavedMenu;
+	case 0x1C0009:
+		return kBBDOUSaveFailedMenu;
+	case 0x1C000A:
+		return kBBDOULoadFailedMenu;
+	/* Unused/unimplemented debug menus
+	case 0x1C0003: debugStartMenu
+	case 0x1C0004: debugPauseMenu
+	case 0x1C0005: unitTestsMenu
+	*/
+	default:
+		error("BBDOUMenuSystem() Menu ID %08X not found", menuId);
+	}
+}
+
+bool BBDOUMenuSystem::initMenuCursor() {
+	bool cursorInitialVisibleFlag = false;
+	Control *cursorControl = _vm->getObjectControl(0x40004);
+	if (cursorControl) {
+		if (cursorControl->_flags & 1) {
+			cursorInitialVisibleFlag = false;
+		} else {
+			cursorInitialVisibleFlag = true;
+			cursorControl->appearActor();
+		}
+	} else {
+		Common::Point pos = _vm->getNamedPointPosition(0x70023);
+		_vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
+		cursorControl = _vm->getObjectControl(0x40004);
+	}
+	return cursorInitialVisibleFlag;
+}
+
+int BBDOUMenuSystem::getGameState() {
+	return _vm->_cursor->_status;
+}
+
+void BBDOUMenuSystem::setMenuCursorNum(int cursorNum) {
+	Control *mouseCursor = _vm->getObjectControl(0x40004);
+	_vm->_cursor->setActorIndex(5, cursorNum, 0);
+	mouseCursor->startSequenceActor(0x60001, 2, 0);
+}
+
+void BBDOUMenuSystem::setGameState(int gameState) {
+	_vm->_cursor->_status = gameState;
+}
+
+void BBDOUMenuSystem::playSoundEffect(int sfxId) {
+	// TODO
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/menusystem_bbdou.h b/engines/illusions/bbdou/menusystem_bbdou.h
new file mode 100644
index 0000000..1c98b28
--- /dev/null
+++ b/engines/illusions/bbdou/menusystem_bbdou.h
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_MENUSYSTEM_BBDOU_H
+#define ILLUSIONS_BBDOU_MENUSYSTEM_BBDOU_H
+
+#include "illusions/menusystem.h"
+
+namespace Illusions {
+
+enum {
+	kBBDOUMainMenu,
+	kBBDOUPauseMenu,
+	kBBDOULoadGameMenu,
+	kBBDOUSaveGameMenu,
+	kBBDOUGameSavedMenu,
+	kBBDOUSaveFailedMenu,
+	kBBDOULoadFailedMenu,
+	kBBDOULastMenuIndex
+};
+
+class IllusionsEngine_BBDOU;
+
+class BBDOUMenuSystem : public BaseMenuSystem {
+public:
+	BBDOUMenuSystem(IllusionsEngine_BBDOU *vm);
+	~BBDOUMenuSystem();
+	void runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
+		uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId);
+public://protected:
+	IllusionsEngine_BBDOU *_vm;
+	BaseMenu *_menus[kBBDOULastMenuIndex];
+	void clearMenus();
+	void freeMenus();
+	BaseMenu *getMenuById(int menuId);
+	BaseMenu *createMenuById(int menuId);
+	BaseMenu *createMainMenu();
+	BaseMenu *createPauseMenu();
+	BaseMenu *createLoadGameMenu();
+	BaseMenu *createSaveGameMenu();
+	BaseMenu *createGameSavedMenu();
+	BaseMenu *createSaveFailedMenu();
+	BaseMenu *createLoadFailedMenu();
+	BaseMenu *createOptionsMenu();
+	int convertRootMenuId(uint32 menuId);
+	virtual bool initMenuCursor();
+	virtual int getGameState();
+	virtual void setGameState(int gameState);
+	virtual void setMenuCursorNum(int cursorNum);
+	virtual void playSoundEffect(int sfxId);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_MENUSYSTEM_BBDOU_H
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index f0e406e..1c8d400 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -22,6 +22,8 @@
 
 #include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/scriptopcodes_bbdou.h"
+#include "illusions/bbdou/bbdou_menukeys.h"
+#include "illusions/bbdou/menusystem_bbdou.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
 #include "illusions/dictionary.h"
@@ -311,12 +313,12 @@ void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCal
 //uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
 //uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
 //uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
-//uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
+uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
 //uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
 //uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
 //uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
 //uint32 dsceneId = 0x0001007D, dthreadId = 0x000203B9;
-uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;
+// uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012; // Food minigame
 
 void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
@@ -691,17 +693,34 @@ void ScriptOpcodes_BBDOU::opAddMenuChoice(ScriptThread *scriptThread, OpCall &op
 }
 
 void ScriptOpcodes_BBDOU::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
-	ARG_INT16(unk1);
+	ARG_INT16(timeoutChoiceOfs);
 	ARG_UINT32(menuId);
-	ARG_UINT32(unk2);
-	// TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId);
-	// Remove menu choices from the stack
+	ARG_UINT32(timeOutDuration);
+
+	MenuChoiceOffsets menuChoiceOffsets;
+
+	// Load menu choices from the stack
 	do {
-		_vm->_stack->pop();
+		int16 choiceOffs = _vm->_stack->pop();
+		menuChoiceOffsets.push_back(choiceOffs);
 	} while (_vm->_stack->pop() == 0);
 
-	//DEBUG Resume calling thread, later done by the video player
-	_vm->notifyThreadId(opCall._callerThreadId);
+	// TODO DBEUG Start menu not yet implemented, fake selection of "Start game"
+	if (menuId == 0x001C0001) {
+		_vm->_menuChoiceOfs = 88;
+		_vm->notifyThreadId(opCall._callerThreadId);
+		return;
+	}
+
+	// Duckman has the timeout choice offset on the stack and the index as parameter
+	// BBDOU instead has only the choice offset as parameter
+	// So we just add the timeout choice offset and use its index.
+	menuChoiceOffsets.push_back(timeoutChoiceOfs);
+	uint timeOutMenuChoiceIndex = menuChoiceOffsets.size() - 1;
+
+	_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs,
+		menuId, timeOutDuration, timeOutMenuChoiceIndex,
+		opCall._callerThreadId);
 
 }
 
@@ -918,7 +937,8 @@ void ScriptOpcodes_BBDOU::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCal
 	ARG_SKIP(2);
 	ARG_UINT32(key);
 	ARG_UINT32(threadId);
-	// TODO _vm->addMenuKey(key, threadId);
+	debug("addMenuKey(%08X; %08X)", key, threadId);
+	_vm->_menuKeys->addMenuKey(key, threadId);
 }
 
 void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index 77a1bda..3b554bd 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -83,6 +83,7 @@ public:
 	bool hasNewEvents();
 	void discardEvent(uint evt);
 	void discardAllEvents();
+	bool pollButton(uint bitMask);
 	void activateButton(uint bitMask);
 	void deactivateButton(uint bitMask);
 	Common::Point getCursorPosition();
@@ -99,7 +100,6 @@ protected:
 	InputEvent _inputEvents[kEventMax];
 	void handleKey(Common::KeyCode key, int mouseButton, bool down);
 	void handleMouseButton(int mouseButton, bool down);
-	bool pollButton(uint bitMask);
 	void discardButtons(uint bitMask);
 	bool lookButtonStates(uint bitMask);
 	bool lookNewButtons(uint bitMask);
diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp
index 6d83591..149070d 100644
--- a/engines/illusions/menusystem.cpp
+++ b/engines/illusions/menusystem.cpp
@@ -243,7 +243,11 @@ void BaseMenuSystem::initActorHoverBackground() {
 		WidthHeight dimensions;
 		dimensions._width = 300;
 		dimensions._height = 15;
-		_vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 18);
+		if (_vm->getGameId() == kGameIdBBDOU) {
+			_vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 91);
+		} else {
+			_vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 18);
+		}
 		v0 = _vm->getObjectControl(0x4013E);
 		v0->_flags |= 8;
 	}
@@ -291,9 +295,15 @@ void BaseMenuSystem::initActorTextColorRect() {
 	Control *v0 = _vm->getObjectControl(0x40143);
 	if (!v0) {
 		WidthHeight dimensions;
-		dimensions._width = 300;
-		dimensions._height = 180;
-		_vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 17);
+		if (_vm->getGameId() == kGameIdBBDOU) {
+			dimensions._width = 420;
+			dimensions._height = 180;
+			_vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 90);
+		} else {
+			dimensions._width = 300;
+			dimensions._height = 180;
+			_vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 17);
+		}
 		v0 = _vm->getObjectControl(0x40143);
 		v0->_flags |= 8;
 	}
@@ -317,7 +327,6 @@ void BaseMenuSystem::placeActorTextColorRect() {
 
 	v0->setActorPosition(textInfoPosition);
 	v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_textColor);
-
 }
 
 void BaseMenuSystem::hideActorTextColorRect() {
@@ -340,7 +349,11 @@ void BaseMenuSystem::openMenu(BaseMenu *menu) {
 
 	setMenuCursorNum(1);
 
-	setGameState(4);
+	if (_vm->getGameId() == kGameIdDuckman) {
+		setGameState(4);
+	} else if (_vm->getGameId() == kGameIdBBDOU) {
+		setGameState(3);
+	}
 
 	activateMenu(menu);
 
@@ -417,8 +430,14 @@ uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
 		flags |= TEXT_FLAG_BORDER_DECORATION;
 
 	WidthHeight dimensions;
-	dimensions._width = 300;
-	dimensions._height = 180;
+
+	if (_vm->getGameId() == kGameIdDuckman) {
+		dimensions._width = 300;
+		dimensions._height = 180;
+	} else if (_vm->getGameId() == kGameIdBBDOU) {
+		dimensions._width = 580;
+		dimensions._height = 420;
+	}
 
 	uint16 *outTextPtr;
 	if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_backgroundColor, menu->_borderColor, 0xFF, 0xFF, 0xFF, outTextPtr)) {
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index dc915c3..cb68485 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -8,10 +8,12 @@ MODULE_OBJS := \
 	bbdou/bbdou_credits_staticdata.o \
 	bbdou/bbdou_foodctl.o \
 	bbdou/bbdou_inventory.o \
+	bbdou/bbdou_menukeys.o \
 	bbdou/bbdou_specialcode.o \
 	bbdou/bbdou_videoplayer.o \
 	bbdou/bbdou_triggerfunctions.o \
 	bbdou/illusions_bbdou.o \
+	bbdou/menusystem_bbdou.o \
 	bbdou/scriptopcodes_bbdou.o \
 	camera.o \
 	cursor.o \
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index 9979cd1..b6cbd2d 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -542,6 +542,10 @@ void Screen8Bit::fillSurface(Graphics::Surface *surface, byte color) {
 	surface->fillRect(Common::Rect(surface->w, surface->h), color);
 }
 
+void Screen8Bit::fillSurfaceRect(Graphics::Surface *surface, Common::Rect r, byte color) {
+	surface->fillRect(r, color);
+}
+
 bool Screen8Bit::isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
 	const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) {
 	// Unused in Duckman
@@ -742,6 +746,10 @@ void Screen16Bit::fillSurface(Graphics::Surface *surface, byte color) {
 	surface->fillRect(Common::Rect(surface->w, surface->h), convertColor(color));
 }
 
+void Screen16Bit::fillSurfaceRect(Graphics::Surface *surface, Common::Rect r, byte color) {
+	surface->fillRect(r, convertColor(color));
+}
+
 bool Screen16Bit::isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
 	const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) {
 
diff --git a/engines/illusions/screen.h b/engines/illusions/screen.h
index 63750ce..7025e29 100644
--- a/engines/illusions/screen.h
+++ b/engines/illusions/screen.h
@@ -173,6 +173,7 @@ public:
 	virtual void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags) = 0;
 	virtual void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count) = 0;
 	virtual void fillSurface(Graphics::Surface *surface, byte color) = 0;
+	virtual void fillSurfaceRect(Graphics::Surface *surface, Common::Rect r, byte color) = 0;
 	virtual bool isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
 		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels) = 0;
 public:
@@ -194,6 +195,7 @@ public:
 	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
 	void fillSurface(Graphics::Surface *surface, byte color);
+	void fillSurfaceRect(Graphics::Surface *surface, Common::Rect r, byte color);
 	bool isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
 		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels);
 public:
@@ -209,6 +211,7 @@ public:
 	void drawSurface(Common::Rect &dstRect, Graphics::Surface *surface, Common::Rect &srcRect, int16 scale, uint32 flags);
 	void drawText(FontResource *font, Graphics::Surface *surface, int16 x, int16 y, uint16 *text, uint count);
 	void fillSurface(Graphics::Surface *surface, byte color);
+	void fillSurfaceRect(Graphics::Surface *surface, Common::Rect r, byte color);
 	bool isSpritePixelSolid(Common::Point &testPt, Common::Point &drawPosition, Common::Point &drawOffset,
 		const SurfInfo &surfInfo, int16 scale, uint flags, byte *compressedPixels);
 public:
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index a7e97ea..fd2ef51 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -151,7 +151,11 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions,
 	}
 	*textPart = 0;
 
-	updateTextInfoPosition(Common::Point(160, 100));
+	if (_vm->getGameId() == kGameIdBBDOU) {
+		updateTextInfoPosition(Common::Point(320, 240));
+	} else {
+		updateTextInfoPosition(Common::Point(160, 100));
+	}
 
 	return done;
 }


Commit: 71edfa3f0af96847a15c58ee5d6d9df25867c7df
    https://github.com/scummvm/scummvm/commit/71edfa3f0af96847a15c58ee5d6d9df25867c7df
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Add quit opcode

(cherry picked from commit 32c2a9b)

Changed paths:
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h


diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 1c8d400..e70be8f 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -138,7 +138,7 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(80, opAddMenuChoice);
 	OPCODE(81, opDisplayMenu);
 	OPCODE(82, opSwitchMenuChoice);
-	// TODO OPCODE(83, opQuitGame);
+	OPCODE(83, opQuitGame);
 	OPCODE(84, opResetGame);
 	// TODO OPCODE(85, opSaveGame);
 	// TODO OPCODE(86, opRestoreGame);
@@ -728,6 +728,10 @@ void ScriptOpcodes_BBDOU::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall
 	opCall._deltaOfs += _vm->_menuChoiceOfs;
 }
 
+void ScriptOpcodes_BBDOU::opQuitGame(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->quitGame();
+}
+
 void ScriptOpcodes_BBDOU::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_threads->terminateThreads(opCall._callerThreadId);
 	_vm->reset();
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index 3171382..a111883 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -108,6 +108,7 @@ protected:
 	void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
 	void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
 	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
+	void opQuitGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);


Commit: 3fca484ae5e485935bf729926641cccba5733050
    https://github.com/scummvm/scummvm/commit/3fca484ae5e485935bf729926641cccba5733050
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Pan talk thread audio when using a named position

Changed paths:
    engines/illusions/threads/talkthread.cpp


diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index c7f7a67..9966308 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -146,8 +146,8 @@ int TalkThread::onUpdate() {
 		if (!(_flags & 4)) {
 			int16 panX = 0;
 			if (_namedPointId) {
-				// TODO pt.x = (unsigned int)artcntrlGetNamedPointPosition((Point)_namedPointId);
-				// TODO panX = convertPanXCoord(pt.x);
+				Common::Point pt = _vm->getNamedPointPosition(_namedPointId);
+				panX = _vm->convertPanXCoord(pt.x);
 			}
 			_vm->_soundMan->startVoice(255, panX);
 		}


Commit: 38577221c06910311902f4c6bb4984e6fce1f75d
    https://github.com/scummvm/scummvm/commit/38577221c06910311902f4c6bb4984e6fce1f75d
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Implement savegame saving/loading from GUI and the launcher

(cherry picked from commit 42f0bd4)

Changed paths:
  A engines/illusions/bbdou/gamestate_bbdou.cpp
  A engines/illusions/bbdou/gamestate_bbdou.h
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/bbdou_specialcode.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/illusions_bbdou.h
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.h
    engines/illusions/illusions.h
    engines/illusions/module.mk
    engines/illusions/specialcode.h


diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 0f3a264..8614fb6 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -214,6 +214,19 @@ void BbdouSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
 	}
 }
 
+void BbdouSpecialCode::resetBeforeResumeSavegame() {
+	if (_vm->getCurrentScene() == 0x00010032)
+		_inventory->close();
+	_vm->_threads->terminateThreads(0);
+	_vm->reset();
+	_vm->_input->activateButton(0xFFFF);
+	// TODO _vm->stopMusic();
+	// TODO _vm->_gameStates->clear();
+	_cursor->reset(0x0004001A);
+	setCursorControlRoutine(0x0004001A, 0);
+	_cursor->enable(0x0004001A);
+}
+
 // Special codes
 
 void BbdouSpecialCode::spcInitCursor(OpCall &opCall) {
diff --git a/engines/illusions/bbdou/bbdou_specialcode.h b/engines/illusions/bbdou/bbdou_specialcode.h
index df9b3f7..c281824 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.h
+++ b/engines/illusions/bbdou/bbdou_specialcode.h
@@ -110,6 +110,7 @@ public:
 	virtual ~BbdouSpecialCode();
 	virtual void init();
 	virtual void run(uint32 specialCodeId, OpCall &opCall);
+	virtual void resetBeforeResumeSavegame();
 public:
 	typedef Common::HashMap<uint32, SpecialCodeFunction*> Map;
 	typedef Map::iterator MapIterator;
diff --git a/engines/illusions/bbdou/gamestate_bbdou.cpp b/engines/illusions/bbdou/gamestate_bbdou.cpp
new file mode 100644
index 0000000..a20e2c4
--- /dev/null
+++ b/engines/illusions/bbdou/gamestate_bbdou.cpp
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "illusions/bbdou/gamestate_bbdou.h"
+#include "illusions/bbdou/illusions_bbdou.h"
+#include "illusions/resources/scriptresource.h"
+
+namespace Illusions {
+
+BBDOU_GameState::BBDOU_GameState(IllusionsEngine_BBDOU *vm)
+	: _vm(vm) {
+}
+
+uint32 BBDOU_GameState::calcWriteBufferSizeInternal() {
+	return
+		4 + // uint32 prevSceneId
+		_vm->_scriptResource->_properties.getSize() +
+		_vm->_scriptResource->_blockCounters.getSize();
+}
+
+bool BBDOU_GameState::readStateInternal(Common::ReadStream *in) {
+	_vm->_prevSceneId = in->readUint32LE();
+	return
+		_vm->_scriptResource->_properties.readFromStream(in) &&
+		_vm->_scriptResource->_blockCounters.readFromStream(in);
+}
+
+void BBDOU_GameState::writeStateInternal(Common::WriteStream *out) {
+	out->writeUint32LE(_vm->_prevSceneId);
+	_vm->_scriptResource->_properties.writeToStream(out);
+	_vm->_scriptResource->_blockCounters.writeToStream(out);
+}
+
+} // End of namespace Illusions
diff --git a/engines/illusions/bbdou/gamestate_bbdou.h b/engines/illusions/bbdou/gamestate_bbdou.h
new file mode 100644
index 0000000..2ef68b5
--- /dev/null
+++ b/engines/illusions/bbdou/gamestate_bbdou.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ILLUSIONS_BBDOU_GAMESTATE_BBDOU_H
+#define ILLUSIONS_BBDOU_GAMESTATE_BBDOU_H
+
+#include "illusions/gamestate.h"
+
+namespace Illusions {
+
+class IllusionsEngine_BBDOU;
+
+class BBDOU_GameState : public GameState {
+public:
+	BBDOU_GameState(IllusionsEngine_BBDOU *vm);
+protected:
+	IllusionsEngine_BBDOU *_vm;
+	uint32 calcWriteBufferSizeInternal();
+	bool readStateInternal(Common::ReadStream *in);
+	void writeStateInternal(Common::WriteStream *out);
+};
+
+} // End of namespace Illusions
+
+#endif // ILLUSIONS_BBDOU_GAMESTATE_BBDOU_H
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 7c129f1..78315c4 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -23,6 +23,7 @@
 #include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/bbdou_menukeys.h"
 #include "illusions/bbdou/bbdou_videoplayer.h"
+#include "illusions/bbdou/gamestate_bbdou.h"
 #include "illusions/bbdou/menusystem_bbdou.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -171,6 +172,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_soundMan = new SoundMan(this);
 	_menuSystem = new BBDOUMenuSystem(this);
 	_videoPlayer = new BBDOUVideoPlayer(this);
+	_gameState = new BBDOU_GameState(this);
 	_menuKeys = new BBDOUMenuKeys(this);
 
 	_screen->setColorKey1(0xF81F);
@@ -205,7 +207,12 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	startScriptThread(0x00020004, 0, 0, 0, 0);
 	_doScriptThreadInit = true;
 
+	if (ConfMan.hasKey("save_slot")) {
+		loadGameState(ConfMan.getInt("save_slot"));
+	}
+
 	_walkthroughStarted = false;
+	_canResumeFromSavegame = false;
 
 	while (!shouldQuit()) {
 		if (_walkthroughStarted) {
@@ -213,6 +220,10 @@ Common::Error IllusionsEngine_BBDOU::run() {
 			startScriptThread(0x00020404, 0, 0, 0, 0);
 			_walkthroughStarted = false;
 		}
+		if (_resumeFromSavegameRequested && _canResumeFromSavegame) {
+			resumeFromSavegame();
+			_resumeFromSavegameRequested = false;
+		}
 		runUpdateFunctions();
 		_system->updateScreen();
 		updateEvents();
@@ -222,6 +233,7 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	delete _scriptOpcodes;
 
 	delete _menuKeys;
+	delete _gameState;
 	delete _videoPlayer;
 	delete _menuSystem;
 	delete _soundMan;
@@ -248,12 +260,9 @@ Common::Error IllusionsEngine_BBDOU::run() {
 
 bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
 	return
-		false;
-		/*
 		(f == kSupportsRTL) ||
 		(f == kSupportsLoadingDuringRuntime) ||
 		(f == kSupportsSavingDuringRuntime);
-		*/
 }
 
 void IllusionsEngine_BBDOU::initInput() {
@@ -460,6 +469,11 @@ void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 call
 
 void IllusionsEngine_BBDOU::startScriptThread(uint32 threadId, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
+	if (threadId == 0x0002041E && ConfMan.hasKey("save_slot")) {
+		// Skip intro videos when loading a savegame from the launcher (kludge)
+		notifyThreadId(callingThreadId);
+		return;
+	}
 	debug(2, "Starting script thread %08X", threadId);
 	byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
 	newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
@@ -503,10 +517,6 @@ uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, u
 	return tempThreadId;
 }
 
-void IllusionsEngine_BBDOU::resumeFromSavegame(uint32 callingThreadId) {
-	// TODO
-}
-
 uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 	uint32 value8, uint32 valueC, uint32 value10) {
 	uint32 tempThreadId = newTempThreadId();
@@ -555,6 +565,13 @@ bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
 		sceneId = _theSceneId;
 	}
 	_activeScenes.push(sceneId);
+	if (sceneId == 0x0001007D) {
+		// Savegame loading from the ScummVM GUI or command line is only
+		// possible after resources have been initialized by the startup script.
+		// Once that script is done, it switches to the start menu scene.
+		// After that the game is ready and a savegame can finally be loaded.
+		_canResumeFromSavegame = true;
+	}
 	return sceneInfo != 0;
 }
 
@@ -647,4 +664,34 @@ void IllusionsEngine_BBDOU::reset() {
 	// TODO script_sub_417FF0(1, 0);
 }
 
+void IllusionsEngine_BBDOU::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
+	// NOTE Just loads the savegame, doesn't activate it yet
+	const char *fileName = getSavegameFilename(_savegameSlotNum);
+	_loadGameResult = loadgame(fileName);
+}
+
+void IllusionsEngine_BBDOU::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
+	// TODO
+	// const char *fileName = getSavegameFilename(slotNum);
+	_saveGameResult = false;//savegame(fileName, _savegameDescription.c_str());
+}
+
+void IllusionsEngine_BBDOU::activateSavegame(uint32 callingThreadId) {
+	uint32 sceneId, threadId;
+	_prevSceneId = 0x10000;
+	_gameState->readState(sceneId, threadId);
+	enterScene(sceneId, callingThreadId);
+	// TODO Check if value8, valueC, value10 are needed at all
+	startAnonScriptThread(threadId, 0, 0, 0, 0);
+	_gameState->deleteReadStream();
+}
+
+void IllusionsEngine_BBDOU::resumeFromSavegame() {
+	// Resetting the game is usually done by the script, when loading from the ScummVM menu or
+	// command line this has to be done manually.
+	_specialCode->resetBeforeResumeSavegame();
+	dumpActiveScenes(0x00010003, 0);
+	activateSavegame(0);
+}
+
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/illusions_bbdou.h b/engines/illusions/bbdou/illusions_bbdou.h
index 2bad993..f9311e1 100644
--- a/engines/illusions/bbdou/illusions_bbdou.h
+++ b/engines/illusions/bbdou/illusions_bbdou.h
@@ -75,11 +75,14 @@ public:
 	uint32 _theThreadId;
 	uint32 _globalSceneId;
 
+	bool _loadGameResult, _saveGameResult;
+
 	BBDOUMenuSystem *_menuSystem;
 	BBDOUVideoPlayer *_videoPlayer;
 	BBDOUMenuKeys *_menuKeys;
 
 	bool _walkthroughStarted;
+	bool _canResumeFromSavegame;
 
 	void initInput();
 
@@ -128,7 +131,6 @@ public:
 		uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
 	uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10);
-	void resumeFromSavegame(uint32 callingThreadId);
 
 	void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
 		byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
@@ -151,6 +153,11 @@ public:
 	bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
 	void reset();
 
+	void loadSavegameFromScript(int16 slotNum, uint32 callingThreadId);
+	void saveSavegameFromScript(int16 slotNum, uint32 callingThreadId);
+	void activateSavegame(uint32 callingThreadId);
+	void resumeFromSavegame();
+
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index e70be8f..9dd9c9a 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -23,6 +23,7 @@
 #include "illusions/bbdou/illusions_bbdou.h"
 #include "illusions/bbdou/scriptopcodes_bbdou.h"
 #include "illusions/bbdou/bbdou_menukeys.h"
+#include "illusions/bbdou/gamestate_bbdou.h"
 #include "illusions/bbdou/menusystem_bbdou.h"
 #include "illusions/actor.h"
 #include "illusions/camera.h"
@@ -140,8 +141,8 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(82, opSwitchMenuChoice);
 	OPCODE(83, opQuitGame);
 	OPCODE(84, opResetGame);
-	// TODO OPCODE(85, opSaveGame);
-	// TODO OPCODE(86, opRestoreGame);
+	OPCODE(85, opSaveGame);
+	OPCODE(86, opRestoreGameState);
 	OPCODE(87, opDeactivateButton);
 	OPCODE(88, opActivateButton);
 	OPCODE(89, opNop);
@@ -170,6 +171,10 @@ void ScriptOpcodes_BBDOU::initOpcodes() {
 	OPCODE(161, opSetActorUsePan);
 	OPCODE(168, opStartAbortableThread);
 	OPCODE(169, opKillThread);
+	OPCODE(170, opLoadGame);
+	OPCODE(171, opPushLoadgameResult);
+	OPCODE(172, opPushSavegameResult);
+	// 173, 174 unused
 	OPCODE(175, opSetSceneIdThreadId);
 	OPCODE(176, opStackPush0);
 	OPCODE(177, opSetFontId);
@@ -342,7 +347,7 @@ void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCa
 	_vm->_prevSceneId = _vm->getCurrentScene();
 	_vm->exitScene(opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->_gameState->writeState(sceneId, threadId);
 	_vm->startAnonScriptThread(threadId, 0,
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
 }
@@ -740,6 +745,17 @@ void ScriptOpcodes_BBDOU::opResetGame(ScriptThread *scriptThread, OpCall &opCall
 	// TODO _vm->_gameStates->clear();
 }
 
+void ScriptOpcodes_BBDOU::opSaveGame(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(bankNum)
+	ARG_INT16(slotNum)
+	_vm->saveSavegameFromScript(slotNum, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opRestoreGameState(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->activateSavegame(opCall._callerThreadId);
+}
+
 void ScriptOpcodes_BBDOU::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(button)
 	_vm->_input->deactivateButton(button);
@@ -920,6 +936,21 @@ void ScriptOpcodes_BBDOU::opKillThread(ScriptThread *scriptThread, OpCall &opCal
 	_vm->_threads->killThread(threadId);
 }
 
+void ScriptOpcodes_BBDOU::opLoadGame(ScriptThread *scriptThread, OpCall &opCall) {
+	ARG_SKIP(2);
+	ARG_INT16(bankNum)
+	ARG_INT16(slotNum)
+	_vm->loadSavegameFromScript(slotNum, opCall._callerThreadId);
+}
+
+void ScriptOpcodes_BBDOU::opPushLoadgameResult(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->push(_vm->_loadGameResult ? 1 : 0);
+}
+
+void ScriptOpcodes_BBDOU::opPushSavegameResult(ScriptThread *scriptThread, OpCall &opCall) {
+	_vm->_stack->push(_vm->_saveGameResult ? 1 : 0);
+}
+
 void ScriptOpcodes_BBDOU::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(sceneId);
@@ -941,7 +972,6 @@ void ScriptOpcodes_BBDOU::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCal
 	ARG_SKIP(2);
 	ARG_UINT32(key);
 	ARG_UINT32(threadId);
-	debug("addMenuKey(%08X; %08X)", key, threadId);
 	_vm->_menuKeys->addMenuKey(key, threadId);
 }
 
@@ -954,7 +984,7 @@ void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &o
 	_vm->_prevSceneId = _vm->getCurrentScene();
 	_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
 	_vm->enterScene(sceneId, opCall._callerThreadId);
-	// TODO _vm->_gameStates->writeStates(_vm->_prevSceneId, sceneId, threadId);
+	_vm->_gameState->writeState(sceneId, threadId);
 	_vm->startAnonScriptThread(threadId, 0,
 		scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
 }
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.h b/engines/illusions/bbdou/scriptopcodes_bbdou.h
index a111883..63731cf 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.h
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.h
@@ -110,6 +110,8 @@ protected:
 	void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
 	void opQuitGame(ScriptThread *scriptThread, OpCall &opCall);
 	void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opSaveGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opRestoreGameState(ScriptThread *scriptThread, OpCall &opCall);
 	void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
 	void opNop(ScriptThread *scriptThread, OpCall &opCall);
@@ -135,6 +137,9 @@ protected:
 	void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
 	void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
 	void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
+	void opLoadGame(ScriptThread *scriptThread, OpCall &opCall);
+	void opPushLoadgameResult(ScriptThread *scriptThread, OpCall &opCall);
+	void opPushSavegameResult(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
 	void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
 	void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
diff --git a/engines/illusions/illusions.h b/engines/illusions/illusions.h
index 9c73c47..4140c9c 100644
--- a/engines/illusions/illusions.h
+++ b/engines/illusions/illusions.h
@@ -211,7 +211,6 @@ public:
 	virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
 	virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
 		uint32 value8, uint32 valueC, uint32 value10) = 0;
-	virtual void resumeFromSavegame(uint32 callingThreadId) = 0;
 
 	// Savegame API
 
diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk
index cb68485..6d2a5aa 100644
--- a/engines/illusions/module.mk
+++ b/engines/illusions/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	bbdou/bbdou_menukeys.o \
 	bbdou/bbdou_specialcode.o \
 	bbdou/bbdou_videoplayer.o \
+	bbdou/gamestate_bbdou.o \
 	bbdou/bbdou_triggerfunctions.o \
 	bbdou/illusions_bbdou.o \
 	bbdou/menusystem_bbdou.o \
diff --git a/engines/illusions/specialcode.h b/engines/illusions/specialcode.h
index 9a69b38..25d7032 100644
--- a/engines/illusions/specialcode.h
+++ b/engines/illusions/specialcode.h
@@ -47,6 +47,7 @@ public:
 	virtual ~SpecialCode() {}
 	virtual void init() = 0;
 	virtual void run(uint32 specialCodeId, OpCall &opCall) = 0;
+	virtual void resetBeforeResumeSavegame() {};
 };
 
 } // End of namespace Illusions


Commit: 96a862536d8c8214140c1350d479dedd22b6df3b
    https://github.com/scummvm/scummvm/commit/96a862536d8c8214140c1350d479dedd22b6df3b
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement sequence opcode 32

(cherry picked from commit 30cdb32)

Changed paths:
    engines/illusions/sequenceopcodes.cpp
    engines/illusions/sequenceopcodes.h


diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 7652471..36111a7 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -87,7 +87,7 @@ void SequenceOpcodes::initOpcodes() {
 	OPCODE(28, opNotifyThreadId1);
 	OPCODE(29, opSetPathCtrY);
 	// 30-31 unused in Duckman, CHECKME BBDOU
-	// TODO OPCODE(32, );
+	OPCODE(32, opDisablePathWalkPoints);
 	OPCODE(33, opSetPathWalkPoints);
 	OPCODE(34, opDisableAutoScale);
 	OPCODE(35, opSetScale);
@@ -296,6 +296,10 @@ void SequenceOpcodes::opSetPathCtrY(Control *control, OpCall &opCall) {
 	control->_actor->_pathCtrY = pathCtrY;
 }
 
+void SequenceOpcodes::opDisablePathWalkPoints(Control *control, OpCall &opCall) {
+	control->_actor->_flags &= ~2;
+}
+
 void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {
 	ARG_INT16(pathWalkPointsIndex);
 	BackgroundResource *bgRes = _vm->_backgroundInstances->getActiveBgResource();
diff --git a/engines/illusions/sequenceopcodes.h b/engines/illusions/sequenceopcodes.h
index 5fe5281..a6cc573 100644
--- a/engines/illusions/sequenceopcodes.h
+++ b/engines/illusions/sequenceopcodes.h
@@ -70,6 +70,7 @@ protected:
 	void opFaceActor(Control *control, OpCall &opCall);
 	void opNotifyThreadId1(Control *control, OpCall &opCall);
 	void opSetPathCtrY(Control *control, OpCall &opCall);
+	void opDisablePathWalkPoints(Control *control, OpCall &opCall);
 	void opSetPathWalkPoints(Control *control, OpCall &opCall);
 	void opDisableAutoScale(Control *control, OpCall &opCall);
 	void opSetScale(Control *control, OpCall &opCall);


Commit: 8ca26ea2a0ed82bac3193a3a72564c7d4a820084
    https://github.com/scummvm/scummvm/commit/8ca26ea2a0ed82bac3193a3a72564c7d4a820084
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Use actor flag instead of magic number

Changed paths:
    engines/illusions/sequenceopcodes.cpp


diff --git a/engines/illusions/sequenceopcodes.cpp b/engines/illusions/sequenceopcodes.cpp
index 36111a7..90b9c53 100644
--- a/engines/illusions/sequenceopcodes.cpp
+++ b/engines/illusions/sequenceopcodes.cpp
@@ -297,7 +297,7 @@ void SequenceOpcodes::opSetPathCtrY(Control *control, OpCall &opCall) {
 }
 
 void SequenceOpcodes::opDisablePathWalkPoints(Control *control, OpCall &opCall) {
-	control->_actor->_flags &= ~2;
+	control->_actor->_flags &= ~Illusions::ACTOR_FLAG_HAS_WALK_POINTS;
 }
 
 void SequenceOpcodes::opSetPathWalkPoints(Control *control, OpCall &opCall) {


Commit: 1f6aee019c85771cabddc0e807a4c2cad503cca0
    https://github.com/scummvm/scummvm/commit/1f6aee019c85771cabddc0e807a4c2cad503cca0
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: BBDOU: Rename variables (bubble and special code)

(cherry picked from commit 93255ca)

Changed paths:
    engines/illusions/bbdou/bbdou_bubble.cpp
    engines/illusions/bbdou/bbdou_bubble.h
    engines/illusions/bbdou/bbdou_specialcode.cpp


diff --git a/engines/illusions/bbdou/bbdou_bubble.cpp b/engines/illusions/bbdou/bbdou_bubble.cpp
index cba0dcc..fd2585f 100644
--- a/engines/illusions/bbdou/bbdou_bubble.cpp
+++ b/engines/illusions/bbdou/bbdou_bubble.cpp
@@ -38,7 +38,7 @@ BbdouBubble::~BbdouBubble() {
 
 void BbdouBubble::init() {
 
-	static const uint32 kObjectIds3[] = {
+	static const uint32 kTrailObjectIds[] = {
 		0x0004003B, 0x0004003C, 0x0004003D, 0x0004003E,
 		0x0004003F, 0x00040040, 0x00040041, 0x00040042,
 		0x00040043, 0x00040044, 0x00040045, 0x00040046,
@@ -49,7 +49,7 @@ void BbdouBubble::init() {
 		0x00040057, 0x00040058, 0x00040059, 0x0004005A
 	};
 
-	static const uint32 kObjectIds2[] = {
+	static const uint32 kIconObjectIds[] = {
 		0x0004001B, 0x0004001C, 0x0004001D, 0x0004001E,
 		0x0004001F, 0x00040020, 0x00040021, 0x00040022,
 		0x00040023, 0x00040024, 0x00040025, 0x00040026,
@@ -60,136 +60,136 @@ void BbdouBubble::init() {
 		0x00040037, 0x00040038, 0x00040039, 0x0004003A
 	};
 
-	_objectId1414 = 0x4005B;
-	_objectId1418 = 0x4005C;
+	_bubbleObjectId1 = 0x4005B;
+	_bubbleObjectId2 = 0x4005C;
 
 	for (uint i = 0; i < 32; ++i) {
-		_objectIds[i] = kObjectIds3[i];
+		_trailObjectIds[i] = kTrailObjectIds[i];
 	}
 
 	for (uint i = 0; i < 32; ++i) {
-		_items[i]._objectId = kObjectIds2[i];
-		_items[i]._enabled = 0;
-		_items[i]._position.x = 0;
-		_items[i]._position.y = 0;
-		_items[i]._sequenceId = 0;
+		_icons[i]._objectId = kIconObjectIds[i];
+		_icons[i]._enabled = false;
+		_icons[i]._position.x = 0;
+		_icons[i]._position.y = 0;
+		_icons[i]._sequenceId = 0;
 	}
 
-	_currItem0 = 0;
-	_prevItem0 = 0;
-	_someItem0 = 0;
-	_pt1.x = 0;
-	_pt1.y = 0;
-	_pt2.x = 0;
-	_pt2.y = 0;
+	_currBubbleStyle = 0;
+	_showingBubbleStyle = 0;
+	_hidingBubbleStyle = 0;
+	_sourcePt.x = 0;
+	_sourcePt.y = 0;
+	_destPt.x = 0;
+	_destPt.y = 0;
 
 }
 
-void BbdouBubble::addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progResKeywordId,
+void BbdouBubble::addBubbleStyle(uint32 showSequenceId, uint32 hideSequenceId, uint32 progResKeywordId,
 	uint32 namedPointId, int16 count, uint32 *namedPointIds) {
-	Item0 item0;
-	item0._sequenceId1 = sequenceId1;
-	item0._sequenceId2 = sequenceId2;
-	item0._progResKeywordId = progResKeywordId;
-	item0._baseNamedPointId = namedPointId;
-	item0._count = count;
+	BubbleStyle style;
+	style._showSequenceId = showSequenceId;
+	style._hideSequenceId = hideSequenceId;
+	style._progResKeywordId = progResKeywordId;
+	style._baseNamedPointId = namedPointId;
+	style._count = count;
 	for (int16 i = 0; i < count; ++i) {
-		item0._namedPointIds[i] = FROM_LE_32(namedPointIds[i]);
+		style._namedPointIds[i] = FROM_LE_32(namedPointIds[i]);
 	}
-	item0._objectId = 0;
-	item0._pt.x = 0;
-	item0._pt.y = 0;
-	_item0s.push_back(item0);
+	style._objectId = 0;
+	style._position.x = 0;
+	style._position.y = 0;
+	_bubbleStyles.push_back(style);
 }
 
 void BbdouBubble::show() {
-
-	if (_prevItem0) {
+	
+	if (_showingBubbleStyle) {
 		hide();
 	}
 
-	_prevItem0 = _currItem0;
-	_currItem0 = 0;
+	_showingBubbleStyle = _currBubbleStyle;
+	_currBubbleStyle = 0;
 
-	calcBubbles(_pt1, _pt2);
+	calcBubbleTrail(_sourcePt, _destPt);
 
-	Control *control = _vm->_dict->getObjectControl(_prevItem0->_objectId);
-	control->setActorPosition(_pt2);
-	control->startSequenceActor(0x60057, 2, 0);
-	control->startSequenceActor(_prevItem0->_sequenceId1, 2, 0);
-	control->appearActor();
-	control->deactivateObject();
+	Control *bubbleControl = _vm->_dict->getObjectControl(_showingBubbleStyle->_objectId);
+	bubbleControl->setActorPosition(_destPt);
+	bubbleControl->startSequenceActor(0x60057, 2, 0);
+	bubbleControl->startSequenceActor(_showingBubbleStyle->_showSequenceId, 2, 0);
+	bubbleControl->appearActor();
+	bubbleControl->deactivateObject();
 
 	for (uint i = 0; i < 32; ++i) {
-		if (_items[i]._enabled == 1) {
-			Control *subControl = _vm->_dict->getObjectControl(_items[i]._objectId);
-			subControl->setActorPosition(_items[i]._position);
-			subControl->startSequenceActor(_items[i]._sequenceId, 2, 0);
+		if (_icons[i]._enabled) {
+			Control *iconControl = _vm->_dict->getObjectControl(_icons[i]._objectId);
+			iconControl->setActorPosition(_icons[i]._position);
+			iconControl->startSequenceActor(_icons[i]._sequenceId, 2, 0);
 		}
 	}
 
 }
 
 void BbdouBubble::hide() {
-	_someItem0 = _prevItem0;
-	_prevItem0 = 0;
-	if (_someItem0) {
-		Control *control = _vm->_dict->getObjectControl(_someItem0->_objectId);
-		control->startSequenceActor(_someItem0->_sequenceId2, 2, 0);
+	_hidingBubbleStyle = _showingBubbleStyle;
+	_showingBubbleStyle = 0;
+	if (_hidingBubbleStyle) {
+		Control *bubbleControl = _vm->_dict->getObjectControl(_hidingBubbleStyle->_objectId);
+		bubbleControl->startSequenceActor(_hidingBubbleStyle->_hideSequenceId, 2, 0);
 		for (uint i = 0; i < 32; ++i) {
-			Control *subControl = _vm->_dict->getObjectControl(_objectIds[i]);
-			subControl->stopActor();
-			subControl->disappearActor();
+			Control *trailControl = _vm->_dict->getObjectControl(_trailObjectIds[i]);
+			trailControl->stopActor();
+			trailControl->disappearActor();
 		}
 		for (uint i = 0; i < 32; ++i) {
-			Control *subControl = _vm->_dict->getObjectControl(_items[i]._objectId);
-			subControl->stopActor();
-			subControl->disappearActor();
+			Control *iconControl = _vm->_dict->getObjectControl(_icons[i]._objectId);
+			iconControl->stopActor();
+			iconControl->disappearActor();
 		}
 	}
 }
 
-void BbdouBubble::setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId) {
+void BbdouBubble::selectBubbleStyle(int16 minCount, Common::Point sourcePt, Common::Point destPt, uint32 progResKeywordId) {
 	for (uint i = 0; i < 32; ++i) {
-		_items[i]._enabled = 0;
+		_icons[i]._enabled = false;
 	}
 	int16 maxCount = 32;
-	for (uint i = 0; i < _item0s.size(); ++i) {
-		Item0 *item0 = &_item0s[i];
-		if (item0->_count < maxCount && item0->_count >= minCount &&
-			(!progResKeywordId || item0->_progResKeywordId == progResKeywordId)) {
-			maxCount = item0->_count;
-			_currItem0 = item0;
+	for (uint i = 0; i < _bubbleStyles.size(); ++i) {
+		BubbleStyle *style = &_bubbleStyles[i];
+		if (style->_count < maxCount && style->_count >= minCount &&
+			(progResKeywordId == 0 || progResKeywordId == style->_progResKeywordId)) {
+			maxCount = style->_count;
+			_currBubbleStyle = style;
 		}
 	}
-	_pt1 = pt1;
-	_pt2 = pt2;
-	_currItem0->_pt = pt2;
-	_currItem0->_objectId = _objectId1414;
-	if (_prevItem0 && _prevItem0->_objectId == _currItem0->_objectId)
-		_currItem0->_objectId = _objectId1418;
+	_sourcePt = sourcePt;
+	_destPt = destPt;
+	_currBubbleStyle->_position = destPt;
+	_currBubbleStyle->_objectId = _bubbleObjectId1;
+	if (_showingBubbleStyle && _showingBubbleStyle->_objectId == _currBubbleStyle->_objectId)
+		_currBubbleStyle->_objectId = _bubbleObjectId2;
 }
 
-uint32 BbdouBubble::addItem(uint positionIndex, uint32 sequenceId) {
+uint32 BbdouBubble::addBubbleIcon(uint positionIndex, uint32 sequenceId) {
 	for (uint i = 0; i < 32; ++i) {
-		Item141C *item = &_items[i];
-		if (!item->_enabled) {
-			Common::Point itemPos = _vm->getNamedPointPosition(_currItem0->_namedPointIds[positionIndex]);
-			Common::Point basePos = _vm->getNamedPointPosition(_currItem0->_baseNamedPointId);
-			item->_enabled = 1;
-			item->_sequenceId = sequenceId;
-			item->_position.x = itemPos.x + _currItem0->_pt.x - basePos.x;
-			item->_position.y = itemPos.y + _currItem0->_pt.y - basePos.y;
-			return item->_objectId;
+		BubbleIcon *icon = &_icons[i];
+		if (!icon->_enabled) {
+			Common::Point itemPos = _vm->getNamedPointPosition(_currBubbleStyle->_namedPointIds[positionIndex]);
+			Common::Point basePos = _vm->getNamedPointPosition(_currBubbleStyle->_baseNamedPointId);
+			icon->_enabled = true;
+			icon->_sequenceId = sequenceId;
+			icon->_position.x = itemPos.x + _currBubbleStyle->_position.x - basePos.x;
+			icon->_position.y = itemPos.y + _currBubbleStyle->_position.y - basePos.y;
+			return icon->_objectId;
 		}
 	}
 	return 0;
 }
 
-void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
+void BbdouBubble::calcBubbleTrail(Common::Point &sourcePt, Common::Point &destPt) {
 	const int kSequenceIdsCount = 10;
 	const float kDistanceBetweenPoints = 30.0;
-	static const uint32 kSequenceIds[] = {
+	static const uint32 kBubbleTrailSequenceIds[] = {
 		0x00060042, 0x00060043, 0x00060044, 0x00060045, 0x00060046,
 		0x00060047, 0x00060048, 0x00060049, 0x0006004A, 0x0006004B
 	};
@@ -201,7 +201,7 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 	float currentAngle, radius;
 
 	for (int i = 0; i < 32; ++i) {
-		Control *control = _vm->_dict->getObjectControl(_objectIds[i]);
+		Control *control = _vm->_dict->getObjectControl(_trailObjectIds[i]);
 		control->startSequenceActor(0x00060056, 2, 0);
 	}
 
@@ -209,27 +209,27 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 		sequenceCounters[i] = 0;
 	}
 
-	if (pt2.y >= pt1.y) {
+	if (destPt.y >= sourcePt.y) {
 		swapY = true;
-		if (pt1.x == pt2.x)
-			pt2.x = pt2.x + 20;
+		if (sourcePt.x == destPt.x)
+			destPt.x = destPt.x + 20;
 	} else {
 		swapY = false;
-		if (pt1.y == pt2.y)
-			pt2.y = pt2.y + 20;
+		if (sourcePt.y == destPt.y)
+			destPt.y = destPt.y + 20;
 	}
 
 	if (swapY) {
-		centerX = (pt2.x * pt2.x - (pt2.y - pt1.y) * (pt2.y - pt1.y) - pt1.x * pt1.x) / (2 * (pt2.x - pt1.x));
-		centerY = pt2.y;
-		radius = ABS(pt2.x - centerX);
+		centerX = (destPt.x * destPt.x - (destPt.y - sourcePt.y) * (destPt.y - sourcePt.y) - sourcePt.x * sourcePt.x) / (2 * (destPt.x - sourcePt.x));
+		centerY = destPt.y;
+		radius = ABS(destPt.x - centerX);
 	} else {
-		centerX = pt2.x;
-		centerY = (pt2.y * pt2.y - (pt2.x - pt1.x) * (pt2.x - pt1.x) - pt1.y * pt1.y) / (2 * (pt2.y - pt1.y));
-		radius = ABS(pt2.y - centerY);
+		centerX = destPt.x;
+		centerY = (destPt.y * destPt.y - (destPt.x - sourcePt.x) * (destPt.x - sourcePt.x) - sourcePt.y * sourcePt.y) / (2 * (destPt.y - sourcePt.y));
+		radius = ABS(destPt.y - centerY);
 	}
 
-	const float fullDistance = sqrt((pt2.y - pt1.y) * (pt2.y - pt1.y) + (pt2.x - pt1.x) * (pt2.x - pt1.x));
+	const float fullDistance = sqrt((destPt.y - sourcePt.y) * (destPt.y - sourcePt.y) + (destPt.x - sourcePt.x) * (destPt.x - sourcePt.x));
 	const float arcAngle = 2 * asin(CLIP(0.5 * fullDistance / radius, -1.0, 1.0));
 	const float arcLength = arcAngle * radius;
 	int pointsCount = (int)(arcLength / kDistanceBetweenPoints);
@@ -240,26 +240,25 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 	}
 
 	if (!swapY) {
-		if (pt2.y < pt1.y) {
+		if (destPt.y < sourcePt.y) {
 			currentAngle = M_PI * 0.5;
 		} else {
 			currentAngle = M_PI * 1.5;
 			partAngle = -partAngle;
 		}
-		if (pt2.x < pt1.x)
+		if (destPt.x < sourcePt.x)
 			partAngle = -partAngle;
 	} else {
-		if (pt2.x <= pt1.x) {
+		if (destPt.x <= sourcePt.x) {
 			currentAngle = M_PI;
 		} else {
 			currentAngle = 0.0;
 			partAngle = -partAngle;
 		}
-		if (pt2.y > pt1.y)
+		if (destPt.y > sourcePt.y)
 			partAngle = -partAngle;
 	}
 
-	int index = kSequenceIdsCount - 1;
 	float angleStep = partAngle / (float)pointsCount * 0.5;
 	float angleIncr = (float)(pointsCount / 2) * angleStep + partAngle;
 
@@ -275,15 +274,15 @@ void BbdouBubble::calcBubbles(Common::Point &pt1, Common::Point &pt2) {
 			centerX + _vm->getRandom(8) - 2 + (int)(cos(currentAngle) * radius),
 			centerY + _vm->getRandom(8) - 2 - (int)(sin(currentAngle) * radius));
 
-		Control *control = _vm->_dict->getObjectControl(_objectIds[i]);
+		Control *trailControl = _vm->_dict->getObjectControl(_trailObjectIds[i]);
 
-		for (; index >= 0; --index) {
+		for (int index = kSequenceIdsCount - 1; index >= 0; --index) {
 			if (sequenceCounters[index] > 0) {
 				--sequenceCounters[index];
-				control->setActorPosition(newPoint);
-				control->startSequenceActor(kSequenceIds[index], 2, 0);
-				control->appearActor();
-				control->deactivateObject();
+				trailControl->setActorPosition(newPoint);
+				trailControl->startSequenceActor(kBubbleTrailSequenceIds[index], 2, 0);
+				trailControl->appearActor();
+				trailControl->deactivateObject();
 				break;
 			}
 		}
diff --git a/engines/illusions/bbdou/bbdou_bubble.h b/engines/illusions/bbdou/bbdou_bubble.h
index f42ff6e..fbfe726 100644
--- a/engines/illusions/bbdou/bbdou_bubble.h
+++ b/engines/illusions/bbdou/bbdou_bubble.h
@@ -32,23 +32,22 @@ class IllusionsEngine_BBDOU;
 class BbdouSpecialCode;
 class Control;
 
-struct Item0 {
-	uint32 _sequenceId1;
-	uint32 _sequenceId2;
+struct BubbleStyle {
+	uint32 _showSequenceId;
+	uint32 _hideSequenceId;
 	int16 _count;
 	uint32 _progResKeywordId;
 	uint32 _baseNamedPointId;
 	uint32 _namedPointIds[32];
 	uint32 _objectId;
-	Common::Point _pt;
-	Item0() : _count(0) {}
+	Common::Point _position;
+	BubbleStyle() : _count(0) {}
 };
 
-struct Item141C {
+struct BubbleIcon {
 	uint32 _objectId;
-	int16 _enabled;
+	bool _enabled;
 	Common::Point _position;
-	int16 _fieldA;
 	uint32 _sequenceId;
 };
 
@@ -57,26 +56,26 @@ public:
 	BbdouBubble(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
 	~BbdouBubble();
 	void init();
-	void addItem0(uint32 sequenceId1, uint32 sequenceId2, uint32 progResKeywordId,
+	void addBubbleStyle(uint32 showSequenceId, uint32 hideSequenceId, uint32 progResKeywordId,
 		uint32 namedPointId, int16 count, uint32 *namedPointIds);
 	void show();
 	void hide();
-	void setup(int16 minCount, Common::Point pt1, Common::Point pt2, uint32 progResKeywordId);
-	uint32 addItem(uint positionIndex, uint32 sequenceId);
-	void calcBubbles(Common::Point &pt1, Common::Point &pt2);
+	void selectBubbleStyle(int16 minCount, Common::Point sourcePt, Common::Point destPt, uint32 progResKeywordId);
+	uint32 addBubbleIcon(uint positionIndex, uint32 sequenceId);
+	void calcBubbleTrail(Common::Point &sourcePt, Common::Point &destPt);
 protected:
 	IllusionsEngine_BBDOU *_vm;
 	BbdouSpecialCode *_bbdou;
-	Common::Array<Item0> _item0s;
-	Item0 *_currItem0;
-	Item0 *_prevItem0;
-	Item0 *_someItem0;
-	uint32 _objectIds[32];
-	Common::Point _pt1;
-	Common::Point _pt2;
-	int _objectId1414;
-	int _objectId1418;
-	Item141C _items[32];
+	Common::Array<BubbleStyle> _bubbleStyles;
+	BubbleStyle *_currBubbleStyle;
+	BubbleStyle *_showingBubbleStyle;
+	BubbleStyle *_hidingBubbleStyle;
+	uint32 _trailObjectIds[32];
+	Common::Point _sourcePt;
+	Common::Point _destPt;
+	int _bubbleObjectId1;
+	int _bubbleObjectId2;
+	BubbleIcon _icons[32];
 };
 
 } // End of namespace Illusions
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 8614fb6..9bdab0c 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -38,13 +38,13 @@
 
 namespace Illusions {
 
-static const Struct10 kStruct10s[] = {
+static const Struct10 kVerbIconSequenceIds[] = {
 	{0x1B0000,       0,       0,       0},
-	{0x1B0001, 0x6001A, 0x6001B, 0x6001C},
-	{0x1B0002, 0x6001D, 0x6001E, 0x6001F},
-	{0x1B0003, 0x60020, 0x60021, 0x60022},
-	{0x1B0004, 0x60023, 0x60024, 0x60025},
-	{0x1B0005, 0x60026, 0x60027, 0x60028},
+	{0x1B0001, 0x6001A, 0x6001B, 0x6001C}, // TALK, LOOK
+	{0x1B0002, 0x6001D, 0x6001E, 0x6001F}, // USE, LOOK
+	{0x1B0003, 0x60020, 0x60021, 0x60022}, // USE
+	{0x1B0004, 0x60023, 0x60024, 0x60025}, // USE, LOOK
+	{0x1B0005, 0x60026, 0x60027, 0x60028}, // TALK, LOOK
 	{0x1B0006,       0,       0,       0},
 	{0x1B0007,       0,       0,       0},
 	{0x1B0008,       0,       0,       0},
@@ -296,7 +296,7 @@ void BbdouSpecialCode::spcSetupBubble(OpCall &opCall) {
 	ARG_UINT32(progResKeywordId);
 	ARG_UINT32(namedPointId);
 	ARG_INT16(count);
-	_bubble->addItem0(sequenceId1, sequenceId2, progResKeywordId, namedPointId,
+	_bubble->addBubbleStyle(sequenceId1, sequenceId2, progResKeywordId, namedPointId,
 		count, (uint32*)opCall._code);
 	_vm->notifyThreadId(opCall._threadId);
 }
@@ -552,7 +552,7 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 	VerbState *verbState, uint32 progResKeywordId) {
 
 	Common::Rect collisionRect;
-	Control *overlappedControl, *control2, *control3;
+	Control *overlappedControl, *control2;
 	Common::Point bubbleSourcePt(320, 240), bubbleDestPt, currPan;
 
 	overlappedControl = _vm->_dict->getObjectControl(overlappedObjectId);
@@ -574,10 +574,10 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 			bubbleSourcePt.y += 80;
 	}
 
-	_bubble->setup(1, bubbleSourcePt, bubbleDestPt, progResKeywordId);
+	_bubble->selectBubbleStyle(1, bubbleSourcePt, bubbleDestPt, progResKeywordId);
 
-	verbState->_objectIds[0] = _bubble->addItem(0, 0x6005A);
-	verbState->_objectIds[1] = _bubble->addItem(0, 0x6005A);
+	verbState->_objectIds[0] = _bubble->addBubbleIcon(0, 0x6005A);
+	verbState->_objectIds[1] = _bubble->addBubbleIcon(0, 0x6005A);
 	verbState->_index = 0;
 
 	int value = _objectInteractModeMap.getObjectInteractMode(overlappedControl->_objectId);
@@ -591,13 +591,13 @@ void BbdouSpecialCode::showBubble(uint32 objectId, uint32 overlappedObjectId, ui
 		verbState->_verbId = 0x1B0002;
 	}
 
-	uint32 sequenceId = kStruct10s[verbState->_verbId & 0xFFFF]._sequenceId2;
 	_bubble->show();
 
-	control3 = _vm->_dict->getObjectControl(verbState->_objectIds[0]);
-	control3->startSequenceActor(sequenceId, 2, 0);
-	control3->appearActor();
-	control3->deactivateObject();
+	Control *verbIconControl = _vm->_dict->getObjectControl(verbState->_objectIds[0]);
+	uint32 sequenceId = kVerbIconSequenceIds[verbState->_verbId & 0xFFFF]._sequenceId2;
+	verbIconControl->startSequenceActor(sequenceId, 2, 0);
+	verbIconControl->appearActor();
+	verbIconControl->deactivateObject();
 
 	verbState->_isBubbleVisible = true;
 	_vm->_input->discardAllEvents();


Commit: 65049228a8d4bcfbcedae75d098256bbbdc92baa
    https://github.com/scummvm/scummvm/commit/65049228a8d4bcfbcedae75d098256bbbdc92baa
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Remove old TODOs

(cherry picked from commit bb95440)

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_foodctl.h
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/bbdou/scriptopcodes_bbdou.cpp
    engines/illusions/duckman/duckman_specialcode.cpp
    engines/illusions/illusions.cpp
    engines/illusions/input.cpp
    engines/illusions/resources/fontresource.h
    engines/illusions/resourcesystem.h
    engines/illusions/screentext.cpp
    engines/illusions/thread.cpp
    engines/illusions/threads/talkthread.cpp
    engines/illusions/threads/talkthread_duckman.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index a38814c..8c9b28e 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -115,15 +115,6 @@ Actor::Actor(IllusionsEngine *vm)
 	_pathPointsCount = 0;
 	_pathNode = 0;
 
-#if 0 // TODO
-	_field2 = 0;
-	_field164 = 0;
-	_field18C = 0;
-	_field190 = 0;
-	_field192 = 0;
-	_field198 = 0;
-#endif
-
 }
 
 Actor::~Actor() {
@@ -732,11 +723,7 @@ void Control::startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 cal
 	_actor->_pathFlag50 = false;
 	_actor->_seqCodeValue3 = 0;
 	_actor->_seqCodeValue1 = 0;
-	// TODO _actor->_field_BC = _actor->_position.x;
-	// TODO _actor->_field_BE = _actor->_position.x;
 	_actor->_pathInitialPosFlag = true;
-	// TODO _actor->_field_C0 = destPt.x;
-	// TODO _actor->_field_C2 = destPt.y;
 
 	uint newFacing;
 	if (_vm->calcPointDirection(_actor->_position, destPt, newFacing))
@@ -819,7 +806,6 @@ void Control::updateActorMovement(uint32 deltaTime) {
 
 		if (!_actor->_pathFlag50) {
 
-			// TODO Move to own function
 			FixedPoint16 angle;
 			if (currPt.x == prevPt.x) {
 				if (prevPt.y >= currPt.y)
@@ -831,7 +817,6 @@ void Control::updateActorMovement(uint32 deltaTime) {
 			}
 			_actor->_pathAngle = angle;
 
-			// TODO Move to own function
 			int16 v13 = (fixedTrunc(fixedMul(angle, 0x394BB8)) + 360) % 360; // 0x394BB8 is 180 / pi
 			if (deltaX >= 0)
 				v13 += 180;
@@ -1008,7 +993,6 @@ void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entry
 }
 
 void Control::execSequenceOpcode(OpCall &opCall) {
-	// TODO Clean this up
 	_vm->_controls->_sequenceOpcodes->execOpcode(this, opCall);
 }
 
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 0f76050..046a963 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -244,7 +244,6 @@ public:
 	Common::Point _feetPt;
 	Common::Point _position;
 	Common::Point _subobjectsPos[kSubObjectsCount];
-	// TODO 0000001C - 00000054 unknown
 	void startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId);
 	void execSequenceOpcode(OpCall &opCall);
 };
diff --git a/engines/illusions/bbdou/bbdou_foodctl.h b/engines/illusions/bbdou/bbdou_foodctl.h
index 5a31777..70247bf 100644
--- a/engines/illusions/bbdou/bbdou_foodctl.h
+++ b/engines/illusions/bbdou/bbdou_foodctl.h
@@ -30,7 +30,6 @@ namespace Illusions {
 
 class IllusionsEngine_BBDOU;
 
-// TODO Merge counts?
 const uint kFoodMaxPropertyIdsCount = 15;
 const uint kFoodCount = 16;
 
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 78315c4..7e443e2 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -186,7 +186,6 @@ Common::Error IllusionsEngine_BBDOU::run() {
 	_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
 	_stack = new ScriptStack();
 
-	// TODO Move to own class
 	_resGetCtr = 0;
 	_unpauseControlActorFlag = false;
 	_lastUpdateTime = 0;
@@ -266,7 +265,6 @@ bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
 }
 
 void IllusionsEngine_BBDOU::initInput() {
-	// TODO Check if these are correct...
 	_input->setInputEvent(kEventLeftClick, 0x01)
 		.addMouseButton(MOUSE_LEFT_BUTTON)
 		.addKey(Common::KEYCODE_RETURN);
diff --git a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
index 9dd9c9a..d81cf0d 100644
--- a/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
+++ b/engines/illusions/bbdou/scriptopcodes_bbdou.cpp
@@ -504,7 +504,6 @@ void ScriptOpcodes_BBDOU::opStartMoveActor(ScriptThread *scriptThread, OpCall &o
 	ARG_UINT32(namedPointId);
 	// NOTE Skipped checking for stalled sequence, not sure if needed
 	Control *control = _vm->_dict->getObjectControl(objectId);
-	//if (!control) { opCall._deltaOfs = 0; return; }// TODO CHECKME
 	if (!control) { return; }// TODO CHECKME
 	Common::Point pos = _vm->getNamedPointPosition(namedPointId);
 	control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
diff --git a/engines/illusions/duckman/duckman_specialcode.cpp b/engines/illusions/duckman/duckman_specialcode.cpp
index dd4e689..a7f301f 100644
--- a/engines/illusions/duckman/duckman_specialcode.cpp
+++ b/engines/illusions/duckman/duckman_specialcode.cpp
@@ -66,7 +66,6 @@ typedef Common::Functor1Mem<OpCall&, void, DuckmanSpecialCode> SpecialCodeFuncti
 #define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &DuckmanSpecialCode::func);
 
 void DuckmanSpecialCode::init() {
-	// TODO
 	SPECIAL(0x00160001, spcStartScreenShaker);
 	SPECIAL(0x00160002, spcSetCursorHandMode);
 	SPECIAL(0x00160003, spcResetChinesePuzzle);
diff --git a/engines/illusions/illusions.cpp b/engines/illusions/illusions.cpp
index c6ae20c..912c4f6 100644
--- a/engines/illusions/illusions.cpp
+++ b/engines/illusions/illusions.cpp
@@ -142,7 +142,6 @@ uint32 IllusionsEngine::getElapsedUpdateTime() {
 }
 
 int IllusionsEngine::updateActors(uint flags) {
-	// TODO Move to Controls class
 	uint32 deltaTime = getElapsedUpdateTime();
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
@@ -153,7 +152,6 @@ int IllusionsEngine::updateActors(uint flags) {
 }
 
 int IllusionsEngine::updateSequences(uint flags) {
-	// TODO Move to Controls class
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
 		if (control->_pauseCtr == 0 && control->_actor && control->_actor->_seqCodeIp) {
@@ -171,7 +169,6 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	_camera->update(currTime);
 	updateFader();
 
-	// TODO Move to BackgroundInstanceList class
 	BackgroundInstance *backgroundItem = _backgroundInstances->findActiveBackgroundInstance();
 	if (backgroundItem) {
 		BackgroundResource *bgRes = backgroundItem->_bgRes;
@@ -185,7 +182,6 @@ int IllusionsEngine::updateGraphics(uint flags) {
 		}
 	}
 
-	// TODO Move to Controls class
 	for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
 		Control *control = *it;
 		Actor *actor = control->_actor;
@@ -198,12 +194,6 @@ int IllusionsEngine::updateGraphics(uint flags) {
 					frame->_compressedPixels, actor->_surface);
 				actor->_flags &= ~Illusions::ACTOR_FLAG_2000;
 			}
-			/* Unused
-			if (actor->_flags & 0x4000) {
-				nullsub_1(&actor->drawFlags);
-				actor->flags &= ~0x4000;
-			}
-			*/
 			if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
 				uint32 priority = control->getDrawPriority();
 				_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
@@ -214,7 +204,6 @@ int IllusionsEngine::updateGraphics(uint flags) {
 	}
 
 	if (_screenText->_surface) {
-		// TODO Make nicer
 		uint32 priority = getGameId() == kGameIdDuckman ? getPriorityFromBase(19) : getPriorityFromBase(99);
 		_screen->_drawQueue->insertTextSurface(_screenText->_surface, _screenText->_dimensions,
 			_screenText->_position, priority);
diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 12d8436..1374e44 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -97,7 +97,6 @@ Input::Input() {
 }
 
 void Input::processEvent(Common::Event event) {
-	// TODO
 	switch (event.type) {
 	case Common::EVENT_KEYDOWN:
 		handleKey(event.kbd.keycode, MOUSE_NONE, true);
diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h
index 1a433a2..f583ffc 100644
--- a/engines/illusions/resources/fontresource.h
+++ b/engines/illusions/resources/fontresource.h
@@ -40,8 +40,6 @@ protected:
 	IllusionsEngine *_vm;
 };
 
-// TODO
-
 struct CharInfo {
 	int16 _width;
 	int16 _field_2;
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index 56e5f2d..6f61ee6 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -83,8 +83,6 @@ public:
 	virtual bool isFlag(int flag) = 0;
 };
 
-// TODO Possibly split resource loaders from the system?
-
 class ResourceSystem {
 public:
 	ResourceSystem(IllusionsEngine *vm);
diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp
index fd2ef51..f428114 100644
--- a/engines/illusions/screentext.cpp
+++ b/engines/illusions/screentext.cpp
@@ -60,7 +60,6 @@ void ScreenText::updateTextInfoPosition(Common::Point position) {
 }
 
 void ScreenText::clipTextInfoPosition(Common::Point &position) {
-	// TODO Move values outside
 	if (_vm->getGameId() == kGameIdBBDOU) {
 		// BBDOU
 		if (position.x < 2)
diff --git a/engines/illusions/thread.cpp b/engines/illusions/thread.cpp
index afc3a6d..084b28d 100644
--- a/engines/illusions/thread.cpp
+++ b/engines/illusions/thread.cpp
@@ -137,12 +137,10 @@ ThreadList::ThreadList(IllusionsEngine *vm)
 }
 
 void ThreadList::startThread(Thread *thread) {
-	// TODO sceneId has to be set by the Thread class
 	_threads.push_back(thread);
 }
 
 void ThreadList::updateThreads() {
-	// TODO Move outer loop to caller
 	while (1) {
 		Iterator it = _threads.begin();
 		while (it != _threads.end()) {
diff --git a/engines/illusions/threads/talkthread.cpp b/engines/illusions/threads/talkthread.cpp
index 9966308..34abda8 100644
--- a/engines/illusions/threads/talkthread.cpp
+++ b/engines/illusions/threads/talkthread.cpp
@@ -110,10 +110,8 @@ int TalkThread::onUpdate() {
 		_entryText = talkEntry->_text;
 		_entryTblPtr = talkEntry->_tblPtr;
 		if (_sequenceId1) {
-			// TODO _field30 = v6;
 			_pauseCtr = 0;
 		} else {
-			// TODO _field30 = 0;
 			_flags |= 2;
 			_flags |= 1;
 		}
@@ -358,7 +356,6 @@ void TalkThread::onKill() {
 }
 
 uint32 TalkThread::sendMessage(int msgNum, uint32 msgValue) {
-	// TODO
 	switch (msgNum) {
 	case kMsgQueryTalkThreadActive:
 		if (_status != 1 && _status != 2)
@@ -367,7 +364,6 @@ uint32 TalkThread::sendMessage(int msgNum, uint32 msgValue) {
 	case kMsgClearSequenceId1:
 		_sequenceId1 = 0;
 		_flags |= 3;
-		// TODO _field30 = 0;
 		break;
 	case kMsgClearSequenceId2:
 		_sequenceId2 = 0;
diff --git a/engines/illusions/threads/talkthread_duckman.cpp b/engines/illusions/threads/talkthread_duckman.cpp
index 2b1294c..0b67248 100644
--- a/engines/illusions/threads/talkthread_duckman.cpp
+++ b/engines/illusions/threads/talkthread_duckman.cpp
@@ -268,7 +268,6 @@ uint32 TalkThread_Duckman::sendMessage(int msgNum, uint32 msgValue) {
 	case kMsgClearSequenceId1:
 		_sequenceId1 = 0;
 		_flags |= 3;
-		// TODO _pauseCtrPtr = 0;
 		break;
 	case kMsgClearSequenceId2:
 		_sequenceId2 = 0;


Commit: 33ece271fde12033c620660216a678e1c506fd75
    https://github.com/scummvm/scummvm/commit/33ece271fde12033c620660216a678e1c506fd75
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Implement cursor movement via arrow keys

(cherry picked from commit 12e33c6)

Changed paths:
    engines/illusions/input.cpp
    engines/illusions/input.h


diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp
index 1374e44..6af6cd5 100644
--- a/engines/illusions/input.cpp
+++ b/engines/illusions/input.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "illusions/input.h"
+#include "common/system.h"
 
 namespace Illusions {
 
@@ -93,6 +94,7 @@ Input::Input() {
 	_cursorPos.y = 0;
 	_prevCursorPos.x = 0;
 	_prevCursorPos.y = 0;
+	_cursorMovedByKeyboard = false;
 	_cheatCodeIndex = 0;
 }
 
@@ -105,6 +107,7 @@ void Input::processEvent(Common::Event event) {
 		handleKey(event.kbd.keycode, MOUSE_NONE, false);
 		break;
 	case Common::EVENT_MOUSEMOVE:
+		_cursorMovedByKeyboard = false;
 		_cursorPos.x = event.mouse.x;
 		_cursorPos.y = event.mouse.y;
 		break;
@@ -172,6 +175,22 @@ InputEvent& Input::setInputEvent(uint evt, uint bitMask) {
 }
 
 void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
+	switch (key) {
+	case Common::KEYCODE_UP:
+		moveCursorByKeyboard(0, -4);
+		break;
+	case Common::KEYCODE_DOWN:
+		moveCursorByKeyboard(0, 4);
+		break;
+	case Common::KEYCODE_RIGHT:
+		moveCursorByKeyboard(4, 0);
+		break;
+	case Common::KEYCODE_LEFT:
+		moveCursorByKeyboard(-4, 0);
+		break;
+	default:
+		break;
+	}
 	for (uint i = 0; i < kEventMax; ++i) {
 		_newKeys |= _inputEvents[i].handle(key, mouseButton, down);
 	}
@@ -221,6 +240,12 @@ void Input::discardButtons(uint bitMask) {
 	_buttonStates &= ~bitMask;
 }
 
+void Input::moveCursorByKeyboard(int deltaX, int deltaY) {
+	_cursorMovedByKeyboard = true;
+	_cursorPos.x = CLIP(_cursorPos.x + deltaX, 0, g_system->getWidth() - 1);
+	_cursorPos.y = CLIP(_cursorPos.y + deltaY, 0, g_system->getHeight() - 1);
+}
+
 bool Input::isCheatModeActive() {
 	return _cheatCodeIndex == 7;
 }
diff --git a/engines/illusions/input.h b/engines/illusions/input.h
index 3b554bd..b92cd1d 100644
--- a/engines/illusions/input.h
+++ b/engines/illusions/input.h
@@ -90,6 +90,7 @@ public:
 	void setCursorPosition(Common::Point mousePos);
 	Common::Point getCursorDelta();
 	InputEvent& setInputEvent(uint evt, uint bitMask);
+	bool isCursorMovedByKeyboard() const { return _cursorMovedByKeyboard; }
 	bool isCheatModeActive();
 protected:
 	uint _cheatCodeIndex;
@@ -98,12 +99,14 @@ protected:
 	uint _newKeys;
 	Common::Point _cursorPos, _prevCursorPos;
 	InputEvent _inputEvents[kEventMax];
+	bool _cursorMovedByKeyboard;
 	void handleKey(Common::KeyCode key, int mouseButton, bool down);
 	void handleMouseButton(int mouseButton, bool down);
 	void discardButtons(uint bitMask);
 	bool lookButtonStates(uint bitMask);
 	bool lookNewButtons(uint bitMask);
 	void setButtonState(uint bitMask);
+	void moveCursorByKeyboard(int deltaX, int deltaY);
 };
 
 } // End of namespace Illusions


Commit: aee54f4bca73b35371e9bc13f718209b38bfa731
    https://github.com/scummvm/scummvm/commit/aee54f4bca73b35371e9bc13f718209b38bfa731
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Minor cleanup

(cherry picked from commit 9abaa9e)

Changed paths:
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_credits.cpp
    engines/illusions/duckman/illusions_duckman.cpp
    engines/illusions/duckman/scriptopcodes_duckman.cpp


diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 046a963..358ecbd 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -234,7 +234,6 @@ public:
 	int _pauseCtr;
 	int16 _priority;
 	Actor *_actor;
-	//field_6 dw
 	uint32 _sceneId;
 	uint32 _objectId;
 	uint32 _actorTypeId;
diff --git a/engines/illusions/bbdou/bbdou_credits.cpp b/engines/illusions/bbdou/bbdou_credits.cpp
index 08f9900..b373595 100644
--- a/engines/illusions/bbdou/bbdou_credits.cpp
+++ b/engines/illusions/bbdou/bbdou_credits.cpp
@@ -97,7 +97,6 @@ void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint ali
 	uint16 wtext[128];
 	charToWChar(text, wtext, ARRAYSIZE(wtext));
 
-	// TODO Extract to Actor class
 	Control *control = _vm->getObjectControl(objectId);
 	FontResource *font = _vm->_dict->findFont(_currFontId);
 	TextDrawer textDrawer;
diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp
index d22b947..1ffb9f6 100644
--- a/engines/illusions/duckman/illusions_duckman.cpp
+++ b/engines/illusions/duckman/illusions_duckman.cpp
@@ -247,7 +247,6 @@ bool IllusionsEngine_Duckman::hasFeature(EngineFeature f) const {
 }
 
 void IllusionsEngine_Duckman::initInput() {
-	// TODO Check if these are correct...
 	_input->setInputEvent(kEventLeftClick, 0x01)
 		.addMouseButton(MOUSE_LEFT_BUTTON)
 		.addKey(Common::KEYCODE_RETURN);
@@ -288,8 +287,6 @@ void IllusionsEngine_Duckman::initUpdateFunctions() {
 #undef UPDATEFUNCTION
 
 int IllusionsEngine_Duckman::updateScript(uint flags) {
-	// TODO Some more stuff
-
 	if (_screen->isDisplayOn() && !_screenPalette->isFaderActive() && _pauseCtr == 0) {
 		if (_input->pollEvent(kEventAbort)) {
 			startScriptThread(0x00020342, 0);
@@ -297,7 +294,6 @@ int IllusionsEngine_Duckman::updateScript(uint flags) {
 			startScriptThread(0x0002033F, 0);
 		}
 	}
-
 	_threads->updateThreads();
 
 	//TODO need to call startScriptThread2(0x10002, 0x20001, 0);
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 01a9c96..5e4dab4 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -683,9 +683,8 @@ void ScriptOpcodes_Duckman::opQuitGame(ScriptThread *scriptThread, OpCall &opCal
 
 void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->reset();
-	_vm->_input->activateButton(0xFFFF);
-	// TODO _vm->stopMusic();
-	// TODO _vm->_gameStates->clear();
+	_vm->_soundMan->stopMidiMusic();
+	_vm->_soundMan->clearMidiMusicQueue();
 }
 
 void ScriptOpcodes_Duckman::opLoadGame(ScriptThread *scriptThread, OpCall &opCall) {


Commit: 617e9439cfa51cdb7221a388762e0661ccd5f5ad
    https://github.com/scummvm/scummvm/commit/617e9439cfa51cdb7221a388762e0661ccd5f5ad
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: DUCKMAN: Implement MIDI music fading

(cherry picked from commit 056b303)

Changed paths:
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/sound.cpp
    engines/illusions/sound.h


diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index 5e4dab4..ee4979a 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -640,7 +640,7 @@ void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &
 void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_INT16(duration);
 	ARG_INT16(finalVolume);
-	// TODO _vm->fadeMidiMusic(2, finalVolume, duration, opCall._threadId);
+	_vm->_soundMan->fadeMidiMusic(finalVolume, duration, opCall._threadId);
 }
 
 void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index d8b0316..6a62ee5 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -23,6 +23,7 @@
 #include "common/config-manager.h"
 #include "illusions/illusions.h"
 #include "illusions/sound.h"
+#include "illusions/time.h"
 #include "audio/mididrv.h"
 #include "audio/midiparser.h"
 
@@ -384,6 +385,15 @@ void SoundMan::stopMidiMusic() {
 	_midiPlayer->stop();
 }
 
+void SoundMan::fadeMidiMusic(int16 finalVolume, int16 duration, uint32 notifyThreadId) {
+	_midiMusicFader._active = true;
+	_midiMusicFader._notifyThreadId = notifyThreadId;
+	_midiMusicFader._startVolume = _midiMusicFader._currVolume;
+	_midiMusicFader._finalVolume = finalVolume;
+	_midiMusicFader._startTime = getCurrentTime();
+	_midiMusicFader._duration = duration;
+}
+
 void SoundMan::clearMidiMusicQueue() {
 	_midiMusicQueue.clear();
 }
@@ -467,7 +477,26 @@ void SoundMan::updateMidi() {
 		_midiMusicQueue.remove_at(0);
 		_midiPlayer->play(musicId);
 	}
-	// TODO Update music volume fading
+	updateMidiMusicFader();
+}
+
+void SoundMan::updateMidiMusicFader() {
+	if (_midiMusicFader._active) {
+		int16 currTime = getCurrentTime();
+		if (currTime - _midiMusicFader._startTime > _midiMusicFader._duration) {
+			_midiMusicFader._active = false;
+			currTime = _midiMusicFader._startTime + _midiMusicFader._duration;
+			if (_midiMusicFader._notifyThreadId) {
+				_vm->notifyThreadId(_midiMusicFader._notifyThreadId);
+				_midiMusicFader._notifyThreadId = 0;
+			}
+		}
+		const int16 elapsedTime = currTime - _midiMusicFader._startTime;
+		const int16 volumeDelta = _midiMusicFader._finalVolume - _midiMusicFader._startVolume;
+		const int masterMusicVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
+		_midiMusicFader._currVolume = _midiMusicFader._startVolume + (elapsedTime * volumeDelta / _midiMusicFader._duration);
+		_midiPlayer->setVolume(_midiMusicFader._currVolume * masterMusicVolume / 255);
+	}
 }
 
 void SoundMan::setMusicVolume(uint16 volume) {
diff --git a/engines/illusions/sound.h b/engines/illusions/sound.h
index 2669102..027228e 100644
--- a/engines/illusions/sound.h
+++ b/engines/illusions/sound.h
@@ -112,6 +112,18 @@ protected:
 	bool _looping;
 };
 
+struct MidiMusicFader {
+	bool _active;
+	uint _flags;
+	int16 _currVolume;
+	int16 _startVolume;
+	int16 _finalVolume;
+	int16 _startTime;
+	int16 _duration;
+	uint32 _notifyThreadId;
+	MidiMusicFader() : _active(false), _currVolume(255) {}
+};
+
 class SoundMan {
 public:
 	SoundMan(IllusionsEngine *vm);
@@ -123,6 +135,7 @@ public:
 
 	void playMidiMusic(uint32 musicId);
 	void stopMidiMusic();
+	void fadeMidiMusic(int16 finalVolume, int16 duration, uint32 notifyThreadId);
 	void clearMidiMusicQueue();
 
 	uint16 getMusicVolume();
@@ -158,8 +171,10 @@ protected:
 	VoicePlayer *_voicePlayer;
 	SoundList _sounds;
 	Common::Array<uint32> _midiMusicQueue;
+	MidiMusicFader _midiMusicFader;
 	Sound *getSound(uint32 soundEffectId);
 	void updateMidi();
+	void updateMidiMusicFader();
 	uint16 calcAdjustedVolume(const Common::String &volumeConfigKey, uint16 volume);
 };
 


Commit: 565de60234af381862200aef7c57ae3de94bf489
    https://github.com/scummvm/scummvm/commit/565de60234af381862200aef7c57ae3de94bf489
Author: johndoe123 (john_doe at techie.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Clean up/fix some TODOs

(cherry picked from commit 66094e4)

Changed paths:
    engines/illusions/actor.cpp
    engines/illusions/actor.h
    engines/illusions/bbdou/bbdou_cursor.cpp
    engines/illusions/bbdou/bbdou_inventory.cpp
    engines/illusions/bbdou/bbdou_specialcode.cpp
    engines/illusions/bbdou/illusions_bbdou.cpp
    engines/illusions/cursor.cpp
    engines/illusions/cursor.h
    engines/illusions/duckman/illusions_duckman.h
    engines/illusions/duckman/scriptopcodes_duckman.cpp
    engines/illusions/resourcesystem.h
    engines/illusions/screen.cpp
    engines/illusions/sound.cpp
    engines/illusions/textdrawer.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index 8c9b28e..b4ea72e 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -89,7 +89,6 @@ Actor::Actor(IllusionsEngine *vm)
 	}
 	_notifyThreadId1 = 0;
 	_notifyThreadId2 = 0;
-	_surfaceTextFlag = 0;
 	_entryTblPtr = 0;
 	_seqCodeIp = 0;
 	_sequenceId = 0;
@@ -131,13 +130,11 @@ void Actor::unpause() {
 
 void Actor::createSurface(SurfInfo &surfInfo) {
 	_surface = _vm->_screen->allocSurface(surfInfo);
-	if (_frameIndex) {
-		if (_surfaceTextFlag) {
-			/* TODO
-			Font *font = _vm->findFont(_fontId);
-			_surface->fillRect(Common::Rect(surfInfo._dimensions._width, surfInfo._dimensions._height), 0);
-			gfx_sub_40CA70(_surface, font, _field18C, _surfInfo._dimensions, _field198);
-			*/
+	if (_vm->getGameId() == kGameIdDuckman) {
+		if (_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
+			if (_frameIndex) {
+				_flags |= Illusions::ACTOR_FLAG_2000;
+			}
 			_flags |= Illusions::ACTOR_FLAG_4000;
 		}
 		else {
@@ -194,10 +191,10 @@ Control::Control(IllusionsEngine *vm)
 	_pauseCtr = 0;
 	_priority = 0;
 	_objectId = 0;
-	_unkPt.x = 0;
-	_unkPt.y = 0;
-	_pt.x = 0;
-	_pt.y = 0;
+	_bounds._topLeft.x = 0;
+	_bounds._topLeft.y = 0;
+	_bounds._bottomRight.x = 0;
+	_bounds._bottomRight.y = 0;
 	_feetPt.x = 0;
 	_feetPt.y = 0;
 	_position.x = 0;
@@ -212,10 +209,11 @@ Control::~Control() {
 
 void Control::pause() {
 
-	_vm->_dict->setObjectControl(_objectId, 0);
-
-	if (_objectId == Illusions::CURSOR_OBJECT_ID)
-		_vm->setCursorControl(0);
+	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & Illusions::ACTOR_FLAG_SCALED)) {
+		_vm->_dict->setObjectControl(_objectId, 0);
+		if (_objectId == Illusions::CURSOR_OBJECT_ID)
+			_vm->setCursorControl(0);
+	}
 
 	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200))
 		_actor->destroySurface();
@@ -223,11 +221,13 @@ void Control::pause() {
 }
 
 void Control::unpause() {
-	_vm->_dict->setObjectControl(_objectId, this);
-
-	if (_objectId == Illusions::CURSOR_OBJECT_ID)
-		_vm->setCursorControl(this);
 
+	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & Illusions::ACTOR_FLAG_SCALED)) {
+		_vm->_dict->setObjectControl(_objectId, this);
+		if (_objectId == Illusions::CURSOR_OBJECT_ID)
+			_vm->setCursorControl(this);
+	}
+  
 	if (_actor && !(_actor->_flags & Illusions::ACTOR_FLAG_200)) {
 		SurfInfo surfInfo;
 		ActorType *actorType = _vm->_dict->findActorType(_actorTypeId);
@@ -318,11 +318,11 @@ void Control::deactivateObject() {
 }
 
 void Control::readPointsConfig(byte *pointsConfig) {
-	_unkPt.x = READ_LE_UINT16(pointsConfig + 0);
-	_unkPt.y = READ_LE_UINT16(pointsConfig + 2);
+	_bounds._topLeft.x = READ_LE_UINT16(pointsConfig + 0);
+	_bounds._topLeft.y = READ_LE_UINT16(pointsConfig + 2);
 	pointsConfig += 4;
-	_pt.x = READ_LE_UINT16(pointsConfig + 0);
-	_pt.y = READ_LE_UINT16(pointsConfig + 2);
+	_bounds._bottomRight.x = READ_LE_UINT16(pointsConfig + 0);
+	_bounds._bottomRight.y = READ_LE_UINT16(pointsConfig + 2);
 	pointsConfig += 4;
 	_feetPt.x = READ_LE_UINT16(pointsConfig + 0);
 	_feetPt.y = READ_LE_UINT16(pointsConfig + 2);
@@ -493,7 +493,7 @@ void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {
 			-_position.x + _actor->_surfInfo._dimensions._width - 1,
 			-_position.y + _actor->_surfInfo._dimensions._height - 1);
 	} else {
-		collisionRect = Common::Rect(_unkPt.x, _unkPt.y, _pt.x, _pt.y);
+		collisionRect = Common::Rect(_bounds._topLeft.x, _bounds._topLeft.y, _bounds._bottomRight.x, _bounds._bottomRight.y);
 	}
 
 	if (_actor) {
@@ -514,7 +514,7 @@ void Control::getCollisionRectAccurate(Common::Rect &collisionRect) {
 }
 
 void Control::getCollisionRect(Common::Rect &collisionRect) {
-	collisionRect = Common::Rect(_unkPt.x, _unkPt.y, _pt.x, _pt.y);
+	collisionRect = Common::Rect(_bounds._topLeft.x, _bounds._topLeft.y, _bounds._bottomRight.x, _bounds._bottomRight.y);
 	if (_actor) {
 		if (_actor->_scale != 100) {
 			collisionRect.left = collisionRect.left * _actor->_scale / 100;
@@ -769,7 +769,6 @@ PointArray *Control::createPath(Common::Point destPt) {
 
 void Control::updateActorMovement(uint32 deltaTime) {
 	// TODO This needs some cleanup
-	// TODO Move while loop to caller
 
 	static const int16 kAngleTbl[] = {60, 0, 120, 0, 60, 0, 120, 0};
 	bool fastWalked = false;
@@ -1102,10 +1101,10 @@ void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, Wi
 	control->_flags = 0;
 	control->_priority = priority;
 	control->_objectId = objectId;
-	control->_unkPt.x = 0;
-	control->_unkPt.y = 0;
-	control->_pt.y = dimensions._height - 1;
-	control->_pt.x = dimensions._width - 1;
+	control->_bounds._topLeft.x = 0;
+	control->_bounds._topLeft.y = 0;
+	control->_bounds._bottomRight.x = dimensions._width - 1;
+	control->_bounds._bottomRight.y = dimensions._height - 1;
 	control->_feetPt.x = dimensions._width / 2;
 	control->_feetPt.y = dimensions._height / 2;
 	control->_position.x = 0;
@@ -1131,11 +1130,11 @@ void Controls::placeSequenceLessActor(uint32 objectId, Common::Point placePt, Wi
 void Controls::placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags) {
 	Control *control = newControl();
 	control->_flags = flags;
-	control->_unkPt = feetPt;
 	control->_feetPt = feetPt;
 	control->_priority = priority;
 	control->_objectId = objectId;
-	control->_pt = pt;
+	control->_bounds._topLeft = feetPt;
+	control->_bounds._bottomRight = pt;
 	control->_position.x = 0;
 	control->_position.y = 0;
 	control->_actorTypeId = 0;
@@ -1466,10 +1465,10 @@ uint32 Controls::newTempObjectId() {
 
 void Controls::destroyControlInternal(Control *control) {
 
-	if (!(control->_flags & 4) && control->_pauseCtr <= 0)
+	if ((_vm->getGameId() == kGameIdBBDOU || !(control->_flags & 4)) && control->_pauseCtr <= 0)
 		_vm->_dict->setObjectControl(control->_objectId, 0);
 
-	if (!(control->_flags & 4) && control->_objectId == Illusions::CURSOR_OBJECT_ID && control->_pauseCtr <= 0)
+	if ((_vm->getGameId() == kGameIdBBDOU || !(control->_flags & 4)) && control->_objectId == Illusions::CURSOR_OBJECT_ID && control->_pauseCtr <= 0)
 		_vm->setCursorControl(0);
 
 	if (control->_actor) {
@@ -1477,10 +1476,6 @@ void Controls::destroyControlInternal(Control *control) {
 			delete control->_actor->_pathNode;
 		if (!(control->_actor->_flags & Illusions::ACTOR_FLAG_200))
 			control->_actor->destroySurface();
-		/* TODO
-		if (control->_actor->_field2)
-			largeObj_sub_4061E0();
-		*/
 		delete control->_actor;
 		control->_actor = 0;
 	}
diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h
index 358ecbd..b52c4a6 100644
--- a/engines/illusions/actor.h
+++ b/engines/illusions/actor.h
@@ -153,8 +153,6 @@ public:
 	uint32 _notifyThreadId2;
 	byte *_entryTblPtr;
 
-	int _surfaceTextFlag;
-
 	ActorControlRoutine *_controlRoutine;
 
 	uint32 _sequenceId;
@@ -237,9 +235,7 @@ public:
 	uint32 _sceneId;
 	uint32 _objectId;
 	uint32 _actorTypeId;
-	// TODO Move points into own struct
-	Common::Point _unkPt;
-	Common::Point _pt;
+	WRect _bounds;
 	Common::Point _feetPt;
 	Common::Point _position;
 	Common::Point _subobjectsPos[kSubObjectsCount];
diff --git a/engines/illusions/bbdou/bbdou_cursor.cpp b/engines/illusions/bbdou/bbdou_cursor.cpp
index 2d546de..e9046c3 100644
--- a/engines/illusions/bbdou/bbdou_cursor.cpp
+++ b/engines/illusions/bbdou/bbdou_cursor.cpp
@@ -48,7 +48,6 @@ void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
 	_vm->_controls->placeActor(0x50001, pos, 0x6000C, objectId, 0);
 
 	Control *control = _vm->_dict->getObjectControl(objectId);
-	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::actorControlRoutine1));
 	control->_flags |= 8;
 
 	_data._mode = 1;
@@ -100,7 +99,6 @@ void BbdouCursor::disable(uint32 objectId) {
 
 void BbdouCursor::reset(uint32 objectId) {
 	Control *control = _vm->_dict->getObjectControl(objectId);
-
 	_data._verbState._cursorState = 1;
 	_data._mode = 1;
 	_data._mode2 = 0;
@@ -125,10 +123,7 @@ void BbdouCursor::reset(uint32 objectId) {
 	resetActiveVerbs();
 	control->setActorIndexTo1();
 	control->startSequenceActor(0x60029, 2, 0);
-
 	_bbdou->hideVerbBubble(control->_objectId, &_data._verbState);
-	// TODO? control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, BbdouCursor>(this, &BbdouCursor::cursorInteractControlRoutine));
-
 }
 
 void BbdouCursor::addCursorSequenceId(uint32 objectId, uint32 sequenceId) {
diff --git a/engines/illusions/bbdou/bbdou_inventory.cpp b/engines/illusions/bbdou/bbdou_inventory.cpp
index 14c2d76..88184b9 100644
--- a/engines/illusions/bbdou/bbdou_inventory.cpp
+++ b/engines/illusions/bbdou/bbdou_inventory.cpp
@@ -298,7 +298,6 @@ void BbdouInventory::clear() {
 }
 
 void BbdouInventory::cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId) {
-	// TODO
 	uint32 foundSceneId, foundVerbId, foundObjectId2, foundObjectId;
 	bool found = false;
 	InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
diff --git a/engines/illusions/bbdou/bbdou_specialcode.cpp b/engines/illusions/bbdou/bbdou_specialcode.cpp
index 9bdab0c..542b3d8 100644
--- a/engines/illusions/bbdou/bbdou_specialcode.cpp
+++ b/engines/illusions/bbdou/bbdou_specialcode.cpp
@@ -220,8 +220,9 @@ void BbdouSpecialCode::resetBeforeResumeSavegame() {
 	_vm->_threads->terminateThreads(0);
 	_vm->reset();
 	_vm->_input->activateButton(0xFFFF);
-	// TODO _vm->stopMusic();
-	// TODO _vm->_gameStates->clear();
+	_vm->_soundMan->stopMusic();
+	_vm->_scriptResource->_blockCounters.clear();
+	_vm->_scriptResource->_properties.clear();
 	_cursor->reset(0x0004001A);
 	setCursorControlRoutine(0x0004001A, 0);
 	_cursor->enable(0x0004001A);
@@ -934,9 +935,8 @@ void BbdouSpecialCode::cursorCrosshairControlRoutine(Control *cursorControl, uin
 			}
 
 		} else if (_vm->_input->pollEvent(kEventRightClick) && cursorData._verbState._isBubbleVisible && !cursorData._verbState._flag56) {
-			// TODO I don't think this is used; _isBubbleVisible seems to be always 0 here
-			debug("Cursor_sub_10004DD0 TODO");
-			// TODO Cursor_sub_10004DD0(controla->objectId, cursorData->currOverlappedObjectId, cursorData->holdingObjectId, &cursorData->verbState);
+			// I don't think this is used; _isBubbleVisible seems to be always 0 here
+			warning("Cursor function not implemented");
 		}
 
 	} else if (_vm->_input->pollEvent(kEventLeftClick)) {
diff --git a/engines/illusions/bbdou/illusions_bbdou.cpp b/engines/illusions/bbdou/illusions_bbdou.cpp
index 7e443e2..30726ea 100644
--- a/engines/illusions/bbdou/illusions_bbdou.cpp
+++ b/engines/illusions/bbdou/illusions_bbdou.cpp
@@ -659,7 +659,7 @@ bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint
 void IllusionsEngine_BBDOU::reset() {
 	_scriptResource->_blockCounters.clear();
 	_scriptResource->_properties.clear();
-	// TODO script_sub_417FF0(1, 0);
+	setTextDuration(1, 0);
 }
 
 void IllusionsEngine_BBDOU::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
diff --git a/engines/illusions/cursor.cpp b/engines/illusions/cursor.cpp
index da8a83b..0ae21be 100644
--- a/engines/illusions/cursor.cpp
+++ b/engines/illusions/cursor.cpp
@@ -50,8 +50,23 @@ void Cursor::place(Control *control, uint32 sequenceId) {
 	_vm->_input->setCursorPosition(_control->_actor->_position);
 }
 
-void Cursor::setActorIndex(int a2, int a3, int a4) {
-	_control->_actor->_actorIndex = 1;// TODO?!? *((_BYTE *)&stru_42C040[30].y + 2 * ((always0 != 0) + 2 * a2) + a3 + 1);
+void Cursor::setActorIndex(int actorIndex, int a, int b) {
+	static int kCursorMap[13][2][2] = {
+		{{ 1,  2}, { 0,  0}},
+		{{ 3,  4}, { 0,  0}},
+		{{ 5,  6}, {13, 14}},
+		{{ 7,  8}, { 0,  0}},
+		{{ 9, 10}, { 0,  0}},
+		{{11, 12}, { 0,  0}},
+		{{ 1,  2}, { 0,  0}},
+		{{ 0,  0}, { 0,  0}},
+		{{ 0,  0}, { 0,  0}},
+		{{15, 16}, { 0,  0}},
+		{{17, 18}, { 0,  0}},
+		{{19, 20}, { 0,  0}},
+		{{21, 22}, { 0,  0}}
+	};
+	_control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
 }
 
 void Cursor::setControl(Control *control) {
diff --git a/engines/illusions/cursor.h b/engines/illusions/cursor.h
index e528f09..2271beb 100644
--- a/engines/illusions/cursor.h
+++ b/engines/illusions/cursor.h
@@ -32,7 +32,7 @@ class Cursor {
 public:
 	Cursor(IllusionsEngine *vm);
 	void place(Control *control, uint32 sequenceId);
-	void setActorIndex(int a2, int a3, int a4);
+	void setActorIndex(int actorIndex, int a, int b);
 	void setControl(Control *control);
 	void show();
 	void hide();
diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h
index d4583bc..0dfdf3a 100644
--- a/engines/illusions/duckman/illusions_duckman.h
+++ b/engines/illusions/duckman/illusions_duckman.h
@@ -84,7 +84,6 @@ protected:
 	virtual bool hasFeature(EngineFeature f) const;
 public:
 
-	// TODO ActiveScenes _activeScenes;
 	uint32 _prevSceneId;
 	uint32 _theSceneId;
 	uint32 _theThreadId;
diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp
index ee4979a..24e69db 100644
--- a/engines/illusions/duckman/scriptopcodes_duckman.cpp
+++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp
@@ -323,6 +323,7 @@ void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall
 void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
 	_vm->_input->discardAllEvents();
 	if (_vm->_scriptResource->_properties.get(0x000E0027)) {
+		// NOTE This would switch to the debug menu which is not currently supported
 		_vm->startScriptThread2(0x10002, 0x20001, 0);
 		opCall._result = kTSTerminate;
 	} else {
@@ -584,7 +585,7 @@ void ScriptOpcodes_Duckman::opStartCursorHoldingObject(ScriptThread *scriptThrea
 void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
 	ARG_SKIP(2);
 	ARG_UINT32(videoId);
-#if 1 // TODO DEBUG Set to 0 to skip videos
+#if 1 // NOTE DEBUG Set to 0 to skip videos
 	_vm->playVideo(videoId, opCall._threadId);
 #else
 	//DEBUG Resume calling thread, later done by the video player
diff --git a/engines/illusions/resourcesystem.h b/engines/illusions/resourcesystem.h
index 6f61ee6..2cdf04f 100644
--- a/engines/illusions/resourcesystem.h
+++ b/engines/illusions/resourcesystem.h
@@ -90,7 +90,6 @@ public:
 
 	void addResourceLoader(uint32 resTypeId, BaseResourceLoader *resourceLoader);
 
-	// TODO Handle threadId in caller as well as pausing of timer
 	void loadResource(uint32 resId, uint32 sceneId, uint32 threadId);
 	void unloadResourceById(uint32 resId);
 	void unloadResourcesBySceneId(uint32 sceneId);
diff --git a/engines/illusions/screen.cpp b/engines/illusions/screen.cpp
index b6cbd2d..8d87069 100644
--- a/engines/illusions/screen.cpp
+++ b/engines/illusions/screen.cpp
@@ -405,7 +405,12 @@ bool Screen::isDisplayOn() {
 
 void Screen::setDisplayOn(bool isOn) {
 	_displayOn = isOn;
-	// TODO Clear screen when off
+	if (!_displayOn) {
+		// Clear screen when off
+		_backSurface->fillRect(Common::Rect(_backSurface->w, _backSurface->h), 0);
+		g_system->copyRectToScreen((byte*)_backSurface->getBasePtr(0, 0), _backSurface->pitch, 0, 0, _backSurface->w, _backSurface->h);
+		g_system->updateScreen();
+	}
 }
 
 void Screen::setScreenOffset(Common::Point offsPt) {
diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp
index 6a62ee5..bd39469 100644
--- a/engines/illusions/sound.cpp
+++ b/engines/illusions/sound.cpp
@@ -360,7 +360,6 @@ SoundMan::~SoundMan() {
 
 void SoundMan::update() {
 	updateMidi();
-	// TODO voc_testCued();
 	if (_musicNotifyThreadId && !_musicPlayer->isPlaying())
 		_vm->notifyThreadId(_musicNotifyThreadId);
 }
diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp
index 31839e0..f626316 100644
--- a/engines/illusions/textdrawer.cpp
+++ b/engines/illusions/textdrawer.cpp
@@ -195,7 +195,12 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh
 }
 
 bool TextDrawer::textHasChar(uint16 c) {
-	// TODO
+	uint16 *textp = _text;
+	while (*textp != 0) {
+		if (*textp == c)
+			return true;
+		++textp;
+	}
 	return false;
 }
 


Commit: baf8011b07d70a95019e925ecff8e1979899cf5c
    https://github.com/scummvm/scummvm/commit/baf8011b07d70a95019e925ecff8e1979899cf5c
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Fix merge error.

Changed paths:
    engines/illusions/actor.cpp


diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp
index b4ea72e..c052a27 100644
--- a/engines/illusions/actor.cpp
+++ b/engines/illusions/actor.cpp
@@ -137,7 +137,8 @@ void Actor::createSurface(SurfInfo &surfInfo) {
 			}
 			_flags |= Illusions::ACTOR_FLAG_4000;
 		}
-		else {
+	} else {
+		if (_frameIndex) {
 			_flags |= Illusions::ACTOR_FLAG_2000;
 			_flags |= Illusions::ACTOR_FLAG_4000;
 		}
@@ -209,7 +210,7 @@ Control::~Control() {
 
 void Control::pause() {
 
-	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & Illusions::ACTOR_FLAG_SCALED)) {
+	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & 4)) {
 		_vm->_dict->setObjectControl(_objectId, 0);
 		if (_objectId == Illusions::CURSOR_OBJECT_ID)
 			_vm->setCursorControl(0);
@@ -222,7 +223,7 @@ void Control::pause() {
 
 void Control::unpause() {
 
-	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & Illusions::ACTOR_FLAG_SCALED)) {
+	if (_vm->getGameId() == kGameIdBBDOU || !(_flags & 4)) {
 		_vm->_dict->setObjectControl(_objectId, this);
 		if (_objectId == Illusions::CURSOR_OBJECT_ID)
 			_vm->setCursorControl(this);


Commit: 848b5a5ff5c317a92ba26254b94d273f2aa6bf97
    https://github.com/scummvm/scummvm/commit/848b5a5ff5c317a92ba26254b94d273f2aa6bf97
Author: Eric Fry (yuv422 at users.noreply.github.com)
Date: 2018-07-20T06:43:33Z

Commit Message:
ILLUSIONS: Formatting fixes

Changed paths:
    engines/illusions/bbdou/menusystem_bbdou.cpp
    engines/illusions/pathfinder.cpp
    engines/illusions/resources/midiresource.cpp


diff --git a/engines/illusions/bbdou/menusystem_bbdou.cpp b/engines/illusions/bbdou/menusystem_bbdou.cpp
index 9d1b718..ab255be 100644
--- a/engines/illusions/bbdou/menusystem_bbdou.cpp
+++ b/engines/illusions/bbdou/menusystem_bbdou.cpp
@@ -55,13 +55,15 @@ void BBDOUMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuCh
 }
 
 void BBDOUMenuSystem::clearMenus() {
-	for (int i = 0; i < kBBDOULastMenuIndex; ++i)
+	for (int i = 0; i < kBBDOULastMenuIndex; ++i) {
 		_menus[i] = 0;
+	}
 }
 
 void BBDOUMenuSystem::freeMenus() {
-	for (int i = 0; i < kBBDOULastMenuIndex; ++i)
+	for (int i = 0; i < kBBDOULastMenuIndex; ++i) {
 		delete _menus[i];
+	}
 }
 
 BaseMenu *BBDOUMenuSystem::getMenuById(int menuId) {
diff --git a/engines/illusions/pathfinder.cpp b/engines/illusions/pathfinder.cpp
index 2f72356..777cb4d 100644
--- a/engines/illusions/pathfinder.cpp
+++ b/engines/illusions/pathfinder.cpp
@@ -83,7 +83,7 @@ PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point d
 
 void PathFinder::postProcess(Common::Point sourcePt, PointArray *foundPath) {
 	// For each three points A, B and C, removes B if the line between A and C is not blocked
-    for (uint index = 0; index + 2 < foundPath->size(); ++index) {
+	for (uint index = 0; index + 2 < foundPath->size(); ++index) {
 		PathLine line;
 		line.p0 = index == 0 ? sourcePt : (*foundPath)[index - 1];
 		line.p1 = (*foundPath)[index + 1];
@@ -91,7 +91,7 @@ void PathFinder::postProcess(Common::Point sourcePt, PointArray *foundPath) {
 			debug("remove point");
 			foundPath->remove_at(index);
 		}
-    }
+	}
 }
 
 bool PathFinder::isLineBlocked(PathLine &line) {
diff --git a/engines/illusions/resources/midiresource.cpp b/engines/illusions/resources/midiresource.cpp
index 060565b..036755b 100644
--- a/engines/illusions/resources/midiresource.cpp
+++ b/engines/illusions/resources/midiresource.cpp
@@ -69,8 +69,9 @@ void MidiGroupResource::load(byte *data, uint32 dataSize) {
 	debug("_midiMusicCount: %d; midiMusicOffs: %08X", _midiMusicCount, midiMusicOffs);
 	_midiMusic = new MidiMusic[_midiMusicCount];
 	stream.seek(midiMusicOffs);
-	for (uint i = 0; i < _midiMusicCount; ++i)
+	for (uint i = 0; i < _midiMusicCount; ++i) {
 		_midiMusic[i].load(stream);
+	}
 
 }
 





More information about the Scummvm-git-logs mailing list