/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "scumm/actor.h"
#include "scumm/charset.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/scumm_v0.h"
#include "scumm/scumm_v7.h"
#include "scumm/verbs.h"

namespace Scumm {

enum {
	kInventoryUpArrow = 4,
	kInventoryDownArrow = 5,
	kSentenceLine = 6
};

struct VerbSettings {
	int id;
	int x_pos;
	int y_pos;
	int prep;
	const char *name;
};

static const VerbSettings v0VerbTable_English[] = {
	{ 1,  8, 0,   0, "Open"},
	{ 2,  8, 1,   0, "Close"},
	{ 3,  0, 2,   4, "Give"},
	{ 4, 32, 0,   0, "Turn on"},
	{ 5, 32, 1,   0, "Turn off"},
	{ 6, 32, 2,   2, "Fix"},
	{ 7, 24, 0,   0, "New Kid"},
	{ 8, 24, 1,   2, "Unlock"},
	{ 9,  0, 0,   0, "Push"},
	{10,  0, 1,   0, "Pull"},
	{11, 24, 2, 255, "Use"},
	{12,  8, 2,   0, "Read"},
	{13, 15, 0,   0, "Walk to"},
	{14, 15, 1,   0, "Pick up"},
	{15, 15, 2,   0, "What is"}
};

// FIXME: Replace * with the correct character
static const VerbSettings v0VerbTable_German[] = {
	{ 1,  7, 0,   0, "$ffne"},
	{ 2, 13, 1,   0, "Schlie*e"},
	{ 3,  0, 2,   4, "Gebe"},
	{ 4, 37, 1,   0, "Ein"},
	{ 5, 37, 0,   0, "Aus"},
	{ 6, 23, 1,   2, "Repariere"},
	{ 7, 34, 2,   0, "Person"},
	{ 8, 23, 0,   2, "Schlie*e auf"},
	{ 9,  0, 0,   0, "Dr<cke"},
	{10,  0, 1,   0, "Ziehe"},
	{11, 23, 2, 255, "Benutz"},
	{12,  7, 2,   0, "Lese"},
	{13, 13, 0,   0, "Gehe zu"},
	{14,  7, 1,   0, "Nimm"},
	{15, 13, 2,   0, "Was ist"}
};

void ScummEngine_v0::resetVerbs() {
	VirtScreen *virt = &_virtscr[kVerbVirtScreen];
	VerbSlot *vs;
	const VerbSettings *vtable;
	int i;

	switch (_language) {
	case Common::DE_DEU:
		vtable = (const VerbSettings*)v0VerbTable_German;
		break;
	default:
		vtable = (const VerbSettings*)v0VerbTable_English;
	}

	for (i = 1; i < 16; i++)
		killVerb(i);

	for (i = 1; i < 16; i++) {
		vs = &_verbs[i];
		vs->verbid = vtable[i - 1].id;
		vs->color = 5;
		vs->hicolor = 7;
		vs->dimcolor = 11;
		vs->type = kTextVerbType;
		vs->charset_nr = _string[0]._default.charset;
		vs->curmode = 1;
		vs->saveid = 0;
		vs->key = 0;
		vs->center = 0;
		vs->imgindex = 0;
		vs->prep = vtable[i - 1].prep;
		vs->curRect.left = vtable[i - 1].x_pos * 8;
		vs->curRect.top = vtable[i - 1].y_pos * 8 + virt->topline + 8;
		loadPtrToResource(rtVerb, i, (const byte*)vtable[i - 1].name);
	}
}

void ScummEngine_v0::setNewKidVerbs() {
	VirtScreen *virt = &_virtscr[kVerbVirtScreen];
	VerbSlot *vs;
	int i;

	for (i = 1; i < 16; i++)
		killVerb(i);

	for (i = 1; i < 4; i++) {
		vs = &_verbs[i];
		vs->verbid = i;
		vs->color = 5;
		vs->hicolor = 7;
		vs->dimcolor = 11;
		vs->type = kTextVerbType;
		vs->charset_nr = _string[0]._default.charset;
		vs->curmode = 1;
		vs->saveid = 0;
		vs->key = 0;
		vs->center = 0;
		vs->imgindex = 0;
		vs->prep = 0;
		vs->curRect.left = (i * 8) * 8;
		vs->curRect.top = virt->topline + 8;

		Actor *a = derefActor(VAR(96 + i), "setNewKidVerbs");
		loadPtrToResource(rtVerb, i, (const byte*)a->getActorName());
	}
	setUserState(191);
}

void ScummEngine_v0::switchActor(int slot) {
	resetSentence(false);

	if (_currentRoom == 45)
		return;

	// radiation suit? don't let the player switch
	if (VAR(VAR_EGO) == 8)
		return;

	// verbs disabled? or just new kid button?
	if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2)
		return;

	VAR(VAR_EGO) = VAR(97 + slot);
	resetVerbs();
	actorFollowCamera(VAR(VAR_EGO));
	setUserState(247);
}

void ScummEngine_v2::initV2MouseOver() {
	int i;
	int arrow_color, color, hi_color;

	if (_game.version == 2) {
		color = 13;
		hi_color = 14;
		arrow_color = 1;
	} else {
		color = 16;
		hi_color = 7;
		arrow_color = 6;
	}

	_mouseOverBoxV2 = -1;

	// Inventory items

	for (i = 0; i < 2; i++) {
		_mouseOverBoxesV2[2 * i].rect.left = 0;
		_mouseOverBoxesV2[2 * i].rect.right = 144;
		_mouseOverBoxesV2[2 * i].rect.top = 32 + 8 * i;
		_mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;

		_mouseOverBoxesV2[2 * i].color = color;
		_mouseOverBoxesV2[2 * i].hicolor = hi_color;

		_mouseOverBoxesV2[2 * i + 1].rect.left = 176;
		_mouseOverBoxesV2[2 * i + 1].rect.right = 320;
		_mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
		_mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;

		_mouseOverBoxesV2[2 * i + 1].color = color;
		_mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
	}

	// Inventory arrows

	_mouseOverBoxesV2[kInventoryUpArrow].rect.left = 144;
	_mouseOverBoxesV2[kInventoryUpArrow].rect.right = 176;
	_mouseOverBoxesV2[kInventoryUpArrow].rect.top = 32;
	_mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 40;

	_mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
	_mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;

	_mouseOverBoxesV2[kInventoryDownArrow].rect.left = 144;
	_mouseOverBoxesV2[kInventoryDownArrow].rect.right = 176;
	_mouseOverBoxesV2[kInventoryDownArrow].rect.top = 40;
	_mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 48;

	_mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
	_mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;

	// Sentence line

	_mouseOverBoxesV2[kSentenceLine].rect.left = 0;
	_mouseOverBoxesV2[kSentenceLine].rect.right = 320;
	_mouseOverBoxesV2[kSentenceLine].rect.top = 0;
	_mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;

	_mouseOverBoxesV2[kSentenceLine].color = color;
	_mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
}

