/* 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/resource.h"
#include "engines/kyra/sound.h"
#include "engines/kyra/sprites.h"
#include "engines/kyra/animator_lok.h"
#include "engines/kyra/timer.h"

#include "common/system.h"

namespace Kyra {

void KyraEngine_LoK::enterNewScene(int sceneId, int facing, int unk1, int unk2, int brandonAlive) {
	int unkVar1 = 1;
	_screen->hideMouse();

	// TODO: Check how the original handled sfx still playing
	_sound->stopAllSoundEffects();

	if (_flags.platform == Common::kPlatformFMTowns) {
		int newSfxFile = -1;
		if (_currentCharacter->sceneId == 7 && sceneId == 24)
			newSfxFile = 2;
		else if (_currentCharacter->sceneId == 25 && sceneId == 109)
			newSfxFile = 3;
		else if (_currentCharacter->sceneId == 120 && sceneId == 37)
			newSfxFile = 4;
		else if (_currentCharacter->sceneId == 52 && sceneId == 199)
			newSfxFile = 5;
		else if (_currentCharacter->sceneId == 37 && sceneId == 120)
			newSfxFile = 3;
		else if (_currentCharacter->sceneId == 109 && sceneId == 25)
			newSfxFile = 2;
		else if (_currentCharacter->sceneId == 24 && sceneId == 7)
			newSfxFile = 1;

		if (newSfxFile != -1) {
			_curSfxFile = newSfxFile;
			_sound->loadSoundFile(_curSfxFile);
		}
	}

	switch (_currentCharacter->sceneId) {
	case 1:
		if (sceneId == 0) {
			moveCharacterToPos(0, 0, _currentCharacter->x1, 84);
			unkVar1 = 0;
		}
		break;

	case 3:
		if (sceneId == 2) {
			moveCharacterToPos(0, 6, 155, _currentCharacter->y1);
			unkVar1 = 0;
		}
		break;

	case 26:
		if (sceneId == 27) {
			moveCharacterToPos(0, 6, 155, _currentCharacter->y1);
			unkVar1 = 0;
		}
		break;

	case 44:
		if (sceneId == 45) {
			moveCharacterToPos(0, 2, 192, _currentCharacter->y1);
			unkVar1 = 0;
		}
		break;

	default:
		break;
	}

	if (unkVar1 && unk1) {
		int xpos = _currentCharacter->x1;
		int ypos = _currentCharacter->y1;
		switch (facing) {
		case 0:
			ypos = _currentCharacter->y1 - 6;
			break;

		case 2:
			xpos = 336;
			break;

		case 4:
			ypos = 143;
			break;

		case 6:
			xpos = -16;
			break;

		default:
			break;
		}

		moveCharacterToPos(0, facing, xpos, ypos);
	}

	for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i)
		_movieObjects[i]->close();

	if (!brandonAlive) {
		_emc->init(&_scriptClick, &_scriptClickData);
		_emc->start(&_scriptClick, 5);
		while (_emc->isValid(&_scriptClick))
			_emc->run(&_scriptClick);
	}

	memset(_entranceMouseCursorTracks, 0xFF, sizeof(_entranceMouseCursorTracks));
	_currentCharacter->sceneId = sceneId;

	assert(sceneId < _roomTableSize);
	assert(_roomTable[sceneId].nameIndex < _roomFilenameTableSize);

	Room *currentRoom = &_roomTable[sceneId];

	setupSceneResource(sceneId);

	_currentRoom = sceneId;

	int tableId = _roomTable[sceneId].nameIndex;
	char fileNameBuffer[32];
	strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
	strcat(fileNameBuffer, ".DAT");
	_sprites->loadDat(fileNameBuffer, _sceneExits);
	_sprites->setupSceneAnims();
	_emc->unload(&_scriptClickData);
	loadSceneMsc();

	_walkBlockNorth = currentRoom->northExit;
	_walkBlockEast = currentRoom->eastExit;
	_walkBlockSouth = currentRoom->southExit;
	_walkBlockWest = currentRoom->westExit;

	if (_walkBlockNorth == 0xFFFF)
		_screen->blockOutRegion(0, 0, 320, (_northExitHeight & 0xFF) + 3);
	if (_walkBlockEast == 0xFFFF)
		_screen->blockOutRegion(312, 0, 8, 139);
	if (_walkBlockSouth == 0xFFFF)
		_screen->blockOutRegion(0, 135, 320, 8);
	if (_walkBlockWest == 0xFFFF)
		_screen->blockOutRegion(0, 0, 8, 139);

	if (!brandonAlive)
		updatePlayerItemsForScene();

	startSceneScript(brandonAlive);
	setupSceneItems();

	initSceneData(facing, unk2, brandonAlive);

	_loopFlag2 = 0;
	_screen->showMouse();
	if (!brandonAlive)
		seq_poisonDeathNow(0);
	updateMousePointer(true);
	_changedScene = true;
}

void KyraEngine_LoK::transcendScenes(int roomIndex, int roomName) {
	assert(roomIndex < _roomTableSize);

	if (_flags.isTalkie) {
		char file[32];
		assert(roomIndex < _roomTableSize);
		int tableId = _roomTable[roomIndex].nameIndex;
		assert(tableId < _roomFilenameTableSize);
		strcpy(file, _roomFilenameTable[tableId]);
		strcat(file, ".VRM");
		_res->unloadPakFile(file);
	}

	_roomTable[roomIndex].nameIndex = roomName;
	_unkScreenVar2 = 1;
	_unkScreenVar3 = 1;
	_unkScreenVar1 = 0;
	_brandonPosX = _currentCharacter->x1;
	_brandonPosY = _currentCharacter->y1;
	enterNewScene(roomIndex, _currentCharacter->facing, 0, 0, 0);
	_unkScreenVar1 = 1;
	_unkScreenVar2 = 0;
	_unkScreenVar3 = 0;
}

void KyraEngine_LoK::setSceneFile(int roomIndex, int roomName) {
	assert(roomIndex < _roomTableSize);
	_roomTable[roomIndex].nameIndex = roomName;
}

void KyraEngine_LoK::moveCharacterToPos(int character, int facing, int xpos, int ypos) {
	Character *ch = &_characterList[character];
	ch->facing = facing;
	_screen->hideMouse();
	xpos = (int16)(xpos & 0xFFFC);
	ypos = (int16)(ypos & 0xFFFE);
	_timer->disable(19);
	_timer->disable(14);
	_timer->disable(18);
	uint32 nextFrame = 0;

	switch (facing) {
	case 0:
		while (ypos < ch->y1) {
			nextFrame = _timer->getDelay(5 + character) * _tickLength + _system->getMillis();
			setCharacterPositionWithUpdate(character);
			delayUntil(nextFrame, true);
		}
		break;

	case 2:
		while (ch->x1 < xpos) {
			nextFrame = _timer->getDelay(5 + character) * _tickLength + _system->getMillis();
			setCharacterPositionWithUpdate(character);
			delayUntil(nextFrame, true);
		}
		break;

	case 4:
		while (ypos > ch->y1) {
			nextFrame = _timer->getDelay(5 + character) * _tickLength + _system->getMillis();
			setCharacterPositionWithUpdate(character);
			delayUntil(nextFrame, true);
		}
		break;

	case 6:
		while (ch->x1 > xpos) {
			nextFrame = _timer->getDelay(5 + character) * _tickLength + _system->getMillis();
			setCharacterPositionWithUpdate(character);
			delayUntil(nextFrame, true);
		}
		break;

	default:
		break;
	}

	_timer->enable(19);
	_timer->enable(14);
	_timer->enable(18);
	_screen->showMouse();
}

void KyraEngine_LoK::setCharacterPositionWithUpdate(int character) {
	setCharacterPosition(character, 0);
	_sprites->updateSceneAnims();
	_timer->update();
	_animator->updateAllObjectShapes();
	updateTextFade();

	if (_currentCharacter->sceneId == 210)
		updateKyragemFading();
}

int KyraEngine_LoK::setCharacterPosition(int character, int *facingTable) {
	if (character == 0) {
		_currentCharacter->x1 += _charAddXPosTable[_currentCharacter->facing];
		_currentCharacter->y1 += _charAddYPosTable[_currentCharacter->facing];
		setCharacterPositionHelper(0, facingTable);
		return 1;
	} else {
		_characterList[character].x1 += _charAddXPosTable[_characterList[character].facing];
		_characterList[character].y1 += _charAddYPosTable[_characterList[character].facing];
		if (_characterList[character].sceneId == _currentCharacter->sceneId)
			setCharacterPositionHelper(character, 0);
	}
	return 0;
}

void KyraEngine_LoK::setCharacterPositionHelper(int character, int *facingTable) {
	Character *ch = &_characterList[character];
	++ch->currentAnimFrame;
	int facing = ch->facing;
	if (facingTable) {
		if (*facingTable != *(facingTable - 1)) {
			if (*(facingTable - 1) == *(facingTable + 1)) {
				facing = getOppositeFacingDirection(*(facingTable - 1));
				*facingTable = *(facingTable - 1);
			}
		}
	}

	if (facing == 0) {
		++_characterFacingZeroCount[character];
	} else {
		bool resetTables = false;
		if (facing != 7) {
			if (facing - 1 != 0) {
				if (facing != 4) {
					if (facing == 3 || facing == 5) {
						if (_characterFacingFourCount[character] > 2)
							facing = 4;
						resetTables = true;
					}
				} else {
					++_characterFacingFourCount[character];
				}
			} else {
				if (_characterFacingZeroCount[character] > 2)
					facing = 0;
				resetTables = true;
			}
		} else {
			if (_characterFacingZeroCount[character] > 2)
				facing = 0;
			resetTables = true;
		}

		if (resetTables) {
			_characterFacingZeroCount[character] = 0;
			_characterFacingFourCount[character] = 0;
		}
	}

	static const uint16 maxAnimationFrame[] = {
		0x000F, 0x0031, 0x0055, 0x0000, 0x0000, 0x0000,
		0x0008, 0x002A, 0x004E, 0x0000, 0x0000, 0x0000,
		0x0022, 0x0046, 0x006A, 0x0000, 0x0000, 0x0000,
		0x001D, 0x0041, 0x0065, 0x0000, 0x0000, 0x0000,
		0x001F, 0x0043, 0x0067, 0x0000, 0x0000, 0x0000,
		0x0028, 0x004C, 0x0070, 0x0000, 0x0000, 0x0000,
		0x0023, 0x0047, 0x006B, 0x0000, 0x0000, 0x0000
	};

	if (facing == 0) {
		if (maxAnimationFrame[36 + character] > ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[36 + character];
		if (maxAnimationFrame[30 + character] < ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[36 + character];
	} else if (facing == 4) {
		if (maxAnimationFrame[18 + character] > ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[18 + character];
		if (maxAnimationFrame[12 + character] < ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[18 + character];
	} else {
		if (maxAnimationFrame[18 + character] < ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[30 + character];
		if (maxAnimationFrame[character] == ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[6 + character];
		if (maxAnimationFrame[character] < ch->currentAnimFrame)
			ch->currentAnimFrame = maxAnimationFrame[6 + character] + 2;
	}

	if (character == 0 && (_brandonStatusBit & 0x10))
		ch->currentAnimFrame = 88;

	_animator->animRefreshNPC(character);
}

void KyraEngine_LoK::loadSceneMsc() {
	assert(_currentCharacter->sceneId < _roomTableSize);
	int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
	assert(tableId < _roomFilenameTableSize);
	char fileNameBuffer[32];
	strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
	strcat(fileNameBuffer, ".MSC");
	_screen->fillRect(0, 0, 319, 199, 0, 5);
	_res->exists(fileNameBuffer, true);
	_screen->loadBitmap(fileNameBuffer, 3, 5, 0);
}

void KyraEngine_LoK::startSceneScript(int brandonAlive) {
	assert(_currentCharacter->sceneId < _roomTableSize);
	int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
	assert(tableId < _roomFilenameTableSize);
	char fileNameBuffer[32];
	strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
	strcat(fileNameBuffer, ".CPS");
	_screen->clearPage(3);
	_res->exists(fileNameBuffer, true);
	// FIXME: check this hack for amiga version
	_screen->loadBitmap(fileNameBuffer, 3, 3, (_flags.platform == Common::kPlatformAmiga ? &_screen->getPalette(0) : 0));
	_sprites->loadSceneShapes();
	_exitListPtr = 0;

	_scaleMode = 1;
	for (int i = 0; i < 145; ++i)
		_scaleTable[i] = 256;

	clearNoDropRects();
	_emc->init(&_scriptClick, &_scriptClickData);
	strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
	strcat(fileNameBuffer, ".EMC");
	_res->exists(fileNameBuffer, true);
	_emc->unload(&_scriptClickData);
	_emc->load(fileNameBuffer, &_scriptClickData, &_opcodes);
	_emc->start(&_scriptClick, 0);
	_scriptClick.regs[0] = _currentCharacter->sceneId;
	_scriptClick.regs[7] = brandonAlive;

	while (_emc->isValid(&_scriptClick))
		_emc->run(&_scriptClick);
}

void KyraEngine_LoK::initSceneData(int facing, int unk1, int brandonAlive) {
	int16 xpos2 = 0;
	int setFacing = 1;

	int16 xpos = 0, ypos = 0;

	if (_brandonPosX == -1 && _brandonPosY == -1) {
		switch (facing + 1) {
		case 0:
			xpos = ypos = -1;
			break;

		case 1: case 2: case 8:
			xpos = _sceneExits.southXPos;
			ypos = _sceneExits.southYPos;
			break;

		case 3:
			xpos = _sceneExits.westXPos;
			ypos = _sceneExits.westYPos;
			break;

		case 4: case 5: case 6:
			xpos = _sceneExits.northXPos;
			ypos = _sceneExits.northYPos;
			break;

		case 7:
			xpos = _sceneExits.eastXPos;
			ypos = _sceneExits.eastYPos;
			break;

		default:
			break;
		}

		if ((uint8)(_northExitHeight & 0xFF) + 2 >= ypos)
			ypos = (_northExitHeight & 0xFF) + 4;
		if (xpos >= 308)
			xpos = 304;
		if ((uint8)(_northExitHeight >> 8) - 2 <= ypos)
			ypos = (_northExitHeight >> 8) - 4;
		if (xpos <= 12)
			xpos = 16;
	}

	if (_brandonPosX > -1)
		xpos = _brandonPosX;
	if (_brandonPosY > -1)
		ypos = _brandonPosY;

	int16 ypos2 = 0;
	if (_brandonPosX > -1 && _brandonPosY > -1) {
		switch (_currentCharacter->sceneId) {
		case 1:
			_currentCharacter->x1 = xpos;
			_currentCharacter->x2 = xpos;
			_currentCharacter->y1 = ypos;
			_currentCharacter->y2 = ypos;
			facing = 4;
			xpos2 = 192;
			ypos2 = 104;
			setFacing = 0;
			unk1 = 1;
			break;

		case 3:
			_currentCharacter->x1 = xpos;
			_currentCharacter->x2 = xpos;
			_currentCharacter->y1 = ypos;
			_currentCharacter->y2 = ypos;
			facing = 2;
			xpos2 = 204;
			ypos2 = 94;
			setFacing = 0;
			unk1 = 1;
			break;

		case 26:
			_currentCharacter->x1 = xpos;
			_currentCharacter->x2 = xpos;
			_currentCharacter->y1 = ypos;
			_currentCharacter->y2 = ypos;
			facing = 2;
			xpos2 = 192;
			ypos2 = 128;
			setFacing = 0;
			unk1 = 1;
			break;

		case 44:
			_currentCharacter->x1 = xpos;
			_currentCharacter->x2 = xpos;
			_currentCharacter->y1 = ypos;
			_currentCharacter->y2 = ypos;
			facing = 6;
			xpos2 = 156;
			ypos2 = 96;
			setFacing = 0;
			unk1 = 1;
			break;

		case 37:
			_currentCharacter->x1 = xpos;
			_currentCharacter->x2 = xpos;
			_currentCharacter->y1 = ypos;
			_currentCharacter->y2 = ypos;
			facing = 2;
			xpos2 = 148;
			ypos2 = 114;
			setFacing = 0;
			unk1 = 1;
			break;

		default:
			break;
		}
	}

	_brandonPosX = _brandonPosY = -1;

	if (unk1 && setFacing) {
		ypos2 = ypos;
		xpos2 = xpos;
		switch (facing) {
		case 0:
			ypos = 142;
			break;

		case 2:
			xpos = -16;
			break;

		case 4:
			ypos = (uint8)(_northExitHeight & 0xFF) - 4;
			break;

		case 6:
			xpos = 336;
			break;

		default:
			break;
		}
	}

	xpos2 = (int16)(xpos2 & 0xFFFC);
	ypos2 = (int16)(ypos2 & 0xFFFE);
	xpos = (int16)(xpos & 0xFFFC);
	ypos = (int16)(ypos & 0xFFFE);
	_currentCharacter->facing = facing;
	_currentCharacter->x1 = xpos;
	_currentCharacter->x2 = xpos;
	_currentCharacter->y1 = ypos;
	_currentCharacter->y2 = ypos;

	initSceneObjectList(brandonAlive);

	if (unk1 && brandonAlive == 0)
		moveCharacterToPos(0, facing, xpos2, ypos2);

	_scriptClick.regs[4] = _itemInHand;
	_scriptClick.regs[7] = brandonAlive;
	_emc->start(&_scriptClick, 3);
	while (_emc->isValid(&_scriptClick))
		_emc->run(&_scriptClick);
}

void KyraEngine_LoK::initSceneObjectList(int brandonAlive) {
	for (int i = 0; i < 28; ++i)
		_animator->actors()[i].active = 0;

	int startAnimFrame = 0;

	Animator_LoK::AnimObject *curAnimState = _animator->actors();
	curAnimState->active = 1;
	curAnimState->drawY = _currentCharacter->y1;
	curAnimState->sceneAnimPtr = _shapes[_currentCharacter->currentAnimFrame];
	curAnimState->animFrameNumber = _currentCharacter->currentAnimFrame;
	startAnimFrame = _currentCharacter->currentAnimFrame - 7;
	int xOffset = _defaultShapeTable[startAnimFrame].xOffset;
	int yOffset = _defaultShapeTable[startAnimFrame].yOffset;

	if (_scaleMode) {
		curAnimState->x1 = _currentCharacter->x1;
		curAnimState->y1 = _currentCharacter->y1;

		_animator->_brandonScaleX = _scaleTable[_currentCharacter->y1];
		_animator->_brandonScaleY = _scaleTable[_currentCharacter->y1];

		curAnimState->x1 += (_animator->_brandonScaleX * xOffset) >> 8;
		curAnimState->y1 += (_animator->_brandonScaleY * yOffset) >> 8;
	} else {
		curAnimState->x1 = _currentCharacter->x1 + xOffset;
		curAnimState->y1 = _currentCharacter->y1 + yOffset;
	}

	curAnimState->x2 = curAnimState->x1;
	curAnimState->y2 = curAnimState->y1;
	curAnimState->refreshFlag = 1;
	curAnimState->bkgdChangeFlag = 1;
	_animator->clearQueue();
	_animator->addObjectToQueue(curAnimState);

	int listAdded = 0;
	int addedObjects = 1;

	for (int i = 1; i < 5; ++i) {
		Character *ch = &_characterList[i];
		curAnimState = &_animator->actors()[addedObjects];
		if (ch->sceneId != _currentCharacter->sceneId) {
			curAnimState->active = 0;
			curAnimState->refreshFlag = 0;
			curAnimState->bkgdChangeFlag = 0;
			++addedObjects;
			continue;
		}

		curAnimState->drawY = ch->y1;
		curAnimState->sceneAnimPtr = _shapes[ch->currentAnimFrame];
		curAnimState->animFrameNumber = ch->currentAnimFrame;
		startAnimFrame = ch->currentAnimFrame - 7;
		xOffset = _defaultShapeTable[startAnimFrame].xOffset;
		yOffset = _defaultShapeTable[startAnimFrame].yOffset;
		if (_scaleMode) {
			curAnimState->x1 = ch->x1;
			curAnimState->y1 = ch->y1;

			_animator->_brandonScaleX = _scaleTable[ch->y1];
			_animator->_brandonScaleY = _scaleTable[ch->y1];

			curAnimState->x1 += (_animator->_brandonScaleX * xOffset) >> 8;
			curAnimState->y1 += (_animator->_brandonScaleY * yOffset) >> 8;
		} else {
			curAnimState->x1 = ch->x1 + xOffset;
			curAnimState->y1 = ch->y1 + yOffset;
		}
		curAnimState->x2 = curAnimState->x1;
		curAnimState->y2 = curAnimState->y1;
		curAnimState->active = 1;
		curAnimState->refreshFlag = 1;
		curAnimState->bkgdChangeFlag = 1;

		if (ch->facing >= 1 && ch->facing <= 3)
			curAnimState->flags |= 1;
		else if (ch->facing >= 5 && ch->facing <= 7)
			curAnimState->flags &= 0xFFFFFFFE;

		_animator->addObjectToQueue(curAnimState);

		++addedObjects;
		++listAdded;
		if (listAdded < 2)
			i = 5;
	}

	for (int i = 0; i < 11; ++i) {
		curAnimState = &_animator->sprites()[i];

		if (_sprites->_anims[i].play) {
			curAnimState->active = 1;
			curAnimState->refreshFlag = 1;
			curAnimState->bkgdChangeFlag = 1;
		} else {
			curAnimState->active = 0;
			curAnimState->refreshFlag = 0;
			curAnimState->bkgdChangeFlag = 0;
		}
		curAnimState->height = _sprites->_anims[i].height;
		curAnimState->height2 = _sprites->_anims[i].height2;
		curAnimState->width = _sprites->_anims[i].width + 1;
		curAnimState->width2 = _sprites->_anims[i].width2;
		curAnimState->drawY = _sprites->_anims[i].drawY;
		curAnimState->x1 = curAnimState->x2 = _sprites->_anims[i].x;
		curAnimState->y1 = curAnimState->y2 = _sprites->_anims[i].y;
		curAnimState->background = _sprites->_anims[i].background;
		curAnimState->sceneAnimPtr = _sprites->_sceneShapes[_sprites->_anims[i].sprite];

		curAnimState->disable = _sprites->_anims[i].disable;

		if (_sprites->_anims[i].unk2)
			curAnimState->flags = 0x800;
		else
			curAnimState->flags = 0;

		if (_sprites->_anims[i].flipX)
			curAnimState->flags |= 0x1;

		_animator->addObjectToQueue(curAnimState);
	}

	for (int i = 0; i < 12; ++i) {
		curAnimState = &_animator->items()[i];
		Room *curRoom = &_roomTable[_currentCharacter->sceneId];
		byte curItem = curRoom->itemsTable[i];
		if (curItem != 0xFF) {
			curAnimState->drawY = curRoom->itemsYPos[i];
			curAnimState->sceneAnimPtr = _shapes[216 + curItem];
			curAnimState->animFrameNumber = (int16)0xFFFF;
			curAnimState->y1 = curRoom->itemsYPos[i];
			curAnimState->x1 = curRoom->itemsXPos[i];

			curAnimState->x1 -= (_animator->fetchAnimWidth(curAnimState->sceneAnimPtr, _scaleTable[curAnimState->drawY])) >> 1;
			curAnimState->y1 -= _animator->fetchAnimHeight(curAnimState->sceneAnimPtr, _scaleTable[curAnimState->drawY]);

			curAnimState->x2 = curAnimState->x1;
			curAnimState->y2 = curAnimState->y1;

			curAnimState->active = 1;
			curAnimState->refreshFlag = 1;
			curAnimState->bkgdChangeFlag = 1;

			_animator->addObjectToQueue(curAnimState);
		} else {
			curAnimState->active = 0;
			curAnimState->refreshFlag = 0;
			curAnimState->bkgdChangeFlag = 0;
		}
	}

	_animator->preserveAnyChangedBackgrounds();
	curAnimState = _animator->actors();
	curAnimState->bkgdChangeFlag = 1;
	curAnimState->refreshFlag = 1;
	for (int i = 1; i < 28; ++i) {
		curAnimState = &_animator->objects()[i];
		if (curAnimState->active) {
			curAnimState->bkgdChangeFlag = 1;
			curAnimState->refreshFlag = 1;
		}
	}
	_animator->restoreAllObjectBackgrounds();
	_animator->preserveAnyChangedBackgrounds();
	_animator->prepDrawAllObjects();
	initSceneScreen(brandonAlive);
	_animator->copyChangedObjectsForward(0);
}

void KyraEngine_LoK::initSceneScreen(int brandonAlive) {
	if (_flags.platform == Common::kPlatformAmiga) {
		if (_unkScreenVar1 && !queryGameFlag(0xF0)) {
			_screen->getPalette(2).clear();
			if (_currentCharacter->sceneId != 117 || !queryGameFlag(0xB3))
				_screen->setScreenPalette(_screen->getPalette(2));
		}

		if (_unkScreenVar2 == 1)
			_screen->shuffleScreen(8, 8, 304, 128, 2, 0, _unkScreenVar3, false);
		else
			_screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK);

		if (_unkScreenVar1 && !queryGameFlag(0xA0)) {
			if (_currentCharacter->sceneId == 45 && _cauldronState)
				_screen->getPalette(0).copy(_screen->getPalette(4), 12, 1);

			if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245 && (_brandonStatusBit & 1))
				_screen->copyPalette(0, 10);

			_screen->setScreenPalette(_screen->getPalette(0));
		}
	} else {
		if (_unkScreenVar1 && !queryGameFlag(0xA0)) {
			for (int i = 0; i < 60; ++i) {
				uint16 col = _screen->getPalette(0)[684 + i];
				col += _screen->getPalette(1)[684 + i] << 1;
				col >>= 2;
				_screen->getPalette(0)[684 + i] = col;
			}
			_screen->setScreenPalette(_screen->getPalette(0));
		}

		if (_unkScreenVar2 == 1)
			_screen->shuffleScreen(8, 8, 304, 128, 2, 0, _unkScreenVar3, false);
		else
			_screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);

		if (_unkScreenVar1 && _paletteChanged) {
			if (!queryGameFlag(0xA0)) {
				_screen->getPalette(0).copy(_screen->getPalette(1), 228, 20);
				_screen->setScreenPalette(_screen->getPalette(0));
			} else {
				_screen->getPalette(0).clear();
			}
		}
	}

	if (!_emc->start(&_scriptClick, 2))
		error("Could not start script function 2 of scene script");

	_scriptClick.regs[7] = brandonAlive;

	while (_emc->isValid(&_scriptClick))
		_emc->run(&_scriptClick);

	setTextFadeTimerCountdown(-1);

	if (_currentCharacter->sceneId == 210) {
		if (_itemInHand != kItemNone)
			magicOutMouseItem(2, -1);

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

int KyraEngine_LoK::handleSceneChange(int xpos, int ypos, int unk1, int frameReset) {
	if (queryGameFlag(0xEF))
		unk1 = 0;

	int sceneId = _currentCharacter->sceneId;
	_pathfinderFlag = 0;

	if (xpos < 12) {
		if (_roomTable[sceneId].westExit != 0xFFFF) {
			xpos = 12;
			ypos = _sceneExits.westYPos;
			_pathfinderFlag = 7;
		}
	} else if (xpos >= 308) {
		if (_roomTable[sceneId].eastExit != 0xFFFF) {
			xpos = 307;
			ypos = _sceneExits.eastYPos;
			_pathfinderFlag = 13;
		}
	}

	if (ypos <= (_northExitHeight & 0xFF) + 2) {
		if (_roomTable[sceneId].northExit != 0xFFFF) {
			xpos = _sceneExits.northXPos;
			ypos = _northExitHeight & 0xFF;
			_pathfinderFlag = 14;
		}
	} else if (ypos >= 136) {
		if (_roomTable[sceneId].southExit != 0xFFFF) {
			xpos = _sceneExits.southXPos;
			ypos = 136;
			_pathfinderFlag = 11;
		}
	}

	int temp = xpos - _currentCharacter->x1;
	if (ABS(temp) < 4) {
		temp = ypos - _currentCharacter->y1;
		if (ABS(temp) < 2)
			return 0;
	}

	int x = (int16)(_currentCharacter->x1 & 0xFFFC);
	int y = (int16)(_currentCharacter->y1 & 0xFFFE);
	xpos = (int16)(xpos & 0xFFFC);
	ypos = (int16)(ypos & 0xFFFE);

	int ret = findWay(x, y, xpos, ypos, _movFacingTable, 150);
	_pathfinderFlag = 0;

	if (ret >= _lastFindWayRet)
		_lastFindWayRet = ret;

	if (ret == 0x7D00 || ret == 0)
		return 0;

	return processSceneChange(_movFacingTable, unk1, frameReset);
}

int KyraEngine_LoK::processSceneChange(int *table, int unk1, int frameReset) {
	if (queryGameFlag(0xEF))
		unk1 = 0;

	int *tableStart = table;
	_sceneChangeState = 0;
	_loopFlag2 = 0;
	bool running = true;
	int returnValue = 0;
	uint32 nextFrame = 0;

	while (running) {
		bool forceContinue = false;
		switch (*table) {
		case 0: case 1: case 2:
		case 3: case 4: case 5:
		case 6: case 7:
			_currentCharacter->facing = getOppositeFacingDirection(*table);
			break;

		case 8:
			forceContinue = true;
			running = false;
			break;

		default:
			++table;
			forceContinue = true;
		}

		returnValue = changeScene(_currentCharacter->facing);
		if (returnValue)
			running = false;

		if (unk1) {
			if (skipFlag()) {
				resetSkipFlag(false);
				running = false;
				_sceneChangeState = 1;
			}
		}

		if (forceContinue || !running)
			continue;

		int temp = 0;
		if (table == tableStart || table[1] == 8)
			temp = setCharacterPosition(0, 0);
		else
			temp = setCharacterPosition(0, table);

		if (temp)
			++table;

		nextFrame = _timer->getDelay(5) * _tickLength + _system->getMillis();
		while (_system->getMillis() < nextFrame) {
			_timer->update();

			if (_currentCharacter->sceneId == 210) {
				updateKyragemFading();
				if (seq_playEnd() || _beadStateVar == 4 || _beadStateVar == 5) {
					*table = 8;
					running = false;
					break;
				}
			}

			if ((nextFrame - _system->getMillis()) >= 10)
				delay(10, true);
		}
	}

	if (frameReset && !(_brandonStatusBit & 2))
		_currentCharacter->currentAnimFrame = 7;

	_animator->animRefreshNPC(0);
	_animator->updateAllObjectShapes();
	return returnValue;
}

int KyraEngine_LoK::changeScene(int facing) {
	if (queryGameFlag(0xEF)) {
		if (_currentCharacter->sceneId == 5)
			return 0;
	}

	int xpos = _charAddXPosTable[facing] + _currentCharacter->x1;
	int ypos = _charAddYPosTable[facing] + _currentCharacter->y1;

	if (xpos >= 12 && xpos <= 308) {
		if (!lineIsPassable(xpos, ypos))
			return false;
	}

	if (_exitListPtr) {
		int16 *ptr = _exitListPtr;
		// this loop should be only entered one time, seems to be some hack in the original
		while (true) {
			if (*ptr == -1)
				break;

			if (*ptr > _currentCharacter->x1 || _currentCharacter->y1 < ptr[1] || _currentCharacter->x1 > ptr[2] || _currentCharacter->y1 > ptr[3]) {
				ptr += 10;
				break;
			}

			_brandonPosX = ptr[6];
			_brandonPosY = ptr[7];
			uint16 sceneId = ptr[5];
			facing = ptr[4];
			int unk1 = ptr[8];
			int unk2 = ptr[9];

			if (sceneId == 0xFFFF) {
				switch (facing) {
				case 0:
					sceneId = _roomTable[_currentCharacter->sceneId].northExit;
					break;

				case 2:
					sceneId = _roomTable[_currentCharacter->sceneId].eastExit;
					break;

				case 4:
					sceneId = _roomTable[_currentCharacter->sceneId].southExit;
					break;

				case 6:
					sceneId = _roomTable[_currentCharacter->sceneId].westExit;
					break;

				default:
					break;
				}
			}

			_currentCharacter->facing = facing;
			_animator->animRefreshNPC(0);
			_animator->updateAllObjectShapes();
			enterNewScene(sceneId, facing, unk1, unk2, 0);
			resetGameFlag(0xEE);
			return 1;
		}
	}

	int returnValue = 0;
	facing = 0;

	if ((_northExitHeight & 0xFF) + 2 >= ypos || (_northExitHeight & 0xFF) + 2 >= _currentCharacter->y1) {
		facing = 0;
		returnValue = 1;
	}

	if (xpos >= 308 || (_currentCharacter->x1 + 4) >= 308) {
		facing = 2;
		returnValue = 1;
	}

	if (((_northExitHeight >> 8) & 0xFF) - 2 < ypos || ((_northExitHeight >> 8) & 0xFF) - 2 < _currentCharacter->y1) {
		facing = 4;
		returnValue = 1;
	}

	if (xpos <= 12 || _currentCharacter->y1 <= 12) {
		facing = 6;
		returnValue = 1;
	}

	if (!returnValue)
		return 0;

	uint16 sceneId = 0xFFFF;
	switch (facing) {
	case 0:
		sceneId = _roomTable[_currentCharacter->sceneId].northExit;
		break;

	case 2:
		sceneId = _roomTable[_currentCharacter->sceneId].eastExit;
		break;

	case 4:
		sceneId = _roomTable[_currentCharacter->sceneId].southExit;
		break;

	default:
		sceneId = _roomTable[_currentCharacter->sceneId].westExit;
	}

	if (sceneId == 0xFFFF)
		return 0;

	enterNewScene(sceneId, facing, 1, 1, 0);
	return returnValue;
}

void KyraEngine_LoK::setCharactersInDefaultScene() {
	static const uint32 defaultSceneTable[][4] = {
		{ 0xFFFF, 0x0004, 0x0003, 0xFFFF },
		{ 0xFFFF, 0x0022, 0xFFFF, 0x0000 },
		{ 0xFFFF, 0x001D, 0x0021, 0xFFFF },
		{ 0xFFFF, 0x0000, 0x0000, 0xFFFF }
	};

	for (int i = 1; i < 5; ++i) {
		Character *cur = &_characterList[i];
		//cur->field_20 = 0;

		const uint32 *curTable = defaultSceneTable[i - 1];
		cur->sceneId = curTable[0];

		if (cur->sceneId == _currentCharacter->sceneId)
			//++cur->field_20;
			cur->sceneId = curTable[1/*cur->field_20*/];

		//cur->field_23 = curTable[cur->field_20+1];
	}
}

