scummvm/engines/bladerunner/actor_walk.cpp
Peter Kohaut 1740c490d7 BLADERUNNER: improvements to the walking code
path finding is still missing
video player has audio preloading
small fixes
2017-04-01 08:57:57 +02:00

376 lines
10 KiB
C++

/* 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 "bladerunner/actor_walk.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/actor.h"
#include "bladerunner/gameinfo.h"
#include "bladerunner/obstacles.h"
#include "bladerunner/scene.h"
#include "bladerunner/scene_objects.h"
#include "bladerunner/set.h"
namespace BladeRunner {
ActorWalk::ActorWalk(BladeRunnerEngine *vm) {
_vm = vm;
_walking = 0;
_running = 0;
_facing = -1;
_status = 0;
_nearActors.clear();
}
ActorWalk::~ActorWalk() {}
bool ActorWalk::setup(int actorId, bool run, const Vector3 &from, const Vector3 &to, bool unk1, bool *arrived) {
Vector3 next;
*arrived = false;
int r = nextOnPath(actorId, from, to, next);
if (r == 0) {
if (actorId != 0) {
_current = from;
_destination = to;
stop(actorId, false, kAnimationModeCombatIdle, kAnimationModeIdle);
} else {
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
}
return false;
}
if (r == -1) {
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
*arrived = true;
return false;
}
_nearActors.clear();
_vm->_sceneObjects->setMoving(actorId + SCENE_OBJECTS_ACTORS_OFFSET, true);
_vm->_actors[actorId]->setMoving(true);
if (_running) {
run = true;
}
int animationMode;
if (_vm->_actors[actorId]->inCombat()) {
animationMode = run ? kAnimationModeCombatRun : kAnimationModeCombatWalk;
} else {
animationMode = run ? kAnimationModeRun : kAnimationModeWalk;
}
_vm->_actors[actorId]->changeAnimationMode(animationMode);
_destination = to;
_originalDestination = to;
_current = from;
_next = next;
if (next.x == _current.x && next.z == _current.z) {
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
*arrived = true;
return false;
}
_facing = angle_1024(_current, next);
_walking = true;
_running = run;
_status = 2;
return true;
}
bool ActorWalk::tick(int actorId, float stepDistance, bool inWalkLoop) {
bool walkboxFound;
if (_status == 5) {
if (inWalkLoop) {
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
return true;
}
if (actorId != 0 && _vm->_rnd.getRandomNumberRng(1, 15) != 1) { // why random?
return false;
}
_status = 3;
}
bool nearActorExists = addNearActors(actorId);
if (_nearActors.size() > 0) {
nearActorExists = true;
if (_vm->_sceneObjects->existsOnXZ(actorId + SCENE_OBJECTS_ACTORS_OFFSET, _destination.x, _destination.z, true, true)) {
if (actorId > 0) {
if (_vm->_actors[actorId]->inWalkLoop()) {
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
_nearActors.clear();
return true;
} else {
Vector3 newDestination;
findNearestEmptyPositionToOriginalDestination(actorId, newDestination);
_destination = newDestination;
return false;
}
} else {
if (_vm->_playerActor->inWalkLoop()) {
_destination = _current;
}
stop(0, true, kAnimationModeCombatIdle, kAnimationModeIdle);
_nearActors.clear();
return true;
}
}
}
_status = 3;
if (stepDistance > distance(_current, _destination)) {
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
_current = _destination;
_current.y = _vm->_scene->_set->getAltitudeAtXZ(_current.x, _current.z, &walkboxFound);
return true;
}
float distanceToNext = distance(_current, _next);
if (1.0f < distanceToNext) {
_facing = angle_1024(_current, _next);
}
bool nextIsCloseEnough = stepDistance > distanceToNext;
if (nextIsCloseEnough || nearActorExists || _status == 3) {
if (nextIsCloseEnough) {
_current = _next;
}
_status = 1;
Vector3 next;
obstaclesAddNearActors(actorId);
int r = nextOnPath(actorId, _current, _destination, next);
obstaclesRestore();
if (r == 0) {
stop(actorId, actorId == 0, kAnimationModeCombatIdle, kAnimationModeIdle);
return false;
}
if (r != -1) {
_next = next;
_facing = angle_1024(_current, _next);
_status = 2;
int animationMode;
if (_vm->_actors[actorId]->inCombat()) {
animationMode = _running ? kAnimationModeCombatRun : kAnimationModeCombatWalk;
} else {
animationMode = _running ? kAnimationModeRun : kAnimationModeWalk;
}
_vm->_actors[actorId]->changeAnimationMode(animationMode);
if (nextIsCloseEnough) {
return false;
}
}
}
float angle_rad = _facing / 512.0 * M_PI;
_current.x += stepDistance * sinf(angle_rad);
_current.z -= stepDistance * cosf(angle_rad);
_current.y = _vm->_scene->_set->getAltitudeAtXZ(_current.x, _current.z, &walkboxFound);
return false;
}
void ActorWalk::getCurrentPosition(int actorId, Vector3 *pos, int *facing) const {
*pos = _current;
*facing = _facing;
}
void ActorWalk::stop(int actorId, bool immediately, int combatAnimationMode, int animationMode) {
_vm->_sceneObjects->setMoving(actorId + SCENE_OBJECTS_ACTORS_OFFSET, false);
_vm->_actors[actorId]->setMoving(false);
if (_vm->_actors[actorId]->inCombat()) {
_vm->_actors[actorId]->changeAnimationMode(combatAnimationMode, false);
} else {
_vm->_actors[actorId]->changeAnimationMode(animationMode, false);
}
if (immediately) {
_walking = false;
_running = false;
_status = 0;
} else {
_walking = true;
_running = false;
_status = 5;
}
}
bool ActorWalk::isXYZEmpty(float x, float y, float z, int actorId) const {
if (_vm->_scene->_set->findWalkbox(x, z) == -1) {
return true;
}
if (_vm->_actors[actorId]->isImmuneToObstacles()) {
return false;
}
return _vm->_sceneObjects->existsOnXZ(actorId + SCENE_OBJECTS_ACTORS_OFFSET, x, z, false, false);
}
bool ActorWalk::findNearestEmptyPosition(int actorId, const Vector3 &destination, int dist, Vector3 &out) const {
bool inWalkbox;
int facingToMinDistance = -1;
float minDistance = -1.0f;
float x = 0.0f;
float z = 0.0f;
out.x = 0.0f;
out.y = 0.0f;
out.z = 0.0f;
for (int facing = 0; facing < 1024; facing += 128) {
x = destination.x + sin_1024(facing) * dist;
z = destination.z - cos_1024(facing) * dist;
float distanceBetweenActorAndDestination = distance(x, z, _vm->_actors[actorId]->getX(), _vm->_actors[actorId]->getZ());
if (minDistance == -1.0f || minDistance > distanceBetweenActorAndDestination) {
minDistance = distanceBetweenActorAndDestination;
facingToMinDistance = facing;
}
}
int facingLeft = facingToMinDistance;
int facingRight = facingToMinDistance;
int facing = -1024;
while (facing < 0) {
x = destination.x + sin_1024(facingRight) * dist;
z = destination.z - cos_1024(facingRight) * dist;
if (!_vm->_sceneObjects->existsOnXZ(actorId + SCENE_OBJECTS_ACTORS_OFFSET, x, z, true, true) && _vm->_scene->_set->findWalkbox(x, z) >= 0) {
break;
}
x = destination.x + sin_1024(facingLeft) * dist;
z = destination.z - cos_1024(facingLeft) * dist;
if (!_vm->_sceneObjects->existsOnXZ(actorId + SCENE_OBJECTS_ACTORS_OFFSET, x, z, true, true) && _vm->_scene->_set->findWalkbox(x, z) >= 0) {
break;
}
facingRight -= 64;
if (facingRight < 0) {
facingRight += 1024;
}
facingLeft += 64;
if (facingLeft >= 1024) {
facingLeft -= 1024;
}
facing += 64;
}
float y = _vm->_scene->_set->getAltitudeAtXZ(x, z, &inWalkbox);
if (inWalkbox) {
out.x = x;
out.y = y;
out.z = z;
return true;
}
return false;
}
bool ActorWalk::findNearestEmptyPositionToOriginalDestination(int actorId, Vector3 &out) const {
return findNearestEmptyPosition(actorId, _originalDestination, 30, out);
}
bool ActorWalk::addNearActors(int skipActorId) {
bool added = false;
int setId = _vm->_scene->getSetId();
for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); i++) {
// TODO: remove null check after implemetantion of all actors
if (_vm->_actors[i] != nullptr
&& _vm->_actors[skipActorId] != nullptr
&& _vm->_actors[i]->getSetId() == setId
&& i != skipActorId) {
if (_nearActors.contains(i)) {
_nearActors.setVal(i, false);
} else if (_vm->_actors[skipActorId]->distanceFromActor(i) <= 48.0f) {
_nearActors.setVal(i, true);
added = true;
}
}
}
return added;
}
void ActorWalk::obstaclesAddNearActors(int actorId) const {
Vector3 position = _vm->_actors[actorId]->getPosition();
for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) {
Actor *otherActor = _vm->_actors[it->_key];
// TODO: remove null check after implemetantion of all actors
if (otherActor == nullptr || otherActor->isRetired()) {
continue;
}
Vector3 otherPosition = otherActor->getPosition();
float x0 = otherPosition.x - 12.0f;
float z0 = otherPosition.z - 12.0f;
float x1 = otherPosition.x + 12.0f;
float z1 = otherPosition.z + 12.0f;
if (position.x < (x0 - 12.0f) || position.z < (z0 - 12.0f) || position.x > (x1 + 12.0f) || position.z > (z1 + 12.0f)) {
_vm->_obstacles->add(x0, z0, x1, z1);
}
}
}
void ActorWalk::obstaclesRestore() const {
_vm->_obstacles->restore();
}
int ActorWalk::nextOnPath(int actorId, const Vector3 &from, const Vector3 &to, Vector3 &next) const {
next = from;
if (distance(from, to) < 6.0) {
return -1;
}
if (_vm->_actors[actorId]->isImmuneToObstacles()) {
next = to;
return 1;
}
if (_vm->_scene->_set->findWalkbox(to.x, to.z) == -1) {
return 0;
}
if (_vm->_sceneObjects->existsOnXZ(actorId + SCENE_OBJECTS_ACTORS_OFFSET, to.x, to.z, false, false)) {
return 0;
}
Vector3 next1;
if (_vm->_obstacles->find(from, to, &next1)) {
next = next1;
return 1;
}
return 0;
}
} // End of namespace BladeRunner