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

#include "hdb/hdb.h"
#include "hdb/ai.h"
#include "hdb/ai-player.h"
#include "hdb/gfx.h"
#include "hdb/lua-script.h"
#include "hdb/map.h"
#include "hdb/mpc.h"
#include "hdb/sound.h"
#include "hdb/window.h"

namespace HDB {

//-------------------------------------------------------------------
//
//	OMNIBOT : This guy moves on a path and if he sees the player
//		directly ahead, he will shoot at him
//
//-------------------------------------------------------------------

void aiOmniBotInit(AIEntity *e) {
	if (e->value1 == 1)
		e->aiAction = aiOmniBotMove;
	else if (g_hdb->_ai->findPath(e))
		e->aiAction = aiOmniBotAction;
}

void aiOmniBotInit2(AIEntity *e) {
	e->standdownGfx[0] = e->movedownGfx[0];
	e->standupGfx[0] = e->movedownGfx[0];
	e->standleftGfx[0] = e->moveleftGfx[0];
	e->standrightGfx[0] = e->moverightGfx[0];
	e->standdownFrames = e->standupFrames = e->standleftFrames = e->standrightFrames = 1;
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiOmniBotMove(AIEntity *e) {
	if (e->goalX)
		g_hdb->_ai->animateEntity(e);
	else
		g_hdb->_ai->animEntFrames(e);
}

void aiOmniBotAction(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();
	if (e->goalX) {
		if (!e->sequence) {
			g_hdb->_ai->animateEntity(e);
			// Is the Player collding?
			if (hitPlayer(e->x, e->y) && (p->level == e->level)) {
				g_hdb->_ai->killPlayer(DEATH_FRIED);
				return;
			}

			// Shoot player ?
			if (onEvenTile(e->x, e->y) && g_hdb->getActionMode()) {
				int xv = 0, yv = 0, result;
				bool shoot = false;

				// FIXME: Is reloading Player required here?
				p = g_hdb->_ai->getPlayer();

				// On same level/screen?
				if ((e->level != p->level) || g_hdb->_ai->playerDead() || !e->onScreen)
					return;

				// Is Player in Line of Sight?
				switch (e->dir) {
				case DIR_UP:
					if (p->x == e->x && p->y < e->y) {
						shoot = true;
						yv = -1;
					}
					break;
				case DIR_DOWN:
					if (p->x == e->x && p->y > e->y) {
						shoot = true;
						yv = 1;
					}
					break;
				case DIR_LEFT:
					if (p->x < e->x && p->y == e->y) {
						shoot = true;
						xv = -1;
					}
					break;
				case DIR_RIGHT:
					if (p->x > e->x && p->y == e->y) {
						shoot = true;
						xv = 1;
					}
					break;
				case DIR_NONE:
					break;
				}

				// If shoot = true, take the shot
				// (1) Check we're not shooting into a solid tile
				// (2) Check we're not shooting into an Entity unless it's the player
				AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + xv, e->tileY + yv, e->level, &result);
				if (shoot && !hit && result) {
					AIEntity *omni = g_hdb->_ai->spawn(AI_OMNIBOT_MISSILE, e->dir, e->tileX + xv, e->tileY + yv, NULL, NULL, NULL, DIR_NONE, e->level, 0, 0, 1);
					omni->xVel = xv * kPlayerMoveSpeed * 2;
					omni->yVel = yv * kPlayerMoveSpeed * 2;
					if (g_hdb->_map->onScreen(e->tileX, e->tileY))
						g_hdb->_sound->playSound(SND_OMNIBOT_FIRE);
					if (!g_hdb->getActionMode()) {
						omni->xVel >>= 1;
						omni->yVel >>= 1;
					}
					e->sequence = 16;
				}
			}
		}
	} else {
		g_hdb->_ai->findPath(e);
		if (e->onScreen)
			g_hdb->_sound->playSound(SND_OMNIBOT_AMBIENT);
	}

	if (e->sequence)
		e->sequence--;
}

//-------------------------------------------------------------------
//
//	OMNIBOT MISSILE : Used by the FOURFIRER and OMNIBOT, this deadly
//		missile flies through the air, killing anything it hits
//
//-------------------------------------------------------------------

void aiOmniBotMissileInit(AIEntity *e) {
	e->state = STATE_MOVEDOWN;
	e->aiAction = aiOmniBotMissileAction;
}

void aiOmniBotMissileInit2(AIEntity *e) {
	for (int i = 0; i < e->movedownFrames; i++)
		e->moveleftGfx[i] = e->moverightGfx[i] = e->moveupGfx[i] = e->movedownGfx[i];

	e->moveleftFrames = e->moverightFrames = e->moveupFrames = e->movedownFrames;
	e->draw = e->movedownGfx[0];
}

void aiOmniBotMissileAction(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();

	g_hdb->_ai->animEntFrames(e);
	e->x += e->xVel;
	e->y += e->yVel;
	e->tileX = e->x / kTileWidth;
	e->tileY = e->y / kTileHeight;

	// Did we hit a solid wall?
	int result;
	AIEntity *hit = g_hdb->_ai->legalMoveOverWaterIgnore(e->tileX, e->tileY, e->level, &result, e);

	if (hit || !result) {
		g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 3, ANIM_FAST, false, false, "steam_puff_sit");
		g_hdb->_ai->removeEntity(e);
	}

	// On Even tiles, check for hitting player
	if (onEvenTile(e->x, e->y))
		if (hitPlayer(e->x, e->y) && (p->level == e->level)) {
			g_hdb->_ai->killPlayer(DEATH_NORMAL);
			g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 3, ANIM_FAST, false, false, "steam_puff_sit");
			g_hdb->_ai->removeEntity(e);
		}
}

//-------------------------------------------------------------------
//
//	TURNBOT : Moves straight ahead until it hits a wall, then turns
//		right and continues.
//
//-------------------------------------------------------------------

void aiTurnBotInit(AIEntity *e) {
	e->aiAction = aiTurnBotAction;
}

void aiTurnBotInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiTurnBotChoose(AIEntity *e) {
	static const int xvAhead[5] = { 9, 0, 0, -1, 1 };
	static const int yvAhead[5] = { 9, -1, 1, 0, 0 };
	static const AIDir turnRight[5] = { DIR_NONE, DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN };
	static const AIState dirState[5] = { STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT };

	int xv = xvAhead[e->dir];
	int yv = yvAhead[e->dir];
	if (g_hdb->_map->getMapBGTileFlags(e->tileX + xv, e->tileY + yv) & (kFlagSolid | kFlagWater)) {
		e->xVel = e->yVel = 0;
		e->animFrame = 0;
		e->animDelay = e->animCycle;
		e->dir = turnRight[e->dir];
		e->state = dirState[e->dir];
	} else {
		e->xVel = xv * kPlayerMoveSpeed;
		e->yVel = yv * kPlayerMoveSpeed;
		if (!g_hdb->getActionMode()) {
			e->xVel >>= 1;
			e->yVel >>= 1;
		}
		e->goalX = e->tileX + xv;
		e->goalY = e->tileY + yv;
		e->state = dirState[e->dir];
		if (e->dir == DIR_DOWN)
			e->animFrame = 3;
	}
}

void aiTurnBotAction(AIEntity *e) {
	if (e->goalX)
		g_hdb->_ai->animateEntity(e);
	else {
		aiTurnBotChoose(e);
		g_hdb->_ai->animateEntity(e);
		if (e->onScreen)
			g_hdb->_sound->playSound(SND_TURNBOT_TURN);
	}

	if (e->onScreen && onEvenTile(e->x, e->y) && g_hdb->_ai->checkPlayerCollision(e->x, e->y, 0) && !g_hdb->_ai->playerDead())
		g_hdb->_ai->killPlayer(DEATH_NORMAL);
}

//-------------------------------------------------------------------
//
//	SHOCKBOT : Moves on a path, electrifying all tiles surrounding it
//		that are METAL.  Will pause when changing directions.
//
//-------------------------------------------------------------------

void aiShockBotInit(AIEntity *e) {
	g_hdb->_ai->findPath(e);
	e->aiAction = aiShockBotAction;
	e->animCycle = 0;
	e->sequence = 0;
	e->aiDraw = aiShockBotShock;
}