void KyraEngine_LoK::setCharactersPositions(int character) {
	static const uint16 initXPosTable[] = {
		0x3200, 0x0024, 0x2230, 0x2F00, 0x0020, 0x002B,
		0x00CA, 0x00F0, 0x0082, 0x00A2, 0x0042
	};
	static const uint8 initYPosTable[] = {
		0x00, 0xA2, 0x00, 0x42, 0x00,
		0x67, 0x67, 0x60, 0x5A, 0x71,
		0x76
	};

	assert(character < ARRAYSIZE(initXPosTable));
	Character *edit = &_characterList[character];
	edit->x1 = edit->x2 = initXPosTable[character];
	edit->y1 = edit->y2 = initYPosTable[character];
}

#pragma mark -
#pragma mark - Pathfinder
#pragma mark -

int KyraEngine_LoK::findWay(int x, int y, int toX, int toY, int *moveTable, int moveTableSize) {
	int ret = KyraEngine_v1::findWay(x, y, toX, toY, moveTable, moveTableSize);
	if (ret == 0x7D00)
		return 0;
	return getMoveTableSize(moveTable);
}

bool KyraEngine_LoK::lineIsPassable(int x, int y) {
	if (queryGameFlag(0xEF)) {
		if (_currentCharacter->sceneId == 5)
			return true;
	}

	if (_pathfinderFlag & 2) {
		if (x >= 312)
			return false;
	}

	if (_pathfinderFlag & 4) {
		if (y >= 136)
			return false;
	}

	if (_pathfinderFlag & 8) {
		if (x < 8)
			return false;
	}

	if (_pathfinderFlag2) {
		if (x <= 8 || x >= 312)
			return true;
		if (y < (_northExitHeight & 0xFF) || y > 135)
			return true;
	}

	if (y > 137)
		return false;

	if (y < 0)
		y = 0;

	int ypos = 8;
	if (_scaleMode) {
		ypos = (_scaleTable[y] >> 5) + 1;
		if (8 < ypos)
			ypos = 8;
	}

	x -= (ypos >> 1);

	int xpos = x;
	int xtemp = xpos + ypos - 1;
	if (x < 0)
		xpos = 0;

	if (xtemp > 319)
		xtemp = 319;

	for (; xpos < xtemp; ++xpos) {
		if (!_screen->getShapeFlag1(xpos, y))
			return false;
	}
	return true;
}

