[Scummvm-cvs-logs] SF.net SVN: scummvm: [29318] scummvm/trunk

cyx at users.sourceforge.net cyx at users.sourceforge.net
Tue Oct 30 00:35:50 CET 2007


Revision: 29318
          http://scummvm.svn.sourceforge.net/scummvm/?rev=29318&view=rev
Author:   cyx
Date:     2007-10-29 16:35:50 -0700 (Mon, 29 Oct 2007)

Log Message:
-----------
added 'igor' engine for the game 'Igor: Objective Uikokahonia'

Modified Paths:
--------------
    scummvm/trunk/base/plugins.cpp
    scummvm/trunk/base/plugins.h
    scummvm/trunk/configure
    scummvm/trunk/engines/engines.mk

Added Paths:
-----------
    scummvm/trunk/engines/igor/
    scummvm/trunk/engines/igor/detection.cpp
    scummvm/trunk/engines/igor/igor.cpp
    scummvm/trunk/engines/igor/igor.h
    scummvm/trunk/engines/igor/menu.cpp
    scummvm/trunk/engines/igor/module.mk
    scummvm/trunk/engines/igor/parts/
    scummvm/trunk/engines/igor/parts/part_04.cpp
    scummvm/trunk/engines/igor/parts/part_05.cpp
    scummvm/trunk/engines/igor/parts/part_06.cpp
    scummvm/trunk/engines/igor/parts/part_12.cpp
    scummvm/trunk/engines/igor/parts/part_13.cpp
    scummvm/trunk/engines/igor/parts/part_14.cpp
    scummvm/trunk/engines/igor/parts/part_15.cpp
    scummvm/trunk/engines/igor/parts/part_16.cpp
    scummvm/trunk/engines/igor/parts/part_17.cpp
    scummvm/trunk/engines/igor/parts/part_18.cpp
    scummvm/trunk/engines/igor/parts/part_19.cpp
    scummvm/trunk/engines/igor/parts/part_21.cpp
    scummvm/trunk/engines/igor/parts/part_22.cpp
    scummvm/trunk/engines/igor/parts/part_23.cpp
    scummvm/trunk/engines/igor/parts/part_24.cpp
    scummvm/trunk/engines/igor/parts/part_25.cpp
    scummvm/trunk/engines/igor/parts/part_26.cpp
    scummvm/trunk/engines/igor/parts/part_27.cpp
    scummvm/trunk/engines/igor/parts/part_28.cpp
    scummvm/trunk/engines/igor/parts/part_30.cpp
    scummvm/trunk/engines/igor/parts/part_31.cpp
    scummvm/trunk/engines/igor/parts/part_33.cpp
    scummvm/trunk/engines/igor/parts/part_36.cpp
    scummvm/trunk/engines/igor/parts/part_37.cpp
    scummvm/trunk/engines/igor/parts/part_75.cpp
    scummvm/trunk/engines/igor/parts/part_85.cpp
    scummvm/trunk/engines/igor/parts/part_90.cpp
    scummvm/trunk/engines/igor/parts/part_95.cpp
    scummvm/trunk/engines/igor/parts/part_main.cpp
    scummvm/trunk/engines/igor/resource_ids.h
    scummvm/trunk/engines/igor/resource_tbl.h
    scummvm/trunk/engines/igor/staticres.cpp

Modified: scummvm/trunk/base/plugins.cpp
===================================================================
--- scummvm/trunk/base/plugins.cpp	2007-10-29 22:03:55 UTC (rev 29317)
+++ scummvm/trunk/base/plugins.cpp	2007-10-29 23:35:50 UTC (rev 29318)
@@ -107,6 +107,9 @@
 		#ifndef DISABLE_GOB
 		LINK_PLUGIN(GOB)
 		#endif
+		#ifndef DISABLE_IGOR
+		LINK_PLUGIN(IGOR)
+		#endif
 		#ifndef DISABLE_KYRA
 		LINK_PLUGIN(KYRA)
 		#endif

Modified: scummvm/trunk/base/plugins.h
===================================================================
--- scummvm/trunk/base/plugins.h	2007-10-29 22:03:55 UTC (rev 29317)
+++ scummvm/trunk/base/plugins.h	2007-10-29 23:35:50 UTC (rev 29318)
@@ -89,7 +89,7 @@
  *      Note: This MUST succeed for every gameID on the list returned by
  *      gameIDList(), but MAY also work for additional gameids (e.g. to support
  *      obsolete targets).
- * - DetectedGameList Engine_##ID##_detectGames(const FSList &fslist)
+ * - GameList Engine_##ID##_detectGames(const FSList &fslist)
  *   -> scans through the given file list (usually the contents of a directory),
  *      and attempts to detects games present in that location.
  * - PluginError Engine_##ID##_create(OSystem *syst, Engine **engine)

Modified: scummvm/trunk/configure
===================================================================
--- scummvm/trunk/configure	2007-10-29 22:03:55 UTC (rev 29317)
+++ scummvm/trunk/configure	2007-10-29 23:35:50 UTC (rev 29318)
@@ -55,6 +55,7 @@
 _build_cruise=no
 _build_drascula=no
 _build_gob=yes
+_build_igor=no
 _build_kyra=yes
 _build_lure=yes
 _build_parallaction=yes
@@ -350,6 +351,7 @@
   --enable-cruise          build the Cruise for a Corpse engine
   --enable-drascula        build the Drascula: The Vampire Strikes Back engine
   --disable-gob            don't build the Gobli*ns engine
+  --enable-igor            build the Igor: Objective Uikokahonia engine
   --disable-kyra           don't build the Legend of Kyrandia engine
   --disable-lure           don't build the Lure of the Temptress engine
   --disable-parallaction   don't build the Parallaction engine
@@ -423,6 +425,7 @@
       --enable-cruise)          _build_cruise=yes ;;
       --enable-drascula)        _build_drascula=yes ;;
       --disable-gob)            _build_gob=no ;;
+      --enable-igor)            _build_igor=yes ;;
       --disable-kyra)           _build_kyra=no ;;
       --disable-lure)           _build_lure=no ;;
       --disable-parallaction)   _build_parallaction=no ;;
@@ -720,6 +723,7 @@
 add_to_config_mk_if_no $_build_cruise       'DISABLE_CRUISE = 1'
 add_to_config_mk_if_no $_build_drascula     'DISABLE_DRASCULA = 1'
 add_to_config_mk_if_no $_build_gob          'DISABLE_GOB = 1'
+add_to_config_mk_if_no $_build_igor         'DISABLE_IGOR = 1'
 add_to_config_mk_if_no $_build_kyra         'DISABLE_KYRA = 1'
 add_to_config_mk_if_no $_build_lure         'DISABLE_LURE = 1'
 add_to_config_mk_if_no $_build_parallaction 'DISABLE_PARALLACTION = 1'
@@ -1375,6 +1379,12 @@
 else
 	echo "disable"
 fi
+echo_n "    Igor: Objective Uikokahonia engine..."
+if test "$_build_igor" = yes ; then
+	echo "enable"
+else
+	echo "disable"
+fi
 echo_n "    Legend of Kyrandia engine..."
 if test "$_build_kyra" = yes ; then
 	echo "enable"

Modified: scummvm/trunk/engines/engines.mk
===================================================================
--- scummvm/trunk/engines/engines.mk	2007-10-29 22:03:55 UTC (rev 29317)
+++ scummvm/trunk/engines/engines.mk	2007-10-29 23:35:50 UTC (rev 29318)
@@ -49,6 +49,12 @@
 MODULES += engines/gob
 endif
 
+ifdef DISABLE_IGOR
+DEFINES += -DDISABLE_IGOR
+else
+MODULES += engines/igor
+endif
+
 ifdef DISABLE_KYRA
 DEFINES += -DDISABLE_KYRA
 else

Added: scummvm/trunk/engines/igor/detection.cpp
===================================================================
--- scummvm/trunk/engines/igor/detection.cpp	                        (rev 0)
+++ scummvm/trunk/engines/igor/detection.cpp	2007-10-29 23:35:50 UTC (rev 29318)
@@ -0,0 +1,92 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/fs.h"
+
+#include "igor/igor.h"
+
+static const PlainGameDescriptor igorGameDescriptor = {
+	"igor", "Igor: Objective Uikokahonia"
+};
+
+static const char *igorDetectFileName = "IGOR.DAT";
+static uint32 igorDetectFileSize = 4086790;
+
+GameList Engine_IGOR_gameIDList() {
+	GameList games;
+	games.push_back(igorGameDescriptor);
+	return games;
+}
+
+GameDescriptor Engine_IGOR_findGameID(const char *gameid) {
+	if (scumm_stricmp(igorGameDescriptor.gameid, gameid) == 0) {
+		return igorGameDescriptor;
+	}
+	return GameDescriptor();
+}
+
+GameList Engine_IGOR_detectGames(const FSList &fslist) {
+	GameList detectedGames;
+	for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+		if (file->isDirectory()) {
+			continue;
+		}
+		if (file->getName().equalsIgnoreCase(igorDetectFileName)) {
+			Common::File f;
+			if (!f.open(*file)) {
+				continue;
+			}
+			const uint32 sig = f.readUint32BE();
+			if (sig == MKID_BE('FBOV') && f.size() == igorDetectFileSize) {
+				GameDescriptor gd(igorGameDescriptor.gameid, igorGameDescriptor.description, Common::EN_ANY, Common::kPlatformPC);
+				gd.updateDesc("Demo");
+				detectedGames.push_back(gd);
+				break;
+			}
+		}
+	}
+	return detectedGames;
+}
+
+PluginError Engine_IGOR_create(OSystem *system, Engine **engine) {
+	FSList fslist;
+	FilesystemNode dir(ConfMan.get("path"));
+	if (!dir.getChildren(fslist, FilesystemNode::kListFilesOnly)) {
+		return kInvalidPathError;
+	}
+	GameList gameList = Engine_IGOR_detectGames(fslist);
+	if (gameList.size() != 1) {
+		return kNoGameDataFoundError;
+	}
+	assert(engine);
+	*engine = new Igor::IgorEngine(system);
+	return kNoError;
+}
+
+REGISTER_PLUGIN(IGOR, "Igor: Objective Uikokahonia", "Igor: Objective Uikokahonia (C) Pendulo Studios");


Property changes on: scummvm/trunk/engines/igor/detection.cpp
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native

