/* 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 "hdb/hdb.h"

namespace HDB {

/*
	Adds a tile to an animation list
*/
void AI::addAnimateTarget(int x, int y, int start, int end, AnimSpeed speed, bool killAuto, bool inMap, const char *tileName) {
	AnimTarget *at = new AnimTarget;

	at->x = x;
	at->y = y;
	at->start = start;
	at->end = end;
	at->killAuto = killAuto; // Doesn't actually kill it, deactivates it

	// Set animCycle and animFrame as per speed
	switch (speed) {
	case ANIM_SLOW:
		at->animCycle = 10;
		at->animFrame = 10;
		break;
	case ANIM_NORMAL:
		at->animCycle = 6;
		at->animFrame = 6;
		break;
	case ANIM_FAST:
		at->animCycle = 2;
		at->animFrame = 2;
		break;
	}

	// Set +1/-1 for tile anim direction
	if ((end - start) > 0)
		at->vel = 1;
	else
		at->vel = -1;

	// Set Info if this is not an inMap animation
	at->inMap = inMap;
	if (!inMap) {

		char name[32];
		uint32 size;

		for (int i = start; i <= end; i++) {
			if (i < 10)
				snprintf(name, 32, "%s0%d", tileName, i + 1);
			else
				snprintf(name, 32, "%s%d", tileName, i + 1);
			size = g_hdb->_fileMan->getLength(name, TYPE_TILE32);
			at->gfxList[i] = g_hdb->_gfx->getTileGfx(name, size);
		}
	}

	// Insert in the beginning
	_animTargets.insert_at(0, at);
}

/*
	Animate _animTargets
	Called every frame
*/
void AI::animateTargets() {
	AnimTarget *at;
	int mx, my;
	int layer;

	g_hdb->_map->getMapXY(&mx, &my);

	for (uint i = 0; i < _animTargets.size(); i++) {
		at = _animTargets[i];
		debug(9, "AnimTarget #%i: at: at->x: %d, at->y: %d, at->start: %d, at->end: %d, at->vel: %d", i, at->x, at->y, at->start, at->end, at->vel);

		// Draw Non-map stuff every frame
		if (!at->inMap)
			// FIXME: Out of bounds reference to gfxList
			at->gfxList[at->start]->drawMasked(at->x - mx, at->y - my);

		// Frame Timer
		if (at->animFrame-- < 1) {
			at->animFrame = at->animCycle;

			if (at->inMap) {
				// Animate Map Tiles
				layer = 0; // BG layer
				if (!(at->start == g_hdb->_map->getMapBGTileIndex(at->x, at->y)))
					layer = 1;

				// Change Tile Anim
				at->start += at->vel;

				// Set it back in map
				if (!layer)
					g_hdb->_map->setMapBGTileIndex(at->x, at->y, at->start);
				else
					g_hdb->_map->setMapFGTileIndex(at->x, at->y, at->start);
			} else {
				// Animate non-map tiles
				at->start++;
			}

			// Animation Finished ?
			if (at->start == at->end) {

				if (at->killAuto)
					autoDeactivate(at->x, at->y);

				_animTargets.remove_at(i);
				i--;
				continue;
			}
		}
	}
}

// Add an action location to the list of possible actions
// Each action must be paired with another of the same number
void AI::addToActionList(int actionIndex, int x, int y, char *luaFuncInt, char *luaFuncUse) {

	if (!_actions[actionIndex].x1) {
		_actions[actionIndex].x1 = x;
		_actions[actionIndex].y1 = y;
		if (luaFuncInt[0] != '*')
			strcpy(_actions[actionIndex].luaFuncInit, luaFuncInt);
		if (luaFuncInt[0] != '*')
			strcpy(_actions[actionIndex].luaFuncUse, luaFuncUse);

		if (_actions[actionIndex].luaFuncInit[0]) {
			g_hdb->_lua->callFunction(_actions[actionIndex].luaFuncInit, 2);
			strcpy(_actions[actionIndex].entityName, g_hdb->_lua->getStringOffStack());
			strcpy(_actions[actionIndex].entityName, g_hdb->_lua->getStringOffStack());
		}
		return;
	}

	if (!_actions[actionIndex].x2) {
		_actions[actionIndex].x2 = x;
		_actions[actionIndex].y2 = y;
		if (luaFuncInt[0] != '*')
			strcpy(_actions[actionIndex].luaFuncInit, luaFuncInt);
		if (luaFuncInt[0] != '*')
			strcpy(_actions[actionIndex].luaFuncUse, luaFuncUse);

		if (_actions[actionIndex].luaFuncInit[0]) {
			g_hdb->_lua->callFunction(_actions[actionIndex].luaFuncInit, 2);
			strcpy(_actions[actionIndex].entityName, g_hdb->_lua->getStringOffStack());
			strcpy(_actions[actionIndex].entityName, g_hdb->_lua->getStringOffStack());
		}
		return;
	}

	warning("Adding a 3rd action to ACTION-%d is illegal", actionIndex);
}