void ScummEngine_v2::initNESMouseOver() {
	int i;
	int arrow_color, color, hi_color;

	color = 0;
	hi_color = 0;
	arrow_color = 0;

	_mouseOverBoxV2 = -1;

	// Inventory items

	for (i = 0; i < 2; i++) {
		_mouseOverBoxesV2[2 * i].rect.left = 16;
		_mouseOverBoxesV2[2 * i].rect.right = 120;
		_mouseOverBoxesV2[2 * i].rect.top = 48 + 8 * i;
		_mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;

		_mouseOverBoxesV2[2 * i].color = color;
		_mouseOverBoxesV2[2 * i].hicolor = hi_color;

		_mouseOverBoxesV2[2 * i + 1].rect.left = 152;
		_mouseOverBoxesV2[2 * i + 1].rect.right = 256;
		_mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
		_mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;

		_mouseOverBoxesV2[2 * i + 1].color = color;
		_mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
	}

	// Inventory arrows

	_mouseOverBoxesV2[kInventoryUpArrow].rect.left = 128;
	_mouseOverBoxesV2[kInventoryUpArrow].rect.right = 136;
	_mouseOverBoxesV2[kInventoryUpArrow].rect.top = 48;
	_mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 56;

	_mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
	_mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;

	_mouseOverBoxesV2[kInventoryDownArrow].rect.left = 136;
	_mouseOverBoxesV2[kInventoryDownArrow].rect.right = 144;
	_mouseOverBoxesV2[kInventoryDownArrow].rect.top = 48;
	_mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 56;

	_mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
	_mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;

	// Sentence line

	_mouseOverBoxesV2[kSentenceLine].rect.left = 16;
	_mouseOverBoxesV2[kSentenceLine].rect.right = 256;
	_mouseOverBoxesV2[kSentenceLine].rect.top = 0;
	_mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;

	_mouseOverBoxesV2[kSentenceLine].color = color;
	_mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
}

void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
	VirtScreen *vs = &_virtscr[kVerbVirtScreen];
	Common::Rect rect;
	byte *ptr, *dst;
	int i, x, y, new_box = -1;

	// Don't do anything unless the inventory is active
	if (!(_userState & 64)) {
		_mouseOverBoxV2 = -1;
		return;
	}

	if (_cursor.state > 0) {
		for (i = 0; i < ARRAYSIZE(_mouseOverBoxesV2); i++) {
			if (_mouseOverBoxesV2[i].rect.contains(pos.x, pos.y - vs->topline)) {
				new_box = i;
				break;
			}
		}
	}

	if ((new_box != _mouseOverBoxV2) || (_game.version == 0)) {
		if (_mouseOverBoxV2 != -1) {
			rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect;

			dst = ptr = vs->getPixels(rect.left, rect.top);

			// Remove highlight.
			for (y = rect.height() - 1; y >= 0; y--) {
				for (x = rect.width() - 1; x >= 0; x--) {
					if (dst[x] == _mouseOverBoxesV2[_mouseOverBoxV2].hicolor)
						dst[x] = _mouseOverBoxesV2[_mouseOverBoxV2].color;
				}
				dst += vs->pitch;
			}

			markRectAsDirty(kVerbVirtScreen, rect);
		}

		if (new_box != -1) {
			rect = _mouseOverBoxesV2[new_box].rect;

			dst = ptr = vs->getPixels(rect.left, rect.top);

			// Apply highlight
			for (y = rect.height() - 1; y >= 0; y--) {
				for (x = rect.width() - 1; x >= 0; x--) {
					if (dst[x] == _mouseOverBoxesV2[new_box].color)
						dst[x] = _mouseOverBoxesV2[new_box].hicolor;
				}
				dst += vs->pitch;
			}

			markRectAsDirty(kVerbVirtScreen, rect);
		}

		_mouseOverBoxV2 = new_box;
	}
}

void ScummEngine_v2::checkV2Inventory(int x, int y) {
	int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
	int object = 0;

	y -= _virtscr[kVerbVirtScreen].topline;

	if ((y < inventoryArea) || !(_mouseAndKeyboardStat & MBS_LEFT_CLICK))
		return;

	if (_mouseOverBoxesV2[kInventoryUpArrow].rect.contains(x, y)) {
		if (_inventoryOffset >= 2) {
			_inventoryOffset -= 2;
			redrawV2Inventory();
		}
	} else if (_mouseOverBoxesV2[kInventoryDownArrow].rect.contains(x, y)) {
		if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
			_inventoryOffset += 2;
			redrawV2Inventory();
		}
	}

	for (object = 0; object < 4; object++) {
		if (_mouseOverBoxesV2[object].rect.contains(x, y)) {
			break;
		}
	}

	if (object >= 4)
		return;

	object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);

	if (object > 0) {
		if (_game.version == 0) {
			_activeInventory = object;

		} else {
			runInputScript(kInventoryClickArea, object, 0);
		}
	}
}