void aiShockBotInit2(AIEntity *e) {
	e->standupFrames = e->standdownFrames = e->standleftFrames = e->standrightFrames =
		e->moveupFrames = e->moverightFrames = e->moveleftFrames = e->movedownFrames;

	for (int i = 0; i < e->movedownFrames; i++)
		e->standupGfx[i] = e->standleftGfx[i] = e->standrightGfx[i] = e->standdownGfx[i] = e->moveupGfx[i] = e->moveleftGfx[i] = e->moverightGfx[i] = e->movedownGfx[i];

	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiShockBotAction(AIEntity *e) {
	if (e->goalX) {
		if (!e->sequence) {
			if (hitPlayer(e->x, e->y))
				g_hdb->_ai->killPlayer(DEATH_SHOCKED);
			g_hdb->_ai->animateEntity(e);
		} else
			g_hdb->_ai->animEntFrames(e);
	} else {
		g_hdb->_ai->findPath(e);
		e->sequence = 20;
		g_hdb->_ai->animEntFrames(e);
		if (e->onScreen)
			g_hdb->_sound->playSound(SND_SHOCKBOT_AMBIENT);

	}

	if (e->sequence)
		e->sequence--;
}

void aiShockBotShock(AIEntity *e, int mx, int my) {
	static const int offX[8] = { -1, 0, 1, 1, 1, 0, -1, -1 };
	static const int offY[8] = { -1, -1, -1, 0, 1, 1, 1, 0 };

	// Only on a exact tile boundary do we change the shocked tiles
	// Start at top left and go around
	if (g_hdb->_map->getMapBGTileFlags(e->tileX, e->tileY) & kFlagMetal)
		e->special1Gfx[e->animFrame]->drawMasked(e->tileX * kTileWidth - mx, e->tileY * kTileHeight - my);

	for (int i = 0; i < 8; i++) {
		uint32 flags = g_hdb->_map->getMapBGTileFlags(e->tileX + offX[i], e->tileY + offY[i]);
		if (flags & kFlagMetal) {
			// Is the shocking tile onScreen?
			if (g_hdb->_map->checkXYOnScreen((e->tileX + offX[i]) * kTileWidth, (e->tileY + offY[i]) * kTileHeight)) {
				// Draw shocking tile animation
				e->special1Gfx[e->animFrame]->drawMasked((e->tileX + offX[i])*kTileWidth - mx, (e->tileY + offY[i])*kTileHeight - my);
				// Did the player get fried?
				// Check every 4 frames
				if (e->onScreen && !e->animFrame && g_hdb->_ai->checkPlayerTileCollision(e->tileX + offX[i], e->tileY + offY[i]) && !g_hdb->_ai->playerDead()) {
					g_hdb->_ai->killPlayer(DEATH_SHOCKED);
					return;
				}
				if (!e->animFrame && g_hdb->_map->boomBarrelExist(e->tileX + offX[i], e->tileY + offY[i])) {
					AIEntity *e2 = g_hdb->_ai->findEntityType(AI_BOOMBARREL, e->tileX + offX[i], e->tileY + offY[i]);
					aiBarrelExplode(e2);
				}
			}
		}
	}
}

//-------------------------------------------------------------------
//
//	RIGHTBOT
//
//	Rules: Follows the right-hand wall.  That's it!
//
//-------------------------------------------------------------------

void aiRightBotInit(AIEntity *e) {
	e->moveSpeed = kPlayerMoveSpeed;
	if (!g_hdb->getActionMode())
		e->moveSpeed >>= 1;
	e->aiAction = aiRightBotAction;
}

void aiRightBotInit2(AIEntity *e) {
	switch (e->dir) {
	case DIR_UP:
		e->draw = e->moveupGfx[0];
		e->state = STATE_MOVEUP;
		break;
	case DIR_DOWN:
		e->draw = e->movedownGfx[0];
		e->state = STATE_MOVEDOWN;
		break;
	case DIR_LEFT:
		e->draw = e->moveleftGfx[0];
		e->state = STATE_MOVELEFT;
		break;
	case DIR_RIGHT:
		e->draw = e->moverightGfx[0];
		e->state = STATE_MOVERIGHT;
		break;
	case DIR_NONE:
		break;
	}
}

void aiRightBotFindGoal(AIEntity *e) {
	static const int xvAhead[5] = { 9, 0, 0,-1, 1 };
	static const int yvAhead[5] = { 9,-1, 1, 0, 0 };
	static const int xvAToR[5]  = { 9, 1,-1,-1, 1 };
	static const int yvAToR[5]  = { 9,-1, 1,-1, 1 };
	static const int xvToR[5]   = { 9, 1,-1, 0, 0 };
	static const int yvToR[5]   = { 9, 0, 0,-1, 1 };
	static const int xvToL[5]   = { 9,-1, 1, 0, 0 };
	static const int yvToL[5]   = { 9, 0, 0, 1,-1 };

	AIEntity *p = g_hdb->_ai->getPlayer();
	int rotate = 0;

	int	xv, yv;
	int	bg, bg2, bg3;
	AIEntity *e1, *e2, *e3;
	int	sx, sy;

	do {
		xv = xvAhead[e->dir];	// Search Ahead
		yv = yvAhead[e->dir];
		int xv2 = xvAToR[e->dir];	// Search Ahead and to the Right
		int yv2 = yvAToR[e->dir];
		int xv3 = xvToR[e->dir];	// Search to the Right
		int yv3 = yvToR[e->dir];

		// Search until we hit a wall...or empty space to our right (and forward)
		bool hit = false;
		sx = e->tileX;
		sy = e->tileY;

		while (!hit) {
			bg = g_hdb->_map->getMapBGTileFlags(sx + xv, sy + yv) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
			e1 = g_hdb->_ai->findEntity(sx + xv, sy + yv);
			if (e1 && e1 == p)
				e1 = NULL;
			bg2 = g_hdb->_map->getMapBGTileFlags(sx + xv2, sy + yv2) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
			e2 = g_hdb->_ai->findEntity(sx + xv2, sy + yv2);
			if (e2 && e2 == p)
				e2 = NULL;
			bg3 = g_hdb->_map->getMapBGTileFlags(sx + xv3, sy + yv3) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
			e3 = g_hdb->_ai->findEntity(sx + xv3, sy + yv3);
			if (e3 && e3 == p)
				e3 = NULL;

			// Okay to move forward?
			if ((!bg && !e1) && (bg2 || e2 || bg3 || e3)) {
				sx += xv;
				sy += yv;
				rotate = 0;
			} else
				hit = true;
		}

		// Are we stuck in a corner?
		if (sx == e->tileX && sy == e->tileY) {
			sx = e->tileX;
			sy = e->tileY;
			rotate += 1;

			// Need to check for turning RIGHT when we're in a corner
			xv = xvToL[e->dir];
			yv = yvToL[e->dir];

			// Check Tile flags to our left and right
			bg = g_hdb->_map->getMapBGTileFlags(sx + xv, sy + yv) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
			e1 = g_hdb->_ai->findEntity(sx + xv, sy + yv);
			bg2 = g_hdb->_map->getMapBGTileFlags(sx + xv3, sy + yv3) & (kFlagSolid | kFlagWater | kFlagSlime | kFlagSpecial);
			e2 = g_hdb->_ai->findEntity(sx + xv3, sy + yv3);
			if (e1 && e1->type == AI_GUY)
				e1 = NULL;
			if (e2 && e2->type == AI_GUY)
				e2 = NULL;

			// Is tile to the right clear?
			// Is tile to the left clear?
			// If neither, go backwards
			if (!bg2 && !e2) {
				switch (e->dir) {
				case DIR_UP:
					e->dir = DIR_RIGHT;
					break;
				case DIR_DOWN:
					e->dir = DIR_LEFT;
					break;
				case DIR_LEFT:
					e->dir = DIR_UP;
					break;
				case DIR_RIGHT:
					e->dir = DIR_DOWN;
					break;
				case DIR_NONE:
					break;
				}
			} else if (!bg && !e1) {
				switch (e->dir) {
				case DIR_UP:
					e->dir = DIR_LEFT;
					break;
				case DIR_DOWN:
					e->dir = DIR_RIGHT;
					break;
				case DIR_LEFT:
					e->dir = DIR_DOWN;
					break;
				case DIR_RIGHT:
					e->dir = DIR_UP;
					break;
				case DIR_NONE:
					break;
				}
			} else {
				switch (e->dir) {
				case DIR_UP:
					e->dir = DIR_DOWN;
					yv = 1;
					xv = 0;
					break;
				case DIR_DOWN:
					e->dir = DIR_UP;
					yv = -1;
					xv = 0;
					break;
				case DIR_LEFT:
					e->dir = DIR_RIGHT;
					yv = 0;
					xv = 1;
					break;
				case DIR_RIGHT:
					e->dir = DIR_LEFT;
					yv = 0;
					xv = -1;
					break;
				case DIR_NONE:
					break;
				}
				sx += xv;
				sy += yv;
				rotate = 4;
			}
		}
	} while (rotate >= 1 && rotate < 4);

	switch (e->dir) {
	case DIR_UP:
		e->state = STATE_MOVEUP;
		break;
	case DIR_DOWN:
		e->state = STATE_MOVEDOWN;
		break;
	case DIR_LEFT:
		e->state = STATE_MOVELEFT;
		break;
	case DIR_RIGHT:
		e->state = STATE_MOVERIGHT;
		break;
	case DIR_NONE:
		break;
	}

	e->goalX = sx;
	e->goalY = sy;
	e->xVel = xv * e->moveSpeed;
	e->yVel = yv * e->moveSpeed;
	if (e->onScreen)
		g_hdb->_sound->playSound(SND_RIGHTBOT_TURN);
}

void aiRightBotAction(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();

	if (e->goalX) {
		if (e->onScreen && g_hdb->_ai->checkPlayerCollision(e->x, e->y, 0) && p->state != STATE_DEAD && p->level == e->level && !g_hdb->_ai->playerDead())
			g_hdb->_ai->killPlayer(DEATH_NORMAL);
		g_hdb->_ai->animateEntity(e);
	} else {
		aiRightBotFindGoal(e);
		g_hdb->_ai->animEntFrames(e);
	}
}

//-------------------------------------------------------------------
//
//	PUSHBOT : Very simple, this guy goes forward and pushes anything in his
//		path all the way until it can't go any further.  Then, he turns 180
//		degress and comes back until he can't go any further.  Then... he
//		turns 180 degrees and does it all over again!
//
//-------------------------------------------------------------------

void aiPushBotInit(AIEntity *e) {
	if (e->value1 != 1)
		e->aiAction = aiPushBotAction;
}

void aiPushBotInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiPushBotAction(AIEntity *e) {
	static const AIState moveState[5] = { STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT };
	static const int xvAhead[5] = { 9, 0, 0,-1, 1 };
	static const int yvAhead[5] = { 9,-1, 1, 0, 0 };
	static const AIDir oneEighty[5] = { DIR_NONE, DIR_DOWN, DIR_UP, DIR_RIGHT, DIR_LEFT };

	AIEntity *e1 = nullptr;

	if (e->goalX) {
		g_hdb->_ai->animateEntity(e);
		if (hitPlayer(e->x, e->y))
			g_hdb->_ai->killPlayer(DEATH_NORMAL);
	} else {
		if (hitPlayer(e->x, e->y))
			g_hdb->_ai->killPlayer(DEATH_NORMAL);

		// Where to go next
		int nx = e->tileX + xvAhead[e->dir];
		int ny = e->tileY + yvAhead[e->dir];

		int result;
		e1 = g_hdb->_ai->legalMove(nx, ny, e->level, &result);

		// Push something
		// Turn Around
		// Move Forward
		if (e1 && onEvenTile(e1->x, e1->y) && (e1->type == AI_LIGHTBARREL || e1->type == AI_HEAVYBARREL || e1->type == AI_BOOMBARREL || e1->type == AI_CRATE)) {
			// Actually going over a floating crate?
			if (e1 && (e1->state == STATE_FLOATING || e1->state == STATE_MELTED)) {
				e->state = moveState[e->dir];
				g_hdb->_ai->setEntityGoal(e, nx, ny);
				g_hdb->_ai->animateEntity(e);
				return;
			}

			int nx2 = nx + xvAhead[e->dir];
			int ny2 = ny + yvAhead[e->dir];

			uint32 bgFlags = g_hdb->_map->getMapBGTileFlags(nx2, ny2);
			uint32 fgFlags = g_hdb->_map->getMapFGTileFlags(nx2, ny2);
			AIEntity *e2 = g_hdb->_ai->findEntity(nx2, ny2);
			result = (e->level == 1) ? (bgFlags & kFlagSolid) : !(fgFlags & kFlagGrating) && (bgFlags & kFlagSolid);

			// If we're going to push something onto a floating thing, that's ok
			if (e2 && (e2->state == STATE_FLOATING || e2->state == STATE_MELTED))
				e2 = nullptr;

			// If no walls in front & no entities
			if (!result && !e2 && e1->state != STATE_EXPLODING) {
				e->state = moveState[e->dir];
				g_hdb->_ai->setEntityGoal(e, nx, ny);

				e1->dir = e->dir;
				e1->state = e->state;
				e1->moveSpeed = e->moveSpeed;
				g_hdb->_ai->setEntityGoal(e1, nx2, ny2);
				switch (e1->type) {
				case AI_CRATE:
					g_hdb->_sound->playSound(SND_CRATE_SLIDE);
					break;
				case AI_HEAVYBARREL:
				case AI_BOOMBARREL:
					g_hdb->_sound->playSound(SND_HEAVY_SLIDE);
					break;
				case AI_LIGHTBARREL:
					g_hdb->_sound->playSound(SND_LIGHT_SLIDE);
					break;
				default:
					break;
				}
			} else {
				if (e->onScreen)
					g_hdb->_sound->playSound(SND_PUSHBOT_STRAIN);
				e->dir = oneEighty[e->dir];
				e->state = moveState[e->dir];
				nx = e->tileX + xvAhead[e->dir];
				ny = e->tileY + yvAhead[e->dir];
				e1 = g_hdb->_ai->legalMove(nx, ny, e->level, &result);
				if (!e1 && result)
					g_hdb->_ai->setEntityGoal(e, nx, ny);
			}
		} else if (!result || (e1 && !onEvenTile(e1->x, e1->y))) {
			e->dir = oneEighty[e->dir];
			e->state = moveState[e->dir];
			nx = e->tileX + xvAhead[e->dir];
			ny = e->tileY + yvAhead[e->dir];
			e1 = g_hdb->_ai->legalMove(nx, ny, e->level, &result);
			if (!e1 && result)
				g_hdb->_ai->setEntityGoal(e, nx, ny);
		} else {
			e->state = moveState[e->dir];
			g_hdb->_ai->setEntityGoal(e, nx, ny);
		}
		g_hdb->_ai->animateEntity(e);
	}
}

//-------------------------------------------------------------------
//
//	RAILRIDER : crazy green goopy dude -- he gives you rides on his
//		special track!
//
//-------------------------------------------------------------------

void aiRailRiderInit(AIEntity *e) {
	if (e->type == AI_RAILRIDER_ON) {
		// On the tracks already - spawn RED arrow
		g_hdb->_ai->addToPathList(e->tileX, e->tileY, 0, e->dir);
		e->state = STATE_STANDUP;
		e->aiAction = aiRailRiderOnAction;
		e->aiUse = aiRailRiderOnUse;
	} else {
		e->state = STATE_STANDDOWN;
		e->sequence = 0;
		e->aiAction = aiRailRiderAction;
		e->aiUse = aiRailRiderUse;
	}
	e->moveSpeed = kPlayerMoveSpeed;
}

void aiRailRiderInit2(AIEntity *e) {
	e->draw = e->standdownGfx[0];
}

// Talking to RailRider off track
void aiRailRiderUse(AIEntity *e) {
	e->sequence = 1;
}

void aiRailRiderAction(AIEntity *e) {
	switch (e->sequence) {
		// Waiting for Dialog to goaway
	case 1:
		// Dialog gone?
		if (!g_hdb->_window->dialogActive()) {
			e->sequence = 2;
			switch (e->dir) {
			case DIR_UP:
				e->xVel = 0;
				e->yVel = -1;
				break;
			case DIR_DOWN:
				e->xVel = 0;
				e->yVel = 1;
				break;
			case DIR_LEFT:
				e->xVel = -1;
				e->yVel = 0;
				break;
			case DIR_RIGHT:
				e->xVel = 1;
				e->yVel = 0;
				break;
			case DIR_NONE:
				break;
			}
		}
		break;
		// Walking over to track
	case 2:
		e->x += e->xVel;
		e->y += e->yVel;
		if (onEvenTile(e->x, e->y)) {
			ArrowPath *arrowPath;
			e->tileX = e->x / kTileWidth;
			e->tileY = e->y / kTileHeight;
			e->sequence = 3;	// Wait for use
			e->type = AI_RAILRIDER_ON;
			e->state = STATE_STANDUP;
			e->aiAction = aiRailRiderOnAction;
			e->aiUse = aiRailRiderOnUse;
			arrowPath = g_hdb->_ai->findArrowPath(e->tileX, e->tileY);
			e->dir = arrowPath->dir;
			e->value1 = 0;	// Not in a tunnel
		}
		break;
	}

	// Cycle through animation frames
	if (e->animDelay-- > 0)
		return;
	e->animDelay = e->animCycle;
	e->animFrame++;
	if (e->animFrame == e->standdownFrames)
		e->animFrame = 0;

	e->draw = e->standdownGfx[e->animFrame];
}

// Talking to RailRider on track
void aiRailRiderOnUse(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();

	if (p->tileX == e->tileX) {
		if (p->tileY > e->tileY)
			g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
		else
			g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
	} else if (p->tileX > e->tileX)
		g_hdb->_ai->setEntityGoal(p, p->tileX - 1, p->tileY);
	else
		g_hdb->_ai->setEntityGoal(p, p->tileX + 1, p->tileY);

	e->sequence = -1;	// Waiting for player to board
}

void aiRailRiderOnAction(AIEntity *e) {
	static const int xv[5] = { 9, 0, 0, -1, 1 };
	static const int yv[5] = { 9, -1, 1, 0, 0 };

	AIEntity*p = g_hdb->_ai->getPlayer();

	switch (e->sequence) {
	// Player is boarding
	case -1:
		if (!p->goalX)
			e->sequence = 1; // Boarded yet?
		// fallthrough
	// Cycle Animation Frames
	case 3:
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle;
		e->animFrame++;
		if (e->animFrame == e->standupFrames)
			e->animFrame = 0;

		e->draw = e->standupGfx[e->animFrame];
		break;
	// Player is in - lock him
	case 1:
		g_hdb->_ai->setPlayerInvisible(true);
		g_hdb->_ai->setPlayerLock(true);
		g_hdb->_ai->setEntityGoal(e, e->tileX + xv[e->dir], e->tileY + yv[e->dir]);
		g_hdb->_sound->playSound(SND_RAILRIDER_TASTE);
		e->sequence = 2;
		e->value1 = 0;
		// fallthrough

	// New RailRider gfx
	// Move the RailRider
	case 2: {
		// Done moving to next spot?
		if (!e->goalX) {
			ArrowPath *arrowPath = g_hdb->_ai->findArrowPath(e->tileX, e->tileY);
			if (arrowPath) {
				// Stop Arrow?
				if (!arrowPath->type) {
					HereT *h;
					e->sequence = 4;	// Get Player off RailRider - RIGHT SIDE ONLY
					p->tileX = e->tileX;
					p->tileY = e->tileY;
					p->x = e->x;
					p->y = e->y;
					// Try to find a HERE icon to either side of the track and go there
					switch (e->dir) {
					case DIR_UP:
						h = g_hdb->_ai->findHere(e->tileX - 1, e->tileY);
						if (h)
							g_hdb->_ai->setEntityGoal(p, e->tileX - 1, e->tileY);
						else
							g_hdb->_ai->setEntityGoal(p, e->tileX + 1, e->tileY);
						break;
					case DIR_DOWN:
						h = g_hdb->_ai->findHere(e->tileX + 1, e->tileY);
						if (h)
							g_hdb->_ai->setEntityGoal(p, e->tileX + 1, e->tileY);
						else
							g_hdb->_ai->setEntityGoal(p, e->tileX - 1, e->tileY);
						break;
					case DIR_LEFT:
						h = g_hdb->_ai->findHere(e->tileX, e->tileY + 1);
						if (h)
							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY + 1);
						else
							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY - 1);
						break;
					case DIR_RIGHT:
						h = g_hdb->_ai->findHere(e->tileX, e->tileY - 1);
						if (h)
							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY - 1);
						else
							g_hdb->_ai->setEntityGoal(p, e->tileX, e->tileY + 1);
						break;
					case DIR_NONE:
						break;
					}
					g_hdb->_ai->setPlayerInvisible(false);
					g_hdb->_sound->playSound(SND_RAILRIDER_EXIT);
				} else if (arrowPath->type == 1) {
					e->dir = arrowPath->dir;
					g_hdb->_ai->setEntityGoal(e, e->tileX + xv[e->dir], e->tileY + yv[e->dir]);
				}
			} else
				g_hdb->_ai->setEntityGoal(e, e->tileX + xv[e->dir], e->tileY + yv[e->dir]);

			g_hdb->_sound->playSound(SND_RAILRIDER_ONTRACK);
		}

		p->tileX = e->tileX;
		p->tileY = e->tileY;
		p->x = e->x;
		p->y = e->y;
		g_hdb->_ai->animateEntity(e);
		switch (e->dir) {
		case DIR_UP:
			e->draw = e->moveupGfx[0];
			break;
		case DIR_DOWN:
			e->draw = e->movedownGfx[0];
			break;
		case DIR_LEFT:
			e->draw = e->moveleftGfx[0];
			break;
		case DIR_RIGHT:
			e->draw = e->moverightGfx[0];
			break;
		default:
			break; // DIR_NONE
		}
		g_hdb->_map->centerMapXY(e->x + 16, e->y + 16);

		SingleTele t;
		// Did we hit a tunnel entrance?
		if (onEvenTile(e->x, e->y) && g_hdb->_ai->findTeleporterDest(e->tileX, e->tileY, &t) && !e->value1 && !e->dir2) {
			// Set tunnel destination
			e->value1 = t.x;
			e->value2 = t.y;
			e->dir2 = (AIDir)(t.x + t.y);	// Flag for coming out of tunnel
		}

		// Are we going through a tunnel?
		if (e->value1) {
			// Reach the End?
			// If not, don't draw RailRider
			if (onEvenTile(e->x, e->y) && e->tileX == e->value1 && e->tileY == e->value2)
				e->value1 = 0;
			else
				e->draw = NULL;
		} else if (e->dir2 && e->dir2 != (AIDir)(e->tileX + e->tileY))
			e->dir2 = DIR_NONE;
		break;
		}
	// Waiting for Player to move to Dest
	case 4:
		if (!p->goalX) {
			g_hdb->_ai->setPlayerLock(false);
			e->sequence = 3;	// Wait for Use
		}

		// Cycle Animation frames
		if (e->animDelay-- > 0)
			return;

		e->animDelay = e->animCycle;
		e->animFrame++;
		if (e->animFrame == e->standupFrames)
			e->animFrame = 0;

		e->draw = e->standupGfx[e->animFrame];
		break;
	}
}

