mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 02:12:14 +00:00
cd076b26ae
In case the compiler won't optimize such cases
467 lines
13 KiB
C++
467 lines
13 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/game_constants.h"
|
|
#include "bladerunner/game_info.h"
|
|
#include "bladerunner/obstacles.h"
|
|
#include "bladerunner/savefile.h"
|
|
#include "bladerunner/scene.h"
|
|
#include "bladerunner/scene_objects.h"
|
|
#include "bladerunner/set.h"
|
|
|
|
namespace BladeRunner {
|
|
|
|
ActorWalk::ActorWalk(BladeRunnerEngine *vm) {
|
|
_vm = vm;
|
|
|
|
reset();
|
|
}
|
|
|
|
ActorWalk::~ActorWalk() {}
|
|
|
|
// added method for bug fix (bad new game state for player actor) and better management of object
|
|
void ActorWalk::reset() {
|
|
_walking = false;
|
|
_running = false;
|
|
_facing = -1;
|
|
_status = 0;
|
|
|
|
_destination = Vector3(0.0f, 0.0f, 0.0f);
|
|
_originalDestination = Vector3(0.0f, 0.0f, 0.0f);
|
|
_current = Vector3(0.0f, 0.0f, 0.0f);
|
|
_next = Vector3(0.0f, 0.0f, 0.0f);
|
|
|
|
_nearActors.clear();
|
|
}
|
|
|
|
bool ActorWalk::setup(int actorId, bool runFlag, const Vector3 &from, const Vector3 &to, bool mustReach, 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);
|
|
}
|
|
// debug("actor id: %d, arrived: %d - false setup 01", actorId, (*arrived)? 1:0);
|
|
return false;
|
|
}
|
|
|
|
if (r == -1) {
|
|
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
|
|
*arrived = true;
|
|
// debug("actor id: %d, arrived: %d - false setup 02", actorId, (*arrived)? 1:0);
|
|
return false;
|
|
}
|
|
|
|
_nearActors.clear();
|
|
_vm->_sceneObjects->setMoving(actorId + kSceneObjectOffsetActors, true);
|
|
_vm->_actors[actorId]->setMoving(true);
|
|
|
|
if (_running) {
|
|
runFlag = true;
|
|
}
|
|
|
|
int animationMode;
|
|
if (_vm->_actors[actorId]->inCombat()) {
|
|
animationMode = runFlag ? kAnimationModeCombatRun : kAnimationModeCombatWalk;
|
|
} else {
|
|
animationMode = runFlag ? 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;
|
|
// debug("actor id: %d, arrived: %d - false setup 03", actorId, (*arrived)? 1:0);
|
|
return false;
|
|
}
|
|
|
|
_facing = angle_1024(_current, next);
|
|
_walking = true;
|
|
_running = runFlag;
|
|
_status = 2;
|
|
|
|
// debug("actor id: %d, arrived: %d - true setup 01", actorId, (*arrived)? 1:0);
|
|
return true;
|
|
}
|
|
|
|
bool ActorWalk::tick(int actorId, float stepDistance, bool mustReachWalkDestination) {
|
|
bool walkboxFound;
|
|
|
|
if (_status == 5) {
|
|
if (mustReachWalkDestination) {
|
|
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 + kSceneObjectOffsetActors, _destination.x, _destination.z, true, true)) {
|
|
if (actorId > 0) {
|
|
if (_vm->_actors[actorId]->mustReachWalkDestination()) {
|
|
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle);
|
|
_nearActors.clear();
|
|
return true;
|
|
} else {
|
|
Vector3 newDestination;
|
|
findEmptyPositionAroundToOriginalDestination(actorId, newDestination);
|
|
_destination = newDestination;
|
|
return false;
|
|
}
|
|
} else {
|
|
if (_vm->_playerActor->mustReachWalkDestination()) {
|
|
_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 == kActorMcCoy, 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;
|
|
}
|
|
} else {
|
|
stop(actorId, true, kAnimationModeCombatIdle, kAnimationModeIdle); // too close
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#if !BLADERUNNER_ORIGINAL_BUGS
|
|
// safety-guard / validator check
|
|
if (_facing >= 1024) {
|
|
_facing = (_facing % 1024);
|
|
} else if (_facing < 0) {
|
|
_facing = (-1) * _facing;
|
|
_facing = (_facing % 1024);
|
|
if (_facing > 0) {
|
|
_facing = 1024 - _facing; // this will always be in [1, 1023]
|
|
}
|
|
}
|
|
#endif
|
|
_current.x += stepDistance * _vm->_sinTable1024->at(_facing);
|
|
_current.z -= stepDistance * _vm->_cosTable1024->at(_facing);
|
|
_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 + kSceneObjectOffsetActors, 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;
|
|
}
|
|
}
|
|
|
|
void ActorWalk::run(int actorId) {
|
|
_running = true;
|
|
|
|
int animationMode = kAnimationModeRun;
|
|
if (_vm->_actors[actorId]->inCombat()) {
|
|
animationMode = kAnimationModeCombatRun;
|
|
}
|
|
_vm->_actors[actorId]->changeAnimationMode(animationMode, false);
|
|
}
|
|
|
|
void ActorWalk::save(SaveFileWriteStream &f) {
|
|
f.writeInt(_walking);
|
|
f.writeInt(_running);
|
|
f.writeVector3(_destination);
|
|
// _originalDestination is not saved
|
|
f.writeVector3(_current);
|
|
f.writeVector3(_next);
|
|
f.writeInt(_facing);
|
|
|
|
assert(_nearActors.size() <= 20);
|
|
for (Common::HashMap<int, bool>::const_iterator it = _nearActors.begin(); it != _nearActors.end(); ++it) {
|
|
f.writeInt(it->_key);
|
|
f.writeBool(it->_value);
|
|
}
|
|
f.padBytes(8 * (20 - _nearActors.size()));
|
|
f.writeInt(_nearActors.size());
|
|
|
|
f.writeInt(0); // _notUsed
|
|
f.writeInt(_status);
|
|
}
|
|
|
|
void ActorWalk::load(SaveFileReadStream &f) {
|
|
_walking = f.readInt();
|
|
_running = f.readInt();
|
|
_destination = f.readVector3();
|
|
// _originalDestination is not saved
|
|
_current = f.readVector3();
|
|
_next = f.readVector3();
|
|
_facing = f.readInt();
|
|
|
|
int actorId[20];
|
|
bool isNear[20];
|
|
|
|
for (int i = 0; i < 20; ++i) {
|
|
actorId[i] = f.readInt();
|
|
isNear[i] = f.readBool();
|
|
}
|
|
|
|
int count = f.readInt();
|
|
for (int i = 0; i < count; ++i) {
|
|
_nearActors.setVal(actorId[i], isNear[i]);
|
|
}
|
|
|
|
f.skip(4); // _notUsed
|
|
_status = f.readInt();
|
|
}
|
|
|
|
bool ActorWalk::isXYZOccupied(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 + kSceneObjectOffsetActors, x, z, false, false);
|
|
}
|
|
|
|
bool ActorWalk::findEmptyPositionAround(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 + _vm->_sinTable1024->at(facing) * dist;
|
|
z = destination.z - _vm->_cosTable1024->at(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 + _vm->_sinTable1024->at(facingRight) * dist;
|
|
z = destination.z - _vm->_cosTable1024->at(facingRight) * dist;
|
|
|
|
if (!_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, x, z, true, true) && _vm->_scene->_set->findWalkbox(x, z) >= 0) {
|
|
break;
|
|
}
|
|
|
|
x = destination.x + _vm->_sinTable1024->at(facingLeft) * dist;
|
|
z = destination.z - _vm->_cosTable1024->at(facingLeft) * dist;
|
|
|
|
if (!_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, 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::findEmptyPositionAroundToOriginalDestination(int actorId, Vector3 &out) const {
|
|
return findEmptyPositionAround(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) {
|
|
assert(_vm->_actors[i] != nullptr);
|
|
|
|
if (_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];
|
|
assert(otherActor != nullptr);
|
|
|
|
if ( 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) {
|
|
// debug("Id: %d Distance: %f::Result -1", actorId, distance(from, to));
|
|
return -1;
|
|
}
|
|
|
|
if (_vm->_actors[actorId]->isImmuneToObstacles()) {
|
|
next = to;
|
|
return 1;
|
|
}
|
|
if (_vm->_scene->_set->findWalkbox(to.x, to.z) == -1) {
|
|
// debug("Id: %d No walkbox::Result 0", actorId);
|
|
return 0;
|
|
}
|
|
if (_vm->_sceneObjects->existsOnXZ(actorId + kSceneObjectOffsetActors, to.x, to.z, false, false)) {
|
|
// debug("Actor Id: %d existsOnXZ::Result 0", actorId);
|
|
return 0;
|
|
}
|
|
Vector3 next1;
|
|
if (_vm->_obstacles->findNextWaypoint(from, to, &next1)) {
|
|
next = next1;
|
|
return 1;
|
|
}
|
|
// debug("Id: %d DEFAULTED::Result 0", actorId);
|
|
return 0;
|
|
}
|
|
|
|
} // End of namespace BladeRunner
|