// Checks if the location passed-in matches an action pair.
// If so, activate it if possible. Returns TRUE for finding pair.
bool AI::checkActionList(AIEntity *e, int x, int y, bool lookAndGrab) {
	for (int i = 0; i < kMaxActions; i++) {
		if ((_actions[i].x1 == x && _actions[i].y1 == y) || (_actions[i].x2 == x && _actions[i].y2 == y)) {
			int targetX = _actions[i].x2;
			int targetY = _actions[i].y2;
			bool success;

			// Choose target co-ordinates
			if (x == targetX && y == targetY) {
				targetX = _actions[i].x1;
				targetY = _actions[i].y1;
			}

			// Is this an actual switch?
			uint32 flags = g_hdb->_map->getMapFGTileFlags(x, y);
			if (!flags)
				flags = g_hdb->_map->getMapBGTileFlags(x, y);
			if (!(flags & kFlagSolid) && (_player->tileX != x && _player->tileY != y))
				return false;
			// Closing on something?
			if (findEntity(targetX, targetY))
				return false;

			success = activateAction(e, x, y, targetX, targetY);

			// If successful, remove action from list
			if (success) {
				_actions[i].x1 = _actions[i].y1 = _actions[i].x2 = _actions[i].y2 = 0;

				// Call Lua Use function if it exists
				if (_actions[i].luaFuncUse[0])
					g_hdb->_lua->callFunction(_actions[i].luaFuncUse, 0);
			} else if (e == _player && !checkForTouchplate(x, y))
				addWaypoint(e->tileX, e->tileY, x, y, e->level);

			if (lookAndGrab && e == _player) {
				lookAtXY(x, y);
				animGrabbing();
			}

			return true;
		}
	}

	return false;
}

void AI::addToHereList(const char *entName, int x, int y) {
	HereT *h = new HereT;
	strcpy(h->entName, entName);
	h->x = x;
	h->y = y;
	_hereList->push_back(h);
}

HereT *AI::findHere(int x, int y) {
	for (Common::Array<HereT *>::iterator it = _hereList->begin(); it != _hereList->end(); it++) {
		if ((*it)->x == x && (*it)->y == y)
			return *it;
	}
	return NULL;
}

void AI::addToAutoList(int x, int y, const char *luaFuncInit, const char *luaFuncUse) {

	const char *get;

	for (int i = 0; i < kMaxAutoActions; i++) {
		if (!_autoActions[i].x) {
			_autoActions[i].x = x;
			_autoActions[i].y = y;
			_autoActions[i].activated = false;
			if (luaFuncInit[0] != '*')
				strcpy(&_autoActions[i].luaFuncInit[0], luaFuncInit);
			if (luaFuncUse[0] != '*')
				strcpy(&_autoActions[i].luaFuncUse[0], luaFuncUse);

			if (_autoActions[i].luaFuncInit[0]) {
				g_hdb->_lua->callFunction(_autoActions[i].luaFuncInit, 2);
				get = g_hdb->_lua->getStringOffStack();
				if (!get)
					return;
				strcpy(&_autoActions[i].entityName[0], get);
				get = g_hdb->_lua->getStringOffStack();
				if (!get)
					return;
				strcpy(&_autoActions[i].entityName[0], get);
			}
			return;
		}
	}
}

