1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-07 11:22:45 +01:00

clang-format files in rwengine/src/ai

This commit is contained in:
Daniel Evans 2016-09-09 21:13:18 +01:00
parent 9aa3af6703
commit d5e853d23f
11 changed files with 1018 additions and 1051 deletions

View File

@ -1,116 +1,116 @@
#include "ai/AIGraph.hpp" #include "ai/AIGraph.hpp"
#include <objects/GameObject.hpp>
#include <ai/AIGraphNode.hpp> #include <ai/AIGraphNode.hpp>
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include <iostream> #include <iostream>
#include <objects/GameObject.hpp>
AIGraph::~AIGraph() AIGraph::~AIGraph() {
{ for (auto n : nodes) {
for( auto n : nodes ) { delete n;
delete n; }
}
} }
void AIGraph::createPathNodes(const glm::vec3& position, const glm::quat& rotation, PathData& path) void AIGraph::createPathNodes(const glm::vec3& position,
{ const glm::quat& rotation, PathData& path) {
size_t startIndex = nodes.size(); size_t startIndex = nodes.size();
std::vector<AIGraphNode*> pathNodes; std::vector<AIGraphNode*> pathNodes;
pathNodes.reserve(path.nodes.size()); pathNodes.reserve(path.nodes.size());
for( size_t n = 0; n < path.nodes.size(); ++n ) { for (size_t n = 0; n < path.nodes.size(); ++n) {
auto& node = path.nodes[n]; auto& node = path.nodes[n];
AIGraphNode* ainode = nullptr; AIGraphNode* ainode = nullptr;
glm::vec3 nodePosition = position + (rotation * node.position); glm::vec3 nodePosition = position + (rotation * node.position);
if( node.type == PathNode::EXTERNAL ) { if (node.type == PathNode::EXTERNAL) {
for( size_t rn = 0; rn < externalNodes.size(); ++rn ) { for (size_t rn = 0; rn < externalNodes.size(); ++rn) {
auto& realNode = externalNodes[rn]; auto& realNode = externalNodes[rn];
auto d = glm::distance2(realNode->position, nodePosition); auto d = glm::distance2(realNode->position, nodePosition);
if( d < 1.f ) { if (d < 1.f) {
pathNodes.push_back(realNode); pathNodes.push_back(realNode);
ainode = realNode; ainode = realNode;
break; break;
} }
} }
} }
if( ainode == nullptr ) {
ainode = new AIGraphNode;
ainode->type = (path.type == PathData::PATH_PED ? AIGraphNode::Pedestrian : AIGraphNode::Vehicle);
ainode->nextIndex = node.next >= 0 ? startIndex + node.next : -1;
ainode->flags = AIGraphNode::None;
ainode->size = node.size;
ainode->other_thing = node.other_thing;
ainode->other_thing2 = node.other_thing2;
ainode->position = nodePosition;
ainode->external = node.type == PathNode::EXTERNAL;
ainode->disabled = false;
pathNodes.push_back(ainode); if (ainode == nullptr) {
nodes.push_back(ainode); ainode = new AIGraphNode;
ainode->type =
(path.type == PathData::PATH_PED ? AIGraphNode::Pedestrian
: AIGraphNode::Vehicle);
ainode->nextIndex = node.next >= 0 ? startIndex + node.next : -1;
ainode->flags = AIGraphNode::None;
ainode->size = node.size;
ainode->other_thing = node.other_thing;
ainode->other_thing2 = node.other_thing2;
ainode->position = nodePosition;
ainode->external = node.type == PathNode::EXTERNAL;
ainode->disabled = false;
if( ainode->external ) pathNodes.push_back(ainode);
{ nodes.push_back(ainode);
externalNodes.push_back(ainode);
// Determine which grid cell this node falls into
float lowerCoord = -(WORLD_GRID_SIZE)/2.f;
auto gridrel = glm::vec2(ainode->position) - glm::vec2(lowerCoord, lowerCoord);
auto gridcoord = glm::floor(gridrel / glm::vec2(WORLD_CELL_SIZE));
if( gridcoord.x < 0 || gridcoord.y < 0 || gridcoord.x >= WORLD_GRID_WIDTH || gridcoord.y >= WORLD_GRID_WIDTH )
{
std::cout << "Warning: Node outside of grid at coord " << gridcoord.x << " " << gridcoord.y << std::endl;
}
auto index = (gridcoord.x * WORLD_GRID_WIDTH) + gridcoord.y;
gridNodes[index].push_back(ainode);
}
}
}
for(size_t pn = 0; pn < path.nodes.size(); ++pn) { if (ainode->external) {
if(path.nodes[pn].next >= 0 && (unsigned) path.nodes[pn].next < pathNodes.size()) { externalNodes.push_back(ainode);
auto node = pathNodes[pn];
auto next = pathNodes[path.nodes[pn].next]; // Determine which grid cell this node falls into
float lowerCoord = -(WORLD_GRID_SIZE) / 2.f;
node->connections.push_back(next); auto gridrel = glm::vec2(ainode->position) -
next->connections.push_back(node); glm::vec2(lowerCoord, lowerCoord);
} auto gridcoord =
} glm::floor(gridrel / glm::vec2(WORLD_CELL_SIZE));
if (gridcoord.x < 0 || gridcoord.y < 0 ||
gridcoord.x >= WORLD_GRID_WIDTH ||
gridcoord.y >= WORLD_GRID_WIDTH) {
std::cout << "Warning: Node outside of grid at coord "
<< gridcoord.x << " " << gridcoord.y << std::endl;
}
auto index = (gridcoord.x * WORLD_GRID_WIDTH) + gridcoord.y;
gridNodes[index].push_back(ainode);
}
}
}
for (size_t pn = 0; pn < path.nodes.size(); ++pn) {
if (path.nodes[pn].next >= 0 &&
(unsigned)path.nodes[pn].next < pathNodes.size()) {
auto node = pathNodes[pn];
auto next = pathNodes[path.nodes[pn].next];
node->connections.push_back(next);
next->connections.push_back(node);
}
}
} }
glm::ivec2 worldToGrid(const glm::vec2& world) glm::ivec2 worldToGrid(const glm::vec2& world) {
{ static const float lowerCoord = -(WORLD_GRID_SIZE) / 2.f;
static const float lowerCoord = -(WORLD_GRID_SIZE)/2.f; return glm::ivec2((world - glm::vec2(lowerCoord)) /
return glm::ivec2((world - glm::vec2(lowerCoord)) / glm::vec2(WORLD_CELL_SIZE)); glm::vec2(WORLD_CELL_SIZE));
} }
void AIGraph::gatherExternalNodesNear(const glm::vec3& center, const float radius, std::vector< AIGraphNode* >& nodes) void AIGraph::gatherExternalNodesNear(const glm::vec3& center,
{ const float radius,
// the bounds end up covering more than might fit std::vector<AIGraphNode*>& nodes) {
auto planecoords = glm::vec2(center); // the bounds end up covering more than might fit
auto minWorld = planecoords - glm::vec2(radius); auto planecoords = glm::vec2(center);
auto maxWorld = planecoords + glm::vec2(radius); auto minWorld = planecoords - glm::vec2(radius);
auto minGrid = worldToGrid(minWorld); auto maxWorld = planecoords + glm::vec2(radius);
auto maxGrid = worldToGrid(maxWorld); auto minGrid = worldToGrid(minWorld);
auto maxGrid = worldToGrid(maxWorld);
for( int x = minGrid.x; x <= maxGrid.x; ++x ) for (int x = minGrid.x; x <= maxGrid.x; ++x) {
{ for (int y = minGrid.y; y <= maxGrid.y; ++y) {
for( int y = minGrid.y; y <= maxGrid.y; ++y ) int i = (x * WORLD_GRID_WIDTH) + y;
{ if (i < 0 || i >= (int)gridNodes.size()) {
int i = (x * WORLD_GRID_WIDTH) + y; continue;
if( i < 0 || i >= (int)gridNodes.size() ) }
{ auto& external = gridNodes[i];
continue; for (AIGraphNode* node : external) {
} if (glm::distance2(center, node->position) < radius * radius) {
auto& external = gridNodes[i]; nodes.push_back(node);
for ( AIGraphNode* node : external ) }
{ }
if ( glm::distance2( center, node->position ) < radius*radius ) }
{ }
nodes.push_back( node );
}
}
}
}
} }

View File

