mirror of
https://github.com/rwengine/openrw.git
synced 2024-09-15 15:02:34 +02:00
Implement basic traffic
This includes spawning vehicles on the road and rudimentary traffic control with changing lanes and braking in front of characters
This commit is contained in:
parent
c4ad3d9846
commit
5f2fe96167
@ -1,5 +1,6 @@
|
|||||||
#include "ai/AIGraph.hpp"
|
#include "ai/AIGraph.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
@ -46,8 +47,8 @@ void AIGraph::createPathNodes(const glm::vec3& position,
|
|||||||
ainode->nextIndex = node.next >= 0 ? startIndex + node.next : -1;
|
ainode->nextIndex = node.next >= 0 ? startIndex + node.next : -1;
|
||||||
ainode->flags = AIGraphNode::None;
|
ainode->flags = AIGraphNode::None;
|
||||||
ainode->size = node.size;
|
ainode->size = node.size;
|
||||||
ainode->other_thing = node.other_thing;
|
ainode->leftLanes = node.leftLanes;
|
||||||
ainode->other_thing2 = node.other_thing2;
|
ainode->rightLanes = node.rightLanes;
|
||||||
ainode->position = nodePosition;
|
ainode->position = nodePosition;
|
||||||
ainode->external = node.type == PathNode::EXTERNAL;
|
ainode->external = node.type == PathNode::EXTERNAL;
|
||||||
ainode->disabled = false;
|
ainode->disabled = false;
|
||||||
@ -96,7 +97,8 @@ glm::ivec2 worldToGrid(const glm::vec2& world) {
|
|||||||
|
|
||||||
void AIGraph::gatherExternalNodesNear(const glm::vec3& center,
|
void AIGraph::gatherExternalNodesNear(const glm::vec3& center,
|
||||||
const float radius,
|
const float radius,
|
||||||
std::vector<AIGraphNode*>& nodes) {
|
std::vector<AIGraphNode*>& nodes,
|
||||||
|
AIGraphNode::NodeType type) {
|
||||||
// the bounds end up covering more than might fit
|
// the bounds end up covering more than might fit
|
||||||
auto planecoords = glm::vec2(center);
|
auto planecoords = glm::vec2(center);
|
||||||
auto minWorld = planecoords - glm::vec2(radius);
|
auto minWorld = planecoords - glm::vec2(radius);
|
||||||
@ -111,11 +113,12 @@ void AIGraph::gatherExternalNodesNear(const glm::vec3& center,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto& external = gridNodes[i];
|
auto& external = gridNodes[i];
|
||||||
for (AIGraphNode* node : external) {
|
copy_if(external.begin(), external.end(), back_inserter(nodes),
|
||||||
if (glm::distance2(center, node->position) < radius * radius) {
|
[¢er, &radius, &type](const AIGraphNode* node) {
|
||||||
nodes.push_back(node);
|
return node->type == type &&
|
||||||
}
|
glm::distance2(center, node->position) <
|
||||||
}
|
radius * radius;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "ai/AIGraphNode.hpp"
|
||||||
|
|
||||||
#include <rw/types.hpp>
|
#include <rw/types.hpp>
|
||||||
|
|
||||||
struct AIGraphNode;
|
struct AIGraphNode;
|
||||||
@ -32,7 +34,7 @@ public:
|
|||||||
PathData& path);
|
PathData& path);
|
||||||
|
|
||||||
void gatherExternalNodesNear(const glm::vec3& center, const float radius,
|
void gatherExternalNodesNear(const glm::vec3& center, const float radius,
|
||||||
std::vector<AIGraphNode*>& nodes);
|
std::vector<AIGraphNode*>& nodes, AIGraphNode::NodeType type);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,8 +16,8 @@ struct AIGraphNode {
|
|||||||
NodeType type;
|
NodeType type;
|
||||||
glm::vec3 position{};
|
glm::vec3 position{};
|
||||||
float size;
|
float size;
|
||||||
int other_thing;
|
int leftLanes;
|
||||||
int other_thing2;
|
int rightLanes;
|
||||||
bool external;
|
bool external;
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ void CharacterController::update(float dt) {
|
|||||||
_currentActivity = nullptr;
|
_currentActivity = nullptr;
|
||||||
if (_nextActivity) {
|
if (_nextActivity) {
|
||||||
setActivity(std::move(_nextActivity));
|
setActivity(std::move(_nextActivity));
|
||||||
|
_nextActivity = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,6 +144,255 @@ bool Activities::GoTo::update(CharacterObject *character,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 CharacterController::calculateRoadTarget(const glm::vec3 &target,
|
||||||
|
const glm::vec3 &start,
|
||||||
|
const glm::vec3 &end) {
|
||||||
|
// @todo set the real value
|
||||||
|
static constexpr float roadWidth = 5.f;
|
||||||
|
|
||||||
|
static const glm::vec3 up = glm::vec3(0.f, 0.f, 1.f);
|
||||||
|
const glm::vec3 dir = glm::normalize(start - end);
|
||||||
|
|
||||||
|
// Calculate the strafe vector
|
||||||
|
glm::vec3 strafe = glm::cross(up, dir);
|
||||||
|
|
||||||
|
const glm::vec3 laneOffset =
|
||||||
|
strafe *
|
||||||
|
(roadWidth / 2 + roadWidth * static_cast<float>(getLane() - 1));
|
||||||
|
|
||||||
|
return target + laneOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterController::steerTo(const glm::vec3 &target) {
|
||||||
|
// We can't drive without a vehicle
|
||||||
|
VehicleObject* vehicle = character->getCurrentVehicle();
|
||||||
|
if (vehicle == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the steeringAngle
|
||||||
|
float steeringAngle = 0.0f;
|
||||||
|
float deviation = glm::abs(vehicle->isOnSide(target)) / 5.f;
|
||||||
|
|
||||||
|
// If we are almost at the right angle, decrease the deviation to reduce wiggling
|
||||||
|
if (deviation < 1.f) deviation = deviation / 5.f;
|
||||||
|
|
||||||
|
// Make sure to normalize the value
|
||||||
|
deviation = glm::clamp(deviation, 0.f, 1.f);
|
||||||
|
|
||||||
|
// Set the right sign
|
||||||
|
steeringAngle = std::copysign(deviation, -vehicle->isOnSide(target));
|
||||||
|
|
||||||
|
vehicle->setSteeringAngle(steeringAngle, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo replace this by raytest/raycast logic
|
||||||
|
bool CharacterController::checkForObstacles()
|
||||||
|
{
|
||||||
|
// We can't drive without a vehicle
|
||||||
|
VehicleObject* vehicle = character->getCurrentVehicle();
|
||||||
|
if (vehicle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The minimal distance we test for objects
|
||||||
|
static constexpr float minColDist = 20.f;
|
||||||
|
|
||||||
|
// Try to stop before pedestrians
|
||||||
|
for (const auto &obj : character->engine->pedestrianPool.objects) {
|
||||||
|
// Verify that the character isn't the driver and is walking
|
||||||
|
if (obj.second != character && ((CharacterObject*)obj.second)->getCurrentVehicle() == nullptr) {
|
||||||
|
// Only check characters that are near our vehicle
|
||||||
|
if (glm::distance(vehicle->getPosition(), obj.second->getPosition()) <= minColDist) {
|
||||||
|
// Check if the character is in front of us and in our way
|
||||||
|
if ( vehicle->isInFront(obj.second->getPosition()) > -3.f
|
||||||
|
&& vehicle->isInFront(obj.second->getPosition()) < 10.f
|
||||||
|
&& glm::abs(vehicle->isOnSide(obj.second->getPosition())) < 3.f ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brake when a car is in front of us and change lanes when possible
|
||||||
|
for (const auto &obj : character->engine->vehiclePool.objects) {
|
||||||
|
// Verify that the vehicle isn't our vehicle
|
||||||
|
if (obj.second != vehicle) {
|
||||||
|
// Only check vehicles that are near our vehicle
|
||||||
|
if (glm::distance(vehicle->getPosition(), obj.second->getPosition()) <= minColDist) {
|
||||||
|
// Check if the vehicle is in front of us and in our way
|
||||||
|
if ( vehicle->isInFront(obj.second->getPosition()) > 0.f
|
||||||
|
&& vehicle->isInFront(obj.second->getPosition()) < 10.f
|
||||||
|
&& glm::abs(vehicle->isOnSide(obj.second->getPosition())) < 2.5f ) {
|
||||||
|
// Check if the road has more than one lane
|
||||||
|
// @todo we don't know the direction of the road, so for now, choose the bigger value
|
||||||
|
int maxLanes = targetNode->rightLanes > targetNode->leftLanes ?
|
||||||
|
targetNode->rightLanes : targetNode->leftLanes;
|
||||||
|
if (maxLanes > 1) {
|
||||||
|
// Change the lane, firstly check if there is an occupant
|
||||||
|
if (((VehicleObject *)obj.second)->getDriver() !=
|
||||||
|
nullptr) {
|
||||||
|
// @todo for now we don't know the lane where the player is currently driving
|
||||||
|
// so just slow down, in the future calculate the lane
|
||||||
|
if (((VehicleObject *)obj.second)
|
||||||
|
->getDriver()
|
||||||
|
->isPlayer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int avoidLane = ((VehicleObject *)obj.second)
|
||||||
|
->getDriver()
|
||||||
|
->controller->getLane();
|
||||||
|
|
||||||
|
// @todo for now just two lanes
|
||||||
|
if (avoidLane == 1) character->controller->setLane(2);
|
||||||
|
else character->controller->setLane(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Activities::DriveTo::update(CharacterObject *character,
|
||||||
|
CharacterController *controller) {
|
||||||
|
|
||||||
|
// We can't drive without a vehicle
|
||||||
|
VehicleObject* vehicle = character->getCurrentVehicle();
|
||||||
|
if (vehicle == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the nodes from the controller
|
||||||
|
AIGraphNode* lastTargetNode = controller->lastTargetNode;
|
||||||
|
AIGraphNode* nextTargetNode = controller->nextTargetNode;
|
||||||
|
|
||||||
|
|
||||||
|
// That's the position the vehicle is actually targeting
|
||||||
|
// depending on the lane, we have to shift its position
|
||||||
|
glm::vec3 roadTarget;
|
||||||
|
|
||||||
|
// A list of nodes we can choose from
|
||||||
|
std::vector<AIGraphNode*> potentialNodes = targetNode->connections;
|
||||||
|
|
||||||
|
// Make sure that we have a lastTargetNode
|
||||||
|
if (lastTargetNode == nullptr) {
|
||||||
|
for (const auto &node : potentialNodes) {
|
||||||
|
if (vehicle->isInFront(node->position) < 0.f) {
|
||||||
|
lastTargetNode = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastTargetNode == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unwanted nodes
|
||||||
|
for( auto i = potentialNodes.begin(); i != potentialNodes.end(); ) {
|
||||||
|
// @todo we don't know the direction of the road, so for now, choose the bigger value
|
||||||
|
int maxLanes = (*i)->rightLanes > (*i)->leftLanes ? (*i)->rightLanes : (*i)->leftLanes;
|
||||||
|
|
||||||
|
// We don't want roads with lanes <= 0, also ignore the lastTargetNode
|
||||||
|
if ( (*i) == lastTargetNode || maxLanes <= 0) {
|
||||||
|
i = potentialNodes.erase(i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// That's a dead end, try to turn around
|
||||||
|
if (potentialNodes.empty()) {
|
||||||
|
//@todo try to turn around
|
||||||
|
}
|
||||||
|
// Just a normal road
|
||||||
|
if (potentialNodes.size() == 1) {
|
||||||
|
roadTarget = controller->calculateRoadTarget(
|
||||||
|
targetNode->position, lastTargetNode->position,
|
||||||
|
potentialNodes.at(0)->position);
|
||||||
|
}
|
||||||
|
// Intersection, choose a direction
|
||||||
|
else if (potentialNodes.size() > 1) {
|
||||||
|
// Choose the next node randomly
|
||||||
|
if(nextTargetNode == nullptr) {
|
||||||
|
auto& random = character->engine->randomEngine;
|
||||||
|
int i = std::uniform_int_distribution<>(0, potentialNodes.size() - 1)(random);
|
||||||
|
nextTargetNode = potentialNodes.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the nextTargetNode to make sure we go this direction
|
||||||
|
controller->nextTargetNode = nextTargetNode;
|
||||||
|
|
||||||
|
roadTarget = controller->calculateRoadTarget(targetNode->position,
|
||||||
|
lastTargetNode->position,
|
||||||
|
nextTargetNode->position);
|
||||||
|
}
|
||||||
|
// Otherwise set the target to the current node
|
||||||
|
else {
|
||||||
|
roadTarget = targetNode->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether we reached the node
|
||||||
|
const auto targetDistance = glm::vec2(vehicle->getPosition() - roadTarget);
|
||||||
|
|
||||||
|
static constexpr float reachDistance = 5.0f;
|
||||||
|
|
||||||
|
if (glm::length(targetDistance) <= reachDistance) {
|
||||||
|
// Finish the activity
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo set real values
|
||||||
|
static constexpr float maximumSpeed = 10.f;
|
||||||
|
static constexpr float intersectionSpeed = 3.5f;
|
||||||
|
|
||||||
|
float currentSpeed = 0.f;
|
||||||
|
|
||||||
|
// Set the speed depending on where we are driving
|
||||||
|
if ( potentialNodes.size() == 1 ) {
|
||||||
|
currentSpeed = maximumSpeed;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentSpeed = intersectionSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether a pedestrian or vehicle is in our way
|
||||||
|
if (controller->checkForObstacles() == true) {
|
||||||
|
currentSpeed = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the vehicle slower than it should be
|
||||||
|
if (vehicle->getVelocity() < currentSpeed) {
|
||||||
|
vehicle->setHandbraking(false);
|
||||||
|
|
||||||
|
// The vehicle is driving backwards, accelerate
|
||||||
|
if (vehicle->getVelocity() < 0) {
|
||||||
|
vehicle->setThrottle(1.f);
|
||||||
|
}
|
||||||
|
// Slowly accelerate until we reach the designated speed
|
||||||
|
else {
|
||||||
|
vehicle->setThrottle(1.f - (vehicle->getVelocity() / currentSpeed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We are to fast, activate the handbrake - works better
|
||||||
|
else {
|
||||||
|
vehicle->setHandbraking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steer to the target
|
||||||
|
controller->steerTo(roadTarget);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Activities::Jump::update(CharacterObject *character,
|
bool Activities::Jump::update(CharacterObject *character,
|
||||||
CharacterController *controller) {
|
CharacterController *controller) {
|
||||||
RW_UNUSED(controller);
|
RW_UNUSED(controller);
|
||||||
@ -276,6 +526,11 @@ bool Activities::EnterVehicle::update(CharacterObject *character,
|
|||||||
// Play the pullout animation and tell the other character to get
|
// Play the pullout animation and tell the other character to get
|
||||||
// out.
|
// out.
|
||||||
character->playCycle(cycle_pullout);
|
character->playCycle(cycle_pullout);
|
||||||
|
|
||||||
|
if (currentOccupant->controller->getCurrentActivity() != nullptr) {
|
||||||
|
currentOccupant->controller->skipActivity();
|
||||||
|
}
|
||||||
|
|
||||||
currentOccupant->controller->setNextActivity(
|
currentOccupant->controller->setNextActivity(
|
||||||
std::make_unique<Activities::ExitVehicle>(true));
|
std::make_unique<Activities::ExitVehicle>(true));
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,7 +51,11 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Wander randomly around the map
|
* Wander randomly around the map
|
||||||
*/
|
*/
|
||||||
TrafficWander
|
TrafficWander,
|
||||||
|
/**
|
||||||
|
* Drive randomly around the map
|
||||||
|
*/
|
||||||
|
TrafficDriver
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -68,12 +72,19 @@ protected:
|
|||||||
|
|
||||||
float m_closeDoorTimer{0.f};
|
float m_closeDoorTimer{0.f};
|
||||||
|
|
||||||
|
// When driving a vehicle
|
||||||
|
int m_lane;
|
||||||
|
|
||||||
// Goal related variables
|
// Goal related variables
|
||||||
Goal currentGoal{None};
|
Goal currentGoal{None};
|
||||||
CharacterObject* leader = nullptr;
|
CharacterObject* leader = nullptr;
|
||||||
AIGraphNode* targetNode = nullptr;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
AIGraphNode* targetNode;
|
||||||
|
AIGraphNode* lastTargetNode;
|
||||||
|
AIGraphNode* nextTargetNode;
|
||||||
|
|
||||||
CharacterController() = default;
|
CharacterController() = default;
|
||||||
|
|
||||||
virtual ~CharacterController() = default;
|
virtual ~CharacterController() = default;
|
||||||
@ -132,6 +143,30 @@ public:
|
|||||||
void setMoveDirection(const glm::vec3& movement);
|
void setMoveDirection(const glm::vec3& movement);
|
||||||
void setLookDirection(const glm::vec2& look);
|
void setLookDirection(const glm::vec2& look);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief createRoadTarget When driving on a road, the targetNode must be shifted to a specific lane
|
||||||
|
*/
|
||||||
|
glm::vec3 calculateRoadTarget(const glm::vec3& target,
|
||||||
|
const glm::vec3& start, const glm::vec3& end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief steerTo When owning a vehicle, set the steering angle to drive to a target
|
||||||
|
*/
|
||||||
|
void steerTo(const glm::vec3& target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief checkForObstacles Check whether a pedestrian or vehicle is the way
|
||||||
|
*/
|
||||||
|
bool checkForObstacles();
|
||||||
|
|
||||||
|
void setLane(int lane) {
|
||||||
|
m_lane = lane;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLane() const {
|
||||||
|
return m_lane;
|
||||||
|
}
|
||||||
|
|
||||||
void setRunning(bool run);
|
void setRunning(bool run);
|
||||||
|
|
||||||
void setGoal(Goal goal) {
|
void setGoal(Goal goal) {
|
||||||
@ -182,6 +217,25 @@ struct GoTo : public CharacterController::Activity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DriveTo : public CharacterController::Activity {
|
||||||
|
DECL_ACTIVITY(DriveTo)
|
||||||
|
|
||||||
|
AIGraphNode* targetNode = nullptr;
|
||||||
|
bool rampant = false; // Drive fast, ignore traffic rules @todo
|
||||||
|
|
||||||
|
DriveTo() = default;
|
||||||
|
|
||||||
|
DriveTo(AIGraphNode* targetNode, bool _rampant = false)
|
||||||
|
: targetNode(targetNode), rampant(_rampant) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update(CharacterObject* character, CharacterController* controller) override;
|
||||||
|
|
||||||
|
bool canSkip(CharacterObject*, CharacterController*) const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Jump : public CharacterController::Activity {
|
struct Jump : public CharacterController::Activity {
|
||||||
DECL_ACTIVITY(Jump)
|
DECL_ACTIVITY(Jump)
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "engine/GameWorld.hpp"
|
#include "engine/GameWorld.hpp"
|
||||||
#include "objects/CharacterObject.hpp"
|
#include "objects/CharacterObject.hpp"
|
||||||
|
#include "objects/VehicleObject.hpp"
|
||||||
|
|
||||||
glm::vec3 DefaultAIController::getTargetPosition() {
|
glm::vec3 DefaultAIController::getTargetPosition() {
|
||||||
/*if(targetNode) {
|
/*if(targetNode) {
|
||||||
@ -80,7 +81,11 @@ void DefaultAIController::update(float dt) {
|
|||||||
auto& graph = getCharacter()->engine->aigraph;
|
auto& graph = getCharacter()->engine->aigraph;
|
||||||
AIGraphNode* node = nullptr;
|
AIGraphNode* node = nullptr;
|
||||||
float mindist = std::numeric_limits<float>::max();
|
float mindist = std::numeric_limits<float>::max();
|
||||||
for (auto n : graph.nodes) {
|
for (const auto& n : graph.nodes) {
|
||||||
|
if (n->type != AIGraphNode::Pedestrian) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
float d = glm::distance(n->position,
|
float d = glm::distance(n->position,
|
||||||
getCharacter()->getPosition());
|
getCharacter()->getPosition());
|
||||||
if (d < mindist) {
|
if (d < mindist) {
|
||||||
@ -91,6 +96,123 @@ void DefaultAIController::update(float dt) {
|
|||||||
targetNode = node;
|
targetNode = node;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case TrafficDriver: {
|
||||||
|
|
||||||
|
// If we don't own a car, become a pedestrian
|
||||||
|
if (getCharacter()->getCurrentVehicle() == nullptr)
|
||||||
|
{
|
||||||
|
targetNode = nullptr;
|
||||||
|
nextTargetNode = nullptr;
|
||||||
|
lastTargetNode = nullptr;
|
||||||
|
currentGoal = TrafficWander;
|
||||||
|
|
||||||
|
// Try to skip the current activity
|
||||||
|
if (getCharacter()->controller->getCurrentActivity() != nullptr) {
|
||||||
|
getCharacter()->controller->skipActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
setNextActivity(std::make_unique<Activities::ExitVehicle>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a target
|
||||||
|
if (targetNode) {
|
||||||
|
// Either we reached the node or we started new, therefore set the next activity
|
||||||
|
if (getCurrentActivity() == nullptr) {
|
||||||
|
// Assign the last target node
|
||||||
|
lastTargetNode = targetNode;
|
||||||
|
|
||||||
|
// Assign the next target node, either it is already set,
|
||||||
|
// or we have to find one by ourselves
|
||||||
|
if (nextTargetNode != nullptr) {
|
||||||
|
targetNode = nextTargetNode;
|
||||||
|
nextTargetNode = nullptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float mindist = std::numeric_limits<float>::max();
|
||||||
|
for (const auto& node : lastTargetNode->connections) {
|
||||||
|
const float distance =
|
||||||
|
getCharacter()->getCurrentVehicle()->isInFront(
|
||||||
|
node->position);
|
||||||
|
const float lastDistance =
|
||||||
|
getCharacter()->getCurrentVehicle()->isInFront(
|
||||||
|
lastTargetNode->position);
|
||||||
|
|
||||||
|
// Make sure, that the next node is in front of us, and farther away then the last node
|
||||||
|
if (distance > 0.f && distance > lastDistance) {
|
||||||
|
const float d = glm::distance(
|
||||||
|
node->position, getCharacter()
|
||||||
|
->getCurrentVehicle()
|
||||||
|
->getPosition());
|
||||||
|
|
||||||
|
if (d < mindist) {
|
||||||
|
targetNode = node;
|
||||||
|
mindist = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't found a node, choose one randomly
|
||||||
|
if (!targetNode) {
|
||||||
|
auto& random = getCharacter()->engine->randomEngine;
|
||||||
|
int nodeIndex = std::uniform_int_distribution<>(0, lastTargetNode->connections.size() - 1)(random);
|
||||||
|
targetNode = lastTargetNode->connections.at(nodeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the maximum amount of lanes changed and adjust our lane
|
||||||
|
// @todo we don't know the direction of the street, so for now, choose the bigger value
|
||||||
|
int maxLanes = targetNode->rightLanes > targetNode->leftLanes ? targetNode->rightLanes : targetNode->leftLanes;
|
||||||
|
|
||||||
|
if (maxLanes < getLane()) {
|
||||||
|
if(maxLanes <= 0) {
|
||||||
|
setLane(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setLane(maxLanes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNextActivity(std::make_unique<Activities::DriveTo>(
|
||||||
|
targetNode, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We need to pick an initial node
|
||||||
|
auto& graph = getCharacter()->engine->aigraph;
|
||||||
|
AIGraphNode* node = nullptr;
|
||||||
|
float mindist = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
for (const auto& n : graph.nodes) {
|
||||||
|
// No vehicle node, continue
|
||||||
|
if (n->type != AIGraphNode::Vehicle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The node must be ahead of the vehicle
|
||||||
|
if (getCharacter()->getCurrentVehicle()->isInFront(n->position) < 0.f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float d = glm::distance(
|
||||||
|
n->position,
|
||||||
|
getCharacter()->getCurrentVehicle()->getPosition());
|
||||||
|
|
||||||
|
if (d < mindist) {
|
||||||
|
node = n;
|
||||||
|
mindist = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targetNode = node;
|
||||||
|
|
||||||
|
// Set the next activity
|
||||||
|
if (targetNode) {
|
||||||
|
setNextActivity(std::make_unique<Activities::DriveTo>(
|
||||||
|
targetNode, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
#include "ai/AIGraph.hpp"
|
#include "ai/AIGraph.hpp"
|
||||||
#include "ai/AIGraphNode.hpp"
|
#include "ai/AIGraphNode.hpp"
|
||||||
@ -17,7 +20,6 @@
|
|||||||
#include "objects/VehicleObject.hpp"
|
#include "objects/VehicleObject.hpp"
|
||||||
#include "render/ViewCamera.hpp"
|
#include "render/ViewCamera.hpp"
|
||||||
|
|
||||||
#include <glm/gtx/norm.hpp>
|
|
||||||
#ifdef RW_WINDOWS
|
#ifdef RW_WINDOWS
|
||||||
#include <rw_mingw.hpp>
|
#include <rw_mingw.hpp>
|
||||||
#endif
|
#endif
|
||||||
@ -32,20 +34,28 @@ std::vector<AIGraphNode*> TrafficDirector::findAvailableNodes(
|
|||||||
std::vector<AIGraphNode*> available;
|
std::vector<AIGraphNode*> available;
|
||||||
available.reserve(20);
|
available.reserve(20);
|
||||||
|
|
||||||
graph->gatherExternalNodesNear(camera.position, radius, available);
|
graph->gatherExternalNodesNear(camera.position, radius, available, type);
|
||||||
|
|
||||||
float density = type == AIGraphNode::Vehicle ? carDensity : pedDensity;
|
float density = type == AIGraphNode::Vehicle ? carDensity : pedDensity;
|
||||||
float minDist = (10.f / density) * (10.f / density);
|
float minDist = (15.f / density) * (15.f / density);
|
||||||
float halfRadius2 = std::pow(radius / 2.f, 2.f);
|
float halfRadius2 = std::pow(radius / 2.f, 2.f);
|
||||||
|
|
||||||
// Check if any of the nearby nodes are blocked by a pedestrian standing on
|
// Check if any of the nearby nodes are blocked by a pedestrian or vehicle standing on
|
||||||
// it
|
// it
|
||||||
// or because it's inside the view frustum
|
// or because it's inside the view frustum
|
||||||
for (auto it = available.begin(); it != available.end();) {
|
for (auto it = available.begin(); it != available.end();) {
|
||||||
bool blocked = false;
|
bool blocked = false;
|
||||||
float dist2 = glm::distance2(camera.position, (*it)->position);
|
float dist2 = glm::distance2(camera.position, (*it)->position);
|
||||||
|
|
||||||
for (auto& obj : world->pedestrianPool.objects) {
|
for (const auto& obj : world->pedestrianPool.objects) {
|
||||||
|
if (glm::distance2((*it)->position, obj.second->getPosition()) <=
|
||||||
|
minDist) {
|
||||||
|
blocked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& obj : world->vehiclePool.objects) {
|
||||||
if (glm::distance2((*it)->position, obj.second->getPosition()) <=
|
if (glm::distance2((*it)->position, obj.second->getPosition()) <=
|
||||||
minDist) {
|
minDist) {
|
||||||
blocked = true;
|
blocked = true;
|
||||||
@ -83,10 +93,15 @@ void TrafficDirector::setDensity(AIGraphNode::NodeType type, float density) {
|
|||||||
|
|
||||||
std::vector<GameObject*> TrafficDirector::populateNearby(
|
std::vector<GameObject*> TrafficDirector::populateNearby(
|
||||||
const ViewCamera& camera, float radius, int maxSpawn) {
|
const ViewCamera& camera, float radius, int maxSpawn) {
|
||||||
|
|
||||||
|
auto& random = world->randomEngine;
|
||||||
|
std::vector<GameObject*> created;
|
||||||
|
|
||||||
int availablePeds =
|
int availablePeds =
|
||||||
maximumPedestrians - world->pedestrianPool.objects.size();
|
maximumPedestrians - world->pedestrianPool.objects.size();
|
||||||
|
|
||||||
std::vector<GameObject*> created;
|
int availableCars =
|
||||||
|
maximumCars - world->vehiclePool.objects.size();
|
||||||
|
|
||||||
/// @todo Check how "in player view" should be determined.
|
/// @todo Check how "in player view" should be determined.
|
||||||
|
|
||||||
@ -121,16 +136,9 @@ std::vector<GameObject*> TrafficDirector::populateNearby(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = AIGraphNode::Pedestrian;
|
// Hardcoded cop Pedestrian
|
||||||
auto available = findAvailableNodes(type, camera, radius);
|
|
||||||
|
|
||||||
if (availablePeds <= 0) {
|
|
||||||
// We have already reached the limit of spawned traffic
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hardcoded cop Pedestrian
|
|
||||||
std::vector<uint16_t> peds = {1};
|
std::vector<uint16_t> peds = {1};
|
||||||
|
|
||||||
// Determine which zone the viewpoint is in
|
// Determine which zone the viewpoint is in
|
||||||
auto zone = world->data->findZoneAt(camera.position);
|
auto zone = world->data->findZoneAt(camera.position);
|
||||||
bool day = (world->state->basic.gameHour >= 8 &&
|
bool day = (world->state->basic.gameHour >= 8 &&
|
||||||
@ -139,34 +147,124 @@ std::vector<GameObject*> TrafficDirector::populateNearby(
|
|||||||
const auto& group = world->data->pedgroups.at(groupid);
|
const auto& group = world->data->pedgroups.at(groupid);
|
||||||
peds.insert(peds.end(), group.cbegin(), group.cend());
|
peds.insert(peds.end(), group.cbegin(), group.cend());
|
||||||
|
|
||||||
std::random_device rd;
|
// Vehicles for normal traffic @todo create correct vehicle list
|
||||||
std::default_random_engine re(rd());
|
static constexpr std::array<uint16_t, 32> cars = {90, 91, 92, 94, 95, 97, 98, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
|
||||||
std::uniform_int_distribution<> d(0, peds.size() - 1);
|
111, 112, 116, 119, 128, 129, 130, 134, 135, 136, 138, 139, 144, 146};
|
||||||
const glm::vec3 kSpawnOffset{0.f, 0.f, 1.f};
|
|
||||||
|
|
||||||
int counter = availablePeds;
|
auto availablePedsNodes = findAvailableNodes(AIGraphNode::Pedestrian, camera, radius);
|
||||||
// maxSpawn can be -1 for "as many as possible"
|
|
||||||
if (maxSpawn > -1) {
|
// We have not reached the limit of spawned pedestrians
|
||||||
counter = std::min(availablePeds, maxSpawn);
|
if (availablePeds > 0) {
|
||||||
|
static const glm::vec3 kSpawnOffset{0.f, 0.f, 1.f};
|
||||||
|
|
||||||
|
int counter = availablePeds;
|
||||||
|
// maxSpawn can be -1 for "as many as possible"
|
||||||
|
if (maxSpawn > -1) {
|
||||||
|
counter = std::min(availablePeds, maxSpawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AIGraphNode* spawn : availablePedsNodes) {
|
||||||
|
if (spawn->type != AIGraphNode::Pedestrian) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter > -1) {
|
||||||
|
if (counter <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn a pedestrian from the available pool
|
||||||
|
const uint16_t pedId = peds[std::uniform_int_distribution<>(
|
||||||
|
0, peds.size() - 1)(random)];
|
||||||
|
auto ped = world->createPedestrian(pedId,
|
||||||
|
spawn->position + kSpawnOffset);
|
||||||
|
ped->setLifetime(GameObject::TrafficLifetime);
|
||||||
|
ped->controller->setGoal(CharacterController::TrafficWander);
|
||||||
|
created.push_back(ped);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AIGraphNode* spawn : available) {
|
auto availableVehicleNodes = findAvailableNodes(AIGraphNode::Vehicle, camera, radius);
|
||||||
if (spawn->type != AIGraphNode::Pedestrian) {
|
|
||||||
continue;
|
// We have not reached the limit of spawned vehicles
|
||||||
}
|
if (availableCars > 0) {
|
||||||
if (counter > -1) {
|
static const glm::vec3 kSpawnOffset{0.f, 0.f, 1.f};
|
||||||
if (counter == 0) {
|
|
||||||
break;
|
int counter = availableCars;
|
||||||
}
|
// maxSpawn can be -1 for "as many as possible"
|
||||||
counter--;
|
if (maxSpawn > -1) {
|
||||||
|
counter = std::min(availableCars, maxSpawn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawn a pedestrian from the available pool
|
|
||||||
auto ped = world->createPedestrian(peds[d(re)],
|
for (AIGraphNode* spawn : availableVehicleNodes) {
|
||||||
spawn->position + kSpawnOffset);
|
if (spawn->type != AIGraphNode::Vehicle) {
|
||||||
ped->setLifetime(GameObject::TrafficLifetime);
|
continue;
|
||||||
ped->controller->setGoal(CharacterController::TrafficWander);
|
}
|
||||||
created.push_back(ped);
|
|
||||||
|
if (counter > -1) {
|
||||||
|
if (counter <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next node, to spawn in between
|
||||||
|
AIGraphNode* next = spawn->connections.at(0);
|
||||||
|
|
||||||
|
// Set the spawn point to the middle of the two nodes
|
||||||
|
const glm::vec3 diff = (spawn->position - next->position) / 2.f;
|
||||||
|
|
||||||
|
// Calculate the orientation of the vehicle
|
||||||
|
glm::mat4 rotMat = glm::lookAt(next->position, spawn->position, glm::vec3(0,0,1));
|
||||||
|
const glm::mat4 rotate =
|
||||||
|
glm::rotate(glm::radians(90.f), glm::vec3(1, 0, 0));
|
||||||
|
rotMat = rotate * rotMat;
|
||||||
|
|
||||||
|
const glm::quat orientation = glm::conjugate(glm::toQuat(rotMat));
|
||||||
|
|
||||||
|
const glm::vec3 up = glm::vec3(0, 0, 1);
|
||||||
|
const glm::vec3 dir =
|
||||||
|
glm::normalize(next->position - spawn->position);
|
||||||
|
|
||||||
|
// Calculate the strafe vector
|
||||||
|
const glm::vec3 strafe = glm::cross(up, dir);
|
||||||
|
|
||||||
|
// @todo we don't know the direction of the street, so for now, choose the smaller value
|
||||||
|
int maxLanes = spawn->rightLanes < spawn->leftLanes ? spawn->rightLanes : spawn->leftLanes;
|
||||||
|
|
||||||
|
// This street has no lanes, continue
|
||||||
|
if( maxLanes <= 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose a random lane
|
||||||
|
const int lane =
|
||||||
|
std::uniform_int_distribution<>(1, maxLanes)(random);
|
||||||
|
const glm::vec3 laneOffset =
|
||||||
|
strafe * (2.5f + 5.f * static_cast<float>(lane - 1));
|
||||||
|
|
||||||
|
// Spawn a vehicle from the available pool
|
||||||
|
const uint16_t carId = cars[std::uniform_int_distribution<>(
|
||||||
|
0, cars.size() - 1)(random)];
|
||||||
|
auto vehicle = world->createVehicle(carId, next->position + kSpawnOffset + diff + laneOffset, orientation);
|
||||||
|
vehicle->setLifetime(GameObject::TrafficLifetime);
|
||||||
|
vehicle->setHandbraking(false);
|
||||||
|
|
||||||
|
// Spawn a pedestrian and put it into the vehicle
|
||||||
|
int pedId = peds[std::uniform_int_distribution<>(0, peds.size() - 1)(random)];
|
||||||
|
CharacterObject* character = world->createPedestrian(pedId, vehicle->getPosition());
|
||||||
|
character->setLifetime(GameObject::TrafficLifetime);
|
||||||
|
character->setCurrentVehicle(vehicle, 0);
|
||||||
|
character->controller->setGoal(CharacterController::TrafficDriver);
|
||||||
|
character->controller->setLane(lane);
|
||||||
|
vehicle->setOccupant(0, character);
|
||||||
|
|
||||||
|
created.push_back(character);
|
||||||
|
created.push_back(vehicle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find places it's legal to spawn things
|
// Find places it's legal to spawn things
|
||||||
|
@ -16,8 +16,8 @@ struct PathNode {
|
|||||||
int32_t next;
|
int32_t next;
|
||||||
glm::vec3 position{};
|
glm::vec3 position{};
|
||||||
float size;
|
float size;
|
||||||
int other_thing;
|
int leftLanes;
|
||||||
int other_thing2;
|
int rightLanes;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathData {
|
struct PathData {
|
||||||
|
@ -232,10 +232,10 @@ bool LoaderIDE::load(const std::string &filename, const PedStatsList &stats) {
|
|||||||
node.size = strtof(buff.c_str(), nullptr) / 16.f;
|
node.size = strtof(buff.c_str(), nullptr) / 16.f;
|
||||||
|
|
||||||
getline(buffstream, buff, ',');
|
getline(buffstream, buff, ',');
|
||||||
node.other_thing = atoi(buff.c_str());
|
node.leftLanes = atoi(buff.c_str());
|
||||||
|
|
||||||
getline(buffstream, buff, ',');
|
getline(buffstream, buff, ',');
|
||||||
node.other_thing2 = atoi(buff.c_str());
|
node.rightLanes = atoi(buff.c_str());
|
||||||
|
|
||||||
path.nodes.push_back(node);
|
path.nodes.push_back(node);
|
||||||
}
|
}
|
||||||
|
@ -551,8 +551,18 @@ float VehicleObject::getHealth() const {
|
|||||||
return health;
|
return health;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VehicleObject::setSteeringAngle(float a) {
|
void VehicleObject::setSteeringAngle(float a, bool force) {
|
||||||
steerAngle = a;
|
steerAngle = a;
|
||||||
|
|
||||||
|
if (force && physVehicle) {
|
||||||
|
for (int w = 0; w < physVehicle->getNumWheels(); ++w) {
|
||||||
|
btWheelInfo& wi = physVehicle->getWheelInfo(w);
|
||||||
|
|
||||||
|
if (wi.m_bIsFrontWheel) {
|
||||||
|
physVehicle->setSteeringValue(a, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float VehicleObject::getSteeringAngle() const {
|
float VehicleObject::getSteeringAngle() const {
|
||||||
@ -595,7 +605,7 @@ void VehicleObject::ejectAll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameObject* VehicleObject::getOccupant(size_t seat) {
|
GameObject* VehicleObject::getOccupant(size_t seat) const {
|
||||||
auto it = seatOccupants.find(seat);
|
auto it = seatOccupants.find(seat);
|
||||||
if (it != seatOccupants.end()) {
|
if (it != seatOccupants.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
@ -623,6 +633,10 @@ bool VehicleObject::isOccupantDriver(size_t seat) const {
|
|||||||
return seat == 0;
|
return seat == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CharacterObject* VehicleObject::getDriver() const {
|
||||||
|
return static_cast<CharacterObject*>(getOccupant(0));
|
||||||
|
}
|
||||||
|
|
||||||
VehicleObject::Part* VehicleObject::getSeatEntryDoor(size_t seat) {
|
VehicleObject::Part* VehicleObject::getSeatEntryDoor(size_t seat) {
|
||||||
auto pos = info->seats[seat].offset + glm::vec3(0.f, 0.5f, 0.f);
|
auto pos = info->seats[seat].offset + glm::vec3(0.f, 0.5f, 0.f);
|
||||||
Part* nearestDoor = nullptr;
|
Part* nearestDoor = nullptr;
|
||||||
@ -905,3 +919,66 @@ void VehicleObject::grantOccupantRewards(CharacterObject* character) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float VehicleObject::isInFront(const glm::vec3& point) {
|
||||||
|
// The point we need to test
|
||||||
|
glm::vec3 testPoint;
|
||||||
|
|
||||||
|
testPoint = point - getPosition();
|
||||||
|
|
||||||
|
// The two endpoints of the line
|
||||||
|
glm::vec3 v1;
|
||||||
|
glm::vec3 v2;
|
||||||
|
|
||||||
|
static const glm::vec3 up = glm::vec3(0.f, 0.f, 1.f);
|
||||||
|
const glm::vec3 dir =
|
||||||
|
glm::normalize(getRotation() * glm::vec3(0.f, 1.f, 0.f));
|
||||||
|
|
||||||
|
// Calculate the strafe vector
|
||||||
|
glm::vec3 strafe = glm::cross(up, dir);
|
||||||
|
|
||||||
|
v1 = strafe;
|
||||||
|
v2 = -strafe;
|
||||||
|
|
||||||
|
// Check if the point is behind or in front the car
|
||||||
|
|
||||||
|
glm::vec3 normal(v1.y - v2.y, 0, v2.x - v1.x);
|
||||||
|
normal = glm::normalize(normal);
|
||||||
|
|
||||||
|
const glm::vec3 vecTemp(testPoint.x - v1.x, 0, testPoint.y - v1.y);
|
||||||
|
double distance = glm::dot(vecTemp, normal);
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
float VehicleObject::isOnSide(const glm::vec3& point) {
|
||||||
|
// The point we need to test
|
||||||
|
glm::vec3 testPoint;
|
||||||
|
|
||||||
|
testPoint = point - getPosition();
|
||||||
|
|
||||||
|
// The two endpoints of the line
|
||||||
|
glm::vec3 v1;
|
||||||
|
glm::vec3 v2;
|
||||||
|
|
||||||
|
static const glm::vec3 up = glm::vec3(0.f, 0.f, 1.f);
|
||||||
|
const glm::vec3 dir =
|
||||||
|
glm::normalize(getRotation() * glm::vec3(1.f, 0.f, 0.f));
|
||||||
|
|
||||||
|
// Calculate the strafe vector
|
||||||
|
glm::vec3 strafe = glm::cross(up, dir);
|
||||||
|
|
||||||
|
v1 = strafe;
|
||||||
|
v2 = -strafe;
|
||||||
|
|
||||||
|
// Check if the point is behind or in front the car
|
||||||
|
|
||||||
|
glm::vec3 normal(v1.y - v2.y, 0, v2.x - v1.x);
|
||||||
|
normal = glm::normalize(normal);
|
||||||
|
|
||||||
|
const glm::vec3 vecTemp(testPoint.x - v1.x, 0, testPoint.y - v1.y);
|
||||||
|
double distance = glm::dot(vecTemp, normal);
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ public:
|
|||||||
|
|
||||||
void setExtraEnabled(size_t extra, bool enabled);
|
void setExtraEnabled(size_t extra, bool enabled);
|
||||||
|
|
||||||
void setSteeringAngle(float);
|
void setSteeringAngle(float, bool=false);
|
||||||
|
|
||||||
float getSteeringAngle() const;
|
float getSteeringAngle() const;
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ public:
|
|||||||
|
|
||||||
void ejectAll();
|
void ejectAll();
|
||||||
|
|
||||||
GameObject* getOccupant(size_t seat);
|
GameObject* getOccupant(size_t seat) const;
|
||||||
|
|
||||||
void setOccupant(size_t seat, GameObject* occupant);
|
void setOccupant(size_t seat, GameObject* occupant);
|
||||||
|
|
||||||
@ -151,6 +151,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool isOccupantDriver(size_t seat) const;
|
bool isOccupantDriver(size_t seat) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief getDriver
|
||||||
|
* @return CharacterObject* if there is a driver
|
||||||
|
*/
|
||||||
|
CharacterObject* getDriver() const;
|
||||||
|
|
||||||
glm::vec3 getSeatEntryPosition(size_t seat) const {
|
glm::vec3 getSeatEntryPosition(size_t seat) const {
|
||||||
auto pos = info->seats[seat].offset;
|
auto pos = info->seats[seat].offset;
|
||||||
pos -= glm::vec3(glm::sign(pos.x) * -0.81756252f, 0.34800607f,
|
pos -= glm::vec3(glm::sign(pos.x) * -0.81756252f, 0.34800607f,
|
||||||
@ -192,6 +198,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool collectSpecial();
|
bool collectSpecial();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief isInFront
|
||||||
|
* @return a positive distance when the point is in front of the car
|
||||||
|
* and a negative distance when the point is behind the car
|
||||||
|
*/
|
||||||
|
float isInFront(const glm::vec3& point);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief collectSpecial
|
||||||
|
* @return a positive distance when the point is at the right side of the car
|
||||||
|
* and a negative distance when the point is at the left side of the car
|
||||||
|
*/
|
||||||
|
float isOnSide(const glm::vec3& point);
|
||||||
|
|
||||||
|
|
||||||
void grantOccupantRewards(CharacterObject* character);
|
void grantOccupantRewards(CharacterObject* character);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -240,22 +240,20 @@ inline void getClosestNode(const ScriptArguments& args, ScriptVec3& coord, AIGra
|
|||||||
coord = script::getGround(args, coord);
|
coord = script::getGround(args, coord);
|
||||||
float closest = 10000.f;
|
float closest = 10000.f;
|
||||||
std::vector<AIGraphNode*> nodes;
|
std::vector<AIGraphNode*> nodes;
|
||||||
args.getWorld()->aigraph.gatherExternalNodesNear(coord, closest, nodes);
|
args.getWorld()->aigraph.gatherExternalNodesNear(coord, closest, nodes, type);
|
||||||
|
|
||||||
for (const auto &node : nodes) {
|
for (const auto &node : nodes) {
|
||||||
if (node->type == type) {
|
// This is how the original game calculates distance,
|
||||||
// This is how the original game calculates distance,
|
// weighted manhattan-distance where the vertical distance
|
||||||
// weighted manhattan-distance where the vertical distance
|
// has to be 3x as close to be considered.
|
||||||
// has to be 3x as close to be considered.
|
const float dist = std::abs(coord.x - node->position.x) +
|
||||||
float dist = std::abs(coord.x - node->position.x);
|
std::abs(coord.y - node->position.y) +
|
||||||
dist += std::abs(coord.y - node->position.y);
|
std::abs(coord.z - node->position.z) * 3.f;
|
||||||
dist += std::abs(coord.z - node->position.z) * 3.f;
|
if (dist < closest) {
|
||||||
if (dist < closest) {
|
closest = dist;
|
||||||
closest = dist;
|
xCoord = node->position.x;
|
||||||
xCoord = node->position.x;
|
yCoord = node->position.y;
|
||||||
yCoord = node->position.y;
|
zCoord = node->position.z;
|
||||||
zCoord = node->position.z;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2057,9 +2057,16 @@ void opcode_00ad(const ScriptArguments& args, const ScriptVehicle vehicle, const
|
|||||||
*/
|
*/
|
||||||
void opcode_00ae(const ScriptArguments& args, const ScriptVehicle vehicle, const ScriptDrivingMode arg2) {
|
void opcode_00ae(const ScriptArguments& args, const ScriptVehicle vehicle, const ScriptDrivingMode arg2) {
|
||||||
RW_UNIMPLEMENTED_OPCODE(0x00ae);
|
RW_UNIMPLEMENTED_OPCODE(0x00ae);
|
||||||
RW_UNUSED(vehicle);
|
|
||||||
RW_UNUSED(arg2);
|
RW_UNUSED(arg2);
|
||||||
RW_UNUSED(args);
|
RW_UNUSED(args);
|
||||||
|
|
||||||
|
// Check whether we have a driver
|
||||||
|
if (vehicle->getDriver() != nullptr)
|
||||||
|
{
|
||||||
|
// @todo set the right driving style and lane
|
||||||
|
vehicle->getDriver()->controller->setGoal(CharacterController::TrafficDriver);
|
||||||
|
vehicle->getDriver()->controller->setLane(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -754,6 +754,23 @@ void RWGame::renderDebugPaths(float time) {
|
|||||||
debug.drawLine(position, position + left, color);
|
debug.drawLine(position, position + left, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw the targetNode if a character is driving a vehicle
|
||||||
|
for (auto& p : world->pedestrianPool.objects) {
|
||||||
|
auto v = static_cast<CharacterObject*>(p.second);
|
||||||
|
|
||||||
|
static const btVector3 color(1.f, 1.f, 0.f);
|
||||||
|
|
||||||
|
if (v->controller->targetNode && v->getCurrentVehicle()) {
|
||||||
|
const glm::vec3 pos1 = v->getPosition();
|
||||||
|
const glm::vec3 pos2 = v->controller->targetNode->position;
|
||||||
|
|
||||||
|
btVector3 position1(pos1.x, pos1.y, pos1.z);
|
||||||
|
btVector3 position2(pos2.x, pos2.y, pos2.z);
|
||||||
|
|
||||||
|
debug.drawLine(position1, position2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug.flush(&renderer);
|
debug.flush(&renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user