void ScummEngine_v2::redrawV2Inventory() {
	VirtScreen *vs = &_virtscr[kVerbVirtScreen];
	int i;
	int max_inv;
	Common::Rect inventoryBox;
	int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;
	int maxChars = (_game.platform == Common::kPlatformNES) ? 13: 18;

	_mouseOverBoxV2 = -1;

	if (!(_userState & 64))	// Don't draw inventory unless active
		return;

	// Clear on all invocations
	inventoryBox.top = vs->topline + inventoryArea;
	inventoryBox.bottom = vs->topline + vs->h;
	inventoryBox.left = 0;
	inventoryBox.right = vs->w;
	restoreBackground(inventoryBox);

	_string[1].charset = 1;

	max_inv = getInventoryCount(_scummVars[VAR_EGO]) - _inventoryOffset;
	if (max_inv > 4)
		max_inv = 4;
	for (i = 0; i < max_inv; i++) {
		int obj = findInventory(_scummVars[VAR_EGO], i + 1 + _inventoryOffset);
		if (obj == 0)
			break;

		_string[1].ypos = _mouseOverBoxesV2[i].rect.top + vs->topline;
		_string[1].xpos = _mouseOverBoxesV2[i].rect.left;
		_string[1].right = _mouseOverBoxesV2[i].rect.right - 1;
		_string[1].color = _mouseOverBoxesV2[i].color;

		_v0ObjectInInventory = true;
		const byte *tmp = getObjOrActorName(obj);
		_v0ObjectInInventory = false;
		assert(tmp);

		// Prevent inventory entries from overflowing by truncating the text
		byte msg[20];
		msg[maxChars] = 0;
		strncpy((char *)msg, (const char *)tmp, maxChars);

		// Draw it
		drawString(1, msg);
	}


	// If necessary, draw "up" arrow
	if (_inventoryOffset > 0) {
		_string[1].xpos = _mouseOverBoxesV2[kInventoryUpArrow].rect.left;
		_string[1].ypos = _mouseOverBoxesV2[kInventoryUpArrow].rect.top + vs->topline;
		_string[1].right = _mouseOverBoxesV2[kInventoryUpArrow].rect.right - 1;
		_string[1].color = _mouseOverBoxesV2[kInventoryUpArrow].color;
		if (_game.platform == Common::kPlatformNES)
			drawString(1, (const byte *)"\x7E");
		else
			drawString(1, (const byte *)" \1\2");
	}

	// If necessary, draw "down" arrow
	if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
		_string[1].xpos = _mouseOverBoxesV2[kInventoryDownArrow].rect.left;
		_string[1].ypos = _mouseOverBoxesV2[kInventoryDownArrow].rect.top + vs->topline;
		_string[1].right = _mouseOverBoxesV2[kInventoryDownArrow].rect.right - 1;
		_string[1].color = _mouseOverBoxesV2[kInventoryDownArrow].color;
		if (_game.platform == Common::kPlatformNES)
			drawString(1, (const byte *)"\x7F");
		else
			drawString(1, (const byte *)" \3\4");
	}
}

void ScummEngine::redrawVerbs() {
	if (_game.version <= 2 && !(_userState & 128)) // Don't draw verbs unless active
		return;

	int i, verb = 0;
	if (_cursor.state > 0)
		verb = findVerbAtPos(_mouse.x, _mouse.y);

	// Iterate over all verbs.
	// Note: This is the correct order (at least for MI EGA, MI2, Full Throttle).
	// Do not change it! If you discover, based on disasm, that some game uses
	// another (e.g. the reverse) order here, you have to use an if/else construct
	// to add it as a special case!
	for (i = 0; i < _numVerbs; i++) {
		if (i == verb && _verbs[verb].hicolor)
			drawVerb(i, 1);
		else
			drawVerb(i, 0);
	}
	_verbMouseOver = verb;
}

void ScummEngine::handleMouseOver(bool updateInventory) {
	if (_completeScreenRedraw) {
		verbMouseOver(0);
	} else {
		if (_cursor.state > 0)
			verbMouseOver(findVerbAtPos(_mouse.x, _mouse.y));
	}
}

void ScummEngine_v2::handleMouseOver(bool updateInventory) {
	ScummEngine::handleMouseOver(updateInventory);

	if (updateInventory) {
		// FIXME/TODO: Reset and redraw the sentence line
		_inventoryOffset = 0;
	}
	if (_completeScreenRedraw || updateInventory) {
		redrawV2Inventory();
	}
	checkV2MouseOver(_mouse);
}

void ScummEngine_v0::handleMouseOver(bool updateInventory) {
	drawSentence();
	ScummEngine_v2::handleMouseOver(updateInventory);
}

#ifdef ENABLE_HE
void ScummEngine_v72he::checkExecVerbs() {
	VAR(VAR_MOUSE_STATE) = 0;

	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
		return;

	VAR(VAR_MOUSE_STATE) = _mouseAndKeyboardStat;

	ScummEngine::checkExecVerbs();
}
#endif