@ -1,37 +1,37 @@
#pragma once #pragma once
#ifndef _AIGRAPH_HPP_ #ifndef _AIGRAPH_HPP_
#define _AIGRAPH_HPP_ #define _AIGRAPH_HPP_
#include <vector>
#include <glm/gtc/quaternion.hpp>
#include <data/PathData.hpp>
#include <array> #include <array>
#include <data/PathData.hpp>
#include <glm/gtc/quaternion.hpp>
#include <rw/types.hpp> #include <rw/types.hpp>
#include <vector>
struct AIGraphNode; struct AIGraphNode;
class AIGraph class AIGraph {
{
public: public:
~AIGraph();
~AIGraph(); std::vector<AIGraphNode*> nodes;
std::vector<AIGraphNode*> nodes; /**
* List of external nodes, which are links between each
* Instance's paths and where new pedestrians and vehicles
* are spawned
*/
std::vector<AIGraphNode*> externalNodes;
/** /**
* List of external nodes, which are links between each * Stores the external AI Grid Nodes organised by world grid cell
* Instance's paths and where new pedestrians and vehicles */
* are spawned std::array<std::vector<AIGraphNode*>, WORLD_GRID_CELLS> gridNodes;
*/
std::vector<AIGraphNode*> externalNodes;
/** void createPathNodes(const glm::vec3& position, const glm::quat& rotation,
* Stores the external AI Grid Nodes organised by world grid cell PathData& path);
*/
std::array<std::vector<AIGraphNode*>,WORLD_GRID_CELLS> gridNodes;
void createPathNodes(const glm::vec3& position, const glm::quat& rotation, PathData& path); void gatherExternalNodesNear(const glm::vec3& center, const float radius,
std::vector<AIGraphNode*>& nodes);
void gatherExternalNodesNear(const glm::vec3& center, const float radius, std::vector<AIGraphNode*>& nodes);
}; };
#endif #endif

View File

@ -1,35 +1,32 @@
#pragma once #pragma once
#ifndef _AIGRAPHNODE_HPP_ #ifndef _AIGRAPHNODE_HPP_
#define _AIGRAPHNODE_HPP_ #define _AIGRAPHNODE_HPP_
#include <glm/glm.hpp>
#include <cstdint> #include <cstdint>
#include <glm/glm.hpp>
#include <vector> #include <vector>
struct AIGraphNode struct AIGraphNode {
{ enum NodeType { Vehicle, Pedestrian };
enum NodeType {
Vehicle,
Pedestrian
};
enum { enum {
None = 0, None = 0,
CrossesRoad = 1 /// No documentation for other flags yet, but this is mentioned. CrossesRoad =
1 /// No documentation for other flags yet, but this is mentioned.
}; };
NodeType type; NodeType type;
glm::vec3 position; glm::vec3 position;
float size; float size;
int other_thing; int other_thing;
int other_thing2; int other_thing2;
bool external; bool external;
uint8_t flags; uint8_t flags;
int32_t nextIndex; int32_t nextIndex;
bool disabled; bool disabled;
std::vector<AIGraphNode*> connections; std::vector<AIGraphNode*> connections;
}; };
#endif #endif

View File