//-------------------------------------------------------------------
//
//	MAINTBOT : This little fella likes to cause trouble!  He just jubs
//		around the map and looks for stuff to press.  Touch him and you die.
//
//-------------------------------------------------------------------

void aiMaintBotInit(AIEntity *e) {
	// value1 field determines whether the "MMM!" sound plays
	// 1 means NO
	e->int1 = e->value1;
	e->aiAction = aiMaintBotAction;
	e->value1 = 0;
	g_hdb->_ai->findPath(e);
}

void aiMaintBotInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiMaintBotAction(AIEntity *e) {
	static const AIState useState[5]   = {STATE_NONE, STATE_USEUP, STATE_USEDOWN, STATE_USELEFT, STATE_USERIGHT};
	static const AIState standState[5] = {STATE_NONE, STATE_STANDUP, STATE_STANDDOWN, STATE_STANDLEFT, STATE_STANDRIGHT};
	static const int xvAhead[5]  = {9, 0, 0,-1, 1};
	static const int yvAhead[5]  = {9,-1, 1, 0, 0};
	static const int whistles[3] = {SND_MBOT_WHISTLE1, SND_MBOT_WHISTLE2, SND_MBOT_WHISTLE3};
	static const AIDir lookRight[5] = {DIR_NONE, DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN};
	static const AIDir lookLeft[5] = {DIR_NONE, DIR_LEFT, DIR_RIGHT, DIR_DOWN, DIR_UP};
	static const AIDir dirList[5] = {DIR_NONE, DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT};

	// Waiting at an arrow (or hit by player)?
	if (e->sequence) {
		e->sequence--;
		g_hdb->_ai->animEntFrames(e);

		// Use Something here
		if (!e->value2)
			switch (e->sequence) {
			case 50:
				if (e->onScreen && !e->int1 && !g_hdb->isDemo()) {
					if (g_hdb->_rnd->getRandomNumber(1))
						g_hdb->_sound->playSound(SND_MBOT_HMMM2);
					else
						g_hdb->_sound->playSound(SND_MBOT_HMMM);
				}
				break;
			// Need to USE the object
			case 30: {
				e->state = useState[e->dir];
				int nx = e->tileX + xvAhead[e->dir];
				int ny = e->tileY + yvAhead[e->dir];
				AIEntity *it = g_hdb->_ai->findEntity(nx, ny);
				if (it) {
					if (e->onScreen)
						e->value1 = 1;
					g_hdb->useEntity(it);
					break;
				}
				// Did the MaintBot use an Action Tile?
				if (g_hdb->_ai->checkActionList(e, nx, ny, true)) {
					if (e->onScreen)
						e->value1 = 1;
					break;
				}
				// Did the MaintBot use an AutoAction Tile?
				if (g_hdb->_ai->checkAutoList(e, nx, ny)) {
					if (e->onScreen)
						e->value1 = 1;
					break;
				}
				// Did the MaintBot use a LUA Tile?
				if (g_hdb->_ai->checkLuaList(e, nx, ny)) {
					if (e->onScreen)
						e->value1 = 1;
					break;
				}
				break;
				}
			// Play a sound if we used something
			case 25:
				e->value1 = 0;
				break;
			// Change to Standing frames
			case 20:
				e->state = standState[e->dir];
				break;
			// All done - find a new path
			case 0:
				e->dir = e->dir2;
				g_hdb->_ai->findPath(e);
				g_hdb->_ai->animateEntity(e);
				break;
			}
		// Deciding where to go at 4-way
		else {
			switch (e->sequence) {
			// HMM
			case 50:
				if (e->onScreen && !e->int1 && !g_hdb->isDemo())
					g_hdb->_sound->playSound(SND_MBOT_HMMM);
				break;
			// Look Right
			case 40:
				e->dir = lookRight[e->dir2];
				e->state = standState[e->dir];
				break;
			// Look Left
			case 30:
				e->dir = lookLeft[e->dir];
				e->state = standState[e->dir];
				break;
			// HMM2
			case 25:
				if (e->onScreen && !e->int1 && !g_hdb->isDemo())
					g_hdb->_sound->playSound(SND_MBOT_HMMM2);
				break;
			// Decide direction and GO
			case 0:
				int dir = (g_hdb->_rnd->getRandomNumber(3)) + 1;
				e->dir = dirList[dir];
				g_hdb->_ai->findPath(e);
				if (e->onScreen && !g_hdb->isDemo())
					g_hdb->_sound->playSound(whistles[g_hdb->_rnd->getRandomNumber(2)]);
				break;
			}
		}
		return;
	}

	// Moving already, keep going
	if (e->goalX) {
		g_hdb->_ai->animateEntity(e);
		if (hitPlayer(e->x, e->y)) {
			g_hdb->_ai->killPlayer(DEATH_GRABBED);
			if (!g_hdb->isDemo())
				g_hdb->_sound->playSound(SND_MBOT_DEATH);
		}
	} else {
		// Check if there's an arrow UNDER the bot, and if its RED
		// If so, turn in that direction and use something
		ArrowPath *ar = g_hdb->_ai->findArrowPath(e->tileX, e->tileY);
		if (ar) {
			// STOP		Arrow
			// GO		Arrow
			// 4-way	Arrow
			if (!ar->type) {
				e->dir2 = e->dir; // dir2 holds the last direction we were travelling in
				e->dir = ar->dir;
				e->sequence = 64; // sequence is the timer of events
				e->state = standState[e->dir];
				e->value2 = 0;
				return;
			} else if (ar->type == 1) {
				g_hdb->_ai->findPath(e);
				if (!g_hdb->isDemo())
					g_hdb->_sound->playSound(whistles[g_hdb->_rnd->getRandomNumber(2)]);
			} else {
				e->sequence = 64;
				e->dir2 = e->dir;
				e->value2 = 1;
				return;
			}
		}
		g_hdb->_ai->animateEntity(e);
	}
}

//-------------------------------------------------------------------
//
//	FOURFIRER : This bot turns and fires in the direction it's facing,
//		but only if the player is visible
//
//-------------------------------------------------------------------

void aiFourFirerInit(AIEntity *e) {
	e->value1 = 0;
	e->aiAction = aiFourFirerAction;
}

void aiFourFirerInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiFourFirerAction(AIEntity *e) {
	static const AIState state[5] = {STATE_NONE, STATE_STANDUP, STATE_STANDDOWN, STATE_STANDLEFT, STATE_STANDRIGHT};
	static const AIDir turn[5] = {DIR_NONE, DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN};

	AIEntity *p = g_hdb->_ai->getPlayer();
	// Time to turn right?
	if (!e->value1) {
		e->dir = turn[e->dir];
		e->state = state[e->dir];
		e->value1 = 16;
		if (e->onScreen)
			g_hdb->_sound->playSound(SND_FOURFIRE_TURN);
	}
	e->value1--;

	// Waiting before firing again?
	if (e->sequence) {
		e->sequence--;
		return;
	}

	g_hdb->_ai->animEntFrames(e);

	// Can we see the player on the same level?
	if ((e->level != p->level) || g_hdb->_ai->playerDead() || !e->onScreen)
		return;

	// Check player direction
	bool shoot = false;
	int xv = 0;
	int yv = 0;

	switch (e->dir) {
	case DIR_UP:
		if (p->x == e->x && p->y < e->y) {
			shoot = true;
			yv = -1; }
		break;
	case DIR_DOWN:
		if (p->x == e->x && p->y > e->y) {
			shoot = true;
			yv = 1;
		}
		break;
	case DIR_LEFT:
		if (p->y == e->y && p->x < e->x) {
			shoot = true;
			xv = -1;
		}
		break;
	case DIR_RIGHT:
		if (p->y == e->y && p->x > e->x) {
			shoot = true;
			xv = 1;
		}
		break;
	case DIR_NONE:
		break;
	}

	// Shoot if needed
	// Make sure not shooting into solid tile
	// Make sure if shooting at entity it is the player
	int result;
	AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + xv, e->tileY + yv, e->level, &result);
	if (hit && hit->type == AI_GUY)
		hit = nullptr;

	if (shoot && !hit && result) {
		AIEntity *fire = g_hdb->_ai->spawn(AI_OMNIBOT_MISSILE, e->dir, e->tileX + xv, e->tileY + yv, NULL, NULL, NULL, DIR_NONE, e->level, 0, 0, 1);
		if (g_hdb->_map->onScreen(e->tileX, e->tileY))
			g_hdb->_sound->playSound(SND_FOUR_FIRE);
		fire->xVel = xv * kPlayerMoveSpeed * 2;
		fire->yVel = yv * kPlayerMoveSpeed * 2;
		if (!g_hdb->getActionMode()) {
			fire->xVel >>= 1;
			fire->yVel >>= 1;
		}
		e->sequence = 16;
		if (hitPlayer(fire->tileX*kTileWidth, fire->tileY*kTileHeight))
			g_hdb->_ai->killPlayer(DEATH_FRIED);
	}
}

//-------------------------------------------------------------------
//
//	DEADEYE : Crazy attack dog with Chompie(tm) sounds!  Will sit in one spot
//		looking around, then run in a random direction and distance.  If, while
//		scanning, Deadeye sees the player, he goes nuts and attacks!
//
//-------------------------------------------------------------------

void aiDeadEyeInit(AIEntity *e) {
	e->sequence = 64;
	e->blinkFrames = e->goalX = 0;
	if (e->value1 == 1)
		e->aiAction = aiDeadEyeWalkInPlace;
	else
		e->aiAction = aiDeadEyeAction;
}

void aiDeadEyeInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiDeadEyeWalkInPlace(AIEntity *e) {
	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};

	e->sequence--;

	switch (e->sequence) {
	case 50:
	case 40:
	case 30:
	case 20:
	case 10:
		{
			int rnd = g_hdb->_rnd->getRandomNumber(3) + 1;
			e->dir = (AIDir)rnd;
			e->state = state[rnd];
			if (e->onScreen) {
				if (e->sequence == 50)
					g_hdb->_sound->playSound(SND_DEADEYE_AMB01);
				else if (e->sequence == 10)
					g_hdb->_sound->playSound(SND_DEADEYE_AMB02);
			}
		}
		break;
	case 0:
		e->sequence = 64;
	}
	g_hdb->_ai->animEntFrames(e);
}

