/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "got/game/move_patterns.h"
#include "got/events.h"
#include "got/game/back.h"
#include "got/game/boss1.h"
#include "got/game/boss2.h"
#include "got/game/boss3.h"
#include "got/game/move.h"
#include "got/game/object.h"
#include "got/game/script.h"
#include "got/game/special_tile.h"
#include "got/vars.h"

namespace Got {

#define TILE_SOLID 80
#define TILE_FLY 140
#define TILE_SPECIAL 200

int checkSpecialMove1(int x, int y, Actor *actor);

int checkMove0(int x, int y, Actor *actor);
int checkMove1(int x, int y, Actor *actor);

int movementZero(Actor *actor);
int movementOne(Actor *actor);
int movementTwo(Actor *actor);
int movementThree(Actor *actor);
int movementFour(Actor *actor);
int movementFive(Actor *actor);
int movementSix(Actor *actor);
int movementSeven(Actor *actor);
int movementEight(Actor *actor);
int movementNine(Actor *actor);
int movementTen(Actor *actor);
int movementEleven(Actor *actor);
int movementTwelve(Actor *actor);      // Horizontal straight until bump
int movementThirteen(Actor *actor);    // Pause-seek (mushroom)
int movementFourteen(Actor *actor);    // Move-bump-stop (boulder)
int movementFifteen(Actor *actor);     // No move, no frame cycle
int movementSixteen(Actor *actor);     // Tornado 1
int movementSeventeen(Actor *actor);   // Tornado 2
int movementEighteen(Actor *actor);    // Random-seek-bite-run
int movementNineteen(Actor *actor);    // Tornado 2
int movementTwenty(Actor *actor);      // Tornado 2
int movementTwentyOne(Actor *actor);   // Eyeball
int movementTwentyTwo(Actor *actor);   // Spear
int movementTwentyThree(Actor *actor); // Spinball cw
int movementTwentyFour(Actor *actor);  // Spinball ccw
int movementTwentyFive(Actor *actor);  //
int movementTwentySix(Actor *actor);   //
int movementTwentySeven(Actor *actor); //
int movementTwentyEight(Actor *actor); // Tree boss
int movementTwentyNine(Actor *actor);  // Horizontal or vertical (pass_val)
int movementThirty(Actor *actor);      // Vertical straight
int movementThirtyOne(Actor *actor);   // Drop (stalagtite)
int movementThirtyTwo(Actor *actor);   // Bomb 1
int movementThirtyThree(Actor *actor); // Bomb 2
int movementThirtyFour(Actor *actor);  // Gun (4-dir)
int movementThirtyFive(Actor *actor);  // Gun (4-dir)
int movementThirtySix(Actor *actor);   // Acid drop
int movementThirtySeven(Actor *actor); // 4-way rnd,rnd len
int movementThirtyEight(Actor *actor); // Timed darting
int movementThirtyNine(Actor *actor);  // Troll 1
int movementForty(Actor *actor);       // Troll 2

int (*const _movementFunc[])(Actor *actor) = {
	movementZero,
	movementOne,
	movementTwo,
	movementThree,
	movementFour,
	movementFive,
	movementSix,
	movementSeven,
	movementEight,
	movementNine,
	movementTen,
	movementEleven,
	movementTwelve,
	movementThirteen,
	movementFourteen,
	movementFifteen,
	movementSixteen,
	movementSeventeen,
	movementEighteen,
	movementNineteen,
	movementTwenty,
	movementTwentyOne,
	movementTwentyTwo,
	movementTwentyThree,
	movementTwentyFour,
	movementTwentyFive,
	movementTwentySix,
	movementTwentySeven,
	movementTwentyEight,
	movementTwentyNine,
	movementThirty,
	movementThirtyOne,
	movementThirtyTwo,
	movementThirtyThree,
	movementThirtyFour,
	movementThirtyFive,
	movementThirtySix,
	movementThirtySeven,
	movementThirtyEight,
	movementThirtyNine,
	movementForty};

int specialMovementOne(Actor *actor);
int specialMovementTwo(Actor *actor);
int specialMovementThree(Actor *actor);
int specialMovementFour(Actor *actor);
int specialMovementFive(Actor *actor);
int specialMovementSix(Actor *actor);
int specialMovementSeven(Actor *actor);
int specialMovementEight(Actor *actor);
int specialMovementNine(Actor *actor);
int specialMovementTen(Actor *actor);
int specialMovementEleven(Actor *actor);

int (*specialMovementFunc[])(Actor *actor) = {
	nullptr,
	specialMovementOne,
	specialMovementTwo,
	specialMovementThree,
	specialMovementFour,
	specialMovementFive,
	specialMovementSix,
	specialMovementSeven,
	specialMovementEight,
	specialMovementNine,
	specialMovementTen,
	specialMovementEleven};

// Check Thor move
int checkMove0(const int x, const int y, Actor *actor) {
	_G(thorIcon1) = 0;
	_G(thorIcon2) = 0;
	_G(thorIcon3) = 0;
	_G(thorIcon4) = 0;

	if (x < 0) {
		if (_G(currentLevel) > 0) {
			_G(newLevel) = _G(currentLevel) - 1;
			actor->_x = 304;
			actor->_lastX[0] = 304;
			actor->_lastX[1] = 304;
			actor->_show = 0;
			actor->_moveCount = 0;
			setThorVars();
			return 1;
		}

		return 0;
	}
	
	if (x > 306) {
		if (_G(currentLevel) < 119) {
			_G(newLevel) = _G(currentLevel) + 1;
			actor->_x = 0;
			actor->_lastX[0] = 0;
			actor->_lastX[1] = 0;
			actor->_show = 0;
			actor->_moveCount = 0;
			setThorVars();
			return 1;
		}

		return 0;
	}
	
	if (y < 0) {
		if (_G(currentLevel) > 9) {
			_G(newLevel) = _G(currentLevel) - 10;
			actor->_y = 175;
			actor->_lastY[0] = 175;
			actor->_show = 0;
			actor->_lastY[1] = 175;
			actor->_moveCount = 0;
			setThorVars();
			return 1;
		}

		return 0;
	}
	
	if (y > 175) {
		if (_G(currentLevel) < 110) {
			_G(newLevel) = _G(currentLevel) + 10;
			actor->_y = 0;
			actor->_lastY[0] = 0;
			actor->_lastY[1] = 0;
			actor->_show = 0;
			actor->_moveCount = 0;
			setThorVars();
			return 1;
		}

		return 0;
	}
	
	int x1 = (x + 1) >> 4;
	int y1 = (y + 8) >> 4;
	int x2 = (_G(thor)->_dir > 1) ? (x + 12) >> 4 : (x + 10) >> 4;
	int y2 = (y + 15) >> 4;

	_G(slipFlag) = false;

	// Check for cheat flying mode
	if (!actor->_flying) {
		byte icn1 = _G(scrn)._iconGrid[y1][x1];
		byte icn2 = _G(scrn)._iconGrid[y2][x1];
		byte icn3 = _G(scrn)._iconGrid[y1][x2];
		byte icn4 = _G(scrn)._iconGrid[y2][x2];
		int ti = 0;

		if (icn1 < TILE_FLY) {
			_G(thorIcon1) = 1;
			ti = 1;
		}
		
		if (icn2 < TILE_FLY) {
			_G(thorIcon2) = 1;
			ti = 1;
		}
		
		if (icn3 < TILE_FLY) {
			_G(thorIcon3) = 1;
			ti = 1;
		}
		
		if (icn4 < TILE_FLY) {
			_G(thorIcon4) = 1;
			ti = 1;
		}
		
		if (ti)
			return 0;

		if (icn1 > TILE_SPECIAL) {
			if (!specialTileThor(y1, x1, icn1))
				return 0;
			icn2 = _G(scrn)._iconGrid[y2][x1];
			icn3 = _G(scrn)._iconGrid[y1][x2];
			icn4 = _G(scrn)._iconGrid[y2][x2];
		}

		if (icn2 > TILE_SPECIAL) {
			if (!specialTileThor(y2, x1, icn2))
				return 0;
			icn3 = _G(scrn)._iconGrid[y1][x2];
			icn4 = _G(scrn)._iconGrid[y2][x2];
		}

		if (icn3 > TILE_SPECIAL) {
			if (!specialTileThor(y1, x2, icn3))
				return 0;
			icn4 = _G(scrn)._iconGrid[y2][x2];
		}

		if (icn4 > TILE_SPECIAL && !specialTileThor(y2, x2, icn4))
			return 0;
	}

	if (!_G(slipFlag)) {
		_G(slipping) = false;
		_G(slipCount) = 0;
	}
	
	if (_G(slipFlag) && !_G(slipping))
		_G(slipCount++);
	
	if (_G(slipCount) > 8)
		_G(slipping) = true;
	
	_G(slipFlag) = false;

	x1 = x + 1;
	y1 = y + 8;
	
	x2 = x + 12;	
	y2 = y + 15;

	_G(thorSpecialFlag) = false;
	for (int i = 3; i < MAX_ACTORS; i++) {
		Actor *act = &_G(actor[i]);
		if ((act->_solid & 128) || !act->_active)
			continue;

		const int x3 = act->_x + 1;
		const int y3 = act->_y + 1;
		
		if (ABS(x3 - x1) > 16 || ABS(y3 - y1) > 16)
			continue;

		const int x4 = act->_x + act->_sizeX - 1;
		const int y4 = act->_y + act->_sizeY - 1;
		if (overlap(x1, y1, x2, y2, x3, y3, x4, y4)) {
			if (act->_funcNum > 0) { // 255=explosion
				if (act->_funcNum == 255)
					return 0;
				act->_temp1 = x;
				act->_temp2 = y;
				_G(thorSpecialFlag) = true;
				
				return specialMovementFunc[act->_funcNum](act);
			}

			_G(thorSpecialFlag) = false;
			thorDamaged(act);
			if (act->_solid < 2) {
				if (!act->_vulnerableCountdown && (!(act->_type & 1)))
					playSound(PUNCH1, false);

				if (!_G(hammer)->_active && _G(keyFlag[key_fire]))
					actorDamaged(act, _G(hammer)->_hitStrength);
				else
					actorDamaged(act, _G(thor)->_hitStrength);
			}
			return 1;
		}
	}
	actor->_x = x;
	actor->_y = y;
	return 1;
}

// Check hammer move
int checkMove1(const int x, const int y, Actor *actor) {
	if (x < 0 || x > 306 || y < 0 || y > 177)
		return 0;

	int x1 = (x + 1) >> 4;
	int y1 = (y + 6) >> 4;
	int x2 = (x + 10) >> 4;
	int y2 = (y + 10) >> 4;

	// Check for solid or fly over
	int icn = TILE_FLY;
	if (actor->_flying)
		icn = TILE_SOLID;

	const byte icn1 = _G(scrn)._iconGrid[y1][x1];
	const byte icn2 = _G(scrn)._iconGrid[y2][x1];
	const byte icn3 = _G(scrn)._iconGrid[y1][x2];
	const byte icn4 = _G(scrn)._iconGrid[y2][x2];
	if (icn1 < icn || icn2 < icn || icn3 < icn || icn4 < icn) {
		if (actor->_actorNum == 1 && actor->_moveType == 2)
			playSound(CLANG, false);

		return 0;
	}

	if (icn1 > TILE_SPECIAL && !specialTile(actor, y1, x1, icn1))
		return 0;

	if (icn2 > TILE_SPECIAL && !specialTile(actor, y2, x1, icn2))
		return 0;

	if (icn3 > TILE_SPECIAL && !specialTile(actor, y1, x2, icn3))
		return 0;

	if (icn4 > TILE_SPECIAL && !specialTile(actor, y2, x2, icn4))
		return 0;

	x1 = x + 1;
	y1 = y + 1;
	x2 = x + 10;
	y2 = y + 10;

	int f = 0;
	for (int i = 3; i < MAX_ACTORS; i++) {
		Actor *act = &_G(actor[i]);
		if (!act->_active || act->_type == 3)
			continue;

		const int x3 = act->_x;
		const int y3 = act->_y;

		if (ABS(x3 - x1) > 16 || ABS(y3 - y1) > 16)
			continue;

		const int x4 = act->_x + act->_sizeX - 1;
		const int y4 = act->_y + act->_sizeY - 1;

		if (overlap(x1, y1, x2, y2, x3, y3, x4, y4)) {
			if (_G(bossActive) && !GAME3) {
				switch (_G(area)) {
				case 1:
					boss1CheckHit(act, x1, y1, x2, y2, i);
					break;
				case 2:
					boss2CheckHit(act);
					break;
				default:
					// Area 3 boss Loki isn't checked here
					break;
				}
			} else {
				if (act->_solid == 2 && (actor->_moveType == 16 || actor->_moveType == 17))
					return 0;
				actorDamaged(act, actor->_hitStrength);
			}
			f++;
		}
	}
	if (f && actor->_moveType == 2)
		return 0;

	actor->_x = x;
	actor->_y = y;
	return 1;
}

// Check enemy move
int checkMove2(const int x, const int y, Actor *actor) {
	if (actor->_actorNum < 3)
		return checkMove1(x, y, actor);

	if (x < 0 || x > (319 - actor->_sizeX) || y < 0 || y > 175)
		return 0;

	int x1 = (x + 1) >> 4;
	int y1;
	if (!actor->_funcNum)
		y1 = (y + (actor->_sizeY / 2)) >> 4;
	else
		y1 = (y + 1) >> 4;

	int x2 = ((x + actor->_sizeX) - 1) >> 4;
	int y2 = ((y + actor->_sizeY) - 1) >> 4;

	// Check for solid or fly over

	int icn = TILE_FLY;
	if (actor->_flying)
		icn = TILE_SOLID;

	const byte icn1 = _G(scrn)._iconGrid[y1][x1];
	const byte icn2 = _G(scrn)._iconGrid[y2][x1];
	const byte icn3 = _G(scrn)._iconGrid[y1][x2];
	const byte icn4 = _G(scrn)._iconGrid[y2][x2];
	if (icn1 < icn || icn2 < icn || icn3 < icn || icn4 < icn)
		return 0;

	if (icn1 > TILE_SPECIAL)
		if (!specialTile(actor, y1, x1, icn1))
			return 0;
	if (icn2 > TILE_SPECIAL)
		if (!specialTile(actor, y2, x1, icn2))
			return 0;
	if (icn3 > TILE_SPECIAL)
		if (!specialTile(actor, y1, x2, icn3))
			return 0;
	if (icn4 > TILE_SPECIAL)
		if (!specialTile(actor, y2, x2, icn4))
			return 0;

	x1 = x + 1;
	y1 = y + 1;
	x2 = (x + actor->_sizeX) - 1;
	y2 = (y + actor->_sizeY) - 1;

	for (int i = 0; i < MAX_ACTORS; i++) {
		Actor *act = &_G(actor[i]);
		if (act->_actorNum == actor->_actorNum || act->_actorNum == 1 || !act->_active)
			continue;
		if (act->_type == 3)
			continue; // Shot

		if (i == 0) {
			if (overlap(x1, y1, x2, y2, _G(thorX1), _G(thorY1), _G(thorX2), _G(thorY2))) {
				thorDamaged(actor);
				return 0;
			}
		} else {
			const int x3 = act->_x;
			const int y3 = act->_y;
			
			if (ABS(x3 - x1) > 16 || ABS(y3 - y1) > 16)
				continue;

			const int x4 = act->_x + act->_sizeX;
			const int y4 = act->_y + act->_sizeY;
			if (overlap(x1, y1, x2, y2, x3, y3, x4, y4)) {
				if (actor->_moveType == 38) {
					if (act->_funcNum == 4)
						_G(switchUsed) = 1;
					else if (act->_funcNum == 7)
						_G(switchUsed) = 2;
				}
				return 0;
			}
		}
	}
	actor->_x = x;
	actor->_y = y;
	return 1;
}

// Check enemy shot move
int checkMove3(const int x, const int y, Actor *actor) {
	if (x < 0 || x > (319 - actor->_sizeX) || y < 0 || y > 175)
		return 0;

	int x1 = (x + 1) >> 4;
	int y1 = (y + (actor->_sizeY / 2)) >> 4;
	int x2 = ((x + actor->_sizeX) - 1) >> 4;
	int y2 = ((y + actor->_sizeY) - 1) >> 4;

	// Check for solid or fly over

	int icn = TILE_FLY;
	if (actor->_flying)
		icn = TILE_SOLID;

	const byte icn1 = _G(scrn)._iconGrid[y1][x1];
	const byte icn2 = _G(scrn)._iconGrid[y2][x1];
	const byte icn3 = _G(scrn)._iconGrid[y1][x2];
	const byte icn4 = _G(scrn)._iconGrid[y2][x2];
	if (icn1 < icn || icn2 < icn || icn3 < icn || icn4 < icn)
		return 0;

	if (icn1 > TILE_SPECIAL && !specialTile(actor, y1, x1, icn1))
		return 0;
	if (icn2 > TILE_SPECIAL && !specialTile(actor, y2, x1, icn2))
		return 0;
	if (icn3 > TILE_SPECIAL && !specialTile(actor, y1, x2, icn3))
		return 0;
	if (icn4 > TILE_SPECIAL && !specialTile(actor, y2, x2, icn4))
		return 0;

	// Check for solid or fly over
	x1 = x + 1;
	y1 = y + 1;
	x2 = (x + actor->_sizeX) - 1;
	y2 = (y + actor->_sizeY) - 1;

	if (overlap(x1, y1, x2, y2, _G(thorX1), _G(thorRealY1), _G(thorX2), _G(thorY2))) {
		thorDamaged(actor);
		return 0;
	}
	for (int i = 3; i < MAX_ACTORS; i++) {
		if (i == actor->_actorNum)
			continue;

		Actor *act = &_G(actor[i]);

		if (!act->_active)
			continue;
		if (act->_solid < 2)
			continue;
		if (act->_type == 3)
			continue; // Shot
		if (act->_actorNum == actor->_creator)
			continue;

		const int x3 = act->_x;
		const int y3 = act->_y;

		if (ABS(x3 - x1) > 16 || ABS(y3 - y1) > 16)
			continue;

		const int x4 = x3 + 15;
		const int y4 = y3 + 15;
		if (overlap(x1, y1, x2, y2, x3, y3, x4, y4))
			return 0;
	}
	actor->_x = x;
	actor->_y = y;
	return 1;
}

// Flying enemies
int checkMove4(const int x, const int y, Actor *actor) {
	if (x < 0 || x > (319 - actor->_sizeX) || y < 0 || y > 175)
		return 0;
	if (overlap(x, y, x + actor->_sizeX - 1, y + actor->_sizeY - 1,
				_G(thorX1), _G(thorY1), _G(thorX2), _G(thorY2))) {
		if (actor->_type == 3)
			thorDamaged(actor);
		return 0;
	}
	actor->_x = x;
	actor->_y = y;
	return 1;
}

#define THOR_PAD1 2
#define THOR_PAD2 4

int check_thor_move(const int x, const int y, Actor *actor) {
	if (checkMove0(x, y, actor))
		return 1;
	
	if (_G(diagFlag) || _G(thorSpecialFlag))
		return 0;

	if (_G(thorIcon1) + _G(thorIcon2) + _G(thorIcon3) + _G(thorIcon4) > 1)
		return 0;

	switch (actor->_dir) {
	case 0:
		if (_G(thorIcon1)) {
			actor->_dir = 3;
			if (checkMove0(x + THOR_PAD1, y + 2, actor)) {
				actor->_dir = 0;
				return 1;
			}
		} else if (_G(thorIcon3)) {
			actor->_dir = 2;
			if (checkMove0(x - THOR_PAD1, y + 2, actor)) {
				actor->_dir = 0;
				return 1;
			}
		}
		actor->_dir = 0;
		break;
		
	case 1:
		if (_G(thorIcon2)) {
			actor->_dir = 3;
			if (checkMove0(x + THOR_PAD1, y - 2, actor)) {
				actor->_dir = 1;
				return 1;
			}
		} else if (_G(thorIcon4)) {
			actor->_dir = 2;
			if (checkMove0(x - THOR_PAD1, y - 2, actor)) {
				actor->_dir = 1;
				return 1;
			}
		}
		actor->_dir = 1;
		break;
		
	case 2:
		if (_G(thorIcon1)) {
			if (checkMove0(x + 2, y + THOR_PAD1, actor))
				return 1;
		} else if (_G(thorIcon2)) {
			if (checkMove0(x + 2, y - THOR_PAD1, actor))
				return 1;
		}
		break;
		
	case 3:
		if (_G(thorIcon3)) {
			if (checkMove0(x - 2, y + THOR_PAD1, actor))
				return 1;
		} else if (_G(thorIcon4)) {
			if (checkMove0(x - 2, y - THOR_PAD1, actor))
				return 1;
		}
		break;

	default:
		break;
	}

	return 0;
}

// Player control
int movementZero(Actor *actor) {
	int d = actor->_dir;
	const int oldDir = d;

	setThorVars();

	if (_G(hammer)->_active && _G(hammer)->_moveType == 5) {
		if (overlap(_G(thorX1), _G(thorY1), _G(thorX2), _G(thorY2),
					_G(hammer)->_x, _G(hammer)->_y, _G(hammer)->_x + 13, _G(hammer)->_y + 13)) {
			actorDestroyed(_G(hammer));
		}
	}
	int x = actor->_x;
	int y = actor->_y;
	_G(diagFlag) = false;
	if (actor->_moveCounter)
		actor->_moveCounter--;

	if (_G(slipping)) {
		if (_G(slipCount) == 8)
			playSound(FALL, true);

		y += 2;
		_G(slipCount--);
		if (!_G(slipCount))
			_G(slipping) = false;

		check_thor_move(x, y, actor);
		_G(thor)->_moveCountdown = 4;
		return d;
	}

	if (_G(keyFlag[key_up]) && _G(keyFlag[key_left])) {
		d = 2;
		actor->_dir = d;
		_G(diag) = 1;
		_G(diagFlag) = true;
		if (check_thor_move(x - 2, y - 2, actor)) {
			nextFrame(actor);
			return d;
		}
	} else if (_G(keyFlag[key_up]) && _G(keyFlag[key_right])) {
		d = 3;
		actor->_dir = d;
		_G(diag) = 2;
		_G(diagFlag) = true;
		if (check_thor_move(x + 2, y - 2, actor)) {
			nextFrame(actor);
			return d;
		}
	} else if (_G(keyFlag[key_down]) && _G(keyFlag[key_left])) {
		d = 2;
		actor->_dir = d;
		_G(diag) = 4;
		_G(diagFlag) = true;
		if (check_thor_move(x - 2, y + 2, actor)) {
			nextFrame(actor);
			return d;
		}
	} else if (_G(keyFlag[key_down]) && _G(keyFlag[key_right])) {
		d = 3;
		actor->_dir = d;
		_G(diag) = 3;
		_G(diagFlag) = true;
		if (check_thor_move(x + 2, y + 2, actor)) {
			nextFrame(actor);
			return d;
		}
	}
	_G(diag) = 0;
	if (_G(keyFlag[key_right]) && !_G(keyFlag[key_left])) {
		d = 3;
		actor->_dir = d;
		if (check_thor_move(x + 2, y, actor)) {
			nextFrame(actor);
			return d;
		}
	}

	if (_G(keyFlag[key_left]) && !_G(keyFlag[key_right])) {
		d = 2;
		actor->_dir = d;
		if (check_thor_move(x - 2, y, actor)) {
			nextFrame(actor);
			return d;
		}
	}

	if (_G(keyFlag[key_down]) && !_G(keyFlag[key_up])) {
		d = 1;
		actor->_dir = d;
		if (check_thor_move(x, y + 2, actor)) {
			nextFrame(actor);
			return d;
		}
	}

	if (_G(keyFlag[key_up]) && !_G(keyFlag[key_down])) {
		d = 0;
		actor->_dir = d;
		if (check_thor_move(x, y - 2, actor)) {
			nextFrame(actor);
			return d;
		}
	}

	if (_G(eyeballs)) {
		nextFrame(actor);
		actor->_dir = oldDir;
		return d;
	}

	actor->_moveCounter = 5;
	actor->_nextFrame = 0;
	actor->_dir = oldDir;
	return d;
}

int checkSpecialMove1(const int x, const int y, Actor *actor) {
	int x3, y3, x4, y4;

	Actor *act;

	if (actor->_actorNum < 3)
		return checkMove1(x, y, actor);

	if (x < 0 || x > 304 || y < 0 || y > 176)
		return 0;

	int x1 = x >> 4;
	int y1 = y >> 4;
	int x2 = (x + 15) >> 4;
	int y2 = (y + 15) >> 4;

	// Check for solid or fly over

	int icn = TILE_FLY;
	if (actor->_flying)
		icn = TILE_SOLID;

	const byte icn1 = _G(scrn)._iconGrid[y1][x1];
	const byte icn2 = _G(scrn)._iconGrid[y2][x1];
	const byte icn3 = _G(scrn)._iconGrid[y1][x2];
	const byte icn4 = _G(scrn)._iconGrid[y2][x2];
	if (icn1 < icn || icn2 < icn || icn3 < icn || icn4 < icn)
		return 0;

	if (icn1 > TILE_SPECIAL && !specialTile(actor, y1, x1, icn1))
		return 0;
	if (icn2 > TILE_SPECIAL && !specialTile(actor, y2, x1, icn2))
		return 0;
	if (icn3 > TILE_SPECIAL && !specialTile(actor, y1, x2, icn3))
		return 0;
	if (icn4 > TILE_SPECIAL && !specialTile(actor, y2, x2, icn4))
		return 0;

	x1 = x;
	y1 = y;
	x2 = (x + 15);
	y2 = (y + 15);

	for (int i = 3; i < MAX_ACTORS; i++) {
		act = &_G(actor[i]);
		if (act->_actorNum == actor->_actorNum)
			continue;
		if (!act->_active)
			continue;
		if (act->_type == 3)
			continue; //shot
		x3 = act->_x;
		if ((ABS(x3 - x1)) > 16)
			continue;
		y3 = act->_y;
		if ((ABS(y3 - y1)) > 16)
			continue;
		x4 = act->_x + act->_sizeX;
		y4 = act->_y + 15;
		if (overlap(x1, y1, x2, y2, x3, y3, x4, y4))
			return 0;
	}
	for (int i = 3; i < MAX_ACTORS; i++) {
		act = &_G(actor[i]);
		if (act->_actorNum == actor->_actorNum)
			continue;
		if (!act->_active)
			continue;
		if (act->_type == 3)
			continue; // Shot
		x3 = act->_x;
		if ((ABS(x3 - x1)) > 16)
			continue;
		y3 = act->_y;
		if ((ABS(y3 - y1)) > 16)
			continue;
		x4 = act->_x + act->_sizeX;
		y4 = act->_y + act->_sizeY;
		if (overlap(_G(thorX1), _G(thorY1), _G(thorX2), _G(thorY2), x3, y3, x4, y4))
			return 0;
	}
	actor->_x = x;
	actor->_y = y;
	return 1;
}

//*==========================================================================

// Block
int specialMovementOne(Actor *actor) {
	if (_G(diagFlag))
		return 0;

	const int d = _G(thor)->_dir;
	int x1 = actor->_x;
	int y1 = actor->_y;
	const int sd = actor->_lastDir;
	actor->_lastDir = d;

	switch (d) {
	case 0:
		y1 -= 2;
		if (!checkSpecialMove1(x1, y1, actor)) {
			actor->_lastDir = sd;
			return 0;
		}
		break;
		
	case 1:
		y1 += 2;
		if (!checkSpecialMove1(x1, y1, actor)) {
			actor->_lastDir = sd;
			return 0;
		}
		break;
		
	case 2:
		x1 -= 2;
		if (!checkSpecialMove1(x1, y1, actor)) {
			actor->_lastDir = sd;
			return 0;
		}
		break;
		
	case 3:
		x1 += 2;
		if (!checkSpecialMove1(x1, y1, actor)) {
			actor->_lastDir = sd;
			return 0;
		}
		break;
		
	default:
		break;
	}
	nextFrame(actor);
	actor->_lastDir = d;
	return 1;
}

// Angle
int specialMovementTwo(Actor *actor) {
	const int x1 = actor->_temp1; // Calc thor pos
	const int y1 = actor->_temp2;
	const int x2 = x1 + 13;
	const int y2 = y1 + 14;

	for (int i = 3; i < MAX_ACTORS; i++) {
		Actor *act = &_G(actor[i]);
		if (actor->_actorNum == act->_actorNum)
			continue;
		if (!act->_active)
			continue;
		const int x3 = act->_x;
		if ((ABS(x3 - x1)) > 16)
			continue;
		const int y3 = act->_y;
		if ((ABS(y3 - y1)) > 16)
			continue;
		const int x4 = act->_x + act->_sizeX;
		const int y4 = act->_y + act->_sizeY;
		if (overlap(x1, y1, x2, y2, x3, y3, x4, y4))
			return 0;
	}
	if (!actor->_funcPass) {
		if (_G(thor)->_health < 150) {
			if (!soundPlaying())
				playSound(ANGEL, false);
			_G(thor)->_health += 1;
		}
	} else if (_G(thorInfo)._magic < 150) {
		if (!soundPlaying())
			playSound(ANGEL, false);
		_G(thorInfo)._magic += 1;
	}

	return 1;
}

// Yellow globe
int specialMovementThree(Actor *actor) {
	if (_G(thunderSnakeCounter))
		return 0;

	long lind = (long)_G(currentLevel);
	lind *= 1000;
	lind += (long)actor->_actorNum;
	executeScript(lind, _G(odin));

	return 0;
}

// Peg switch
int specialMovementFour(Actor *actor) {
	if (actor->_shotCountdown != 0)
		return 0;
	actor->_shotCountdown = 30;

	_G(switchUsed) = 1;
	return 0;
}

// Boulder roll
int specialMovementFive(Actor *actor) {
	int d = _G(thor)->_dir;

	if (_G(diagFlag)) {
		switch (_G(diag)) {
		case 1:
			if (_G(thorX1) < (actor->_x + 15))
				d = 0;
			else
				d = 2;
			break;
			
		case 2:
			if (_G(thorX2) < actor->_x)
				d = 3;
			else
				d = 0;
			break;
			
		case 3:
			if (_G(thorX2) > (actor->_x))
				d = 1;
			else
				d = 3;
			break;
			
		case 4:
			if (_G(thorX1) > (actor->_x + 15))
				d = 2;
			else
				d = 1;
			break;

		default:
			break;
		}
	}

	actor->_lastDir = d;
	actor->_moveType = 14;
	return 0;
}

int specialMovementSix(Actor *actor) {
	thorDamaged(actor);
	return 0;
}

int specialMovementSeven(Actor *actor) {
	if (actor->_shotCountdown != 0)
		return 0;

	actor->_shotCountdown = 30;

	_G(switchUsed) = 2;
	return 0;
}

int specialMovementEight(Actor *actor) {
	if (_G(thor)->_dir < 2 || _G(diagFlag))
		return 0;

	actor->_lastDir = _G(thor)->_dir;
	actor->_moveType = 14;
	return 0;
}

int specialMovementNine(Actor *actor) {
	if (_G(thor)->_dir > 1 || _G(diagFlag))
		return 0;

	actor->_lastDir = _G(thor)->_dir;
	actor->_moveType = 14;
	return 0;
}

int specialMovementTen(Actor *actor) {
	byte &actor_ctr = GAME1 ? actor->_temp6 : actor->_talkCounter;

	if (actor_ctr) {
		actor_ctr--;
		return 0;
	}

	if (_G(thunderSnakeCounter))
		return 0;

	actor_ctr = 10;
	actorSpeaks(actor, 0 - actor->_passValue, 0);
	return 0;
}

// Red guard
int specialMovementEleven(Actor *actor) {
	if (actor->_talkCounter) {
		actor->_talkCounter--;
		return 0;
	}

	const int oldType = actor->_type;
	actor->_type = 4;
	actorSpeaks(actor, 0, 0);
	actor->_type = oldType;
	actor->_talkCounter = 10;

	return 0;
}

// No movement - frame cycle
int movementOne(Actor *actor) {
	nextFrame(actor);
	return actor->_dir;
}

// Hammer only
int movementTwo(Actor *actor) {
	int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	switch (d) {
	case 0:
		y1 -= 2;
		break;
		
	case 1:
		y1 += 2;
		break;
		
	case 2:
		x1 -= 2;
		break;
		
	case 3:
		x1 += 2;
		break;
		
	default:
		break;
	}
	if (!checkMove2(x1, y1, actor)) {
		if (actor->_actorNum == 1) {
			_G(hammer)->_moveType = 5;
			d = reverseDirection(_G(hammer));
			_G(hammer)->_dir = d;
		}
		if (actor->_actorNum == 2) {
			actor->_active = false;
			actor->_dead = 2;
			_G(tornadoUsed) = false;
		}
	}
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Walk-bump-random turn
int movementThree(Actor *actor) {
	int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	switch (d) {
	case 0:
		y1 -= 2;
		if (!checkMove2(x1, y1, actor)) {
			d = g_events->getRandomNumber(3);
		}
		break;
		
	case 1:
		y1 += 2;
		if (!checkMove2(x1, y1, actor)) {
			d = g_events->getRandomNumber(3);
		}
		break;
		
	case 2:
		x1 -= 2;
		if (!checkMove2(x1, y1, actor)) {
			d = g_events->getRandomNumber(3);
		}
		break;
		
	case 3:
		x1 += 2;
		if (!checkMove2(x1, y1, actor)) {
			d = g_events->getRandomNumber(3);
		}
		break;

	default:
		break;
	}
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Simple tracking
int movementFour(Actor *actor) {
	int d = actor->_lastDir;

	int x1 = actor->_x;
	int y1 = actor->_y;

	int f = 0;
	if (x1 > _G(thorX1) - 1) {
		x1 -= 2;
		d = 2;
		f = 1;
	} else if (x1 < _G(thorX1) - 1) {
		x1 += 2;
		d = 3;
		f = 1;
	}

	if (f)
		f = checkMove2(x1, y1, actor);

	if (!f) {
		if (y1 < (_G(thorRealY1))) {
			d = (_G(thorRealY1)) - y1;
			if (d > 2)
				d = 2;
			y1 += d;
			d = 1;
			f = 1;
		} else if (y1 > (_G(thorRealY1))) {
			d = y1 - (_G(thorRealY1));
			if (d > 2)
				d = 2;
			y1 -= d;
			d = 0;
			f = 1;
		}
		if (f)
			f = checkMove2(actor->_x, y1, actor);
		if (!f)
			checkMove2(actor->_x, actor->_y, actor);
	}
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

int movementFive(Actor *actor) {
	int x1 = actor->_x;
	int y1 = actor->_y;
	int xd = 0;
	int yd = 0;
	int d = actor->_lastDir;

	if (x1 > (_G(thor)->_x + 1))
		xd = -2; //+1
	else if (x1 < (_G(thor)->_x - 1))
		xd = 2;

	if (actor->_actorNum == 1) {
		if (y1 < (_G(thorY1) - 6))
			yd = 2;
		else if (y1 > (_G(thorY1) - 6))
			yd = -2;
	} else {
		if (y1 < (_G(thorRealY1) - 1))
			yd = 2;
		else if (y1 > (_G(thorRealY1) + 1))
			yd = -2;
	}

	if (xd && yd) {
		if (xd == -2 && ABS(yd) == 2)
			d = 2;
		else if (xd == 2 && ABS(yd) == 2)
			d = 3;

		x1 += xd;
		y1 += yd;
		if (checkMove2(x1, y1, actor)) {
			nextFrame(actor);
			actor->_lastDir = d;
			if (actor->_directions == 1)
				return 0;
			return d;
		}
	} else {
		if (xd == 0 && yd == 2)
			d = 1;
		else if (xd == 0 && yd == -2)
			d = 0;
		else if (xd == 2 && yd == 0)
			d = 3;
		else if (xd == -2 && yd == 0)
			d = 2;
	}
	x1 = actor->_x;
	y1 = actor->_y;
	actor->_toggle ^= 1;

	if (actor->_toggle) {
		if (xd) {
			x1 += xd;
			if (checkMove2(x1, y1, actor)) {
				if (xd > 0)
					d = 3;
				else
					d = 2;
				nextFrame(actor);
				actor->_lastDir = d;
				if (actor->_directions == 1)
					return 0;
				return d;
			}
			x1 = actor->_x;
		}
		if (yd) {
			y1 += yd;
			if (checkMove2(x1, y1, actor)) {
				if (yd > 0)
					d = 1;
				else
					d = 0;
				nextFrame(actor);
				actor->_lastDir = d;
				if (actor->_directions == 1)
					return 0;
				return d;
			}
		}
	} else {
		if (yd) {
			y1 += yd;
			if (checkMove2(x1, y1, actor)) {
				if (yd > 0)
					d = 1;
				else
					d = 0;
				nextFrame(actor);
				actor->_lastDir = d;
				if (actor->_directions == 1)
					return 0;
				return d;
			}
			y1 = actor->_y;
		}
		if (xd) {
			x1 += xd;
			if (checkMove2(x1, y1, actor)) {
				if (xd > 0)
					d = 3;
				else
					d = 2;
				nextFrame(actor);
				actor->_lastDir = d;
				if (actor->_directions == 1)
					return 0;
				return d;
			}
		}
	}
	checkMove2(actor->_x, actor->_y, actor);
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Explosion only
int movementSix(Actor *actor) {
	if (actor->_currNumShots > 0) {
		actor->_nextFrame++;
		if (actor->_nextFrame > 2) {
			actor->_nextFrame = 0;
			if (_G(bossDead))
				playSound(EXPLODE, false);
		}
		actor->_currNumShots--;
	} else {
		actor->_dead = 2;
		actor->_active = false;
		if (!_G(bossDead) && !_G(endGame)) {
			if (actor->_type == 2)
				dropRandomObject(actor);
		}
	}

	nextFrame(actor);
	return 0;
}

// Walk-bump-random turn (pause also)
int movementSeven(Actor *actor) {
	if (actor->_nextFrame == 0 && actor->_frameCount == actor->_frameSpeed) {
		actor->_moveCountdown = 12;
		actor->_lastDir = g_events->getRandomNumber(3);
	}
	return movementThree(actor);
}

// Follow thor
int movementEight(Actor *actor) {
	if (_G(thor)->_x > 0)
		actor->_x = _G(thor)->_x - 1;
	else
		actor->_x = _G(thor)->_x;
	actor->_y = _G(thor)->_y;
	nextFrame(actor);
	return 0;
}

// 4-way straight (random length) change
int movementNine(Actor *actor) {
	int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	int f = 0;
	if (actor->_counter) {
		actor->_counter--;
		switch (d) {
		case 0:
			y1 -= 2;
			if (!checkMove4(x1, y1, actor))
				f = 1;
			break;
			
		case 1:
			y1 += 2;
			if (!checkMove4(x1, y1, actor))
				f = 1;
			break;
			
		case 2:
			x1 -= 2;
			if (!checkMove4(x1, y1, actor))
				f = 1;
			break;
			
		case 3:
			x1 += 2;
			if (!checkMove4(x1, y1, actor))
				f = 1;
			break;

		default:
			break;
		}
	} else
		f = 1;

	if (f == 1) {
		actor->_counter = g_events->getRandomNumber(10, 99);
		d = g_events->getRandomNumber(3);
	}
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Vertical straight (random length) change
int movementTen(Actor *actor) {
	int lastDir = actor->_lastDir;
	const int x1 = actor->_x;
	int y1 = actor->_y;

	bool setRandomDirFl = false;
	if (actor->_counter) {
		if (actor->_passValue != 1)
			actor->_counter--;
		switch (lastDir) {
		case 0:
		case 2:
			y1 -= 2;
			if (!checkMove2(x1, y1, actor))
				setRandomDirFl = true;
			break;
		case 1:
		case 3:
			y1 += 2;
			if (!checkMove2(x1, y1, actor))
				setRandomDirFl = true;
			break;

		default:
			break;
		}
	} else
		setRandomDirFl = true;

	if (setRandomDirFl) {
		actor->_counter = g_events->getRandomNumber(10, 99);
		lastDir = g_events->getRandomNumber(1);
	}

	if (lastDir > 1)
		lastDir -= 2;

	nextFrame(actor);
	actor->_lastDir = lastDir;
	if (actor->_directions == 1)
		return 0;
	return lastDir;
}

// Horizontal only (bats)
int movementEleven(Actor *actor) {
	int d = actor->_lastDir;

	switch (d) {
	case 0:
		if (checkMove2(actor->_x - 2, actor->_y - 2, actor))
			break;
		d = 1;
		if (checkMove2(actor->_x - 2, actor->_y + 2, actor))
			break;
		d = 2;
		break;
		
	case 1:
		if (checkMove2(actor->_x - 2, actor->_y + 2, actor))
			break;
		d = 0;
		if (checkMove2(actor->_x - 2, actor->_y - 2, actor))
			break;
		d = 3;
		break;
		
	case 2:
		if (checkMove2(actor->_x + 2, actor->_y - 2, actor))
			break;
		d = 3;
		if (checkMove2(actor->_x + 2, actor->_y + 2, actor))
			break;
		d = 0;
		break;
		
	case 3:
		if (checkMove2(actor->_x + 2, actor->_y + 2, actor))
			break;
		d = 2;
		if (checkMove2(actor->_x + 2, actor->_y - 2, actor))
			break;
		d = 1;
		break;

	default:
		break;
		
	}

	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Horizontal straight until bump
int movementTwelve(Actor *actor) {
	int d = actor->_lastDir;

	switch (d) {
	case 0:
	case 2:
		if (checkMove2(actor->_x - 2, actor->_y, actor))
			break;
		d = 3;
		break;
		
	case 1:
	case 3:
		if (checkMove2(actor->_x + 2, actor->_y, actor))
			break;
		d = 2;
		break;
		
	default:
		break;
	}
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Pause-seek (mushroom)
int movementThirteen(Actor *actor) {
	const int d = actor->_lastDir;

	if (actor->_counter == 0 && actor->_unpauseCountdown == 0) {
		actor->_unpauseCountdown = 60;
		return d;
	}
	if (actor->_unpauseCountdown > 0) {
		actor->_unpauseCountdown--;
		if (!actor->_unpauseCountdown)
			actor->_counter = 60;
		actor->_vulnerableCountdown = 5;
		actor->_hitStrength = 0;
		return d;
	}
	if (actor->_counter > 0) {
		actor->_counter--;
		if (!actor->_counter)
			actor->_unpauseCountdown = 60;
		actor->_hitStrength = 10;
		return movementFive(actor);
	}
	return d;
}

// Move-bump-stop (boulder)
int movementFourteen(Actor *actor) {
	const int d = actor->_lastDir;
	actor->_dir = d;
	int x1 = actor->_x;
	int y1 = actor->_y;

	switch (d) {
	case 0:
		y1 -= 2;
		if (!checkMove2(x1, y1, actor)) {
			actor->_moveType = 15;
			return 0;
		}
		break;
		
	case 1:
		y1 += 2;
		if (!checkMove2(x1, y1, actor)) {
			actor->_moveType = 15;
			return 0;
		}
		break;
		
	case 2:
		x1 -= 2;
		if (!checkMove2(x1, y1, actor)) {
			actor->_moveType = 15;
			return 0;
		}
		break;
		
	case 3:
		x1 += 2;
		if (!checkMove2(x1, y1, actor)) {
			actor->_moveType = 15;
			return 0;
		}
		break;

	default:
		break;
	}
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// No movement - no frame cycle
int movementFifteen(Actor *actor) {
	return actor->_dir;
}

// Tornado 1
int movementSixteen(Actor *actor) {
	int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	switch (d) {
	case 0:
		y1 -= 2;
		break;
		
	case 1:
		y1 += 2;
		break;
		
	case 2:
		x1 -= 2;
		break;
		
	case 3:
		x1 += 2;
		break;
		
	default:
		break;
	}

	if (!checkMove1(x1, y1, actor)) {
		actor->_moveType = 17;
		d = g_events->getRandomNumber(3);
	}

	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Tornado 2
int movementSeventeen(Actor *actor) {
	int d = actor->_lastDir;

	switch (d) {
	case 0:
		if (checkMove1(actor->_x - 2, actor->_y - 2, actor))
			break;
		d = 1;
		if (checkMove1(actor->_x - 2, actor->_y + 2, actor))
			break;
		d = 2;
		break;
		
	case 1:
		if (checkMove1(actor->_x - 2, actor->_y + 2, actor))
			break;
		d = 0;
		if (checkMove1(actor->_x - 2, actor->_y - 2, actor))
			break;
		d = 3;
		break;
		
	case 2:
		if (checkMove1(actor->_x + 2, actor->_y - 2, actor))
			break;
		d = 3;
		if (checkMove1(actor->_x + 2, actor->_y + 2, actor))
			break;
		d = 0;
		break;
		
	case 3:
		if (checkMove1(actor->_x + 2, actor->_y + 2, actor))
			break;
		d = 2;
		if (checkMove1(actor->_x + 2, actor->_y - 2, actor))
			break;
		d = 1;
		break;
		
	default:
		break;
	}
	
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// No movement - frame cycle
int movementEighteen(Actor *actor) {
	int d;

	if (actor->_temp5) {
		actor->_temp5--;
		if (!actor->_temp5)
			actor->_numMoves = 1;
	}
	if (actor->_temp1) {
		d = movementFive(actor);
		actor->_rand--;
		if (actor->_hitThor || !actor->_rand) {
			if (actor->_hitThor) {
				actor->_temp5 = 50;
				actor->_numMoves = 2;
				actor->_hitThor = false;
				actor->_dir = d;
				d = reverseDirection(actor);
			}
			actor->_temp1 = 0;
			actor->_rand = g_events->getRandomNumber(50, 149);
		}
	} else {
		d = movementThree(actor);
		actor->_rand--;
		if (!actor->_rand) {
			actor->_temp5 = 0;
			actor->_temp1 = 1;
			actor->_rand = g_events->getRandomNumber(50, 149);
		}
		if (actor->_hitThor) {
			actor->_temp5 = 50;
			actor->_numMoves = 2;
			actor->_hitThor = false;
			actor->_dir = d;
			d = reverseDirection(actor);
		}
	}
	nextFrame(actor);
	return d;
}

// No movement - frame cycle
int movementNineteen(Actor *actor) {
	return movementSeven(actor);
}

int movementTwenty(Actor *actor) {
	if (GAME1)
		// Boss - snake
		return boss1Movement(actor);

	return movementOne(actor);
}

// No movement - frame cycle
int movementTwentyOne(Actor *actor) {
	return movementThree(actor);
}

// Spear
int movementTwentyTwo(Actor *actor) {
	int d = actor->_lastDir;
	if (actor->_directions == 1)
		d = 0;

redo:

	switch (actor->_temp2) {
	case 0:
		if (backgroundTile(actor->_x, actor->_y) >= TILE_SOLID)
			actor->_nextFrame = 1;
		else {
			actor->_temp2 = 6;
			actor->_temp1 = 1;
			goto redo;
		}
		actor->_temp2++;
		break;
		
	case 1:
		actor->_nextFrame = 2;
		actor->_temp2++;
		break;
		
	case 2:
		actor->_nextFrame = 3;
		actor->_hitStrength = 255;
		actor->_temp2++;
		actor->_temp1 = 10;
		break;
		
	case 3:
		checkMove2(actor->_x, actor->_y, actor);
		actor->_temp1--;
		if (actor->_temp1)
			break;
		actor->_temp2++;
		actor->_nextFrame = 2;
		break;
		
	case 4:
		actor->_hitStrength = 0;
		actor->_temp2++;
		actor->_nextFrame = 1;
		break;
		
	case 5:
		actor->_temp2++;
		actor->_nextFrame = 0;
		actor->_temp1 = 10;
		break;
		
	case 6:
		actor->_temp1--;
		if (actor->_temp1)
			break;
		actor->_temp2 = 0;
		actor->_nextFrame = 0;
		switch (d) {
		case 0:
			actor->_x += 16;
			actor->_y += 16;
			d = 3;
			if (backgroundTile(actor->_x, actor->_y) < TILE_SOLID)
				goto redo;
			break;
			
		case 1:
			actor->_x -= 16;
			actor->_y -= 16;
			d = 2;
			if (backgroundTile(actor->_x, actor->_y) < TILE_SOLID)
				goto redo;
			break;
			
		case 2:
			actor->_x += 16;
			actor->_y -= 16;
			d = 0;
			if (backgroundTile(actor->_x, actor->_y) < TILE_SOLID)
				goto redo;
			break;
			
		case 3:
			actor->_x -= 16;
			actor->_y += 16;
			d = 1;
			if (backgroundTile(actor->_x, actor->_y) < TILE_SOLID)
				goto redo;
			break;
		default:
			break;
		}
		actor->_dir = d;
		actor->_lastDir = d;
		break;
		
	default:
		break;
	}
	return d;
}

// Spinball counter-clockwise
int movementTwentyThree(Actor *actor) {
	int d = actor->_lastDir;
	nextFrame(actor);
	if (actor->_passValue & 2)
		actor->_numMoves = 2;

	switch (d) {
	case 0:
		if (backgroundTile(actor->_x - 2, actor->_y) >= TILE_FLY &&
			backgroundTile(actor->_x - 2, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
			d = 2;
			actor->_x -= 2;
		} else {
			if (backgroundTile(actor->_x, actor->_y - 2) < TILE_FLY ||
				backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y - 2) < TILE_FLY) {
				if (backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y) >= TILE_FLY &&
					backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
					d = 3;
					actor->_x += 2;
				} else {
					d = 1;
					break;
				}
			} else
				actor->_y -= 2;
		}
		break;
		
	case 1:
		if (backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y) >= TILE_FLY &&
			backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
			d = 3;
			actor->_x += 2;
		} else {
			if (backgroundTile(actor->_x, actor->_y + actor->_sizeY + 1) < TILE_FLY ||
				backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y + actor->_sizeY + 1) < TILE_FLY) {
				if (backgroundTile(actor->_x - 2, actor->_y) >= TILE_FLY &&
					backgroundTile(actor->_x - 2, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
					d = 2;
					actor->_x -= 2;
				} else {
					d = 0;
					break;
				}
			} else
				actor->_y += 2;
		}
		break;
		
	case 2:
		if (backgroundTile(actor->_x, actor->_y + actor->_sizeY + 1) >= TILE_FLY &&
			backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y + actor->_sizeY + 1) >= TILE_FLY) {
			d = 1;
			actor->_y += 2;
		} else {
			if (backgroundTile(actor->_x - 2, actor->_y) < TILE_FLY ||
				backgroundTile(actor->_x - 2, actor->_y + actor->_sizeY - 1) < TILE_FLY) {
				if (backgroundTile(actor->_x, actor->_y - 2) >= TILE_FLY &&
					backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y - 2) >= TILE_FLY) {
					d = 0;
					actor->_y -= 2;
				} else {
					d = 3;
					break;
				}
			} else
				actor->_x -= 2;
		}
		break;
		
	case 3:
		if (backgroundTile(actor->_x, actor->_y - 2) >= TILE_FLY &&
			backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y - 2) >= TILE_FLY) {
			d = 0;
			actor->_y -= 2;
		} else {
			if (backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y) < TILE_FLY ||
				backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y + actor->_sizeY - 1) < TILE_FLY) {
				if (backgroundTile(actor->_x, actor->_y + actor->_sizeY + 1) >= TILE_FLY &&
					backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y + actor->_sizeY + 1) >= TILE_FLY) {
					d = 1;
					actor->_y += 2;
				} else {
					d = 2;
					break;
				}
			} else
				actor->_x += 2;
		}
		break;

	default:
		break;
	}
	
	checkMove2(actor->_x, actor->_y, actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Spinball  clockwise
int movementTwentyFour(Actor *actor) {
	int d = actor->_lastDir;
	nextFrame(actor);
	if (actor->_passValue & 2)
		actor->_numMoves = 2;

	switch (d) {
	case 0:
		if (backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y) >= TILE_FLY &&
			backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
			d = 3;
			actor->_x += 2;
		} else {
			if (backgroundTile(actor->_x, actor->_y - 2) < TILE_FLY ||
				backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y - 2) < TILE_FLY) {
				if (backgroundTile(actor->_x - 2, actor->_y) >= TILE_FLY &&
					backgroundTile(actor->_x - 2, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
					d = 2;
					actor->_x -= 2;
				} else {
					d = 1;
					break;
				}
			} else
				actor->_y -= 2;
		}
		break;
		
	case 1:
		if (backgroundTile(actor->_x - 2, actor->_y) >= TILE_FLY &&
			backgroundTile(actor->_x - 2, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
			d = 2;
			actor->_x -= 2;
		} else {
			if (backgroundTile(actor->_x, actor->_y + actor->_sizeY + 1) < TILE_FLY ||
				backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y + actor->_sizeY + 1) < TILE_FLY) {
				if (backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y) >= TILE_FLY &&
					backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y + actor->_sizeY - 1) >= TILE_FLY) {
					d = 3;
					actor->_x += 2;
				} else {
					d = 0;
					break;
				}
			} else
				actor->_y += 2;
		}
		break;
		
	case 2:
		if (backgroundTile(actor->_x, actor->_y - 2) >= TILE_FLY &&
			backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y - 2) >= TILE_FLY) {
			d = 0;
			actor->_y -= 2;
		} else {
			if (backgroundTile(actor->_x - 2, actor->_y) < TILE_FLY ||
				backgroundTile(actor->_x - 2, actor->_y + actor->_sizeY - 1) < TILE_FLY) {
				if (backgroundTile(actor->_x, actor->_y + actor->_sizeY + 1) >= TILE_FLY &&
					backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y + actor->_sizeY + 1) >= TILE_FLY) {
					d = 1;
					actor->_y += 2;
				} else {
					d = 3;
					break;
				}
			} else
				actor->_x -= 2;
		}
		break;
		
	case 3:
		if (backgroundTile(actor->_x, actor->_y + actor->_sizeY + 1) >= TILE_FLY &&
			backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y + actor->_sizeY + 1) >= TILE_FLY) {
			d = 1;
			actor->_y += 2;
		} else {
			if (backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y) < TILE_FLY ||
				backgroundTile(actor->_x + actor->_sizeX + 1, actor->_y + actor->_sizeY - 1) < TILE_FLY) {
				if (backgroundTile(actor->_x, actor->_y - 2) >= TILE_FLY &&
					backgroundTile(actor->_x + actor->_sizeX - 1, actor->_y - 2) >= TILE_FLY) {
					d = 0;
					actor->_y -= 2;
				} else {
					d = 2;
					break;
				}
			} else
				actor->_x += 2;
		}
		break;

	default:
		break;
	}
	
	checkMove2(actor->_x, actor->_y, actor);
	actor->_lastDir = d;
	
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Acid puddle
int movementTwentyFive(Actor *actor) {
	if (actor->_temp2) {
		actor->_temp2--;
		return movementOne(actor);
	}
	if (!actor->_temp1) {
		actor->_lastDir = g_events->getRandomNumber(3);
		int i = 4;
		while (i--) {
			int ret = movementThree(actor);
			if (ret)
				return ret;
			actor->_lastDir++;
			if (actor->_lastDir > 3)
				actor->_lastDir = 0;
		}
		actor->_temp1 = 16;
	}
	actor->_temp1--;
	return movementThree(actor);
}