@ -1,7 +1,7 @@
#include <btBulletDynamicsCommon.h>
#include <ai/CharacterController.hpp> #include <ai/CharacterController.hpp>
#include <objects/CharacterObject.hpp> #include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp> #include <objects/VehicleObject.hpp>
#include <btBulletDynamicsCommon.h>
#include <data/Model.hpp> #include <data/Model.hpp>
#include <engine/Animator.hpp> #include <engine/Animator.hpp>
@ -10,504 +10,477 @@
constexpr float kCloseDoorIdleTime = 2.f; constexpr float kCloseDoorIdleTime = 2.f;
CharacterController::CharacterController(CharacterObject* character) CharacterController::CharacterController(CharacterObject *character)
: character(character) : character(character)
, _currentActivity(nullptr) , _currentActivity(nullptr)
, _nextActivity(nullptr) , _nextActivity(nullptr)
, m_closeDoorTimer(0.f) , m_closeDoorTimer(0.f)
, currentGoal(None) , currentGoal(None)
, leader(nullptr) , leader(nullptr)
, targetNode(nullptr) , targetNode(nullptr) {
{ character->controller = this;
character->controller = this;
} }
bool CharacterController::updateActivity() bool CharacterController::updateActivity() {
{ if (_currentActivity && character->isAlive()) {
if( _currentActivity && character->isAlive() ) { return _currentActivity->update(character, this);
return _currentActivity->update(character, this); }
}
return false; return false;
} }
void CharacterController::setActivity(CharacterController::Activity* activity) void CharacterController::setActivity(CharacterController::Activity *activity) {
{ if (_currentActivity) delete _currentActivity;
if( _currentActivity ) delete _currentActivity; _currentActivity = activity;
_currentActivity = activity;
} }
void CharacterController::skipActivity() void CharacterController::skipActivity() {
{ // Some activities can't be cancelled, such as the final phase of entering a
// Some activities can't be cancelled, such as the final phase of entering a vehicle // vehicle
// or jumping. // or jumping.
if (getCurrentActivity() != nullptr && if (getCurrentActivity() != nullptr &&
getCurrentActivity()->canSkip(character, this)) getCurrentActivity()->canSkip(character, this))
setActivity(nullptr); setActivity(nullptr);
} }
void CharacterController::setNextActivity(CharacterController::Activity* activity) void CharacterController::setNextActivity(
{ CharacterController::Activity *activity) {
if( _currentActivity == nullptr ) { if (_currentActivity == nullptr) {
setActivity(activity); setActivity(activity);
_nextActivity = nullptr; _nextActivity = nullptr;
} } else {
else { if (_nextActivity) delete _nextActivity;
if(_nextActivity) delete _nextActivity; _nextActivity = activity;
_nextActivity = activity; }
}
} }
bool CharacterController::isCurrentActivity(const std::string& activity) const bool CharacterController::isCurrentActivity(const std::string &activity) const {
{ if (getCurrentActivity() == nullptr) return false;
if (getCurrentActivity() == nullptr) return false; return getCurrentActivity()->name() == activity;
return getCurrentActivity()->name() == activity;
} }
void CharacterController::update(float dt) void CharacterController::update(float dt) {
{ if (character->getCurrentVehicle()) {
if( character->getCurrentVehicle() ) { // Nevermind, the player is in a vehicle.
// Nevermind, the player is in a vehicle.
auto& d = character->getMovement(); auto &d = character->getMovement();
if( character->getCurrentSeat() == 0 ) if (character->getCurrentSeat() == 0) {
{ character->getCurrentVehicle()->setSteeringAngle(d.y);
character->getCurrentVehicle()->setSteeringAngle(d.y);
if( std::abs(d.x) > 0.01f )
{
character->getCurrentVehicle()->setHandbraking(false);
}
character->getCurrentVehicle()->setThrottle(d.x);
}
if( _currentActivity == nullptr ) { if (std::abs(d.x) > 0.01f) {
// If character is idle in vehicle, try to close the door. character->getCurrentVehicle()->setHandbraking(false);
auto v = character->getCurrentVehicle(); }
auto entryDoor = v->getSeatEntryDoor(character->getCurrentSeat()); character->getCurrentVehicle()->setThrottle(d.x);
}
if (entryDoor && entryDoor->constraint) { if (_currentActivity == nullptr) {
if (glm::length( d ) <= 0.1f) { // If character is idle in vehicle, try to close the door.
if (m_closeDoorTimer >= kCloseDoorIdleTime) { auto v = character->getCurrentVehicle();
character->getCurrentVehicle()->setPartTarget(entryDoor, true, entryDoor->closedAngle); auto entryDoor = v->getSeatEntryDoor(character->getCurrentSeat());
}
m_closeDoorTimer += dt;
}
else {
m_closeDoorTimer = 0.f;
}
}
}
}
else
{
m_closeDoorTimer = 0.f;
}
if( updateActivity() ) { if (entryDoor && entryDoor->constraint) {
character->activityFinished(); if (glm::length(d) <= 0.1f) {
if( _currentActivity ) { if (m_closeDoorTimer >= kCloseDoorIdleTime) {
delete _currentActivity; character->getCurrentVehicle()->setPartTarget(
_currentActivity = nullptr; entryDoor, true, entryDoor->closedAngle);
} }
if( _nextActivity ) { m_closeDoorTimer += dt;
setActivity( _nextActivity ); } else {
_nextActivity = nullptr; m_closeDoorTimer = 0.f;
} }
} }
}
} else {
m_closeDoorTimer = 0.f;
}
if (updateActivity()) {
character->activityFinished();
if (_currentActivity) {
delete _currentActivity;
_currentActivity = nullptr;
}
if (_nextActivity) {
setActivity(_nextActivity);
_nextActivity = nullptr;
}
}
} }
CharacterObject *CharacterController::getCharacter() const CharacterObject *CharacterController::getCharacter() const {
{ return character;
return character;
} }
void CharacterController::setMoveDirection(const glm::vec3 &movement) void CharacterController::setMoveDirection(const glm::vec3 &movement) {
{ character->setMovement(movement);
character->setMovement(movement);
} }
void CharacterController::setLookDirection(const glm::vec2 &look) void CharacterController::setLookDirection(const glm::vec2 &look) {
{ character->setLook(look);
character->setLook(look);
} }
void CharacterController::setRunning(bool run) void CharacterController::setRunning(bool run) {
{ character->setRunning(run);
character->setRunning(run);
} }
bool Activities::GoTo::update(CharacterObject *character,
CharacterController *controller) {
/* TODO: Use the ai nodes to navigate to the position */
auto cpos = character->getPosition();
glm::vec3 targetDirection = target - cpos;
bool Activities::GoTo::update(CharacterObject *character, CharacterController *controller) // Ignore vertical axis for the sake of simplicity.
{ if (glm::length(glm::vec2(targetDirection)) < 0.1f) {
/* TODO: Use the ai nodes to navigate to the position */ character->setPosition(glm::vec3(glm::vec2(target), cpos.z));
auto cpos = character->getPosition(); controller->setMoveDirection({0.f, 0.f, 0.f});
glm::vec3 targetDirection = target - cpos; character->controller->setRunning(false);
return true;
}
// Ignore vertical axis for the sake of simplicity. float hdg =
if( glm::length(glm::vec2(targetDirection)) < 0.1f ) { atan2(targetDirection.y, targetDirection.x) - glm::half_pi<float>();
character->setPosition(glm::vec3(glm::vec2(target), cpos.z)); character->setHeading(glm::degrees(hdg));
controller->setMoveDirection({0.f, 0.f, 0.f});
character->controller->setRunning(false);
return true;
}
float hdg = atan2(targetDirection.y, targetDirection.x) - glm::half_pi<float>(); controller->setMoveDirection({1.f, 0.f, 0.f});
character->setHeading(glm::degrees(hdg)); controller->setRunning(sprint);
controller->setMoveDirection({1.f, 0.f, 0.f}); return false;
controller->setRunning(sprint);
return false;
} }
bool Activities::Jump::update(CharacterObject* character, CharacterController* controller) bool Activities::Jump::update(CharacterObject *character,
{ CharacterController *controller) {
RW_UNUSED(controller); RW_UNUSED(controller);
if (character->physCharacter == nullptr) return true; if (character->physCharacter == nullptr) return true;
if( !jumped ) if (!jumped) {
{ character->jump();
character->jump(); jumped = true;
jumped = true; } else {
} if (character->physCharacter->canJump()) {
else return true;
{ }
if (character->physCharacter->canJump()) { }
return true;
} return false;
}
return false;
} }
bool Activities::EnterVehicle::canSkip(CharacterObject *character, CharacterController *) const bool Activities::EnterVehicle::canSkip(CharacterObject *character,
{ CharacterController *) const {
// If we're already inside the vehicle, it can't helped. // If we're already inside the vehicle, it can't helped.
return character->getCurrentVehicle() == nullptr; return character->getCurrentVehicle() == nullptr;
} }
bool Activities::EnterVehicle::update(CharacterObject *character, CharacterController *controller) bool Activities::EnterVehicle::update(CharacterObject *character,
{ CharacterController *controller) {
constexpr float kSprintToEnterDistance = 5.f; constexpr float kSprintToEnterDistance = 5.f;
constexpr float kGiveUpDistance = 100.f; constexpr float kGiveUpDistance = 100.f;
RW_UNUSED(controller); RW_UNUSED(controller);
// Boats don't have any kind of entry animation unless you're onboard. // Boats don't have any kind of entry animation unless you're onboard.
if( vehicle->vehicle->type == VehicleData::BOAT ) { if (vehicle->vehicle->type == VehicleData::BOAT) {
character->enterVehicle(vehicle, seat); character->enterVehicle(vehicle, seat);
return true; return true;
} }
if( seat == ANY_SEAT )
{
// Determine which seat to take.
float nearest = std::numeric_limits<float>::max();
for(unsigned int s = 1; s < vehicle->info->seats.size(); ++s)
{
auto entry = vehicle->getSeatEntryPositionWorld(s);
float dist = glm::distance(entry, character->getPosition());
if( dist < nearest )
{
seat = s;
nearest = dist;
}
}
}
auto entryDoor = vehicle->getSeatEntryDoor(seat);
auto entryPos = vehicle->getSeatEntryPositionWorld(seat);
auto entryPosLocal = vehicle->getSeatEntryPosition(seat);
auto anm_open = character->animations.car_open_lhs; if (seat == ANY_SEAT) {
auto anm_enter = character->animations.car_getin_lhs; // Determine which seat to take.
auto anm_pullout = character->animations.car_pullout_lhs; float nearest = std::numeric_limits<float>::max();
for (unsigned int s = 1; s < vehicle->info->seats.size(); ++s) {
auto entry = vehicle->getSeatEntryPositionWorld(s);
float dist = glm::distance(entry, character->getPosition());
if (dist < nearest) {
seat = s;
nearest = dist;
}
}
}
if ( entryPosLocal.x > 0.f ) auto entryDoor = vehicle->getSeatEntryDoor(seat);
{ auto entryPos = vehicle->getSeatEntryPositionWorld(seat);
anm_open = character->animations.car_open_rhs; auto entryPosLocal = vehicle->getSeatEntryPosition(seat);
anm_enter = character->animations.car_getin_rhs;
anm_pullout = character->animations.car_pullout_rhs;
}
// If there's someone in this seat already, we may have to ask them to leave. auto anm_open = character->animations.car_open_lhs;
auto currentOccupant= static_cast<CharacterObject*>(vehicle->getOccupant(seat)); auto anm_enter = character->animations.car_getin_lhs;
auto anm_pullout = character->animations.car_pullout_lhs;
bool tryToEnter = false; if (entryPosLocal.x > 0.f) {
anm_open = character->animations.car_open_rhs;
if( entering ) { anm_enter = character->animations.car_getin_rhs;
if( character->animator->getAnimation(AnimIndexAction) == anm_open ) { anm_pullout = character->animations.car_pullout_rhs;
if( character->animator->isCompleted(AnimIndexAction) ) { }
tryToEnter = true;
}
else if( entryDoor && character->animator->getAnimationTime(AnimIndexAction) >= 0.5f )
{
vehicle->setPartTarget(entryDoor, true, entryDoor->openAngle);
}
else {
//character->setPosition(vehicle->getSeatEntryPosition(seat));
character->rotation = vehicle->getRotation();
}
}
else if (character->animator->getAnimation(AnimIndexAction) == anm_pullout) {
if (character->animator->isCompleted(AnimIndexAction)) {
tryToEnter = true;
}
}
else if( character->animator->getAnimation(AnimIndexAction) == anm_enter ) {
if( character->animator->isCompleted(AnimIndexAction) ) {
// VehicleGetIn is over, finish activity
return true;
}
}
}
else {
glm::vec3 targetDirection = entryPos - character->getPosition();
targetDirection.z = 0.f;
float targetDistance = glm::length(targetDirection); // If there's someone in this seat already, we may have to ask them to
// leave.
auto currentOccupant =
static_cast<CharacterObject *>(vehicle->getOccupant(seat));
if( targetDistance <= 0.4f ) { bool tryToEnter = false;
entering = true;
// Warp character to vehicle orientation
character->controller->setMoveDirection({0.f, 0.f, 0.f});
character->controller->setRunning(false);
character->setHeading(
glm::degrees(glm::roll(vehicle->getRotation())));
// Determine if the door open animation should be skipped.
if( entryDoor == nullptr || (entryDoor->constraint != nullptr && glm::abs(entryDoor->constraint->getHingeAngle()) >= 0.6f ) )
{
tryToEnter = true;
}
else
{
character->playActivityAnimation(anm_open, false, true);
}
}
else if (targetDistance > kGiveUpDistance) {
return true;
}
else {
if( targetDistance > kSprintToEnterDistance ) {
character->controller->setRunning(true);
}
character->setHeading(
glm::degrees(atan2(targetDirection.y, targetDirection.x) - glm::half_pi<float>()));
character->controller->setMoveDirection({1.f, 0.f, 0.f});
}
}
if (tryToEnter) { if (entering) {
if (currentOccupant != nullptr && currentOccupant != character) { if (character->animator->getAnimation(AnimIndexAction) == anm_open) {
// Play the pullout animation and tell the other character to get out. if (character->animator->isCompleted(AnimIndexAction)) {
character->playActivityAnimation(anm_pullout, false, true); tryToEnter = true;
currentOccupant->controller->setNextActivity(new Activities::ExitVehicle(true)); } else if (entryDoor &&
} character->animator->getAnimationTime(AnimIndexAction) >=
else { 0.5f) {
character->playActivityAnimation(anm_enter, false, true); vehicle->setPartTarget(entryDoor, true, entryDoor->openAngle);
character->enterVehicle(vehicle, seat); } else {
} // character->setPosition(vehicle->getSeatEntryPosition(seat));
} character->rotation = vehicle->getRotation();
return false; }
} else if (character->animator->getAnimation(AnimIndexAction) ==
anm_pullout) {
if (character->animator->isCompleted(AnimIndexAction)) {
tryToEnter = true;
}
} else if (character->animator->getAnimation(AnimIndexAction) ==
anm_enter) {
if (character->animator->isCompleted(AnimIndexAction)) {
// VehicleGetIn is over, finish activity
return true;
}
}
} else {
glm::vec3 targetDirection = entryPos - character->getPosition();
targetDirection.z = 0.f;
float targetDistance = glm::length(targetDirection);
if (targetDistance <= 0.4f) {
entering = true;
// Warp character to vehicle orientation
character->controller->setMoveDirection({0.f, 0.f, 0.f});
character->controller->setRunning(false);
character->setHeading(
glm::degrees(glm::roll(vehicle->getRotation())));
// Determine if the door open animation should be skipped.
if (entryDoor == nullptr ||
(entryDoor->constraint != nullptr &&
glm::abs(entryDoor->constraint->getHingeAngle()) >= 0.6f)) {
tryToEnter = true;
} else {
character->playActivityAnimation(anm_open, false, true);
}
} else if (targetDistance > kGiveUpDistance) {
return true;
} else {
if (targetDistance > kSprintToEnterDistance) {
character->controller->setRunning(true);
}
character->setHeading(
glm::degrees(atan2(targetDirection.y, targetDirection.x) -
glm::half_pi<float>()));
character->controller->setMoveDirection({1.f, 0.f, 0.f});
}
}
if (tryToEnter) {
if (currentOccupant != nullptr && currentOccupant != character) {
// Play the pullout animation and tell the other character to get
// out.
character->playActivityAnimation(anm_pullout, false, true);
currentOccupant->controller->setNextActivity(
new Activities::ExitVehicle(true));
} else {
character->playActivityAnimation(anm_enter, false, true);
character->enterVehicle(vehicle, seat);
}
}
return false;
} }
bool Activities::ExitVehicle::update(CharacterObject *character,
CharacterController *controller) {
RW_UNUSED(controller);
bool Activities::ExitVehicle::update(CharacterObject *character, CharacterController *controller) if (jacked) {
{ auto anm_jacked_lhs = character->animations.car_jacked_lhs;
RW_UNUSED(controller); auto anm_jacked_rhs = character->animations.car_jacked_lhs;
auto anm_current = character->animator->getAnimation(AnimIndexAction);
if (jacked) { if (anm_current == anm_jacked_lhs || anm_current == anm_jacked_rhs) {
auto anm_jacked_lhs = character->animations.car_jacked_lhs; if (character->animator->isCompleted(AnimIndexAction)) {
auto anm_jacked_rhs = character->animations.car_jacked_lhs; return true;
auto anm_current = character->animator->getAnimation(AnimIndexAction); }
} else {
if (character->getCurrentVehicle() == nullptr) return true;
if (anm_current == anm_jacked_lhs || anm_current == anm_jacked_rhs) { auto vehicle = character->getCurrentVehicle();
if (character->animator->isCompleted(AnimIndexAction)) { auto seat = character->getCurrentSeat();
return true; auto door = vehicle->getSeatEntryDoor(seat);
} auto exitPos = vehicle->getSeatEntryPositionWorld(seat);
} auto exitPosLocal = vehicle->getSeatEntryPosition(seat);
else {
if (character->getCurrentVehicle() == nullptr) return true;
auto vehicle = character->getCurrentVehicle(); character->rotation = vehicle->getRotation();
auto seat = character->getCurrentSeat();
auto door = vehicle->getSeatEntryDoor(seat);
auto exitPos = vehicle->getSeatEntryPositionWorld(seat);
auto exitPosLocal = vehicle->getSeatEntryPosition(seat);
character->rotation = vehicle->getRotation(); // Exit the vehicle immediatley
character->enterVehicle(nullptr, seat);
character->setPosition(exitPos);
// Exit the vehicle immediatley if (exitPosLocal.x > 0.f) {
character->enterVehicle(nullptr, seat); character->playActivityAnimation(anm_jacked_rhs, false, true);
character->setPosition(exitPos); } else {
character->playActivityAnimation(anm_jacked_lhs, false, true);
}
// No need to open the door, it should already be open.
}
return false;
}
if (exitPosLocal.x > 0.f) { if (character->getCurrentVehicle() == nullptr) return true;
character->playActivityAnimation(anm_jacked_rhs, false, true);
}
else {
character->playActivityAnimation(anm_jacked_lhs, false, true);
}
// No need to open the door, it should already be open.
}
return false;
}
if( character->getCurrentVehicle() == nullptr ) return true; auto vehicle = character->getCurrentVehicle();
auto seat = character->getCurrentSeat();
auto door = vehicle->getSeatEntryDoor(seat);
auto exitPos = vehicle->getSeatEntryPositionWorld(seat);
auto exitPosLocal = vehicle->getSeatEntryPosition(seat);
auto vehicle = character->getCurrentVehicle(); auto anm_exit = character->animations.car_getout_lhs;
auto seat = character->getCurrentSeat();
auto door = vehicle->getSeatEntryDoor(seat);
auto exitPos = vehicle->getSeatEntryPositionWorld(seat);
auto exitPosLocal = vehicle->getSeatEntryPosition(seat);
auto anm_exit = character->animations.car_getout_lhs; if (exitPosLocal.x > 0.f) {
anm_exit = character->animations.car_getout_rhs;
}
if( exitPosLocal.x > 0.f ) if (vehicle->vehicle->type == VehicleData::BOAT) {
{ auto ppos = character->getPosition();
anm_exit = character->animations.car_getout_rhs; character->enterVehicle(nullptr, seat);
} character->setPosition(ppos);
return true;
}
if( vehicle->vehicle->type == VehicleData::BOAT ) { bool isDriver = vehicle->isOccupantDriver(character->getCurrentSeat());
auto ppos = character->getPosition();
character->enterVehicle(nullptr, seat);
character->setPosition(ppos);
return true;
}
bool isDriver = vehicle->isOccupantDriver(character->getCurrentSeat()); // If the vehicle is going too fast, slow down
if (isDriver) {
if (!vehicle->canOccupantExit()) {
vehicle->setBraking(1.f);
return false;
}
}
// If the vehicle is going too fast, slow down if (character->animator->getAnimation(AnimIndexAction) == anm_exit) {
if (isDriver) if (character->animator->isCompleted(AnimIndexAction)) {
{ character->enterVehicle(nullptr, seat);
if (!vehicle->canOccupantExit()) character->setPosition(exitPos);
{
vehicle->setBraking(1.f);
return false;
}
}
if( character->animator->getAnimation(AnimIndexAction) == anm_exit ) { if (isDriver) {
if( character->animator->isCompleted(AnimIndexAction) ) { // Apply the handbrake
vehicle->setHandbraking(true);
vehicle->setThrottle(0.f);
}
character->enterVehicle(nullptr, seat); return true;
character->setPosition(exitPos); }
} else {
character->playActivityAnimation(anm_exit, false, true);
if (door) {
vehicle->setPartTarget(door, true, door->openAngle);
}
}
if (isDriver) return false;
{
// Apply the handbrake
vehicle->setHandbraking(true);
vehicle->setThrottle(0.f);
}
return true;
}
}
else {
character->playActivityAnimation(anm_exit, false, true);
if( door )
{
vehicle->setPartTarget(door, true, door->openAngle);
}
}
return false;
} }
#include <engine/GameWorld.hpp>
#include <engine/GameData.hpp>
#include <data/Model.hpp> #include <data/Model.hpp>
bool Activities::ShootWeapon::update(CharacterObject *character, CharacterController *controller) #include <engine/GameData.hpp>
{ #include <engine/GameWorld.hpp>
RW_UNUSED(controller); bool Activities::ShootWeapon::update(CharacterObject *character,
CharacterController *controller) {
RW_UNUSED(controller);
auto& wepdata = _item->getWeaponData(); auto &wepdata = _item->getWeaponData();
// Instant hit weapons loop their anim // Instant hit weapons loop their anim
// Thrown projectiles have lob / throw. // Thrown projectiles have lob / throw.
// Update player direction // Update player direction
character->setRotation(glm::angleAxis(character->getLook().x, glm::vec3{0.f, 0.f, 1.f})); character->setRotation(
glm::angleAxis(character->getLook().x, glm::vec3{0.f, 0.f, 1.f}));
RW_CHECK(wepdata->inventorySlot < maxInventorySlots, "Inventory slot out of bounds"); RW_CHECK(wepdata->inventorySlot < maxInventorySlots,
auto& itemState = character->getCurrentState().weapons[wepdata->inventorySlot]; "Inventory slot out of bounds");
if (itemState.bulletsClip == 0 && itemState.bulletsTotal > 0) { auto &itemState =
itemState.bulletsClip += std::min(int(itemState.bulletsTotal), wepdata->clipSize); character->getCurrentState().weapons[wepdata->inventorySlot];
itemState.bulletsTotal -= itemState.bulletsClip; if (itemState.bulletsClip == 0 && itemState.bulletsTotal > 0) {
} itemState.bulletsClip +=
bool hasammo = itemState.bulletsClip > 0; std::min(int(itemState.bulletsTotal), wepdata->clipSize);
itemState.bulletsTotal -= itemState.bulletsClip;
}
bool hasammo = itemState.bulletsClip > 0;
if( wepdata->fireType == WeaponData::INSTANT_HIT ) { if (wepdata->fireType == WeaponData::INSTANT_HIT) {
if( _item->isFiring(character) && hasammo ) { if (_item->isFiring(character) && hasammo) {
auto shootanim =
character->engine->data->animations[wepdata->animation1];
if (shootanim) {
if (character->animator->getAnimation(AnimIndexAction) !=
shootanim) {
character->playActivityAnimation(shootanim, false, false);
}
auto shootanim = character->engine->data->animations[wepdata->animation1]; auto loopstart = wepdata->animLoopStart / 100.f;
if( shootanim ) { auto loopend = wepdata->animLoopEnd / 100.f;
if( character->animator->getAnimation(AnimIndexAction) != shootanim ) { auto firetime = wepdata->animFirePoint / 100.f;
character->playActivityAnimation(shootanim, false, false);
}
auto loopstart = wepdata->animLoopStart / 100.f; auto currID =
auto loopend = wepdata->animLoopEnd / 100.f; character->animator->getAnimationTime(AnimIndexAction);
auto firetime = wepdata->animFirePoint / 100.f;
auto currID = character->animator->getAnimationTime(AnimIndexAction); if (currID >= firetime && !_fired) {
itemState.bulletsClip--;
_item->fire(character);
_fired = true;
}
if (currID > loopend) {
character->animator->setAnimationTime(AnimIndexAction,
loopstart);
_fired = false;
}
}
} else {
if (character->animator->isCompleted(AnimIndexAction)) {
return true;
}
}
}
/// @todo Use Thrown flag instead of project (RPG isn't thrown eg.)
else if (wepdata->fireType == WeaponData::PROJECTILE && hasammo) {
auto shootanim =
character->engine->data->animations[wepdata->animation1];
auto throwanim =
character->engine->data->animations[wepdata->animation2];
if( currID >= firetime && ! _fired ) { if (character->animator->getAnimation(AnimIndexAction) == shootanim) {
itemState.bulletsClip --; if (character->animator->isCompleted(AnimIndexAction)) {
_item->fire(character); character->playActivityAnimation(throwanim, false, false);
_fired = true; }
} } else if (character->animator->getAnimation(AnimIndexAction) ==
if( currID > loopend ) { throwanim) {
character->animator->setAnimationTime( AnimIndexAction, loopstart ); auto firetime = wepdata->animCrouchFirePoint / 100.f;
_fired = false; auto currID =
} character->animator->getAnimationTime(AnimIndexAction);
}
}
else {
if( character->animator->isCompleted(AnimIndexAction) ) {
return true;
}
}
}
/// @todo Use Thrown flag instead of project (RPG isn't thrown eg.)
else if( wepdata->fireType == WeaponData::PROJECTILE && hasammo ) {
auto shootanim = character->engine->data->animations[wepdata->animation1];
auto throwanim = character->engine->data->animations[wepdata->animation2];
if( character->animator->getAnimation(AnimIndexAction) == shootanim ) { if (currID >= firetime && !_fired) {
if( character->animator->isCompleted(AnimIndexAction) ) { itemState.bulletsClip--;
character->playActivityAnimation(throwanim, false, false); _item->fire(character);
} _fired = true;
} }
else if( character->animator->getAnimation(AnimIndexAction) == throwanim ) { if (character->animator->isCompleted(AnimIndexAction)) {
auto firetime = wepdata->animCrouchFirePoint / 100.f; return true;
auto currID = character->animator->getAnimationTime(AnimIndexAction); }
} else {
character->playActivityAnimation(throwanim, false, true);
}
} else if (wepdata->fireType == WeaponData::MELEE) {
RW_CHECK(wepdata->fireType != WeaponData::MELEE,
"Melee attacks not implemented");
return true;
} else {
RW_ERROR("Unrecognized fireType: " << wepdata->fireType);
return true;
}
if( currID >= firetime && !_fired ) { return false;
itemState.bulletsClip --;
_item->fire(character);
_fired = true;
}
if( character->animator->isCompleted(AnimIndexAction) ) {
return true;
}
}
else {
character->playActivityAnimation(throwanim, false, true);
}
}
else if( wepdata->fireType == WeaponData::MELEE ) {
RW_CHECK(wepdata->fireType != WeaponData::MELEE, "Melee attacks not implemented");
return true;
}
else
{
RW_ERROR("Unrecognized fireType: " << wepdata->fireType);
return true;
}
return false;
} }

