/* 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 "engines/kyra/kyra_lok.h"
#include "engines/kyra/animator_lok.h"

#include "common/system.h"

namespace Kyra {

int KyraEngine_LoK::findDuplicateItemShape(int shape) {
	static const uint8 dupTable[] = {
		0x48, 0x46, 0x49, 0x47, 0x4A, 0x46, 0x4B, 0x47,
		0x4C, 0x46, 0x4D, 0x47, 0x5B, 0x5A, 0x5C, 0x5A,
		0x5D, 0x5A, 0x5E, 0x5A, 0xFF, 0xFF
	};

	int i = 0;

	while (dupTable[i] != 0xFF) {
		if (dupTable[i] == shape)
			return dupTable[i + 1];
		i += 2;
	}
	return -1;
}

void KyraEngine_LoK::addToNoDropRects(int x, int y, int w, int h) {
	for (int rect = 0; rect < ARRAYSIZE(_noDropRects); ++rect) {
		if (_noDropRects[rect].top == -1) {
			_noDropRects[rect].left = x;
			_noDropRects[rect].top = y;
			_noDropRects[rect].right = x + w;
			_noDropRects[rect].bottom = y + h;
			break;
		}
	}
}

void KyraEngine_LoK::clearNoDropRects() {
	memset(_noDropRects, -1, sizeof(_noDropRects));
}

byte KyraEngine_LoK::findFreeItemInScene(int scene) {
	assert(scene < _roomTableSize);
	Room *room = &_roomTable[scene];

	for (int i = 0; i < 12; ++i) {
		if (room->itemsTable[i] == kItemNone)
			return i;
	}

	return 0xFF;
}

byte KyraEngine_LoK::findItemAtPos(int x, int y) {
	assert(_currentCharacter->sceneId < _roomTableSize);
	const int8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
	const uint16 *xposOffset = _roomTable[_currentCharacter->sceneId].itemsXPos;
	const uint8 *yposOffset = _roomTable[_currentCharacter->sceneId].itemsYPos;

	int highestYPos = -1;
	Item returnValue = kItemNone;

	for (int i = 0; i < 12; ++i) {
		if (*itemsTable != kItemNone) {
			int xpos = *xposOffset - 11;
			int xpos2 = *xposOffset + 10;
			if (x > xpos && x < xpos2) {
				assert(*itemsTable >= 0);
				int itemHeight = _itemHtDat[*itemsTable];
				int ypos = *yposOffset + 3;
				int ypos2 = ypos - itemHeight - 3;

				if (y > ypos2 && ypos > y) {
					if (highestYPos <= ypos) {
						returnValue = i;
						highestYPos = ypos;
					}
				}
			}
		}

		++xposOffset;
		++yposOffset;
		++itemsTable;
	}

	return returnValue;
}

void KyraEngine_LoK::placeItemInGenericMapScene(int item, int index) {
	static const uint16 itemMapSceneMinTable[] = {
		0x0000, 0x0011, 0x006D, 0x0025, 0x00C7, 0x0000
	};
	static const uint16 itemMapSceneMaxTable[] = {
		0x0010, 0x0024, 0x00C6, 0x006C, 0x00F5, 0x0000
	};

	int minValue = itemMapSceneMinTable[index];
	int maxValue = itemMapSceneMaxTable[index];

	while (true) {
		int room = _rnd.getRandomNumberRng(minValue, maxValue);
		assert(room < _roomTableSize);
		int nameIndex = _roomTable[room].nameIndex;
		bool placeItem = false;

		switch (nameIndex) {
		case 0:  case 1:   case 2:   case 3:
		case 4:  case 5:   case 6:   case 11:
		case 12: case 16:  case 17:  case 20:
		case 22: case 23:  case 25:  case 26:
		case 27: case 31:  case 33:  case 34:
		case 36: case 37:  case 58:  case 59:
		case 60: case 61:  case 83:  case 84:
		case 85: case 104: case 105: case 106:
			placeItem = true;
			break;

		case 51:
			if (room != 46)
				placeItem = true;
			break;

		default:
			break;
		}

		if (placeItem) {
			Room *roomPtr = &_roomTable[room];
			if (roomPtr->northExit == 0xFFFF && roomPtr->eastExit == 0xFFFF && roomPtr->southExit == 0xFFFF && roomPtr->westExit == 0xFFFF)
				placeItem = false;
			else if (_currentCharacter->sceneId == room)
				placeItem = false;
		}

		if (placeItem) {
			if (!processItemDrop(room, item, -1, -1, 2, 0))
				continue;
			break;
		}
	}
}

void KyraEngine_LoK::setHandItem(Item item) {
	setMouseItem(item);
	_itemInHand = item;
}

void KyraEngine_LoK::removeHandItem() {
	_screen->setMouseCursor(1, 1, _shapes[0]);
	_itemInHand = kItemNone;
}

void KyraEngine_LoK::setMouseItem(Item item) {
	if (item == kItemNone)
		_screen->setMouseCursor(1, 1, _shapes[6]);
	else
		_screen->setMouseCursor(8, 15, _shapes[216 + item]);
}

void KyraEngine_LoK::wipeDownMouseItem(int xpos, int ypos) {
	if (_itemInHand == kItemNone)
		return;

	xpos -= 8;
	ypos -= 15;
	_screen->hideMouse();
	backUpItemRect1(xpos, ypos);
	int y = ypos;
	int height = 16;

	while (height >= 0) {
		restoreItemRect1(xpos, ypos);
		_screen->setNewShapeHeight(_shapes[216 + _itemInHand], height);
		uint32 nextTime = _system->getMillis() + 1 * _tickLength;
		_screen->drawShape(0, _shapes[216 + _itemInHand], xpos, y, 0, 0);
		_screen->updateScreen();
		y += 2;
		height -= 2;
		delayUntil(nextTime);
	}
	restoreItemRect1(xpos, ypos);
	_screen->resetShapeHeight(_shapes[216 + _itemInHand]);
	removeHandItem();
	_screen->showMouse();
}

void KyraEngine_LoK::setupSceneItems() {
	uint16 sceneId = _currentCharacter->sceneId;
	assert(sceneId < _roomTableSize);
	Room *currentRoom = &_roomTable[sceneId];
	for (int i = 0; i < 12; ++i) {
		uint8 item = currentRoom->itemsTable[i];
		if (item == 0xFF || !currentRoom->needInit[i])
			continue;

		int xpos = 0;
		int ypos = 0;

		if (currentRoom->itemsXPos[i] == 0xFFFF) {
			xpos = currentRoom->itemsXPos[i] = _rnd.getRandomNumberRng(24, 296);
			ypos = currentRoom->itemsYPos[i] = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 130);
		} else {
			xpos = currentRoom->itemsXPos[i];
			ypos = currentRoom->itemsYPos[i];
		}

		_lastProcessedItem = i;

		int stop = 0;
		while (!stop) {
			stop = processItemDrop(sceneId, item, xpos, ypos, 3, 0);
			if (!stop) {
				xpos = currentRoom->itemsXPos[i] = _rnd.getRandomNumberRng(24, 296);
				ypos = currentRoom->itemsYPos[i] = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 130);
				if (countItemsInScene(sceneId) >= 12)
					break;
			} else {
				currentRoom->needInit[i] = 0;
			}
		}
	}
}

int KyraEngine_LoK::countItemsInScene(uint16 sceneId) {
	assert(sceneId < _roomTableSize);
	Room *currentRoom = &_roomTable[sceneId];

	int items = 0;

	for (int i = 0; i < 12; ++i) {
		if (currentRoom->itemsTable[i] != kItemNone)
			++items;
	}

	return items;
}

int KyraEngine_LoK::processItemDrop(uint16 sceneId, uint8 item, int x, int y, int unk1, int unk2) {
	int freeItem = -1;
	uint8 itemIndex = findItemAtPos(x, y);
	if (unk1)
		itemIndex = 0xFF;

	if (itemIndex != 0xFF) {
		exchangeItemWithMouseItem(sceneId, itemIndex);
		return 0;
	}

	assert(sceneId < _roomTableSize);
	Room *currentRoom = &_roomTable[sceneId];

	if (unk1 != 3) {
		for (int i = 0; i < 12; ++i) {
			if (currentRoom->itemsTable[i] == kItemNone) {
				freeItem = i;
				break;
			}
		}
	} else {
		freeItem = _lastProcessedItem;
	}

	if (freeItem == -1)
		return 0;

	if (sceneId != _currentCharacter->sceneId) {
		addItemToRoom(sceneId, item, freeItem, x, y);
		return 1;
	}

	int itemHeight = _itemHtDat[item];
	_lastProcessedItemHeight = itemHeight;

	if (x == -1)
		x = _rnd.getRandomNumberRng(16, 304);

	if (y == -1)
		y = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 135);

	int xpos = x;
	int ypos = y;
	int destY = -1;
	int destX = -1;
	int running = 1;

	while (running) {
		if ((_northExitHeight & 0xFF) <= ypos) {
			bool running2 = true;

			if (_screen->getDrawLayer(xpos, ypos) > 1) {
				if (((_northExitHeight >> 8) & 0xFF) != ypos)
					running2 = false;
			}

			if (_screen->getDrawLayer2(xpos, ypos, itemHeight) > 1) {
				if (((_northExitHeight >> 8) & 0xFF) != ypos)
					running2 = false;
			}

			if (!isDropable(xpos, ypos)) {
				if (((_northExitHeight >> 8) & 0xFF) != ypos)
					running2 = false;
			}

			int xpos2 = xpos;
			int xpos3 = xpos;

			while (running2) {
				if (isDropable(xpos2, ypos)) {
					if (_screen->getDrawLayer2(xpos2, ypos, itemHeight) < 7) {
						if (findItemAtPos(xpos2, ypos) == 0xFF) {
							destX = xpos2;
							destY = ypos;
							running = 0;
							running2 = false;
						}
					}
				}

				if (isDropable(xpos3, ypos)) {
					if (_screen->getDrawLayer2(xpos3, ypos, itemHeight) < 7) {
						if (findItemAtPos(xpos3, ypos) == 0xFF) {
							destX = xpos3;
							destY = ypos;
							running = 0;
							running2 = false;
						}
					}
				}

				if (!running2)
					continue;

				xpos2 -= 2;
				if (xpos2 < 16)
					xpos2 = 16;

				xpos3 += 2;
				if (xpos3 > 304)
					xpos3 = 304;

				if (xpos2 > 16)
					continue;
				if (xpos3 < 304)
					continue;
				running2 = false;
			}
		}

		if (((_northExitHeight >> 8) & 0xFF) == ypos) {
			running = 0;
			destY -= _rnd.getRandomNumberRng(0, 3);

			if ((_northExitHeight & 0xFF) < destY)
				continue;

			destY = (_northExitHeight & 0xFF) + 1;
			continue;
		}
		ypos += 2;
		if (((_northExitHeight >> 8) & 0xFF) >= ypos)
			continue;
		ypos = (_northExitHeight >> 8) & 0xFF;
	}

	if (destX == -1 || destY == -1)
		return 0;

	if (unk1 == 3) {
		currentRoom->itemsXPos[freeItem] = destX;
		currentRoom->itemsYPos[freeItem] = destY;
		return 1;
	}

	if (unk1 == 2)
		itemSpecialFX(x, y, item);

	if (unk1 == 0)
		removeHandItem();

	itemDropDown(x, y, destX, destY, freeItem, item);

	if (unk1 == 0 && unk2 != 0) {
		assert(_itemList && _droppedList);
		updateSentenceCommand(_itemList[getItemListIndex(item)], _droppedList[0], 179);
	}

	return 1;
}

void KyraEngine_LoK::exchangeItemWithMouseItem(uint16 sceneId, int itemIndex) {
	_animator->animRemoveGameItem(itemIndex);
	assert(sceneId < _roomTableSize);
	Room *currentRoom = &_roomTable[sceneId];

	int item = currentRoom->itemsTable[itemIndex];
	currentRoom->itemsTable[itemIndex] = _itemInHand;
	_itemInHand = item;
	_animator->animAddGameItem(itemIndex, sceneId);
	snd_playSoundEffect(53);

	setMouseItem(_itemInHand);
	assert(_itemList && _takenList);
	if (_flags.platform == Common::kPlatformAmiga)
		updateSentenceCommand(_itemList[getItemListIndex(_itemInHand)], _takenList[0], 179);
	else
		updateSentenceCommand(_itemList[getItemListIndex(_itemInHand)], _takenList[1], 179);
	clickEventHandler2();
}

void KyraEngine_LoK::addItemToRoom(uint16 sceneId, uint8 item, int itemIndex, int x, int y) {
	assert(sceneId < _roomTableSize);
	Room *currentRoom = &_roomTable[sceneId];
	currentRoom->itemsTable[itemIndex] = item;
	currentRoom->itemsXPos[itemIndex] = x;
	currentRoom->itemsYPos[itemIndex] = y;
	currentRoom->needInit[itemIndex] = 1;
}

int KyraEngine_LoK::checkNoDropRects(int x, int y) {
	if (_lastProcessedItemHeight < 1 || _lastProcessedItemHeight > 16)
		_lastProcessedItemHeight = 16;
	if (_noDropRects[0].left == -1)
		return 0;

	for (int i = 0; i < ARRAYSIZE(_noDropRects); ++i) {
		if (_noDropRects[i].left == -1)
			break;

		int xpos = _noDropRects[i].left;
		int ypos = _noDropRects[i].top;
		int xpos2 = _noDropRects[i].right;
		int ypos2 = _noDropRects[i].bottom;

		if (xpos > x + 16)
			continue;
		if (xpos2 <= x)
			continue;
		if (y < ypos)
			continue;
		if (ypos2 <= y - _lastProcessedItemHeight)
			continue;
		return 1;
	}

	return 0;
}

int KyraEngine_LoK::isDropable(int x, int y) {
	x -= 8;
	y -= 1;

	if (checkNoDropRects(x, y))
		return 0;

	for (int xpos = x; xpos < x + 16; ++xpos) {
		if (_screen->getShapeFlag1(xpos, y) == 0)
			return 0;
	}
	return 1;
}

void KyraEngine_LoK::itemDropDown(int x, int y, int destX, int destY, byte freeItem, int item) {
	assert(_currentCharacter->sceneId < _roomTableSize);
	Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
	if (x == destX && y == destY) {
		currentRoom->itemsXPos[freeItem] = destX;
		currentRoom->itemsYPos[freeItem] = destY;
		currentRoom->itemsTable[freeItem] = item;
		snd_playSoundEffect(0x32);
		_animator->animAddGameItem(freeItem, _currentCharacter->sceneId);
		return;
	}
	_screen->hideMouse();
	if (y <= destY) {
		int tempY = y;
		int addY = 2;
		int drawX = x - 8;
		int drawY = 0;

		backUpItemRect0(drawX, y - 16);

		while (tempY < destY) {
			restoreItemRect0(drawX, tempY - 16);
			tempY += addY;
			if (tempY > destY)
				tempY = destY;
			++addY;
			drawY = tempY - 16;
			backUpItemRect0(drawX, drawY);
			uint32 nextTime = _system->getMillis() + 1 * _tickLength;
			_screen->drawShape(0, _shapes[216 + item], drawX, drawY, 0, 0);
			_screen->updateScreen();
			delayUntil(nextTime);
		}

		bool skip = false;
		if (x == destX) {
			if (destY - y <= 16)
				skip = true;
		}

		if (!skip) {
			snd_playSoundEffect(0x47);
			if (addY < 6)
				addY = 6;

			int xDiff = (destX - x) << 4;
			xDiff /= addY;
			int startAddY = addY;
			addY >>= 1;
			if (destY - y <= 8)
				addY >>= 1;
			addY = -addY;
			int unkX = x << 4;
			while (--startAddY) {
				drawX = (unkX >> 4) - 8;
				drawY = tempY - 16;
				restoreItemRect0(drawX, drawY);
				tempY += addY;
				unkX += xDiff;
				if (tempY > destY)
					tempY = destY;
				++addY;
				drawX = (unkX >> 4) - 8;
				drawY = tempY - 16;
				backUpItemRect0(drawX, drawY);
				uint32 nextTime = _system->getMillis() + 1 * _tickLength;
				_screen->drawShape(0, _shapes[216 + item], drawX, drawY, 0, 0);
				_screen->updateScreen();
				delayUntil(nextTime);
			}
			restoreItemRect0(drawX, drawY);
		} else {
			restoreItemRect0(drawX, tempY - 16);
		}
	}
	currentRoom->itemsXPos[freeItem] = destX;
	currentRoom->itemsYPos[freeItem] = destY;
	currentRoom->itemsTable[freeItem] = item;
	snd_playSoundEffect(0x32);
	_animator->animAddGameItem(freeItem, _currentCharacter->sceneId);
	_screen->showMouse();
}

void KyraEngine_LoK::dropItem(int unk1, int item, int x, int y, int unk2) {
	if (processItemDrop(_currentCharacter->sceneId, item, x, y, unk1, unk2))
		return;
	snd_playSoundEffect(54);

	// Old floppy versions don't print warning messages and don't have the necessary string resources.
	// These versions will only play the warning sound effect.
	if (_flags.isOldFloppy && !_noDropList)
		return;

	assert(_noDropList);

	if (12 == countItemsInScene(_currentCharacter->sceneId))
		drawSentenceCommand(_noDropList[0], 6);
	else
		drawSentenceCommand(_noDropList[1], 6);
}

void KyraEngine_LoK::itemSpecialFX(int x, int y, int item) {
	if (item == 41)
		itemSpecialFX1(x, y, item);
	else
		itemSpecialFX2(x, y, item);
}

void KyraEngine_LoK::itemSpecialFX1(int x, int y, int item) {
	uint8 *shape = _shapes[216 + item];
	x -= 8;
	int startY = y;
	y -= 15;
	_screen->hideMouse();
	backUpItemRect0(x, y);
	for (int i = 1; i <= 16; ++i) {
		_screen->setNewShapeHeight(shape, i);
		--startY;
		restoreItemRect0(x, y);
		uint32 nextTime = _system->getMillis() + 1 * _tickLength;
		_screen->drawShape(0, shape, x, startY, 0, 0);
		_screen->updateScreen();
		delayUntil(nextTime);
	}
	restoreItemRect0(x, y);
	_screen->showMouse();
}

void KyraEngine_LoK::itemSpecialFX2(int x, int y, int item) {
	x -= 8;
	y -= 15;
	int yAdd = (int8)(((16 - _itemHtDat[item]) >> 1) & 0xFF);
	backUpItemRect0(x, y);
	if (item >= 80 && item <= 89)
		snd_playSoundEffect(55);

	for (int i = 201; i <= 205; ++i) {
		restoreItemRect0(x, y);
		uint32 nextTime = _system->getMillis() + 3 * _tickLength;
		_screen->drawShape(0, _shapes[i], x, y + yAdd, 0, 0);
		_screen->updateScreen();
		delayUntil(nextTime);
	}

	for (int i = 204; i >= 201; --i) {
		restoreItemRect0(x, y);
		uint32 nextTime = _system->getMillis() + 3 * _tickLength;
		_screen->drawShape(0, _shapes[216 + item], x, y, 0, 0);
		_screen->drawShape(0, _shapes[i], x, y + yAdd, 0, 0);
		_screen->updateScreen();
		delayUntil(nextTime);
	}
	restoreItemRect0(x, y);
}

void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
	int videoPageBackUp = _screen->_curPage;
	_screen->_curPage = 0;
	int x = 0, y = 0;

	if (itemPos == kItemNone) {
		Common::Point mouse = getMousePos();
		x = mouse.x - 12;
		y = mouse.y - 18;
	} else {
		x = _itemPosX[itemPos] - 4;
		y = _itemPosY[itemPos] - 3;
	}

	if (_itemInHand == kItemNone && itemPos == -1)
		return;

	int tableIndex = 0, loopStart = 0, maxLoops = 0;
	if (animIndex == 0) {
		tableIndex = _rnd.getRandomNumberRng(0, 5);
		loopStart = 35;
		maxLoops = 9;
	} else if (animIndex == 1) {
		tableIndex = _rnd.getRandomNumberRng(0, 11);
		loopStart = 115;
		maxLoops = 8;
	} else if (animIndex == 2) {
		tableIndex = 0;
		loopStart = 124;
		maxLoops = 4;
	} else {
		tableIndex = -1;
	}

	if (animIndex == 2)
		snd_playSoundEffect(0x5E);
	else
		snd_playSoundEffect(0x37);
	_screen->hideMouse();
	backUpItemRect1(x, y);

	for (int shape = _magicMouseItemStartFrame[animIndex]; shape <= _magicMouseItemEndFrame[animIndex]; ++shape) {
		restoreItemRect1(x, y);
		uint32 nextTime = _system->getMillis() + 4 * _tickLength;
		_screen->drawShape(0, _shapes[216 + _itemInHand], x + 4, y + 3, 0, 0);
		if (tableIndex == -1)
			_screen->drawShape(0, _shapes[shape], x, y, 0, 0);
		else
			specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
		_screen->updateScreen();
		delayUntil(nextTime);
	}

	if (itemPos != -1) {
		restoreItemRect1(x, y);
		_screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, 0);
		backUpItemRect1(x, y);
	}

	for (int shape = _magicMouseItemStartFrame2[animIndex]; shape <= _magicMouseItemEndFrame2[animIndex]; ++shape) {
		restoreItemRect1(x, y);
		uint32 nextTime = _system->getMillis() + 4 * _tickLength;
		_screen->drawShape(0, _shapes[216 + _itemInHand], x + 4, y + 3, 0, 0);
		if (tableIndex == -1)
			_screen->drawShape(0, _shapes[shape], x, y, 0, 0);
		else
			specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
		_screen->updateScreen();
		delayUntil(nextTime);
	}
	restoreItemRect1(x, y);

	if (itemPos == -1) {
		_screen->setMouseCursor(1, 1, _shapes[0]);
		_itemInHand = kItemNone;
	} else {
		_characterList[0].inventoryItems[itemPos] = kItemNone;
		_screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, 0);
	}

	_screen->showMouse();
	_screen->_curPage = videoPageBackUp;
}

void KyraEngine_LoK::magicInMouseItem(int animIndex, int item, int itemPos) {
	int videoPageBackUp = _screen->_curPage;
	_screen->_curPage = 0;
	int x = 0, y = 0;
	if (itemPos == -1) {
		Common::Point mouse = getMousePos();
		x = mouse.x - 12;
		y = mouse.y - 18;
	} else {
		x = _itemPosX[itemPos] - 4;
		y = _itemPosX[itemPos] - 3;
	}
	if (item < 0)
		return;

	int tableIndex = -1, loopStart = 0, maxLoops = 0;
	if (animIndex == 0) {
		tableIndex = _rnd.getRandomNumberRng(0, 5);
		loopStart = 35;
		maxLoops = 9;
	} else if (animIndex == 1) {
		tableIndex = _rnd.getRandomNumberRng(0, 11);
		loopStart = 115;
		maxLoops = 8;
	} else if (animIndex == 2) {
		tableIndex = 0;
		loopStart = 124;
		maxLoops = 4;
	}

	_screen->hideMouse();
	backUpItemRect1(x, y);
	if (animIndex == 2)
		snd_playSoundEffect(0x5E);
	else
		snd_playSoundEffect(0x37);

	for (int shape = _magicMouseItemStartFrame[animIndex]; shape <= _magicMouseItemEndFrame[animIndex]; ++shape) {
		restoreItemRect1(x, y);
		uint32 nextTime = _system->getMillis() + 4 * _tickLength;
		if (tableIndex == -1)
			_screen->drawShape(0, _shapes[shape], x, y, 0, 0);
		else
			specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
		_screen->updateScreen();
		delayUntil(nextTime);
	}

	for (int shape = _magicMouseItemStartFrame2[animIndex]; shape <= _magicMouseItemEndFrame2[animIndex]; ++shape) {
		restoreItemRect1(x, y);
		uint32 nextTime = _system->getMillis() + 4 * _tickLength;
		if (tableIndex == -1)
			_screen->drawShape(0, _shapes[shape], x, y, 0, 0);
		else
			specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
		_screen->updateScreen();
		delayUntil(nextTime);
	}
	restoreItemRect1(x, y);
	if (itemPos == -1) {
		_screen->setMouseCursor(8, 15, _shapes[216 + item]);
		_itemInHand = item;
	} else {
		_characterList[0].inventoryItems[itemPos] = item;
		_screen->drawShape(0, _shapes[216 + item], _itemPosX[itemPos], _itemPosY[itemPos], 0, 0);
	}
	_screen->showMouse();
	_screen->_curPage = videoPageBackUp;
}

void KyraEngine_LoK::specialMouseItemFX(int shape, int x, int y, int animIndex, int tableIndex, int loopStart, int maxLoops) {
	static const uint8 table1[] = {
		0x23, 0x45, 0x55, 0x72, 0x84, 0xCF, 0x00, 0x00
	};
	static const uint8 table2[] = {
		0x73, 0xB5, 0x80, 0x21, 0x13, 0x39, 0x45, 0x55, 0x62, 0xB4, 0xCF, 0xD8
	};
	static const uint8 table3[] = {
		0x7C, 0xD0, 0x74, 0x84, 0x87, 0x00, 0x00, 0x00
	};
	int tableValue = 0;
	if (animIndex == 0)
		tableValue = table1[tableIndex];
	else if (animIndex == 1)
		tableValue = table2[tableIndex];
	else if (animIndex == 2)
		tableValue = table3[tableIndex];
	else
		return;
	processSpecialMouseItemFX(shape, x, y, tableValue, loopStart, maxLoops);
}

void KyraEngine_LoK::processSpecialMouseItemFX(int shape, int x, int y, int tableValue, int loopStart, int maxLoops) {
	uint8 shapeColorTable[16];
	uint8 *shapePtr = _shapes[shape] + 10;
	if (_flags.useAltShapeHeader)
		shapePtr += 2;

	for (int i = 0; i < 16; ++i)
		shapeColorTable[i] = shapePtr[i];

	for (int i = loopStart; i < loopStart + maxLoops; ++i) {
		for (int i2 = 0; i2 < 16; ++i2) {
			if (shapePtr[i2] == i)
				shapeColorTable[i2] = (i + tableValue) - loopStart;
		}
	}
	_screen->drawShape(0, _shapes[shape], x, y, 0, 0x8000, shapeColorTable);
}

void KyraEngine_LoK::updatePlayerItemsForScene() {
	if (_itemInHand >= 29 && _itemInHand < 33) {
		++_itemInHand;
		if (_itemInHand > 33)
			_itemInHand = 33;
		_screen->setMouseCursor(8, 15, _shapes[216 + _itemInHand]);
	}

	bool redraw = false;
	for (int i = 0; i < 10; ++i) {
		uint8 item = _currentCharacter->inventoryItems[i];
		if (item >= 29 && item < 33) {
			++item;
			if (item > 33)
				item = 33;
			_currentCharacter->inventoryItems[i] = item;
			redraw = true;
		}
	}

	if (redraw) {
		redrawInventory(0);
	}

	if (_itemInHand == 33)
		magicOutMouseItem(2, -1);

	_screen->hideMouse();
	for (int i = 0; i < 10; ++i) {
		uint8 item = _currentCharacter->inventoryItems[i];
		if (item == 33)
			magicOutMouseItem(2, i);
	}
	_screen->showMouse();
}

void KyraEngine_LoK::redrawInventory(int page) {
	int videoPageBackUp = _screen->_curPage;
	_screen->_curPage = page;
	for (int i = 0; i < 10; ++i) {
		_screen->fillRect(_itemPosX[i], _itemPosY[i], _itemPosX[i] + 15, _itemPosY[i] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, page);

		if (_currentCharacter->inventoryItems[i] != kItemNone) {
			uint8 item = _currentCharacter->inventoryItems[i];
			_screen->drawShape(page, _shapes[216 + item], _itemPosX[i], _itemPosY[i], 0, 0);
		}
	}
	_screen->_curPage = videoPageBackUp;
	_screen->updateScreen();
}

void KyraEngine_LoK::backUpItemRect0(int xpos, int ypos) {
	_screen->rectClip(xpos, ypos, 3 << 3, 24);
	_screen->copyRegionToBuffer(_screen->_curPage, xpos, ypos, 3 << 3, 24, _itemBkgBackUp[0]);
}

void KyraEngine_LoK::restoreItemRect0(int xpos, int ypos) {
	_screen->rectClip(xpos, ypos, 3 << 3, 24);
	_screen->copyBlockToPage(_screen->_curPage, xpos, ypos, 3 << 3, 24, _itemBkgBackUp[0]);
}

void KyraEngine_LoK::backUpItemRect1(int xpos, int ypos) {
	_screen->rectClip(xpos, ypos, 4 << 3, 32);
	_screen->copyRegionToBuffer(_screen->_curPage, xpos, ypos, 4 << 3, 32, _itemBkgBackUp[1]);
}

void KyraEngine_LoK::restoreItemRect1(int xpos, int ypos) {
	_screen->rectClip(xpos, ypos, 4 << 3, 32);
	_screen->copyBlockToPage(_screen->_curPage, xpos, ypos, 4 << 3, 32, _itemBkgBackUp[1]);
}

int KyraEngine_LoK::getItemListIndex(Item item) {
	if (_flags.platform != Common::kPlatformAmiga)
		return item;

	// "Unknown item" is at 81.
	if (item == kItemNone)
		return 81;
	// The first item names are mapped directly
	else if (item <= 28)
		return item;
	// There's only one string for "Fireberries"
	else if (item >= 29 && item <= 33)
		return 29;
	// Correct offsets
	else if (item >= 34 && item <= 59)
		return item - 4;
	// There's only one string for "Red Potion"
	else if (item >= 60 && item <= 61)
		return 56;
	// There's only one string for "Blue Potion"
	else if (item >= 62 && item <= 63)
		return 57;
	// There's only one string for "Yellow Potion"
	else if (item >= 64 && item <= 65)
		return 58;
	// Correct offsets
	else if (item >= 66 && item <= 69)
		return item - 7;
	// There's only one string for "Fresh Water"
	else if (item >= 70 && item <= 71)
		return 63;
	// There's only one string for "Salt Water"
	else if (item >= 72 && item <= 73)
		return 64;
	// There's only one string for "Mineral Water"
	else if (item >= 74 && item <= 75)
		return 65;
	// There's only one string for "Magical Water"
	else if (item >= 76 && item <= 77)
		return 66;
	// There's only one string for "Empty Flask"
	else if (item >= 78 && item <= 79)
		return 67;
	// There's only one string for "Scroll"
	else if (item >= 80 && item <= 89)
		return 68;
	// There's only one string for "Parchment scrap"
	else if (item >= 90 && item <= 94)
		return 69;
	// Correct offsets
	else if (item >= 95)
		return item - 25;

	// This should never happen, but still GCC warns about it.
	return 81;
}

} // End of namespace Kyra