#pragma mark -

void KyraEngine_LoK::setupZanthiaPalette(int pal) {
	uint8 r, g, b;

	switch (pal - 17) {
	case 0:
		// 0x88F
		r = 33;
		g = 33;
		b = 63;
		break;

	case 1:
		// 0x00F
		r = 0;
		g = 0;
		b = 63;
		break;

	case 2:
		// 0xF88
		r = 63;
		g = 33;
		b = 33;
		break;

	case 3:
		// 0xF00
		r = 63;
		g = 0;
		b = 0;
		break;

	case 4:
		// 0xFF9
		r = 63;
		g = 63;
		b = 37;
		break;

	case 5:
		// 0xFF1
		r = 63;
		g = 63;
		b = 4;
		break;

	default:
		// 0xFFF
		r = 63;
		g = 63;
		b = 63;
	}

	_screen->getPalette(4)[12 * 3 + 0] = r;
	_screen->getPalette(4)[12 * 3 + 1] = g;
	_screen->getPalette(4)[12 * 3 + 2] = b;
}

#pragma mark -

void KyraEngine_LoK::setupSceneResource(int sceneId) {
	if (!_flags.isTalkie)
		return;

	if (_currentRoom != 0xFFFF) {
		assert(_currentRoom < _roomTableSize);
		int tableId = _roomTable[_currentRoom].nameIndex;
		assert(tableId < _roomFilenameTableSize);

		// unload our old room
		char file[64];
		strcpy(file, _roomFilenameTable[tableId]);
		strcat(file, ".VRM");
		_res->unloadPakFile(file);

		strcpy(file, _roomFilenameTable[tableId]);
		strcat(file, ".PAK");
		_res->unloadPakFile(file);

		strcpy(file, _roomFilenameTable[tableId]);
		strcat(file, ".APK");
		_res->unloadPakFile(file);
	}

	assert(sceneId < _roomTableSize);
	int tableId = _roomTable[sceneId].nameIndex;
	assert(tableId < _roomFilenameTableSize);

	// load our new room
	char file[64];
	strcpy(file, _roomFilenameTable[tableId]);
	strcat(file, ".VRM");
	if (_res->exists(file))
		_res->loadPakFile(file);

	strcpy(file, _roomFilenameTable[tableId]);
	strcat(file, ".PAK");
	if (_res->exists(file))
		_res->loadPakFile(file);

	strcpy(file, _roomFilenameTable[tableId]);
	strcat(file, ".APK");
	if (_res->exists(file))
		_res->loadPakFile(file);
}

} // End of namespace Kyra