void aiDeadEyeAction(AIEntity *e) {
	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
	static const int xvAhead[5] = {9, 0, 0, -1, 1};
	static const int yvAhead[5] = {9, -1, 1, 0, 0};

	if (e->sequence) {
		e->sequence--;

		if (e->blinkFrames)	// Between attacks timer
			e->blinkFrames--;

		// Is player visible to us?
		AIEntity *p = g_hdb->_ai->getPlayer();
		if (e->onScreen && p->level == e->level && !e->blinkFrames) {
			bool nuts = false;
			switch (e->dir) {
			case DIR_UP:
				if (p->tileX == e->tileX && p->tileY < e->tileY)
					nuts = true;
				break;
			case DIR_DOWN:
				if (p->tileX == e->tileX && p->tileY > e->tileY)
					nuts = true;
				break;
			case DIR_LEFT:
				if (p->tileY == e->tileY && p->tileX < e->tileX)
					nuts = true;
				break;
			case DIR_RIGHT:
				if (p->tileY == e->tileY && p->tileX > e->tileX)
					nuts = true;
				break;
			case DIR_NONE:
				break;
			}

			// Did we see the player (and we're done moving)?
			if (nuts && e->aiAction != aiDeadEyeWalkInPlace) {
				e->sequence = 0;
				e->blinkFrames = 20;

				int xv = xvAhead[e->dir];
				int yv = yvAhead[e->dir];
				int newX = e->tileX + xv;
				int newY = e->tileY + yv;

				bool okToMove = false;
				bool done = false;
				do {
					int result;
					AIEntity *hit = g_hdb->_ai->legalMove(newX, newY, e->level, &result);
					if (hit && hit->type == AI_GUY)
						hit = nullptr;
					if (result && !hit) {
						okToMove = true;
						newX += xv;
						newY += yv;
						if (newX == p->tileX && newY == p->tileY)
							done = true;
					} else {
						newX -= xv;
						newY -= yv;
						done = true;
					}
				} while (!done);

				// If we can move in the direction of the player, set our goal at him
				if (okToMove) {
					e->moveSpeed = kPlayerMoveSpeed << 1;
					g_hdb->_ai->setEntityGoal(e, newX, newY);
					(p->tileX & 1) ? g_hdb->_sound->playSound(SND_DEADEYE_ATTACK01) : g_hdb->_sound->playSound(SND_DEADEYE_ATTACK02);
				}
				g_hdb->_ai->animateEntity(e);
				return;
			}
		}

		switch (e->sequence) {
		// Look around
		case 50:
		case 40:
		case 30:
		case 20:
		case 10:
			{
				int dir = g_hdb->_rnd->getRandomNumber(3) + 1;
				e->dir = (AIDir)dir;
				e->state = state[dir];

				if (e->onScreen) {
					if (e->sequence == 50)
						g_hdb->_sound->playSound(SND_DEADEYE_AMB01);
					else if (e->sequence == 10)
						g_hdb->_sound->playSound(SND_DEADEYE_AMB02);
				}
			}
			break;
		case 0:
			// Pick a random direction and random number of tiles in that direction
			int dir = g_hdb->_rnd->getRandomNumber(3) + 1;
			int walk = g_hdb->_rnd->getRandomNumber(4) + 1;

			e->dir = (AIDir)dir;
			e->state = state[dir];

			int xv = xvAhead[dir] * walk;
			if (e->tileX + xv < 1)
				xv = 1 - e->tileX;
			if (e->tileX + xv > g_hdb->_map->_width)
				xv = g_hdb->_map->_width - e->tileX - 1;

			int yv = yvAhead[dir] * walk;
			if (e->tileY + yv < 1)
				yv = 1 - e->tileY;
			if (e->tileY + yv > g_hdb->_map->_height)
				yv = g_hdb->_map->_height - e->tileY - 1;

			e->value1 = xvAhead[dir];
			e->value2 = yvAhead[dir];
			e->moveSpeed = kPlayerMoveSpeed;
			int result;
			AIEntity *hit = g_hdb->_ai->legalMove(e->tileX + xvAhead[e->dir], e->tileY + yvAhead[e->dir], e->level, &result);
			if (hit && hit->type == AI_GUY)
				hit = nullptr;

			if (!hit && result)
				g_hdb->_ai->setEntityGoal(e, e->tileX + xv, e->tileY + yv);
			break;
		}
		g_hdb->_ai->animEntFrames(e);
		return;
	}

	// In the process of moving around
	if (e->goalX) {
		// Hit the player
		if (hitPlayer(e->x, e->y)) {
			g_hdb->_ai->killPlayer(DEATH_GRABBED);
			return;
		}
		// Did we run into a wall, entity, water, slime etc?
		// If so, Pick new direction
		if (onEvenTile(e->x, e->y)) {
			int result;
			AIEntity *hit = g_hdb->_ai->legalMove(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
			if (hit && hit->type == AI_GUY)
				hit = nullptr;
			if (!result || hit) {
				g_hdb->_ai->stopEntity(e);
				e->state = STATE_MOVEDOWN;
				e->sequence = 64;
				return;
			}
		}
		g_hdb->_ai->animateEntity(e);
	} else
		// If not, start looking around
		e->sequence = 64;
}

//-------------------------------------------------------------------
//
//	LASER
//
//-------------------------------------------------------------------

void aiLaserInit(AIEntity *e) {
	e->aiDraw = aiLaserDraw;
	// start & end of laser beam
	e->value1 = e->value2 = 0;
}

void aiLaserInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
	if (!g_hdb->_ai->_gfxLaserbeamUD[0]) {
		char name[64];
		for (int i = 0; i < 4; i++) {
			sprintf(name, FORCEFIELD_UD"0%d", i + 1);
			g_hdb->_ai->_gfxLaserbeamUD[i] = g_hdb->_gfx->loadTile(name);
			sprintf(name, FORCESPLASH_TOP"0%d", i + 1);
			g_hdb->_ai->_gfxLaserbeamUDTop[i] = g_hdb->_gfx->loadTile(name);
			sprintf(name, FORCESPLASH_BTM"0%d", i + 1);
			g_hdb->_ai->_gfxLaserbeamUDBottom[i] = g_hdb->_gfx->loadTile(name);
			sprintf(name, FORCEFIELD_LR"0%d", i + 1);
			g_hdb->_ai->_gfxLaserbeamLR[i] = g_hdb->_gfx->loadTile(name);
			sprintf(name, FORCESPLASH_LEFT"0%d", i + 1);
			g_hdb->_ai->_gfxLaserbeamLRLeft[i] = g_hdb->_gfx->loadTile(name);
			sprintf(name, FORCESPLASH_RIGHT"0%d", i + 1);
			g_hdb->_ai->_gfxLaserbeamLRRight[i] = g_hdb->_gfx->loadTile(name);
		}
	}
}

void aiLaserAction(AIEntity *e) {
	static const int xva[] = {9, 0, 0,-1, 1};
	static const int yva[] = {9,-1, 1, 0, 0};

	AIEntity *hit = e;
	int moveOK = 0;
	int moveCount = 0;

	do {
		int nx = hit->tileX;
		int ny = hit->tileY;

		if (hit->type != AI_DIVERTER) {
			hit->int1 = xva[hit->dir];
			hit->int2 = yva[hit->dir];

			if (hit->dir == DIR_UP || hit->dir == DIR_DOWN)
				hit->value1 = ny;
			else
				hit->value1 = nx;
		} else {
			// diverter is on y-plane?
			if (hit->tileX == e->tileX) {
				hit->value1 = nx;
				hit->int2 = 0;
				switch (hit->dir2) {
				case DIR_UP:
					hit->int1 = 1;
					break;
				case DIR_DOWN:
					hit->int1 = -1;
					break;
				case DIR_LEFT:
					hit->int1 = -1;
					break;
				case DIR_RIGHT:
					hit->int1 = 1;
					break;
				case DIR_NONE:
					break;
				}
			} else {
				// diverter is on x-plane
				hit->value1 = ny;
				hit->int1 = 0;
				switch (hit->dir2) {
				case DIR_UP:
					hit->int2 = 1;
					break;
				case DIR_DOWN:
					hit->int2 = 1;
					break;
				case DIR_LEFT:
					hit->int2 = -1;
					break;
				case DIR_RIGHT:
					hit->int2 = -1;
					break;
				case DIR_NONE:
					break;
				}
			}
		}
		e = hit;

		//
		// scan for all legal moves
		//
		do {
			nx += e->int1;
			ny += e->int2;
			hit = g_hdb->_ai->legalMoveOverWater(nx, ny, e->level, &moveOK);
			g_hdb->_map->setLaserBeam(nx, ny, 1);
			if (hit && hit->type != AI_LASERBEAM) {
				//
				// hit player = death
				//
				if (hit == g_hdb->_ai->getPlayer() && onEvenTile(hit->x, hit->y) && !g_hdb->_ai->playerDead())
					g_hdb->_ai->killPlayer(DEATH_FRIED);
				else if (hit->type == AI_BOOMBARREL && hit->state != STATE_EXPLODING && onEvenTile(hit->x, hit->y)) {
					// hit BOOM BARREL = explodes
					aiBarrelExplode(hit);
					aiBarrelBlowup(hit, nx, ny);
				} else if (hit->type == AI_LIGHTBARREL || hit->type == AI_HEAVYBARREL || hit->type == AI_CRATE) {
					// hit LIGHT/HEAVY BARREL = blocking
					moveOK = 0;
				} else if (hit->type == AI_DIVERTER) {
					// hit a diverter?
					moveOK = 0;
				} else if (onEvenTile(hit->x, hit->y) && hit != g_hdb->_ai->getPlayer()) {
					switch (hit->type) {
					// cannot kill Vortexians!
					case AI_VORTEXIAN:
						continue;

					case AI_BOOMBARREL:
						if (hit->state == STATE_EXPLODING)
							continue;
						break;
					case AI_LASER:
						g_hdb->_ai->_laserRescan = true;
						break;
					case ITEM_KEYCARD_WHITE:
					case ITEM_KEYCARD_BLUE:
					case ITEM_KEYCARD_RED:
					case ITEM_KEYCARD_GREEN:
					case ITEM_KEYCARD_PURPLE:
					case ITEM_KEYCARD_BLACK:
					case ITEM_CABKEY:
						g_hdb->_window->centerTextOut("CARD DESTROYED!", 306, 5 * 60);
						g_hdb->_sound->playSound(SND_QUEST_FAILED);
						break;
					default:
						break;
					}
					g_hdb->_ai->removeEntity(hit);
					g_hdb->_ai->addAnimateTarget(nx * kTileWidth,
						ny * kTileHeight, 0, 3, ANIM_NORMAL, false, false, GROUP_EXPLOSION_BOOM_SIT);
					g_hdb->_sound->playSound(SND_BARREL_EXPLODE);
				}
			}
		} while (moveOK);

		if (e->int2) {
			e->value2 = ny;
			// check for hitting the BACK of a Diverter.  It stops the laser.
			if (hit && hit->type == AI_DIVERTER) {
				if (e->int2 < 0 && hit->state != STATE_DIVERTER_BL && hit->state != STATE_DIVERTER_BR)
					hit = NULL;
				else if (e->int2 > 0 && hit->state != STATE_DIVERTER_TL && hit->state != STATE_DIVERTER_TR)
					hit = NULL;
			}
		} else {
			e->value2 = nx;
			// check for hitting the BACK of a Diverter.  It stops the laser.
			if (hit && hit->type == AI_DIVERTER) {
				if (e->int1 < 0 && hit->state != STATE_DIVERTER_BR && hit->state != STATE_DIVERTER_TR)
					hit = NULL;
				else if (e->int1 > 0 && hit->state != STATE_DIVERTER_TL && hit->state != STATE_DIVERTER_BL)
					hit = NULL;
			}
		}

		moveCount++;

		// It is possible to set a configuration which leads to a closed loop.
		// Thus, we're breaking it here
		if (moveCount > 1000)
			hit = NULL;
	} while (hit && hit->type == AI_DIVERTER);
}

void aiLaserDraw(AIEntity *e, int mx, int my) {
	int	i;
	int	frame = e->movedownFrames & 3;
	int onScreen = 0;

	switch (e->dir) {
	case DIR_UP:
	{
		for (i = e->value1 - 1; i > e->value2; i--)
			onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileWidth - my);
		onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame & 3]->drawMasked(e->x - mx, i * kTileWidth - my);
		if (onScreen) {
			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
			g_hdb->_ai->_laserOnScreen = true;
		}
	}
		break;
	case DIR_DOWN:
	{
		for (i = e->value1 + 1; i < e->value2; i++)
			onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileWidth - my);
		onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame]->drawMasked(e->x - mx, i * kTileWidth - my);
		if (onScreen) {
			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
			g_hdb->_ai->_laserOnScreen = true;
		}
	}
		break;
	case DIR_LEFT:
	{
		for (i = e->value1 - 1; i > e->value2; i--)
			onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
		onScreen += g_hdb->_ai->_gfxLaserbeamLRRight[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
		if (onScreen) {
			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
			g_hdb->_ai->_laserOnScreen = true;
		}
	}
		break;
	case DIR_RIGHT:
	{
		for (i = e->value1 + 1; i < e->value2; i++)
			onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
		onScreen += g_hdb->_ai->_gfxLaserbeamLRLeft[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
		if (onScreen) {
			g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
			g_hdb->_ai->_laserOnScreen = true;
		}
	}
		break;
	default:
		break;
	}
	e->movedownFrames++;
}

//-------------------------------------------------------------------
//
//	DIVERTER
//
//-------------------------------------------------------------------

void aiDiverterInit(AIEntity *e) {
	e->aiDraw = aiDiverterDraw;
	e->aiAction = aiDiverterAction;
	e->moveSpeed = kPlayerMoveSpeed << 1;
	e->dir2 = e->dir;
}

void aiDiverterInit2(AIEntity *e) {
	e->movedownGfx[0] = e->standdownGfx[0];
	e->moveupGfx[0] = e->standupGfx[0];
	e->moveleftGfx[0] = e->standleftGfx[0];
	e->moverightGfx[0] = e->standrightGfx[0];
	e->movedownFrames =
		e->moveupFrames =
		e->moveleftFrames =
		e->moverightFrames = 1;

	// this is to handle loadgames...
	AIDir d = e->dir2;
	if (e->dir2 == DIR_NONE)
		d = e->dir;
	switch (d) {
	case DIR_DOWN:
		e->state = STATE_DIVERTER_BL;
		e->draw = e->standdownGfx[0];
		break;
	case DIR_UP:
		e->state = STATE_DIVERTER_BR;
		e->draw = e->standupGfx[0];
		break;
	case DIR_LEFT:
		e->state = STATE_DIVERTER_TL;
		e->draw = e->standleftGfx[0];
		break;
	case DIR_RIGHT:
		e->state = STATE_DIVERTER_TR;
		e->draw = e->standrightGfx[0];
		break;
	case DIR_NONE:
		break;
	}

	g_hdb->_ai->_laserRescan = true;
}