View File

@ -11,127 +11,141 @@ class VehicleObject;
/** /**
* @class CharacterController * @class CharacterController
* Character Controller Interface, translates high-level behaviours into low level actions. * Character Controller Interface, translates high-level behaviours into low
* level actions.
*/ */
class CharacterController class CharacterController {
{
public: public:
/**
* @brief The Activity struct interface
*/
struct Activity {
virtual ~Activity() {
}
/** virtual std::string name() const = 0;
* @brief The Activity struct interface
*/
struct Activity {
virtual ~Activity() {} /**
* @brief canSkip
* @return true if the activity can be skipped.
*/
virtual bool canSkip(CharacterObject*, CharacterController*) const {
return false;
}
virtual std::string name() const = 0; virtual bool update(CharacterObject* character,
CharacterController* controller) = 0;
};
/** /**
* @brief canSkip * Available AI goals.
* @return true if the activity can be skipped. */
*/ enum Goal {
virtual bool canSkip(CharacterObject*, CharacterController*) const { return false; } /**
* No goal, will idle or execute external Activities.
virtual bool update(CharacterObject* character, CharacterController* controller) = 0; */
}; None,
/**
/** * Keep close to leader character
* Available AI goals. */
*/ FollowLeader,
enum Goal /**
{ * Wander randomly around the map
/** */
* No goal, will idle or execute external Activities. TrafficWander
*/ };
None,
/**
* Keep close to leader character
*/
FollowLeader,
/**
* Wander randomly around the map
*/
TrafficWander
};
protected: protected:
/**
/** * The character being controlled.
* The character being controlled. */
*/ CharacterObject* character;
CharacterObject* character;
Activity* _currentActivity; Activity* _currentActivity;
Activity* _nextActivity; Activity* _nextActivity;
bool updateActivity(); bool updateActivity();
void setActivity(Activity* activity); void setActivity(Activity* activity);
float m_closeDoorTimer; float m_closeDoorTimer;
// Goal related variables // Goal related variables
Goal currentGoal; Goal currentGoal;
CharacterObject* leader; CharacterObject* leader;
AIGraphNode* targetNode; AIGraphNode* targetNode;
public: public:
CharacterController(CharacterObject* character);
CharacterController(CharacterObject* character);
virtual ~CharacterController() { } virtual ~CharacterController() {
}
Activity* getCurrentActivity() const { return _currentActivity; } Activity* getCurrentActivity() const {
return _currentActivity;
}
Activity* getNextActivity() const { return _nextActivity; } Activity* getNextActivity() const {
return _nextActivity;
}
/** /**
* @brief skipActivity Cancel the current activity immediatley, if possible. * @brief skipActivity Cancel the current activity immediatley, if possible.
*/ */
void skipActivity(); void skipActivity();
/** /**
* @brief setNextActivity Sets the next Activity with a parameter. * @brief setNextActivity Sets the next Activity with a parameter.
* @param activity * @param activity
* @param position * @param position
*/ */
void setNextActivity( Activity* activity ); void setNextActivity(Activity* activity);
/** /**
* @brief IsCurrentActivity * @brief IsCurrentActivity
* @param activity Name of activity to check for * @param activity Name of activity to check for
* @return if the given activity is the current activity * @return if the given activity is the current activity
*/ */
bool isCurrentActivity(const std::string& activity) const; bool isCurrentActivity(const std::string& activity) const;
/**
* @brief update Updates the controller.
* @param dt
*/
virtual void update(float dt);
virtual glm::vec3 getTargetPosition() = 0; /**
* @brief update Updates the controller.
* @param dt
*/
virtual void update(float dt);
/** virtual glm::vec3 getTargetPosition() = 0;
* @brief
* @return Returns the Character Object
*/
CharacterObject* getCharacter() const;
void setMoveDirection(const glm::vec3& movement); /**
void setLookDirection(const glm::vec2& look); * @brief
* @return Returns the Character Object
*/
CharacterObject* getCharacter() const;
void setRunning(bool run); void setMoveDirection(const glm::vec3& movement);
void setLookDirection(const glm::vec2& look);
void setGoal(Goal goal) { currentGoal = goal; }
Goal getGoal() const { return currentGoal; } void setRunning(bool run);
void setTargetCharacter(CharacterObject* c) { leader = c; } void setGoal(Goal goal) {
CharacterObject* getTargetCharacter() const { return leader; } currentGoal = goal;
}
Goal getGoal() const {
return currentGoal;
}
void setTargetCharacter(CharacterObject* c) {
leader = c;
}
CharacterObject* getTargetCharacter() const {
return leader;
}
}; };
#define DECL_ACTIVITY( activity_name ) \ #define DECL_ACTIVITY(activity_name) \
static constexpr auto ActivityName = #activity_name; \ static constexpr auto ActivityName = #activity_name; \
std::string name() const { return ActivityName; } std::string name() const { \
return ActivityName; \
}
// TODO: Refactor this with an ugly macro to reduce code dup. // TODO: Refactor this with an ugly macro to reduce code dup.
class WeaponItem; class WeaponItem;
@ -142,74 +156,78 @@ class WeaponItem;
* @todo Move into ControllerActivities.hpp or equivelant * @todo Move into ControllerActivities.hpp or equivelant
*/ */
namespace Activities { namespace Activities {
struct GoTo : public CharacterController::Activity { struct GoTo : public CharacterController::Activity {
DECL_ACTIVITY( GoTo ) DECL_ACTIVITY(GoTo)
glm::vec3 target; glm::vec3 target;
bool sprint; bool sprint;
GoTo( const glm::vec3& target, bool _sprint = false ) GoTo(const glm::vec3& target, bool _sprint = false)
: target( target ), sprint(_sprint) {} : target(target), sprint(_sprint) {
}
bool update(CharacterObject* character, CharacterController* controller); bool update(CharacterObject* character, CharacterController* controller);
bool canSkip(CharacterObject *, CharacterController *) const { return true; } bool canSkip(CharacterObject*, CharacterController*) const {
}; return true;
}
struct Jump : public CharacterController::Activity };
{
DECL_ACTIVITY( Jump )
bool jumped;
Jump() : jumped(false) {}
bool update(CharacterObject* character, CharacterController* controller);
};
struct EnterVehicle : public CharacterController::Activity { struct Jump : public CharacterController::Activity {
DECL_ACTIVITY( EnterVehicle ) DECL_ACTIVITY(Jump)
VehicleObject* vehicle; bool jumped;
int seat;
enum {
ANY_SEAT = -1 // Magic number for any seat but the driver's.
};
bool entering; Jump() : jumped(false) {
}
EnterVehicle( VehicleObject* vehicle, int seat = 0 ) bool update(CharacterObject* character, CharacterController* controller);
: vehicle( vehicle ), seat( seat ), entering(false) {} };
bool canSkip(CharacterObject* character, CharacterController*) const override; struct EnterVehicle : public CharacterController::Activity {
DECL_ACTIVITY(EnterVehicle)
bool update(CharacterObject *character, CharacterController *controller); VehicleObject* vehicle;
}; int seat;
struct ExitVehicle : public CharacterController::Activity { enum {
DECL_ACTIVITY( ExitVehicle ) ANY_SEAT = -1 // Magic number for any seat but the driver's.
};
const bool jacked; bool entering;
ExitVehicle(bool jacked_ = false) EnterVehicle(VehicleObject* vehicle, int seat = 0)
: jacked(jacked_) : vehicle(vehicle), seat(seat), entering(false) {
{} }
bool update(CharacterObject *character, CharacterController *controller); bool canSkip(CharacterObject* character,
}; CharacterController*) const override;
struct ShootWeapon : public CharacterController::Activity { bool update(CharacterObject* character, CharacterController* controller);
DECL_ACTIVITY( ShootWeapon ) };
WeaponItem* _item; struct ExitVehicle : public CharacterController::Activity {
bool _fired; DECL_ACTIVITY(ExitVehicle)
ShootWeapon( WeaponItem* item ) const bool jacked;
: _item(item), _fired(false) {}
bool update(CharacterObject *character, CharacterController *controller); ExitVehicle(bool jacked_ = false) : jacked(jacked_) {
}; }
bool update(CharacterObject* character, CharacterController* controller);
};
struct ShootWeapon : public CharacterController::Activity {
DECL_ACTIVITY(ShootWeapon)
WeaponItem* _item;
bool _fired;
ShootWeapon(WeaponItem* item) : _item(item), _fired(false) {
}
bool update(CharacterObject* character, CharacterController* controller);
};
} }
#endif #endif