int movementTwentySix(Actor *actor) {
	if (GAME2)
		return boss2Movement(actor);
	if (GAME3)
		return boss3Movement(actor);

	return movementOne(actor);
}

int movementTwentySeven(Actor *actor) {
	if (GAME2)
		return boss2Movement(actor);

	return movementOne(actor);
}

void setThorVars() {
	_G(thorX1) = _G(thor)->_x + 1;
	_G(thorY1) = _G(thor)->_y + 8;

	_G(thorRealY1) = _G(thor)->_y;
	_G(thorX2) = (_G(thor)->_x + 12);
	_G(thorY2) = _G(thor)->_y + 15;
}

// Fish
int movementTwentyEight(Actor *actor) {
	const int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;
	int ret;
	
	if (actor->_i1)
		actor->_i1--;
	else {
		if (!actor->_temp3) {
			if (!actor->_nextFrame) {
				actor->_frameCount = 1;
				actor->_frameSpeed = 4;
			}

			nextFrame(actor);
			if (actor->_nextFrame == 3) {
				if (actor->_currNumShots < actor->_numShotsAllowed)
					actorShoots(actor, 0);
				actor->_temp3 = 1;
			}
		} else {
			const int fcount = actor->_frameCount - 1;
			if (fcount <= 0) {
				actor->_nextFrame--;
				actor->_frameCount = actor->_frameSpeed;
				if (!actor->_nextFrame) {
					actor->_temp3 = 0;
					actor->_frameSpeed = 4;
					actor->_i1 = g_events->getRandomNumber(60, 159);
				}
			} else
				actor->_frameCount = fcount;
		}
		goto done;
	}
	switch (actor->_temp2) {
	case 0:
		y1 -= 2;
		break;
	case 1:
		y1 += 2;
		break;
	case 2:
		x1 -= 2;
		break;
	case 3:
		x1 += 2;
		break;
	default:
		break;
	}
	
	ret = backgroundTile(x1, y1);
	if (ret != 100 && ret != 106 && ret != 110 && ret != 111 && ret != 113)
		goto chg_dir;
	ret = backgroundTile((x1 + actor->_sizeX) - 1, y1);
	if (ret != 100 && ret != 106 && ret != 110 && ret != 111 && ret != 113)
		goto chg_dir;
	ret = backgroundTile(x1, (y1 + actor->_sizeY) - 1);
	if (ret != 100 && ret != 106 && ret != 110 && ret != 111 && ret != 113)
		goto chg_dir;
	ret = backgroundTile((x1 + actor->_sizeX) - 1, (y1 + actor->_sizeY) - 1);
	if (ret != 100 && ret != 106 && ret != 110 && ret != 111 && ret != 113)
		goto chg_dir;

	actor->_x = x1;
	actor->_y = y1;

	goto done;

chg_dir:
	actor->_temp2 = _G(rand1) % 4;

done:
	if (actor->_nextFrame) {
		x1 = actor->_x;
		y1 = actor->_y;
		actor->_solid = 1;
		checkMove2(x1, y1, actor);
		actor->_x = x1;
		actor->_y = y1;
	} else
		actor->_solid = 2;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Horizontal or vertical (pass_val)
int movementTwentyNine(Actor *actor) {
	if (!actor->_passValue)
		return movementThirty(actor);

	return movementTwelve(actor);
}

// Vertical straight
int movementThirty(Actor *actor) {
	int d = actor->_lastDir;
	const int x1 = actor->_x;
	int y1 = actor->_y;

	switch (d) {
	case 0:
		y1 -= 2;
		break;
	case 1:
		y1 += 2;
		break;
	default:
		break;
	}
	if (!checkMove2(x1, y1, actor))
		d ^= 1;

	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Drop (stalagtite)
int movementThirtyOne(Actor *actor) {
	const int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	if (actor->_temp1) {
		y1 += 2;
		if (!checkMove2(x1, y1, actor))
			actorDestroyed(actor);
	} else if (_G(thorY1) > y1 && ABS(x1 - _G(thorX1)) < 16) {
		const int cx = (actor->_x + (actor->_sizeX / 2)) >> 4;
		const int cy = ((actor->_y + actor->_sizeY) - 2) >> 4;
		const int ty = _G(thor)->_centerY;
		for (int i = cy; i <= ty; i++)
			if (_G(scrn)._iconGrid[i][cx] < TILE_SOLID)
				goto done;
		actor->_numMoves = actor->_passValue + 1;
		actor->_temp1 = 1;
	}

done:
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Unused
int movementThirtyTwo(Actor *actor) {
	return 0;
}

// Unused
int movementThirtyThree(Actor *actor) {
	return 0;
}

// Unused
int movementThirtyFour(Actor *actor) {
	return 0;
}

// Gun (single)
int movementThirtyFive(Actor *actor) {
	actor->_nextFrame = actor->_lastDir;
	return actor->_dir;
}

// Acid drop
int movementThirtySix(Actor *actor) {
	actor->_speed = actor->_passValue;
	nextFrame(actor);
	if (actor->_nextFrame == 0 && actor->_frameCount == actor->_frameSpeed) {
		actorAlwaysShoots(actor, 1);
		_G(actor[actor->_shotActor])._x -= 2;
	}
	return 0;
}

// 4-way straight (random length) change
int movementThirtySeven(Actor *actor) {
	int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	bool check = false;
	if (actor->_counter) {
		actor->_counter--;
		switch (d) {
		case 0:
			y1 -= 2;
			if (!checkMove2(x1, y1, actor))
				check = true;
			break;
			
		case 1:
			y1 += 2;
			if (!checkMove2(x1, y1, actor))
				check = true;
			break;
			
		case 2:
			x1 -= 2;
			if (!checkMove2(x1, y1, actor))
				check = true;
			break;
			
		case 3:
			x1 += 2;
			if (!checkMove2(x1, y1, actor))
				check = true;
			break;

		default:
			break;
		}
	} else
		check = true;

	if (check) {
		actor->_counter = g_events->getRandomNumber(10, 99);
		d = g_events->getRandomNumber(3);
	}
	
	nextFrame(actor);
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Timed darting
#define TIMER actor->_i1
#define INIT_DIR actor->_temp1
#define OTHER_DIR actor->_temp2
#define FLAG actor->_temp3
#define OX actor->_i2
#define OY actor->_i3
#define CNT actor->_i4
int movementThirtyEight(Actor *actor) {
	int d = actor->_lastDir;
	int x1 = actor->_x;
	int y1 = actor->_y;

	if (!FLAG) {
		FLAG = 1;
		if (actor->_passValue)
			TIMER = actor->_passValue * 15;
		else
			TIMER = g_events->getRandomNumber(5, 364);
		INIT_DIR = actor->_lastDir;
		OX = x1;
		OY = y1;
		CNT = 0;
		switch (INIT_DIR) {
		case 0:
			OTHER_DIR = 1;
			break;
		case 1:
			OTHER_DIR = 0;
			break;
		case 2:
			OTHER_DIR = 3;
			break;
		case 3:
			OTHER_DIR = 2;
			break;
		default:
			break;
		}
	}
	if (TIMER) {
		TIMER--;
		goto done;
	}

	switch (d) {
	case 0:
		y1 -= 2;
		break;
	case 1:
		y1 += 2;
		break;
	case 2:
		x1 -= 2;
		break;
	case 3:
		x1 += 2;
		break;
	default:
		break;
	}

	switch (FLAG) {
	case 1:
		if (!checkMove2(x1, y1, actor)) {
			if (CNT) {
				d = OTHER_DIR;
				actor->_lastDir = d;
				FLAG = 2;
			} else {
				actor->_nextFrame = 0;
				FLAG = 0;
				goto done;
			}
		} else
			CNT++;
		break;
		
	case 2:
		checkMove2(x1, y1, actor);
		if (x1 == OX && y1 == OY) {
			FLAG = 0;
			d = INIT_DIR;
			actor->_lastDir = d;
			actor->_nextFrame = 0;
			goto done;
		}
		break;

	default:
		break;
	}
	nextFrame(actor);

done:
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

// Troll 1
int movementThirtyNine(Actor *actor) {
	if (_G(setup)._difficultyLevel == 0) {
		actor->_speed = 3;
		actor->_numMoves = 1;
	} else if (_G(setup)._difficultyLevel == 1) {
		actor->_speed = 2;
		actor->_numMoves = 1;
	} else if (_G(setup)._difficultyLevel == 2) {
		actor->_speed = 1;
		actor->_numMoves = 1;
	}

	if (actor->_passValue < 5)
		return movementForty(actor);
	
	if (actor->_passValue == 10) {
		if (overlap(actor->_x + 1, actor->_y + 1, actor->_x + actor->_sizeX - 1,
					actor->_y + actor->_sizeY - 1, _G(thorX1), _G(thorY1), _G(thorX2), _G(thorY2))) {
			actor->_hitStrength = 255;
			thorDamaged(actor);
		}
		return actor->_dir;
	}
	if (actor->_actorNum != 3)
		return actor->_dir;

	if (actor->_i1) {
		actor->_i1--;
		actor->_x -= 2;
		checkMove2(actor->_x, actor->_y, actor);
		_G(actor[4])._x -= 2;
		_G(actor[5])._x -= 2;
		_G(actor[6])._x -= 2;
	}
	nextFrame(actor);
	if (actor->_nextFrame == 3)
		actor->_nextFrame = 0;
	_G(actor[4])._nextFrame = _G(actor[3])._nextFrame;
	_G(actor[5])._nextFrame = 0;
	_G(actor[6])._nextFrame = 0;
	return actor->_dir;
}

// Troll 2
int movementForty(Actor *actor) {
	if (overlap(actor->_x + 1, actor->_y + 1, actor->_x + actor->_sizeX + 3,
				actor->_y + actor->_sizeY - 1, _G(thorX1), _G(thorY1), _G(thorX2), _G(thorY2))) {
		actor->_hitStrength = 150;
		thorDamaged(actor);
	}
	int a = 5 + (actor->_passValue * 4);
	const int x1 = actor->_x;
	int d = actor->_lastDir;

	if (actor->_lastDir == 2) {
		if (backgroundTile(x1 - 2, actor->_y) >= TILE_SOLID) {
			_G(actor[a]._x) -= 2;
			_G(actor[a - 1])._x -= 2;
			_G(actor[a - 2])._x -= 2;
			_G(actor[a + 1])._x -= 2;
		} else
			d = 3;
	} else if (backgroundTile(_G(actor[a + 1])._x + 14, _G(actor[a + 1])._y) >= TILE_SOLID) {
		_G(actor[a])._x += 2;
		_G(actor[a - 1])._x += 2;
		_G(actor[a - 2])._x += 2;
		_G(actor[a + 1])._x += 2;
	} else
		d = 2;

	if (actor->_nextFrame == 3 && !actor->_currNumShots && actor->_frameCount == actor->_frameSpeed) {
		actorAlwaysShoots(actor, 1);
		_G(actor[actor->_shotActor])._x += 6;
	}

	nextFrame(actor);
	_G(actor[a - 2])._nextFrame = actor->_nextFrame;
	_G(actor[a - 1])._nextFrame = actor->_nextFrame;
	_G(actor[a + 1])._nextFrame = actor->_nextFrame;
	_G(actor[a - 2])._lastDir = d;
	_G(actor[a - 1])._lastDir = d;
	_G(actor[a + 1])._lastDir = d;
	actor->_lastDir = d;
	if (actor->_directions == 1)
		return 0;
	return d;
}

} // namespace Got