void aiDiverterAction(AIEntity *e) {
	if (e->goalX) {
		g_hdb->_ai->animateEntity(e);
		g_hdb->_ai->_laserRescan = true;

		// have to reset the state because we might have been moved...
		switch (e->dir2) {
		case DIR_DOWN:
			e->state = STATE_DIVERTER_BL;
			e->draw = e->standdownGfx[0];
			break;
		case DIR_UP:
			e->state = STATE_DIVERTER_BR;
			e->draw = e->standupGfx[0];
			break;
		case DIR_LEFT:
			e->state = STATE_DIVERTER_TL;
			e->draw = e->standleftGfx[0];
			break;
		case DIR_RIGHT:
			e->state = STATE_DIVERTER_TR;
			e->draw = e->standrightGfx[0];
			break;
		case DIR_NONE:
			break;
		}
	}
}

void aiDiverterDraw(AIEntity *e, int mx, int my) {
	if (!e->value1 && !e->value2)
		return;

	int	frame = e->movedownFrames & 3;
	int onScreen = 0;
	int i;
	switch (e->dir2) {
	case DIR_UP:
		if (e->tileY == e->value1 && e->int2) {	// going down or right?
			for (i = e->value1 + 1; i < e->value2; i++)
				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamUDTop[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		} else {
			for (i = e->value1 + 1; i < e->value2; i++)
				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamLRLeft[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		}
		break;
	case DIR_DOWN:
		if (e->tileY == e->value1 && e->int2) {	// going down or left?
			for (i = e->value1 + 1; i < e->value2; i++)
				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamUDTop[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		} else {
			for (i = e->value1 - 1; i > e->value2; i--)
				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamLRRight[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		}
		break;
	case DIR_LEFT:
		if (e->tileY == e->value1 && e->int2) {	// going up or left?
			for (i = e->value1 - 1; i > e->value2; i--)
				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		} else {
			for (i = e->value1 - 1; i > e->value2; i--)
				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamLRRight[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		}
		break;
	case DIR_RIGHT:
		if (e->tileY == e->value1 && e->int2) {	// going up or right?
			for (i = e->value1 - 1; i > e->value2; i--)
				onScreen += g_hdb->_ai->_gfxLaserbeamUD[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamUDBottom[frame]->drawMasked(e->x - mx, i * kTileHeight - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		} else {
			for (i = e->value1 + 1; i < e->value2; i++)
				onScreen += g_hdb->_ai->_gfxLaserbeamLR[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			onScreen += g_hdb->_ai->_gfxLaserbeamLRLeft[frame]->drawMasked(i * kTileWidth - mx, e->y - my);
			if (onScreen) {
				g_hdb->_sound->playSoundEx(SND_LASER_LOOP, kLaserChannel, true);
				g_hdb->_ai->_laserOnScreen = true;
			}
		}
		break;
	case DIR_NONE:
		break;
	}
	e->movedownFrames++;
}

//-------------------------------------------------------------------
//
//	MEERKAT : nutty little groundhog dude that will bite Guy if he's on
//		his mound.  That blows 1-5 gems outta Guy!
//
//-------------------------------------------------------------------

void aiMeerkatInit(AIEntity *e) {
	e->state = STATE_NONE;
	e->sequence = 0;
	if (e->value1 == 1) {
		e->aiAction = aiMeerkatLookAround;
		e->state = STATE_MEER_LOOK;
	} else
		e->aiAction = aiMeerkatAction;
}

void aiMeerkatInit2(AIEntity *e) {
	//  hidden at the start!
	e->draw = NULL;

	// make the looking around cycle better...
	e->movedownGfx[3] = e->movedownGfx[1];
	e->movedownFrames++;
}

void aiMeerkatDraw(AIEntity *e, int mx, int my) {
	char word[3];
	g_hdb->_window->getGemGfx()->drawMasked(e->value1 - mx, e->value2 - my, 255 - e->blinkFrames * 16);
	g_hdb->_gfx->setCursor(e->value1 + 12 - mx, e->value2 - 8 - my);
	word[2] = 0;
	if (!e->special1Frames) {
		word[0] = '0';
		word[1] = 0;
	} else {
		word[0] = '-';
		word[1] = '0' + e->special1Frames;
	}
	g_hdb->_gfx->drawText(word);
}

void aiMeerkatAction(AIEntity *e) {
	static const int gem_xv[] = { 0, 0,-2,-3,-4,-4,-3,-2,-2,-2,-2,-1,-1, 100};
	static const int gem_yv[] = {-6,-5,-4,-3,-2,-1, 0, 0, 1, 2, 3, 4, 5, 100};

	AIEntity *p = g_hdb->_ai->getPlayer();

	switch (e->sequence) {
		// waiting to see the player
	case 0:
		if ((abs(p->tileX - e->tileX) <= 1 && p->tileY == e->tileY) ||
			(abs(p->tileY - e->tileY) <= 1 && p->tileX == e->tileX)) {
			e->sequence = 1;
			e->state = STATE_MEER_MOVE;
			e->animFrame = 0;
			e->animCycle = 1;
			e->animDelay = e->animCycle;
			if (e->onScreen)
				g_hdb->_sound->playSound(SND_MEERKAT_WARNING);
		}
		break;
		// time to show the mound for a sec...
	case 1:
		g_hdb->_ai->animateEntity(e);
		if (!e->animFrame && e->animDelay == e->animCycle)
			e->sequence++;
		if (e->sequence == 2) {
			e->state = STATE_MEER_APPEAR;
			e->animFrame = 0;
			e->animDelay = e->animCycle;
			if (e->onScreen)
				g_hdb->_sound->playSound(SND_MEERKAT_APPEAR);
		}
		break;

		// pop outta the dirt!
	case 2:
		g_hdb->_ai->animateEntity(e);
		// done w/sequence?
		if (!e->animFrame && e->animDelay == e->animCycle) {
			e->sequence++;
			e->state = STATE_MEER_LOOK;
			e->animFrame = 0;
			e->animCycle = 2;
			e->animDelay = e->animCycle;
		}
		break;

		// looking around...... time to bite the player!?
	case 3:
	case 4:
		g_hdb->_ai->animateEntity(e);
		if (!e->animFrame && e->animDelay == e->animCycle) {
			e->sequence++;
			if (e->sequence == 5)
				e->state = STATE_MEER_DISAPPEAR;
		}
		if (g_hdb->_ai->checkPlayerTileCollision(e->tileX, e->tileY)) {
			e->state = STATE_MEER_BITE;
			e->sequence = 6;
			e->animFrame = 0;
			e->animDelay = e->animCycle;
			if (e->onScreen)
				g_hdb->_sound->playSound(SND_MEERKAT_BITE);
		}
		break;

		// going back underground!
	case 5:
		g_hdb->_ai->animateEntity(e);
		if (!e->animFrame && e->animDelay == e->animCycle) {
			e->sequence = 0;
			e->state = STATE_NONE;
			e->draw = NULL;
		}
		break;

		// biting the player right now!
	case 6:
		g_hdb->_ai->animateEntity(e);
		// hit the player?
		if (g_hdb->_ai->checkPlayerTileCollision(e->tileX, e->tileY)) {
			g_hdb->_ai->stopEntity(p);
			g_hdb->_ai->setPlayerLock(true);
			e->sequence = 7;
			p->moveSpeed <<= 1;
			if (g_hdb->_ai->findEntity(p->tileX, p->tileY + 1))
				g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
			else
				g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
			e->aiDraw = aiMeerkatDraw;
			e->value1 = e->x;
			e->value2 = e->y;
			e->blinkFrames = 0;		// index into movement table...

			// figure # of gems to take
			e->special1Frames = g_hdb->_rnd->getRandomNumber(4) + 1;
			int	amt = g_hdb->_ai->getGemAmount();
			if (amt - e->special1Frames < 0)
				e->special1Frames = amt;

			// if we're in Puzzle Mode and there's no gems left, give one back
			if (!g_hdb->getActionMode() && !(e->special1Frames - amt) && e->special1Frames)
				e->special1Frames--;

			amt -= e->special1Frames;
			g_hdb->_ai->setGemAmount(amt);
		}
		// go back to looking?
		if (!e->animFrame && e->animDelay == e->animCycle) {
			e->sequence = 3;
			e->state = STATE_MEER_LOOK;
			e->animFrame = 0;
			e->animDelay = e->animCycle;
		}
		break;

		// waiting for player to blast backward
	case 7:
		g_hdb->_ai->animateEntity(e);
		if (!p->goalX) {
			p->moveSpeed = kPlayerMoveSpeed;
			g_hdb->_ai->setPlayerLock(false);
			e->sequence = 5;
			e->state = STATE_MEER_DISAPPEAR;
			e->animFrame = 0;
			e->animDelay = e->animCycle;
		}
		break;
	}

	// blasting a gem outta Guy?
	if (e->value1) {
		if (gem_xv[e->blinkFrames] == 100) {
			e->value1 = 0;
			e->aiDraw = NULL;
			return;
		}
		e->value1 += gem_xv[e->blinkFrames];
		e->value2 += gem_yv[e->blinkFrames];
		e->blinkFrames++;
	}
}

void aiMeerkatLookAround(AIEntity *e) {
	g_hdb->_ai->animEntFrames(e);
}

//-------------------------------------------------------------------
//
//	FATFROG : Just sits in place and blasts out his tongue if you're
//		within range.
//
//-------------------------------------------------------------------

void aiFatFrogInit(AIEntity *e) {
	e->aiAction = aiFatFrogAction;
}

void aiFatFrogInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
	// load tongue tiles
	switch (e->dir) {
	case DIR_DOWN:
		if (!g_hdb->_ai->_tileFroglickMiddleUD) {
			g_hdb->_ai->_tileFroglickMiddleUD = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_MIDDLE);
			g_hdb->_ai->_tileFroglickWiggleUD[0] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_WIGGLE_L);
			g_hdb->_ai->_tileFroglickWiggleUD[1] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_WIGGLE_M);
			g_hdb->_ai->_tileFroglickWiggleUD[2] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_UD_WIGGLE_R);
		}
		e->state = STATE_STANDDOWN;
		break;
	case DIR_LEFT:
		if (!g_hdb->_ai->_tileFroglickMiddleLR)
			g_hdb->_ai->_tileFroglickMiddleLR = g_hdb->_gfx->loadTile(TILE_FFTONGUE_LR_MIDDLE);

		if (!g_hdb->_ai->_tileFroglickWiggleLeft[0]) {
			g_hdb->_ai->_tileFroglickWiggleLeft[0] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_L_WIGGLE_U);
			g_hdb->_ai->_tileFroglickWiggleLeft[1] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_L_WIGGLE_M);
			g_hdb->_ai->_tileFroglickWiggleLeft[2] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_L_WIGGLE_D);
		}
		e->state = STATE_STANDLEFT;
		break;
	case DIR_RIGHT:
		if (!g_hdb->_ai->_tileFroglickMiddleLR)
			g_hdb->_ai->_tileFroglickMiddleLR = g_hdb->_gfx->loadTile(TILE_FFTONGUE_LR_MIDDLE);

		if (!g_hdb->_ai->_tileFroglickWiggleRight[0]) {
			g_hdb->_ai->_tileFroglickWiggleRight[0] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_R_WIGGLE_U);
			g_hdb->_ai->_tileFroglickWiggleRight[1] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_R_WIGGLE_M);
			g_hdb->_ai->_tileFroglickWiggleRight[2] = g_hdb->_gfx->loadTile(TILE_FFTONGUE_R_WIGGLE_D);
		}
		e->state = STATE_STANDRIGHT;
		break;
	default:
		break;
	}
}