void ScummEngine::checkExecVerbs() {
	int i, over;
	VerbSlot *vs;

	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
		return;

	if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
		/* Check keypresses */
		if (!(_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD)) {
			// This is disabled in the SegaCD version as the "vs->key" values setup
			// by script-17 conflict with the values expected by the generic keyboard
			// input script. See tracker item #1193185.
			vs = &_verbs[1];
			for (i = 1; i < _numVerbs; i++, vs++) {
				if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
					if (_mouseAndKeyboardStat == vs->key) {
						// Trigger verb as if the user clicked it
						runInputScript(kVerbClickArea, vs->verbid, 1);
						return;
					}
				}
			}
		}

		if ((_game.id == GID_INDY4 || _game.id == GID_PASS) && _mouseAndKeyboardStat >= '0' && _mouseAndKeyboardStat <= '9') {
			// To support keyboard fighting in FOA, we need to remap the number keys.
			// FOA apparently expects PC scancode values (see script 46 if you want
			// to know where I got these numbers from). Oddly enough, the The Indy 3
			// part of the "Passport to Adventure" demo expects the same keyboard
			// mapping, even though the full game doesn't.
			static const int numpad[10] = {
				'0',
				335, 336, 337,
				331, 332, 333,
				327, 328, 329
			};
			_mouseAndKeyboardStat = numpad[_mouseAndKeyboardStat - '0'];
		}

		if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) {
			// HACK: In the FM-TOWNS games Indy3, Loom and Zak the most significant bit is set for special keys
			// like F5 (=0x8005) or joystick buttons (mask 0xFE00, e.g. SELECT=0xFE40 for the save/load menu).
			// Hence the distinction with (_mouseAndKeyboardStat < MBS_MAX_KEY) between mouse- and key-events is not applicable
			// to this games, so we have to remap the special keys here.
			if (_mouseAndKeyboardStat == 319) {
				_mouseAndKeyboardStat = 0x8005;
			}
		}

		if ((_game.platform == Common::kPlatformFMTowns && _game.id == GID_ZAK) &&
			(_mouseAndKeyboardStat >= 315 && _mouseAndKeyboardStat <= 318)) {
			// Hack: Handle switching to a person via F1-F4 keys.
			// This feature isn't available in the scripts of the FM-TOWNS version.
			int fKey = _mouseAndKeyboardStat - 314;
			int switchSlot = getVerbSlot(36, 0);
			// check if switch-verb is enabled
			if (_verbs[switchSlot].curmode == 1) {
				// Check if person is available (see script 23 from ZAK_FM-TOWNS and script 4 from ZAK_PC).
				// Zak: Var[144 Bit 15], Annie: Var[145 Bit 0], Melissa: Var[145 Bit 1], Leslie: Var[145 Bit 2]
				if (!readVar(0x890E + fKey)) {
					runInputScript(kVerbClickArea, 36 + fKey, 0);
				}
			}
			return;
		}

		// Generic keyboard input
		runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
	} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
		VirtScreen *zone = findVirtScreen(_mouse.y);
		const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;

		// This could be kUnkVirtScreen.
		// Fixes bug #1536932: "MANIACNES: Crash on click in speechtext-area"
		if (!zone)
			return;

		over = findVerbAtPos(_mouse.x, _mouse.y);
		if (over != 0) {
			// Verb was clicked
			runInputScript(kVerbClickArea, _verbs[over].verbid, code);
		} else {
			// Scene was clicked
			runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
		}
	}
}

void ScummEngine_v2::checkExecVerbs() {
	int i, over;
	VerbSlot *vs;

	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
		return;

	if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
		/* Check keypresses */
		vs = &_verbs[1];
		for (i = 1; i < _numVerbs; i++, vs++) {
			if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
				if (_mouseAndKeyboardStat == vs->key) {
					// Trigger verb as if the user clicked it
					runInputScript(kVerbClickArea, vs->verbid, 1);
					return;
				}
			}
		}

		// Simulate inventory picking and scrolling keys
		int object = -1;

		switch (_mouseAndKeyboardStat) {
		case 'u': // arrow up
			if (_inventoryOffset >= 2) {
				_inventoryOffset -= 2;
				redrawV2Inventory();
			}
			return;
		case 'j': // arrow down
			if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
				_inventoryOffset += 2;
				redrawV2Inventory();
			}
			return;
		case 'i': // object
			object = 0;
			break;
		case 'o':
			object = 1;
			break;
		case 'k':
			object = 2;
			break;
		case 'l':
			object = 3;
			break;
		}

		if (object != -1) {
			object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);

			if (object > 0) {
				if (_game.version == 0) {
					_activeInventory = object;

				} else {
					runInputScript(kInventoryClickArea, object, 0);
				}
			}
			return;
		}

		// Generic keyboard input
		runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
	} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
		VirtScreen *zone = findVirtScreen(_mouse.y);
		const byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
		const int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;

		// This could be kUnkVirtScreen.
		// Fixes bug #1536932: "MANIACNES: Crash on click in speechtext-area"
		if (!zone)
			return;

		if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
			// Click into V2 sentence line
			runInputScript(kSentenceClickArea, 0, 0);
		} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + inventoryArea) {
			// Click into V2 inventory
			checkV2Inventory(_mouse.x, _mouse.y);
		} else {
			over = findVerbAtPos(_mouse.x, _mouse.y);
			if (over != 0) {
				// Verb was clicked
				runInputScript(kVerbClickArea, _verbs[over].verbid, code);
			} else {
				// Scene was clicked
				runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
			}
		}
	}
}

void ScummEngine_v0::runObject(int obj, int entry) {
	bool prev = _v0ObjectInInventory;

	if (getVerbEntrypoint(obj, entry) == 0) {
		// If nothing was found, attempt to find the 'WHAT-IS' verb script
		// (which is not really the what-is script, as this verb never actually executes
		//  it merely seems to be some type of fallback)
		if (getVerbEntrypoint(obj, 0x0F) != 0) {
			entry = 0x0F;
		}
	}

	_v0ObjectInInventory = prev;

	if (getVerbEntrypoint(obj, entry) != 0) {
		_v0ObjectInInventory = prev;
		runObjectScript(obj, entry, false, false, NULL);
	} else if (entry != 13 && entry != 15) {
		if (_activeVerb != 3) {
			VAR(VAR_ACTIVE_VERB) = entry;
			runScript(3, 0, 0, 0);

		// For some reasons, certain objects don't have a "give" script
		} else if (VAR(VAR_ACTIVE_ACTOR) > 0 && VAR(VAR_ACTIVE_ACTOR) < 8) {
			if (_activeInventory)
				setOwnerOf(_activeInventory, VAR(VAR_ACTIVE_ACTOR));
		}
	}
}

bool ScummEngine_v0::verbMoveToActor(int actor) {
	Actor *a = derefActor(VAR(VAR_EGO), "verbMoveToActor");
	Actor *a2 = derefActor(actor, "verbMoveToActor");
	int dist = getDist(a->getRealPos().x, a->getRealPos().y, a2->getRealPos().x, a2->getRealPos().y);

	if (!a->_moving && dist > 4) {
		a->startWalkActor(a2->getRealPos().x, a2->getRealPos().y, -1);
	} else {
		if (dist <= 4) {
			a->stopActorMoving();
			return false;
		}
	}

	return true;
}