View File

@ -1,101 +1,88 @@
#include <ai/DefaultAIController.hpp> #include <ai/DefaultAIController.hpp>
#include <objects/CharacterObject.hpp>
#include <engine/GameWorld.hpp> #include <engine/GameWorld.hpp>
#include <objects/CharacterObject.hpp>
glm::vec3 DefaultAIController::getTargetPosition() glm::vec3 DefaultAIController::getTargetPosition() {
{ /*if(targetNode) {
/*if(targetNode) { if(lastNode && character->getCurrentVehicle()) {
if(lastNode && character->getCurrentVehicle()) { auto nDir = glm::normalize(targetNode->position -
auto nDir = glm::normalize(targetNode->position - lastNode->position); lastNode->position);
auto right = glm::cross(nDir, glm::vec3(0.f, 0.f, 1.f)); auto right = glm::cross(nDir, glm::vec3(0.f, 0.f, 1.f));
return targetNode->position + right * 2.2f; return targetNode->position + right * 2.2f;
} }
return targetNode->position; return targetNode->position;
}*/ }*/
return glm::vec3(); return glm::vec3();
} }
const float followRadius = 5.f; const float followRadius = 5.f;
void DefaultAIController::update(float dt) void DefaultAIController::update(float dt) {
{ switch (currentGoal) {
switch(currentGoal) case FollowLeader: {
{ if (!leader) break;
case FollowLeader: if (getCharacter()->getCurrentVehicle()) {
{ if (leader->getCurrentVehicle() !=
if( ! leader ) break; getCharacter()->getCurrentVehicle()) {
if( getCharacter()->getCurrentVehicle() ) skipActivity();
{ setNextActivity(new Activities::ExitVehicle);
if( leader->getCurrentVehicle() != getCharacter()->getCurrentVehicle() ) }
{ // else we're already in the right spot.
skipActivity(); } else {
setNextActivity(new Activities::ExitVehicle); if (leader->getCurrentVehicle()) {
} setNextActivity(new Activities::EnterVehicle(
// else we're already in the right spot. leader->getCurrentVehicle(), 1));
} } else {
else glm::vec3 dir =
{ leader->getPosition() - getCharacter()->getPosition();
if( leader->getCurrentVehicle() ) if (glm::length(dir) > followRadius) {
{ if (glm::distance(gotoPos, leader->getPosition()) >
setNextActivity(new Activities::EnterVehicle(leader->getCurrentVehicle(), 1)); followRadius) {
} gotoPos =
else leader->getPosition() +
{ (glm::normalize(-dir) * followRadius * 0.7f);
glm::vec3 dir = leader->getPosition() - getCharacter()->getPosition(); skipActivity();
if( glm::length(dir) > followRadius ) setNextActivity(new Activities::GoTo(gotoPos));
{ }
if( glm::distance(gotoPos, leader->getPosition()) > followRadius ) }
{ }
gotoPos = leader->getPosition() + ( glm::normalize(-dir) * followRadius * 0.7f ); }
skipActivity(); } break;
setNextActivity(new Activities::GoTo(gotoPos)); case TrafficWander: {
} if (targetNode) {
} auto targetDistance =
} glm::vec2(character->getPosition() - targetNode->position);
} if (glm::length(targetDistance) <= 0.1f) {
} // Assign the next target node
break; auto lastTarget = targetNode;
case TrafficWander: std::random_device rd;
{ std::default_random_engine re(rd());
if( targetNode ) std::uniform_int_distribution<> d(
{ 0, lastTarget->connections.size() - 1);
auto targetDistance = glm::vec2(character->getPosition() - targetNode->position); targetNode = lastTarget->connections.at(d(re));
if( glm::length(targetDistance) <= 0.1f ) setNextActivity(new Activities::GoTo(targetNode->position));
{ } else if (getCurrentActivity() == nullptr) {
// Assign the next target node setNextActivity(new Activities::GoTo(targetNode->position));
auto lastTarget = targetNode; }
std::random_device rd; } else {
std::default_random_engine re(rd()); // We need to pick an initial node
std::uniform_int_distribution<> d(0, lastTarget->connections.size()-1); auto& graph = getCharacter()->engine->aigraph;
targetNode = lastTarget->connections.at(d(re)); AIGraphNode* node = nullptr;
setNextActivity(new Activities::GoTo(targetNode->position)); float mindist = std::numeric_limits<float>::max();
} for (auto n : graph.nodes) {
else if ( getCurrentActivity() == nullptr ) float d = glm::distance(n->position,
{ getCharacter()->getPosition());
setNextActivity(new Activities::GoTo(targetNode->position)); if (d < mindist) {
} node = n;
} mindist = d;
else }
{ }
// We need to pick an initial node targetNode = node;
auto& graph = getCharacter()->engine->aigraph; }
AIGraphNode* node = nullptr; } break;
float mindist = std::numeric_limits<float>::max(); default:
for( auto n : graph.nodes ) break;
{ }
float d = glm::distance( n->position, getCharacter()->getPosition() );
if( d < mindist ) CharacterController::update(dt);
{
node = n;
mindist = d;
}
}
targetNode = node;
}
}
break;
default: break;
}
CharacterController::update(dt);
} }

View File

@ -5,16 +5,16 @@
#include <random> #include <random>
struct AIGraphNode; struct AIGraphNode;
class DefaultAIController : public CharacterController class DefaultAIController : public CharacterController {
{ glm::vec3 gotoPos;
glm::vec3 gotoPos;
public: public:
DefaultAIController(CharacterObject* character)
DefaultAIController(CharacterObject* character) : CharacterController(character) {
: CharacterController(character) {} }
glm::vec3 getTargetPosition();
glm::vec3 getTargetPosition();
virtual void update(float dt); virtual void update(float dt);
}; };

View File

@ -1,81 +1,74 @@
#include <ai/PlayerController.hpp> #include <ai/PlayerController.hpp>
#include <objects/CharacterObject.hpp> #include <engine/Animator.hpp>
#include <objects/VehicleObject.hpp>
#include <engine/GameWorld.hpp> #include <engine/GameWorld.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <engine/Animator.hpp> #include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
PlayerController::PlayerController(CharacterObject* character) PlayerController::PlayerController(CharacterObject* character)
: CharacterController(character), lastRotation(glm::vec3(0.f, 0.f, 0.f)), _enabled(true) : CharacterController(character)
{ , lastRotation(glm::vec3(0.f, 0.f, 0.f))
, _enabled(true) {
} }
void PlayerController::setInputEnabled(bool enabled) void PlayerController::setInputEnabled(bool enabled) {
{ _enabled = enabled;
_enabled = enabled;
} }
bool PlayerController::isInputEnabled() const bool PlayerController::isInputEnabled() const {
{ return _enabled;
return _enabled;
} }
void PlayerController::updateCameraDirection(const glm::quat& rot) void PlayerController::updateCameraDirection(const glm::quat& rot) {
{ cameraRotation = rot;
cameraRotation = rot;
} }
void PlayerController::updateMovementDirection(const glm::vec3& dir, const glm::vec3 &rawdirection) void PlayerController::updateMovementDirection(const glm::vec3& dir,
{ const glm::vec3& rawdirection) {
if( _currentActivity == nullptr ) { if (_currentActivity == nullptr) {
direction = dir; direction = dir;
setMoveDirection(rawdirection); setMoveDirection(rawdirection);
} }
} }
void PlayerController::exitVehicle() void PlayerController::exitVehicle() {
{ if (character->getCurrentVehicle()) {
if(character->getCurrentVehicle()) { setNextActivity(new Activities::ExitVehicle());
setNextActivity(new Activities::ExitVehicle()); }
}
} }
void PlayerController::enterNearestVehicle() void PlayerController::enterNearestVehicle() {
{ if (!character->getCurrentVehicle()) {
if(! character->getCurrentVehicle()) { auto world = character->engine;
auto world = character->engine; VehicleObject* nearest = nullptr;
VehicleObject* nearest = nullptr; float d = 10.f; float d = 10.f;
for( auto& p : world->vehiclePool.objects ) { for (auto& p : world->vehiclePool.objects) {
auto object = p.second; auto object = p.second;
float vd = glm::length( character->getPosition() - object->getPosition()); float vd =
if( vd < d ) { glm::length(character->getPosition() - object->getPosition());
d = vd; if (vd < d) {
nearest = static_cast<VehicleObject*>(object); d = vd;
} nearest = static_cast<VehicleObject*>(object);
} }
}
if( nearest ) { if (nearest) {
setNextActivity(new Activities::EnterVehicle(nearest, 0)); setNextActivity(new Activities::EnterVehicle(nearest, 0));
} }
} }
} }
void PlayerController::update(float dt) void PlayerController::update(float dt) {
{ CharacterController::update(dt);
CharacterController::update(dt);
} }
glm::vec3 PlayerController::getTargetPosition() glm::vec3 PlayerController::getTargetPosition() {
{ return direction;
return direction;
} }
void PlayerController::jump() void PlayerController::jump() {
{ if (!character->isInWater()) {
if(! character->isInWater() ) { setNextActivity(new Activities::Jump());
setNextActivity(new Activities::Jump()); }
}
} }

View File

@ -3,45 +3,46 @@
#define _PLAYERCONTROLLER_HPP_ #define _PLAYERCONTROLLER_HPP_
#include <ai/CharacterController.hpp> #include <ai/CharacterController.hpp>
class PlayerController : public CharacterController class PlayerController : public CharacterController {
{ glm::quat cameraRotation;
glm::quat cameraRotation;
glm::vec3 direction;
glm::quat lastRotation; glm::vec3 direction;
glm::quat lastRotation;
bool _enabled;
bool _enabled;
public: public:
PlayerController(CharacterObject* character);
PlayerController(CharacterObject* character);
/**
* @brief Enables and disables player input.
* @todo actually implement input being disabled.
*/
void setInputEnabled(bool enabled);
bool isInputEnabled() const;
void updateCameraDirection(const glm::quat& rot);
void updateMovementDirection(const glm::vec3& pos,
const glm::vec3& rawdirection);
void exitVehicle();
void enterNearestVehicle();
/**
* @brief Enables and disables player input.
* @todo actually implement input being disabled.
*/
void setInputEnabled(bool enabled);
bool isInputEnabled() const;
void updateCameraDirection(const glm::quat& rot);
void updateMovementDirection(const glm::vec3& pos, const glm::vec3& rawdirection);
void exitVehicle();
void enterNearestVehicle();
virtual void update(float dt); virtual void update(float dt);
virtual glm::vec3 getTargetPosition();
void jump();
/** virtual glm::vec3 getTargetPosition();
* returns 0 (only one player supported)
*/ void jump();
int getScriptObjectID() const { return 0; }
/**
* returns 0 (only one player supported)
*/
int getScriptObjectID() const {
return 0;
}
}; };
#endif #endif

View File

@ -1,172 +1,169 @@
#include "ai/TrafficDirector.hpp" #include "ai/TrafficDirector.hpp"
#include <ai/AIGraphNode.hpp> #include <ai/AIGraphNode.hpp>
#include <ai/CharacterController.hpp> #include <ai/CharacterController.hpp>
#include <engine/GameWorld.hpp> #include <core/Logger.hpp>
#include <engine/GameState.hpp> #include <engine/GameState.hpp>
#include <objects/GameObject.hpp> #include <engine/GameWorld.hpp>
#include <objects/CharacterObject.hpp> #include <objects/CharacterObject.hpp>
#include <objects/GameObject.hpp>
#include <objects/VehicleObject.hpp> #include <objects/VehicleObject.hpp>
#include <render/ViewCamera.hpp> #include <render/ViewCamera.hpp>
#include <core/Logger.hpp>
#include <glm/gtx/string_cast.hpp>
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include <glm/gtx/string_cast.hpp>
#ifdef RW_WINDOWS #ifdef RW_WINDOWS
#include <rw_mingw.hpp> #include <rw_mingw.hpp>
#endif #endif
TrafficDirector::TrafficDirector(AIGraph* g, GameWorld* w) TrafficDirector::TrafficDirector(AIGraph* g, GameWorld* w)
: graph( g ), world( w ), pedDensity(1.f), carDensity(1.f), : graph(g)
maximumPedestrians(20), maximumCars(10) , world(w)
{ , pedDensity(1.f)
, carDensity(1.f)
, maximumPedestrians(20)
, maximumCars(10) {
} }
std::vector< AIGraphNode* > TrafficDirector::findAvailableNodes(AIGraphNode::NodeType type, const ViewCamera& camera, float radius) std::vector<AIGraphNode*> TrafficDirector::findAvailableNodes(
{ AIGraphNode::NodeType type, const ViewCamera& camera, float radius) {
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);
float density = type == AIGraphNode::Vehicle ? carDensity : pedDensity; float density = type == AIGraphNode::Vehicle ? carDensity : pedDensity;
float minDist = (10.f / density) * (10.f / density); float minDist = (10.f / density) * (10.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 it // Check if any of the nearby nodes are blocked by a pedestrian standing on
// or because it's inside the view frustum // it
for (auto it = available.begin(); it != available.end();) { // or because it's inside the view frustum
bool blocked = false; for (auto it = available.begin(); it != available.end();) {
float dist2 = glm::distance2(camera.position, (*it)->position); bool blocked = false;
float dist2 = glm::distance2(camera.position, (*it)->position);
for (auto& obj : world->pedestrianPool.objects) { for (auto& obj : world->pedestrianPool.objects) {
if (glm::distance2( (*it)->position, obj.second->getPosition() ) <= minDist) { if (glm::distance2((*it)->position, obj.second->getPosition()) <=
blocked = true; minDist) {
break; blocked = true;
} break;
} }
}
// Check that we're not going to spawn something right where the player is looking // Check that we're not going to spawn something right where the player
if (dist2 <= halfRadius2 && camera.frustum.intersects((*it)->position, 1.f)) { // is looking
blocked = true; if (dist2 <= halfRadius2 &&
} camera.frustum.intersects((*it)->position, 1.f)) {
blocked = true;
if (blocked) { }
it = available.erase( it );
}
else {
it++;
}
}
return available; if (blocked) {
it = available.erase(it);
} else {
it++;
}
}
return available;
} }
void TrafficDirector::setDensity(AIGraphNode::NodeType type, float density) void TrafficDirector::setDensity(AIGraphNode::NodeType type, float density) {
{ switch (type) {
switch ( type ) case AIGraphNode::Vehicle:
{ carDensity = density;
case AIGraphNode::Vehicle: break;
carDensity = density; case AIGraphNode::Pedestrian:
break; pedDensity = density;
case AIGraphNode::Pedestrian: break;
pedDensity = density; }
break;
}
} }
std::vector<GameObject*> TrafficDirector::populateNearby(const ViewCamera& camera, float radius, int maxSpawn) std::vector<GameObject*> TrafficDirector::populateNearby(
{ const ViewCamera& camera, float radius, int maxSpawn) {
int availablePeds = maximumPedestrians - world->pedestrianPool.objects.size(); int availablePeds =
maximumPedestrians - world->pedestrianPool.objects.size();
std::vector<GameObject*> created; std::vector<GameObject*> created;
/// @todo Check how "in player view" should be determined. /// @todo Check how "in player view" should be determined.
// Don't check the frustum for things more than 1/2 of the radius away // Don't check the frustum for things more than 1/2 of the radius away
// so that things will spawn as you drive towards them // so that things will spawn as you drive towards them
float halfRadius2 = std::pow(radius/ 2.f, 2.f); float halfRadius2 = std::pow(radius / 2.f, 2.f);
// Spawn vehicles at vehicle generators // Spawn vehicles at vehicle generators
auto camera2D = glm::vec2(camera.position); auto camera2D = glm::vec2(camera.position);
for (auto& gen : world->state->vehicleGenerators) { for (auto& gen : world->state->vehicleGenerators) {
/// @todo verify how vehicle generator proximity is determined /// @todo verify how vehicle generator proximity is determined
auto gen2D = glm::vec2(gen.position); auto gen2D = glm::vec2(gen.position);
float dist2 = glm::distance2(camera2D, gen2D); float dist2 = glm::distance2(camera2D, gen2D);
if (dist2 < radius * radius) { if (dist2 < radius * radius) {
auto position = gen.position; auto position = gen.position;
// Check that the on-ground position is not in view // Check that the on-ground position is not in view
if (gen.position.z < -90.f) { if (gen.position.z < -90.f) {
position = world->getGroundAtPosition(position); position = world->getGroundAtPosition(position);
} }
if (dist2 <= halfRadius2 && if (dist2 <= halfRadius2 &&
camera.frustum.intersects(position, 1.f)) { camera.frustum.intersects(position, 1.f)) {
if (!gen.alwaysSpawn) { if (!gen.alwaysSpawn) {
// Don't spawn in the view frustum unless we're forced to // Don't spawn in the view frustum unless we're forced to
continue; continue;
} }
} }
auto spawned = world->tryToSpawnVehicle(gen); auto spawned = world->tryToSpawnVehicle(gen);
if (spawned) { if (spawned) {
created.push_back(spawned); created.push_back(spawned);
} }
} }
} }
auto type = AIGraphNode::Pedestrian; auto type = AIGraphNode::Pedestrian;
auto available = findAvailableNodes(type, camera, radius); 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> validPeds = { 1 };
validPeds.insert(validPeds.end(), {20, 11, 19, 5});
std::random_device rd;
std::default_random_engine re(rd());
std::uniform_int_distribution<> d(0, validPeds.size()-1);
int counter = availablePeds; if (availablePeds <= 0) {
// maxSpawn can be -1 for "as many as possible" // We have already reached the limit of spawned traffic
if( maxSpawn > -1 ) return {};
{ }
counter = std::min( availablePeds, maxSpawn );
}
for ( AIGraphNode* spawn : available ) /// Hardcoded cop Pedestrian
{ std::vector<uint16_t> validPeds = {1};
if( spawn->type != AIGraphNode::Pedestrian ) validPeds.insert(validPeds.end(), {20, 11, 19, 5});
{ std::random_device rd;
continue; std::default_random_engine re(rd());
} std::uniform_int_distribution<> d(0, validPeds.size() - 1);
if ( counter > -1 )
{
if ( counter == 0 )
{
break;
}
counter --;
}
// Spawn a pedestrian from the available pool int counter = availablePeds;
auto ped = world->createPedestrian(validPeds[d(re)], spawn->position + glm::vec3( 0.f, 0.f, 1.f ) ); // maxSpawn can be -1 for "as many as possible"
ped->setLifetime(GameObject::TrafficLifetime); if (maxSpawn > -1) {
ped->controller->setGoal(CharacterController::TrafficWander); counter = std::min(availablePeds, maxSpawn);
created.push_back( ped ); }
}
for (AIGraphNode* spawn : available) {
// Find places it's legal to spawn things if (spawn->type != AIGraphNode::Pedestrian) {
continue;
return created; }
if (counter > -1) {
if (counter == 0) {
break;
}
counter--;
}
// Spawn a pedestrian from the available pool
auto ped = world->createPedestrian(
validPeds[d(re)], spawn->position + glm::vec3(0.f, 0.f, 1.f));
ped->setLifetime(GameObject::TrafficLifetime);
ped->controller->setGoal(CharacterController::TrafficWander);
created.push_back(ped);
}
// Find places it's legal to spawn things
return created;
} }
void TrafficDirector::setPopulationLimits(int maxPeds, int maxCars) void TrafficDirector::setPopulationLimits(int maxPeds, int maxCars) {
{ maximumPedestrians = maxPeds;
maximumPedestrians = maxPeds; maximumCars = maxCars;
maximumCars = maxCars;
} }

View File

@ -11,34 +11,35 @@ class GameObject;
class GameWorld; class GameWorld;
class ViewCamera; class ViewCamera;
class TrafficDirector class TrafficDirector {
{
public: public:
TrafficDirector(AIGraph* graph, GameWorld* world);
TrafficDirector(AIGraph* graph, GameWorld* world);
std::vector< AIGraphNode* > findAvailableNodes(AIGraphNode::NodeType type, const ViewCamera& camera, float radius);
void setDensity(AIGraphNode::NodeType type, float density);
/** std::vector<AIGraphNode*> findAvailableNodes(AIGraphNode::NodeType type,
* Creates new traffic at available locations. const ViewCamera& camera,
* @param camera The camera to spawn around float radius);
* @param radius the maximum distance to spawn in
* @param max The maximum number of traffic to create. void setDensity(AIGraphNode::NodeType type, float density);
*/
std::vector<GameObject*> populateNearby( const ViewCamera& camera, float radius, int maxSpawn = -1 ); /**
* Creates new traffic at available locations.
* @param camera The camera to spawn around
* @param radius the maximum distance to spawn in
* @param max The maximum number of traffic to create.
*/
std::vector<GameObject*> populateNearby(const ViewCamera& camera,
float radius, int maxSpawn = -1);
/**
* Sets the maximum number of pedestrians and cars in the traffic system
*/
void setPopulationLimits(int maxPeds, int maxCars);
/**
* Sets the maximum number of pedestrians and cars in the traffic system
*/
void setPopulationLimits(int maxPeds, int maxCars);
private: private:
AIGraph* graph; AIGraph* graph;
GameWorld* world; GameWorld* world;
float pedDensity; float pedDensity;
float carDensity; float carDensity;
int maximumPedestrians; int maximumPedestrians;
int maximumCars; int maximumCars;
}; };