Added: scummvm/trunk/engines/igor/igor.cpp
===================================================================
--- scummvm/trunk/engines/igor/igor.cpp	                        (rev 0)
+++ scummvm/trunk/engines/igor/igor.cpp	2007-10-29 23:35:50 UTC (rev 29318)
@@ -0,0 +1,2941 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "common/system.h"
+
+#include "graphics/cursorman.h"
+
+#include "sound/voc.h"
+
+#include "igor/igor.h"
+
+namespace Igor {
+
+IgorEngine::IgorEngine(OSystem *system)
+	: Engine(system) {
+
+	_screenVGA = (uint8 *)malloc(320 * 200);
+	for (int i = 0; i < 4; ++i) {
+		_facingIgorFrames[i] = (uint8 *)malloc(13500);
+	}
+	_screenLayer1 = (uint8 *)malloc(320 * 144);
+	_screenLayer2 = (uint8 *)malloc(320 * 144);
+	_screenTextLayer = (uint8 *)malloc(320 * 144);
+	_screenTempLayer = (uint8 *)malloc(9996);
+	_igorHeadFrames = (uint8 *)malloc(3696);
+	_animFramesBuffer = (uint8 *)malloc(65535);
+	_inventoryPanelBuffer = (uint8 *)malloc(9600 * 2);
+	_inventoryImagesBuffer = (uint8 *)malloc(48000);
+	_verbsPanelBuffer = (uint8 *)malloc(3840);
+
+	Common::addSpecialDebugLevel(kDebugEngine,   "Engine",   "Engine debug level");
+	Common::addSpecialDebugLevel(kDebugResource, "Resource", "Resource debug level");
+	Common::addSpecialDebugLevel(kDebugScreen,   "Screen",   "Screen debug level");
+	Common::addSpecialDebugLevel(kDebugWalk,     "Walk",     "Walk debug level");
+	Common::addSpecialDebugLevel(kDebugGame,     "Game",     "Game debug level");
+}
+
+IgorEngine::~IgorEngine() {
+	Common::clearAllSpecialDebugLevels();
+	free(_screenVGA);
+	for (int i = 0; i < 4; ++i) {
+		free(_facingIgorFrames[i]);
+	}
+	free(_screenLayer1);
+	free(_screenLayer2);
+	free(_screenTextLayer);
+	free(_screenTempLayer);
+	free(_igorHeadFrames);
+	free(_animFramesBuffer);
+	free(_inventoryPanelBuffer);
+	free(_inventoryImagesBuffer);
+	free(_verbsPanelBuffer);
+}
+
+int IgorEngine::init() {
+	_system->beginGFXTransaction();
+		initCommonGFX(false);
+		_system->initSize(320, 200);
+	_system->endGFXTransaction();
+
+	_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+	return 0;
+}
+
+void IgorEngine::restart() {
+	_screenVGAVOffset = 0;
+
+	memset(&_gameState, 0, sizeof(_gameState));
+	_nextTimer = 0;
+	_fastMode = false;
+	_language = kLanguageEnglish;
+
+	memset(_walkData, 0, sizeof(_walkData));
+	_walkCurrentPos = 0;
+	_walkDataLastIndex = _walkDataCurrentIndex = 0;
+	_walkCurrentFrame = 0;
+	_walkDataCurrentPosX = _walkDataCurrentPosY = 0;
+	_walkToObjectPosX = _walkToObjectPosY = 0;
+
+	memset(&_currentAction, 0, sizeof(_currentAction));
+	_currentAction.verb = kVerbWalk;
+	_actionCode = 0;
+	_actionWalkPoint = 0;
+	memset(_inputVars, 0, sizeof(_inputVars));
+	memset(_musicSequenceTable, 0, sizeof(_musicSequenceTable));
+
+	_talkDelay = _talkSpeechCounter = _talkDelayCounter = 0;
+	memset(_dialogueTextsTable, 0, sizeof(_dialogueTextsTable));
+	_dialogueTextsStart = 0;
+	_dialogueTextsCount = 0;
+	_dialogueDirtyRectY = 0;
+	_dialogueDirtyRectSize = 0;
+	memset(_dialogueQuestions, 0, sizeof(_dialogueQuestions));
+	memset(_dialogueReplies, 0, sizeof(_dialogueReplies));
+	_dialogueEnded = false;
+	_dialogueChoiceSelected = 0;
+	memset(_dialogueInfo, 0, sizeof(_dialogueInfo));
+
+	memset(_objectsState, 0, sizeof(_objectsState));
+	memcpy(_inventoryImages, INVENTORY_IMG_INIT, 36);
+	memset(_inventoryInfo, 0, sizeof(_inventoryInfo));
+	memset(_verbPrepositions, 0, sizeof(_verbPrepositions));
+	memset(_globalObjectNames, 0, sizeof(_globalObjectNames));
+	memset(_globalDialogueTexts, 0, sizeof(_globalDialogueTexts));
+	memset(_verbsName, 0, sizeof(_verbsName));
+	memset(_roomObjectNames, 0, sizeof(_roomObjectNames));
+
+	_igorTempFrames = _facingIgorFrames[0] + 10500;
+
+	memset(_roomObjectAreasTable, 0, sizeof(_roomObjectAreasTable));
+	memset(_roomActionsTable, 0, sizeof(_roomActionsTable));
+	_executeMainAction = 0;
+	_executeRoomAction = 0;
+	_previousMusic = 0;
+	memset(_musicSequenceTable, 0, sizeof(_musicSequenceTable));
+	_actionCode = 0;
+	_actionWalkPoint = 0;
+	memset(_inputVars, 0, sizeof(_inputVars));
+	_scrollInventory = false;
+	_roomCursorOn = true;
+	_currentCursor = 0;
+	_dialogueCursorOn = true;
+	_updateDialogue = 0;
+	_updateRoomBackground = 0;
+
+	_gameTicks = 0;
+}
+
+int IgorEngine::go() {
+	restart();
+	setupDefaultPalette();
+	_currentPart = kStartupPart;
+	if (!_ovlFile.open("IGOR.DAT")) {
+		error("Unable to open 'IGOR.DAT'");
+	}
+	if (!_sndFile.open("IGOR.FSD")) {
+		error("Unable to open 'IGOR.FSD'");
+	}
+	loadMainTexts();
+	loadIgorFrames();
+	_gameState.talkMode = kTalkModeTextOnly;
+	_gameState.talkSpeed = 3;
+	_talkSpeechCounter = 5;
+	_eventQuitGame = false;
+	PART_MAIN();
+	_ovlFile.close();
+	_sndFile.close();
+	return 0;
+}
+
+void IgorEngine::waitForTimer(int ticks) {
+	_system->copyRectToScreen(_screenVGA, 320, 0, _screenVGAVOffset, 320, 200 - _screenVGAVOffset);
+	_system->updateScreen();
+	uint32 endTicks = (ticks == -1) ? _nextTimer : _system->getMillis() + ticks * 1000 / kTickDelay;
+	do {
+		Common::Event ev;
+		while (_eventMan->pollEvent(ev)) {
+			switch (ev.type) {
+			case Common::EVENT_QUIT:
+				_currentPart = 255;
+				_eventQuitGame = true;
+				break;
+			case Common::EVENT_KEYDOWN:
+				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+					_inputVars[kInputEscape] = 1;
+				} else if (ev.kbd.keycode == Common::KEYCODE_SPACE) {
+					_inputVars[kInputOptions] = 1;
+				} else if (ev.kbd.keycode == Common::KEYCODE_p) {
+					_inputVars[kInputPause] = 1;
+				}
+				break;
+			case Common::EVENT_MOUSEMOVE:
+				_inputVars[kInputCursorXPos] = ev.mouse.x;
+				_inputVars[kInputCursorYPos] = ev.mouse.y;
+				break;
+			case Common::EVENT_RBUTTONDOWN:
+				_inputVars[kInputSkipDialogue] = 1;
+				break;
+			case Common::EVENT_LBUTTONDOWN:
+				_inputVars[kInputClick] = 1;
+				_inputVars[kInputCursorXPos] = ev.mouse.x;
+				_inputVars[kInputCursorYPos] = ev.mouse.y;
+				break;
+			default:
+				break;
+			}
+		}
+		_system->delayMillis(10);
+		if (_system->getMillis() >= endTicks) {
+			break;
+		}
+	} while (!_fastMode);
+	_nextTimer = _system->getMillis() + 8 * 1000 / kTickDelay;
+	if (ticks != -1) {
+		return;
+	}
+	_gameTicks += 8;
+	if (_gameTicks == 64) {
+		_gameTicks = 0;
+		setCursor(_currentCursor);
+		_currentCursor = (_currentCursor + 1) & 3;
+	}
+}
+
+void IgorEngine::copyArea(uint8 *dst, int dstOffset, int dstPitch, const uint8 *src, int srcPitch, int w, int h, bool transparent) {
+	uint8 *p = dst + dstOffset;
+	for (int y = 0; y < h; ++y) {
+		if (transparent) {
+			for (int x = 0; x < w; ++x) {
+				if (src[x] != 0) {
+					p[x] = src[x];
+				}
+			}
+		} else {
+			memcpy(p, src, w);
+		}
+		p += dstPitch;
+		src += srcPitch;
+	}
+}
+
+int IgorEngine::getRandomNumber(int m) {
+	assert(m != 0);
+	return rand() % m;
+}
+
+void IgorEngine::playMusic(int num) {
+	debug(kDebugEngine, "playMusic() %d", num);
+	const int *seq = 0;
+	switch (num) {
+	case 2: {
+			static const int cmf[] = { CMF_2_1, CMF_2_2, CMF_2_3, CMF_2_4, 0 };
+			seq = cmf;
+		}
+		break;
+	case 3: {
+			static const int cmf[] = { CMF_3, 0 };
+			seq = cmf;
+		}
+		break;
+	case 4: {
+			static const int cmf[] = { CMF_4, 0 };
+			seq = cmf;
+		}
+		break;
+	case 7: {
+			static const int cmf[] = { CMF_7_1, CMF_7_2, CMF_7_3, CMF_7_4, 0 };
+			seq = cmf;
+		}
+		break;
+	case 8: {
+			static const int cmf[] = { CMF_8, 0 };
+			seq = cmf;
+		}
+		break;
+	case 9: {
+			static const int cmf[] = { CMF_9, 0 };
+			seq = cmf;
+		}
+		break;
+	case 10: {
+			static const int cmf[] = { CMF_10, 0 };
+			seq = cmf;
+		}
+		break;
+	case 11: {
+			static const int cmf[] = { CMF_11, 0 };
+			seq = cmf;
+		}
+		break;
+	case 12: {
+			static const int cmf[] = { CMF_12, 0 };
+			seq = cmf;
+		}
+		break;
+	}
+	if (seq) {
+		for (int i = 0; i < 5; ++i) {
+			free(_musicSequenceTable[i]);
+		}
+		for (int i = 0; seq[i]; ++i) {
+			_musicSequenceTable[i] = loadData(seq[i]);
+		}
+	}
+	_gameState.musicNum = num;
+	_gameState.musicSequenceIndex = 1;
+}
+
+void IgorEngine::updateMusic() {
+	if (_gameState.jumpToNextMusic) {
+		if (_gameState.musicNum == 2) {
+			_gameState.musicSequenceIndex = getRandomNumber(4) + 1;
+//			_timerHandler0x1CCounter = 5;
+		} else if (_gameState.musicNum == 3 || _gameState.musicNum == 4 || _gameState.musicNum == 8 || _gameState.musicNum == 9 || _gameState.musicNum == 10) {
+//			_timerHandler0x1CCounter = 50;
+		} else if (_gameState.musicNum == 7) {
+			if (_gameState.musicSequenceIndex == 4) {
+				_gameState.musicSequenceIndex = 1;
+			} else {
+				++_gameState.musicSequenceIndex;
+			}
+//			_timerHandler0x1CCounter = 5;
+		} else if (_gameState.musicNum == 11) {
+//			_timerHandler0x1CCounter = 5;
+		}
+	}
+}
+
+void IgorEngine::playSound(int num, int fl) {
+	if (fl && _mixer->isSoundHandleActive(_sfxHandle)) {
+		return;
+	}
+	if (!fl) {
+//		num = 20; // "Speech not recorded"
+		return;
+	}
+	--num;
+	_sndFile.seek(_fdsOffsetsTable[num]);
+	Audio::AudioStream *stream = Audio::makeVOCStream(_sndFile);
+	if (stream) {
+		_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, stream);
+	}
+}
+
+void IgorEngine::stopSound() {
+	_mixer->stopHandle(_sfxHandle);
+}
+
+void IgorEngine::loadIgorFrames() {
+	loadData(FRM_IgorDirBack, _facingIgorFrames[0]);
+	loadData(FRM_IgorDirRight, _facingIgorFrames[1]);
+	loadData(FRM_IgorDirFront, _facingIgorFrames[2]);
+	loadData(FRM_IgorDirLeft, _facingIgorFrames[3]);
+	loadData(FRM_IgorHead, _igorHeadFrames);
+	memcpy(_igorPalette, PAL_IGOR_1, 48);
+}
+
+void IgorEngine::fixDialogueTextPosition(int num, int count, int *x, int *y) {
+	int textLineWidth = 0;
+	for (int i = 0; i < count; ++i) {
+		int w = getStringWidth(_globalDialogueTexts[num + i]);
+		if (w > textLineWidth) {
+			textLineWidth = w;
+		}
+	}
+	int textX = *x;
+	textX += textLineWidth / 2 - 1;
+	if (textX > 318) {
+		textX = 317 - textLineWidth;
+	} else {
+		textX -= textLineWidth;
+	}
+	if (textX < 1) {
+		textX = textLineWidth / 2 + 1;
+	} else {
+		textX += textLineWidth / 2;
+	}
+	*x = textX;
+
+	int textY = *y;
+	textY -= count * 10;
+	if (textY < 1) {
+		textY = 1;
+	}
+	*y = textY;
+}
+
+void IgorEngine::startCutsceneDialogue(int x, int y, int r, int g, int b) {
+	debug(kDebugEngine, "startCutsceneDialogue() pos %d,%d color %d,%d,%d", x, y, r, g, b);
+	--_dialogueTextsCount;
+	int talkX = x;
+	int talkY = y;
+	const DialogueText *dt = &_dialogueTextsTable[_dialogueTextsStart];
+	fixDialogueTextPosition(dt->num, dt->count, &talkX, &talkY);
+	_dialogueDirtyRectY = talkY * 320;
+	_dialogueDirtyRectSize = dt->count * 11 * 320;
+	assert(_dialogueDirtyRectSize < 320 * 72);
+	memcpy(_screenTextLayer, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+	memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+	for (int i = 0; i < dt->count; ++i) {
+		const char *textLine = _globalDialogueTexts[dt->num + i];
+		int textLineWidth = getStringWidth(textLine);
+		int textX = talkX - textLineWidth / 2;
+		int textY = i * 10;
+		drawString(_screenTextLayer, textLine, textX, textY, kTalkColor, kTalkShadowColor, kTalkShadowColor);
+	}
+	setPaletteColor(kTalkColor, r, g, b);
+	setPaletteColor(kTalkShadowColor, 0, 0, 0);
+	if (_gameState.talkMode != kTalkModeSpeechOnly) {
+		memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer, _dialogueDirtyRectSize);
+	}
+	if (_gameState.talkMode == kTalkModeTextOnly) {
+		_talkDelay = (2 * dt->count) * _talkDelays[_gameState.talkSpeed];
+		_talkDelayCounter = 0;
+	} else {
+		_talkDelay = -1;
+		_talkDelayCounter = 0;
+	}
+	if (_gameState.talkMode == kTalkModeTextOnly) {
+		playSound(24, 0);
+	}
+	_gameState.dialogueTextRunning = true;
+	_inputVars[kInputSkipDialogue] = 0;
+}
+
+void IgorEngine::waitForEndOfCutsceneDialogue(int x, int y, int r, int g, int b) {
+	do {
+		if (_gameState.dialogueTextRunning && _inputVars[kInputSkipDialogue]) {
+			_talkDelayCounter = _talkDelay;
+			_inputVars[kInputSkipDialogue] = 0;
+		}
+		if (compareGameTick(19, 32) && _gameState.dialogueTextRunning) {
+			if (_talkSpeechCounter > 2) {
+				if (_gameState.talkMode < 2) {
+					_talkDelayCounter = _talkDelay;
+				}
+				if (_talkDelay == _talkDelayCounter) {
+					if (_updateDialogue) {
+						(this->*_updateDialogue)(kUpdateDialogueAnimEndOfSentence);
+					}
+					memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer + 23040, _dialogueDirtyRectSize);
+					if (_dialogueTextsCount == 0) {
+						_gameState.dialogueTextRunning = 0;
+					} else {
+						++_dialogueTextsStart;
+						if (_gameState.talkMode < 2) {
+							if (_talkSpeechCounter != -1) {
+								_talkSpeechCounter = 0;
+							} else {
+								_talkSpeechCounter = 5;
+								startCutsceneDialogue(x, y, r, g, b);
+							}
+						} else {
+							startCutsceneDialogue(x, y, r, g, b);
+						}
+					}
+				} else {
+					++_talkDelayCounter;
+					if (_updateDialogue) {
+						(this->*_updateDialogue)(kUpdateDialogueAnimMiddleOfSentence);
+					}
+				}
+			} else {
+				if (_talkSpeechCounter == 2) {
+					startCutsceneDialogue(x, y, r, g, b);
+				}
+				++_talkSpeechCounter;
+			}
+		}
+		if (_updateRoomBackground) {
+			(this->*_updateRoomBackground)();
+		}
+		if (_inputVars[kInputEscape]) return;
+		waitForTimer();
+	} while (_gameState.dialogueTextRunning);
+}
+
+void IgorEngine::fixIgorDialogueTextPosition(int num, int count, int *x, int *y) {
+	WalkData *wd = &_walkData[_walkDataLastIndex - 1];
+	*x = wd->x;
+	*y = wd->y - wd->scaleWidth - 3;
+	fixDialogueTextPosition(num, count, x, y);
+}
+
+void IgorEngine::startIgorDialogue() {
+	debug(kDebugEngine, "startIgorDialogue()");
+//	if (_dialogueCursorOn) {
+//		clearCursor();
+//	}
+	--_dialogueTextsCount;
+	int talkX, talkY;
+	const DialogueText *dt = &_dialogueTextsTable[_dialogueTextsStart];
+	fixIgorDialogueTextPosition(dt->num, dt->count, &talkX, &talkY);
+	_dialogueDirtyRectY = talkY * 320;
+	_dialogueDirtyRectSize = dt->count * 11 * 320;
+	assert(_dialogueDirtyRectSize < 320 * 72);
+	memcpy(_screenTextLayer, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+
+	if (_currentPart == 690) {
+		memcpy(_screenTextLayer + 320 * 72, _screenLayer1 + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+		if (_currentAction.verb == kVerbLook && _currentAction.object1Num == 15) {
+			memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+		}
+		if (_currentAction.verb == kVerbLook && _currentAction.object1Num == 19) {
+			memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+		}
+	} else {
+		memcpy(_screenTextLayer + 320 * 72, _screenVGA + _dialogueDirtyRectY, _dialogueDirtyRectSize);
+	}
+	for (int i = 0; i < dt->count; ++i) {
+		const char *textLine = _globalDialogueTexts[dt->num + i];
+		int textLineWidth = getStringWidth(textLine);
+		int textX = talkX - textLineWidth / 2;
+		int textY = i * 10;
+		drawString(_screenTextLayer, textLine, textX, textY, kTalkColor, kTalkShadowColor, kTalkShadowColor);
+	}
+	setPaletteColor(kTalkColor, _dialogueColor[0], _dialogueColor[1], _dialogueColor[2]);
+	setPaletteColor(kTalkShadowColor, 0, 0, 0);
+	if (_gameState.talkMode > 0) {
+		memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer, _dialogueDirtyRectSize);
+	}
+	if (_gameState.talkMode == 2) {
+		_talkDelay = (2 * dt->count) * _talkDelays[_gameState.talkSpeed];
+		_talkDelayCounter = 0;
+	} else {
+		_talkDelay = -1;
+		_talkDelayCounter = 0;
+	}
+	if (_gameState.talkMode >= 2) {
+		playSound(24, 0);
+	}
+	_gameState.dialogueTextRunning = true;
+	_inputVars[kInputSkipDialogue] = 0;
+}
+
+void IgorEngine::waitForEndOfIgorDialogue() {
+	do {
+		if (_gameState.dialogueTextRunning && _inputVars[kInputSkipDialogue]) {
+			_talkDelayCounter = _talkDelay;
+			_inputVars[kInputSkipDialogue] = 0;
+		}
+		if (compareGameTick(19, 32) && _gameState.dialogueTextRunning) {
+			if (_talkSpeechCounter > 2) {
+				if (_gameState.talkMode < 2) {
+					_talkDelayCounter = _talkDelay;
+				}
+				if (_talkDelay == _talkDelayCounter) {
+					animateIgorTalking(0);
+					memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer + 23040, _dialogueDirtyRectSize);
+					if (_dialogueTextsCount == 0) {
+						_gameState.dialogueTextRunning = 0;
+					} else {
+						++_dialogueTextsStart;
+						if (_gameState.talkMode < 2) {
+							if (_talkSpeechCounter != -1) {
+								_talkSpeechCounter = 0;
+							} else {
+								_talkSpeechCounter = 5;
+								startIgorDialogue();
+							}
+						} else {
+							startIgorDialogue();
+						}
+					}
+				} else {
+					animateIgorTalking(getRandomNumber(6));
+					++_talkDelayCounter;
+				}
+			} else {
+				if (_talkSpeechCounter == 2) {
+					startIgorDialogue();
+				}
+				++_talkSpeechCounter;
+			}
+		}
+		if (_updateRoomBackground) {
+			(this->*_updateRoomBackground)();
+		}
+		if (_inputVars[kInputEscape]) return;
+		waitForTimer();
+	} while (_gameState.dialogueTextRunning);
+}
+
+int IgorEngine::getObjectFromInventory(int x) const {
+	if (x >= 20 && x <= 299) {
+		int i = (x - 20) / 40 + _inventoryInfo[72];
+		if (i <= _inventoryInfo[73]) {
+			return _inventoryInfo[i - 1];
+		}
+	}
+	return 0;
+}
+
+int IgorEngine::getSelectedVerb() const {
+	for (int i = 0; i <= 6; ++i) {
+		for (int w = 0; w <= 19; ++w) {
+			const uint8 *p = _screenVGA + 320 * 156 + i * 46 + w;
+			if (*p == 251) {
+				return i + 2;
+			}
+		}
+	}
+	return 0;
+}
+
+const ResourceEntry *IgorEngine::findData(int id) const {
+	--id;
+	assert(id >= 0 && id < _resourceEntriesCount);
+	const ResourceEntry *re = &_resourceEntriesTable[id];
+	assert(re->id == id + 1);
+	return re;
+}
+
+uint8 *IgorEngine::loadData(int id, uint8 *dst, int *size) {
+	const ResourceEntry *re = findData(id);
+	assert(re);
+	debug(kDebugResource, "loadData() id %d offset %d size %d", id, re->offs, re->size);
+	if (!dst) {
+		dst = (uint8 *)malloc(re->size);
+		if (!dst) {
+			error("Unable to allocate %d bytes", re->size);
+		}
+	}
+	_ovlFile.seek(re->offs);
+	_ovlFile.read(dst, re->size);
+	if (size) {
+		*size = re->size;
+	}
+	return dst;
+}
+
+static void decodeRoomString(const uint8 *src, char *dst, int sz) {
+	for (int i = 0; i < sz; ++i) {
+		uint8 code = *src++;
+		if ((code >= 0xAE && code <= 0xC7) || (code >= 0xCE && code <= 0xE7)) {
+			code -= 0x6D;
+		} else if (code > 0xE7) {
+			switch (code) {
+			case 0xE8:
+				code = 0xA0;
+				break;
+			case 0xE9:
+				code = 0x82;
+				break;
+			case 0xEA:
+				code = 0xA1;
+				break;
+			case 0xEB:
+				code = 0xA2;
+				break;
+			case 0xEC:
+				code = 0xA3;
+				break;
+			case 0xED:
+				code = 0xA4;
+				break;
+			case 0xEE:
+				code = 0xA5;
+				break;
+			}
+		}
+		*dst++ = (char)code;
+	}
+}
+
+void IgorEngine::decodeRoomStrings(const uint8 *p, bool skipObjectNames) {
+	if (!skipObjectNames) {
+		for (int i = 0; i < 20; ++i) {
+			_roomObjectNames[i][0] = '\0';
+		}
+		uint8 code = *p++;
+		int index = -1;
+		while (code != 0xF6) {
+			if (code == 0xF4) {
+				++index;
+			}
+			int len = *p++;
+			if (len != 0) {
+				assert(index >= 0);
+				decodeRoomString(p, _roomObjectNames[index], len);
+				p += len;
+				_roomObjectNames[index][len] = '\0';
+				debug(kDebugResource, "decodeRoomStrings() _roomObjectNames[%d] '%s'", index, _roomObjectNames[index]);
+			}
+			code = *p++;
+		}
+	}
+	for (int i = 200; i < 250; ++i) {
+		_globalDialogueTexts[i][0] = '\0';
+	}
+	uint8 code = *p++;
+	int index = 200;
+	while (code != 0xF6) {
+		if (code == 0xF4) {
+			++index;
+		}
+		int len = *p++;
+		if (len != 0) {
+			decodeRoomString(p, _globalDialogueTexts[index], len);
+			p += len;
+			_globalDialogueTexts[index][len] = '\0';
+			debug(kDebugResource, "decodeRoomStrings() _globalDialogueTexts[%d] '%s'", index, _globalDialogueTexts[index]);
+		}
+		code = *p++;
+	}
+}
+
+void IgorEngine::decodeRoomText(const uint8 *p) {
+	debug(kDebugResource, "decodeRoomText()");
+	memcpy(_walkXScaleRoom, p, 320); p += 320;
+	memcpy(_walkYScaleRoom, p, 432); p += 432;
+	decodeRoomStrings(p);
+}
+
+void IgorEngine::decodeRoomAreas(const uint8 *p, int count) {
+	for (int i = 0; i < count; ++i) {
+		RoomObjectArea *roa = &_roomObjectAreasTable[i];
+		roa->area = *p++;
+		assert((roa->area & 0x80) == 0);
+		roa->object = *p++;
+		roa->y1Lum = *p++;
+		roa->y2Lum = *p++;
+		roa->deltaLum = *p++;
+	}
+}
+
+void IgorEngine::decodeRoomMask(const uint8 *p) {
+	uint8 *dst = _screenLayer2;
+	int sz = 320 * 144;
+	while (sz != 0) {
+		uint8 b = *p++;
+		int len = READ_LE_UINT16(p); p += 2;
+		if (len > sz) {
+			len = sz;
+		}
+		memset(dst, b, len);
+		dst += len;
+		sz -= len;
+	}
+}
+
+void IgorEngine::loadRoomData(int pal, int img, int box, int msk, int txt) {
+	if (pal != 0) {
+		loadData(pal, _paletteBuffer);
+	}
+	if (img != 0) {
+		loadData(img, _screenLayer1);
+	}
+	if (box != 0) {
+		int sz;
+		uint8 *p = loadData(box, 0, &sz);
+		memset(_roomObjectAreasTable, 0, sizeof(_roomObjectAreasTable));
+		assert((sz % 5) == 0);
+		decodeRoomAreas(p, sz / 5);
+		free(p);
+	}
+	if (msk != 0) {
+		uint8 *p = loadData(msk);
+		decodeRoomMask(p);
+		free(p);
+	}
+	if (txt != 0) {
+		uint8 *p = loadData(txt);
+		decodeRoomText(p);
+		free(p);
+	}
+}
+
+void IgorEngine::loadAnimData(const int *anm, int loadOffset) {
+	if (loadOffset == 0) {
+		memset(_animFramesBuffer, 0, 65535);
+	}
+	while (*anm) {
+		int dataSize;
+		loadData(*anm++, &_animFramesBuffer[loadOffset], &dataSize);
+		loadOffset += dataSize;
+	}
+}
+
+void IgorEngine::loadActionData(int act) {
+	if (act != 0) {
+		assert(findData(act)->size <= 0x2000);
+		loadData(act, _roomActionsTable);
+	}
+}
+
+void IgorEngine::loadDialogueData(int dlg) {
+	uint8 *p = loadData(dlg);
+	int dialogueDataSize = _roomDataOffsets.dlg.questionsOffset + 164 + 41;
+	assert(dialogueDataSize <= 500);
+	memcpy(_gameState.dialogueData, p, dialogueDataSize);
+	assert(_roomDataOffsets.dlg.questionsSize <= MAX_DIALOGUE_QUESTIONS);
+	for (int i = 0; i < _roomDataOffsets.dlg.questionsSize; ++i) {
+		for (int n = 0; n < 2; ++n) {
+			const uint8 *src = p + _roomDataOffsets.dlg.questionsOffset + (i + 1) * 164 + _language * 82 + (n + 1) * 41;
+			int len = *src++;
+			if (len != 0) {
+				decodeRoomString(src, _dialogueQuestions[i][n], len);
+				_dialogueQuestions[i][n][len] = '\0';
+				debug(kDebugResource, "loadDialogueData() _dialogueQuestions[%d][%d] '%s'", i, n, _dialogueQuestions[i][n]);
+			}
+		}
+	}
+	assert(_roomDataOffsets.dlg.repliesSize <= MAX_DIALOGUE_REPLIES);
+	for (int i = 0; i < _roomDataOffsets.dlg.repliesSize; ++i) {
+		const uint8 *src = p + _roomDataOffsets.dlg.repliesOffset + (i + 1) * 102 + _language * 51;
+		int len = *src++;
+		if (len != 0) {
+			decodeRoomString(src, _dialogueReplies[i], len);
+			_dialogueReplies[i][len] = '\0';
+			debug(kDebugResource, "loadDialogueData() _dialogueReplies[%d] '%s'", i, _dialogueReplies[i]);
+		}
+	}
+	free(p);
+}
+
+static void decodeMainString(const uint8 *src, char *dst) {
+	int sz = *src - 0x6D;
+	if (sz != 0) {
+		++src;
+		for (int i = 0; i < sz; ++i) {
+			uint8 code = src[i] - 0x6D;
+			dst[i] = (char)code;
+		}
+	}
+	dst[sz] = '\0';
+}
+
+void IgorEngine::loadMainTexts() {
+	loadData(IMG_VerbsPanel, _verbsPanelBuffer);
+	int dataSize;
+	uint8 *p = loadData(TXT_MainTable, 0, &dataSize);
+	const uint8 *src = &p[0] + _language * 7;
+	for (int i = 0; i < 3; ++i, src += 7 * 2) {
+		decodeMainString(src, _verbPrepositions[i]);
+		debug(kDebugResource, "loadMainTexts() _verbPrepositions[%d] '%s'", i, _verbPrepositions[i]);
+	}
+	src = &p[0x2A] + _language * 31;
+	for (int i = 0; i < 35; ++i, src += 31 * 2) {
+		decodeMainString(src, _globalObjectNames[i]);
+		debug(kDebugResource, "loadMainTexts() _globalObjectNames[%d] '%s'", i, _globalObjectNames[i]);
+	}
+	src = &p[0x8BA] + _language * 51;
+	for (int i = 0; i < 250; ++i, src += 51 * 2) {
+		decodeMainString(src, _globalDialogueTexts[i]);
+		debug(kDebugResource, "loadMainTexts() _globalDialogueTexts[%d] '%s'", i, _globalDialogueTexts[i]);
+	}
+	src = &p[0x6CA4] + _language * 12;
+	for (int i = 0; i < 9; ++i, src += 12 * 2) {
+		decodeMainString(src, _verbsName[i]);
+		debug(kDebugResource, "loadMainTexts() _verbsName[%d] '%s'", i, _verbsName[i]);
+	}
+	free(p);
+}
+
+void IgorEngine::setupDefaultPalette() {
+	memset(_currentPalette, 0, 255 * 3);
+	memset(&_currentPalette[255 * 3], 63, 3);
+	updatePalette(768);
+}
+
+void IgorEngine::updatePalette(int count) {
+	assert(count <= 768);
+	uint8 pal[1024];
+	for (int j = 0, i = 0; i < count; ++i) {
+		pal[j++] = (_currentPalette[i] << 2) | (_currentPalette[i] >> 4);
+		if (((i + 1) % 3) == 0) {
+			pal[j++] = 0;
+		}
+	}
+	_system->setPalette(pal, 0, count / 3);
+}
+
+void IgorEngine::clearPalette() {
+	memset(_currentPalette, 0, 768);
+	updatePalette(768);
+}
+
+void IgorEngine::setPaletteColor(uint8 index, uint8 r, uint8 g, uint8 b) {
+	uint8 pal[4];
+	pal[0] = (r << 2) | (r >> 4);
+	pal[1] = (g << 2) | (g >> 4);
+	pal[2] = (b << 2) | (b >> 4);
+	pal[3] = 0;
+	_system->setPalette(pal, index, 1);
+}
+
+void IgorEngine::setPaletteRange(int startColor, int endColor) {
+	debug(kDebugScreen, "setPaletteRange(%d, %d)", startColor, endColor);
+	assert(endColor - startColor + 1 <= 256);
+	for (int i = startColor; i <= endColor; ++i) {
+		setPaletteColor(i, _currentPalette[3 * i], _currentPalette[3 * i + 1], _currentPalette[3 * i + 2]);
+	}
+}
+
+void IgorEngine::fadeInPalette(int count) {
+	debug(kDebugScreen, "fadeInPalette(%d)", count);
+	_system->copyRectToScreen(_screenVGA, 320, 0, _screenVGAVOffset, 320, 200 - _screenVGAVOffset);
+	int m = 66;
+	do {
+		m -= 3;
+		for (int i = 0; i < count; ++i) {
+			if (_paletteBuffer[i] >= m) {
+				uint8 color = _currentPalette[i] + 3;
+				if (color > _paletteBuffer[i]) {
+					color = _paletteBuffer[i];
+				}
+				_currentPalette[i] = color;
+			}
+		}
+		updatePalette(count);
+		_system->updateScreen();
+		_system->delayMillis(1000 / 60);
+	} while (m > 0);
+}
+
+void IgorEngine::fadeOutPalette(int count) {
+	debug(kDebugScreen, "fadeOutPalette(%d)", count);
+	_system->copyRectToScreen(_screenVGA, 320, 0, _screenVGAVOffset, 320, 200 - _screenVGAVOffset);
+	memcpy(_paletteBuffer, _currentPalette, 768);
+	int m = 0;
+	do {
+		for (int i = 0; i < count; ++i) {
+			if (_paletteBuffer[i] >= m) {
+				uint8 color = _currentPalette[i];
+				if (color >= 3) {
+					color -= 3;
+				} else {
+					color = 0;
+				}
+				_currentPalette[i] = color;
+			}
+		}
+		updatePalette(count);
+		_system->updateScreen();
+		_system->delayMillis(1000 / 60);
+		m += 3;
+	} while (m < 66);
+}
+
+void IgorEngine::scrollPalette(int startColor, int endColor) {
+	debug(kDebugScreen, "scrollPalette(%d, %d)", startColor, endColor);
+	uint8 c[3];
+	memcpy(c, &_currentPalette[startColor * 3], 3);
+	memmove(&_currentPalette[startColor * 3], &_currentPalette[(startColor + 1) * 3], (endColor - startColor) * 3);
+	memcpy(&_currentPalette[endColor * 3], c, 3);
+}
+
+void IgorEngine::drawString(uint8 *dst, const char *s, int x, int y, int color1, int color2, int color3) {
+	int dx = 0;
+	while (*s) {
+		if (*s == ' ') {
+			dx += 5;
+		} else {
+			const int chr = _fontCharIndex[(uint8)*s];
+			const int chrWidth = _fontCharWidth[chr];
+			if (x + chrWidth > 320) {
+				break;
+			}
+			for (int j = 0; j < 11; ++j) {
+				uint8 *p = dst + (j + y) * 320 + x + dx;
+				for (int i = 0; i < 9; ++i) {
+					uint8 code = _fontData[(chr * 11 + j) * 9 + i];
+					if (code == 1) {
+						p[i] = color1;
+					} else if (code == 2 && color2 != -1) {
+						p[i] = color2;
+					} else if (code == 3 && color3 != -1) {
+						p[i] = color3;
+					}
+				}
+			}
+			dx += chrWidth;
+		}
+		++s;
+	}
+}
+
+int IgorEngine::getStringWidth(const char *s) const {
+	int w = 0;
+	while (*s) {
+		if (*s == ' ') {
+			w += 5;
+		} else {
+			w += _fontCharWidth[_fontCharIndex[(uint8)*s]];
+		}
+		++s;
+	}
+	return w;
+}
+
+void IgorEngine::drawActionSentence(const char *sentence, uint8 color) {
+	memset(_screenVGA + 144 * 320, 0, 11 * 320);
+	int w = getStringWidth(sentence);
+	int x = (320 - w) / 2;
+	drawString(_screenVGA, sentence, x, 144, color, 0, 0);
+}
+
+void IgorEngine::formatActionSentence(uint8 color) {
+	char actionSentence[512];
+	if (_currentAction.verb == kVerbWalk && _inputVars[kInputCursorYPos] > 143) {
+		_currentAction.object1Num = 0;
+	}
+	strcpy(actionSentence, _verbsName[_currentAction.verb]);
+	if (_currentAction.object1Num != 0) {
+		if (_currentAction.object1Type == kObjectTypeInventory) {
+			strcat(actionSentence, _globalObjectNames[_currentAction.object1Num]);
+		} else {
+			strcat(actionSentence, _roomObjectNames[_currentAction.object1Num]);
+		}
+		if (_currentAction.verbType != 0) {
+			strcat(actionSentence, _verbPrepositions[_currentAction.verbType]);
+			if (_currentAction.object2Num != 0) {
+				if (_currentAction.object2Type == kObjectTypeInventory) {
+					strcat(actionSentence, _globalObjectNames[_currentAction.object2Num]);
+				} else {
+					strcat(actionSentence, _roomObjectNames[_currentAction.object2Num]);
+				}
+			}
+		}
+	}
+	drawActionSentence(actionSentence, _sentenceColorIndex[color]);
+}
+
+const uint8 *IgorEngine::getAnimFrame(int baseOffset, int tableOffset, int frame) {
+	const uint8 *src = _animFramesBuffer + baseOffset;
+	assert(frame >= 1);
+	int frameOffset = READ_LE_UINT16(src + tableOffset + (frame - 1) * 2);
+	return src + frameOffset - 1;
+}
+
+void IgorEngine::decodeAnimFrame(const uint8 *src, uint8 *dst, bool preserveText) {
+	int y = READ_LE_UINT16(src) * 320; src += 2;
+	int h = READ_LE_UINT16(src); src += 2;
+	while (h--) {
+		int w = *src++;
+		int pos = y;
+		while (w--) {
+			pos += *src++;
+			int len = *src++;
+			if (len & 0x80) {
+				uint8 color = *src++;
+				len = 256 - len;
+				if (preserveText) {
+					for (int i = pos; i < pos + len; ++i) {
+						if (dst[i] != kTalkColor && dst[i] != kTalkShadowColor) {
+							dst[i] = color;
+						}
+					}
+				} else {
+					memset(dst + pos, color, len);
+				}
+				pos += len;
+			} else {
+				if (preserveText) {
+					for (int i = pos; i < pos + len; ++i) {
+						if (dst[i] != kTalkColor && dst[i] != kTalkShadowColor) {
+							dst[i] = src[i - pos];
+						}
+					}
+				} else {
+					memcpy(dst + pos, src, len);
+				}
+				src += len;
+				pos += len;
+			}
+		}
+		y += 320;
+	}
+}
+
+void IgorEngine::copyAnimFrame(int srcOffset, int frame, int frameSize, int w, int h, int dstOffset) {
+	for (int y = 0; y <= h; ++y) {
+		memcpy(_screenLayer1 + y * 320 + dstOffset, _animFramesBuffer + frame * frameSize + y * w + srcOffset, w);
+	}
+}
+
+void IgorEngine::setCursor(int num) {
+	uint8 cursor[11 * 11];
+	memset(cursor, 0, 11 * 11);
+	const uint8 *mask = &_mouseCursorMask[num * 24];
+	for (int i = 0; i < 24; ++i) {
+		if (mask[i]) {
+			const int offset = ((int8)_mouseCursorData[i + 24] + 5) * 11 + ((int8)_mouseCursorData[i] + 5);
+			cursor[offset] = 255;
+		}
+	}
+	CursorMan.replaceCursor(cursor, 11, 11, 5, 5, 0);
+}
+
+void IgorEngine::showCursor() {
+	debug(kDebugEngine, "showCursor()");
+	_roomCursorOn = true;
+	CursorMan.showMouse(_roomCursorOn);
+}
+
+void IgorEngine::hideCursor() {
+	debug(kDebugEngine, "hideCursor()");
+	_roomCursorOn = false;
+	CursorMan.showMouse(_roomCursorOn);
+}
+
+void IgorEngine::updateRoomLight(int fl) {
+	WalkData *wd = &_walkData[_walkDataLastIndex - 1];
+	if (wd->scaleHeight != 50 || _gameState.dialogueTextRunning) {
+		return;
+	}
+	int offset = 320 * (wd->y + 1 - wd->scaleWidth);
+	int x = wd->x - _walkWidthScaleTable[wd->scaleHeight - 1] / 2;
+	if (x <= 0) {
+		return;
+	}
+	offset += x;
+	int color = (fl == 0) ? 196 : 195;
+	switch (wd->posNum) {
+	case 2: {
+			RoomObjectArea *roa = &_roomObjectAreasTable[_screenLayer2[offset + 1298]];
+			if (wd->y > roa->y1Lum) {
+				 if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) {
+				 	color -= roa->deltaLum;
+				 }
+				 _screenVGA[offset + 1298] = color;
+			}
+		}
+		break;
+	case 3: {
+			RoomObjectArea *roa = &_roomObjectAreasTable[_screenLayer2[offset + 1293]];
+			if (wd->y > roa->y1Lum) {
+				 if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) {
+				 	color -= roa->deltaLum;
+				 }
+				 _screenVGA[offset + 1293] = color;
+			}
+			color = (fl == 0) ? 196 : 195;
+			roa = &_roomObjectAreasTable[_screenLayer2[offset + 1296]];
+			if (wd->y > roa->y1Lum) {
+				 if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) {
+				 	color -= roa->deltaLum;
+				 }
+				 _screenVGA[offset + 1296] = color;
+			}
+		}
+		break;
+	case 4: {
+			RoomObjectArea *roa = &_roomObjectAreasTable[_screenLayer2[offset + 1291]];
+			if (wd->y > roa->y1Lum) {
+				 if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) {
+				 	color -= roa->deltaLum;
+				 }
+				 _screenVGA[offset + 1291] = color;
+			}
+		}
+		break;
+	}
+}
+
+void IgorEngine::drawVerbsPanel() {
+	memcpy(_screenVGA + 320 * 156, _verbsPanelBuffer, 320 * 12);
+}
+
+void IgorEngine::redrawVerb(uint8 verb, bool highlight) {
+	uint8 verbBitmap[44 * 12];
+	if (verb >= 2 && verb <= 8) {
+		verb -= 2;
+		for (int i = 0; i <= 11; ++i) {
+			for (int j = 0; j <= 43; ++j) {
+				uint8 color = _verbsPanelBuffer[i * 320 + verb * 46 + j];
+				if (highlight && color != 0) {
+					color += 8;
+				}
+				verbBitmap[i * 44 + j] = color;
+			}
+		}
+		for (int i = 0; i <= 11; ++i) {
+			memcpy(_screenVGA + 320 * 156 + verb * 46 + i * 320, verbBitmap + i * 44, 44);
+		}
+	}
+}
+
+void IgorEngine::drawInventory(int start, int mode) {
+	loadData(IMG_InventoryPanel, _inventoryPanelBuffer);
+	loadData(IMG_Objects, _inventoryImagesBuffer);
+	int end = start + 6;
+	if (start <= end) {
+		int x = 1;
+		for (int y = start; y != end; ++y) {
+			if (_inventoryInfo[y - 1] == 0) {
+				for (int i = 1; i <= 30; ++i) {
+					memset(_inventoryPanelBuffer + x * 40 - 20 + (i - 1) * 320, 0, 40);
+				}
+			} else {
+				for (int i = 1; i <= 30; ++i) {
+					int img = _inventoryInfo[y - 1];
+					assert(img >= 1);
+					memcpy(_inventoryPanelBuffer + x * 40 - 20 + i * 320 - 321, _inventoryImagesBuffer + (i - 1) * 40 + (_inventoryImages[img - 1] - 1) * 1200, 40);
+				}
+			}
+			++x;
+		}
+	}
+	if (_inventoryInfo[72] == 1) {
+		// 'hide' scroll up
+		for (int y = 5; y <= 11; ++y) {
+			for (int x = 4; x <= 12; ++x) {
+				uint8 *p = _inventoryPanelBuffer + y * 320 + x - 321;
+				if (*p == 0xF2) {
+					*p = 0xF3;
+					p = _inventoryPanelBuffer + y * 320 + x + 305 - 321;
+					*p = 0xF3;
+				}
+			}
+		}
+	}
+	if (_inventoryInfo[73] <= _inventoryInfo[72] + 6 || _inventoryInfo[72] >= _inventoryInfo[73] - 6) {
+		// 'hide' scroll down
+		for (int y = 19; y <= 25; ++y) {
+			for (int x = 4; x <= 12; ++x) {
+				uint8 *p = _inventoryPanelBuffer + y * 320 + x - 321;
+				if (*p == 0xF2) {
+					*p = 0xF3;
+					p = _inventoryPanelBuffer + y * 320 + x + 305 - 321;
+					*p = 0xF3;
+				}
+			}
+		}
+	}
+	switch (mode) {
+	case 0:
+		memcpy(_screenVGA + 54400, _inventoryPanelBuffer, 9600);
+		_scrollInventory = false;
+		break;
+	case 1:
+		for (int y = 0;	y <= 11; ++y) {
+			for (int x = 0; x <= 14; ++x) {
+				uint8 *p = _screenVGA + x + y * 320 + 59520;
+				if ((*p & 0x80) != 0) {
+					*p += 8;
+					p = _screenVGA + x + y * 320 + 59825;
+					*p += 8;
+				}
+			}
+		}
+		memmove(_inventoryPanelBuffer + 9600, _inventoryPanelBuffer, 9600);
+		memcpy(_inventoryPanelBuffer, _screenVGA + 54400, 9600);
+		_scrollInventoryStartY = 7;
+		_scrollInventoryEndY = 31;
+		_scrollInventoryDy = 6;
+		_scrollInventory = true;
+		break;
+	case 2:
+		for (int y = 0;	y <= 11; ++y) {
+			for (int x = 0; x <= 14; ++x) {
+				uint8 *p = _screenVGA + x + y * 320 + 55040;
+				if ((*p & 0x80) != 0) {
+					*p += 8;
+					p = _screenVGA + x + y * 320 + 55345;
+					*p += 8;
+				}
+			}
+		}
+		memmove(_inventoryPanelBuffer + 9600, _inventoryPanelBuffer, 9600);
+		memcpy(_inventoryPanelBuffer + 9600, _screenVGA + 54400, 9600);
+		_scrollInventoryStartY = 25;
+		_scrollInventoryEndY = 1;
+		_scrollInventoryDy = -6;
+		_scrollInventory = true;
+		break;
+	}
+}
+
+void IgorEngine::packInventory() {
+	for (int i = 1; i <= _inventoryInfo[73]; ++i) {
+		if (_inventoryImages[i - 1] != 0) {
+			continue;
+		}
+		int count = _inventoryInfo[73] - 1;
+		for (int index = i; index <= count; ++index) {
+			_inventoryImages[index - 1] = _inventoryImages[index];
+			_inventoryImages[_inventoryImages[index - 1] - 1] = index;
+		}
+		_inventoryImages[_inventoryInfo[73] - 1] = 0;
+		--_inventoryInfo[73];
+	}
+}
+
+void IgorEngine::scrollInventory() {
+	if (_scrollInventoryStartY == _scrollInventoryEndY) {
+		memcpy(_screenVGA + 54400, _inventoryPanelBuffer + (_scrollInventoryStartY - 1) * 320, 9600);
+		_scrollInventory = false;
+	} else {
+		_gameState.counter[2] = 54420;
+		for (_gameState.counter[1] = _scrollInventoryStartY; _gameState.counter[1] < _scrollInventoryStartY + 29; ++_gameState.counter[1]) {
+			memcpy(_screenVGA + _gameState.counter[2], _inventoryPanelBuffer + 320 * _gameState.counter[1] - 300, 280);
+			_gameState.counter[2] += 320;
+		}
+		_scrollInventoryStartY += _scrollInventoryDy;
+	}
+}
+
+void IgorEngine::addObjectToInventory(int object, int index) {
+	++_inventoryInfo[73];
+	_inventoryInfo[_inventoryInfo[73] - 1] = object;
+	_inventoryInfo[index] = _inventoryInfo[73];
+	_inventoryInfo[72] = _inventoryOffsetTable[(_inventoryInfo[73] - 1) / 7];
+	drawInventory(_inventoryInfo[72], 0);
+	playSound(51, 1);
+}
+
+void IgorEngine::removeObjectFromInventory(int index) {
+	_inventoryInfo[_inventoryInfo[index] - 1] = 0;
+	_inventoryInfo[index] = 0;
+	packInventory();
+	if (_inventoryInfo[72] > _inventoryInfo[73]) {
+		_inventoryInfo[72] = _inventoryOffsetTable[(_inventoryInfo[73] - 1) / 7];
+	}
+	drawInventory(_inventoryInfo[72], 0);
+	playSound(63, 1);
+}
+
+void IgorEngine::executeAction(int action) {
+	debug(kDebugEngine, "executeAction %d", action);
+	assert(action < 200);
+	if (action <= 100) {
+		(this->*_executeMainAction)(action);
+	} else {
+		(this->*_executeRoomAction)(action);
+	}
+}
+
+void IgorEngine::clearAction() {
+	redrawVerb(_currentAction.verb, false);
+	memset(&_currentAction, 0, sizeof(_currentAction));
+	_currentAction.verb = kVerbWalk;
+	_actionCode = 0;
+	_actionWalkPoint = 0;
+}
+
+void IgorEngine::handleRoomInput() {
+	if (_inputVars[kInputPause]) {
+		_inputVars[kInputPause] = 0;
+		handlePause();
+	}
+	if (_inputVars[kInputOptions]) {
+		_inputVars[kInputOptions] = 0;
+		handleOptionsMenu();
+	}
+	if (_inputVars[kInputSkipDialogue] && _gameState.dialogueTextRunning) {
+		_talkDelayCounter = _talkDelay;
+		if (_gameState.talkMode != kTalkModeTextOnly && _talkSpeechCounter > 2) {
+			stopSound();
+			_talkSpeechCounter = -1;
+		}
+		_inputVars[kInputSkipDialogue] = 0;
+	}
+	if (!_roomCursorOn || _gameState.dialogueTextRunning || _scrollInventory) {
+		return;
+	}
+	if (_inputVars[kInputCursorYPos] >= 156 && _inputVars[kInputCursorYPos] <= 167) {
+		if (_inputVars[kInputClick]) {
+			int verb = _verbAreasTable[_inputVars[kInputCursorXPos]];
+			if (verb != _currentAction.verb) {
+				redrawVerb(_currentAction.verb, false);
+				_currentAction.verb = verb;
+				redrawVerb(_currentAction.verb, true);
+			}
+			// reset action command
+			memset(&_currentAction, 0, sizeof(_currentAction));
+			_currentAction.verb = verb;
+			_currentAction.verbType = 0;
+			formatActionSentence(0);
+			_inputVars[kInputClick] = 0;
+		}
+		return;
+	}
+	if (_inputVars[kInputCursorYPos] >= 172 && _inputVars[kInputCursorYPos] <= 183 && (_inputVars[kInputCursorXPos] < 15 || _inputVars[kInputCursorXPos] > 304)) {
+		if (_inputVars[kInputClick]) {
+			if (_inventoryInfo[72] > 1) {
+				_inventoryInfo[72] -= 7;
+				drawInventory(_inventoryInfo[72], 2);
+			}
+			_inputVars[kInputClick] = 0;
+		}
+		return;
+	}
+	if (_inputVars[kInputCursorYPos] >= 186 && _inputVars[kInputCursorYPos] <= 197 && (_inputVars[kInputCursorXPos] < 15 || _inputVars[kInputCursorXPos] > 304)) {
+		if (_inputVars[kInputClick]) {
+			if (_inventoryInfo[73] > _inventoryInfo[72] + 6) {
+				_inventoryInfo[72] += 7;
+				drawInventory(_inventoryInfo[72], 1);
+			}
+			_inputVars[kInputClick] = 0;
+		}
+		return;
+	}
+
+/*	if (_inputVars[kInputClick]) {
+		if (_gameState.igorMoving) {
+			_walkDataCurrentPosX = _walkData[_walkDataCurrentIndex - 1].x;
+			_walkDataCurrentPosY = _walkData[_walkDataCurrentIndex - 1].y;
+			if (_roomObjectAreasTable[_screenLayer2[_walkDataCurrentPosY * 320 + _walkDataCurrentPosX].area == 0) {
+				return;
+			}
+			_walkDataCurrentPosX = _walkData[_walkDataCurrentIndex + 1].x;
+			_walkDataCurrentPosY = _walkData[_walkDataCurrentIndex + 1].y;
+			if (_roomObjectAreasTable[_screenLayer2[_walkDataCurrentPosY * 320 + _walkDataCurrentPosX].area == 0) {
+				return;
+			}
+		}
+		_inputVars[kInputClick] = 0;
+	}*/
+
+	bool actionHovering = !_inputVars[kInputClick];
+	_inputVars[kInputClick] = 0;
+
+	if (actionHovering && _actionCode != 0) {
+		return;
+	}
+
+	Action previousAction = _currentAction;
+	if (_inputVars[kInputCursorYPos] >= 170 && _inputVars[kInputCursorYPos] <= 199) {
+		int object = getObjectFromInventory(_inputVars[kInputCursorXPos]);
+		if (_currentAction.verbType == 0) {
+			_currentAction.object1Num = object;
+			_currentAction.object1Type = kObjectTypeInventory;
+			if (_currentAction.verb == kVerbUse && _roomActionsTable[_roomDataOffsets.action.useVerb + 10 + _currentAction.object1Num] != 0) {
+				formatActionSentence(0);
+				if (!actionHovering) {
+					_currentAction.verbType = 1;
+				}
+				return;
+			}
+			if (_currentAction.verb == kVerbGive && _roomActionsTable[_roomDataOffsets.action.giveVerb + 10 + _currentAction.object1Num] != 0) {
+				formatActionSentence(0);
+				if (!actionHovering) {
+					_currentAction.verbType = 2;
+				}
+				return;
+			}
+		} else {
+			_currentAction.object2Num = object;
+			_currentAction.object2Type = kObjectTypeInventory;
+		}
+	} else if (_inputVars[kInputCursorYPos] < 144) {
+		int area = _screenLayer2[_inputVars[kInputCursorYPos] * 320 + _inputVars[kInputCursorXPos]];
+		int object = _roomObjectAreasTable[area].object;
+		if (_currentAction.verbType == 0) {
+			_currentAction.object1Num = object;
+			_currentAction.object1Type = kObjectTypeRoom;
+			if (_currentAction.verb == kVerbUse && _roomActionsTable[_roomDataOffsets.action.useVerb + 48 + _currentAction.object1Num] != 0) {
+				formatActionSentence(0);
+				if (!actionHovering) {
+					_currentAction.verbType = 1;
+				}
+				return;
+			}
+			if (_currentAction.verb == kVerbGive && _roomActionsTable[_roomDataOffsets.action.giveVerb + 48 + _currentAction.object1Num] != 0) {
+				formatActionSentence(0);
+				if (!actionHovering) {
+					_currentAction.verbType = 2;
+				}
+				return;
+			}
+		} else {
+			_currentAction.object2Num = object;
+			_currentAction.object2Type = kObjectTypeRoom;
+		}
+	} else {
+		return;
+	}
+
+	if (_currentAction.verbType == 0) {
+		if (_currentAction.object1Type == kObjectTypeInventory) {
+			_actionCode = _inventoryActionsTable[(_currentAction.verb - 1) * 2 + _currentAction.object1Num * 20];
+		} else {
+			_actionCode = _roomActionsTable[_roomDataOffsets.action.defaultVerb + _currentAction.verb * 2 + _currentAction.object1Num * 20];
+		}
+	}
+	if (_currentAction.verbType == 1) {
+		int offset = _roomActionsTable[_roomDataOffsets.action.object2 + _currentAction.object2Num + _currentAction.object2Type * 38] * 2;
+		offset += _roomActionsTable[_roomDataOffsets.action.object1 + _currentAction.object1Num + _currentAction.object1Type * 38] * _roomDataOffsets.action.objectSize;
+		_actionCode = _roomActionsTable[_roomDataOffsets.action.useVerb + offset];
+	}
+	if (_currentAction.verbType == 2) {
+		int offset = _roomActionsTable[_roomDataOffsets.action.object2 + _currentAction.object2Num + _currentAction.object2Type * 38] * 2;
+		offset += _roomActionsTable[_roomDataOffsets.action.object1 + _currentAction.object1Num + _currentAction.object1Type * 38] * _roomDataOffsets.action.objectSize;
+		_actionCode = _roomActionsTable[_roomDataOffsets.action.giveVerb + offset];
+	}
+
+	if (actionHovering) {
+		formatActionSentence(0);
+		_currentAction.object2Num = 0;
+		_actionCode = 0;
+		return;
+	}
+	debug(kDebugEngine, "handleRoomInput() actionCode %d", _actionCode);
+	if (_actionCode == 0) {
+		clearAction();
+		return;
+	}
+
+	formatActionSentence(1);
+	if (_currentAction.verbType == 0) {
+		if (_currentAction.object1Type == kObjectTypeRoom) {
+			_actionWalkPoint = _roomActionsTable[_roomDataOffsets.action.defaultVerb + _currentAction.verb * 2 + _currentAction.object1Num * 20 + 1];
+			if (_actionWalkPoint > 0) {
+				if (_currentAction.object1Num == 0) {
+					// no object selected, just walk
+					_walkToObjectPosX = _inputVars[kInputCursorXPos];
+					_walkToObjectPosY = _inputVars[kInputCursorYPos];
+					if (_roomObjectAreasTable[_screenLayer2[_walkToObjectPosY * 320 + _walkToObjectPosX]].area == 0) {
+						fixWalkPosition(&_walkToObjectPosX, &_walkToObjectPosY);
+					}
+				} else {
+					// walk to object
+					int offset = READ_LE_UINT16(_roomActionsTable + _roomDataOffsets.obj.walkPoints + _currentAction.object1Num * 2);
+					_walkToObjectPosX = offset % 320;
+					_walkToObjectPosY = offset / 320;
+					debug(kDebugEngine, "handleRoomInput() walkToObject offset %d (0x%X)", offset, _roomDataOffsets.obj.walkPoints);
+				}
+				if (_gameState.igorMoving) {
+					// stop igor at the current position
+					_walkDataLastIndex = _walkDataCurrentIndex - 1;
+					_walkDataCurrentPosX = _walkData[_walkDataLastIndex].x;
+					_walkDataCurrentPosY = _walkData[_walkDataLastIndex].y;
+					_walkCurrentFrame = _walkData[_walkDataLastIndex].frameNum;
+					_walkCurrentPos = _walkData[_walkDataLastIndex].posNum;
+					WalkData::setNextFrame(_walkCurrentPos, _walkCurrentFrame);
+				} else {
+					--_walkDataLastIndex;
+					_walkDataCurrentPosX = _walkData[_walkDataLastIndex].x;
+					_walkDataCurrentPosY = _walkData[_walkDataLastIndex].y;
+					_walkCurrentPos = _walkData[_walkDataLastIndex].posNum;
+					_walkCurrentFrame = 1;
+				}
+				if (_walkDataCurrentPosX != _walkToObjectPosX || _walkDataCurrentPosY != _walkToObjectPosY) {
+					if (_roomDataOffsets.area.boxSize == 0) {
+						buildWalkPathSimple(_walkDataCurrentPosX, _walkDataCurrentPosY, _walkToObjectPosX, _walkToObjectPosY);
+					} else {
+						buildWalkPath(_walkDataCurrentPosX, _walkDataCurrentPosY, _walkToObjectPosX, _walkToObjectPosY);
+					}
+					if (_actionWalkPoint != 3) {
+						_walkCurrentFrame = 0;
+						_walkData[_walkDataLastIndex].frameNum = 0;
+					}
+					if (_actionWalkPoint == 1) {
+						_walkCurrentPos = _roomActionsTable[_roomDataOffsets.obj.walkFacingPosition + _currentAction.object1Num];
+						_walkData[_walkDataLastIndex].posNum = _walkCurrentPos;
+					}
+					_walkDataCurrentIndex = 1;
+					_gameState.igorMoving = true;
+				}
+				return;
+			}
+		}
+		hideCursor();
+		executeAction(_actionCode);
+		if (!_gameState.dialogueTextRunning) {
+			showCursor();
+		}
+		clearAction();
+		return;
+	}
+}
+
+void IgorEngine::animateIgorTalking(int frame) {
+	if (_currentPart == 850) {
+		PART_85_HELPER_6(frame);
+		return;
+	}
+	WalkData *wd = &_walkData[_walkDataLastIndex - 1];
+	int y = (wd->y - wd->scaleWidth + 1) * 320;
+	int delta = wd->x - _walkWidthScaleTable[wd->scaleHeight - 1] / 2;
+	if (delta > 0) {
+		y += delta;
+	}
+	for (int yOffset = 0; yOffset < wd->scaleWidth; y += 320, ++yOffset) {
+		int index = READ_LE_UINT16(_walkScaleTable + 0x6CE + wd->scaleHeight * 2) + yOffset;
+		uint8 yScale = _walkScaleTable[index];
+		if (yScale >= 11) {
+			continue;
+		}
+		for (int x = 0, xOffset = wd->clipSkipX - 1; x < wd->clipWidth; ++x, ++xOffset) {
+			index = READ_LE_UINT16(_walkScaleTable + 0x734 + _walkWidthScaleTable[wd->scaleHeight - 1] * 2) + xOffset;
+			uint8 xScale = _walkScaleTable[0x4FC + index];
+			if (xScale < 8 || xScale > 21) {
+				continue;
+			}
+			uint8 screenColor = _screenVGA[y + x];
+			if (screenColor < kTalkColor || screenColor > kTalkShadowColor) {
+				int offset = yScale * 14 + frame * 154 + (wd->posNum - 1) * 924 + (xScale - 8);
+				uint8 srcColor = _igorHeadFrames[offset];
+				if (srcColor == 0) {
+					_screenVGA[y + x] = _screenLayer1[y + x];
+					continue;
+				}
+				RoomObjectArea *roa = &_roomObjectAreasTable[_screenLayer2[y + x]];
+				if (wd->y <= roa->y1Lum) {
+					_screenVGA[y + x] = _screenLayer1[y + x];
+					continue;
+				}
+				if (wd->y <= roa->y2Lum && _gameState.enableLight == 1) {
+					srcColor -= roa->deltaLum;
+				}
+				_screenVGA[y + x] = srcColor;
+			}
+		}
+	}
+}
+
+void IgorEngine::handleRoomDialogue() {
+	if (_gameState.dialogueTextRunning) {
+		if (_talkDelayCounter == _talkDelay) {
+			animateIgorTalking(0);
+			memcpy(_screenVGA + _dialogueDirtyRectY, _screenTextLayer + 23040, _dialogueDirtyRectSize);
+			if (_dialogueTextsCount == 0) {
+				_gameState.dialogueTextRunning = false;
+				showCursor();
+			} else {
+				++_dialogueTextsStart;
+				startIgorDialogue();
+			}
+		} else {
+			animateIgorTalking(getRandomNumber(6));
+			++_talkDelayCounter;
+		}
+	}
+}
+
+void IgorEngine::handleRoomIgorWalk() {
+	if (_walkDataCurrentIndex > _walkDataLastIndex) {
+		_gameState.igorMoving = false;
+		_walkDataLastIndex = _walkDataCurrentIndex;
+		if (_actionCode > 0) {
+			hideCursor();
+			executeAction(_actionCode);
+			if (!_gameState.dialogueTextRunning) {
+				showCursor();
+			}
+			clearAction();
+		}
+	}
+	if (_gameState.igorMoving) {
+		moveIgor(_walkData[_walkDataCurrentIndex].posNum, _walkData[_walkDataCurrentIndex].frameNum);
+		++_walkDataCurrentIndex;
+	}
+}
+
+void IgorEngine::handleRoomInventoryScroll() {
+	if (_scrollInventory) {
+		scrollInventory();
+	}
+}
+
+void IgorEngine::handleRoomLight() {
+	if (_gameState.dialogueTextRunning || _gameState.igorMoving) {
+		_gameState.updateLight = false;
+	} else if (_gameState.updateLight) {
+		updateRoomLight(0);
+		_gameState.updateLight = 0;
+	} else if (getRandomNumber(10) == 0) {
+		updateRoomLight(1);
+		_gameState.updateLight = true;
+	}
+}
+
+int IgorEngine::lookupScale(int xOffset, int yOffset, int h) const {
+	int index = READ_LE_UINT16(_walkScaleTable + 0x734 + _walkWidthScaleTable[h - 1] * 2);
+	int offset = _walkScaleTable[0x4FC + index + xOffset];
+	index = READ_LE_UINT16(_walkScaleTable + 0x6CE + h * 2);
+	offset += _walkScaleTable[index + yOffset] * 30;
+	return offset;
+}
+
+void IgorEngine::moveIgorHelper1(int pos, int frame) {
+	debug(kDebugWalk, "moveIgorHelper1 _walkDataCurrentIndex %d pos %d frame %d", _walkDataCurrentIndex, pos, frame);
+	uint8 _walkClipSkipX = _walkData[_walkDataCurrentIndex].clipSkipX;
+	uint8 _walkHeightScale = _walkData[_walkDataCurrentIndex].scaleHeight;
+	int16 _walkClipWidth = _walkData[_walkDataCurrentIndex].clipWidth;
+	uint16 _walkScaleWidth = _walkData[_walkDataCurrentIndex].scaleWidth;
+	uint8 _walkXPosChanged = _walkData[_walkDataCurrentIndex].xPosChanged;
+	int16 _walkDxPos = _walkData[_walkDataCurrentIndex].dxPos + 1;
+	uint8 _walkYPosChanged = _walkData[_walkDataCurrentIndex].yPosChanged;
+	int16 _walkDyPos = _walkData[_walkDataCurrentIndex].dyPos;
+	int16 _walkDataCurrentPosX2 = _walkData[_walkDataCurrentIndex].x;
+	int16 _walkDataCurrentPosY2 = _walkData[_walkDataCurrentIndex].y;
+
+	uint16 _walkDataDrawOffset = (_walkData[_walkDataCurrentIndex].y - _walkData[_walkDataCurrentIndex].scaleWidth + 1) * 320;
+
+	int xPos = _walkWidthScaleTable[_walkData[_walkDataCurrentIndex].scaleHeight - 1] / 2;
+	if (_walkData[_walkDataCurrentIndex].x > xPos) {
+		_walkDataDrawOffset += _walkData[_walkDataCurrentIndex].x - xPos;
+	}
+	if (_walkXPosChanged != 0) {
+		_walkDataDrawOffset -= _walkDxPos;
+	}
+	if (_walkYPosChanged != 0) {
+		_walkDataDrawOffset -= _walkDyPos * 320;
+	}
+	if (_gameState.enableLight == 2) {
+		int8 colorLum = _roomObjectAreasTable[_screenLayer2[_walkDataCurrentPosY2 * 320 + _walkDataCurrentPosX2]].y2Lum;
+		if (_gameState.colorLum != colorLum) {
+			for (int color = 192 * 3; color <= 207 * 3; ++color) {
+				int c = _currentPalette[color] + colorLum;
+				if (c < 1) {
+					c = 0;
+				} else if (c > 62) {
+					c = 63;
+				}
+				_currentPalette[color] = c;
+			}
+			setPaletteRange(192, 207);
+			_gameState.colorLum = colorLum;
+		}
+	}
+	uint16 screenIgorDrawOffset = _walkDataDrawOffset;
+	uint16 igorScaledWidth = _walkDxPos + _walkClipWidth;
+	uint16 igorScaledHeight = _walkHeightScale + _walkDyPos;
+	uint16 igorBodyScanLine = 0;
+	if (_walkYPosChanged != 0) {
+		for (int i = 1; i <= _walkDyPos; ++i) {
+			memcpy(_igorTempFrames + igorBodyScanLine * 50, _screenLayer1 + _walkDataDrawOffset, igorScaledWidth);
+			_walkDataDrawOffset += 320;
+			++igorBodyScanLine;
+		}
+	}
+	if (_walkXPosChanged != 0) {
+		for (int yOffset = 0; yOffset < _walkScaleWidth; ++yOffset) {
+			assert(_walkDxPos > 0);
+			assert(igorBodyScanLine * 50 + _walkDxPos <= 3000);
+			memcpy(_igorTempFrames + igorBodyScanLine * 50, _screenLayer1 + _walkDataDrawOffset, _walkDxPos);
+			int xOffset = _walkClipSkipX - 1;
+			for (int i = 0; i < _walkClipWidth; ++i) {
+/*				int index = READ_LE_UINT16(_walkScaleTable + 0x734 + _walkWidthScaleTable[_walkHeightScale - 1] * 2);
+				int offset = _walkScaleTable[0x4FC + index + xOffset];
+				index = READ_LE_UINT16(_walkScaleTable + 0x6CE + _walkHeightScale * 2);
+				offset += _walkScaleTable[index + yOffset] * 30;*/
+				int offset = lookupScale(xOffset, yOffset, _walkHeightScale);
+				offset += frame * 1500;
+				uint8 color = _facingIgorFrames[pos - 1][offset];
+				if (color != 0) {
+					assert(_walkDataDrawOffset + _walkDxPos + i >= 0);
+					int index = _screenLayer2[_walkDataDrawOffset + _walkDxPos + i];
+					int yPos = _roomObjectAreasTable[index].y1Lum;
+					if (_walkData[_walkDataCurrentIndex].y <= yPos) {
+						_igorTempFrames[igorBodyScanLine * 50 + i + _walkDxPos] = _screenLayer1[_walkDataDrawOffset + _walkDxPos + i];
+					} else {
+						if (_gameState.enableLight == 1 && _walkData[_walkDataCurrentIndex].y <= _roomObjectAreasTable[index].y2Lum) {
+							color -= _roomObjectAreasTable[index].deltaLum;
+						}
+						_igorTempFrames[igorBodyScanLine * 50 + i + _walkDxPos] = color;
+					}
+				} else {
+					_igorTempFrames[igorBodyScanLine * 50 + i + _walkDxPos] = _screenLayer1[_walkDataDrawOffset + _walkDxPos + i];
+				}
+				++xOffset;
+			}
+			_walkDataDrawOffset += 320;
+			++igorBodyScanLine;
+		}
+	} else {
+		for (int yOffset = 0; yOffset < _walkScaleWidth; ++yOffset) {
+			int xOffset = _walkClipSkipX - 1;
+			for (int i = 0; i < _walkClipWidth; ++i) {
+/*				int index = READ_LE_UINT16(_walkScaleTable + 0x734 + _walkWidthScaleTable[_walkHeightScale - 1] * 2);
+				int offset = _walkScaleTable[0x4FC + index + xOffset];
+				index = READ_LE_UINT16(_walkScaleTable + 0x6CE + _walkHeightScale * 2);
+				offset += _walkScaleTable[index + yOffset] * 30;*/
+				int offset = lookupScale(xOffset, yOffset, _walkHeightScale);
+				offset += frame * 1500;
+				uint8 color = _facingIgorFrames[pos - 1][offset];
+				if (color != 0) {
+					assert(_walkDataDrawOffset + i >= 0);
+					int index = _screenLayer2[_walkDataDrawOffset + i];
+					int yPos = _roomObjectAreasTable[index].y1Lum;
+					if (_walkData[_walkDataCurrentIndex].y <= yPos) {
+						_igorTempFrames[igorBodyScanLine * 50 + i] = _screenLayer1[_walkDataDrawOffset + i];
+					} else {
+						if (_gameState.enableLight == 1 && _walkData[_walkDataCurrentIndex].y <= _roomObjectAreasTable[index].y2Lum) {
+							color -= _roomObjectAreasTable[index].deltaLum;
+						}
+						_igorTempFrames[igorBodyScanLine * 50 + i] = color;
+					}
+				} else {
+					_igorTempFrames[igorBodyScanLine * 50 + i] = _screenLayer1[_walkDataDrawOffset + i];
+				}
+				++xOffset;
+			}
+			const uint8 *src = _screenLayer1 + _walkDataDrawOffset + _walkClipWidth;
+			memcpy(_igorTempFrames + igorBodyScanLine * 50 + _walkClipWidth, src, _walkDxPos);
+			_walkDataDrawOffset += 320;
+			++igorBodyScanLine;
+		}
+	}
+	if (_walkYPosChanged == 0) {
+		for (int i = 1; i <= _walkDyPos; ++i) {
+			memcpy(_igorTempFrames + igorBodyScanLine * 50, _screenLayer1 + _walkDataDrawOffset, igorScaledWidth);
+			_walkDataDrawOffset += 320;
+			++igorBodyScanLine;
+		}
+	}
+	for (igorBodyScanLine = 0; igorBodyScanLine < igorScaledHeight; ++igorBodyScanLine) {
+		assert(screenIgorDrawOffset + igorScaledWidth <= 320 * 200);
+		memcpy(_screenVGA + screenIgorDrawOffset, _igorTempFrames + igorBodyScanLine * 50, igorScaledWidth);
+		screenIgorDrawOffset += 320;
+	}
+}
+
+void IgorEngine::moveIgor(int pos, int frame) {
+	assert(_gameState.enableLight == 1 || _gameState.enableLight == 2);
+	moveIgorHelper1(pos, frame);
+}
+
+void IgorEngine::buildWalkPathSimple(int srcX, int srcY, int dstX, int dstY) {
+	debug(kDebugWalk, "IgorEngine::buildWalkPathSimple(%d, %d, %d, %d)", srcX, srcY, dstX, dstY);
+	if (srcX != dstX || srcY != dstY) {
+		_walkData[0] = _walkData[_walkDataLastIndex];
+		_walkDataLastIndex = 1;
+		buildWalkPathArea(srcX, srcY, dstX, dstY);
+		--_walkDataLastIndex;
+	}
+}
+
+void IgorEngine::getClosestAreaTrianglePoint(int dstArea, int srcArea, int *dstY, int *dstX, int srcY, int srcX) {
+	int minSqrDist = -1;
+	assert(dstArea >= 1 && srcArea >= 1);
+	const uint8 *p = _roomActionsTable + ((dstArea - 1) + (srcArea - 1) * _roomDataOffsets.area.boxSize) * 6;
+	for (int i = 0; i < 3; ++i) {
+		const uint16 offset = READ_LE_UINT16(p + i * 2);
+		const int yPos = offset / 320;
+		const int xPos = offset % 320;
+		int y = srcY - yPos;
+		int x = srcX - xPos;
+		int sqrDist = y * y + x * x;
+		if (minSqrDist == -1 || sqrDist < minSqrDist) {
+			*dstY = yPos;
+			*dstX = xPos;
+			minSqrDist = sqrDist;
+		}
+	}
+	debug(kDebugWalk, "getClosestAreaTrianglePoint() sqrDist %d pos %d,%d", minSqrDist, *dstX, *dstY);
+}
+
+void IgorEngine::getClosestAreaTrianglePoint2(int dstArea, int srcArea, int *dstY, int *dstX, int srcY1, int srcX1, int srcY2, int srcX2) {
+	int minSqrDist = -1;
+	assert(dstArea >= 1 && srcArea >= 1);
+	const uint8 *p = _roomActionsTable + ((dstArea - 1) + (srcArea - 1) * _roomDataOffsets.area.boxSize) * 6;
+	for (int i = 0; i < 3; ++i) {
+		const uint16 offset = READ_LE_UINT16(p + i * 2);
+		const int yPos = offset / 320;
+		const int xPos = offset % 320;
+		int y1 = srcY1 - yPos;
+		int x1 = srcX1 - xPos;
+		int y2 = srcY2 - yPos;
+		int x2 = srcX2 - xPos;
+		int sqrDist = y1 * y1 + x1 * x1 + y2 * y2 + x2 * x2;
+		if (minSqrDist == -1 || sqrDist < minSqrDist) {
+			*dstY = yPos;
+			*dstX = xPos;
+			minSqrDist = sqrDist;
+		}
+	}
+	debug(kDebugWalk, "getClosestAreaTrianglePoint2() sqrDist %d pos %d,%d", minSqrDist, *dstX, *dstY);
+}
+
+void IgorEngine::buildWalkPath(int srcX, int srcY, int dstX, int dstY) {
+	if (srcX != dstX || srcY != dstY) {
+		_walkData[0] = _walkData[_walkDataLastIndex];
+		_walkDataLastIndex = 1;
+		int srcArea = _roomObjectAreasTable[_screenLayer2[srcY * 320 + srcX]].area;
+		int dstArea = _roomObjectAreasTable[_screenLayer2[dstY * 320 + dstX]].area;
+		debug(kDebugWalk, "srcArea = %d dstArea = %d", srcArea, dstArea);
+		int currentArea = srcArea;
+		for (int i = 1; dstArea != currentArea; ++i) {
+			const int boxOffset = srcArea * _roomDataOffsets.area.boxSrcSize + dstArea * _roomDataOffsets.area.boxDstSize;
+			int nextArea = _roomActionsTable[boxOffset + i + _roomDataOffsets.area.box];
+			debug(kDebugWalk, "nextArea %d (%d,%d,%d)", nextArea, _roomDataOffsets.area.box, _roomDataOffsets.area.boxSrcSize, _roomDataOffsets.area.boxDstSize);
+			int nextPosX, nextPosY;
+			if (dstArea != nextArea) {
+				getClosestAreaTrianglePoint(nextArea, currentArea, &nextPosY, &nextPosX, srcY, srcX);
+			} else {
+				getClosestAreaTrianglePoint2(nextArea, currentArea, &nextPosY, &nextPosX, dstY, dstX, srcY, srcX);
+			}
+			debug(kDebugWalk, "buildWalkPath() transitionArea = %d next %d,%d pos %d,%d offset 0x%X", nextArea, nextPosX, nextPosY, dstX, dstY, _roomDataOffsets.area.box);
+			buildWalkPathArea(srcX, srcY, nextPosX, nextPosY);
+			srcX = nextPosX;
+			srcY = nextPosY;
+			currentArea = nextArea;
+		}
+		buildWalkPathArea(srcX, srcY, dstX, dstY);
+		--_walkDataLastIndex;
+	}
+	debug(kDebugWalk, "buildWalkPath() end _walkDataLastIndex %d", _walkDataLastIndex);
+}
+
+void IgorEngine::buildWalkPathArea(int srcX, int srcY, int dstX, int dstY) {
+	if (srcX != dstX || srcY != dstY) {
+		const int dx = dstX - srcX;
+		const int dy = dstY - srcY;
+		debug(kDebugWalk, "buildWalkPathArea() dx = %d dy = %d src %d,%d dst %d,%d", dx, dy, srcX, srcY, dstX, dstY);
+		assert(_walkDataLastIndex > 0);
+		if (ABS(dy) * 2 > ABS(dx)) {
+			if (srcY > dstY) {
+				buildWalkPathAreaUpDirection(srcX, srcY, dstX, dstY);
+			} else {
+				buildWalkPathAreaDownDirection(srcX, srcY, dstX, dstY);
+			}
+		} else {
+			if (srcX < dstX) {
+				buildWalkPathAreaRightDirection(srcX, srcY, dstX, dstY);
+			} else {
+				buildWalkPathAreaLeftDirection(srcX, srcY, dstX, dstY);
+			}
+		}
+	}
+}
+
+static int16 roundReal(float f) {
+	return (int16)(f + .5);
+}
+
+static int16 truncReal(float f) {
+	return (int16)f;
+}
+
+int IgorEngine::getVerticalStepsCount(int minX, int minY, int maxX, int maxY) {
+	debug(kDebugWalk, "getVerticalStepsCount() %d %d %d %d", minX, minY, maxX, maxY);
+	int curX = 2;
+	if ((_walkXScaleRoom[minX] != 1 || _walkXScaleRoom[maxX] != 3) && (_walkXScaleRoom[maxX] != 1 || _walkXScaleRoom[minX] != 3)) {
+		curX = _walkXScaleRoom[minX];
+	}
+	int curY = minY;
+	int count = 0;
+	while (1) {
+		uint8 scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+		scale = _walkScaleTable[0x901 + scale];
+		if (maxY - curY <= scale) {
+			break;
+		}
+		curY += scale;
+		++count;
+	}
+	return count;
+}
+
+int IgorEngine::getHorizontalStepsCount(int minX, int minY, int maxX, int maxY) {
+	uint8 scale, frame = _walkCurrentFrame;
+
+	scale = _walkYScaleRoom[(_walkXScaleRoom[maxX] - 1) * 144 + maxY];
+	float r1 = _walkScaleSpeedTable[scale - 1];
+	scale = _walkYScaleRoom[(_walkXScaleRoom[minX] - 1) * 144 + minY];
+	float r2 = _walkScaleSpeedTable[scale - 1];
+	debug(kDebugWalk, "getHorizontalStepsCount() maxX - minX = %d r1 = %f r2 = %f", maxX - minX, r1, r2);
+
+	int16 steps = roundReal((maxX - minX) / ((r1 + r2) / 2.));
+	int count = 0;
+	if (steps != 0) {
+		float r3 = (maxY - minY) / (float)steps;
+		int curX = minX;
+		int curY = minY;
+		float r4 = r3;
+		while (1) {
+			scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+			uint8 c = _walkScaleTable[0x769 + scale * 8 + frame];
+			if (maxX - curX <= c) {
+				break;
+			}
+			curX += c;
+			curY = minY + truncReal(r4);
+			if (frame == 8) {
+				frame = 1;
+			} else {
+				++frame;
+			}
+			r4 += r3;
+			++count;
+		}
+	}
+	return count;
+}
+
+void IgorEngine::lookupScale(int curX, int curY, uint8 &scale, uint8 &xScale, uint8 &yScale) const {
+	scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+	yScale = _walkScaleTable[0x901 + scale];
+	xScale = _walkWidthScaleTable[scale - 1];
+}
+
+void IgorEngine::buildWalkPathAreaUpDirection(int srcX, int srcY, int dstX, int dstY) {
+	int _walkCurrentPosLocalVar = _walkCurrentPos;
+	if (_walkCurrentPos == 3) {
+		_walkCurrentPos = 1;
+		_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+		_walkData[_walkDataLastIndex].posNum = (dstX > srcX) ? kFacingPositionRight : kFacingPositionLeft;
+		_walkData[_walkDataLastIndex].frameNum = 5;
+		++_walkDataLastIndex;
+	} else {
+		_walkCurrentPos = 1;
+	}
+	if (_walkCurrentFrame > 6) {
+		_walkCurrentFrame -= 6;
+	}
+	WalkData *wd;
+	uint8 scale, xScale, yScale;
+	float vStepDist, vStepCur;
+	int curX = srcX;
+	int curY = srcY;
+	if (srcX > dstX) {
+		int vStepsCount = getVerticalStepsCount(dstX, dstY, srcX, srcY);
+		if (vStepsCount > 0) {
+			vStepCur = vStepDist = (srcX - dstX) / (float)vStepsCount;
+			for (int i = 1; i <= vStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				curX = srcX - truncReal(vStepCur);
+				lookupScale(curX, curY, scale, xScale, yScale);
+				curY -= yScale;
+				lookupScale(curX, curY, scale, xScale, yScale);
+				wd->setPos(curX, curY, 1, _walkCurrentFrame);
+				WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame);
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 0;
+				wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 0;
+					wd->dxPos = 0;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x;
+					}
+				}
+				++_walkDataLastIndex;
+				vStepCur += vStepDist;
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 1 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 1;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		lookupScale(dstX, dstY, scale, xScale, yScale);
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 1, _walkCurrentFrame);
+		WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame);
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 0;
+		wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale / 2);
+			wd->xPosChanged = 0;
+			wd->dxPos = 0;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			}
+		}
+		++_walkDataLastIndex;
+	} else {
+		int vStepsCount = getVerticalStepsCount(dstX, dstY, srcX, srcY);
+		if (vStepsCount > 0) {
+			vStepCur = vStepDist = (dstX - srcX) / (float)vStepsCount;
+			for (int i = 1; i <= vStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				curX = srcX + truncReal(vStepCur);
+				lookupScale(curX, curY, scale, xScale, yScale);
+				curY -= yScale;
+				lookupScale(curX, curY, scale, xScale, yScale);
+				wd->setPos(curX, curY, 1, _walkCurrentFrame);
+				WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame);
+				lookupScale(curX, curY, scale, xScale, yScale);
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 0;
+				wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 0;
+					wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = 0;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x;
+					}
+				}
+				++_walkDataLastIndex;
+				vStepCur += vStepDist;
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 1 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 1;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		lookupScale(dstX, dstY, scale, xScale, yScale);
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 1, _walkCurrentFrame);
+		WalkData::setNextFrame(kFacingPositionBack, _walkCurrentFrame);
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 0;
+		wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			int16 _dx = x - xScale;
+			int16 _cx = 319 - _dx;
+			wd->clipWidth = _cx;
+			wd->xPosChanged = 0;
+			wd->dxPos = dstX - curX;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = 0;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = dstX - curX;
+			}
+		}
+		++_walkDataLastIndex;
+	}
+}
+
+void IgorEngine::buildWalkPathAreaDownDirection(int srcX, int srcY, int dstX, int dstY) {
+	int _walkCurrentPosLocalVar = _walkCurrentPos;
+	if (_walkCurrentPos == 1) {
+		_walkCurrentPos = 3;
+		_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+		_walkData[_walkDataLastIndex].posNum = (srcX > dstX) ? kFacingPositionLeft : kFacingPositionRight;
+		_walkData[_walkDataLastIndex].frameNum = 1;
+		++_walkDataLastIndex;
+	} else {
+		_walkCurrentPos = 3;
+	}
+	if (_walkCurrentFrame > 6) {
+		_walkCurrentFrame -= 6;
+	}
+	WalkData *wd;
+	uint8 scale, xScale, yScale;
+	float vStepCur, vStepDist;
+	int curX = srcX;
+	int curY = srcY;
+	if (srcX > dstX) {
+		int vStepsCount = getVerticalStepsCount(srcX, srcY, dstX, dstY);
+		if (vStepsCount > 0) {
+			vStepCur = vStepDist = (srcX - dstX) / (float)vStepsCount;
+			for (int i = 1; i <= vStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				curX = srcX - truncReal(vStepCur);
+				lookupScale(curX, curY, scale, xScale, yScale);
+				curY += yScale;
+				lookupScale(curX, curY, scale, xScale, yScale);
+				wd->setPos(curX, curY, 3, _walkCurrentFrame);
+				WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame);
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 1;
+				wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 0;
+					wd->dxPos = 0;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = _walkData[_walkDataLastIndex - 1].x - wd->x;
+					}
+				}
+				++_walkDataLastIndex;
+				vStepCur += vStepDist;
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 3 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 3;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		lookupScale(dstX, dstY, scale, xScale, yScale);
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 3, _walkCurrentFrame);
+		WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame);
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 1;
+		wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale);
+			wd->xPosChanged = 0;
+			wd->dxPos = 0;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			}
+		}
+		++_walkDataLastIndex;
+	} else {
+		int vStepsCount = getVerticalStepsCount(srcX, srcY, dstX, dstY);
+		if (vStepsCount > 0) {
+			vStepCur = vStepDist = (dstX - srcX) / (float)vStepsCount;
+			for (int i = 1; i <= vStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				curX = srcX + truncReal(vStepCur);
+				lookupScale(curX, curY, scale, xScale, yScale);
+				curY += yScale;
+				lookupScale(curX, curY, scale, xScale, yScale);
+				wd->setPos(curX, curY, 3, _walkCurrentFrame);
+				WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame);
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 1;
+				wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 1;
+					wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = 0;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = wd->x - _walkData[_walkDataLastIndex - 1].x;
+					}
+				}
+				++_walkDataLastIndex;
+				vStepCur += vStepDist;
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 3 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 3;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		lookupScale(dstX, dstY, scale, xScale, yScale);
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 3, _walkCurrentFrame);
+		WalkData::setNextFrame(kFacingPositionFront, _walkCurrentFrame);
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 1;
+		wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale);
+			wd->xPosChanged = 1;
+			wd->dxPos = dstX - curX;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = 0;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = dstX - curX;
+			}
+		}
+		++_walkDataLastIndex;
+	}
+}
+
+void IgorEngine::buildWalkPathAreaRightDirection(int srcX, int srcY, int dstX, int dstY) {
+	int _walkCurrentPosLocalVar = _walkCurrentPos;
+	if (_walkCurrentPos == kFacingPositionLeft) {
+		_walkCurrentPos = kFacingPositionRight;
+		_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+		_walkData[_walkDataLastIndex].posNum = (srcY > dstY) ? kFacingPositionBack : kFacingPositionFront;
+		_walkData[_walkDataLastIndex].frameNum = 5;
+		++_walkDataLastIndex;
+	} else {
+		_walkCurrentPos = kFacingPositionRight;
+	}
+	WalkData *wd;
+	uint8 scale, xScale, xSkip;
+	float hStepCur, hStepDist;
+	int curX = srcX;
+	int curY = srcY;
+	if (srcY > dstY) {
+		int hStepsCount = getHorizontalStepsCount(srcX, dstY, dstX, srcY);
+		if (hStepsCount > 0) {
+			hStepCur = hStepDist = (srcY - dstY) / (float)hStepsCount;
+			for (int i = 1; i <= hStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame];
+				if ((curX + xSkip) >= dstX) {
+					break;
+				}
+				curX += xSkip;
+				curY = srcY - truncReal(hStepCur);
+				wd->setPos(curX, curY, 2, _walkCurrentFrame);
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xScale = _walkWidthScaleTable[scale - 1];
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 0;
+				wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 1;
+					wd->dxPos = xSkip;
+				} else {
+					x = x - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = 0;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = xSkip;
+					}
+				}
+				++_walkDataLastIndex;
+				hStepCur += hStepDist;
+				WalkData::setNextFrame(kFacingPositionRight, _walkCurrentFrame);
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 2 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 2;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 2, _walkCurrentFrame);
+		scale = _walkYScaleRoom[(_walkXScaleRoom[dstX] - 1) * 144 + dstY];
+		xScale = _walkWidthScaleTable[scale - 1];
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 0;
+		wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+		int x = xScale - xScale / 2 - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale);
+			wd->xPosChanged = 1;
+			wd->dxPos = dstX - curX;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = 0;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = dstX - curX;
+			}
+		}
+		++_walkDataLastIndex;
+		WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame);
+	} else {
+		int hStepsCount = getHorizontalStepsCount(srcX, srcY, dstX, dstY);
+		if (hStepsCount > 0) {
+			hStepCur = hStepDist = (dstY - srcY) / (float)hStepsCount;
+			for (int i = 1; i <= hStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame];
+				if (curX + xSkip >= dstX) {
+					break;
+				}
+				curX += xSkip;
+				curY = srcY + truncReal(hStepCur);
+				_walkData[_walkDataLastIndex].setPos(curX, curY, 2, _walkCurrentFrame);
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xScale = _walkWidthScaleTable[scale - 1];
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 1;
+				wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 1;
+					wd->dxPos = xSkip;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = 0;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 1;
+						wd->dxPos = xSkip;
+					}
+				}
+				++_walkDataLastIndex;
+				hStepCur += hStepDist;
+				WalkData::setNextFrame(kFacingPositionRight, _walkCurrentFrame);
+			}
+		} else {
+ 			if (_walkCurrentPosLocalVar == 2 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 2;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 2, _walkCurrentFrame);
+		scale = _walkYScaleRoom[(_walkXScaleRoom[dstX] - 1) * 144 + dstY];
+		xScale = _walkWidthScaleTable[scale - 1];
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 1;
+		wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale);
+			wd->xPosChanged = 1;
+			wd->dxPos = dstX - curX;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = 0;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 1;
+				wd->dxPos = dstX - curX;
+			}
+		}
+		++_walkDataLastIndex;
+		WalkData::setNextFrame(kFacingPositionRight, _walkCurrentFrame);
+	}
+}
+
+void IgorEngine::buildWalkPathAreaLeftDirection(int srcX, int srcY, int dstX, int dstY) {
+	int _walkCurrentPosLocalVar = _walkCurrentPos;
+	if (_walkCurrentPos == kFacingPositionRight) {
+		_walkCurrentPos = kFacingPositionLeft;
+		_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+		_walkData[_walkDataLastIndex].posNum = (srcY > dstY) ? kFacingPositionBack : kFacingPositionFront;
+		_walkData[_walkDataLastIndex].frameNum = 5;
+		++_walkDataLastIndex;
+	} else {
+		_walkCurrentPos = kFacingPositionLeft;
+	}
+	WalkData *wd;
+	uint8 scale, xScale, xSkip;
+	float hStepCur, hStepDist;
+	int curX = srcX;
+	int curY = srcY;
+	if (srcY > dstY) {
+		int hStepsCount = getHorizontalStepsCount(dstX, dstY, srcX, srcY);
+		if (hStepsCount > 0) {
+			hStepCur = hStepDist = (srcY - dstY) / (float)hStepsCount;
+			for (int i = 1; i <= hStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame];
+				if ((curX - xSkip) <= dstX) {
+					break;
+				}
+				curX -= xSkip;
+				curY = srcY - truncReal(hStepCur);
+				wd->setPos(curX, curY, 4, _walkCurrentFrame);
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xScale = _walkWidthScaleTable[scale - 1];
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 0;
+				wd->dyPos = _walkData[_walkDataLastIndex - 1].y - wd->y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 0;
+					wd->dxPos = 0;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = xSkip;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = xSkip;
+					}
+				}
+				++_walkDataLastIndex;
+				hStepCur += hStepDist;
+				WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame);
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 4 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 4;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 4, _walkCurrentFrame);
+		scale = _walkYScaleRoom[(_walkXScaleRoom[dstX] - 1) * 144 + dstY];
+		xScale = _walkWidthScaleTable[scale - 1];
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 0;
+		wd->dyPos = curY - dstY;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale);
+			wd->xPosChanged = 0;
+			wd->dxPos = 0;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			}
+		}
+		++_walkDataLastIndex;
+		WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame);
+	} else {
+		int hStepsCount = getHorizontalStepsCount(dstX, srcY, srcX, dstY);
+		if (hStepsCount > 0) {
+			hStepCur = hStepDist = (dstY - srcY) / (float)hStepsCount;
+			for (int i = 1; i <= hStepsCount; ++i) {
+				wd = &_walkData[_walkDataLastIndex];
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xSkip = _walkScaleTable[0x769 + scale * 8 + _walkCurrentFrame];
+				if (curX - xSkip <= dstX) {
+					break;
+				}
+				curX -= xSkip;
+				curY = srcY + truncReal(hStepCur);
+				wd->setPos(curX, curY, 4, _walkCurrentFrame);
+				scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+				xScale = _walkWidthScaleTable[scale - 1];
+				wd->setScale(scale, scale);
+				wd->yPosChanged = 1;
+				wd->dyPos = wd->y - _walkData[_walkDataLastIndex - 1].y;
+				int x = xScale - xScale / 2 + curX - 1;
+				if (x > 319) {
+					wd->clipSkipX = 1;
+					wd->clipWidth = 319 - (x - xScale);
+					wd->xPosChanged = 0;
+					wd->dxPos = 0;
+				} else {
+					x = curX - xScale / 2;
+					if (x < 0) {
+						wd->clipWidth = x + xScale;
+						wd->clipSkipX = xScale - wd->clipWidth + 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = xSkip;
+					} else {
+						wd->clipWidth = xScale;
+						wd->clipSkipX = 1;
+						wd->xPosChanged = 0;
+						wd->dxPos = xSkip;
+					}
+				}
+				++_walkDataLastIndex;
+				hStepCur += hStepDist;
+				WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame);
+			}
+		} else {
+			if (_walkCurrentPosLocalVar == 4 && _walkToObjectPosX == dstX && _walkToObjectPosY == dstY) {
+				_walkData[_walkDataLastIndex] = _walkData[_walkDataLastIndex - 1];
+				_walkData[_walkDataLastIndex].posNum = 4;
+				_walkData[_walkDataLastIndex].frameNum = 1;
+				++_walkDataLastIndex;
+			}
+		}
+		wd = &_walkData[_walkDataLastIndex];
+		wd->setPos(dstX, dstY, 4, _walkCurrentFrame);
+		scale = _walkYScaleRoom[(_walkXScaleRoom[curX] - 1) * 144 + curY];
+		xScale = _walkWidthScaleTable[scale - 1];
+		wd->setScale(scale, scale);
+		wd->yPosChanged = 1;
+		wd->dyPos = curY - dstY;
+		int x = xScale - xScale / 2 + dstX - 1;
+		if (x > 319) {
+			wd->clipSkipX = 1;
+			wd->clipWidth = 319 - (x - xScale);
+			wd->xPosChanged = 0;
+			wd->dxPos = 0;
+		} else {
+			x = dstX - xScale / 2;
+			if (x < 0) {
+				wd->clipWidth = xScale + x;
+				wd->clipSkipX = xScale - wd->clipWidth + 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			} else {
+				wd->clipWidth = xScale;
+				wd->clipSkipX = 1;
+				wd->xPosChanged = 0;
+				wd->dxPos = curX - dstX;
+			}
+		}
+		++_walkDataLastIndex;
+		WalkData::setNextFrame(kFacingPositionLeft, _walkCurrentFrame);
+	}
+}
+
+void IgorEngine::waitForIgorMove() {
+	_gameTicks = 0;
+	do {
+		if (compareGameTick(1, 16)) {
+			if (_walkDataCurrentIndex > _walkDataLastIndex) {
+				_gameState.igorMoving = false;
+				_walkDataLastIndex = _walkDataCurrentIndex;
+			}
+			if (_gameState.igorMoving) {
+				moveIgor(_walkData[_walkDataCurrentIndex].posNum, _walkData[_walkDataCurrentIndex].frameNum);
+				++_walkDataCurrentIndex;
+			}
+		}
+		if (_updateRoomBackground) {
+			(this->*_updateRoomBackground)();
+		}
+		waitForTimer();
+	} while (_gameState.igorMoving);
+}
+
+void IgorEngine::setRoomWalkBounds(int x1, int y1, int x2, int y2) {
+	assert(x1 <= x2 && y1 <= y2);
+	_roomWalkBounds.x1 = x1;
+	_roomWalkBounds.x2 = x2;
+	_roomWalkBounds.y1 = y1;
+	_roomWalkBounds.y2 = y2;
+}
+
+void IgorEngine::fixWalkPosition(int *x, int *y) {
+	int xPos = *x;
+	if (xPos < _roomWalkBounds.x1) {
+		xPos = _roomWalkBounds.x1;
+	}
+	if (xPos > _roomWalkBounds.x2) {
+		xPos = _roomWalkBounds.x2;
+	}
+	if (_currentPart == 22) {
+		*x = xPos;
+		*y = _roomWalkBounds.y1;
+		return;
+	}
+	int yPos = *y;
+	if (_currentPart == 13) {
+		if (xPos >= 92 && xPos <= 186 && yPos > 127) {
+			*x = xPos;
+			*y = 127;
+			return;
+		}
+		if (xPos >= 191 && xPos <= 289 && yPos > 127) {
+			*x = xPos;
+			*y = 127;
+			return;
+		}
+	}
+	// skip areas from top to bottom
+	while (_roomObjectAreasTable[_screenLayer2[yPos * 320 + xPos]].area == 0 && yPos < _roomWalkBounds.y2) {
+		++yPos;
+	}
+	if (_currentPart == 17) {
+		if (yPos != 143 || _roomObjectAreasTable[_screenLayer2[45760 + xPos]].area != 0) {
+			*x = xPos;
+			*y = yPos;
+			return;
+		}
+	}
+	// skip areas from bottom to top
+	while (_roomObjectAreasTable[_screenLayer2[yPos * 320 + xPos]].area == 0 && yPos > _roomWalkBounds.y1) {
+		--yPos;
+	}
+	*x = xPos;
+	*y = yPos;
+}
+
+void IgorEngine::recolorDialogueChoice(int num, bool highlight) {
+	uint8 *p = _screenVGA + 320 * (11 * num + 135);
+	for (int i = 0; i < 320 * 11; ++i) {
+		if (highlight) {
+			if (p[i] == 240) {
+				p[i] = 241;
+			}
+		} else {
+			if (p[i] == 241) {
+				p[i] = 240;
+			}
+		}
+	}
+}
+
+void IgorEngine::handleDialogue(int x, int y, int r, int g, int b) {
+	_gameState.dialogueStarted = true;
+	_gameState.dialogueChoiceStart = 1;
+	_gameState.dialogueChoiceCount = 1;
+	_dialogueEnded = false;
+	do {
+		if (_currentPart / 10 == 15 && _objectsState[48] == 0) {
+			_gameState.dialogueData[6] = 0;
+		}
+		if (_currentPart / 10 == 12 && _objectsState[44] == 0) {
+			_gameState.dialogueData[6] = 1;
+		}
+		drawDialogueChoices();
+		(this->*_updateDialogue)(kUpdateDialogueAnimStanding);
+		_dialogueChoiceSelected = selectDialogue();
+		if (_dialogueChoiceSelected == 0) {
+			break;
+		} else if (_dialogueChoiceSelected == -1) {
+			return;
+		}
+		dialogueAskQuestion();
+		dialogueReplyToQuestion(x, y, r, g, b);
+		int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize;
+		int code = _gameState.dialogueData[offset + 5];
+		if ((code >= 1 && code <= 99) || (_currentPart / 10 == 15 && code == 1)) {
+			_gameState.dialogueData[offset] = 0;
+			if (_currentPart / 10 == 21 && (code == 60 || code == 70 || code == 80) && _dialogueInfo[0] == 1) {
+				_gameState.dialogueData[offset + 2] = 4;
+			}
+			if (_currentPart / 10 == 33 && (code == 21 || code == 22 || code == 23) && _dialogueInfo[0] == 1) {
+				_gameState.dialogueData[offset + 2] = 2;
+			}
+		}
+		debug(kDebugEngine, "handleDialogue() action %d offset %d", _gameState.dialogueData[offset + 2], offset);
+		switch (_gameState.dialogueData[offset + 2]) {
+		case 1:
+			_gameState.dialogueChoiceCount = _gameState.dialogueData[offset + 1];
+			++_gameState.dialogueChoiceStart;
+			break;
+		case 2:
+			_gameState.dialogueChoiceCount = _gameState.dialogueData[offset + 1];
+			--_gameState.dialogueChoiceStart;
+			break;
+		case 4:
+			_gameState.dialogueChoiceCount = _gameState.dialogueData[offset + 1];
+			_gameState.dialogueChoiceStart -= 2;
+			break;
+		case 0:
+			_dialogueEnded = true;
+			break;
+		}
+		debug(kDebugEngine, "handleDialogue() end %d start %d count %d", _dialogueEnded, _gameState.dialogueChoiceStart, _gameState.dialogueChoiceCount);
+	} while (!_dialogueEnded);
+	memset(_screenVGA + 46080, 0, 17920);
+	drawVerbsPanel();
+	drawInventory(_inventoryInfo[72], 0);
+	_currentAction.verb = kVerbWalk;
+	_gameState.dialogueStarted = false;
+}
+
+void IgorEngine::drawDialogueChoices() {
+	memset(_screenVGA + 46080, 0, 56 * 320);
+	setPaletteColor(240, 0, 0, 0);
+	_dialogueInfo[0] = 0;
+	for (int i = 1; i <= 5; ++i) {
+		_dialogueInfo[i] = 0;
+	}
+	for (int i = 1; i <= 5; ++i) {
+		int offset = (i - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize;
+		if (_gameState.dialogueData[offset] == 1) {
+			++_dialogueInfo[0];
+			_dialogueInfo[_dialogueInfo[0]] = i;
+			int num = _gameState.dialogueData[offset + 3] - 1;
+			char questionText[128];
+			sprintf(questionText, "@%s %s", _dialogueQuestions[num][0], _dialogueQuestions[num][1]);
+			drawString(_screenVGA, questionText, 0, _dialogueInfo[0] * 11 + 135, 240, 0, 0);
+		}
+		 debug(kDebugEngine, "drawDialogueChoices() i %d state %d num %d", i, _gameState.dialogueData[offset], _gameState.dialogueData[offset + 3]);
+	}
+	setPaletteColor(240, _paletteBuffer[0x2F6 + 1], _paletteBuffer[0x2F6 + 2], _paletteBuffer[0x2F6 + 3]);
+	setPaletteColor(241, _paletteBuffer[0x2F0 + 1], _paletteBuffer[0x2F0 + 2], _paletteBuffer[0x2F0 + 3]);
+}
+
+int IgorEngine::selectDialogue() {
+	showCursor();
+	int hoveredChoice = 0;
+	bool end = false;
+	do {
+		int currentChoice = (_inputVars[kInputCursorYPos] - 134) / 11;
+		if (currentChoice < 0) {
+			currentChoice = 0;
+		}
+
+		if (currentChoice != hoveredChoice) {
+			if (hoveredChoice != 0) {
+				recolorDialogueChoice(hoveredChoice, false);
+			}
+			hoveredChoice = currentChoice;
+			if (hoveredChoice != 0) {
+				recolorDialogueChoice(hoveredChoice, true);
+			}
+		}
+		if (_inputVars[kInputClick]) {
+			if (hoveredChoice != 0 && hoveredChoice <= _dialogueInfo[0]) {
+				end = true;
+			}
+			_inputVars[kInputClick] = 0;
+		}
+
+		waitForTimer();
+	} while (!end && !_eventQuitGame);
+	hideCursor();
+	return hoveredChoice;
+}
+
+void IgorEngine::dialogueAskQuestion() {
+	memset(_screenVGA + 46080, 0, 17920);
+	int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize;
+	int num = _gameState.dialogueData[offset + 3] - 1;
+	if (_currentPart / 10 == 17) {
+		num = 5;
+	}
+	debug(kDebugEngine, "dialogueAskQuestion() num %d offset %d", num, offset);
+	strcpy(_globalDialogueTexts[250], _dialogueQuestions[num][0]);
+	strcpy(_globalDialogueTexts[251], _dialogueQuestions[num][1]);
+	if (_globalDialogueTexts[251][0]) {
+		ADD_DIALOGUE_TEXT(250, 2);
+	} else {
+		ADD_DIALOGUE_TEXT(250, 1);
+	}
+	SET_DIALOGUE_TEXT(1, 1);
+	startIgorDialogue();
+	waitForEndOfIgorDialogue();
+}
+
+void IgorEngine::dialogueReplyToQuestion(int x, int y, int r, int g, int b) {
+	int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize;
+	int reply = _gameState.dialogueData[offset + 4];
+	debug(kDebugEngine, "dialogueReplyToQuestion() dialogue choice %d reply %d", _dialogueChoiceSelected, reply);
+	if (reply == 0) {
+		return;
+	}
+	offset = _roomDataOffsets.dlg.matSize * 3 + reply;
+	int count = _gameState.dialogueData[offset - 1];
+	int dialogueIndex = 250;
+	for (int i = 0; i < count; ++i) {
+		int num = _gameState.dialogueData[offset] - 1;
+		int len = _gameState.dialogueData[offset + 1];
+		debug(kDebugEngine, "dialogueReplyToQuestion() reply %d %d offset %d", num, len, offset);
+		ADD_DIALOGUE_TEXT(dialogueIndex, len);
+		for (int j = 0; j < len; ++j) {
+			strcpy(_globalDialogueTexts[dialogueIndex], _dialogueReplies[num + j]);
+			++dialogueIndex;
+		}
+		offset += 2;
+	}
+	SET_DIALOGUE_TEXT(1, count);
+	startCutsceneDialogue(x, y, r, g, b);
+	waitForEndOfCutsceneDialogue(x, y, r, g, b);
+}
+
+} // namespace Igor


Property changes on: scummvm/trunk/engines/igor/igor.cpp
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native

Added: scummvm/trunk/engines/igor/igor.h
===================================================================
--- scummvm/trunk/engines/igor/igor.h	                        (rev 0)

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list