bool ScummEngine_v0::verbMove(int object, int objectIndex, bool invObject) {
	int x, y, dir;
	Actor *a = derefActor(VAR(VAR_EGO), "verbMove");

	if (_currentMode != 3 && _currentMode != 2)
		return false;

	_v0ObjectIndex = true;
	getObjectXYPos(objectIndex, x, y, dir);
	_v0ObjectIndex = false;

	// Detect distance from target object
	int dist =  getDist(a->getRealPos().x, a->getRealPos().y, x, y);

	if (a->_moving)
		return true;

	if (dist > 5) {
		a->startWalkActor(x, y, dir);
		VAR(6) = x;
		VAR(7) = y;
		return true;
	} else {
		// Finished walk, are we picking up the item?
		if (_verbPickup) {
			int oldActive = _activeObject, oldIndex = _activeObjectIndex;
			_activeObject = object;
			_activeObjectIndex = objectIndex;

			_v0ObjectIndex = true;
			// Execute pickup
			runObject(objectIndex, 14);
			_v0ObjectIndex = false;

			_activeObject = oldActive;
			_activeObjectIndex = oldIndex;

			// Finished picking up
			_verbPickup = false;
		}
	}

	return false;
}

bool ScummEngine_v0::verbObtain(int obj, int objIndex) {
	bool didPickup = false;

	int prep, where = whereIsObjectInventory(obj);

	if (objIndex == 0)
		return false;

	// Object in inventory ?
	if (where != WIO_INVENTORY) {
		_v0ObjectIndex = true;
		prep = verbPrep(objIndex);

		if (prep == 1 || prep == 4) {
			if (_activeVerb != 13 && _activeVerb != 14) {
				_verbPickup = true;
				didPickup = true;
			}
		} else {
			_verbPickup = false;
		}

		// Ignore verbs?
		Actor *a = derefActor(VAR(VAR_EGO), "verbObtain");
		if (((ActorC64 *)a)->_miscflags & 0x40) {
			resetSentence(false);
			return false;
		}

		//attempt move to object
		if (verbMove(obj, objIndex, false))
			return true;

		if (didPickup && (prep == 1 || prep == 4))
			if (_activeVerb != 13 && _activeVerb != 14) {
				_v0ObjectInInventory = true;

				if (whereIsObject(obj) == WIO_INVENTORY)
					_activeInventory = obj;
				else
					resetSentence(false);

				_v0ObjectInInventory = false;
			}
	}

	return false;
}

int ScummEngine_v0::verbPrep(int object) {
	if (!_v0ObjectInInventory)
		_v0ObjectIndex = true;
	else
		_v0ObjectIndex = false;

	byte *ptr = getOBCDFromObject(object);
	_v0ObjectIndex = false;
	assert(ptr);
	return (*(ptr + 11) >> 5);
}

bool ScummEngine_v0::verbExecutes(int object, bool inventory) {
	_v0ObjectInInventory = inventory;
	int prep = verbPrep(object);

	if (prep == 2 || prep == 0) {
		return true;
	}

	return false;
}

bool ScummEngine_v0::verbExec() {
	int prep = 0;
	int entry = (_currentMode != 0 && _currentMode != 1) ? _activeVerb : 15;

	if ((!_activeInvExecute && _activeObject && getObjectIndex(_activeObject) == -1)) {
		resetSentence(false);
		return false;
	}

	// Lets try walk to the object
	if (_activeObject && _activeObjectIndex && !_activeObjectObtained && _currentMode != 0) {
		prep = verbPrep(_activeObjectIndex);

		if (verbObtain(_activeObject, _activeObjectIndex))
			return true;

		_activeObjectObtained = true;
	}

	// Attempt to obtain/reach object2
	if (_activeObject2 && _activeObject2Index && !_activeObject2Obtained && _currentMode != 0) {
		prep = verbPrep(_activeObject2Index);

		_v0ObjectInInventory = false;
		if (verbObtain(_activeObject2, _activeObject2Index))
			return true;

		if (prep != 1 && prep != 4) {
			_activeInventory = _activeObject;
			_activeObject = _activeObject2;
			_activeObjectIndex = _activeObject2Index;
			_activeObject2 = 0;
			_activeObject2Index = 0;
		}

		_activeObject2Obtained = true;
	}

	// Give-To
	if (_activeVerb == 3 && _activeInventory && _activeActor) {
		// FIXME: Actors need to turn and face each other
		if (verbMoveToActor(_activeActor)) {
			// Ignore verbs?
			Actor *a = derefActor(VAR(VAR_EGO), "verbExec");
			if (((ActorC64 *)a)->_miscflags & 0x40) {
				resetSentence(false);
				return false;
			}

			return true;
		}
		_v0ObjectInInventory = true;
		VAR(VAR_ACTIVE_ACTOR) = _activeActor;
		runObject(_activeInventory , 3);
		_v0ObjectInInventory = false;

		resetSentence(false);
		return false;
	}

	// Where we performing an action on an actor?
	if (_activeActor) {
		_v0ObjectIndex = true;
		runObject(_activeActor, entry);
		_v0ObjectIndex = false;
		_verbExecuting = false;

		resetSentence(false);
		return false;
	}

	// If we've finished walking (now near target), execute the action
	if (_activeObject && _activeObjectIndex && verbPrep(_activeObjectIndex) == 2) {
		_v0ObjectIndex = true;
		runObject(_activeObjectIndex, entry);
		_v0ObjectIndex = false;
		_verbExecuting = false;

		if ((_currentMode == 3 || _currentMode == 2) && _activeVerb == 13)
			return false;

		resetSentence(false);
		return false;
	}

	// We acted on an inventory item
	if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) {
		_v0ObjectInInventory = true;
		_activeObject = _activeInventory;
		runObject(_activeInventory, _activeVerb);

		_verbExecuting = false;

		if (_currentMode == 3 && _activeVerb == 13) {
			resetSentence(true);
			return false;
		}

		resetSentence(false);
		return false;
	}

	// Item not in inventory is executed
	if (_activeObject) {
		_v0ObjectIndex = true;
		runObject(_activeObjectIndex, entry);
		_v0ObjectIndex = false;
	} else if (_activeInventory) {
		// Not sure this is the correct way to do this,
		// however its working for most situations - segra
		if (verbExecutes(_activeInventory, true) == false) {
			if (_activeObject2 && _activeObject2Inv && verbExecutes(_activeObject2, true)) {
				_v0ObjectInInventory = true;

				_activeObject = _activeInventory;
				_activeInventory = _activeObject2;

				runObject(_activeObject, _activeVerb);
			} else {
				_v0ObjectInInventory = true;

				if (_activeObject2) {
					_activeObject = _activeObject2;

					runObject(_activeObject, _activeVerb);
				} else
					runObject(_activeInventory, _activeVerb);
			}
		} else {
			_v0ObjectInInventory = true;
			runObject(_activeInventory, _activeVerb);
		}
	}

	_verbExecuting = false;

	if (_activeVerb == 13) {
		resetSentence(true);
		return false;
	}

	resetSentence(false);

	return false;
}

