Initial commit
This commit is contained in:
159
engines/stark/movement/followpath.cpp
Normal file
159
engines/stark/movement/followpath.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/* 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 "engines/stark/movement/followpath.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/path.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
FollowPath::FollowPath(Resources::ItemVisual *item) :
|
||||
Movement(item),
|
||||
_path(nullptr),
|
||||
_speed(0.0),
|
||||
_position(0.0),
|
||||
_previouslyEnabled(true),
|
||||
_anim(nullptr) {
|
||||
}
|
||||
|
||||
FollowPath::~FollowPath() {
|
||||
}
|
||||
|
||||
void FollowPath::start() {
|
||||
Movement::start();
|
||||
|
||||
_previouslyEnabled = _item->isEnabled();
|
||||
_item->setEnabled(true);
|
||||
|
||||
updateItemPosition(0, 0);
|
||||
changeItemAnim();
|
||||
}
|
||||
|
||||
void FollowPath::stop(bool force) {
|
||||
Movement::stop(force);
|
||||
|
||||
changeItemAnim();
|
||||
_item->setEnabled(_previouslyEnabled);
|
||||
}
|
||||
|
||||
void FollowPath::onGameLoop() {
|
||||
// Compute the new position on the path
|
||||
_position += _speed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
// Find the current path edge, and position on the path edge
|
||||
uint currentEdge = 0;
|
||||
float positionInEdge = _position;
|
||||
for (uint i = 0; i < _path->getEdgeCount(); i++) {
|
||||
float edgeLength = _path->getWeightedEdgeLength(i);
|
||||
if (positionInEdge < edgeLength) {
|
||||
break; // Found the current path edge
|
||||
}
|
||||
|
||||
positionInEdge -= edgeLength;
|
||||
currentEdge++;
|
||||
}
|
||||
|
||||
// Check if we went beyond the path's end
|
||||
if (currentEdge >= _path->getEdgeCount()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
updateItemPosition(currentEdge, positionInEdge);
|
||||
}
|
||||
|
||||
void FollowPath::updateItemPosition(uint currentEdge, float positionInEdge) const {// Get the new position for the item
|
||||
Math::Vector3d newPosition = _path->getWeightedPositionInEdge(currentEdge, positionInEdge);
|
||||
|
||||
// Update the item's properties in the scene
|
||||
if (is3D()) {
|
||||
Resources::FloorPositionedItem *item3D = Resources::Object::cast<Resources::FloorPositionedItem>(_item);
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
int32 floorFaceIndex = floor->findFaceContainingPoint(newPosition);
|
||||
if (floorFaceIndex >= 0) {
|
||||
item3D->setFloorFaceIndex(floorFaceIndex);
|
||||
} else {
|
||||
item3D->overrideSortKey(_path->getSortKey());
|
||||
}
|
||||
|
||||
item3D->setPosition3D(newPosition);
|
||||
|
||||
Math::Vector3d direction = _path->getEdgeDirection(currentEdge);
|
||||
item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0)));
|
||||
} else {
|
||||
Common::Point position2D = Common::Point(newPosition.x(), newPosition.y());
|
||||
_item->setPosition2D(position2D);
|
||||
}
|
||||
}
|
||||
|
||||
void FollowPath::changeItemAnim() {
|
||||
if (_ended) {
|
||||
if (_anim) {
|
||||
_item->resetActionAnim();
|
||||
} else {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
}
|
||||
} else {
|
||||
if (_anim) {
|
||||
_item->playActionAnim(_anim);
|
||||
} else {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityWalk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FollowPath::setPath(Resources::Path *path) {
|
||||
_path = path;
|
||||
}
|
||||
|
||||
void FollowPath::setSpeed(float speed) {
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
bool FollowPath::is3D() const {
|
||||
return _path->getSubType() == Resources::Path::kPath3D;
|
||||
}
|
||||
|
||||
void FollowPath::setAnim(Resources::Anim *anim) {
|
||||
_anim = anim;
|
||||
}
|
||||
|
||||
uint32 FollowPath::getType() const {
|
||||
return kTypeFollowPath;
|
||||
}
|
||||
|
||||
void FollowPath::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsResourceReference(&_path);
|
||||
serializer->syncAsResourceReference(&_anim);
|
||||
serializer->syncAsFloat(_position);
|
||||
serializer->syncAsFloat(_speed);
|
||||
serializer->syncAsUint32LE(_previouslyEnabled);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
76
engines/stark/movement/followpath.h
Normal file
76
engines/stark/movement/followpath.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_FOLLOW_PATH_H
|
||||
#define STARK_MOVEMENT_FOLLOW_PATH_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class Anim;
|
||||
class Path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item follow pre-computed path
|
||||
*
|
||||
* Works for 2D and 3D items, with respectively 2D and 3D paths
|
||||
*/
|
||||
class FollowPath : public Movement {
|
||||
public:
|
||||
FollowPath(Resources::ItemVisual *item);
|
||||
virtual ~FollowPath();
|
||||
|
||||
// Movement API
|
||||
void start() override;
|
||||
void onGameLoop() override;
|
||||
void stop(bool force = false) override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the path to follow */
|
||||
void setPath(Resources::Path *path);
|
||||
|
||||
/** Set the movement speed on the path */
|
||||
void setSpeed(float speed);
|
||||
|
||||
/** Override the animation to play while the item follows the path */
|
||||
void setAnim(Resources::Anim *anim);
|
||||
|
||||
private:
|
||||
void changeItemAnim();
|
||||
void updateItemPosition(uint currentEdge, float positionInEdge) const;
|
||||
bool is3D() const;
|
||||
|
||||
Resources::Path *_path;
|
||||
float _speed;
|
||||
|
||||
float _position;
|
||||
bool _previouslyEnabled;
|
||||
|
||||
Resources::Anim *_anim;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_FOLLOW_PATH_H
|
||||
116
engines/stark/movement/followpathlight.cpp
Normal file
116
engines/stark/movement/followpathlight.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/* 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 "engines/stark/movement/followpathlight.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/light.h"
|
||||
#include "engines/stark/resources/path.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
FollowPathLight::FollowPathLight(Resources::ItemVisual *item) :
|
||||
Movement(item),
|
||||
_light(nullptr),
|
||||
_path(nullptr),
|
||||
_speed(0.0),
|
||||
_position(0.0),
|
||||
_previouslyEnabled(true) {
|
||||
}
|
||||
|
||||
FollowPathLight::~FollowPathLight() {
|
||||
}
|
||||
|
||||
void FollowPathLight::start() {
|
||||
Movement::start();
|
||||
|
||||
_previouslyEnabled = _item->isEnabled();
|
||||
_item->setEnabled(true);
|
||||
|
||||
Math::Vector3d newPosition = _path->getWeightedPositionInEdge(0, 0);
|
||||
_light->setPosition(newPosition);
|
||||
}
|
||||
|
||||
void FollowPathLight::stop(bool force) {
|
||||
Movement::stop(force);
|
||||
|
||||
_item->setEnabled(_previouslyEnabled);
|
||||
}
|
||||
|
||||
void FollowPathLight::onGameLoop() {
|
||||
// Compute the new position on the path
|
||||
_position += _speed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
|
||||
// Find the current path edge, and position on the path edge
|
||||
uint currentEdge = 0;
|
||||
float positionInEdge = _position;
|
||||
for (uint i = 0; i < _path->getEdgeCount(); i++) {
|
||||
float edgeLength = _path->getWeightedEdgeLength(i);
|
||||
if (positionInEdge < edgeLength) {
|
||||
break; // Found the current path edge
|
||||
}
|
||||
|
||||
positionInEdge -= edgeLength;
|
||||
currentEdge++;
|
||||
}
|
||||
|
||||
// Check if we went beyond the path's end
|
||||
if (currentEdge >= _path->getEdgeCount()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new position for the light
|
||||
Math::Vector3d newPosition = _path->getWeightedPositionInEdge(currentEdge, positionInEdge);
|
||||
_light->setPosition(newPosition);
|
||||
}
|
||||
|
||||
void FollowPathLight::setPath(Resources::Path *path) {
|
||||
_path = path;
|
||||
}
|
||||
|
||||
void FollowPathLight::setSpeed(float speed) {
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
void FollowPathLight::setLight(Resources::Light *light) {
|
||||
_light = light;
|
||||
}
|
||||
|
||||
uint32 FollowPathLight::getType() const {
|
||||
return kTypeFollowPathLight;
|
||||
}
|
||||
|
||||
void FollowPathLight::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsResourceReference(&_path);
|
||||
serializer->syncAsResourceReference(&_light);
|
||||
serializer->syncAsFloat(_position);
|
||||
serializer->syncAsFloat(_speed);
|
||||
serializer->syncAsUint32LE(_previouslyEnabled);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
69
engines/stark/movement/followpathlight.h
Normal file
69
engines/stark/movement/followpathlight.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_FOLLOW_PATH_LIGHT_H
|
||||
#define STARK_MOVEMENT_FOLLOW_PATH_LIGHT_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class Light;
|
||||
class Path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a light follow pre-computed path
|
||||
*/
|
||||
class FollowPathLight : public Movement {
|
||||
public:
|
||||
FollowPathLight(Resources::ItemVisual *item);
|
||||
virtual ~FollowPathLight();
|
||||
|
||||
// Movement API
|
||||
void start() override;
|
||||
void onGameLoop() override;
|
||||
void stop(bool force = false) override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the path to follow */
|
||||
void setPath(Resources::Path *path);
|
||||
|
||||
/** Set the light to move */
|
||||
void setLight(Resources::Light *light);
|
||||
|
||||
/** Set the movement speed on the path */
|
||||
void setSpeed(float speed);
|
||||
|
||||
private:
|
||||
Resources::Path *_path;
|
||||
Resources::Light *_light;
|
||||
float _speed;
|
||||
|
||||
float _position;
|
||||
bool _previouslyEnabled;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_FOLLOW_PATH_LIGHT_H
|
||||
90
engines/stark/movement/movement.cpp
Normal file
90
engines/stark/movement/movement.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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 "engines/stark/movement/movement.h"
|
||||
|
||||
#include "engines/stark/movement/walk.h"
|
||||
#include "engines/stark/movement/followpath.h"
|
||||
#include "engines/stark/movement/followpathlight.h"
|
||||
#include "engines/stark/movement/turn.h"
|
||||
|
||||
#include "engines/stark/resources/item.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Movement *Movement::construct(uint32 type, Resources::ItemVisual *item) {
|
||||
switch (type) {
|
||||
case kTypeWalk:
|
||||
return new Walk(Resources::Object::cast<Resources::FloorPositionedItem>(item));
|
||||
case kTypeFollowPath:
|
||||
return new FollowPath(item);
|
||||
case kTypeFollowPathLight:
|
||||
return new FollowPathLight(item);
|
||||
case kTypeTurn:
|
||||
return new Turn(Resources::Object::cast<Resources::FloorPositionedItem>(item));
|
||||
default:
|
||||
error("Unexpected movement type '%d'", type);
|
||||
}
|
||||
}
|
||||
|
||||
Movement::Movement(Resources::ItemVisual *item) :
|
||||
_ended(false),
|
||||
_item(item),
|
||||
_defaultTurnAngleSpeed(18.0f * 30.0f / 1000.0f) { // 18 degrees per gameloop at 30 fps
|
||||
}
|
||||
|
||||
Movement::~Movement() {
|
||||
}
|
||||
|
||||
void Movement::start() {
|
||||
_ended = false;
|
||||
}
|
||||
|
||||
void Movement::stop(bool force) {
|
||||
_ended = true;
|
||||
}
|
||||
|
||||
bool Movement::hasEnded() const {
|
||||
return _ended;
|
||||
}
|
||||
|
||||
float Movement::computeAngleBetweenVectorsXYPlane(const Math::Vector3d &v1, const Math::Vector3d &v2) const {
|
||||
Math::Vector3d v1XY = v1;
|
||||
v1XY.z() = 0.0;
|
||||
|
||||
Math::Vector3d v2XY = v2;
|
||||
v2XY.z() = 0.0;
|
||||
|
||||
Math::Angle angle = Math::Vector3d::angle(v1XY, v2XY);
|
||||
Math::Vector3d cross = Math::Vector3d::crossProduct(v1XY, v2XY);
|
||||
if (cross.z() < 0) {
|
||||
angle = -angle;
|
||||
}
|
||||
|
||||
return angle.getDegrees();
|
||||
}
|
||||
|
||||
bool Movement::hasReachedDestination() const {
|
||||
return true;
|
||||
}
|
||||
} // End of namespace Stark
|
||||
103
engines/stark/movement/movement.h
Normal file
103
engines/stark/movement/movement.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_MOVEMENT_H
|
||||
#define STARK_MOVEMENT_MOVEMENT_H
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class ItemVisual;
|
||||
}
|
||||
|
||||
class ResourceSerializer;
|
||||
|
||||
/**
|
||||
* Abstract movement of an item on the current location's floor
|
||||
*/
|
||||
class Movement {
|
||||
public:
|
||||
Movement(Resources::ItemVisual *item);
|
||||
virtual ~Movement();
|
||||
|
||||
enum MovementType {
|
||||
kTypeWalk = 1,
|
||||
kTypeFollowPath = 2,
|
||||
kTypeFollowPathLight = 3,
|
||||
kTypeTurn = 4
|
||||
};
|
||||
|
||||
/** Movement factory */
|
||||
static Movement *construct(uint32 type, Resources::ItemVisual *item);
|
||||
|
||||
/** Obtain the effective movement type */
|
||||
virtual uint32 getType() const = 0;
|
||||
|
||||
/**
|
||||
* Initiate the movement
|
||||
*/
|
||||
virtual void start();
|
||||
|
||||
/**
|
||||
* Stop / abort the movement
|
||||
*/
|
||||
virtual void stop(bool force = false);
|
||||
|
||||
/**
|
||||
* Called once per game loop
|
||||
*/
|
||||
virtual void onGameLoop() = 0;
|
||||
|
||||
/**
|
||||
* Has the movement stopped?
|
||||
*/
|
||||
bool hasEnded() const;
|
||||
|
||||
/**
|
||||
* Has the movement reached its destination successfully?
|
||||
*/
|
||||
virtual bool hasReachedDestination() const;
|
||||
|
||||
/**
|
||||
* Persist / restore the state of the movement so it can be resumed using 'start'
|
||||
*/
|
||||
virtual void saveLoad(ResourceSerializer *serializer) = 0;
|
||||
|
||||
protected:
|
||||
enum TurnDirection {
|
||||
kTurnNone,
|
||||
kTurnLeft,
|
||||
kTurnRight
|
||||
};
|
||||
|
||||
const float _defaultTurnAngleSpeed; // Degrees per ms
|
||||
|
||||
float computeAngleBetweenVectorsXYPlane(const Math::Vector3d &v1, const Math::Vector3d &v2) const;
|
||||
|
||||
bool _ended;
|
||||
Resources::ItemVisual *_item;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_MOVEMENT_H
|
||||
100
engines/stark/movement/shortestpath.cpp
Normal file
100
engines/stark/movement/shortestpath.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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 "engines/stark/movement/shortestpath.h"
|
||||
|
||||
#include "common/hash-ptr.h"
|
||||
|
||||
#include "engines/stark/resources/floor.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
ShortestPath::NodeList ShortestPath::search(const Resources::FloorEdge *start, const Resources::FloorEdge *goal) {
|
||||
NodeList frontier;
|
||||
NodePrecedenceMap cameFrom;
|
||||
NodeCostMap costSoFar;
|
||||
|
||||
frontier.push_back(start);
|
||||
cameFrom[start] = nullptr;
|
||||
costSoFar[start] = 0;
|
||||
|
||||
while (!frontier.empty()) {
|
||||
const Resources::FloorEdge *current = popEdgeWithLowestCost(frontier, costSoFar);
|
||||
|
||||
if (current == goal)
|
||||
break;
|
||||
|
||||
Common::Array<Resources::FloorEdge *> neighbours = current->getNeighbours();
|
||||
for (uint i = 0; i < neighbours.size(); i++) {
|
||||
const Resources::FloorEdge *next = neighbours[i];
|
||||
if (!next->isEnabled())
|
||||
continue;
|
||||
|
||||
float newCost = costSoFar[current] + current->costTo(next);
|
||||
if (!costSoFar.contains(next) || newCost < costSoFar[next]) {
|
||||
frontier.push_back(next);
|
||||
cameFrom[next] = current;
|
||||
costSoFar[next] = newCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rebuildPath(start, goal, cameFrom);
|
||||
}
|
||||
|
||||
ShortestPath::NodeList ShortestPath::rebuildPath(const Resources::FloorEdge *start, const Resources::FloorEdge *goal,
|
||||
const NodePrecedenceMap &cameFrom) const {
|
||||
NodeList path;
|
||||
|
||||
const Resources::FloorEdge *current = goal;
|
||||
path.push_front(goal);
|
||||
|
||||
while (current && current != start) {
|
||||
current = cameFrom.getValOrDefault(current, nullptr);
|
||||
path.push_front(current);
|
||||
}
|
||||
|
||||
if (current != start) {
|
||||
// No path has been found from start to goal
|
||||
return NodeList();
|
||||
}
|
||||
|
||||
path.push_front(start);
|
||||
return path;
|
||||
}
|
||||
|
||||
const Resources::FloorEdge *ShortestPath::popEdgeWithLowestCost(NodeList &frontier, const NodeCostMap &costSoFar) const {
|
||||
// Poor man's priority queue using a list ...
|
||||
NodeList::iterator lowestCostItem = frontier.begin();
|
||||
for (NodeList::iterator it = frontier.begin(); it != frontier.end(); it++) {
|
||||
if (costSoFar[*it] < costSoFar[*lowestCostItem]) {
|
||||
lowestCostItem = it;
|
||||
}
|
||||
}
|
||||
|
||||
const Resources::FloorEdge *result = *lowestCostItem;
|
||||
|
||||
frontier.erase(lowestCostItem);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
58
engines/stark/movement/shortestpath.h
Normal file
58
engines/stark/movement/shortestpath.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_SHORTEST_PATH_H
|
||||
#define STARK_MOVEMENT_SHORTEST_PATH_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class FloorEdge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the shortest path between two nodes in a graph
|
||||
*
|
||||
* This is an implementation of Dijsktra's search algorithm
|
||||
*/
|
||||
class ShortestPath {
|
||||
public:
|
||||
typedef Common::List<const Resources::FloorEdge *> NodeList;
|
||||
|
||||
/** Computes the shortest path between the start and the goal graph nodes */
|
||||
NodeList search(const Resources::FloorEdge *start, const Resources::FloorEdge *goal);
|
||||
|
||||
private:
|
||||
typedef Common::HashMap<const Resources::FloorEdge *, const Resources::FloorEdge *> NodePrecedenceMap;
|
||||
typedef Common::HashMap<const Resources::FloorEdge *, float> NodeCostMap;
|
||||
|
||||
const Resources::FloorEdge *popEdgeWithLowestCost(NodeList &frontier, const NodeCostMap &costSoFar) const;
|
||||
|
||||
NodeList rebuildPath(const Resources::FloorEdge *start, const Resources::FloorEdge *goal,
|
||||
const NodePrecedenceMap &cameFrom) const;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_SHORTEST_PATH_H
|
||||
73
engines/stark/movement/stringpullingpath.cpp
Normal file
73
engines/stark/movement/stringpullingpath.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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 "engines/stark/movement/stringpullingpath.h"
|
||||
|
||||
#include "engines/stark/resources/floor.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
|
||||
#include "math/line3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
StringPullingPath::StringPullingPath() :
|
||||
_targetStep(1) {
|
||||
}
|
||||
|
||||
void StringPullingPath::addStep(const Math::Vector3d &position) {
|
||||
_steps.push_back(position);
|
||||
}
|
||||
|
||||
void StringPullingPath::reset() {
|
||||
_steps.clear();
|
||||
_targetStep = 1;
|
||||
}
|
||||
|
||||
Math::Vector3d StringPullingPath::computeWalkTarget(const Math::Vector3d &fromPosition) {
|
||||
Current *current = StarkGlobal->getCurrent();
|
||||
Resources::Floor *floor = current->getFloor();
|
||||
|
||||
// HACK: Sometimes the character gets stuck because of rounding errors
|
||||
// If we detect the character is stuck on a step, just make it go to the next one.
|
||||
// TODO: Improve the string pulling code so that the targets can also be points between two steps.
|
||||
if (fromPosition.getDistanceTo(_steps[_targetStep]) < 1.0 && _targetStep < _steps.size() - 1) {
|
||||
_targetStep++;
|
||||
}
|
||||
|
||||
for (uint i = _targetStep + 1; i < _steps.size(); i++) {
|
||||
Math::Line3d testSegment = Math::Line3d(fromPosition, _steps[i]);
|
||||
if (!floor->isSegmentInside(testSegment)) {
|
||||
break;
|
||||
}
|
||||
|
||||
_targetStep = i;
|
||||
}
|
||||
|
||||
return _steps[_targetStep];
|
||||
}
|
||||
|
||||
bool StringPullingPath::hasSteps() const {
|
||||
return _steps.size() > 1;
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
60
engines/stark/movement/stringpullingpath.h
Normal file
60
engines/stark/movement/stringpullingpath.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_STRING_PULLING_PATH_H
|
||||
#define STARK_MOVEMENT_STRING_PULLING_PATH_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
/**
|
||||
* Store a path and allow to walk along it smoothly
|
||||
*
|
||||
* The base principle of the string pulling algorithm is to skip steps
|
||||
* if it is possible to walk directly to a later step in straight line.
|
||||
*/
|
||||
class StringPullingPath {
|
||||
public:
|
||||
StringPullingPath();
|
||||
|
||||
/** Append a step to the path */
|
||||
void addStep(const Math::Vector3d &position);
|
||||
|
||||
/** Reset the steps, and the current target on the path */
|
||||
void reset();
|
||||
|
||||
/** Move the walk target forward according to the position */
|
||||
Math::Vector3d computeWalkTarget(const Math::Vector3d &fromPosition);
|
||||
|
||||
/** Returns true if this path is not degenerated (empty or single point) */
|
||||
bool hasSteps() const;
|
||||
|
||||
private:
|
||||
Common::Array<Math::Vector3d> _steps;
|
||||
uint32 _targetStep;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_STRING_PULLING_PATH_H
|
||||
104
engines/stark/movement/turn.cpp
Normal file
104
engines/stark/movement/turn.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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 "engines/stark/movement/turn.h"
|
||||
|
||||
#include "math/matrix3.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Turn::Turn(Resources::FloorPositionedItem *item) :
|
||||
Movement(item),
|
||||
_item3D(item),
|
||||
_turnSpeed(_defaultTurnAngleSpeed) {
|
||||
}
|
||||
|
||||
Turn::~Turn() {
|
||||
}
|
||||
|
||||
void Turn::onGameLoop() {
|
||||
// Compute the direction to turn towards
|
||||
Math::Vector3d direction = _targetDirection;
|
||||
direction.z() = 0;
|
||||
direction.normalize();
|
||||
|
||||
// Compute the angle with the current character direction
|
||||
Math::Vector3d currentDirection = _item3D->getDirectionVector();
|
||||
float directionDeltaAngle = computeAngleBetweenVectorsXYPlane(currentDirection, direction);
|
||||
|
||||
// If the angle between the current direction and the new one is too high,
|
||||
// make the character turn on itself until the angle is low enough
|
||||
TurnDirection turnDirection;
|
||||
if (ABS(directionDeltaAngle) > getAngularSpeed() + 0.1f) {
|
||||
turnDirection = directionDeltaAngle < 0 ? kTurnLeft : kTurnRight;
|
||||
} else {
|
||||
turnDirection = kTurnNone;
|
||||
}
|
||||
|
||||
if (turnDirection == kTurnNone) {
|
||||
direction = _targetDirection;
|
||||
} else {
|
||||
// Make the character turn towards the target direction
|
||||
direction = currentDirection;
|
||||
|
||||
Math::Matrix3 rot;
|
||||
rot.buildAroundZ(turnDirection == kTurnLeft ? -getAngularSpeed() : getAngularSpeed());
|
||||
rot.transformVector(&direction);
|
||||
}
|
||||
|
||||
// Update the item's direction
|
||||
_item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0)));
|
||||
|
||||
// Check if we are close enough to the destination to stop
|
||||
if (direction == _targetDirection) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
float Turn::getAngularSpeed() const {
|
||||
return _turnSpeed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
}
|
||||
|
||||
void Turn::setTargetDirection(const Math::Vector3d &direction) {
|
||||
_targetDirection = direction;
|
||||
}
|
||||
|
||||
void Turn::setSpeed(float speed) {
|
||||
_turnSpeed = speed;
|
||||
}
|
||||
|
||||
uint32 Turn::getType() const {
|
||||
return kTypeTurn;
|
||||
}
|
||||
|
||||
void Turn::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsVector3d(_targetDirection);
|
||||
serializer->syncAsFloat(_turnSpeed);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
62
engines/stark/movement/turn.h
Normal file
62
engines/stark/movement/turn.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_TURN_H
|
||||
#define STARK_MOVEMENT_TURN_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Resources {
|
||||
class FloorPositionedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item turn on itself towards a target direction
|
||||
*/
|
||||
class Turn : public Movement {
|
||||
public:
|
||||
Turn(Resources::FloorPositionedItem *item);
|
||||
virtual ~Turn();
|
||||
|
||||
// Movement API
|
||||
void onGameLoop() override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the direction to turn towards */
|
||||
void setTargetDirection(const Math::Vector3d &direction);
|
||||
|
||||
/** Override the default rotation speed */
|
||||
void setSpeed(float speed);
|
||||
|
||||
private:
|
||||
float getAngularSpeed() const;
|
||||
|
||||
Resources::FloorPositionedItem *_item3D;
|
||||
Math::Vector3d _targetDirection;
|
||||
float _turnSpeed;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_TURN_H
|
||||
525
engines/stark/movement/walk.cpp
Normal file
525
engines/stark/movement/walk.cpp
Normal file
@@ -0,0 +1,525 @@
|
||||
/* 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 "engines/stark/movement/walk.h"
|
||||
|
||||
#include "engines/stark/movement/shortestpath.h"
|
||||
#include "engines/stark/movement/stringpullingpath.h"
|
||||
|
||||
#include "engines/stark/services/global.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/stateprovider.h"
|
||||
|
||||
#include "engines/stark/resources/anim.h"
|
||||
#include "engines/stark/resources/floor.h"
|
||||
#include "engines/stark/resources/floorface.h"
|
||||
#include "engines/stark/resources/item.h"
|
||||
#include "engines/stark/resources/location.h"
|
||||
|
||||
#include "math/vector2d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Walk::Walk(Resources::FloorPositionedItem *item) :
|
||||
Movement(item),
|
||||
_item3D(item),
|
||||
_running(false),
|
||||
_reachedDestination(false),
|
||||
_turnDirection(kTurnNone),
|
||||
_collisionWaitTimeout(-1),
|
||||
_collisionWaitCount(0) {
|
||||
_path = new StringPullingPath();
|
||||
}
|
||||
|
||||
Walk::~Walk() {
|
||||
delete _path;
|
||||
}
|
||||
|
||||
void Walk::start() {
|
||||
Movement::start();
|
||||
|
||||
updatePath();
|
||||
changeItemAnim();
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
location->startFollowingCharacter();
|
||||
}
|
||||
|
||||
void Walk::stop(bool force) {
|
||||
if (force) {
|
||||
_destinations.clear();
|
||||
}
|
||||
|
||||
if (_destinations.empty()) {
|
||||
Movement::stop(force);
|
||||
changeItemAnim();
|
||||
_avoidedItems.clear();
|
||||
} else {
|
||||
Math::Vector3d destination = _destinations.front();
|
||||
_destinations.remove_at(0);
|
||||
setDestination(destination);
|
||||
updatePath();
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::updatePath() const {
|
||||
_path->reset();
|
||||
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
Math::Vector3d startPosition = _item3D->getPosition3D();
|
||||
int32 startFloorFaceIndex = floor->findFaceContainingPoint(startPosition);
|
||||
if (startFloorFaceIndex == -1) {
|
||||
startFloorFaceIndex = 0;
|
||||
}
|
||||
|
||||
Resources::FloorFace *startFloorFace = floor->getFace(startFloorFaceIndex);
|
||||
Resources::FloorEdge *startFloorEdge = startFloorFace->findNearestEdge(startPosition);
|
||||
if (!startFloorEdge) {
|
||||
// Unable to find enabled start edge
|
||||
return;
|
||||
}
|
||||
|
||||
int32 destinationFloorFaceIndex = floor->findFaceContainingPoint(_destination);
|
||||
if (destinationFloorFaceIndex < 0) {
|
||||
// Unable to find the destination's face
|
||||
return;
|
||||
}
|
||||
|
||||
Resources::FloorFace *destinationFloorFace = floor->getFace(destinationFloorFaceIndex);
|
||||
Resources::FloorEdge *destinationFloorEdge = destinationFloorFace->findNearestEdge(_destination);
|
||||
if (!destinationFloorEdge) {
|
||||
// Unable to find enabled destination edge
|
||||
return;
|
||||
}
|
||||
|
||||
ShortestPath pathSearch;
|
||||
ShortestPath::NodeList edgePath = pathSearch.search(startFloorEdge, destinationFloorEdge);
|
||||
|
||||
for (ShortestPath::NodeList::const_iterator it = edgePath.begin(); it != edgePath.end(); it++) {
|
||||
_path->addStep((*it)->getPosition());
|
||||
}
|
||||
|
||||
_path->addStep(_destination);
|
||||
}
|
||||
|
||||
void Walk::queueDestinationToAvoidItem(Resources::FloorPositionedItem *item, const Math::Vector3d &destination) {
|
||||
_destinations.push_back(destination);
|
||||
_avoidedItems.push_back(item);
|
||||
}
|
||||
|
||||
bool Walk::isItemAlreadyAvoided(Resources::FloorPositionedItem *item) const {
|
||||
return Common::find(_avoidedItems.begin(), _avoidedItems.end(), item) != _avoidedItems.end();
|
||||
}
|
||||
|
||||
void Walk::onGameLoop() {
|
||||
Resources::ItemVisual *interactiveItem = StarkGlobal->getCurrent()->getInteractive();
|
||||
|
||||
if (_item != interactiveItem) {
|
||||
// NPCs have a simple collision handling strategy.
|
||||
// They stop when they collide with other items,
|
||||
// and wait for their path to be clear.
|
||||
doWalkCollisionSimple();
|
||||
} else {
|
||||
// April has a more advanced collision handling approach.
|
||||
// She goes either left or right of the items on her path.
|
||||
// When impossible to pick a direction, she walks until the
|
||||
// obstacle is reached.
|
||||
doWalkCollisionAvoid();
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::doWalk() {
|
||||
if (!_path->hasSteps()) {
|
||||
// There is no path to the destination
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
// Get the target to walk to
|
||||
Math::Vector3d currentPosition = _item3D->getPosition3D();
|
||||
Math::Vector3d target = _path->computeWalkTarget(currentPosition);
|
||||
|
||||
// Compute the direction to walk into
|
||||
Math::Vector3d direction = target - currentPosition;
|
||||
direction.z() = 0;
|
||||
direction.normalize();
|
||||
|
||||
// Compute the angle with the current character direction
|
||||
Math::Vector3d currentDirection = _item3D->getDirectionVector();
|
||||
float directionDeltaAngle = computeAngleBetweenVectorsXYPlane(currentDirection, direction);
|
||||
|
||||
// If the angle between the current direction and the new one is too high,
|
||||
// make the character turn on itself until the angle is low enough
|
||||
if (ABS(directionDeltaAngle) > getAngularSpeed() + 0.1f) {
|
||||
_turnDirection = directionDeltaAngle < 0 ? kTurnLeft : kTurnRight;
|
||||
} else {
|
||||
_turnDirection = kTurnNone;
|
||||
}
|
||||
|
||||
float distancePerGameloop = computeDistancePerGameLoop();
|
||||
|
||||
Math::Vector3d newPosition;
|
||||
if (_turnDirection == kTurnNone) {
|
||||
// Compute the new position using the distance per gameloop
|
||||
if (currentPosition.getDistanceTo(target) > distancePerGameloop) {
|
||||
newPosition = currentPosition + direction * distancePerGameloop;
|
||||
} else {
|
||||
newPosition = target;
|
||||
}
|
||||
} else {
|
||||
// The character does not change position when it is turning
|
||||
newPosition = currentPosition;
|
||||
direction = currentDirection;
|
||||
|
||||
Math::Matrix3 rot;
|
||||
rot.buildAroundZ(_turnDirection == kTurnLeft ? -getAngularSpeed() : getAngularSpeed());
|
||||
rot.transformVector(&direction);
|
||||
}
|
||||
|
||||
_previousPosition = currentPosition;
|
||||
_currentTarget = target;
|
||||
|
||||
// Some scripts expect the character position to be the exact destination
|
||||
if (newPosition == _destination) {
|
||||
_reachedDestination = true;
|
||||
stop();
|
||||
}
|
||||
|
||||
// Update the new position's height according to the floor
|
||||
int32 newFloorFaceIndex = floor->findFaceContainingPoint(newPosition);
|
||||
if (newFloorFaceIndex >= 0) {
|
||||
floor->computePointHeightInFace(newPosition, newFloorFaceIndex);
|
||||
} else {
|
||||
warning("Item %s is walking off the floor", _item->getName().c_str());
|
||||
}
|
||||
|
||||
// Update the item's properties
|
||||
_item3D->setPosition3D(newPosition);
|
||||
if (direction.getMagnitude() != 0.f) {
|
||||
_item3D->setDirection(computeAngleBetweenVectorsXYPlane(direction, Math::Vector3d(1.0, 0.0, 0.0)));
|
||||
}
|
||||
if (newFloorFaceIndex >= 0) {
|
||||
// When unable to find the face containing the new position, keep the previous one
|
||||
// to prevent draw order glitches.
|
||||
_item3D->setFloorFaceIndex(newFloorFaceIndex);
|
||||
}
|
||||
|
||||
changeItemAnim();
|
||||
}
|
||||
|
||||
bool Walk::isPointNearPath(const Math::Vector3d &point3d, const Math::Vector3d &pathStart3d, const Math::Vector3d &pathEnd3d) {
|
||||
Math::Vector2d point = Math::Vector2d(point3d.x(), point3d.y());
|
||||
Math::Vector2d pathStart = Math::Vector2d(pathStart3d.x(), pathStart3d.y());
|
||||
Math::Vector2d pathEnd = Math::Vector2d(pathEnd3d.x(), pathEnd3d.y());
|
||||
|
||||
// Project the point onto the path
|
||||
Math::Vector2d pointToStart = point - pathStart;
|
||||
Math::Vector2d path = pathEnd - pathStart;
|
||||
float dot = pointToStart.dotProduct(path);
|
||||
float len = path.getSquareMagnitude();
|
||||
|
||||
float t = dot / len;
|
||||
|
||||
Math::Vector2d projection;
|
||||
if (0.f <= t && t < 1.f) {
|
||||
projection = path * t + pathStart;
|
||||
} else {
|
||||
projection = pathEnd;
|
||||
}
|
||||
|
||||
// Check if the projection is near the actual point
|
||||
return point.getDistanceTo(projection) <= (15.f + 15.f);
|
||||
}
|
||||
|
||||
void Walk::doWalkCollisionSimple() {
|
||||
if (_collisionWaitTimeout > 0) {
|
||||
_collisionWaitTimeout -= StarkGlobal->getMillisecondsPerGameloop();
|
||||
return;
|
||||
} else {
|
||||
_collisionWaitTimeout = -1;
|
||||
}
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
Common::Array<Resources::ModelItem *> characters = location->listModelItems();
|
||||
|
||||
// Check if any of the other characters is in our way
|
||||
for (uint i = 0; i < characters.size(); i++) {
|
||||
Resources::ModelItem *otherItem = characters[i];
|
||||
if (!otherItem || !otherItem->isEnabled() || otherItem == _item) continue;
|
||||
|
||||
Math::Vector3d otherPosition = otherItem->getPosition3D();
|
||||
|
||||
if (isPointNearPath(otherPosition, _previousPosition, _currentTarget)) {
|
||||
if (_previousPosition.getDistanceTo(otherPosition) <= 15.f * 3.f) {
|
||||
if (_collisionWaitCount >= 10) {
|
||||
doWalk();
|
||||
return;
|
||||
}
|
||||
|
||||
// A collision is detected. Remove the walk animation, and wait a bit.
|
||||
if (_item->getAnimActivity() != Resources::Anim::kActorActivityIdle) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
}
|
||||
|
||||
_collisionWaitCount++;
|
||||
_collisionWaitTimeout = 500; // ms
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The path is clear, walk normally
|
||||
_collisionWaitCount = 0;
|
||||
doWalk();
|
||||
}
|
||||
|
||||
void Walk::doWalkCollisionAvoid() {
|
||||
float collisionRadius = 15.f * 2.0999999f;
|
||||
|
||||
Math::Vector3d previousPosition = _item3D->getPosition3D();
|
||||
doWalk();
|
||||
Math::Vector3d newPosition = _item3D->getPosition3D();
|
||||
|
||||
Resources::Location *location = StarkGlobal->getCurrent()->getLocation();
|
||||
Common::Array<Resources::ModelItem *> characters = location->listModelItems();
|
||||
|
||||
// Check if we're colliding with another character, but going away from it.
|
||||
// In that case, the collision is being solved. There is nothing to do.
|
||||
for (uint i = 0; i < characters.size(); i++) {
|
||||
Resources::FloorPositionedItem *otherItem = characters[i];
|
||||
if (!otherItem || !otherItem->isEnabled() || otherItem == _item) continue;
|
||||
|
||||
Math::Vector3d otherPosition = otherItem->getPosition3D();
|
||||
|
||||
Math::Vector2d newPosition2d(newPosition.x(), newPosition.y());
|
||||
Math::Vector2d otherPosition2d(otherPosition.x(), otherPosition.y());
|
||||
|
||||
float newDistance = newPosition2d.getDistanceTo(otherPosition2d);
|
||||
if (newDistance < 15.f + 15.f) {
|
||||
Math::Vector2d previousPosition2d(previousPosition.x(), previousPosition.y());
|
||||
|
||||
float previousDistance = previousPosition2d.getDistanceTo(otherPosition2d);
|
||||
if (previousDistance < newDistance) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
|
||||
for (uint i = 0; i < characters.size(); i++) {
|
||||
Resources::FloorPositionedItem *otherItem = dynamic_cast<Resources::FloorPositionedItem *>(characters[i]);
|
||||
if (!otherItem || !otherItem->isEnabled() || otherItem == _item || isItemAlreadyAvoided(otherItem)) continue;
|
||||
|
||||
Math::Vector3d otherPosition = otherItem->getPosition3D();
|
||||
if (!isPointNearPath(otherPosition, _previousPosition, _currentTarget)) continue;
|
||||
|
||||
Math::Vector3d newPosition2d(newPosition.x(), newPosition.y(), 0.f);
|
||||
Math::Vector3d otherPosition2d(otherPosition.x(), otherPosition.y(), 0.f);
|
||||
|
||||
Math::Vector3d directionToOther = otherPosition2d - newPosition2d;
|
||||
float distanceToOther = directionToOther.getMagnitude();
|
||||
directionToOther.normalize();
|
||||
|
||||
Math::Vector3d up(0.f, 0.f, 1.f);
|
||||
Math::Vector3d rightDirection = Math::Vector3d::crossProduct(directionToOther, up);
|
||||
rightDirection.normalize();
|
||||
|
||||
Math::Vector3d otherPositionNear = newPosition2d + directionToOther * (distanceToOther - collisionRadius);
|
||||
Math::Vector3d otherPostionFar = newPosition2d + directionToOther * (distanceToOther + collisionRadius);
|
||||
|
||||
Math::Vector3d rightOfOtherNear = otherPositionNear + rightDirection * collisionRadius;
|
||||
Math::Vector3d leftOfOtherNear = otherPositionNear - rightDirection * collisionRadius;
|
||||
Math::Vector3d rightOfOtherFar = otherPostionFar + rightDirection * collisionRadius;
|
||||
Math::Vector3d leftOfOtherFar = otherPostionFar - rightDirection * collisionRadius;
|
||||
|
||||
bool canGoRight = false;
|
||||
if (floor->isSegmentInside(Math::Line3d(otherPositionNear, rightOfOtherNear))) {
|
||||
if (floor->isSegmentInside(Math::Line3d(rightOfOtherNear, rightOfOtherFar))) {
|
||||
canGoRight = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool canGoLeft = false;
|
||||
if (floor->isSegmentInside(Math::Line3d(otherPositionNear, leftOfOtherNear))) {
|
||||
if (floor->isSegmentInside(Math::Line3d(leftOfOtherNear, leftOfOtherFar))) {
|
||||
canGoLeft = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint j = 0; j < characters.size(); j++) {
|
||||
if (j == i) continue;
|
||||
Resources::FloorPositionedItem *anotherItem = dynamic_cast<Resources::FloorPositionedItem *>(characters[j]);
|
||||
if (!anotherItem || !anotherItem->isEnabled() || anotherItem == _item) continue;
|
||||
|
||||
Math::Vector3d anotherPosition = anotherItem->getPosition3D();
|
||||
|
||||
if (isPointNearPath(anotherPosition, otherPositionNear, rightOfOtherNear)) {
|
||||
canGoRight = false;
|
||||
}
|
||||
|
||||
if (isPointNearPath(anotherPosition, rightOfOtherNear, rightOfOtherFar)) {
|
||||
canGoRight = false;
|
||||
}
|
||||
|
||||
if (isPointNearPath(anotherPosition, otherPositionNear, leftOfOtherNear)) {
|
||||
canGoLeft = false;
|
||||
}
|
||||
|
||||
if (isPointNearPath(anotherPosition, leftOfOtherNear, leftOfOtherFar)) {
|
||||
canGoLeft = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (distanceToOther < collisionRadius) {
|
||||
int32 floorFace = floor->findFaceContainingPoint(previousPosition);
|
||||
if (floorFace >= 0) {
|
||||
floor->computePointHeightInFace(previousPosition, floorFace);
|
||||
|
||||
_item3D->setFloorFaceIndex(floorFace);
|
||||
_item3D->setPosition3D(previousPosition);
|
||||
}
|
||||
_reachedDestination = false;
|
||||
// _skipped = false;
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
|
||||
// If our target destination is in the collision radius of the tested item
|
||||
// Then adjust our destination to be just outside of the item's collision radius.
|
||||
float distanceToDestination = _destination.getDistanceTo(otherPosition);
|
||||
if (distanceToDestination < collisionRadius) {
|
||||
setDestinationWithoutHeight(otherPosition - directionToOther * collisionRadius);
|
||||
// _field_51 = true;
|
||||
updatePath();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canGoLeft) {
|
||||
Math::Vector3d lookDirection = _item3D->getDirectionVector();
|
||||
Math::Vector3d previousToLeft = leftOfOtherNear - _previousPosition;
|
||||
|
||||
Math::Angle angle = Math::Vector3d::angle(lookDirection, previousToLeft);
|
||||
if (angle > 270) {
|
||||
canGoLeft = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canGoRight) {
|
||||
Math::Vector3d lookDirection = _item3D->getDirectionVector();
|
||||
Math::Vector3d previousToRight = rightOfOtherNear - _previousPosition;
|
||||
|
||||
Math::Angle angle = Math::Vector3d::angle(lookDirection, previousToRight);
|
||||
if (angle > 270) {
|
||||
canGoRight = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canGoRight && !canGoLeft) {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(rightOfOtherNear);
|
||||
updatePath();
|
||||
} else if (!canGoRight && canGoLeft) {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(leftOfOtherNear);
|
||||
updatePath();
|
||||
} else if (canGoRight && canGoLeft) {
|
||||
Math::Vector3d forwardDirection = _currentTarget - _previousPosition;
|
||||
Math::Vector3d cross = Math::Vector3d::crossProduct(forwardDirection, directionToOther);
|
||||
|
||||
if (cross.z() < 0.f) {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(leftOfOtherNear);
|
||||
updatePath();
|
||||
} else {
|
||||
queueDestinationToAvoidItem(otherItem, _destination);
|
||||
setDestinationWithoutHeight(rightOfOtherNear);
|
||||
updatePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Walk::getAngularSpeed() const {
|
||||
return _defaultTurnAngleSpeed * StarkGlobal->getMillisecondsPerGameloop();
|
||||
}
|
||||
|
||||
float Walk::computeDistancePerGameLoop() const {
|
||||
Resources::Anim *anim = _item->getAnim();
|
||||
float distancePerGameloop = anim->getMovementSpeed() * StarkGlobal->getMillisecondsPerGameloop() / 1000.f;
|
||||
|
||||
return distancePerGameloop;
|
||||
}
|
||||
|
||||
void Walk::setDestination(const Math::Vector3d &destination) {
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
void Walk::setDestinationWithoutHeight(Math::Vector3d destination) {
|
||||
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
|
||||
int32 faceIndex = floor->findFaceContainingPoint(destination);
|
||||
if (faceIndex >= 0) {
|
||||
floor->computePointHeightInFace(destination, faceIndex);
|
||||
}
|
||||
|
||||
setDestination(destination);
|
||||
}
|
||||
|
||||
void Walk::setRunning() {
|
||||
_running = true;
|
||||
changeItemAnim();
|
||||
}
|
||||
|
||||
void Walk::changeItemAnim() {
|
||||
if (_ended) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
} else if (_turnDirection != kTurnNone) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityIdle);
|
||||
} else if (_running) {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityRun);
|
||||
} else {
|
||||
_item->setAnimActivity(Resources::Anim::kActorActivityWalk);
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::changeDestination(const Math::Vector3d &destination) {
|
||||
_collisionWaitTimeout = -1;
|
||||
setDestination(destination);
|
||||
updatePath();
|
||||
}
|
||||
|
||||
bool Walk::hasReachedDestination() const {
|
||||
return _reachedDestination;
|
||||
}
|
||||
|
||||
uint32 Walk::getType() const {
|
||||
return kTypeWalk;
|
||||
}
|
||||
|
||||
void Walk::saveLoad(ResourceSerializer *serializer) {
|
||||
serializer->syncAsVector3d(_destination);
|
||||
serializer->syncAsUint32LE(_running);
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
100
engines/stark/movement/walk.h
Normal file
100
engines/stark/movement/walk.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_MOVEMENT_WALK_H
|
||||
#define STARK_MOVEMENT_WALK_H
|
||||
|
||||
#include "engines/stark/movement/movement.h"
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class StringPullingPath;
|
||||
|
||||
namespace Resources {
|
||||
class FloorPositionedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item walk / run to its destination on the current
|
||||
* location's floor
|
||||
*/
|
||||
class Walk : public Movement {
|
||||
public:
|
||||
Walk(Resources::FloorPositionedItem *item);
|
||||
virtual ~Walk();
|
||||
|
||||
// Movement API
|
||||
void start() override;
|
||||
void stop(bool force = false) override;
|
||||
void onGameLoop() override;
|
||||
bool hasReachedDestination() const override;
|
||||
uint32 getType() const override;
|
||||
void saveLoad(ResourceSerializer *serializer) override;
|
||||
|
||||
/** Set the destination */
|
||||
void setDestination(const Math::Vector3d &destination);
|
||||
void setDestinationWithoutHeight(Math::Vector3d destination);
|
||||
|
||||
/** Change the destination and recompute the path */
|
||||
void changeDestination(const Math::Vector3d &destination);
|
||||
|
||||
/** Set the running flag */
|
||||
void setRunning();
|
||||
|
||||
private:
|
||||
void doWalk();
|
||||
void doWalkCollisionSimple();
|
||||
void doWalkCollisionAvoid();
|
||||
|
||||
float computeDistancePerGameLoop() const;
|
||||
float getAngularSpeed() const;
|
||||
|
||||
void changeItemAnim();
|
||||
void updatePath() const;
|
||||
|
||||
void queueDestinationToAvoidItem(Resources::FloorPositionedItem *item, const Math::Vector3d &destination);
|
||||
bool isItemAlreadyAvoided(Resources::FloorPositionedItem *item) const;
|
||||
|
||||
static bool isPointNearPath(const Math::Vector3d &point3d, const Math::Vector3d &pathStart3d, const Math::Vector3d &pathEnd3d);
|
||||
|
||||
Resources::FloorPositionedItem *_item3D;
|
||||
StringPullingPath *_path;
|
||||
|
||||
Math::Vector3d _destination;
|
||||
Common::Array<Math::Vector3d> _destinations;
|
||||
|
||||
Common::Array<Resources::ItemVisual *> _avoidedItems;
|
||||
|
||||
bool _running;
|
||||
bool _reachedDestination;
|
||||
TurnDirection _turnDirection;
|
||||
|
||||
int32 _collisionWaitTimeout;
|
||||
int32 _collisionWaitCount;
|
||||
Math::Vector3d _previousPosition;
|
||||
Math::Vector3d _currentTarget;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MOVEMENT_WALK_H
|
||||
Reference in New Issue
Block a user