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

#include "backends/keymapper/action.h"
#include "backends/keymapper/keymap.h"
#include "backends/keymapper/standard-actions.h"

#include "freescape/freescape.h"

namespace Freescape {

void FreescapeEngine::initKeymaps(Common::Keymap *engineKeyMap, Common::Keymap *infoScreenKeyMap, const char *target) {
	Common::Action *act;

	act = new Common::Action(Common::kStandardActionMoveUp, _("Up"));
	act->setCustomEngineActionEvent(kActionMoveUp);
	act->addDefaultInputMapping("UP");
	act->addDefaultInputMapping("JOY_UP");
	act->addDefaultInputMapping("o");
	engineKeyMap->addAction(act);

	act = new Common::Action(Common::kStandardActionMoveDown, _("Down"));
	act->setCustomEngineActionEvent(kActionMoveDown);
	act->addDefaultInputMapping("DOWN");
	act->addDefaultInputMapping("JOY_DOWN");
	act->addDefaultInputMapping("k");
	engineKeyMap->addAction(act);

	act = new Common::Action(Common::kStandardActionMoveLeft, _("Strafe left"));
	act->setCustomEngineActionEvent(kActionMoveLeft);
	act->addDefaultInputMapping("LEFT");
	act->addDefaultInputMapping("JOY_LEFT");
	// act->addDefaultInputMapping("q");
	engineKeyMap->addAction(act);

	act = new Common::Action(Common::kStandardActionMoveRight, _("Strafe right"));
	act->setCustomEngineActionEvent(kActionMoveRight);
	act->addDefaultInputMapping("RIGHT");
	act->addDefaultInputMapping("JOY_RIGHT");
	// act->addDefaultInputMapping("w");
	engineKeyMap->addAction(act);

	act = new Common::Action("SHOOT", _("Shoot"));
	act->setCustomEngineActionEvent(kActionShoot);
	act->addDefaultInputMapping("JOY_A");
	act->addDefaultInputMapping("KP5");
	act->addDefaultInputMapping("5");
	act->addDefaultInputMapping("KP0");
	act->addDefaultInputMapping("0");
	engineKeyMap->addAction(act);

	act = new Common::Action("ROTUP", _("Rotate up"));
	act->setCustomEngineActionEvent(kActionRotateUp);
	act->addDefaultInputMapping("p");
	engineKeyMap->addAction(act);

	act = new Common::Action("ROTDN", _("Rotate down"));
	act->setCustomEngineActionEvent(kActionRotateDown);
	act->addDefaultInputMapping("l");
	engineKeyMap->addAction(act);

	act = new Common::Action("TURNB", _("Turn back"));
	act->setCustomEngineActionEvent(kActionTurnBack);
	act->addDefaultInputMapping("u");
	engineKeyMap->addAction(act);

	act = new Common::Action("SKIP", _("Skip"));
	act->setCustomEngineActionEvent(kActionSkip);
	act->addDefaultInputMapping("SPACE");
	act->addDefaultInputMapping("RETURN");
	act->addDefaultInputMapping("JOY_X");
	engineKeyMap->addAction(act);

	// I18N: Toggles between cursor lock modes, switching between free cursor movement and camera/head movement.
	act = new Common::Action("SWITCH", _("Change mode"));
	act->setCustomEngineActionEvent(kActionChangeMode);
	act->addDefaultInputMapping("SPACE");
	act->addDefaultInputMapping("JOY_X");
	engineKeyMap->addAction(act);

	act = new Common::Action("ESCAPE", _("Escape"));
	act->setCustomEngineActionEvent(kActionEscape);
	act->addDefaultInputMapping("ESCAPE");
	engineKeyMap->addAction(act);

	act = new Common::Action("MENU", _("Info menu"));
	act->setCustomEngineActionEvent(kActionInfoMenu);
	act->addDefaultInputMapping("i");
	act->addDefaultInputMapping("JOY_GUIDE");
	engineKeyMap->addAction(act);
}

Math::AABB createPlayerAABB(Math::Vector3d const position, int playerHeight, float reductionHeight = 0.0f) {
	Math::Vector3d v1(position.x() + 1, position.y() - playerHeight * reductionHeight - 1, position.z() + 1);
	Math::Vector3d v2(position.x() - 1, position.y() - playerHeight, position.z() - 1);

	Math::AABB boundingBox(v1, v2);

	boundingBox.expand(v1);
	boundingBox.expand(v2);
	return boundingBox;
}

void FreescapeEngine::gotoArea(uint16 areaID, int entranceID) {
	error("Function \"%s\" not implemented", __FUNCTION__);
}

void FreescapeEngine::traverseEntrance(uint16 entranceID) {
	Entrance *entrance = (Entrance *)_currentArea->entranceWithID(entranceID);
	assert(entrance);

	int scale = _currentArea->getScale();
	assert(scale > 0);

	Math::Vector3d rotation = entrance->getRotation();
	_position = entrance->getOrigin();

	if (_position.x() < 0) {
		assert(isCastle());
		_position.x() = _lastPosition.x();
	}

	if (_position.y() < 0) {
		assert(isCastle());
		_position.y() = _lastPosition.y();
	}

	if (_position.z() < 0) {
		assert(isCastle());
		_position.z() = _lastPosition.z();
	}

	// TODO: verify if this is needed
	/*if (scale == 1) {
		_position.x() = _position.x() + 16;
		_position.z() = _position.z() + 16;
	} else if (scale == 5) {
		_position.x() = _position.x() + 4;
		_position.z() = _position.z() + 4;
	}*/

	if (rotation.x() >= 0 && rotation.y() >= 0 && rotation.z() >= 0) {
		_pitch = rotation.x();
		float y = rotation.y();

		// Adjust _yaw based on normalized angle
		if (y >= 0 && y < 90)
			_yaw = 90 - y;			// 0 to 90 maps to 90 to 0 (yaw should be 90 to 0)
		else if (y >= 90 && y <= 180)
			_yaw = 450 - y;			// 90 to 180 maps to 360 to 270 (yaw should be 360 to 270)
		else if (y > 180 && y <= 225)
			_yaw = y;				// 180 to 225 maps to 180 to 225 (yaw should be 180 to 225)
		else if (y > 225 && y < 270)
			_yaw = y - 90;			// 180 to 270 maps to 90 to 0 (yaw should be 90 to 0)
		else
			_yaw = 360 + 90 - y;	// 270 to 360 maps to 90 to 180 (yaw should be 90 to 180)

		_roll = rotation.z();
	}

	debugC(1, kFreescapeDebugMove, "entrace position: %f %f %f", _position.x(), _position.y(), _position.z());
	// Set the player height
	_playerHeight = 0;
	changePlayerHeight(_playerHeightNumber);
	debugC(1, kFreescapeDebugMove, "player height: %d", _playerHeight);

	_sensors = _currentArea->getSensors();
	_gfx->_scale = _currentArea->_scale;
}

void FreescapeEngine::activate() {
	Common::Point center(_viewArea.left + _viewArea.width() / 2, _viewArea.top + _viewArea.height() / 2);
	// Convert to normalized coordinates [-1, 1]
	float ndcX = (2.0f * (_crossairPosition.x - _viewArea.left) / _viewArea.width()) - 1.0f;
	float ndcY = 1.0f - (2.0f * (_crossairPosition.y - _viewArea.top) / _viewArea.height());

	// Calculate angular offsets using perspective projection
	float fovHorizontalRad = (float)(75.0f * M_PI / 180.0f);
	float aspectRatio = isCastle() ? 1.6 : 2.18;
	float fovVerticalRad = 2.0f * atan(tan(fovHorizontalRad / 2.0f) / aspectRatio);

	// Convert NDC to angle offset
	float angleOffsetX = atan(ndcX * tan(fovHorizontalRad / 2.0f)) * 180.0f / M_PI;
	float angleOffsetY = atan(ndcY * tan(fovVerticalRad / 2.0f)) * 180.0f / M_PI;

	Math::Vector3d direction = directionToVector(_pitch + angleOffsetY, _yaw - angleOffsetX, false);
	Math::Ray ray(_position, direction);
	Object *interacted = _currentArea->checkCollisionRay(ray, 1250.0 / _currentArea->getScale());
	if (interacted) {
		GeometricObject *gobj = (GeometricObject *)interacted;
		debugC(1, kFreescapeDebugMove, "Interact with object %d with flags %x", gobj->getObjectID(), gobj->getObjectFlags());

		if (!gobj->_conditionSource.empty())
			debugC(1, kFreescapeDebugMove, "Must use interact = true when executing: %s", gobj->_conditionSource.c_str());

		executeObjectConditions(gobj, false, false, true);
	} else {
		if (!_outOfReachMessage.empty()) {
			clearTemporalMessages();
			insertTemporaryMessage(_outOfReachMessage, _countdown - 2);
		}
	}
	//executeLocalGlobalConditions(true, false, false); // Only execute "on shot" room/global conditions
}


void FreescapeEngine::shoot() {
	if (_shootingFrames > 0) // No more than one shot at a time
		return;

	playSound(_soundIndexShoot, false, _movementSoundHandle);
	g_system->delayMillis(2);
	_shootingFrames = 10;

	// Convert to normalized coordinates [-1, 1]
	float ndcX = (2.0f * (_crossairPosition.x - _viewArea.left) / _viewArea.width()) - 1.0f;
	float ndcY = 1.0f - (2.0f * (_crossairPosition.y - _viewArea.top) / _viewArea.height());

	// Calculate angular offsets using perspective projection
	float fovHorizontalRad = (float)(75.0f * M_PI / 180.0f);
	float aspectRatio = isCastle() ? 1.6 : 2.18;
	float fovVerticalRad = 2.0f * atan(tan(fovHorizontalRad / 2.0f) / aspectRatio);

	// Convert NDC to angle offset
	float angleOffsetX = atan(ndcX * tan(fovHorizontalRad / 2.0f)) * 180.0f / M_PI;
	float angleOffsetY = atan(ndcY * tan(fovVerticalRad / 2.0f)) * 180.0f / M_PI;

	Math::Vector3d direction = directionToVector(_pitch + angleOffsetY, _yaw - angleOffsetX, false);
	Math::Ray ray(_position, direction);
	Object *shot = _currentArea->checkCollisionRay(ray, 8192);
	if (shot) {
		GeometricObject *gobj = (GeometricObject *)shot;
		debugC(1, kFreescapeDebugMove, "Shot object %d with flags %x", gobj->getObjectID(), gobj->getObjectFlags());

		if (!gobj->_conditionSource.empty())
			debugC(1, kFreescapeDebugMove, "Must use shot = true when executing: %s", gobj->_conditionSource.c_str());

		_delayedShootObject = gobj;
	}

	executeLocalGlobalConditions(true, false, false); // Only execute "on shot" room/global conditions
}

void FreescapeEngine::changeAngle(int offset, bool wrapAround) {
	_angleRotationIndex = _angleRotationIndex + offset;

	if (_angleRotationIndex < 0) {
		if (wrapAround)
			_angleRotationIndex = int(_angleRotations.size()) - 1;
		else
			_angleRotationIndex = 0;
	} else if (_angleRotationIndex >= int(_angleRotations.size())) {
		if (wrapAround)
			_angleRotationIndex = 0;
		else
			_angleRotationIndex = int(_angleRotations.size()) - 1;
	}

	_angleRotationIndex = _angleRotationIndex % int(_angleRotations.size());
}

void FreescapeEngine::changePlayerHeight(int index) {
	int scale = _currentArea->getScale();

	_position.setValue(1, _position.y() - _playerHeight);
	_playerHeight = 32 * (index + 1) - 16 / float(scale);
	assert(_playerHeight > 0);
	_position.setValue(1, _position.y() + _playerHeight);
}

void FreescapeEngine::changeStepSize() {
	_playerStepIndex++;
	_playerStepIndex = _playerStepIndex % int(_playerSteps.size());
}

void FreescapeEngine::increaseStepSize() {
	if (_playerStepIndex == int(_playerSteps.size()) - 1)
		return;

	_playerStepIndex++;
}

void FreescapeEngine::decreaseStepSize() {
	if (_playerStepIndex == 0)
		return;

	_playerStepIndex--;
}

bool FreescapeEngine::rise() {
	bool result = false;
	debugC(1, kFreescapeDebugMove, "playerHeightNumber: %d", _playerHeightNumber);
	int previousAreaID = _currentArea->getAreaID();
	if (_flyMode) {
		Math::Vector3d destination = _position;
		destination.y() = destination.y() + _playerSteps[_playerStepIndex];
		resolveCollisions(destination);
	} else {
		if (_playerHeightNumber >= _playerHeightMaxNumber)
			return result;

		_playerHeightNumber++;
		changePlayerHeight(_playerHeightNumber);

		Math::AABB boundingBox = createPlayerAABB(_position, _playerHeight);
		ObjectArray objs = _currentArea->checkCollisions(boundingBox);
		bool collided = objs.size() > 0;
		if (collided) {
			if (_currentArea->getAreaID() == previousAreaID) {
				_playerHeightNumber--;
				changePlayerHeight(_playerHeightNumber);
				if (!isCastle())
					setGameBit(31);
			}
		} else
			result = true;
	}
	checkIfStillInArea();
	_lastPosition = _position;
	debugC(1, kFreescapeDebugMove, "new player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
	executeMovementConditions();
	return result;
}

void FreescapeEngine::lower() {
	debugC(1, kFreescapeDebugMove, "playerHeightNumber: %d", _playerHeightNumber);
	if (_flyMode) {
		Math::Vector3d destination = _position;
		destination.y() = destination.y() - _playerSteps[_playerStepIndex];
		resolveCollisions(destination);
	} else {
		if (_playerHeightNumber == 0)
			return;

		_playerHeightNumber--;
		changePlayerHeight(_playerHeightNumber);
	}
	checkIfStillInArea();
	_lastPosition = _position;
	debugC(1, kFreescapeDebugMove, "new player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
	executeMovementConditions();
}

void FreescapeEngine::checkIfStillInArea() {
	int maxPositiveDistance = 8192;
	int maxNegativeDistance	= 0;

	if (_currentArea->isOutside()) {
		maxPositiveDistance = 16384;
		maxNegativeDistance = -16384;
	}

	for (int i = 0; i < 3; i++) {
		if (_position.getValue(i) < maxNegativeDistance)
			_position.setValue(i, maxNegativeDistance);
		else if (_position.getValue(i) > maxPositiveDistance)
			_position.setValue(i, maxPositiveDistance);
	}
	if (_position.y() >= 2016)
		_position.y() = _lastPosition.z();
}

void FreescapeEngine::updatePlayerMovement(float deltaTime) {
	if (_smoothMovement)
		updatePlayerMovementSmooth(deltaTime);
	else
		updatePlayerMovementClassic(deltaTime);
}

void FreescapeEngine::updatePlayerMovementClassic(float deltaTime) {
	if (!_moveForward && !_moveBackward && !_strafeLeft && !_strafeRight)
		return;

	debugC(1, kFreescapeDebugMove, "old player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
	int previousAreaID = _currentArea->getAreaID();

	Math::Vector3d stepFront;
	Math::Vector3d stepRight;

	if (_playerSteps[_playerStepIndex] > 2) {
		stepFront = _cameraFront * (float(_playerSteps[_playerStepIndex]) / 2 / _cameraFront.length());
		stepRight = _cameraRight * (float(_playerSteps[_playerStepIndex]) / 2 / _cameraRight.length());

		stepFront.x() = floor(stepFront.x()) + 0.5;
		stepFront.z() = floor(stepFront.z()) + 0.5;
	} else {
		stepFront = _cameraFront * (float(_playerSteps[_playerStepIndex]) / _cameraFront.length());
		stepRight = _cameraRight * (float(_playerSteps[_playerStepIndex]) / _cameraRight.length());

		stepFront.x() = ceil(stepFront.x());
		stepFront.z() = ceil(stepFront.z());
	}

	float positionY = _position.y();
	Math::Vector3d destination = _position;

	if (_moveForward)
		destination += stepFront;
	if (_moveBackward)
		destination -= stepFront;
	if (_strafeRight)
		destination -= stepRight;
	if (_strafeLeft)
		destination += stepRight;

	_moveForward = false;
	_moveBackward = false;
	_strafeLeft = false;
	_strafeRight = false;

	if (!_flyMode)
		destination.y() = positionY;
	resolveCollisions(destination);
	checkIfStillInArea();

	_lastPosition = _position;
	debugC(1, kFreescapeDebugMove, "new player position: %f, %f, %f", _position.x(), _position.y(), _position.z());
	//debugC(1, kFreescapeDebugMove, "player height: %f", _position.y() - areaScale * _playerHeight);
	if (_currentArea->getAreaID() == previousAreaID)
		executeMovementConditions();
	_gotoExecuted = false;
	if (!isCastle())
		clearGameBit(31);
}

void FreescapeEngine::updatePlayerMovementSmooth(float deltaTime) {
	if (_moveForward && !_eventManager->isActionActive(kActionMoveUp))
		_moveForward = false;

	if (_moveBackward && !_eventManager->isActionActive(kActionMoveDown))
		_moveBackward = false;

	if (_strafeLeft && !_eventManager->isActionActive(kActionMoveLeft))
		_strafeLeft = false;

	if (_strafeRight && !_eventManager->isActionActive(kActionMoveRight))
		_strafeRight = false;

	if (!_moveForward && !_moveBackward && !_strafeLeft && !_strafeRight)
		return;

	const float moveSpeed = _playerSteps[_playerStepIndex] * 5.0f;
	Math::Vector3d moveDir;

	if (_moveForward)
		moveDir += _cameraFront;
	else if (_moveBackward)
		moveDir -= _cameraFront;
	else if (_strafeLeft)
		moveDir += _cameraRight;
	else if (_strafeRight)
		moveDir -= _cameraRight;

	if (_flyMode) {
		if (_moveUp)
			moveDir.y() += 1.0f;
		if (_moveDown)
			moveDir.y() -= 1.0f;
	}

	moveDir.normalize();
	moveDir = moveDir * moveSpeed * deltaTime;
	if (moveDir.length() > 1.0f) {
		Math::Vector3d destination = _position + moveDir;
		resolveCollisions(destination);
		checkIfStillInArea();
		_lastPosition = _position;
		executeMovementConditions();
	}
	_gotoExecuted = false;
	if (!isCastle())
		clearGameBit(31);
}

void FreescapeEngine::resolveCollisions(Math::Vector3d const position) {
	if (_noClipMode) {
		_position = position;
		return;
	}

	Math::Vector3d newPosition = position;
	Math::Vector3d lastPosition = _lastPosition;

	_gotoExecuted = false;
	bool executed = runCollisionConditions(lastPosition, newPosition);

	if (_gotoExecuted) {
		_gotoExecuted = false;
		return;
	}

	newPosition = _currentArea->resolveCollisions(lastPosition, newPosition, _playerHeight);

	if (_flyMode) {
		if ((lastPosition - newPosition).length() < 1) { // Something is blocking the player
			if (!executed && !isCastle())
				setGameBit(31);
			playSound(_soundIndexCollide, false, _movementSoundHandle);
		}
		_position = newPosition;
		return;
	}

	bool isSteppingUp = false;
	bool isSteppingDown = false;
	bool isCollidingWithWall = false;

	// If the player has not moved, try to step up
	if ((lastPosition - newPosition).length() < 1) { // If the player has not moved
		// Try to step up
		newPosition = position;
		newPosition.y() = newPosition.y() + _stepUpDistance;

		lastPosition = _lastPosition;
		lastPosition.y() = lastPosition.y() + _stepUpDistance;

		newPosition = _currentArea->resolveCollisions(lastPosition, newPosition, _playerHeight);
		if (_lastPosition.y() < newPosition.y())
			isSteppingUp = true;

		if (!executed && !isCastle())
			setGameBit(31);

		isCollidingWithWall = true;
	}

	// Check for falling
	Math::Vector3d fallStart = newPosition;   // current standing point
	Math::Vector3d fallEnd   = fallStart;     // copy for downward probe
	fallEnd.y() = -8192;                      // probe way down below
	newPosition = _currentArea->resolveCollisions(fallStart, fallEnd, _playerHeight);
	int fallen = _lastPosition.y() - newPosition.y();

	if (fallen > _maxFallingDistance) {
		_hasFallen = !_disableFalling;
		if (isDriller() || isDark()) {
			_roll = -90;
			_pitch = 0;
			_yaw = 0;
			changePlayerHeight(0);
		}
		_avoidRenderingFrames = 60 * 3;
		_endGameDelayTicks = 60 * 5;
		if (isEclipse()) // No need for an variable index, since these are special types of sound
			playSoundFx(0, true);
		else
			playSound(_soundIndexFall, false, _movementSoundHandle);

		if (_hasFallen)
			stopMovement();
	}

	if (!_hasFallen && fallen > 0) {
		isSteppingDown = true;
		// Position in Y was changed, let's re-run effects
		runCollisionConditions(_lastPosition, newPosition);
	}

	if (isSteppingUp && (newPosition - _lastPosition).length() <= 1) {
		isCollidingWithWall = true;
		isSteppingUp = false;
	}

	if (isSteppingDown && (newPosition - _lastPosition).length() <= 1) {
		isCollidingWithWall = true;
		isSteppingDown = false;
	}

	if (isSteppingUp)  {
		//debug("Stepping up sound!");
		if (!_mixer->isSoundHandleActive(_movementSoundHandle))
			playSound(_soundIndexStepUp, false, _movementSoundHandle);
	} else if (isSteppingDown) {
		//debug("Stepping down sound!");
		if (!_mixer->isSoundHandleActive(_movementSoundHandle))
			playSound(_soundIndexStepDown, false, _movementSoundHandle);
	} else if (isCollidingWithWall) {
		//debug("Colliding with wall sound!");
		if (!_mixer->isSoundHandleActive(_movementSoundHandle))
			playSound(_soundIndexCollide, false, _movementSoundHandle);
	}

	_position = newPosition;
}

void FreescapeEngine::stopMovement() {
	_moveForward = false;
	_moveBackward = false;
	_strafeLeft = false;
	_strafeRight = false;
	_moveUp = false;
	_moveDown = false;
	_eventManager->purgeKeyboardEvents();
}

bool FreescapeEngine::runCollisionConditions(Math::Vector3d const lastPosition, Math::Vector3d const newPosition) {
	bool floorExecuted = false;
	bool wallExecuted = false;

	GeometricObject *gobj = nullptr;
	Object *collided = nullptr;
	_gotoExecuted = false;

	_speaker->stop();

	Math::Ray ray(newPosition, -_upVector);
	collided = _currentArea->checkCollisionRay(ray, _playerHeight + 3);
	if (collided) {
		gobj = (GeometricObject *)collided;
		debugC(1, kFreescapeDebugMove, "Collided down with object id %d of size %f %f %f", gobj->getObjectID(), gobj->getSize().x(), gobj->getSize().y(), gobj->getSize().z());
		floorExecuted |= executeObjectConditions(gobj, false, true, false);
	}

	if (_gotoExecuted) {
		executeMovementConditions();
		return true;
	}

	Math::Vector3d direction = newPosition - lastPosition;
	direction.normalize();
	int rayLenght = 45;
	if (_currentArea->getScale() == 16)
		rayLenght = 20;
	else if (_currentArea->getScale() >= 5)
		rayLenght = MAX(5, 45 / (2 * _currentArea->getScale()));

	_gotoExecuted = false;
	for (int i = 0; i <= 4; i++) {
		Math::Vector3d rayPosition = lastPosition;
		rayPosition.y() = rayPosition.y() - _playerHeight * (i / 4.0);
		ray = Math::Ray(rayPosition, direction);
		collided = _currentArea->checkCollisionRay(ray, rayLenght);
		if (collided) {
			gobj = (GeometricObject *)collided;
			debugC(1, kFreescapeDebugMove, "Collided with object id %d of size %f %f %f", gobj->getObjectID(), gobj->getSize().x(), gobj->getSize().y(), gobj->getSize().z());
			wallExecuted |= executeObjectConditions(gobj, false, true, false);
		}
		if (_gotoExecuted) {
			executeMovementConditions();
			return true;
		}
		if (wallExecuted)
			break;
	}

	return floorExecuted || wallExecuted;
}

} // namespace Freescape