void ScummEngine_v0::checkExecVerbs() {
	ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
	VirtScreen *zone = findVirtScreen(_mouse.y);

	// Is a verb currently executing
	if (_verbExecuting) {
		// Check if mouse click
		if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
			int over = findVerbAtPos(_mouse.x, _mouse.y);
			int act  = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
			int obj  = findObject(_virtualMouse.x, _virtualMouse.y);

			if (over && over != _activeVerb) {
				_activeVerb = over;
				_verbExecuting = false;
				return;
			}

			if (!obj && !act && !over) {
				resetSentence(false);
			} else {
				a->stopActorMoving();
			}
		} else {

			if (_verbExecuting && !verbExec())
				return;
		}
	}

	// What-Is selected, any object we hover over is selected, on mouse press we set to WalkTo
	if (_activeVerb == 15) {
		int obj = findObject(_virtualMouse.x, _virtualMouse.y);
		int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y);
		_activeObject = obj;
		_activeObjectIndex = objIdx;

		if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK))
			_activeVerb = 13;	// Walk-To

		return;
	}

	if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
		return;

	if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
		/* Check keypresses */
		// TODO
	} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
		if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
			// TODO
		} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
			int prevInventory = _activeInventory;
			int invOff = _inventoryOffset;

			// Click into V2 inventory
			checkV2Inventory(_mouse.x, _mouse.y);

			// Did the Inventory position changed (arrows pressed, do nothing)
			if (invOff != _inventoryOffset)
				return;

			// No inventory selected?
			if (!_activeInventory)
				return;

			// Did we just change the selected inventory item?
			if (prevInventory && prevInventory != _activeInventory && _activeInventory != _activeObject2) {
				_v0ObjectInInventory = true;
				int prep = verbPrep(_activeInventory);
				_v0ObjectInInventory = true;
				int prep2 = verbPrep(prevInventory);

				// Should the new inventory object remain as the secondary selected object
				// Or should the new inventory object become primary?
				if (prep != prep2 || prep != 1) {
					if (prep == 1 || prep == 3) {
						int tmp = _activeInventory;
						_activeInventory = prevInventory;
						prevInventory = tmp;
					}
				}

				// Setup object2
				_activeObject = 0;
				_activeInvExecute = true;
				_activeObject2Inv = true;
				_activeObject2 = _activeInventory;
				_activeInventory = prevInventory;
				return;
			}

			// is the new selected inventory the same as the last selected?, reset to previous if it is
			if (_activeInventory == _activeObject2)
				_activeInventory = prevInventory;

			// Inventory Selected changed
			if (prevInventory != _activeInventory)
				if (!_activeObject2 || prevInventory != _activeObject2)
					return;

			if (_activeVerb == 11 && !(((_activeObject || _activeInventory)) || !_activeObject2))
				return;
		} else {
			int over = findVerbAtPos(_mouse.x, _mouse.y);
			int act  = getActorFromPos(_virtualMouse.x, _virtualMouse.y);
			int obj  = findObject(_virtualMouse.x, _virtualMouse.y);
			int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y);

			// If we already have an object selected, and we just clicked an actor
			// Clear any object we may of also clicked on
			if ((_activeObject || _activeInventory) && act) {
				obj = 0;
				objIdx = 0;
			}

			if (a->_miscflags & 0x80) {
				if (_activeVerb != 7 && over != 7) {
					_activeVerb = 0;
					over = 0;
				}
			}

			// Handle New Kid verb options
			if (_activeVerb == 7 || over == 7) {
				// Disable New-Kid (in the secret lab)
				if (_currentMode == 2 || _currentMode == 0)
					return;

				if (_activeVerb == 7 && over) {
					_activeVerb = 13;
					switchActor(_verbs[over].verbid - 1);
					return;
				}

				setNewKidVerbs();
				_activeVerb = 7;

				return;
			}

			// Clicked on nothing, walk here?
			if (!over && !act && _activeVerb == 13 && !obj && _currentMode != 0) {
				// Clear all selected
				resetSentence(false);

				// 0xB31
				VAR(6) = _virtualMouse.x / V12_X_MULTIPLIER;
				VAR(7) = _virtualMouse.y / V12_Y_MULTIPLIER;

				if (zone->number == kMainVirtScreen) {
					// Ignore verbs?
					if (a->_miscflags & 0x40) {
						resetSentence(false);
						return;
					}
					a->stopActorMoving();
					a->startWalkActor(VAR(6), VAR(7), -1);
					_verbExecuting = true;
				}
				return;
			}

			// No new verb, use previous
			if (over == 0)
				over = _activeVerb;

			// No verb selected, use walk-to
			if (!_activeVerb)
				_activeVerb = over = 13;		// Walk-To

			// New verb selected
			if (_activeVerb != over) {
				_activeVerb = over;
				if (_activeVerb == 13) {
					resetSentence(false);
				}
				return;
			}

			// Only allowing targetting actors if its the GIVE/USE verb
			if (_activeVerb == 3 || _activeVerb == 11) {
				// Different actor selected?
				if (act) {
					if (_activeActor != act) {
						_activeActor = act;
						return;
					}
				}
			}

			if (obj && obj != _activeObject) {
				if (!_activeObject)
					if (_activeInventory)
						_activeInvExecute = true;

				// USE
				if (_activeVerb == 11 || _activeVerb == 8) {
					if (obj != _activeObject || obj != _activeObject2) {
						if (!_activeObject || _activeInventory) {
							_activeObject = obj;
							_activeObjectIndex = objIdx;
							return;
						} else {
							if (_activeObject2 != obj) {
								_activeObject2 = obj;
								_activeObject2Index = objIdx;
								return;
							}
						}
					}
				} else {
					a->stopActorMoving();

					_activeObject = obj;
					_activeObjectIndex = objIdx;

					if (_activeVerb != 13)
						return;

					//return;
				}
			}
		}

		_verbExecuting = true;

	}	// mouse k/b action
}

