302 lines
9.1 KiB
C++
302 lines
9.1 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: "Force-field" obstacle avoidance & steering
|
|
//
|
|
// @Note (toml 06-18-02): Currently only controls direction. Ultimately could
|
|
// also incorporate body facing (yaw), speed, and translational/rotational
|
|
// acceleration.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#ifndef AI_MOVESOLVER_H
|
|
#define AI_MOVESOLVER_H
|
|
|
|
#if defined(_WIN32)
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "ai_obstacle_type.h"
|
|
#include "utlvector.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline float NormalizeAngle(float angle) {
|
|
if (angle < 0.0)
|
|
angle += 360.0;
|
|
else if (angle >= 360.0)
|
|
angle -= 360.0;
|
|
return angle;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ENUMERATIONS
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// STRUCTURES
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-------------------------------------
|
|
// AI_Arc_t
|
|
//
|
|
// Purpose: Represents an arc.
|
|
//
|
|
//-------------------------------------
|
|
struct AI_Arc_t {
|
|
AI_Arc_t() : center(0), span(0) {}
|
|
|
|
// Set by center and span
|
|
void Set(float newCenter, float newSpan);
|
|
|
|
// Set by the right and left extremes (coordinates run counter clockwise)
|
|
void SetByLimits(float yawRight, float yawLeft);
|
|
|
|
// Center of the arc (as "yaw")
|
|
float center;
|
|
|
|
// Span of the arc (in degrees)
|
|
float span;
|
|
};
|
|
|
|
//-------------------------------------
|
|
// AI_MoveSuggestion_t
|
|
//
|
|
// Purpose: Suggests a possible move/avoidance, with a range of acceptable
|
|
// alternatives
|
|
//
|
|
// @Note (toml 06-20-02): this probably will eventually want to incorporate
|
|
// facing and destination of the motivating goal.
|
|
//
|
|
//-------------------------------------
|
|
|
|
enum AI_MoveSuggestionFlags_t {
|
|
AIMS_FAVOR_LEFT = 0x01,
|
|
AIMS_FAVOR_RIGHT = 0x02
|
|
};
|
|
|
|
//-----------------
|
|
|
|
struct AI_MoveSuggestion_t {
|
|
AI_MoveSuggestion_t();
|
|
AI_MoveSuggestion_t(AI_MoveSuggType_t newType, float newWeight,
|
|
float newDir, float newSpan,
|
|
CBaseEntity *pEntity = NULL);
|
|
AI_MoveSuggestion_t(AI_MoveSuggType_t newType, float newWeight,
|
|
const AI_Arc_t &arc, CBaseEntity *pEntity = NULL);
|
|
|
|
void Set(AI_MoveSuggType_t newType, float newWeight, float newDir,
|
|
float newSpan, CBaseEntity *pEntity = NULL);
|
|
void Set(AI_MoveSuggType_t newType, float newWeight, const AI_Arc_t &arc,
|
|
CBaseEntity *pEntity = NULL);
|
|
|
|
//---------------------------------
|
|
|
|
// The kind of suggestion
|
|
AI_MoveSuggType_t type;
|
|
|
|
// The unadjusted weight of the suggestion [0..1], although [-1..1] within
|
|
// the solver
|
|
float weight;
|
|
|
|
// The desired direction to move/avoid
|
|
AI_Arc_t arc;
|
|
|
|
// The causing entity, if any
|
|
EHANDLE hObstacleEntity;
|
|
|
|
// Flags
|
|
unsigned flags;
|
|
};
|
|
|
|
//-----------------
|
|
|
|
typedef CUtlVector<AI_MoveSuggestion_t> CAI_MoveSuggestions;
|
|
|
|
//-------------------------------------
|
|
// AI_MoveSolution_t
|
|
//
|
|
// Purpose: The result of resolving suggestions
|
|
//
|
|
// @Note (toml 06-18-02): Currently, this is a very dopey little structure.
|
|
// However, it will probably eventually incorporate much of the info
|
|
// passed or calculated piecemeal between Move...() and Move...Execute()
|
|
// functions. Once suggestions incorprate more information, the solution
|
|
// may want to include a copy of the winning suggestion, so that the
|
|
// caller need retain less state. If this is not the case, reduce it to just
|
|
// a yaw.
|
|
//
|
|
//-------------------------------------
|
|
struct AI_MoveSolution_t {
|
|
AI_MoveSolution_t() : dir(0) {}
|
|
|
|
// The direction to move
|
|
float dir;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// class CAI_MoveSolver
|
|
//
|
|
// Purpose: Given a set of precalculated "regulations" (typically negative),
|
|
// and a set of instantaneous suggestions (usually
|
|
//positive)
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CAI_MoveSolver {
|
|
public:
|
|
CAI_MoveSolver();
|
|
|
|
//---------------------------------
|
|
// Purpose: A regulation is a suggestion that is kept around as a rule until
|
|
// cleared. They are generally negative suggestions.
|
|
//---------------------------------
|
|
void AddRegulation(const AI_MoveSuggestion_t &suggestion);
|
|
void AddRegulations(const AI_MoveSuggestion_t *pSuggestion,
|
|
int nSuggestions);
|
|
|
|
bool HaveRegulations() const;
|
|
void ClearRegulations();
|
|
|
|
//---------------------------------
|
|
// Purpose: Solve the move, picking the best direction from a set of
|
|
// suggestions,
|
|
// after applying the regulations
|
|
//---------------------------------
|
|
bool Solve(const AI_MoveSuggestion_t *pSuggestions, int nSuggestions,
|
|
AI_MoveSolution_t *pResult);
|
|
bool Solve(const AI_MoveSuggestion_t &suggestion,
|
|
AI_MoveSolution_t *pResult);
|
|
|
|
//---------------------------------
|
|
bool HaveRegulationForObstacle(CBaseEntity *pEntity);
|
|
|
|
//---------------------------------
|
|
// Visualization
|
|
void VisualizeRegulations(const Vector &origin);
|
|
|
|
private:
|
|
enum {
|
|
REGS_RESERVE = 8,
|
|
};
|
|
|
|
//---------------------------------
|
|
void NormalizeSuggestions(AI_MoveSuggestion_t *pBegin,
|
|
AI_MoveSuggestion_t *pEnd);
|
|
|
|
//---------------------------------
|
|
CAI_MoveSuggestions m_Regulations;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// AI_Arc_t inline methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline void AI_Arc_t::Set(float newCenter, float newSpan) {
|
|
center = NormalizeAngle(newCenter);
|
|
span = NormalizeAngle(newSpan);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline void AI_Arc_t::SetByLimits(float yawRight, float yawLeft) {
|
|
// Yaw runs counter-clockwise
|
|
span = yawLeft - yawRight;
|
|
|
|
if (span < 0) span += 360;
|
|
|
|
center = yawRight + span * 0.5;
|
|
|
|
if (center >= 360) center -= 360;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// AI_MoveSuggestion_t inline methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline void AI_MoveSuggestion_t::Set(AI_MoveSuggType_t newType, float newWeight,
|
|
float newDir, float newSpan,
|
|
CBaseEntity *pEntity) {
|
|
type = newType;
|
|
weight = newWeight;
|
|
hObstacleEntity = pEntity;
|
|
flags = 0;
|
|
|
|
arc.Set(newDir, newSpan);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline AI_MoveSuggestion_t::AI_MoveSuggestion_t()
|
|
: type(AIMS_INVALID), weight(0), flags(0) {}
|
|
|
|
//-------------------------------------
|
|
|
|
inline AI_MoveSuggestion_t::AI_MoveSuggestion_t(AI_MoveSuggType_t newType,
|
|
float newWeight, float newDir,
|
|
float newSpan,
|
|
CBaseEntity *pEntity) {
|
|
Set(newType, newWeight, newDir, newSpan, pEntity);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline AI_MoveSuggestion_t::AI_MoveSuggestion_t(AI_MoveSuggType_t newType,
|
|
float newWeight,
|
|
const AI_Arc_t &arc,
|
|
CBaseEntity *pEntity) {
|
|
Set(newType, newWeight, arc.center, arc.span, pEntity);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline void AI_MoveSuggestion_t::Set(AI_MoveSuggType_t newType, float newWeight,
|
|
const AI_Arc_t &arc,
|
|
CBaseEntity *pEntity) {
|
|
Set(newType, newWeight, arc.center, arc.span, pEntity);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CAI_MoveSolver inline methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline CAI_MoveSolver::CAI_MoveSolver() {
|
|
m_Regulations.EnsureCapacity(REGS_RESERVE);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline void CAI_MoveSolver::AddRegulation(
|
|
const AI_MoveSuggestion_t &suggestion) {
|
|
m_Regulations.AddToTail(suggestion);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline void CAI_MoveSolver::AddRegulations(
|
|
const AI_MoveSuggestion_t *pSuggestions, int nSuggestions) {
|
|
for (int i = 0; i < nSuggestions; ++i) {
|
|
m_Regulations.AddToTail(pSuggestions[i]);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline bool CAI_MoveSolver::HaveRegulations() const {
|
|
return (m_Regulations.Count() > 0);
|
|
}
|
|
|
|
//-------------------------------------
|
|
|
|
inline void CAI_MoveSolver::ClearRegulations() { m_Regulations.RemoveAll(); }
|
|
|
|
//-------------------------------------
|
|
|
|
inline bool CAI_MoveSolver::Solve(const AI_MoveSuggestion_t &suggestion,
|
|
AI_MoveSolution_t *pResult) {
|
|
return Solve(&suggestion, 1, pResult);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
#endif // AI_MOVESOLVER_H
|