void aiFatFrogAction(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();

	switch (e->state) {
		//-------------------------------------------------------------------
		// WAITING TO ATTACK
		//-------------------------------------------------------------------
	case STATE_STANDDOWN:
		e->draw = e->standdownGfx[e->animFrame];
		// is player within 2 tiles below fatfrog?
		if (p->tileX == e->tileX && p->tileY - e->tileY < 3 && p->tileY > e->tileY) {
			e->state = STATE_LICKDOWN;
			e->animDelay = e->animCycle << 2;
			e->animFrame = 0;
		}
		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle << 2;
		e->animFrame++;
		if (e->animFrame == e->standdownFrames)
			e->animFrame = 0;
		if (!g_hdb->_rnd->getRandomNumber(29) && e->onScreen)
			g_hdb->_sound->playSound(SND_FROG_RIBBIT1);
		break;

	case STATE_STANDLEFT:
		e->draw = e->standleftGfx[e->animFrame];
		// is player within 2 tiles below fatfrog?
		if (p->tileY == e->tileY && e->tileX - p->tileX < 3 && p->tileX < e->tileX) {
			e->state = STATE_LICKLEFT;
			e->animDelay = e->animCycle << 2;
			e->animFrame = 0;
		}
		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle << 2;
		e->animFrame++;
		if (e->animFrame == e->standleftFrames)
			e->animFrame = 0;
		if (!g_hdb->_rnd->getRandomNumber(29) && e->onScreen)
			g_hdb->_sound->playSound(SND_FROG_RIBBIT2);
		break;

	case STATE_STANDRIGHT:
		e->draw = e->standrightGfx[e->animFrame];
		// is player within 2 tiles below fatfrog?
		if (p->tileY == e->tileY && p->tileX - e->tileX < 3 && p->tileX > e->tileX) {
			e->state = STATE_LICKRIGHT;
			e->animDelay = e->animCycle << 2;
			e->animFrame = 0;
		}
		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle << 2;
		e->animFrame++;
		if (e->animFrame == e->standrightFrames)
			e->animFrame = 0;
		if (!g_hdb->_rnd->getRandomNumber(29) && e->onScreen)
			g_hdb->_sound->playSound(SND_FROG_RIBBIT2);
		break;

		//-------------------------------------------------------------------
		// LICK ATTACK
		//-------------------------------------------------------------------
	case STATE_LICKDOWN:
		e->draw = e->movedownGfx[e->animFrame];
		// ready to start licking?
		if (e->animFrame == e->movedownFrames - 1 && !e->value1) {
			e->value1 = 1;
			e->aiDraw = aiFatFrogTongueDraw;
			g_hdb->_sound->playSound(SND_FROG_LICK);
		} else if (e->animFrame == e->movedownFrames - 1 && e->value1) {
			// animate licking

			// check player death
			if (((p->tileX == e->tileX && p->tileY == e->tileY + 1) ||					// in front of frog + 1 tile!?
				(e->value1 > 3 && p->tileX == e->tileX && p->tileY < e->tileY + 3)) &&	// in front of frog + 2 tiles!?
				g_hdb->_ai->playerDead() == false)
				g_hdb->_ai->killPlayer(DEATH_NORMAL);

			e->value1++;
			if (e->value1 == 14) {
				e->animFrame = e->value1 = 0;
				e->aiDraw = NULL;
				e->state = STATE_STANDDOWN;
			}
		} else {
			// animate pre-licking
			// cycle animation frames
			if (e->animDelay-- > 0)
				return;
			e->animDelay = e->animCycle;
			e->animFrame++;
		}
		break;

	case STATE_LICKLEFT:
		e->draw = e->moveleftGfx[e->animFrame];
		// ready to start licking?
		if (e->animFrame == e->moveleftFrames - 1 && !e->value1) {
			e->value1 = 1;
			e->aiDraw = aiFatFrogTongueDraw;
			g_hdb->_sound->playSound(SND_FROG_LICK);
		} else if (e->animFrame == e->moveleftFrames - 1 && e->value1) {
			// animate licking

			// check player death
			if (((p->tileY == e->tileY && p->tileX == e->tileX - 1) ||					// in front of frog + 1 tile!?
				(e->value1 > 3 && p->tileY == e->tileY && p->tileX > e->tileX - 3)) &&	// in front of frog + 2 tiles!?
				g_hdb->_ai->playerDead() == false)
				g_hdb->_ai->killPlayer(DEATH_NORMAL);

			e->value1++;
			if (e->value1 == 14) {
				e->animFrame = e->value1 = 0;
				e->aiDraw = NULL;
				e->state = STATE_STANDLEFT;
			}
		} else {
			// animate pre-licking
			// cycle animation frames
			if (e->animDelay-- > 0)
				return;
			e->animDelay = e->animCycle;
			e->animFrame++;
		}
		break;

	case STATE_LICKRIGHT:
		e->draw = e->moverightGfx[e->animFrame];
		// ready to start licking?
		if (e->animFrame == e->moverightFrames - 1 && !e->value1) {
			e->value1 = 1;
			e->aiDraw = aiFatFrogTongueDraw;
			g_hdb->_sound->playSound(SND_FROG_LICK);
		} else if (e->animFrame == e->moverightFrames - 1 && e->value1) {
			// animate licking
			// check player death
			//
			if (((p->tileY == e->tileY && p->tileX == e->tileX + 1) ||					// in front of frog + 1 tile!?
				(e->value1 > 3 && p->tileY == e->tileY && p->tileX < e->tileX + 3)) &&	// in front of frog + 2 tiles!?
				g_hdb->_ai->playerDead() == false)
				g_hdb->_ai->killPlayer(DEATH_NORMAL);

			e->value1++;
			if (e->value1 == 14) {
				e->animFrame = e->value1 = 0;
				e->aiDraw = NULL;
				e->state = STATE_STANDRIGHT;
			}
		} else {
			// animate pre-licking
			// cycle animation frames
			if (e->animDelay-- > 0)
				return;
			e->animDelay = e->animCycle;
			e->animFrame++;
		}
		break;
	default:
		// no op
		break;
	}
}

void aiFatFrogTongueDraw(AIEntity *e, int mx, int my) {
	int	nx, ny;

	switch (e->state) {
	case STATE_LICKDOWN:
		switch (e->value1) {
		case 1:
		case 2:
		case 3:
		case 13:
		case 14:
			nx = e->x;
			ny = e->y + 32;
			g_hdb->_ai->_tileFroglickWiggleUD[1]->drawMasked(nx - mx, ny - my);
			break;
		case 4:
		case 7:
		case 10:
			nx = e->x;
			ny = e->y + 32;
			g_hdb->_ai->_tileFroglickMiddleUD->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleUD[0]->drawMasked(nx - mx, ny + 32 - my);
			break;
		case 5:
		case 8:
		case 11:
			nx = e->x;
			ny = e->y + 32;
			g_hdb->_ai->_tileFroglickMiddleUD->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleUD[1]->drawMasked(nx - mx, ny + 32 - my);
			break;
		case 6:
		case 9:
		case 12:
			nx = e->x;
			ny = e->y + 32;
			g_hdb->_ai->_tileFroglickMiddleUD->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleUD[2]->drawMasked(nx - mx, ny + 32 - my);
			break;
		}
		break;

	case STATE_LICKLEFT:
		switch (e->value1) {
		case 1:
		case 2:
		case 3:
		case 13:
		case 14:
			nx = e->x - 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickWiggleLeft[1]->drawMasked(nx - mx, ny - my);
			break;
		case 4:
		case 7:
		case 10:
			nx = e->x - 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleLeft[0]->drawMasked(nx - 32 - mx, ny - my);
			break;
		case 5:
		case 8:
		case 11:
			nx = e->x - 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleLeft[1]->drawMasked(nx - 32 - mx, ny - my);
			break;
		case 6:
		case 9:
		case 12:
			nx = e->x - 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleLeft[2]->drawMasked(nx - 32 - mx, ny - my);
			break;
		}
		break;

	case STATE_LICKRIGHT:
		switch (e->value1) {
		case 1:
		case 2:
		case 3:
		case 13:
		case 14:
			nx = e->x + 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickWiggleRight[1]->drawMasked(nx - 32 - mx, ny - my);
			break;
		case 4:
		case 7:
		case 10:
			nx = e->x + 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleRight[0]->drawMasked(nx + 32 - mx, ny - my);
			break;
		case 5:
		case 8:
		case 11:
			nx = e->x + 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleRight[1]->drawMasked(nx + 32 - mx, ny - my);
			break;
		case 6:
		case 9:
		case 12:
			nx = e->x + 32;
			ny = e->y;
			g_hdb->_ai->_tileFroglickMiddleLR->drawMasked(nx - mx, ny - my);
			g_hdb->_ai->_tileFroglickWiggleRight[2]->drawMasked(nx + 32 - mx, ny - my);
			break;
		}
		break;
	default:
		break;
	}
}

//-------------------------------------------------------------------
//
//	GOODFAIRY
//
//-------------------------------------------------------------------

void aiGoodFairyInit(AIEntity *e) {
	e->aiAction = aiGoodFairyAction;
	e->sequence = 20;
	e->blinkFrames = e->goalX = 0;
}

void aiGoodFairyInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiGoodFairyAction(AIEntity *e) {
	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
	static const int xvAhead[5] = {9, 0, 0,-1, 1};
	static const int yvAhead[5] = {9,-1, 1, 0, 0};

	int	result;

	if (e->sequence) {
		e->sequence--;

		// look around...
		switch (e->sequence) {
		case 19:
			e->state = STATE_MOVEDOWN;
			break;
		case 0:
			{
				// Create a GEM?
				if (g_hdb->_rnd->getRandomNumber(99) > 98) {
					// spawn a gem in a random direction
					int	d = g_hdb->_rnd->getRandomNumber(3) + 1;
					int xv = xvAhead[d];
					int yv = yvAhead[d];

					e->sequence = 30;
					e->state = STATE_MOVEDOWN;
					// is something there already?
					if ((g_hdb->_ai->findEntityType(AI_CRATE, e->tileX + xv, e->tileY + yv) != NULL) ||
						(g_hdb->_ai->findEntityType(AI_LIGHTBARREL, e->tileX + xv, e->tileY + yv) != NULL))
						return;
					int spawnOK;
					AIEntity *hit = g_hdb->_ai->legalMove(e->tileX + xv, e->tileY + yv, e->level, &spawnOK);
					uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + xv, e->tileY + yv);
					if (hit || !spawnOK || (bg_flags & kFlagSpecial))
						return;

					g_hdb->_ai->spawn(ITEM_GEM_WHITE, e->dir, e->tileX + xv, e->tileY + yv, NULL, NULL, NULL, DIR_NONE, e->level, 0, 0, 1);
					g_hdb->_ai->addAnimateTarget(e->x + xv * kTileWidth, e->y + yv * kTileHeight, 0, 3, ANIM_NORMAL, false, false, GEM_FLASH);
					if (e->onScreen) {
						g_hdb->_sound->playSound(SND_GET_GEM);
						g_hdb->_sound->playSound(SND_GOOD_FAERIE_SPELL);
					}
					return;
				}

				int	tries = 4;
				do {
					// pick a random direction, then a random # of tiles in that direction
					AIDir d = (AIDir)(g_hdb->_rnd->getRandomNumber(3) + 1);
					int walk = g_hdb->_rnd->getRandomNumber(4) + 1;
					AIEntity *p = g_hdb->_ai->getPlayer();

					// if player is within 3 tiles, move closer
					if (abs(p->tileX - e->tileX) < 3 && abs(p->tileY - e->tileY) < 3) {
						if (abs(p->tileX - e->tileX) > abs(p->tileY - e->tileY)) {
						testx:
							if (p->tileX != e->tileX) {
								if (p->tileX < e->tileX)
									d = DIR_LEFT;
								else
									d = DIR_RIGHT;
							} else if (p->tileY != e->tileY)
								goto testy;
						} else {
						testy:
							if (p->tileY != e->tileY) {
								if (p->tileY <= e->tileY)
									d = DIR_UP;
								else
									d = DIR_DOWN;
							} else if (p->tileX != e->tileX)
								goto testx;
						}
					}

					// special case: if player is exactly 2 tiles away, move out of the way
					if (abs(p->tileX - e->tileX) == 2 && p->tileY == e->tileY) {
						int	move_ok;
						d = DIR_UP;
						AIEntity *h = g_hdb->_ai->legalMoveOverWater(e->tileX, e->tileY - 1, e->level, &move_ok);
						if (h || !move_ok)
							d = DIR_DOWN;
					} else if (abs(p->tileY - e->tileY) == 2 && p->tileX == e->tileX) {
						int	move_ok;
						d = DIR_LEFT;
						AIEntity *h = g_hdb->_ai->legalMoveOverWater(e->tileX - 1, e->tileY, e->level, &move_ok);
						if (h || !move_ok)
							d = DIR_RIGHT;
					}

					e->dir = d;
					int tmpDir = (int)d;
					e->state = state[tmpDir];
					int xv = xvAhead[tmpDir] * walk;
					if (e->tileX + xv < 1)
						xv = -e->tileX + 1;
					if (e->tileX + xv > g_hdb->_map->_width)
						xv = g_hdb->_map->_width - e->tileX - 1;

					int yv = yvAhead[d] * walk;
					if (e->tileY + yv < 1)
						yv = -e->tileY + 1;
					if (e->tileY + yv > g_hdb->_map->_height)
						yv = g_hdb->_map->_height - e->tileY - 1;

					e->value1 = xvAhead[d];
					e->value2 = yvAhead[d];
					e->moveSpeed = kPlayerMoveSpeed;

					// make sure we can move over water & white gems, but not fg_hdb->_ai->y blockers and solids
					AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
					if (hit && ((hit->type == ITEM_GEM_WHITE) || (hit->type == AI_GUY)))
						hit = NULL;
					uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
					if (result && !hit && !(bg_flags & kFlagSpecial)) {
						g_hdb->_ai->setEntityGoal(e, e->tileX + xv, e->tileY + yv);
						if (e->onScreen && !g_hdb->_rnd->getRandomNumber(29))
							g_hdb->_sound->playSound(SND_GOOD_FAERIE_AMBIENT);
						g_hdb->_ai->animateEntity(e);
						return;
					}
					tries--;		// don't lock the system if the fg_hdb->_ai->y is cornered
				} while (!result && tries);

				// couldn't find a place to move so just sit here for a sec & try agg_hdb->_ai->
				e->dir = DIR_NONE;
				e->state = STATE_MOVEDOWN;
				e->sequence = 1;
				e->value1 = e->value2 = e->xVel = e->yVel = 0;
			}
		}
		g_hdb->_ai->animEntFrames(e);
		return;
	}

	// in the process of moving around...
	if (e->goalX) {
		// did we run into a wall, entity, water, slime etc?
		// if so, pick a new direction!
		if (onEvenTile(e->x, e->y)) {
			// did we hit a Fg_hdb->_ai->YSTONE??? if so - teleport the thing at the other end to here!
			int index = g_hdb->_ai->checkFairystones(e->tileX, e->tileY);
			if (index >= 0) {
				int	sx, sy;
				g_hdb->_ai->getFairystonesSrc(index, &sx, &sy);
				AIEntity *hit = g_hdb->_ai->findEntity(sx, sy);
				if (hit && (hit != g_hdb->_ai->getPlayer())) {
					hit->tileX = e->tileX;
					hit->tileY = e->tileY;
					hit->x = hit->tileX * kTileWidth;
					hit->y = hit->tileY * kTileHeight;
					hit->goalX = hit->goalY = 0;
					g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
					g_hdb->_ai->addAnimateTarget(sx * kTileWidth, sy * kTileHeight, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
					if (e->onScreen)
						g_hdb->_sound->playSound(SND_TELEPORT);
					if (hit->onScreen)
						g_hdb->_sound->playSound(SND_TELEPORT);
				}
			}

			// see if we're about to move to a bad spot, which means:
			// (1) we're gonna hit a solid wall; ok to move over water/slime
			// (2) ok to move thru white gems
			// (3) cannot move thru SPECIAL flagged tiles (fg_hdb->_ai->y blockers)
			AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
			uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
			if (!result || (hit && hit->type != ITEM_GEM_WHITE && hit->type != AI_GUY) || (bg_flags & kFlagSpecial)) {
				g_hdb->_ai->stopEntity(e);
				e->value1 = e->value2 = 0;
				e->state = STATE_MOVEDOWN;
				e->sequence = 20;
				return;
			}
		}
		g_hdb->_ai->animateEntity(e);
	} else {
		// if not, start looking around!
		e->sequence = 20;
	}
}