void ScummEngine::verbMouseOver(int verb) {
	// Don't do anything unless verbs are active
	if (_game.version <= 2 && !(_userState & 128))
		return;

	if (_game.id == GID_FT)
		return;

	if (_verbMouseOver != verb) {
		if (_verbs[_verbMouseOver].type != kImageVerbType) {
			drawVerb(_verbMouseOver, 0);
			_verbMouseOver = verb;
		}

		if (_verbs[verb].type != kImageVerbType && _verbs[verb].hicolor) {
			drawVerb(verb, 1);
			_verbMouseOver = verb;
		}
	}
}

int ScummEngine::findVerbAtPos(int x, int y) const {
	if (!_numVerbs)
		return 0;

	VerbSlot *vs;
	int i = _numVerbs - 1;

	vs = &_verbs[i];
	do {
		if (vs->curmode != 1 || !vs->verbid || vs->saveid || y < vs->curRect.top || y >= vs->curRect.bottom)
			continue;
		if (vs->center) {
			if (x < -(vs->curRect.right - 2 * vs->curRect.left) || x >= vs->curRect.right)
				continue;
		} else {
			if (x < vs->curRect.left || x >= vs->curRect.right)
				continue;
		}

		return i;
	} while (--vs, --i);

	return 0;
}

#ifdef ENABLE_SCUMM_7_8
void ScummEngine_v7::drawVerb(int verb, int mode) {
	VerbSlot *vs;

	if (!verb)
		return;

	vs = &_verbs[verb];

	if (!vs->saveid && vs->curmode && vs->verbid) {
		if (vs->type == kImageVerbType) {
			drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
			return;
		}

		uint8 color = vs->color;
		if (vs->curmode == 2)
			color = vs->dimcolor;
		else if (mode && vs->hicolor)
			color = vs->hicolor;

		const byte *msg = getResourceAddress(rtVerb, verb);
		if (!msg)
			return;

		// Convert the message, and skip a few remaining 0xFF codes (they
		// occur in FT; subtype 10, which is used for the speech associated
		// with the string).
		byte buf[384];
		convertMessageToString(msg, buf, sizeof(buf));
		msg = buf;
		while (*msg == 0xFF)
			msg += 4;

		// Set the specified charset id
		int oldID = _charset->getCurID();
		_charset->setCurID(vs->charset_nr);

		// Compute the text rect
		vs->curRect.right = 0;
		vs->curRect.bottom = 0;
		const byte *msg2 = msg;
		while (*msg2) {
			const int charWidth = _charset->getCharWidth(*msg2);
			const int charHeight = _charset->getCharHeight(*msg2);
			vs->curRect.right += charWidth;
			if (vs->curRect.bottom < charHeight)
				vs->curRect.bottom = charHeight;
			msg2++;
		}
		vs->curRect.right += vs->curRect.left;
		vs->curRect.bottom += vs->curRect.top;
		vs->oldRect = vs->curRect;

		const int maxWidth = _screenWidth - vs->curRect.left;
		if (_charset->getStringWidth(0, buf) > maxWidth && _game.version == 8) {
			byte tmpBuf[384];
			memcpy(tmpBuf, msg, 384);

			int len = resStrLen(tmpBuf) - 1;
			while (len >= 0) {
				if (tmpBuf[len] == ' ') {
					tmpBuf[len] = 0;
					if (_charset->getStringWidth(0, tmpBuf) <= maxWidth) {
						break;
					}
				}
				--len;
			}
			enqueueText(tmpBuf, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
			if (len >= 0) {
				enqueueText(&msg[len + 1], vs->curRect.left, vs->curRect.top + _verbLineSpacing, color, vs->charset_nr, vs->center);
				vs->curRect.bottom += _verbLineSpacing;
			}
		} else {
			enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
		}
		_charset->setCurID(oldID);
	}
}
#endif

void ScummEngine::drawVerb(int verb, int mode) {
	VerbSlot *vs;
	bool tmp;

	if (!verb)
		return;

	vs = &_verbs[verb];

	if (!vs->saveid && vs->curmode && vs->verbid) {
		if (vs->type == kImageVerbType) {
			drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
			return;
		}

		restoreVerbBG(verb);

		_string[4].charset = vs->charset_nr;
		_string[4].xpos = vs->curRect.left;
		_string[4].ypos = vs->curRect.top;
		_string[4].right = _screenWidth - 1;
		_string[4].center = vs->center;

		if (vs->curmode == 2)
			_string[4].color = vs->dimcolor;
		else if (mode && vs->hicolor)
			_string[4].color = vs->hicolor;
		else
			_string[4].color = vs->color;

		// FIXME For the future: Indy3 and under inv scrolling
		/*
		   if (verb >= 31 && verb <= 36)
		   verb += _inventoryOffset;
		 */

		const byte *msg = getResourceAddress(rtVerb, verb);
		if (!msg)
			return;

		tmp = _charset->_center;
		drawString(4, msg);
		_charset->_center = tmp;

		vs->curRect.right = _charset->_str.right;
		vs->curRect.bottom = _charset->_str.bottom;
		vs->oldRect = _charset->_str;
		_charset->_str.left = _charset->_str.right;
	} else if (_game.id != GID_FT) {
		restoreVerbBG(verb);
	}
}

void ScummEngine::restoreVerbBG(int verb) {

	VerbSlot *vs;

	vs = &_verbs[verb];
	uint8 col =
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
		((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 :
#endif
		vs->bkcolor;

	if (vs->oldRect.left != -1) {
		restoreBackground(vs->oldRect, col);
		vs->oldRect.left = -1;
	}
}

void ScummEngine::drawVerbBitmap(int verb, int x, int y) {
	VerbSlot *vst = &_verbs[verb];
	VirtScreen *vs;
	bool twobufs;
	const byte *imptr = 0;
	int ydiff, xstrip;
	int imgw, imgh;
	int i, tmp;
	byte *obim;
	uint32 size;

	if ((vs = findVirtScreen(y)) == NULL)
		return;

	_gdi->disableZBuffer();

	twobufs = vs->hasTwoBuffers;
	vs->hasTwoBuffers = false;

	xstrip = x / 8;
	ydiff = y - vs->topline;

	obim = getResourceAddress(rtVerb, verb);
	assert(obim);
	if (_game.features & GF_OLD_BUNDLE) {
		imgw = obim[0];
		imgh = obim[1] / 8;
		imptr = obim + 2;
	} else if (_game.features & GF_SMALL_HEADER) {
		size = READ_LE_UINT32(obim);

		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
			imgw = (*(obim + size + 10));
			imgh = (*(obim + size + 15)) / 8;
		} else {
			imgw = (*(obim + size + 11));
			imgh = (*(obim + size + 17)) / 8;
		}
		imptr = getObjectImage(obim, 1);
	} else {
		const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), obim);
		if (_game.version >= 7) {
			imgw = READ_LE_UINT16(&imhd->v7.width) / 8;
			imgh = READ_LE_UINT16(&imhd->v7.height) / 8;
		} else {
			imgw = READ_LE_UINT16(&imhd->old.width) / 8;
			imgh = READ_LE_UINT16(&imhd->old.height) / 8;
		}
		imptr = getObjectImage(obim, 1);
	}
	assert(imptr);

	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
		_gdi->_distaff = (vst->verbid != 54);
	}

	for (i = 0; i < imgw; i++) {
		tmp = xstrip + i;
		_gdi->drawBitmap(imptr, vs, tmp, ydiff, imgw * 8, imgh * 8, i, 1, Gdi::dbAllowMaskOr | Gdi::dbObjectMode);
	}

	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
		_gdi->_distaff = false;
	}

	vst->curRect.right = vst->curRect.left + imgw * 8;
	vst->curRect.bottom = vst->curRect.top + imgh * 8;
	vst->oldRect = vst->curRect;

	_gdi->enableZBuffer();

	vs->hasTwoBuffers = twobufs;
}