void AI::autoDeactivate(int x, int y) {
	for (int i = 0; i < kMaxAutoActions; i++) {
		if (_autoActions[i].x == x && _autoActions[i].y == y) {
			_autoActions[i].activated = false;
			return;
		}
	}
}

bool AI::activateAction(AIEntity *e, int x, int y, int targetX, int targetY) {
	bool success = false;
	int tileIndex = g_hdb->_map->getMapFGTileIndex(x, y);

	// If FG tile invisivle or grating, ignore if
	int fgFlags = g_hdb->_map->getMapFGTileFlags(x, y);
	if (fgFlags & (kFlagInvisible | kFlagGrating))
		tileIndex = -1;

	if (tileIndex < 0)
		tileIndex = g_hdb->_map->getMapBGTileIndex(x, y);

	// Check which tile is going to activate
	if ( tileIndex == _useSwitchOff || tileIndex == _useSwitchOff + 1 )
		success = useSwitch( e, x, y, targetX, targetY, _useSwitchOn );
	else
	if ( tileIndex == _useSwitchOn )
		success = useSwitchOn( e, x, y, targetX, targetY, _useSwitchOff );
	else
	//-------------------------------------------------------------------
	if ( tileIndex == _useHandswitchOff || tileIndex == _useHandswitchOff + 1 )
		success = useSwitch( e, x, y, targetX, targetY, _useHandswitchOn );
	else
	if ( tileIndex == _useHandswitchOn )
		success = useSwitchOn( e, x, y, targetX, targetY, _useHandswitchOff );
	else
	//-------------------------------------------------------------------
	if ( tileIndex == _kcHolderWhiteOff || tileIndex == _kcHolderWhiteOff + 1 )
		success = useLockedSwitch( e, x, y, targetX, targetY, _kcHolderWhiteOn, ITEM_KEYCARD_WHITE, "I need a White Keycard." );
	else
	if ( tileIndex == _kcHolderWhiteOn )
		success = useLockedSwitchOn( e, x, y, targetX, targetY, _kcHolderWhiteOff, ITEM_KEYCARD_WHITE );
	else
	if ( tileIndex == _kcHolderBlueOff || tileIndex == _kcHolderBlueOff + 1 )
		success = useLockedSwitch( e, x, y, targetX, targetY, _kcHolderBlueOn, ITEM_KEYCARD_BLUE, "I need a Blue Keycard." );
	else
	if ( tileIndex == _kcHolderBlueOn )
		success = useLockedSwitchOn( e, x, y, targetX, targetY, _kcHolderBlueOff, ITEM_KEYCARD_BLUE );
	else
	if ( tileIndex == _kcHolderRedOff || tileIndex == _kcHolderRedOff + 1 )
		success = useLockedSwitch( e, x, y, targetX, targetY, _kcHolderRedOn, ITEM_KEYCARD_RED, "I need a Red Keycard." );
	else
	if ( tileIndex == _kcHolderRedOn )
		success = useLockedSwitchOn( e, x, y, targetX, targetY, _kcHolderRedOff, ITEM_KEYCARD_RED );
	else
	if ( tileIndex == _kcHolderGreenOff || tileIndex == _kcHolderGreenOff + 1 )
		success = useLockedSwitch( e, x, y, targetX, targetY, _kcHolderGreenOn, ITEM_KEYCARD_GREEN, "I need a Green Keycard." );
	else
	if ( tileIndex == _kcHolderGreenOn )
		success = useLockedSwitchOn( e, x, y, targetX, targetY, _kcHolderGreenOff, ITEM_KEYCARD_GREEN );
	else
	if ( tileIndex == _kcHolderPurpleOff || tileIndex == _kcHolderPurpleOff + 1 )
		success = useLockedSwitch( e, x, y, targetX, targetY, _kcHolderPurpleOn, ITEM_KEYCARD_PURPLE, "I need a Purple Keycard." );
	else
	if ( tileIndex == _kcHolderPurpleOn )
		success = useLockedSwitchOn( e, x, y, targetX, targetY, _kcHolderPurpleOff, ITEM_KEYCARD_PURPLE );
	else
	if ( tileIndex == _kcHolderBlackOff || tileIndex == _kcHolderBlackOff + 1 )
		success = useLockedSwitch( e, x, y, targetX, targetY, _kcHolderBlackOn, ITEM_KEYCARD_BLACK, "I need a Black Keycard." );
	else
	if ( tileIndex == _kcHolderBlackOn )
		success = useLockedSwitchOn( e, x, y, targetX, targetY, _kcHolderBlackOff, ITEM_KEYCARD_BLACK );
	else
	//-------------------------------------------------------------------
	if ( tileIndex == _useSwitch2Off || tileIndex == _useSwitch2Off + 1 )
		success = useSwitch2( e, x, y, targetX, targetY );
	else
	if ( tileIndex == _useHolderEmpty || tileIndex == _useHolderEmpty + 1 )
		success = useCellHolder( e, x, y, targetX, targetY );
	else
	//-------------------------------------------------------------------
	if ( tileIndex == _targetDoorN || tileIndex == _targetDoorN + 3 )
		success = useAutoDoorOpenClose( e, x, y );
	else
	if ( tileIndex == _targetDoorP || tileIndex == _targetDoorP + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	if ( tileIndex == _targetDoorS || tileIndex == _targetDoorS + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	if ( tileIndex == _targetDoorNv || tileIndex == _targetDoorNv + 3 )
		success = useAutoDoorOpenClose( e, x, y );
	else
	if ( tileIndex == _targetDoorPv || tileIndex == _targetDoorPv + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	if ( tileIndex == _targetDoorSv || tileIndex == _targetDoorSv + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	//-------------------------------------------------------------------
	if ( tileIndex == _targetDoorN || tileIndex == _targetDoor2N + 3 )
		success = useAutoDoorOpenClose( e, x, y );
	else
	if ( tileIndex == _targetDoorP || tileIndex == _targetDoor2P + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	if ( tileIndex == _targetDoorS || tileIndex == _targetDoor2S + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	if ( tileIndex == _targetDoorNv || tileIndex == _targetDoor2Nv + 3 )
		success = useAutoDoorOpenClose( e, x, y );
	else
	if ( tileIndex == _targetDoorPv || tileIndex == _targetDoor2Pv + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	if ( tileIndex == _targetDoorSv || tileIndex == _targetDoor2Sv + 3 )
		success = useDoorOpenCloseBot( e, x, y );
	else
	//-------------------------------------------------------------------
	if ( tileIndex == _touchplateOff )
		success = useTouchplate( e, x, y, targetX, targetY, _touchplateOn );
	else
	if ( tileIndex == _touchplateOn )
		success = useTouchplateOn( e, x, y, targetX, targetY, _touchplateOff );
	else
	if ( tileIndex == _templeTouchpOff )
		success = useTouchplate( e, x, y, targetX, targetY, _templeTouchpOn );
	else
	if ( tileIndex == _templeTouchpOn )
		success = useTouchplateOn( e, x, y, targetX, targetY, _templeTouchpOff );

		return success;
}

bool AI::checkAutoList(AIEntity *e, int x, int y) {
	for (int i = 0; i < kMaxAutoActions; i++) {
		if (_autoActions[i].x == x && _autoActions[i].y == y && !_autoActions[i].activated) {
			debug(1, "Activating action for Entity: %s, x: %d, y: %d", e->entityName, x, y);
			bool success = activateAction(e, x, y, 0, 0);
			_autoActions[i].activated = true;

			if (success && _autoActions[i].luaFuncUse[0])
				g_hdb->_lua->callFunction(_autoActions[i].luaFuncUse, 0);

			if (e == _player) {
				lookAtXY(x, y);
				animGrabbing();
			}

			return true;
		}
	}
	return false;
}

bool AI::autoActive(int x, int y) {
	for (int i = 0; i < kMaxAutoActions; i++) {
		if (_autoActions[i].x == x && _autoActions[i].y == y) {
			if (!_autoActions[i].activated)
				return false;
			return true;
		}
	}
	return false;
}

CallbackDef allCallbacks[] = {
	{NO_FUNCTION, NULL},
	{AI_BARREL_EXPLOSION_END, aiBarrelExplosionEnd},
	{CALLBACK_DOOR_OPEN_CLOSE, callbackDoorOpenClose},
	{CALLBACK_AUTODOOR_OPEN_CLOSE, callbackAutoDoorOpenClose},
	{CALLBACK_END, NULL}
};

void AI::addCallback(CallbackType type, int x, int y, int delay) {
	for (int i = kMaxCallbacks - 1; i >= 0; i--)
		if (_callbacks[i].type == NO_FUNCTION) {
			_callbacks[i].type = type;
			_callbacks[i].x = x;
			_callbacks[i].y = y;
			_callbacks[i].delay = delay;
			return;
		}
}

void AI::processCallbackList() {
	for (int i = 0; i < kMaxCallbacks; i++)
		if (_callbacks[i].type != NO_FUNCTION) {
			if (_callbacks[i].delay) {
				_callbacks[i].delay--;
				return;
			}
			allCallbacks[_callbacks[i].type].function(_callbacks[i].x, _callbacks[i].y);
			_callbacks[i].type = NO_FUNCTION;
			_callbacks[i].x = _callbacks[i].y = 0;
			return;
		}
}

void AI::addToLuaList(int x, int y, int value1, int value2, char *luaFuncInit, char *luaFuncAction, char *luaFuncUse) {
	for (int i = 0; i < kMaxLuaEnts; i++) {
		if (!_luaList[i].luaFuncInit[0] && !_luaList[i].luaFuncAction[0] && !_luaList[i].luaFuncUse[0]) {
			_luaList[i].x = x;
			_luaList[i].y = y;
			_luaList[i].value1 = value1;
			_luaList[i].value2 = value2;

			strcpy(_luaList[i].luaFuncInit, luaFuncInit);
			if (luaFuncInit[0] == '*')
				_luaList[i].luaFuncInit[0] = 0;
			strcpy(_luaList[i].luaFuncAction, luaFuncAction);
			if (luaFuncAction[0] == '*')
				_luaList[i].luaFuncAction[0] = 0;
			strcpy(_luaList[i].luaFuncUse, luaFuncUse);
			if (luaFuncUse[0] == '*')
				_luaList[i].luaFuncUse[0] = 0;

			_numLuaList++;
			if (_luaList[i].luaFuncInit[0])
				g_hdb->_lua->invokeLuaFunction(luaFuncInit, x, y, value1, value2);

			spawnBlocking(x, y, 1);

			return;
		}
	}
}

bool AI::checkLuaList(AIEntity *e, int x, int y) {
	for (int i = 0; i < _numLuaList; i++) {
		if (_luaList[i].x == x && _luaList[i].y == y && _luaList[i].luaFuncUse[0]) {
			if (e == _player) {
				lookAtXY(x, y);
				animGrabbing();
			}

			g_hdb->_lua->invokeLuaFunction(_luaList[i].luaFuncUse, _luaList[i].x, _luaList[i].y, _luaList[i].value1, _luaList[i].value2);

			return true;
		}
	}

	return false;
}

bool AI::luaExistAtXY(int x, int y) {
	for (int i = 0; i < _numLuaList; i++) {
		if (_luaList[i].x == x && _luaList[i].y == y && _luaList[i].luaFuncUse[0]) {
			return true;
		}
	}

	return false;
}

void AI::addToTeleportList(int teleIndex, int x, int y, int dir, int level, int anim, int usable, const char *luaFuncUse) {
	if (!level)
		level = 1;

	if (!_teleporters[teleIndex].x1) {
		_teleporters[teleIndex].x1 = x;
		_teleporters[teleIndex].y1 = y;
		_teleporters[teleIndex].dir1 = (AIDir)dir;
		_teleporters[teleIndex].level1 = level;
		_teleporters[teleIndex].anim1 = anim;
		_teleporters[teleIndex].usable1 = usable;
		strcpy(_teleporters[teleIndex].luaFuncUse1, luaFuncUse);
		if (_teleporters[teleIndex].luaFuncUse1[0] == '*')
			_teleporters[teleIndex].luaFuncUse1[0] = 0;
		_numTeleporters++;
		return;
	}
	if (!_teleporters[teleIndex].x2) {
		_teleporters[teleIndex].x2 = x;
		_teleporters[teleIndex].y2 = y;
		_teleporters[teleIndex].dir2 = (AIDir)dir;
		_teleporters[teleIndex].level2 = level;
		_teleporters[teleIndex].anim2 = anim;
		_teleporters[teleIndex].usable2 = usable;
		strcpy(_teleporters[teleIndex].luaFuncUse2, luaFuncUse);
		if (_teleporters[teleIndex].luaFuncUse2[0] == '*')
			_teleporters[teleIndex].luaFuncUse2[0] = 0;
		_numTeleporters++;
		return;
	}

	warning("addToTeleporterList: Adding a 3rd teleporter is illegal");
}

bool AI::findTeleporterDest(int tileX, int tileY, SingleTele *info) {
	for (int i = 0; i < _numTeleporters; i++) {
		if ((_teleporters[i].x1 == tileX) && (_teleporters[i].x1 == tileY)) {
			info->anim = _teleporters[i].anim2;
			info->x = _teleporters[i].x2;
			info->y = _teleporters[i].y2;
			info->dir = _teleporters[i].dir2;
			info->level = _teleporters[i].level2;
			info->usable = _teleporters[i].usable2;
			return true;
		}
		if ((_teleporters[i].x1 == tileX) && (_teleporters[i].x1 == tileY)) {
			info->anim = _teleporters[i].anim1;
			info->x = _teleporters[i].x1;
			info->y = _teleporters[i].y1;
			info->dir = _teleporters[i].dir1;
			info->level = _teleporters[i].level1;
			info->usable = _teleporters[i].usable1;
			return true;
		}
	}
	return false;
}

bool AI::checkTeleportList(AIEntity *e, int x, int y) {
	for (int i = 0; i < kMaxTeleporters; i++) {
		if ((_teleporters[i].x1 == x && _teleporters[i].y1 == y) || (_teleporters[i].x2 == x && _teleporters[i].y2 == y)) {
			int targetX = _teleporters[i].x1;
			int targetY = _teleporters[i].y1;
			int targetX2 = _teleporters[i].x2;
			int targetY2 = _teleporters[i].y2;
			AIDir dir1 = _teleporters[i].dir1;
			AIDir dir2 = _teleporters[i].dir2;
			int level1 = _teleporters[i].level1;
			int level2 = _teleporters[i].level2;
			int usable1 = _teleporters[i].usable1;
			int usable2 = _teleporters[i].usable2;
			int anim1 = _teleporters[i].anim1;
			int anim2 = _teleporters[i].anim2;
			const char *luaFuncUse1 = _teleporters[i].luaFuncUse1;
			const char *luaFuncUse2 = _teleporters[i].luaFuncUse2;

			// Choose which set of co-ordinates is the target
			if (x != targetX || y != targetY) {
				targetX = _teleporters[i].x2;
				targetY = _teleporters[i].y2;
				targetX2 = _teleporters[i].x1;
				targetY2 = _teleporters[i].y1;
				dir1 = _teleporters[i].dir2;
				dir2 = _teleporters[i].dir1;
				level1 = _teleporters[i].level2;
				level2 = _teleporters[i].level1;
				usable1 = _teleporters[i].usable2;
				usable2 = _teleporters[i].usable1;
				anim1 = _teleporters[i].anim2;
				anim2 = _teleporters[i].anim1;
				luaFuncUse1 = _teleporters[i].luaFuncUse2;
				luaFuncUse2 = _teleporters[i].luaFuncUse1;
			}

			// We must be exactly on the teleporter
			if (abs(targetX*kTileWidth - e->x) > 2 || abs(targetY*kTileHeight - e->y) > 2)
				return false;

			// Can this teleporter be used?
			if (usable1)
				return false;

			// Move Entity to new Spot, then walk forward one tile
			e->tileX = targetX2;
			e->tileY = targetY2;
			e->x = targetX2 * kTileWidth;
			e->y = targetY2 * kTileHeight;
			e->xVel = e->yVel = 0;
			e->goalX = e->goalY = 0;
			e->animFrame = 0;
			e->drawXOff = e->drawYOff = 0;
			e->dir = dir2;
			e->level = level2;

			if (luaFuncUse2[0])
				g_hdb->_lua->callFunction(luaFuncUse2, 0);

			e->draw = e->standdownGfx[0];
			if (e == _player) {
				memset(&_waypoints[0], 0, sizeof(_waypoints));
				_numWaypoints = 0;
			}

			switch (e->dir) {
			case DIR_UP:
				setEntityGoal(e, e->tileX, e->tileY - 1);
				break;
			case DIR_DOWN:
				setEntityGoal(e, e->tileX, e->tileY + 1);
				break;
			case DIR_LEFT:
				setEntityGoal(e, e->tileX - 1, e->tileY);
				break;
			case DIR_RIGHT:
				setEntityGoal(e, e->tileX + 1, e->tileY);
				break;
			case DIR_NONE:
				warning("checkTeleporterList: DIR_NONE found");
				break;
			}

			g_hdb->_map->centerMapXY(e->x + 16, e->y + 16);

			// Start up Teleport flash animation only if value1 is set to 1
			if (anim1 == 1 || anim2 == 2) {
				addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, "teleporter_flash_sit");
				warning("STUB: checkTeleporterList: Play SND_TELEPORT");
			}

			// PANIC ZONE Teleports?
			warning("STUB: checkTeleporterList: Toggle Panic Zone");

			// Is there an attack gem still floating around?
			for (Common::Array<AIEntity *>::iterator it = _ents->begin(); it != _ents->end(); it++) {
				if ((*it)->type == AI_GEM_ATTACK) {
					int amt = getGemAmount();
					setGemAmount(amt + 1);
					removeEntity(*it);
					break;
				}
			}

			_playerEmerging = true;
			return true;
		}
	}

	return false;
}

void AI::addToPathList(int x, int y, int type, AIDir dir) {
	ArrowPath *arrowPath = new ArrowPath;

	arrowPath->type = type;
	arrowPath->tileX = x;
	arrowPath->tileY = y;
	arrowPath->dir = dir;

	_arrowPaths->push_back(arrowPath);
}

ArrowPath *AI::findArrowPath(int x, int y) {
	for (Common::Array<ArrowPath *>::iterator it = _arrowPaths->begin(); it != _arrowPaths->end(); it++) {
		if ((*it)->tileX == x && (*it)->tileY == y)
			return *it;
	}
	return NULL;
}

void AI::addToTriggerList(char *luaFuncInit, char *luaFuncUse, int x, int y, int value1, int value2, char *id) {
	Trigger *t = new Trigger;

	strcpy(t->id, id);
	t->x = x;
	t->y = y;
	t->value1 = value1;
	t->value2 = value2;
	if (luaFuncInit[0] != '*')
		strcpy(t->luaFuncInit, luaFuncInit);
	if (luaFuncUse[0] != '*')
		strcpy(t->luaFuncUse, luaFuncUse);

	if (!t->luaFuncUse[0])
		warning("STUB: addToTriggerList: Open MessageBar");

	if (t->luaFuncInit[0]) {
		g_hdb->_lua->pushFunction(t->luaFuncInit);
		g_hdb->_lua->pushInt(x);
		g_hdb->_lua->pushInt(y);
		g_hdb->_lua->pushInt(value1);
		g_hdb->_lua->pushInt(value2);
		g_hdb->_lua->call(4, 0);
	}
}

bool AI::checkTriggerList(char *entName, int x, int y) {
	Trigger *t;

	for (Common::Array<Trigger *>::iterator it = _triggerList->begin(); it != _triggerList->end(); it++) {
		t = *it;
		if (t->x == x && t->y == y) {
			if (!t->luaFuncUse[0])
				return false;

			g_hdb->_lua->pushFunction(t->luaFuncUse);
			g_hdb->_lua->pushString(entName);
			g_hdb->_lua->pushInt(t->x);
			g_hdb->_lua->pushInt(t->y);
			g_hdb->_lua->pushInt(t->value1);
			g_hdb->_lua->pushInt(t->value2);
			g_hdb->_lua->call(5, 0);
			return true;
		}
	}

	return false;
}

void AI::killTrigger(const char *id) {
	for (Common::Array<Trigger *>::iterator it = _triggerList->begin(); it != _triggerList->end(); it++) {
		if (!scumm_stricmp(id, (*it)->id))
			_triggerList->erase(it);
	}
}

} // End of Namespace