//-------------------------------------------------------------------
//
//	BADFAIRY
//
//-------------------------------------------------------------------

void aiBadFairyInit(AIEntity *e) {
	e->aiAction = aiBadFairyAction;
	e->sequence = 20;
	e->blinkFrames = e->goalX = 0;
}

void aiBadFairyInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
}

void aiBadFairyAction(AIEntity *e) {
	static const AIState state[5] = {STATE_NONE, STATE_MOVEUP, STATE_MOVEDOWN, STATE_MOVELEFT, STATE_MOVERIGHT};
	static const AIDir opposite[5] = {DIR_NONE, DIR_DOWN, DIR_UP, DIR_RIGHT, DIR_LEFT};
	static const int xvAhead[5] = {9, 0, 0,-1, 1};
	static const int yvAhead[5] = {9,-1, 1, 0, 0};

	if (e->sequence) {
		e->sequence--;

		// look around...
		switch (e->sequence) {
		case 19:
			e->state = STATE_MOVEDOWN;
			break;
		case 0:
			{
				// Create a GATE PUDDLE?
				if (e->onScreen && (g_hdb->_rnd->getRandomNumber(99) > 90) && g_hdb->getActionMode() && (g_hdb->_ai->getGatePuddles() < kMaxGatePuddles)) {
					if (e->onScreen)
						g_hdb->_sound->playSound(SND_BADFAIRY_SPELL);

					e->sequence = 30;
					e->state = STATE_MOVEUP;
					g_hdb->_ai->spawn(AI_GATEPUDDLE, opposite[e->dir], e->tileX, e->tileY, NULL, NULL, NULL, DIR_NONE, e->level, 0, 0, 1);
					g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
					g_hdb->_ai->addGatePuddle(1);
					if (e->onScreen)
						g_hdb->_sound->playSound(SND_GATEPUDDLE_SPAWN);
					return;
				}

				int	tries = 4;
				int	result;
				do {
					// pick a random direction, then a random # of tiles in that direction
					int d = g_hdb->_rnd->getRandomNumber(3) + 1;
					int	walk = g_hdb->_rnd->getRandomNumber(4) + 1;
					AIEntity *p = g_hdb->_ai->getPlayer();

					e->dir = (AIDir)d;
					e->state = state[d];
					int xv = xvAhead[d] * walk;
					if (e->tileX + xv < 1)
						xv = -e->tileX + 1;
					if (e->tileX + xv > g_hdb->_map->_width)
						xv = g_hdb->_map->_width - e->tileX - 1;

					int yv = yvAhead[d] * walk;
					if (e->tileY + yv < 1)
						yv = -e->tileY + 1;
					if (e->tileY + yv > g_hdb->_map->_height)
						yv = g_hdb->_map->_height - e->tileY - 1;

					e->value1 = xvAhead[d];
					e->value2 = yvAhead[d];
					e->moveSpeed = kPlayerMoveSpeed;
					if (!g_hdb->getActionMode())
						e->moveSpeed >>= 1;

					AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
					uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
					if (hit == p && !g_hdb->_ai->playerDead()) {
						g_hdb->_ai->killPlayer(DEATH_FRIED);
						hit = NULL;
					}

					if (!hit && result && !(bg_flags & kFlagSpecial)) {
						g_hdb->_ai->setEntityGoal(e, e->tileX + xv, e->tileY + yv);
						g_hdb->_ai->animateEntity(e);
						if (e->onScreen && !g_hdb->_rnd->getRandomNumber(19))
							g_hdb->_sound->playSound(SND_BADFAIRY_AMBIENT);
						return;
					}
					tries--;		// don't lock the system if the player gets the fairy cornered
				} while (!result && tries);

				// couldn't find a place to move so just sit here for a sec & try again
				e->dir = DIR_NONE;
				e->state = STATE_MOVEDOWN;
				e->sequence = 1;
				e->value1 = e->value2 = e->xVel = e->yVel = 0;
			}
		}
		g_hdb->_ai->animEntFrames(e);
		return;
	}

	// in the process of moving around...
	if (e->goalX) {
		// hit the player?
		if (hitPlayer(e->x, e->y)) {
			g_hdb->_ai->killPlayer(DEATH_FRIED);
			g_hdb->_sound->playSound(SND_MBOT_DEATH);
			return;
		}

		// did we run into a wall, entity, water, slime etc?
		// if so, pick a new direction!
		if (onEvenTile(e->x, e->y)) {
			int	result;
			AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->tileX + e->value1, e->tileY + e->value2, e->level, &result);
			uint32 bg_flags = g_hdb->_map->getMapBGTileFlags(e->tileX + e->value1, e->tileY + e->value2);
			if (!result || (hit && hit->type != AI_GUY) || (bg_flags & kFlagSpecial)) {
				g_hdb->_ai->stopEntity(e);
				e->state = STATE_MOVEDOWN;
				e->sequence = 20;
				return;
			}
		}
		g_hdb->_ai->animateEntity(e);
	} else
		// if not, start looking around!
		e->sequence = 20;
}

//-------------------------------------------------------------------
//
//	BADFAIRY's GATE PUDDLE!
//
//-------------------------------------------------------------------

void aiGatePuddleInit(AIEntity *e) {
	e->aiAction = aiGatePuddleAction;
	e->value1 = 50;
}

void aiGatePuddleInit2(AIEntity *e) {
}