int ScummEngine::getVerbSlot(int id, int mode) const {
	int i;
	for (i = 1; i < _numVerbs; i++) {
		if (_verbs[i].verbid == id && _verbs[i].saveid == mode) {
			return i;
		}
	}
	return 0;
}

void ScummEngine::killVerb(int slot) {
	VerbSlot *vs;

	if (slot == 0)
		return;

	vs = &_verbs[slot];
	vs->verbid = 0;
	vs->curmode = 0;

	_res->nukeResource(rtVerb, slot);

	if (_game.version <= 6 && vs->saveid == 0) {
		drawVerb(slot, 0);
		verbMouseOver(0);
	}
	vs->saveid = 0;
}

void ScummEngine::setVerbObject(uint room, uint object, uint verb) {
	const byte *obimptr;
	const byte *obcdptr;
	uint32 size, size2;
	FindObjectInRoom foir;
	int i;

	if (_game.heversion >= 70) { // Windows titles. Here we always ignore room
		room = getObjectRoom(object);
	}

	if (whereIsObject(object) == WIO_FLOBJECT)
		error("Can't grab verb image from flobject");

	if (_game.features & GF_OLD_BUNDLE) {
		for (i = (_numLocalObjects-1); i > 0; i--) {
			if (_objs[i].obj_nr == object) {
				findObjectInRoom(&foir, foImageHeader, object, room);
				size = READ_LE_UINT16(foir.obim);
				byte *ptr = _res->createResource(rtVerb, verb, size + 2);
				obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
				ptr[0] = *(obcdptr + 9);	// Width
				ptr[1] = *(obcdptr + 15);	// Height
				memcpy(ptr + 2, foir.obim, size);
				return;
			}
		}
	} else if (_game.features & GF_SMALL_HEADER) {
		for (i = (_numLocalObjects-1); i > 0; i--) {
			if (_objs[i].obj_nr == object) {
				// FIXME: the only thing we need from the OBCD is the image size!
				// So we could use almost the same code (except for offsets)
				// as in the GF_OLD_BUNDLE code. But of course that would break save games
				// unless we insert special conversion code... <sigh>
				findObjectInRoom(&foir, foImageHeader, object, room);
				size = READ_LE_UINT32(foir.obim);
				obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
				size2 = READ_LE_UINT32(obcdptr);
				_res->createResource(rtVerb, verb, size + size2);
				obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
				obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
				memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
				memcpy(getResourceAddress(rtVerb, verb) + size, obcdptr, size2);
				return;
			}
		}
	} else {
		findObjectInRoom(&foir, foImageHeader, object, room);
		size = READ_BE_UINT32(foir.obim + 4);
		_res->createResource(rtVerb, verb, size);
		obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
		memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
	}
}

} // End of namespace Scumm