void aiGatePuddleAction(AIEntity *e) {
	static const int xva[5] = {9, 0, 0,-1, 1};
	static const int yva[5] = {9,-1, 1, 0, 0};

	AIEntity *p = g_hdb->_ai->getPlayer();

	if (e->goalX) {
		g_hdb->_ai->animateEntity(e);
		if (hitPlayer(e->x, e->y)) {
			for (int i = 0; i < kMaxTeleporters; i++) {
				if (g_hdb->_ai->_teleporters[i].anim1 == 2) {	// PANIC ZONE?
					p->tileX = g_hdb->_ai->_teleporters[i].x1;
					p->tileY = g_hdb->_ai->_teleporters[i].y1;
					p->x = p->tileX * kTileWidth;
					p->y = p->tileY * kTileHeight;
					p->xVel = p->yVel = 0;
					p->goalX = p->goalY = 0;
					p->animFrame = 0;
					p->drawXOff = p->drawYOff = 0;
					p->dir = g_hdb->_ai->_teleporters[i].dir1;
					p->level = g_hdb->_ai->_teleporters[i].level1;
					g_hdb->_ai->addAnimateTarget(p->x, p->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
					g_hdb->_sound->playSound(SND_TELEPORT);
					g_hdb->_ai->clearWaypoints();
					g_hdb->_window->startPanicZone();
					g_hdb->_map->centerMapXY(p->x + 16, p->y + 16);
					switch (p->dir) {
					case DIR_UP:
						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
						break;
					case DIR_DOWN:
						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
						break;
					case DIR_LEFT:
						g_hdb->_ai->setEntityGoal(p, p->tileX - 1, p->tileY);
						break;
					case DIR_RIGHT:
						g_hdb->_ai->setEntityGoal(p, p->tileX + 1, p->tileY);
						break;
					case DIR_NONE:
						break;
					}
					g_hdb->_ai->_playerEmerging = true;
					break;
				} else if (g_hdb->_ai->_teleporters[i].anim2 == 2) {	// PANIC ZONE?
					p->tileX = g_hdb->_ai->_teleporters[i].x2;
					p->tileY = g_hdb->_ai->_teleporters[i].y2;
					p->x = p->tileX * kTileWidth;
					p->y = p->tileY * kTileHeight;
					p->xVel = p->yVel = 0;
					p->goalX = p->goalY = 0;
					p->animFrame = 0;
					p->drawXOff = p->drawYOff = 0;
					p->dir = g_hdb->_ai->_teleporters[i].dir2;
					p->level = g_hdb->_ai->_teleporters[i].level2;
					g_hdb->_ai->addAnimateTarget(p->x, p->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
					g_hdb->_sound->playSound(SND_TELEPORT);
					g_hdb->_ai->clearWaypoints();
					g_hdb->_window->startPanicZone();
					g_hdb->_map->centerMapXY(p->x + 16, p->y + 16);
					switch (p->dir) {
					case DIR_UP:
						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY - 1);
						break;
					case DIR_DOWN:
						g_hdb->_ai->setEntityGoal(p, p->tileX, p->tileY + 1);
						break;
					case DIR_LEFT:
						g_hdb->_ai->setEntityGoal(p, p->tileX - 1, p->tileY);
						break;
					case DIR_RIGHT:
						g_hdb->_ai->setEntityGoal(p, p->tileX + 1, p->tileY);
						break;
					case DIR_NONE:
						break;
					}
					g_hdb->_ai->_playerEmerging = true;
					break;
				}
			}
		}
	} else {
		int rnd = g_hdb->_rnd->getRandomNumber(3) + 1;
		e->dir = (AIDir)rnd;
		int nx = e->tileX + xva[rnd];
		int ny = e->tileY + yva[rnd];

		int move_ok;
		AIEntity *hit = g_hdb->_ai->legalMoveOverWater(nx, ny, e->level, &move_ok);
		if (hit == p)
			hit = NULL;

		if (!hit && move_ok) {
			uint32	bg_flags = g_hdb->_map->getMapBGTileFlags(nx, ny);
			// Gate Puddles can't go over METAL!!! It's in their genes...
			if (!(bg_flags & kFlagMetal)) {
				if (e->onScreen)
					g_hdb->_sound->playSound(SND_GATEPUDDLE_AMBIENT);

				g_hdb->_ai->setEntityGoal(e, nx, ny);
				e->state = STATE_MOVEDOWN;
				g_hdb->_ai->animateEntity(e);
			}
		}

		// can only move 50 spaces or collisions
		e->value1--;
		if (!e->value1) {
			g_hdb->_ai->addGatePuddle(-1);
			g_hdb->_ai->addAnimateTarget(e->x, e->y, 0, 7, ANIM_NORMAL, false, false, TELEPORT_FLASH);
			if (e->onScreen)
				g_hdb->_sound->playSound(SND_GATEPUDDLE_DISSIPATE);
			g_hdb->_ai->removeEntity(e);
		}
	}
}

//-------------------------------------------------------------------
//
//	ICEPUFF : Little icy dude peeks out of the ground and pops up and
//		throws a snowball at you if he sees you....then he blasts back
//		into the snow and hides for a while....
//
// Variables used specially:
//	value1, value2	: x,y of snowball
//	dir2			: direction of snowball. DIR_NONE = no snowball
//	sequence	: timer for peeking
//
//-------------------------------------------------------------------

void aiIcePuffSnowballInit(AIEntity *e) {
	// which direction are we throwing in? Load the graphic if we need to
	switch (e->dir) {
	case DIR_DOWN:
		e->value1 = e->x + 12;
		e->value2 = e->y + 32;
		break;
	case DIR_LEFT:
		e->value1 = e->x - 4;
		e->value2 = e->y + 16;
		break;
	case DIR_RIGHT:
		e->value1 = e->x + 32;
		e->value2 = e->y + 16;
		break;
	default:
		break;
	}
	e->aiDraw = aiIcePuffSnowballDraw;
}

void aiIcePuffSnowballAction(AIEntity *e) {
	// check for hit BEFORE moving so snowball is closer to object
	// NOTE: Need to do logic in this draw routine just in case the ICEPUFF gets stunned!
	int result;
	AIEntity *hit = g_hdb->_ai->legalMoveOverWater(e->value1 / kTileWidth, e->value2 / kTileHeight, e->level, &result);
	if (hit && hit->type == AI_GUY && !g_hdb->_ai->playerDead()) {
		g_hdb->_ai->killPlayer(DEATH_NORMAL);
		g_hdb->_ai->addAnimateTarget(hit->x, hit->y, 0, 3, ANIM_NORMAL, false, false, GROUP_WATER_SPLASH_SIT);
		result = 0; // fall thru...
	}

	// hit something solid - kill the snowball
	if (!result) {
		e->dir2 = DIR_NONE;
		e->aiDraw = NULL;
		return;
	}

	int speed = kPlayerMoveSpeed;
	if (!g_hdb->getActionMode())
		speed >>= 1;

	switch (e->dir2) {
	case DIR_DOWN:
		e->value2 += speed;
		break;
	case DIR_LEFT:
		e->value1 -= speed;
		break;
	case DIR_RIGHT:
		e->value1 += speed;
		break;
	default:
		break;
	}
}

void aiIcePuffSnowballDraw(AIEntity *e, int mx, int my) {
	// did we throw a snowball?  make it move!
	if (e->dir2 != DIR_NONE)
		aiIcePuffSnowballAction(e);

	switch (e->dir2) {
	case DIR_DOWN:
		if (!g_hdb->_ai->_icepSnowballGfxDown)
			g_hdb->_ai->_icepSnowballGfxDown = g_hdb->_gfx->loadPic(ICEPUFF_SNOWBALL_DOWN);
		g_hdb->_ai->_icepSnowballGfxDown->drawMasked(e->value1 - mx, e->value2 - my);
		break;
	case DIR_LEFT:
		if (!g_hdb->_ai->_icepSnowballGfxLeft)
			g_hdb->_ai->_icepSnowballGfxLeft = g_hdb->_gfx->loadPic(ICEPUFF_SNOWBALL_LEFT);
		g_hdb->_ai->_icepSnowballGfxLeft->drawMasked(e->value1 - mx, e->value2 - my);
		break;
	case DIR_RIGHT:
		if (!g_hdb->_ai->_icepSnowballGfxRight)
			g_hdb->_ai->_icepSnowballGfxRight = g_hdb->_gfx->loadPic(ICEPUFF_SNOWBALL_RIGHT);
		g_hdb->_ai->_icepSnowballGfxRight->drawMasked(e->value1 - mx, e->value2 - my);
		break;
	default:
		break;
	}
}

void aiIcePuffInit(AIEntity *e) {
	// PEEK - but no head up yet
	e->sequence = 30;				// timed sequence for peeking
	e->state = STATE_ICEP_PEEK;			// start in PEEK mode
	e->dir2 = DIR_NONE;					// no snowball out
	e->aiAction = aiIcePuffAction;
}

void aiIcePuffInit2(AIEntity *e) {
	// empty frame
	e->draw = e->blinkGfx[3];
}

void aiIcePuffAction(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();

	switch (e->state) {
	case STATE_ICEP_PEEK:
		e->sequence--;
		switch (e->sequence) {
		case 20: // underground
			e->draw = e->blinkGfx[0];
			break;
		case 16: // peek - looking
			e->draw = e->blinkGfx[1];
			break;
		case 12: // peek - blinking
			e->draw = e->blinkGfx[2];
			break;
		case 8: // peek - looking
			e->draw = e->blinkGfx[1];
			break;
		case 4: // peek - looking
			e->draw = e->blinkGfx[0];
			break;
		case 3:
			if (e->onScreen && !g_hdb->_rnd->getRandomNumber(5))
				g_hdb->_sound->playSound(SND_ICEPUFF_WARNING);
			break;
		case 0:
			// underground
			e->draw = e->blinkGfx[3];
			e->sequence = 30;
			break;
		}

		// can we see the player? (and no snowball is out)
		if (e->sequence <= 20 && !g_hdb->_ai->playerDead() && e->onScreen) {
			if (p->tileX == e->tileX && p->tileY > e->tileY && e->dir2 == DIR_NONE) {
				e->dir = DIR_DOWN;
				e->state = STATE_ICEP_APPEAR;
				e->animFrame = 0;
				if (e->onScreen)
					g_hdb->_sound->playSound(SND_ICEPUFF_APPEAR);
			} else if (p->tileY == e->tileY && e->dir2 == DIR_NONE) {
				p->tileX < e->tileX ? e->dir = DIR_LEFT : e->dir = DIR_RIGHT;
				e->state = STATE_ICEP_APPEAR;
				e->animFrame = 0;
				if (e->onScreen)
					g_hdb->_sound->playSound(SND_ICEPUFF_APPEAR);
			}
		}
		break;

	case STATE_ICEP_APPEAR:
		e->draw = e->standupGfx[e->animFrame];

		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle;

		e->animFrame++;
		if (e->animFrame == e->standupFrames) {
			e->animFrame = 0;
			switch (e->dir) {
			case DIR_DOWN:
				e->state = STATE_ICEP_THROWDOWN;
				g_hdb->_sound->playSound(SND_ICEPUFF_THROW);
				break;
			case DIR_LEFT:
				e->state = STATE_ICEP_THROWLEFT;
				g_hdb->_sound->playSound(SND_ICEPUFF_THROW);
				break;
			case DIR_RIGHT:
				e->state = STATE_ICEP_THROWRIGHT;
				g_hdb->_sound->playSound(SND_ICEPUFF_THROW);
				break;
			default:
				break;
			}
		}
		break;

	case STATE_ICEP_THROWDOWN:
		e->draw = e->standdownGfx[e->animFrame];

		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle;

		e->animFrame++;
		if (e->animFrame == e->standdownFrames && e->state != STATE_ICEP_DISAPPEAR) {
			// dir2 = direction snowball is moving
			e->dir2 = e->dir;
			// throw it!
			aiIcePuffSnowballInit(e);
			e->animFrame = 0;
			e->state = STATE_ICEP_DISAPPEAR;
		} else if (e->animFrame == e->special1Frames) {
			e->state = STATE_ICEP_PEEK;
			e->draw = e->blinkGfx[3];
			e->sequence = g_hdb->_rnd->getRandomNumber(99) + 30;
		}
		break;

	case STATE_ICEP_THROWLEFT:
		e->draw = e->standleftGfx[e->animFrame];

		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle;

		e->animFrame++;
		if (e->animFrame == e->standdownFrames && e->state != STATE_ICEP_DISAPPEAR) {
			// dir2 = direction snowball is moving
			e->dir2 = e->dir;
			// throw it!
			aiIcePuffSnowballInit(e);
			e->animFrame = 0;
			e->state = STATE_ICEP_DISAPPEAR;
		} else if (e->animFrame == e->special1Frames) {
			e->state = STATE_ICEP_PEEK;
			e->draw = e->blinkGfx[3];
			e->sequence = g_hdb->_rnd->getRandomNumber(99) + 30;
		}
		break;

	case STATE_ICEP_THROWRIGHT:
		e->draw = e->standrightGfx[e->animFrame];

		// cycle animation frames
		if (e->animDelay-- > 0)
			return;
		e->animDelay = e->animCycle;

		e->animFrame++;
		if (e->animFrame == e->standdownFrames && e->state != STATE_ICEP_DISAPPEAR) {
			// dir2 = direction snowball is moving
			e->dir2 = e->dir;
			// throw it!
			aiIcePuffSnowballInit(e);
			e->animFrame = 0;
			e->state = STATE_ICEP_DISAPPEAR;
		} else if (e->animFrame == e->special1Frames) {
			e->state = STATE_ICEP_PEEK;
			e->draw = e->blinkGfx[3];
			e->sequence = g_hdb->_rnd->getRandomNumber(99) + 30;
		}
		break;

	case STATE_ICEP_DISAPPEAR:
		e->draw = e->special1Gfx[e->animFrame];
	default:
		break;
	}
}

//-------------------------------------------------------------------
//
//	BUZZFLY : Simply flies around on paths.... kills you if you touch him.
//		He pauses at corners, too.
//
//-------------------------------------------------------------------

void aiBuzzflyInit(AIEntity *e) {
	e->aiAction = aiBuzzflyAction;
	e->sequence = 0;

	g_hdb->_ai->findPath(e);
}

void aiBuzzflyInit2(AIEntity *e) {
	e->draw = g_hdb->_ai->getStandFrameDir(e);
	for (int i = 0; i < e->movedownFrames; i++) {
		e->standdownGfx[i] = e->movedownGfx[i];
		e->standupGfx[i] = e->moveupGfx[i];
		e->standleftGfx[i] = e->moveleftGfx[i];
		e->standrightGfx[i] = e->moverightGfx[i];
	}
	e->standdownFrames = e->movedownFrames;
	e->standupFrames = e->moveupFrames;
	e->standleftFrames = e->moveleftFrames;
	e->standrightFrames = e->moverightFrames;
}

void aiBuzzflyAction(AIEntity *e) {
	if (!e->goalX) {
		switch (e->sequence) {
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
			if (!e->animFrame && e->animDelay == e->animCycle)
				e->sequence++;

			e->draw = e->standdownGfx[e->animFrame];

			// cycle animation frames
			if (e->animDelay-- > 0)
				return;
			e->animDelay = e->animCycle;
			e->animFrame++;
			if (e->animFrame == e->standdownFrames)
				e->animFrame = 0;

			break;

		case 5:
			g_hdb->_ai->findPath(e);
			if (e->onScreen)
				g_hdb->_sound->playSound(SND_BUZZFLY_FLY);
			e->sequence = 0;
		}
	} else {
		g_hdb->_ai->animateEntity(e);
		if (g_hdb->_ai->checkPlayerCollision(e->x, e->y, 6) && !g_hdb->_ai->playerDead()) {
			g_hdb->_sound->playSound(SND_BUZZFLY_STING);
			g_hdb->_ai->killPlayer(DEATH_GRABBED);
		}
	}
}

//-------------------------------------------------------------------
//
//	DRAGON
//
//-------------------------------------------------------------------

void aiDragonInit(AIEntity *e) {
	e->state = STATE_STANDDOWN;
	e->sequence = 0;	// 0 = sleeping
	e->aiAction = aiDragonAction;
	e->aiDraw = aiDragonDraw;
	e->animCycle = 10;	// time between flaps

	// need to save the dragon's coords and type in the blocking entity for gem-hit-blocking detection
	AIEntity *block = spawnBlocking(e->tileX - 1, e->tileY, e->level);
	block->value1 = (int)AI_DRAGON;
	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
	block = spawnBlocking(e->tileX + 1, e->tileY, e->level);
	block->value1 = (int)AI_DRAGON;
	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
	block = spawnBlocking(e->tileX - 1, e->tileY - 1, e->level);
	block->value1 = (int)AI_DRAGON;
	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
	block = spawnBlocking(e->tileX + 1, e->tileY - 1, e->level);
	block->value1 = (int)AI_DRAGON;
	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
	block = spawnBlocking(e->tileX - 1, e->tileY - 2, e->level);
	block->value1 = (int)AI_DRAGON;
	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
	block = spawnBlocking(e->tileX + 1, e->tileY - 2, e->level);
	block->value1 = (int)AI_DRAGON;
	sprintf(block->luaFuncUse, "%03d%03d", e->tileX, e->tileY);
}

void aiDragonInit2(AIEntity *e) {
	e->draw = NULL;
	if (!g_hdb->_ai->_gfxDragonAsleep) {
		g_hdb->_ai->_gfxDragonAsleep = g_hdb->_gfx->loadPic(DRAGON_ASLEEP);
		g_hdb->_ai->_gfxDragonFlap[0] = g_hdb->_gfx->loadPic(DRAGON_FLAP1);
		g_hdb->_ai->_gfxDragonFlap[1] = g_hdb->_gfx->loadPic(DRAGON_FLAP2);
		g_hdb->_ai->_gfxDragonBreathe[0] = g_hdb->_gfx->loadPic(DRAGON_BREATHE_START);
		g_hdb->_ai->_gfxDragonBreathe[1] = g_hdb->_gfx->loadPic(DRAGON_BREATHING_1);
		g_hdb->_ai->_gfxDragonBreathe[2] = g_hdb->_gfx->loadPic(DRAGON_BREATHING_2);
	}
}

void aiDragonWake(AIEntity *e) {
	// woke up, start flapping and breathing!
	e->sequence = 1;
	e->animFrame = 0;
	e->animDelay = e->animCycle;
}

void aiDragonUse(AIEntity *e) {
	aiDragonWake(e);
}

void aiDragonAction(AIEntity *e) {
	AIEntity *p = g_hdb->_ai->getPlayer();

	switch (e->sequence) {
	// Sleeping, waiting for the player to wake him up
	case 0:
		if (e->onScreen &&
			p->tileX >= e->tileX - 1 &&
			p->tileX <= e->tileX + 1 &&
			p->tileY <= e->tileY + 1 &&
			p->tileY >= e->tileY - 3) {
			if ((p->state >= STATE_ATK_CLUB_UP &&
				p->state <= STATE_ATK_SLUG_RIGHT) || g_hdb->_window->inPanicZone()) {
				aiDragonWake(e);
				if (e->onScreen)
					g_hdb->_sound->playSound(SND_DRAGON_WAKE);
			}
		}
		break;

	// Woke up - flapping wings 3 times!
	case 1:
		e->animDelay--;

		if (e->animDelay < 1) {
			if (e->onScreen)
				g_hdb->_sound->playSound(SND_DRAGON_WAKE);
			e->animDelay = e->animCycle;
			e->animFrame++;
			if (e->animFrame >= 8) {
				e->animFrame = 0;
				e->sequence = 2;
				e->animCycle = 2;
			}
		}
		break;

	// Start breathing fire!
	case 2:
		e->animDelay--;

		if (e->onScreen)
			g_hdb->_sound->playSound(SND_DRAGON_BREATHEFIRE);
		if (e->animDelay < 1) {
			e->animDelay = e->animCycle;
			e->animFrame++;
			if (e->animFrame >= 1) {
				e->animFrame = 0;
				e->sequence = 3;
				e->animCycle = 2;		// time between flaps
			}
		}

		break;

	// Breathing fire!
	case 3:
		{
			if (hitPlayer(e->x, e->y + 32)) {
				g_hdb->_ai->killPlayer(DEATH_FRIED);
				return;
			}

			// whatever entity is in front of the dragon is gettin' USED!
			AIEntity *hit = g_hdb->_ai->findEntity(e->tileX, e->tileY + 1);
			if (hit) {
				switch (hit->type) {
				case AI_CHICKEN:
					g_hdb->_ai->addAnimateTarget(hit->tileX * kTileWidth, hit->tileY * kTileHeight, 0, 2, ANIM_NORMAL, false, false, GROUP_ENT_CHICKEN_DIE);
					g_hdb->_sound->playSound(SND_CHICKEN_DEATH);
					g_hdb->_ai->removeEntity(hit);
					e->sequence = 4;
					break;
				case AI_MAGIC_EGG:
				case AI_ICE_BLOCK:
					aiMagicEggUse(hit);
					break;
				default:
					if (hit->aiUse)
						hit->aiUse(hit);
					if (hit->luaFuncUse[0])
						g_hdb->_lua->callFunction(hit->luaFuncUse, 0);
				}
			}

			e->animDelay--;

			if (e->animDelay < 1) {
				if (e->onScreen && !(e->animFrame & 7))
					g_hdb->_sound->playSound(SND_DRAGON_BREATHEFIRE);

				e->animDelay = e->animCycle;
				e->animFrame++;
				if (e->animFrame >= 30) {
					e->animFrame = 0;
					e->sequence = 4;
					e->animCycle = 10;		// time between flaps
				}
			}
		}

		break;

	// Done burning - flapping wings 3 times
	case 4:
		e->animDelay--;

		if (e->animDelay < 1) {
			e->animDelay = e->animCycle;
			e->animFrame++;
			if (e->animFrame >= 8) {
				e->animFrame = 0;
				e->sequence = 0;
				if (e->onScreen)
					g_hdb->_sound->playSound(SND_DRAGON_FALLASLEEP);
			}
		}
		break;
	}
}

void aiDragonDraw(AIEntity *e, int mx, int my) {
	switch (e->sequence) {
	// sleeping
	case 0:
		g_hdb->_ai->_gfxDragonAsleep->drawMasked(e->x - 32 - mx, e->y - 96 - my);
		break;
	// flapping 3 times
	case 1:
		g_hdb->_ai->_gfxDragonFlap[e->animFrame & 1]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
		break;
	// start breathing (very short)
	case 2:
		g_hdb->_ai->_gfxDragonBreathe[0]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
		break;
	// breathing
	case 3:
		g_hdb->_ai->_gfxDragonBreathe[(e->animFrame & 1) + 1]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
		break;
	// flapping 3 times
	case 4:
		g_hdb->_ai->_gfxDragonBreathe[e->animFrame & 1]->drawMasked(e->x - 32 - mx, e->y - 96 - my);
		break;
	}
}

} // End of Namespace