Bot integration for v3.3b8 (#156)

* Initial bot commit

* Added server commands and cvars for adding AI players to the game.
* Added auto modes for automating the adding and removal of bots
* Bots connect to the server and join teams correctly

* Added round restart and new map detection for AI system

Push before new project added for detour

* Initial bot integration

* Integrated all basic bot code for navigation and task performing
* Added support for multi_managers to better understand how buttons and triggers affect doors

* Improved bot understanding of door triggers and weldables

* Reworked nav profiles

Nav profiles for bots are now dynamically updated to take into account changing capabilities, such as picking up a welder

* Improved bot door usage

* Added weldable obstacles back into navigation

Bots now understand how to get around weldable barriers

* Replaced fixed arrays with vectors

* Resource node and hive lists are now vectors.
* Further improved bot weld behaviour

* Added dynamic reachability calculations

When barriers and doors are open/closed, new reachability calculations are done for structures and items so bots understand when items/structures become reachable or unreachable as the match progresses.

* Added team-based reachability calculations

Reachabilities for structures and items are now based on the team, so bots understand when they can't reach a structure from their spawn point.

* Implemented long-range off-mesh connections and dynamic off-mesh connections

* Implemented fully dynamic off-mesh connections

Phase gates now use connections rather than custom path finding. Much more performant.

* Replaced arrays with vectors for simpler code

* Started Bot Swimming

* Bots understand trigger_changetarget

Bots can now navigate doors operated with a trigger_changetarget so they understand the sequence in which triggers must be activated to make it work

* Push before trying to fix long-range connections

* Implement new off-mesh connection system

* Redid population of door triggers

* Fixed trigger types and links to doors

* Added lift and moving platform support

* Lift improvements

* Bots avoid getting crushed under a lift when summoning it
* Bots are better at judging which stop a platform needs to be at

* Tweak lift and welder usage

* Fixed bug with multiple off-mesh connections close together

* Finish lift movement

* Fixed dodgy path finding

* Improved skulk ladder usage and lerk lift usage

* Fix crash with path finding

* Re-implement commander AI

* Commander improvements

* Improve commander sieging

* Commander scanning tweak

* Reimplemented regular marine AI

* Start reimplementing alien AI

* Implement gorge building behaviours

* Start alien tactical decisioning

* Continuing alien building and other non-combat logic

* More alien role work

* Adjusted base node definitions

* Iterate Capper Logic

* Alien assault AI

* Alien Combat

* Fix grenade throwing, better combat

* Marine combat AI improvements

* Commander improvements

* Commander + nav improvements

* Drop mines

* Improved bot stuck detection

* Commander supply improvements

* Bot fill timing config

* Added nsbots.cfg to configure internal bots

* Changed bot config file to "nsbots.cfg"

* Bug fixing with navigation

* Fix skulk movement on ladders

* Improved commander placement and tactical refresh

* Fixed bug with ladder climbing

* Doors block off-mesh connections

* Finished doors blocking connections

* Marine and alien tactical bug fixes

* Add commander beacon back in

* Start combat mode stuff

* First pass at combat mode

* Bots attack turrets

* Fix ladder and wall climbing

* Commander chat request

* Improved skulk ladders

* Added nav meshes for new bot code

* Added bot configuration to listen server menu

* Added bot config file

* Added default bot config to listenserver.cfg

* Added default bot settings to server.cfg

* Include VS filter for bot files

* Crash fixes

* Bot improvements

* Bot stability and mine placement improvements

* Fixed crash on new map start with bots

* Reverted Svencoop fix

* Fixed crash, added more cvars

* Performance improvement

* Commander building improvements

* Stop bot spasming when waiting to take command

* Fixed doors not blocking connections

* Added bot disabled guard to round start

* Commander improvements, movement improvements

* Tweaked level load sequence

* Performance improvements

* Bot load spread

* Fixed commander update

* Refactor bot frame handling

* Bug fixes + Pierow's dynamic load spread

* Minor bug fixes

* Fix door detection, prep for test

* Fixed commander siege spam

* linux compile test

* fix hardcoded inlcudes

* O1 compile flag for detour
- fix linux server crash

* Revert detour compile flags to original for windows

* linux build update

* remove x64 build configs

* update bot nav meshes and configs

* fix bot physics at high server fps, update navmeshes. from @RGreenlees

---------

Co-authored-by: RGreenlees <RGreenlees@users.noreply.github.com>
Co-authored-by: RichardGreenlees <richard.greenlees@forecast.global>
This commit is contained in:
pierow 2024-03-21 14:17:18 -04:00 committed by GitHub
parent acc1056ec8
commit 58358d0927
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
117 changed files with 52447 additions and 140 deletions

View file

@ -56,6 +56,13 @@ mp_killdelay 3
mp_flashlight 1
mp_footsteps 1
// AI Player Settings
mp_botusemapdefaults 1
mp_botminplayers 0
mp_botallowlerk 1
mp_botallowfade 1
mp_botallowonos 1
// disable autoaim and mad gibs
sv_aim 0
violence_hgibs 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/co_angst.nav Normal file

Binary file not shown.

BIN
main/navmeshes/co_core.nav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/co_pulse.nav Normal file

Binary file not shown.

BIN
main/navmeshes/co_sava.nav Normal file

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/co_umbra.nav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/ns_ayumi.nav Normal file

Binary file not shown.

BIN
main/navmeshes/ns_bast.nav Normal file

Binary file not shown.

BIN
main/navmeshes/ns_caged.nav Normal file

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/ns_eon.nav Normal file

Binary file not shown.

BIN
main/navmeshes/ns_hera.nav Normal file

Binary file not shown.

BIN
main/navmeshes/ns_lost.nav Normal file

Binary file not shown.

BIN
main/navmeshes/ns_lucid.nav Normal file

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/ns_metal.nav Normal file

Binary file not shown.

BIN
main/navmeshes/ns_nancy.nav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/ns_shiva.nav Normal file

Binary file not shown.

Binary file not shown.

BIN
main/navmeshes/ns_veil.nav Normal file

Binary file not shown.

112
main/nsbots.ini Normal file
View file

@ -0,0 +1,112 @@
### General bot settings ###
# What prefix to put in front of a bot's name (can leave blank)
prefix=[BOT]
# If the bot is stuck trying to move for this long(in seconds), it will suicide to start again.
# 0 = Will not suicide
MaxStuckTime=30
# When should the server start adding bots?
# 0 = On map load (after 5 second grace period)
# 1 = When all humans have joined a team (i.e. no more humans left in ready room)
# 2 = When the round has started (after countdown)
BotFillTiming=1
### Skill Settings ###
# Bot skill settings. You can define as many settings as you like and reference them by name
# Format is BotSkillName = name, followed by one of the following:
# ReactionTime = How quickly in seconds the bot will react to sighting enemies
# AimSkill = How accurately the bot can lock sights on you after seeing you (0.0 - 1.0)
# MovementTracking = How accurately the bot can follow a moving target (0.0 - 1.0)
# ViewSpeed = How fast the bot can swivel its view (0.1 - 2.0)
# Reference the difficulties by name using the 'botskill' command (see Help.txt)
BotSkillName=Easy
MarineReactionTime=0.5
MarineAimSkill=0.1
MarineMovementTracking=0.1
MarineViewSpeed=0.5
AlienReactionTime=0.5
AlienAimSkill=0.2
AlienMovementTracking=0.2
AlienViewSpeed=0.75
BotSkillName=Medium
MarineReactionTime=0.2
MarineAimSkill=0.5
MarineMovementTracking=0.4
MarineViewSpeed=1.0
AlienReactionTime=0.2
AlienAimSkill=0.5
AlienMovementTracking=0.5
AlienViewSpeed=1.3
BotSkillName=Hard
MarineReactionTime=0.2
MarineAimSkill=0.6
MarineMovementTracking=0.6
MarineViewSpeed=1.5
AlienReactionTime=0.2
AlienAimSkill=0.8
AlienMovementTracking=0.8
AlienViewSpeed=1.5
BotSkillName=Godlike
MarineReactionTime=0.1
MarineAimSkill=1.0
MarineMovementTracking=1.0
MarineViewSpeed=2.0
AlienReactionTime=0.1
AlienAimSkill=1.0
AlienMovementTracking=1.0
AlienViewSpeed=2.0
# Default bot skill level for all bots created. Must be a valid skill defined above
DefaultSkillLevel=Medium
# Desired team sizes. Only used if bot fill mode is 'fillteams'.
# Format is TeamSize=mapname:nummarines/numaliens
# 'default' will be used if playing a map not listed below
TeamSize=default:7/7
TeamSize=ns_machina:8/8
TeamSize=ns_ragnarok:8/8
TeamSize=co_faceoff:4/4
TeamSize=co_core:4/4
TeamSize=co_pulse:6/6
TeamSize=co_ulysses:6/6
TeamSize=co_niveus:5/5
TeamSize=co_kestrel:5/5
### Commander Settings ###
# If commander is enabled then after match start, how long should the bot wait to allow a human to take command (in seconds)
# Note that the bot will ignore this if no humans are present at the base
CommanderWaitTime=10
### Alien Settings ###
# Preferred chamber sequence. Valid entries are 'defense', 'movement' and 'sensory'. Separate sequence with forward slash
# You can also use ? for random, so if you want movement always first but then defense and sensory at random, use
# ChamberSequence:movement/?/?
# # Or if you want sensory always last, but movement and defence random, use
# ChamberSequence=?/?/sensory
ChamberSequence=defense/movement/sensory
# Lerk cooldown in seconds. How long should bots wait after a lerk has died to replace them?
# Lerks are fragile, so this prevents bots taking it in turns to go lerk every time one dies and burning through all their res.
LerkCooldown=60
# Enabled life forms. If you don't want the bots to evolve to certain life forms then you can disable them here.
# 0 = Disabled, 1 = Enabled
AllowLerk=1
AllowFade=1
AllowOnos=1

View file

@ -58,6 +58,14 @@ mp_killdelay 3
mp_flashlight 1
mp_footsteps 1
// AI Player Settings
mp_botsenabled 0
mp_botminplayers 0
mp_botusemapdefaults 1
mp_botskill 1
mp_botautomode 0
mp_botcommandermode 0
// disable autoaim and mad gibs
sv_aim 0
violence_hgibs 0

View file

@ -32,7 +32,7 @@
// Half-Life Server Configuration Layout Script (stores last settings chosen, too)
// File generated: Sun May 07 10:30:06 AM
// File generated: Thu Mar 14 10:53:31 AM
//
//
// Cvar - Setting
@ -45,7 +45,7 @@ DESCRIPTION SERVER_OPTIONS
{
"LAN Game"
{ BOOL }
{ "0" }
{ "1" }
}
"hostname"
@ -158,4 +158,46 @@ DESCRIPTION SERVER_OPTIONS
{ "0.510000" }
}
"mp_botsenabled"
{
"Enable Bots"
{ BOOL }
{ "0" }
}
"mp_botskill"
{
"Bot Difficulty"
{
LIST
"Easy" "0"
"Medium" "1"
"Hard" "2"
"Godlike" "3"
}
{ "1.000000" }
}
"mp_botautomode"
{
"Bot Mode"
{
LIST
"Manual" "0"
"Automatic" "1"
}
{ "1.000000" }
}
"mp_botcommandermode"
{
"Allow AI Commander"
{
LIST
"Disabled" "0"
"Enabled" "1"
}
{ "1.000000" }
}
}

View file

@ -8,9 +8,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cl_dll.dll", "cl_dll\cl_dll
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ns.dll", "dlls\hl.vcxproj", "{BC87A180-F17B-83FC-5D7D-470FAD003ABC}"
ProjectSection(ProjectDependencies) = postProject
{36E392B2-EA9F-43FE-9700-82DFE548C75D} = {36E392B2-EA9F-43FE-9700-82DFE548C75D}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "particles", "particles\particles.vcxproj", "{5AADD469-7488-4B34-A9FD-01CFAC5972FD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "detour", "detour\detour.vcxproj", "{36E392B2-EA9F-43FE-9700-82DFE548C75D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@ -36,6 +41,12 @@ Global
{5AADD469-7488-4B34-A9FD-01CFAC5972FD}.Playtest|Win32.Build.0 = Particles - Release|Win32
{5AADD469-7488-4B34-A9FD-01CFAC5972FD}.Release|Win32.ActiveCfg = Particles - Release|Win32
{5AADD469-7488-4B34-A9FD-01CFAC5972FD}.Release|Win32.Build.0 = Particles - Release|Win32
{36E392B2-EA9F-43FE-9700-82DFE548C75D}.Debug|Win32.ActiveCfg = Detour - Debug|Win32
{36E392B2-EA9F-43FE-9700-82DFE548C75D}.Debug|Win32.Build.0 = Detour - Debug|Win32
{36E392B2-EA9F-43FE-9700-82DFE548C75D}.Playtest|Win32.ActiveCfg = Detour - Release|Win32
{36E392B2-EA9F-43FE-9700-82DFE548C75D}.Playtest|Win32.Build.0 = Detour - Release|Win32
{36E392B2-EA9F-43FE-9700-82DFE548C75D}.Release|Win32.ActiveCfg = Detour - Release|Win32
{36E392B2-EA9F-43FE-9700-82DFE548C75D}.Release|Win32.Build.0 = Detour - Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,50 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include <stdlib.h>
#include "DetourAlloc.h"
static void *dtAllocDefault(size_t size, dtAllocHint)
{
return malloc(size);
}
static void dtFreeDefault(void *ptr)
{
free(ptr);
}
static dtAllocFunc* sAllocFunc = dtAllocDefault;
static dtFreeFunc* sFreeFunc = dtFreeDefault;
void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
{
sAllocFunc = allocFunc ? allocFunc : dtAllocDefault;
sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
}
void* dtAlloc(size_t size, dtAllocHint hint)
{
return sAllocFunc(size, hint);
}
void dtFree(void* ptr)
{
if (ptr)
sFreeFunc(ptr);
}

View file

@ -0,0 +1,35 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "DetourAssert.h"
#ifndef NDEBUG
static dtAssertFailFunc* sAssertFailFunc = 0;
void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc)
{
sAssertFailFunc = assertFailFunc;
}
dtAssertFailFunc* dtAssertFailGetCustom()
{
return sAssertFailFunc;
}
#endif

View file

@ -0,0 +1,387 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "DetourCommon.h"
#include "DetourMath.h"
//////////////////////////////////////////////////////////////////////////////////////////
void dtClosestPtPointTriangle(float* closest, const float* p,
const float* a, const float* b, const float* c)
{
// Check if P in vertex region outside A
float ab[3], ac[3], ap[3];
dtVsub(ab, b, a);
dtVsub(ac, c, a);
dtVsub(ap, p, a);
float d1 = dtVdot(ab, ap);
float d2 = dtVdot(ac, ap);
if (d1 <= 0.0f && d2 <= 0.0f)
{
// barycentric coordinates (1,0,0)
dtVcopy(closest, a);
return;
}
// Check if P in vertex region outside B
float bp[3];
dtVsub(bp, p, b);
float d3 = dtVdot(ab, bp);
float d4 = dtVdot(ac, bp);
if (d3 >= 0.0f && d4 <= d3)
{
// barycentric coordinates (0,1,0)
dtVcopy(closest, b);
return;
}
// Check if P in edge region of AB, if so return projection of P onto AB
float vc = d1*d4 - d3*d2;
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
{
// barycentric coordinates (1-v,v,0)
float v = d1 / (d1 - d3);
closest[0] = a[0] + v * ab[0];
closest[1] = a[1] + v * ab[1];
closest[2] = a[2] + v * ab[2];
return;
}
// Check if P in vertex region outside C
float cp[3];
dtVsub(cp, p, c);
float d5 = dtVdot(ab, cp);
float d6 = dtVdot(ac, cp);
if (d6 >= 0.0f && d5 <= d6)
{
// barycentric coordinates (0,0,1)
dtVcopy(closest, c);
return;
}
// Check if P in edge region of AC, if so return projection of P onto AC
float vb = d5*d2 - d1*d6;
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
{
// barycentric coordinates (1-w,0,w)
float w = d2 / (d2 - d6);
closest[0] = a[0] + w * ac[0];
closest[1] = a[1] + w * ac[1];
closest[2] = a[2] + w * ac[2];
return;
}
// Check if P in edge region of BC, if so return projection of P onto BC
float va = d3*d6 - d5*d4;
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
{
// barycentric coordinates (0,1-w,w)
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
closest[0] = b[0] + w * (c[0] - b[0]);
closest[1] = b[1] + w * (c[1] - b[1]);
closest[2] = b[2] + w * (c[2] - b[2]);
return;
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
float denom = 1.0f / (va + vb + vc);
float v = vb * denom;
float w = vc * denom;
closest[0] = a[0] + ab[0] * v + ac[0] * w;
closest[1] = a[1] + ab[1] * v + ac[1] * w;
closest[2] = a[2] + ab[2] * v + ac[2] * w;
}
bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
const float* verts, int nverts,
float& tmin, float& tmax,
int& segMin, int& segMax)
{
static const float EPS = 0.00000001f;
tmin = 0;
tmax = 1;
segMin = -1;
segMax = -1;
float dir[3];
dtVsub(dir, p1, p0);
for (int i = 0, j = nverts-1; i < nverts; j=i++)
{
float edge[3], diff[3];
dtVsub(edge, &verts[i*3], &verts[j*3]);
dtVsub(diff, p0, &verts[j*3]);
const float n = dtVperp2D(edge, diff);
const float d = dtVperp2D(dir, edge);
if (fabsf(d) < EPS)
{
// S is nearly parallel to this edge
if (n < 0)
return false;
else
continue;
}
const float t = n / d;
if (d < 0)
{
// segment S is entering across this edge
if (t > tmin)
{
tmin = t;
segMin = j;
// S enters after leaving polygon
if (tmin > tmax)
return false;
}
}
else
{
// segment S is leaving across this edge
if (t < tmax)
{
tmax = t;
segMax = j;
// S leaves before entering polygon
if (tmax < tmin)
return false;
}
}
}
return true;
}
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t)
{
float pqx = q[0] - p[0];
float pqz = q[2] - p[2];
float dx = pt[0] - p[0];
float dz = pt[2] - p[2];
float d = pqx*pqx + pqz*pqz;
t = pqx*dx + pqz*dz;
if (d > 0) t /= d;
if (t < 0) t = 0;
else if (t > 1) t = 1;
dx = p[0] + t*pqx - pt[0];
dz = p[2] + t*pqz - pt[2];
return dx*dx + dz*dz;
}
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
{
tc[0] = 0.0f;
tc[1] = 0.0f;
tc[2] = 0.0f;
for (int j = 0; j < nidx; ++j)
{
const float* v = &verts[idx[j]*3];
tc[0] += v[0];
tc[1] += v[1];
tc[2] += v[2];
}
const float s = 1.0f / nidx;
tc[0] *= s;
tc[1] *= s;
tc[2] *= s;
}
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
{
const float EPS = 1e-6f;
float v0[3], v1[3], v2[3];
dtVsub(v0, c, a);
dtVsub(v1, b, a);
dtVsub(v2, p, a);
// Compute scaled barycentric coordinates
float denom = v0[0] * v1[2] - v0[2] * v1[0];
if (fabsf(denom) < EPS)
return false;
float u = v1[2] * v2[0] - v1[0] * v2[2];
float v = v0[0] * v2[2] - v0[2] * v2[0];
if (denom < 0) {
denom = -denom;
u = -u;
v = -v;
}
// If point lies inside the triangle, return interpolated ycoord.
if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) {
h = a[1] + (v0[1] * u + v1[1] * v) / denom;
return true;
}
return false;
}
/// @par
///
/// All points are projected onto the xz-plane, so the y-values are ignored.
bool dtPointInPolygon(const float* pt, const float* verts, const int nverts)
{
// TODO: Replace pnpoly with triArea2D tests?
int i, j;
bool c = false;
for (i = 0, j = nverts-1; i < nverts; j = i++)
{
const float* vi = &verts[i*3];
const float* vj = &verts[j*3];
if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
(pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
c = !c;
}
return c;
}
bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
float* ed, float* et)
{
// TODO: Replace pnpoly with triArea2D tests?
int i, j;
bool c = false;
for (i = 0, j = nverts-1; i < nverts; j = i++)
{
const float* vi = &verts[i*3];
const float* vj = &verts[j*3];
if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
(pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
c = !c;
ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]);
}
return c;
}
static void projectPoly(const float* axis, const float* poly, const int npoly,
float& rmin, float& rmax)
{
rmin = rmax = dtVdot2D(axis, &poly[0]);
for (int i = 1; i < npoly; ++i)
{
const float d = dtVdot2D(axis, &poly[i*3]);
rmin = dtMin(rmin, d);
rmax = dtMax(rmax, d);
}
}
inline bool overlapRange(const float amin, const float amax,
const float bmin, const float bmax,
const float eps)
{
return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true;
}
/// @par
///
/// All vertices are projected onto the xz-plane, so the y-values are ignored.
bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
const float* polyb, const int npolyb)
{
const float eps = 1e-4f;
for (int i = 0, j = npolya-1; i < npolya; j=i++)
{
const float* va = &polya[j*3];
const float* vb = &polya[i*3];
const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
float amin,amax,bmin,bmax;
projectPoly(n, polya, npolya, amin,amax);
projectPoly(n, polyb, npolyb, bmin,bmax);
if (!overlapRange(amin,amax, bmin,bmax, eps))
{
// Found separating axis
return false;
}
}
for (int i = 0, j = npolyb-1; i < npolyb; j=i++)
{
const float* va = &polyb[j*3];
const float* vb = &polyb[i*3];
const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
float amin,amax,bmin,bmax;
projectPoly(n, polya, npolya, amin,amax);
projectPoly(n, polyb, npolyb, bmin,bmax);
if (!overlapRange(amin,amax, bmin,bmax, eps))
{
// Found separating axis
return false;
}
}
return true;
}
// Returns a random point in a convex polygon.
// Adapted from Graphics Gems article.
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
const float s, const float t, float* out)
{
// Calc triangle araes
float areasum = 0.0f;
for (int i = 2; i < npts; i++) {
areas[i] = dtTriArea2D(&pts[0], &pts[(i-1)*3], &pts[i*3]);
areasum += dtMax(0.001f, areas[i]);
}
// Find sub triangle weighted by area.
const float thr = s*areasum;
float acc = 0.0f;
float u = 1.0f;
int tri = npts - 1;
for (int i = 2; i < npts; i++) {
const float dacc = areas[i];
if (thr >= acc && thr < (acc+dacc))
{
u = (thr - acc) / dacc;
tri = i;
break;
}
acc += dacc;
}
float v = dtMathSqrtf(t);
const float a = 1 - v;
const float b = (1 - u) * v;
const float c = u * v;
const float* pa = &pts[0];
const float* pb = &pts[(tri-1)*3];
const float* pc = &pts[tri*3];
out[0] = a*pa[0] + b*pb[0] + c*pc[0];
out[1] = a*pa[1] + b*pb[1] + c*pc[1];
out[2] = a*pa[2] + b*pb[2] + c*pc[2];
}
inline float vperpXZ(const float* a, const float* b) { return a[0]*b[2] - a[2]*b[0]; }
bool dtIntersectSegSeg2D(const float* ap, const float* aq,
const float* bp, const float* bq,
float& s, float& t)
{
float u[3], v[3], w[3];
dtVsub(u,aq,ap);
dtVsub(v,bq,bp);
dtVsub(w,ap,bp);
float d = vperpXZ(u,v);
if (fabsf(d) < 1e-6f) return false;
s = vperpXZ(v,w) / d;
t = vperpXZ(u,w) / d;
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,776 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include "DetourNavMesh.h"
#include "DetourCommon.h"
#include "DetourMath.h"
#include "DetourNavMeshBuilder.h"
#include "DetourAlloc.h"
#include "DetourAssert.h"
static unsigned short MESH_NULL_IDX = 0xffff;
struct BVItem
{
unsigned short bmin[3];
unsigned short bmax[3];
int i;
};
static int compareItemX(const void* va, const void* vb)
{
const BVItem* a = (const BVItem*)va;
const BVItem* b = (const BVItem*)vb;
if (a->bmin[0] < b->bmin[0])
return -1;
if (a->bmin[0] > b->bmin[0])
return 1;
return 0;
}
static int compareItemY(const void* va, const void* vb)
{
const BVItem* a = (const BVItem*)va;
const BVItem* b = (const BVItem*)vb;
if (a->bmin[1] < b->bmin[1])
return -1;
if (a->bmin[1] > b->bmin[1])
return 1;
return 0;
}
static int compareItemZ(const void* va, const void* vb)
{
const BVItem* a = (const BVItem*)va;
const BVItem* b = (const BVItem*)vb;
if (a->bmin[2] < b->bmin[2])
return -1;
if (a->bmin[2] > b->bmin[2])
return 1;
return 0;
}
static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax,
unsigned short* bmin, unsigned short* bmax)
{
bmin[0] = items[imin].bmin[0];
bmin[1] = items[imin].bmin[1];
bmin[2] = items[imin].bmin[2];
bmax[0] = items[imin].bmax[0];
bmax[1] = items[imin].bmax[1];
bmax[2] = items[imin].bmax[2];
for (int i = imin+1; i < imax; ++i)
{
const BVItem& it = items[i];
if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0];
if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1];
if (it.bmin[2] < bmin[2]) bmin[2] = it.bmin[2];
if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2];
}
}
inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
{
int axis = 0;
unsigned short maxVal = x;
if (y > maxVal)
{
axis = 1;
maxVal = y;
}
if (z > maxVal)
{
axis = 2;
}
return axis;
}
static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes)
{
int inum = imax - imin;
int icur = curNode;
dtBVNode& node = nodes[curNode++];
if (inum == 1)
{
// Leaf
node.bmin[0] = items[imin].bmin[0];
node.bmin[1] = items[imin].bmin[1];
node.bmin[2] = items[imin].bmin[2];
node.bmax[0] = items[imin].bmax[0];
node.bmax[1] = items[imin].bmax[1];
node.bmax[2] = items[imin].bmax[2];
node.i = items[imin].i;
}
else
{
// Split
calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
int axis = longestAxis(node.bmax[0] - node.bmin[0],
node.bmax[1] - node.bmin[1],
node.bmax[2] - node.bmin[2]);
if (axis == 0)
{
// Sort along x-axis
qsort(items+imin, inum, sizeof(BVItem), compareItemX);
}
else if (axis == 1)
{
// Sort along y-axis
qsort(items+imin, inum, sizeof(BVItem), compareItemY);
}
else
{
// Sort along z-axis
qsort(items+imin, inum, sizeof(BVItem), compareItemZ);
}
int isplit = imin+inum/2;
// Left
subdivide(items, nitems, imin, isplit, curNode, nodes);
// Right
subdivide(items, nitems, isplit, imax, curNode, nodes);
int iescape = curNode - icur;
// Negative index means escape.
node.i = -iescape;
}
}
static int createBVTree(dtNavMeshCreateParams* params, dtBVNode* nodes, int /*nnodes*/)
{
// Build tree
float quantFactor = 1 / params->cs;
BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*params->polyCount, DT_ALLOC_TEMP);
for (int i = 0; i < params->polyCount; i++)
{
BVItem& it = items[i];
it.i = i;
// Calc polygon bounds. Use detail meshes if available.
if (params->detailMeshes)
{
int vb = (int)params->detailMeshes[i*4+0];
int ndv = (int)params->detailMeshes[i*4+1];
float bmin[3];
float bmax[3];
const float* dv = &params->detailVerts[vb*3];
dtVcopy(bmin, dv);
dtVcopy(bmax, dv);
for (int j = 1; j < ndv; j++)
{
dtVmin(bmin, &dv[j * 3]);
dtVmax(bmax, &dv[j * 3]);
}
// BV-tree uses cs for all dimensions
it.bmin[0] = (unsigned short)dtClamp((int)((bmin[0] - params->bmin[0])*quantFactor), 0, 0xffff);
it.bmin[1] = (unsigned short)dtClamp((int)((bmin[1] - params->bmin[1])*quantFactor), 0, 0xffff);
it.bmin[2] = (unsigned short)dtClamp((int)((bmin[2] - params->bmin[2])*quantFactor), 0, 0xffff);
it.bmax[0] = (unsigned short)dtClamp((int)((bmax[0] - params->bmin[0])*quantFactor), 0, 0xffff);
it.bmax[1] = (unsigned short)dtClamp((int)((bmax[1] - params->bmin[1])*quantFactor), 0, 0xffff);
it.bmax[2] = (unsigned short)dtClamp((int)((bmax[2] - params->bmin[2])*quantFactor), 0, 0xffff);
}
else
{
const unsigned short* p = &params->polys[i*params->nvp * 2];
it.bmin[0] = it.bmax[0] = params->verts[p[0] * 3 + 0];
it.bmin[1] = it.bmax[1] = params->verts[p[0] * 3 + 1];
it.bmin[2] = it.bmax[2] = params->verts[p[0] * 3 + 2];
for (int j = 1; j < params->nvp; ++j)
{
if (p[j] == MESH_NULL_IDX) break;
unsigned short x = params->verts[p[j] * 3 + 0];
unsigned short y = params->verts[p[j] * 3 + 1];
unsigned short z = params->verts[p[j] * 3 + 2];
if (x < it.bmin[0]) it.bmin[0] = x;
if (y < it.bmin[1]) it.bmin[1] = y;
if (z < it.bmin[2]) it.bmin[2] = z;
if (x > it.bmax[0]) it.bmax[0] = x;
if (y > it.bmax[1]) it.bmax[1] = y;
if (z > it.bmax[2]) it.bmax[2] = z;
}
// Remap y
it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1] * params->ch / params->cs);
it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1] * params->ch / params->cs);
}
}
int curNode = 0;
subdivide(items, params->polyCount, 0, params->polyCount, curNode, nodes);
dtFree(items);
return curNode;
}
static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax)
{
static const unsigned char XP = 1<<0;
static const unsigned char ZP = 1<<1;
static const unsigned char XM = 1<<2;
static const unsigned char ZM = 1<<3;
unsigned char outcode = 0;
outcode |= (pt[0] >= bmax[0]) ? XP : 0;
outcode |= (pt[2] >= bmax[2]) ? ZP : 0;
outcode |= (pt[0] < bmin[0]) ? XM : 0;
outcode |= (pt[2] < bmin[2]) ? ZM : 0;
switch (outcode)
{
case XP: return 0;
case XP|ZP: return 1;
case ZP: return 2;
case XM|ZP: return 3;
case XM: return 4;
case XM|ZM: return 5;
case ZM: return 6;
case XP|ZM: return 7;
};
return 0xff;
}
// TODO: Better error handling.
/// @par
///
/// The output data array is allocated using the detour allocator (dtAlloc()). The method
/// used to free the memory will be determined by how the tile is added to the navigation
/// mesh.
///
/// @see dtNavMesh, dtNavMesh::addTile()
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize)
{
if (params->nvp > DT_VERTS_PER_POLYGON)
return false;
if (params->vertCount >= 0xffff)
return false;
if (!params->vertCount || !params->verts)
return false;
if (!params->polyCount || !params->polys)
return false;
const int nvp = params->nvp;
// Classify off-mesh connection points. We store only the connections
// whose start point is inside the tile.
unsigned char* offMeshConClass = 0;
int storedOffMeshConCount = 0;
int offMeshConLinkCount = 0;
if (params->NumOffMeshConnections > 0)
{
offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP);
if (!offMeshConClass)
return false;
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
bool bOriginates = (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer);
bool bTargets = (con->ToTileX == params->tileX && con->ToTileY == params->tileY && con->ToTileLayer == params->tileLayer);
if (bOriginates)
{
con->state = DT_OFFMESH_DIRTY;
offMeshConLinkCount++;
storedOffMeshConCount++;
}
if (bTargets)
{
con->state = DT_OFFMESH_DIRTY;
offMeshConLinkCount++;
}
}
}
// Off-mesh connectionss are stored as polygons, adjust values.
const int totPolyCount = params->polyCount + storedOffMeshConCount;
const int totVertCount = params->vertCount + storedOffMeshConCount*2;
// Find portal edges which are at tile borders.
int edgeCount = 0;
int portalCount = 0;
for (int i = 0; i < params->polyCount; ++i)
{
const unsigned short* p = &params->polys[i*2*nvp];
for (int j = 0; j < nvp; ++j)
{
if (p[j] == MESH_NULL_IDX) break;
edgeCount++;
if (p[nvp+j] & 0x8000)
{
unsigned short dir = p[nvp+j] & 0xf;
if (dir != 0xf)
portalCount++;
}
}
}
const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2;
// Find unique detail vertices.
int uniqueDetailVertCount = 0;
int detailTriCount = 0;
if (params->detailMeshes)
{
// Has detail mesh, count unique detail vertex count and use input detail tri count.
detailTriCount = params->detailTriCount;
for (int i = 0; i < params->polyCount; ++i)
{
const unsigned short* p = &params->polys[i*nvp*2];
int ndv = params->detailMeshes[i*4+1];
int nv = 0;
for (int j = 0; j < nvp; ++j)
{
if (p[j] == MESH_NULL_IDX) break;
nv++;
}
ndv -= nv;
uniqueDetailVertCount += ndv;
}
}
else
{
// No input detail mesh, build detail mesh from nav polys.
uniqueDetailVertCount = 0; // No extra detail verts.
detailTriCount = 0;
for (int i = 0; i < params->polyCount; ++i)
{
const unsigned short* p = &params->polys[i*nvp*2];
int nv = 0;
for (int j = 0; j < nvp; ++j)
{
if (p[j] == MESH_NULL_IDX) break;
nv++;
}
detailTriCount += nv-2;
}
}
// Calculate data size
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount);
const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount);
const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount);
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount);
const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount);
const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0;
const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection*)*storedOffMeshConCount);
const int dataSize = headerSize + vertsSize + polysSize + linksSize +
detailMeshesSize + detailVertsSize + detailTrisSize +
bvTreeSize + offMeshConsSize;
unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
if (!data)
{
dtFree(offMeshConClass);
return false;
}
memset(data, 0, dataSize);
unsigned char* d = data;
dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize);
float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
dtOffMeshConnection** offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection*>(d, offMeshConsSize);
// Store header
header->magic = DT_NAVMESH_MAGIC;
header->version = DT_NAVMESH_VERSION;
header->x = params->tileX;
header->y = params->tileY;
header->layer = params->tileLayer;
header->userId = params->userId;
header->polyCount = totPolyCount;
header->vertCount = totVertCount;
header->maxLinkCount = maxLinkCount;
dtVcopy(header->bmin, params->bmin);
dtVcopy(header->bmax, params->bmax);
header->detailMeshCount = params->polyCount;
header->detailVertCount = uniqueDetailVertCount;
header->detailTriCount = detailTriCount;
header->bvQuantFactor = 1.0f / params->cs;
header->offMeshBase = params->polyCount;
header->walkableHeight = params->walkableHeight;
header->walkableRadius = params->walkableRadius;
header->walkableClimb = params->walkableClimb;
header->offMeshConCount = offMeshConLinkCount;
header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0;
const int offMeshVertsBase = params->vertCount;
const int offMeshPolyBase = params->polyCount;
// Store vertices
// Mesh vertices
for (int i = 0; i < params->vertCount; ++i)
{
const unsigned short* iv = &params->verts[i*3];
float* v = &navVerts[i*3];
v[0] = params->bmin[0] + iv[0] * params->cs;
v[1] = params->bmin[1] + iv[1] * params->ch;
v[2] = params->bmin[2] + iv[2] * params->cs;
}
// Off-mesh link vertices.
int n = 0;
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Only store connections which start from this tile.
if (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer)
{
const float* linkv = &con->pos[0];
float* v = &navVerts[(offMeshVertsBase + n*2)*3];
dtVcopy(&v[0], &linkv[0]);
dtVcopy(&v[3], &linkv[3]);
n++;
}
}
// Store polygons
// Mesh polys
const unsigned short* src = params->polys;
for (int i = 0; i < params->polyCount; ++i)
{
dtPoly* p = &navPolys[i];
p->vertCount = 0;
p->flags = params->polyFlags[i];
p->setArea(params->polyAreas[i]);
p->setType(DT_POLYTYPE_GROUND);
for (int j = 0; j < nvp; ++j)
{
if (src[j] == MESH_NULL_IDX) break;
p->verts[j] = src[j];
if (src[nvp+j] & 0x8000)
{
// Border or portal edge.
unsigned short dir = src[nvp+j] & 0xf;
if (dir == 0xf) // Border
p->neis[j] = 0;
else if (dir == 0) // Portal x-
p->neis[j] = DT_EXT_LINK | 4;
else if (dir == 1) // Portal z+
p->neis[j] = DT_EXT_LINK | 2;
else if (dir == 2) // Portal x+
p->neis[j] = DT_EXT_LINK | 0;
else if (dir == 3) // Portal z-
p->neis[j] = DT_EXT_LINK | 6;
}
else
{
// Normal connection
p->neis[j] = src[nvp+j]+1;
}
p->vertCount++;
}
src += nvp*2;
}
// Off-mesh connection vertices.
n = 0;
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Only store connections which start from this tile.
if (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer)
{
dtPoly* p = &navPolys[offMeshPolyBase+n];
p->vertCount = 2;
p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
p->flags = con->flags;
p->setArea(con->area);
p->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
n++;
}
}
// Store detail meshes and vertices.
// The nav polygon vertices are stored as the first vertices on each mesh.
// We compress the mesh data by skipping them and using the navmesh coordinates.
if (params->detailMeshes)
{
unsigned short vbase = 0;
for (int i = 0; i < params->polyCount; ++i)
{
dtPolyDetail& dtl = navDMeshes[i];
const int vb = (int)params->detailMeshes[i*4+0];
const int ndv = (int)params->detailMeshes[i*4+1];
const int nv = navPolys[i].vertCount;
dtl.vertBase = (unsigned int)vbase;
dtl.vertCount = (unsigned char)(ndv-nv);
dtl.triBase = (unsigned int)params->detailMeshes[i*4+2];
dtl.triCount = (unsigned char)params->detailMeshes[i*4+3];
// Copy vertices except the first 'nv' verts which are equal to nav poly verts.
if (ndv-nv)
{
memcpy(&navDVerts[vbase*3], &params->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
vbase += (unsigned short)(ndv-nv);
}
}
// Store triangles.
memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount);
}
else
{
// Create dummy detail mesh by triangulating polys.
int tbase = 0;
for (int i = 0; i < params->polyCount; ++i)
{
dtPolyDetail& dtl = navDMeshes[i];
const int nv = navPolys[i].vertCount;
dtl.vertBase = 0;
dtl.vertCount = 0;
dtl.triBase = (unsigned int)tbase;
dtl.triCount = (unsigned char)(nv-2);
// Triangulate polygon (local indices).
for (int j = 2; j < nv; ++j)
{
unsigned char* t = &navDTris[tbase*4];
t[0] = 0;
t[1] = (unsigned char)(j-1);
t[2] = (unsigned char)j;
// Bit for each edge that belongs to poly boundary.
t[3] = (1<<2);
if (j == 2) t[3] |= (1<<0);
if (j == nv-1) t[3] |= (1<<4);
tbase++;
}
}
}
// Store and create BVtree.
if (params->buildBvTree)
{
createBVTree(params, navBvtree, 2*params->polyCount);
}
n = 0;
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Only store connections which start from this tile.
if (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer)
{
con->poly = (unsigned short)(offMeshPolyBase + n);
offMeshCons[n] = con;
n++;
}
}
header->offMeshConCount = n;
dtFree(offMeshConClass);
*outData = data;
*outDataSize = dataSize;
return true;
}
bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
{
dtMeshHeader* header = (dtMeshHeader*)data;
int swappedMagic = DT_NAVMESH_MAGIC;
int swappedVersion = DT_NAVMESH_VERSION;
dtSwapEndian(&swappedMagic);
dtSwapEndian(&swappedVersion);
if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) &&
(header->magic != swappedMagic || header->version != swappedVersion))
{
return false;
}
dtSwapEndian(&header->magic);
dtSwapEndian(&header->version);
dtSwapEndian(&header->x);
dtSwapEndian(&header->y);
dtSwapEndian(&header->layer);
dtSwapEndian(&header->userId);
dtSwapEndian(&header->polyCount);
dtSwapEndian(&header->vertCount);
dtSwapEndian(&header->maxLinkCount);
dtSwapEndian(&header->detailMeshCount);
dtSwapEndian(&header->detailVertCount);
dtSwapEndian(&header->detailTriCount);
dtSwapEndian(&header->bvNodeCount);
dtSwapEndian(&header->offMeshConCount);
dtSwapEndian(&header->offMeshBase);
dtSwapEndian(&header->walkableHeight);
dtSwapEndian(&header->walkableRadius);
dtSwapEndian(&header->walkableClimb);
dtSwapEndian(&header->bmin[0]);
dtSwapEndian(&header->bmin[1]);
dtSwapEndian(&header->bmin[2]);
dtSwapEndian(&header->bmax[0]);
dtSwapEndian(&header->bmax[1]);
dtSwapEndian(&header->bmax[2]);
dtSwapEndian(&header->bvQuantFactor);
// Freelist index and pointers are updated when tile is added, no need to swap.
return true;
}
/// @par
///
/// @warning This function assumes that the header is in the correct endianess already.
/// Call #dtNavMeshHeaderSwapEndian() first on the data if the data is expected to be in wrong endianess
/// to start with. Call #dtNavMeshHeaderSwapEndian() after the data has been swapped if converting from
/// native to foreign endianess.
bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
{
// Make sure the data is in right format.
dtMeshHeader* header = (dtMeshHeader*)data;
if (header->magic != DT_NAVMESH_MAGIC)
return false;
if (header->version != DT_NAVMESH_VERSION)
return false;
// Patch header pointers.
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
unsigned char* d = data + headerSize;
float* verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
dtPoly* polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway.
//dtLink* links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
float* detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped.
//unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
// Vertices
for (int i = 0; i < header->vertCount*3; ++i)
{
dtSwapEndian(&verts[i]);
}
// Polys
for (int i = 0; i < header->polyCount; ++i)
{
dtPoly* p = &polys[i];
// poly->firstLink is update when tile is added, no need to swap.
for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j)
{
dtSwapEndian(&p->verts[j]);
dtSwapEndian(&p->neis[j]);
}
dtSwapEndian(&p->flags);
}
// Links are rebuild when tile is added, no need to swap.
// Detail meshes
for (int i = 0; i < header->detailMeshCount; ++i)
{
dtPolyDetail* pd = &detailMeshes[i];
dtSwapEndian(&pd->vertBase);
dtSwapEndian(&pd->triBase);
}
// Detail verts
for (int i = 0; i < header->detailVertCount*3; ++i)
{
dtSwapEndian(&detailVerts[i]);
}
// BV-tree
for (int i = 0; i < header->bvNodeCount; ++i)
{
dtBVNode* node = &bvTree[i];
for (int j = 0; j < 3; ++j)
{
dtSwapEndian(&node->bmin[j]);
dtSwapEndian(&node->bmax[j]);
}
dtSwapEndian(&node->i);
}
// Off-mesh Connections.
for (int i = 0; i < header->offMeshConCount; ++i)
{
dtOffMeshConnection* con = &offMeshCons[i];
for (int j = 0; j < 6; ++j)
dtSwapEndian(&con->pos[j]);
dtSwapEndian(&con->rad);
dtSwapEndian(&con->poly);
}
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,200 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "DetourNode.h"
#include "DetourAlloc.h"
#include "DetourAssert.h"
#include "DetourCommon.h"
#include <string.h>
#ifdef DT_POLYREF64
// From Thomas Wang, https://gist.github.com/badboy/6267743
inline unsigned int dtHashRef(dtPolyRef a)
{
a = (~a) + (a << 18); // a = (a << 18) - a - 1;
a = a ^ (a >> 31);
a = a * 21; // a = (a + (a << 2)) + (a << 4);
a = a ^ (a >> 11);
a = a + (a << 6);
a = a ^ (a >> 22);
return (unsigned int)a;
}
#else
inline unsigned int dtHashRef(dtPolyRef a)
{
a += ~(a<<15);
a ^= (a>>10);
a += (a<<3);
a ^= (a>>6);
a += ~(a<<11);
a ^= (a>>16);
return (unsigned int)a;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
dtNodePool::dtNodePool(int maxNodes, int hashSize) :
m_nodes(0),
m_first(0),
m_next(0),
m_maxNodes(maxNodes),
m_hashSize(hashSize),
m_nodeCount(0)
{
dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
// pidx is special as 0 means "none" and 1 is the first node. For that reason
// we have 1 fewer nodes available than the number of values it can contain.
dtAssert(m_maxNodes > 0 && m_maxNodes <= DT_NULL_IDX && m_maxNodes <= (1 << DT_NODE_PARENT_BITS) - 1);
m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM);
m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*hashSize, DT_ALLOC_PERM);
dtAssert(m_nodes);
dtAssert(m_next);
dtAssert(m_first);
memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize);
memset(m_next, 0xff, sizeof(dtNodeIndex)*m_maxNodes);
}
dtNodePool::~dtNodePool()
{
dtFree(m_nodes);
dtFree(m_next);
dtFree(m_first);
}
void dtNodePool::clear()
{
memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize);
m_nodeCount = 0;
}
unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes)
{
int n = 0;
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
while (i != DT_NULL_IDX)
{
if (m_nodes[i].id == id)
{
if (n >= maxNodes)
return n;
nodes[n++] = &m_nodes[i];
}
i = m_next[i];
}
return n;
}
dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state)
{
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
while (i != DT_NULL_IDX)
{
if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
return 0;
}
dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
{
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
dtNode* node = 0;
while (i != DT_NULL_IDX)
{
if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
if (m_nodeCount >= m_maxNodes)
return 0;
i = (dtNodeIndex)m_nodeCount;
m_nodeCount++;
// Init node
node = &m_nodes[i];
node->pidx = 0;
node->cost = 0;
node->total = 0;
node->id = id;
node->state = state;
node->flags = 0;
m_next[i] = m_first[bucket];
m_first[bucket] = i;
return node;
}
//////////////////////////////////////////////////////////////////////////////////////////
dtNodeQueue::dtNodeQueue(int n) :
m_heap(0),
m_capacity(n),
m_size(0)
{
dtAssert(m_capacity > 0);
m_heap = (dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
dtAssert(m_heap);
}
dtNodeQueue::~dtNodeQueue()
{
dtFree(m_heap);
}
void dtNodeQueue::bubbleUp(int i, dtNode* node)
{
int parent = (i-1)/2;
// note: (index > 0) means there is a parent
while ((i > 0) && (m_heap[parent]->total > node->total))
{
m_heap[i] = m_heap[parent];
i = parent;
parent = (i-1)/2;
}
m_heap[i] = node;
}
void dtNodeQueue::trickleDown(int i, dtNode* node)
{
int child = (i*2)+1;
while (child < m_size)
{
if (((child+1) < m_size) &&
(m_heap[child]->total > m_heap[child+1]->total))
{
child++;
}
m_heap[i] = m_heap[child];
i = child;
child = (i*2)+1;
}
bubbleUp(i, node);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURALLOCATOR_H
#define DETOURALLOCATOR_H
#include <stddef.h>
/// Provides hint values to the memory allocator on how long the
/// memory is expected to be used.
enum dtAllocHint
{
DT_ALLOC_PERM, ///< Memory persist after a function call.
DT_ALLOC_TEMP ///< Memory used temporarily within a function.
};
/// A memory allocation function.
// @param[in] size The size, in bytes of memory, to allocate.
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see dtAllocSetCustom
typedef void* (dtAllocFunc)(size_t size, dtAllocHint hint);
/// A memory deallocation function.
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAllocFunc.
/// @see dtAllocSetCustom
typedef void (dtFreeFunc)(void* ptr);
/// Sets the base custom allocation functions to be used by Detour.
/// @param[in] allocFunc The memory allocation function to be used by #dtAlloc
/// @param[in] freeFunc The memory de-allocation function to be used by #dtFree
void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
/// Allocates a memory block.
/// @param[in] size The size, in bytes of memory, to allocate.
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see dtFree
void* dtAlloc(size_t size, dtAllocHint hint);
/// Deallocates a memory block.
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc.
/// @see dtAlloc
void dtFree(void* ptr);
#endif

View file

@ -0,0 +1,56 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURASSERT_H
#define DETOURASSERT_H
// Note: This header file's only purpose is to include define assert.
// Feel free to change the file and include your own implementation instead.
#ifdef NDEBUG
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
# define dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
#else
/// An assertion failure function.
// @param[in] expression asserted expression.
// @param[in] file Filename of the failed assertion.
// @param[in] line Line number of the failed assertion.
/// @see dtAssertFailSetCustom
typedef void (dtAssertFailFunc)(const char* expression, const char* file, int line);
/// Sets the base custom assertion failure function to be used by Detour.
/// @param[in] assertFailFunc The function to be invoked in case of failure of #dtAssert
void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc);
/// Gets the base custom assertion failure function to be used by Detour.
dtAssertFailFunc* dtAssertFailGetCustom();
# include <assert.h>
# define dtAssert(expression) \
{ \
dtAssertFailFunc* failFunc = dtAssertFailGetCustom(); \
if(failFunc == NULL) { assert(expression); } \
else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
}
#endif
#endif // DETOURASSERT_H

View file

@ -0,0 +1,572 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURCOMMON_H
#define DETOURCOMMON_H
#include "DetourMath.h"
#include <stddef.h>
/**
@defgroup detour Detour
Members in this module are used to create, manipulate, and query navigation
meshes.
@note This is a summary list of members. Use the index or search
feature to find minor members.
*/
/// @name General helper functions
/// @{
/// Used to ignore a function parameter. VS complains about unused parameters
/// and this silences the warning.
/// @param [in] _ Unused parameter
template<class T> void dtIgnoreUnused(const T&) { }
/// Swaps the values of the two parameters.
/// @param[in,out] a Value A
/// @param[in,out] b Value B
template<class T> inline void dtSwap(T& a, T& b) { T t = a; a = b; b = t; }
/// Returns the minimum of two values.
/// @param[in] a Value A
/// @param[in] b Value B
/// @return The minimum of the two values.
template<class T> inline T dtMin(T a, T b) { return a < b ? a : b; }
/// Returns the maximum of two values.
/// @param[in] a Value A
/// @param[in] b Value B
/// @return The maximum of the two values.
template<class T> inline T dtMax(T a, T b) { return a > b ? a : b; }
/// Returns the absolute value.
/// @param[in] a The value.
/// @return The absolute value of the specified value.
template<class T> inline T dtAbs(T a) { return a < 0 ? -a : a; }
/// Returns the square of the value.
/// @param[in] a The value.
/// @return The square of the value.
template<class T> inline T dtSqr(T a) { return a*a; }
/// Clamps the value to the specified range.
/// @param[in] v The value to clamp.
/// @param[in] mn The minimum permitted return value.
/// @param[in] mx The maximum permitted return value.
/// @return The value, clamped to the specified range.
template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
/// @}
/// @name Vector helper functions.
/// @{
/// Derives the cross product of two vectors. (@p v1 x @p v2)
/// @param[out] dest The cross product. [(x, y, z)]
/// @param[in] v1 A Vector [(x, y, z)]
/// @param[in] v2 A vector [(x, y, z)]
inline void dtVcross(float* dest, const float* v1, const float* v2)
{
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
/// Derives the dot product of two vectors. (@p v1 . @p v2)
/// @param[in] v1 A Vector [(x, y, z)]
/// @param[in] v2 A vector [(x, y, z)]
/// @return The dot product.
inline float dtVdot(const float* v1, const float* v2)
{
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
/// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s))
/// @param[out] dest The result vector. [(x, y, z)]
/// @param[in] v1 The base vector. [(x, y, z)]
/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)]
/// @param[in] s The amount to scale @p v2 by before adding to @p v1.
inline void dtVmad(float* dest, const float* v1, const float* v2, const float s)
{
dest[0] = v1[0]+v2[0]*s;
dest[1] = v1[1]+v2[1]*s;
dest[2] = v1[2]+v2[2]*s;
}
/// Performs a linear interpolation between two vectors. (@p v1 toward @p v2)
/// @param[out] dest The result vector. [(x, y, x)]
/// @param[in] v1 The starting vector.
/// @param[in] v2 The destination vector.
/// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0]
inline void dtVlerp(float* dest, const float* v1, const float* v2, const float t)
{
dest[0] = v1[0]+(v2[0]-v1[0])*t;
dest[1] = v1[1]+(v2[1]-v1[1])*t;
dest[2] = v1[2]+(v2[2]-v1[2])*t;
}
/// Performs a vector addition. (@p v1 + @p v2)
/// @param[out] dest The result vector. [(x, y, z)]
/// @param[in] v1 The base vector. [(x, y, z)]
/// @param[in] v2 The vector to add to @p v1. [(x, y, z)]
inline void dtVadd(float* dest, const float* v1, const float* v2)
{
dest[0] = v1[0]+v2[0];
dest[1] = v1[1]+v2[1];
dest[2] = v1[2]+v2[2];
}
/// Performs a vector subtraction. (@p v1 - @p v2)
/// @param[out] dest The result vector. [(x, y, z)]
/// @param[in] v1 The base vector. [(x, y, z)]
/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)]
inline void dtVsub(float* dest, const float* v1, const float* v2)
{
dest[0] = v1[0]-v2[0];
dest[1] = v1[1]-v2[1];
dest[2] = v1[2]-v2[2];
}
/// Scales the vector by the specified value. (@p v * @p t)
/// @param[out] dest The result vector. [(x, y, z)]
/// @param[in] v The vector to scale. [(x, y, z)]
/// @param[in] t The scaling factor.
inline void dtVscale(float* dest, const float* v, const float t)
{
dest[0] = v[0]*t;
dest[1] = v[1]*t;
dest[2] = v[2]*t;
}
/// Selects the minimum value of each element from the specified vectors.
/// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)]
/// @param[in] v A vector. [(x, y, z)]
inline void dtVmin(float* mn, const float* v)
{
mn[0] = dtMin(mn[0], v[0]);
mn[1] = dtMin(mn[1], v[1]);
mn[2] = dtMin(mn[2], v[2]);
}
/// Selects the maximum value of each element from the specified vectors.
/// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)]
/// @param[in] v A vector. [(x, y, z)]
inline void dtVmax(float* mx, const float* v)
{
mx[0] = dtMax(mx[0], v[0]);
mx[1] = dtMax(mx[1], v[1]);
mx[2] = dtMax(mx[2], v[2]);
}
/// Sets the vector elements to the specified values.
/// @param[out] dest The result vector. [(x, y, z)]
/// @param[in] x The x-value of the vector.
/// @param[in] y The y-value of the vector.
/// @param[in] z The z-value of the vector.
inline void dtVset(float* dest, const float x, const float y, const float z)
{
dest[0] = x; dest[1] = y; dest[2] = z;
}
/// Performs a vector copy.
/// @param[out] dest The result. [(x, y, z)]
/// @param[in] a The vector to copy. [(x, y, z)]
inline void dtVcopy(float* dest, const float* a)
{
dest[0] = a[0];
dest[1] = a[1];
dest[2] = a[2];
}
/// Derives the scalar length of the vector.
/// @param[in] v The vector. [(x, y, z)]
/// @return The scalar length of the vector.
inline float dtVlen(const float* v)
{
return dtMathSqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}
/// Derives the square of the scalar length of the vector. (len * len)
/// @param[in] v The vector. [(x, y, z)]
/// @return The square of the scalar length of the vector.
inline float dtVlenSqr(const float* v)
{
return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
}
/// Returns the distance between two points.
/// @param[in] v1 A point. [(x, y, z)]
/// @param[in] v2 A point. [(x, y, z)]
/// @return The distance between the two points.
inline float dtVdist(const float* v1, const float* v2)
{
const float dx = v2[0] - v1[0];
const float dy = v2[1] - v1[1];
const float dz = v2[2] - v1[2];
return dtMathSqrtf(dx*dx + dy*dy + dz*dz);
}
/// Returns the square of the distance between two points.
/// @param[in] v1 A point. [(x, y, z)]
/// @param[in] v2 A point. [(x, y, z)]
/// @return The square of the distance between the two points.
inline float dtVdistSqr(const float* v1, const float* v2)
{
const float dx = v2[0] - v1[0];
const float dy = v2[1] - v1[1];
const float dz = v2[2] - v1[2];
return dx*dx + dy*dy + dz*dz;
}
/// Derives the distance between the specified points on the xz-plane.
/// @param[in] v1 A point. [(x, y, z)]
/// @param[in] v2 A point. [(x, y, z)]
/// @return The distance between the point on the xz-plane.
///
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
inline float dtVdist2D(const float* v1, const float* v2)
{
const float dx = v2[0] - v1[0];
const float dz = v2[2] - v1[2];
return dtMathSqrtf(dx*dx + dz*dz);
}
/// Derives the square of the distance between the specified points on the xz-plane.
/// @param[in] v1 A point. [(x, y, z)]
/// @param[in] v2 A point. [(x, y, z)]
/// @return The square of the distance between the point on the xz-plane.
inline float dtVdist2DSqr(const float* v1, const float* v2)
{
const float dx = v2[0] - v1[0];
const float dz = v2[2] - v1[2];
return dx*dx + dz*dz;
}
/// Normalizes the vector.
/// @param[in,out] v The vector to normalize. [(x, y, z)]
inline void dtVnormalize(float* v)
{
float d = 1.0f / dtMathSqrtf(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
v[0] *= d;
v[1] *= d;
v[2] *= d;
}
/// Performs a 'sloppy' colocation check of the specified points.
/// @param[in] p0 A point. [(x, y, z)]
/// @param[in] p1 A point. [(x, y, z)]
/// @return True if the points are considered to be at the same location.
///
/// Basically, this function will return true if the specified points are
/// close enough to eachother to be considered colocated.
inline bool dtVequal(const float* p0, const float* p1)
{
static const float thr = dtSqr(1.0f/16384.0f);
const float d = dtVdistSqr(p0, p1);
return d < thr;
}
/// Checks that the specified vector's components are all finite.
/// @param[in] v A point. [(x, y, z)]
/// @return True if all of the point's components are finite, i.e. not NaN
/// or any of the infinities.
inline bool dtVisfinite(const float* v)
{
bool result =
dtMathIsfinite(v[0]) &&
dtMathIsfinite(v[1]) &&
dtMathIsfinite(v[2]);
return result;
}
/// Checks that the specified vector's 2D components are finite.
/// @param[in] v A point. [(x, y, z)]
inline bool dtVisfinite2D(const float* v)
{
bool result = dtMathIsfinite(v[0]) && dtMathIsfinite(v[2]);
return result;
}
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
/// @param[in] u A vector [(x, y, z)]
/// @param[in] v A vector [(x, y, z)]
/// @return The dot product on the xz-plane.
///
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
inline float dtVdot2D(const float* u, const float* v)
{
return u[0]*v[0] + u[2]*v[2];
}
/// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz)
/// @param[in] u The LHV vector [(x, y, z)]
/// @param[in] v The RHV vector [(x, y, z)]
/// @return The dot product on the xz-plane.
///
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
inline float dtVperp2D(const float* u, const float* v)
{
return u[2]*v[0] - u[0]*v[2];
}
/// @}
/// @name Computational geometry helper functions.
/// @{
/// Derives the signed xz-plane area of the triangle ABC, or the relationship of line AB to point C.
/// @param[in] a Vertex A. [(x, y, z)]
/// @param[in] b Vertex B. [(x, y, z)]
/// @param[in] c Vertex C. [(x, y, z)]
/// @return The signed xz-plane area of the triangle.
inline float dtTriArea2D(const float* a, const float* b, const float* c)
{
const float abx = b[0] - a[0];
const float abz = b[2] - a[2];
const float acx = c[0] - a[0];
const float acz = c[2] - a[2];
return acx*abz - abx*acz;
}
/// Determines if two axis-aligned bounding boxes overlap.
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
/// @return True if the two AABB's overlap.
/// @see dtOverlapBounds
inline bool dtOverlapQuantBounds(const unsigned short amin[3], const unsigned short amax[3],
const unsigned short bmin[3], const unsigned short bmax[3])
{
bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
return overlap;
}
/// Determines if two axis-aligned bounding boxes overlap.
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
/// @return True if the two AABB's overlap.
/// @see dtOverlapQuantBounds
inline bool dtOverlapBounds(const float* amin, const float* amax,
const float* bmin, const float* bmax)
{
bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
return overlap;
}
/// Derives the closest point on a triangle from the specified reference point.
/// @param[out] closest The closest point on the triangle.
/// @param[in] p The reference point from which to test. [(x, y, z)]
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
void dtClosestPtPointTriangle(float* closest, const float* p,
const float* a, const float* b, const float* c);
/// Derives the y-axis height of the closest point on the triangle from the specified reference point.
/// @param[in] p The reference point from which to test. [(x, y, z)]
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
/// @param[out] h The resulting height.
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
const float* verts, int nverts,
float& tmin, float& tmax,
int& segMin, int& segMax);
bool dtIntersectSegSeg2D(const float* ap, const float* aq,
const float* bp, const float* bq,
float& s, float& t);
/// Determines if the specified point is inside the convex polygon on the xz-plane.
/// @param[in] pt The point to check. [(x, y, z)]
/// @param[in] verts The polygon vertices. [(x, y, z) * @p nverts]
/// @param[in] nverts The number of vertices. [Limit: >= 3]
/// @return True if the point is inside the polygon.
bool dtPointInPolygon(const float* pt, const float* verts, const int nverts);
bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
float* ed, float* et);
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
/// Derives the centroid of a convex polygon.
/// @param[out] tc The centroid of the polgyon. [(x, y, z)]
/// @param[in] idx The polygon indices. [(vertIndex) * @p nidx]
/// @param[in] nidx The number of indices in the polygon. [Limit: >= 3]
/// @param[in] verts The polygon vertices. [(x, y, z) * vertCount]
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts);
/// Determines if the two convex polygons overlap on the xz-plane.
/// @param[in] polya Polygon A vertices. [(x, y, z) * @p npolya]
/// @param[in] npolya The number of vertices in polygon A.
/// @param[in] polyb Polygon B vertices. [(x, y, z) * @p npolyb]
/// @param[in] npolyb The number of vertices in polygon B.
/// @return True if the two polygons overlap.
bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
const float* polyb, const int npolyb);
/// @}
/// @name Miscellanious functions.
/// @{
inline unsigned int dtNextPow2(unsigned int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
inline unsigned int dtIlog2(unsigned int v)
{
unsigned int r;
unsigned int shift;
r = (v > 0xffff) << 4; v >>= r;
shift = (v > 0xff) << 3; v >>= shift; r |= shift;
shift = (v > 0xf) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);
return r;
}
inline int dtAlign4(int x) { return (x+3) & ~3; }
inline int dtOppositeTile(int side) { return (side+4) & 0x7; }
inline void dtSwapByte(unsigned char* a, unsigned char* b)
{
unsigned char tmp = *a;
*a = *b;
*b = tmp;
}
inline void dtSwapEndian(unsigned short* v)
{
unsigned char* x = (unsigned char*)v;
dtSwapByte(x+0, x+1);
}
inline void dtSwapEndian(short* v)
{
unsigned char* x = (unsigned char*)v;
dtSwapByte(x+0, x+1);
}
inline void dtSwapEndian(unsigned int* v)
{
unsigned char* x = (unsigned char*)v;
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
}
inline void dtSwapEndian(int* v)
{
unsigned char* x = (unsigned char*)v;
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
}
inline void dtSwapEndian(float* v)
{
unsigned char* x = (unsigned char*)v;
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
}
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
const float s, const float t, float* out);
template<typename TypeToRetrieveAs>
TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(const unsigned char*& buffer, const size_t distanceToAdvance)
{
TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
buffer += distanceToAdvance;
return returnPointer;
}
template<typename TypeToRetrieveAs>
TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(unsigned char*& buffer, const size_t distanceToAdvance)
{
TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
buffer += distanceToAdvance;
return returnPointer;
}
/// @}
#endif // DETOURCOMMON_H
///////////////////////////////////////////////////////////////////////////
// This section contains detailed documentation for members that don't have
// a source file. It reduces clutter in the main section of the header.
/**
@fn float dtTriArea2D(const float* a, const float* b, const float* c)
@par
The vertices are projected onto the xz-plane, so the y-values are ignored.
This is a low cost function than can be used for various purposes. Its main purpose
is for point/line relationship testing.
In all cases: A value of zero indicates that all vertices are collinear or represent the same point.
(On the xz-plane.)
When used for point/line relationship tests, AB usually represents a line against which
the C point is to be tested. In this case:
A positive value indicates that point C is to the left of line AB, looking from A toward B.<br/>
A negative value indicates that point C is to the right of lineAB, looking from A toward B.
When used for evaluating a triangle:
The absolute value of the return value is two times the area of the triangle when it is
projected onto the xz-plane.
A positive return value indicates:
<ul>
<li>The vertices are wrapped in the normal Detour wrap direction.</li>
<li>The triangle's 3D face normal is in the general up direction.</li>
</ul>
A negative return value indicates:
<ul>
<li>The vertices are reverse wrapped. (Wrapped opposite the normal Detour wrap direction.)</li>
<li>The triangle's 3D face normal is in the general down direction.</li>
</ul>
*/

View file

@ -0,0 +1,24 @@
/**
@defgroup detour Detour
Members in this module are wrappers around the standard math library
*/
#ifndef DETOURMATH_H
#define DETOURMATH_H
#include <math.h>
// This include is required because libstdc++ has problems with isfinite
// if cmath is included before math.h.
#include <cmath>
inline float dtMathFabsf(float x) { return fabsf(x); }
inline float dtMathSqrtf(float x) { return sqrtf(x); }
inline float dtMathFloorf(float x) { return floorf(x); }
inline float dtMathCeilf(float x) { return ceilf(x); }
inline float dtMathCosf(float x) { return cosf(x); }
inline float dtMathSinf(float x) { return sinf(x); }
inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); }
inline bool dtMathIsfinite(float x) { return std::isfinite(x); }
#endif

View file

@ -0,0 +1,827 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURNAVMESH_H
#define DETOURNAVMESH_H
#include "DetourAlloc.h"
#include "DetourStatus.h"
// Undefine (or define in a build cofnig) the following line to use 64bit polyref.
// Generally not needed, useful for very large worlds.
// Note: tiles build using 32bit refs are not compatible with 64bit refs!
//#define DT_POLYREF64 1
#ifdef DT_POLYREF64
// TODO: figure out a multiplatform version of uint64_t
// - maybe: https://code.google.com/p/msinttypes/
// - or: http://www.azillionmonkeys.com/qed/pstdint.h
#include <stdint.h>
#endif
// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
// It is also recommended that you change dtHashRef() to a proper 64-bit hash.
/// A handle to a polygon within a navigation mesh tile.
/// @ingroup detour
#ifdef DT_POLYREF64
static const unsigned int DT_SALT_BITS = 16;
static const unsigned int DT_TILE_BITS = 28;
static const unsigned int DT_POLY_BITS = 20;
typedef uint64_t dtPolyRef;
#else
typedef unsigned int dtPolyRef;
#endif
/// A handle to a tile within a navigation mesh.
/// @ingroup detour
#ifdef DT_POLYREF64
typedef uint64_t dtTileRef;
#else
typedef unsigned int dtTileRef;
#endif
/// The maximum number of vertices per navigation polygon.
/// @ingroup detour
static const int DT_VERTS_PER_POLYGON = 6;
/// @{
/// @name Tile Serialization Constants
/// These constants are used to detect whether a navigation tile's data
/// and state format is compatible with the current build.
///
/// A magic number used to detect compatibility of navigation tile data.
static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V';
/// A version number used to detect compatibility of navigation tile data.
static const int DT_NAVMESH_VERSION = 7;
/// A magic number used to detect the compatibility of navigation tile states.
static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S';
/// A version number used to detect compatibility of navigation tile states.
static const int DT_NAVMESH_STATE_VERSION = 1;
/// @}
/// A flag that indicates that an entity links to an external entity.
/// (E.g. A polygon edge is a portal that links to another polygon.)
static const unsigned short DT_EXT_LINK = 0x8000;
/// A value that indicates the entity does not link to anything.
static const unsigned int DT_NULL_LINK = 0xffffffff;
/// A flag that indicates that an off-mesh connection can be traversed in both directions. (Is bidirectional.)
static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
/// The maximum number of user defined area ids.
/// @ingroup detour
static const int DT_MAX_AREAS = 64;
/// Tile flags used for various functions and fields.
/// For an example, see dtNavMesh::addTile().
enum dtTileFlags
{
/// The navigation mesh owns the tile memory and is responsible for freeing it.
DT_TILE_FREE_DATA = 0x01,
};
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
enum dtStraightPathFlags
{
DT_STRAIGHTPATH_START = 0x01, ///< The vertex is the start position in the path.
DT_STRAIGHTPATH_END = 0x02, ///< The vertex is the end position in the path.
DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, ///< The vertex is the start of an off-mesh connection.
};
/// Options for dtNavMeshQuery::findStraightPath.
enum dtStraightPathOptions
{
DT_STRAIGHTPATH_AREA_CROSSINGS = 0x01, ///< Add a vertex at every polygon edge crossing where area changes.
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing.
};
/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
enum dtFindPathOptions
{
DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
};
/// Options for dtNavMeshQuery::raycast
enum dtRaycastOptions
{
DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
};
enum dtDetailTriEdgeFlags
{
DT_DETAIL_EDGE_BOUNDARY = 0x01, ///< Detail triangle edge is part of the poly boundary
};
/// Limit raycasting during any angle pahfinding
/// The limit is given as a multiple of the character radius
static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f;
/// Flags representing the type of a navigation mesh polygon.
enum dtPolyTypes
{
/// The polygon is a standard convex polygon that is part of the surface of the mesh.
DT_POLYTYPE_GROUND = 0,
/// The polygon is an off-mesh connection consisting of two vertices.
DT_POLYTYPE_OFFMESH_CONNECTION = 1,
};
enum OffMeshState
{
DT_OFFMESH_EMPTY,
DT_OFFMESH_NEW,
DT_OFFMESH_DIRTY,
DT_OFFMESH_CLEAN,
DT_OFFMESH_REMOVING,
};
/// Defines a polygon within a dtMeshTile object.
/// @ingroup detour
struct dtPoly
{
/// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.)
unsigned int firstLink;
/// The indices of the polygon's vertices.
/// The actual vertices are located in dtMeshTile::verts.
unsigned short verts[DT_VERTS_PER_POLYGON];
/// Packed data representing neighbor polygons references and flags for each edge.
unsigned short neis[DT_VERTS_PER_POLYGON];
/// The user defined polygon flags.
unsigned int flags;
/// The number of vertices in the polygon.
unsigned char vertCount;
/// The bit packed area id and polygon type.
/// @note Use the structure's set and get methods to acess this value.
unsigned char areaAndtype;
/// Sets the user defined area id. [Limit: < #DT_MAX_AREAS]
inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
/// Sets the polygon type. (See: #dtPolyTypes.)
inline void setType(unsigned char t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); }
/// Gets the user defined area id.
inline unsigned char getArea() const { return areaAndtype & 0x3f; }
inline unsigned int getFlags() const { return flags; }
/// Gets the polygon type. (See: #dtPolyTypes)
inline unsigned char getType() const { return areaAndtype >> 6; }
};
/// Defines the location of detail sub-mesh data within a dtMeshTile.
struct dtPolyDetail
{
unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array.
unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array.
unsigned char vertCount; ///< The number of vertices in the sub-mesh.
unsigned char triCount; ///< The number of triangles in the sub-mesh.
};
/// Defines a link between polygons.
/// @note This structure is rarely if ever used by the end user.
/// @see dtMeshTile
struct dtLink
{
dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.)
unsigned int next; ///< Index of the next link.
unsigned char edge; ///< Index of the polygon edge that owns this link.
unsigned char side; ///< If a boundary link, defines on which side the link is.
unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area.
unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area.
int OffMeshID = -1; ///< If an off-mesh connection, this will be the UserID of the connection that made this link
};
/// Bounding volume node.
/// @note This structure is rarely if ever used by the end user.
/// @see dtMeshTile
struct dtBVNode
{
unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)]
unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)]
int i; ///< The node's index. (Negative for escape sequence.)
};
/// Defines an navigation mesh off-mesh connection within a dtMeshTile object.
/// An off-mesh connection is a user defined traversable connection made up to two vertices.
struct dtOffMeshConnection
{
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
float pos[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
/// The radius of the endpoints. [Limit: >= 0]
float rad = 0.0f;
/// The polygon reference of the connection within the tile.
unsigned short poly = 0;
/// Link flags.
/// @note These are not the connection's user defined flags. Those are assigned via the
/// connection's dtPoly definition. These are link flags used for internal purposes.
unsigned int flags = 0;
unsigned char area = 0;
/// End point side.
unsigned char side = 0;
bool bBiDir = false;
/// The id of the offmesh connection. (User assigned when the navigation mesh is built.)
unsigned int userId = 0;
int FromTileX = -1;
int FromTileY = -1;
int FromTileLayer = -1;
int ToTileX = -1;
int ToTileY = -1;
int ToTileLayer = -1;
bool bPendingDelete = false; // This off-mesh needs to be removed completely
bool bDirty = false; // This off-mesh connection has had its from or to tile rebuilt recently so the links need to be re-established
bool bBased = false; // This off-mesh connection has had its links based in the source tile
dtOffMeshConnection* next = nullptr;
OffMeshState state = DT_OFFMESH_EMPTY;
unsigned short salt;
};
/// Provides high level information related to a dtMeshTile object.
/// @ingroup detour
struct dtMeshHeader
{
int magic; ///< Tile magic number. (Used to identify the data format.)
int version; ///< Tile data format version number.
int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer)
int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer)
int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer)
unsigned int userId; ///< The user defined id of the tile.
int polyCount; ///< The number of polygons in the tile.
int vertCount; ///< The number of vertices in the tile.
int maxLinkCount; ///< The number of allocated links.
int detailMeshCount; ///< The number of sub-meshes in the detail mesh.
/// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.)
int detailVertCount;
int detailTriCount; ///< The number of triangles in the detail mesh.
int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
int offMeshConCount; ///< The number of off-mesh connections.
int offMeshBase; ///< The index of the first polygon which is an off-mesh connection.
float walkableHeight; ///< The height of the agents using the tile.
float walkableRadius; ///< The radius of the agents using the tile.
float walkableClimb; ///< The maximum climb height of the agents using the tile.
float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)]
float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)]
/// The bounding volume quantization factor.
float bvQuantFactor;
};
/// Defines a navigation mesh tile.
/// @ingroup detour
struct dtMeshTile
{
unsigned int salt; ///< Counter describing modifications to the tile.
unsigned int linksFreeList; ///< Index to the next free link.
dtMeshHeader* header; ///< The tile header.
dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount]
dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
/// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount]
float* detailVerts;
/// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount].
/// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags.
unsigned char* detailTris;
/// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount]
/// (Will be null if bounding volumes are disabled.)
dtBVNode* bvTree;
dtOffMeshConnection** offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.)
int dataSize; ///< Size of the tile data.
int flags; ///< Tile flags. (See: #dtTileFlags)
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
private:
dtMeshTile(const dtMeshTile&);
dtMeshTile& operator=(const dtMeshTile&);
};
/// Get flags for edge in detail triangle.
/// @param triFlags[in] The flags for the triangle (last component of detail vertices above).
/// @param edgeIndex[in] The index of the first vertex of the edge. For instance, if 0,
/// returns flags for edge AB.
inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex)
{
return (triFlags >> (edgeIndex * 2)) & 0x3;
}
/// Configuration parameters used to define multi-tile navigation meshes.
/// The values are used to allocate space during the initialization of a navigation mesh.
/// @see dtNavMesh::init()
/// @ingroup detour
struct dtNavMeshParams
{
float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)]
float tileWidth; ///< The width of each tile. (Along the x-axis.)
float tileHeight; ///< The height of each tile. (Along the z-axis.)
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely.
int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely.
};
/// A navigation mesh based on tiles of convex polygons.
/// @ingroup detour
class dtNavMesh
{
public:
dtNavMesh();
~dtNavMesh();
/// @{
/// @name Initialization and Tile Management
/// Initializes the navigation mesh for tiled use.
/// @param[in] params Initialization parameters.
/// @return The status flags for the operation.
dtStatus init(const dtNavMeshParams* params);
/// Initializes the navigation mesh for single tile use.
/// @param[in] data Data of the new tile. (See: #dtCreateNavMeshData)
/// @param[in] dataSize The data size of the new tile.
/// @param[in] flags The tile flags. (See: #dtTileFlags)
/// @return The status flags for the operation.
/// @see dtCreateNavMeshData
dtStatus init(unsigned char* data, const int dataSize, const int flags);
/// The navigation mesh initialization params.
const dtNavMeshParams* getParams() const;
/// Adds a tile to the navigation mesh.
/// @param[in] data Data for the new tile mesh. (See: #dtCreateNavMeshData)
/// @param[in] dataSize Data size of the new tile mesh.
/// @param[in] flags Tile flags. (See: #dtTileFlags)
/// @param[in] lastRef The desired reference for the tile. (When reloading a tile.) [opt] [Default: 0]
/// @param[out] result The tile reference. (If the tile was succesfully added.) [opt]
/// @return The status flags for the operation.
dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
/// Removes the specified tile from the navigation mesh.
/// @param[in] ref The reference of the tile to remove.
/// @param[out] data Data associated with deleted tile.
/// @param[out] dataSize Size of the data associated with deleted tile.
/// @return The status flags for the operation.
dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
void GlobalOffMeshLinks(dtOffMeshConnection* con);
void baseOffMeshLinks(dtOffMeshConnection* Connection);
void LinkOffMeshConnectionToTiles(dtOffMeshConnection* con);
void unconnectOffMeshLink(dtOffMeshConnection* con);
/// @}
/// @{
/// @name Query Functions
/// Calculates the tile grid location for the specified world position.
/// @param[in] pos The world position for the query. [(x, y, z)]
/// @param[out] tx The tile's x-location. (x, y)
/// @param[out] ty The tile's y-location. (x, y)
void calcTileLoc(const float* pos, int* tx, int* ty) const;
/// Gets the tile at the specified grid location.
/// @param[in] x The tile's x-location. (x, y, layer)
/// @param[in] y The tile's y-location. (x, y, layer)
/// @param[in] layer The tile's layer. (x, y, layer)
/// @return The tile, or null if the tile does not exist.
dtMeshTile* getTileAt(const int x, const int y, const int layer);
const dtMeshTile* getTileAtConst(const int x, const int y, const int layer) const;
/// Gets all tiles at the specified grid location. (All layers.)
/// @param[in] x The tile's x-location. (x, y)
/// @param[in] y The tile's y-location. (x, y)
/// @param[out] tiles A pointer to an array of tiles that will hold the result.
/// @param[in] maxTiles The maximum tiles the tiles parameter can hold.
/// @return The number of tiles returned in the tiles array.
int getTilesAt(const int x, const int y,
dtMeshTile const** tiles, const int maxTiles) const;
/// Gets the tile reference for the tile at specified grid location.
/// @param[in] x The tile's x-location. (x, y, layer)
/// @param[in] y The tile's y-location. (x, y, layer)
/// @param[in] layer The tile's layer. (x, y, layer)
/// @return The tile reference of the tile, or 0 if there is none.
dtTileRef getTileRefAt(int x, int y, int layer) const;
/// Gets the tile reference for the specified tile.
/// @param[in] tile The tile.
/// @return The tile reference of the tile.
dtTileRef getTileRef(const dtMeshTile* tile) const;
/// Gets the tile for the specified tile reference.
/// @param[in] ref The tile reference of the tile to retrieve.
/// @return The tile for the specified reference, or null if the
/// reference is invalid.
const dtMeshTile* getTileByRef(dtTileRef ref) const;
/// The maximum number of tiles supported by the navigation mesh.
/// @return The maximum number of tiles supported by the navigation mesh.
int getMaxTiles() const;
/// Gets the tile at the specified index.
/// @param[in] i The tile index. [Limit: 0 >= index < #getMaxTiles()]
/// @return The tile at the specified index.
const dtMeshTile* getTile(int i) const;
/// Gets the tile and polygon for the specified polygon reference.
/// @param[in] ref The reference for the a polygon.
/// @param[out] tile The tile containing the polygon.
/// @param[out] poly The polygon.
/// @return The status flags for the operation.
dtStatus getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
/// Returns the tile and polygon for the specified polygon reference.
/// @param[in] ref A known valid reference for a polygon.
/// @param[out] tile The tile containing the polygon.
/// @param[out] poly The polygon.
void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
/// Checks the validity of a polygon reference.
/// @param[in] ref The polygon reference to check.
/// @return True if polygon reference is valid for the navigation mesh.
bool isValidPolyRef(dtPolyRef ref) const;
/// Gets the polygon reference for the tile's base polygon.
/// @param[in] tile The tile.
/// @return The polygon reference for the base polygon in the specified tile.
dtPolyRef getPolyRefBase(const dtMeshTile* tile) const;
/// Gets the endpoints for an off-mesh connection, ordered by "direction of travel".
/// @param[in] prevRef The reference of the polygon before the connection.
/// @param[in] polyRef The reference of the off-mesh connection polygon.
/// @param[out] startPos The start position of the off-mesh connection. [(x, y, z)]
/// @param[out] endPos The end position of the off-mesh connection. [(x, y, z)]
/// @return The status flags for the operation.
dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
/// Gets the specified off-mesh connection.
/// @param[in] ref The polygon reference of the off-mesh connection.
/// @return The specified off-mesh connection, or null if the polygon reference is not valid.
const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
/// @}
/// @{
/// @name State Management
/// These functions do not effect #dtTileRef or #dtPolyRef's.
/// Sets the user defined flags for the specified polygon.
/// @param[in] ref The polygon reference.
/// @param[in] flags The new flags for the polygon.
/// @return The status flags for the operation.
dtStatus setPolyFlags(dtPolyRef ref, unsigned int flags);
/// Gets the user defined flags for the specified polygon.
/// @param[in] ref The polygon reference.
/// @param[out] resultFlags The polygon flags.
/// @return The status flags for the operation.
dtStatus getPolyFlags(dtPolyRef ref, unsigned int* resultFlags) const;
/// Sets the user defined area for the specified polygon.
/// @param[in] ref The polygon reference.
/// @param[in] area The new area id for the polygon. [Limit: < #DT_MAX_AREAS]
/// @return The status flags for the operation.
dtStatus setPolyArea(dtPolyRef ref, unsigned char area);
/// Gets the user defined area for the specified polygon.
/// @param[in] ref The polygon reference.
/// @param[out] resultArea The area id for the polygon.
/// @return The status flags for the operation.
dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
/// Gets the size of the buffer required by #storeTileState to store the specified tile's state.
/// @param[in] tile The tile.
/// @return The size of the buffer required to store the state.
int getTileStateSize(const dtMeshTile* tile) const;
/// Stores the non-structural state of the tile in the specified buffer. (Flags, area ids, etc.)
/// @param[in] tile The tile.
/// @param[out] data The buffer to store the tile's state in.
/// @param[in] maxDataSize The size of the data buffer. [Limit: >= #getTileStateSize]
/// @return The status flags for the operation.
dtStatus storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const;
/// Restores the state of the tile.
/// @param[in] tile The tile.
/// @param[in] data The new state. (Obtained from #storeTileState.)
/// @param[in] maxDataSize The size of the state within the data buffer.
/// @return The status flags for the operation.
dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
/// @}
/// @{
/// @name Encoding and Decoding
/// These functions are generally meant for internal use only.
/// Derives a standard polygon reference.
/// @note This function is generally meant for internal use only.
/// @param[in] salt The tile's salt value.
/// @param[in] it The index of the tile.
/// @param[in] ip The index of the polygon within the tile.
inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
{
#ifdef DT_POLYREF64
return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip;
#else
return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
#endif
}
/// Decodes a standard polygon reference.
/// @note This function is generally meant for internal use only.
/// @param[in] ref The polygon reference to decode.
/// @param[out] salt The tile's salt value.
/// @param[out] it The index of the tile.
/// @param[out] ip The index of the polygon within the tile.
/// @see #encodePolyId
inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
{
#ifdef DT_POLYREF64
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
salt = (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
it = (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
ip = (unsigned int)(ref & polyMask);
#else
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
it = (unsigned int)((ref >> m_polyBits) & tileMask);
ip = (unsigned int)(ref & polyMask);
#endif
}
/// Extracts a tile's salt value from the specified polygon reference.
/// @note This function is generally meant for internal use only.
/// @param[in] ref The polygon reference.
/// @see #encodePolyId
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
{
#ifdef DT_POLYREF64
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
return (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
#else
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
#endif
}
/// Extracts the tile's index from the specified polygon reference.
/// @note This function is generally meant for internal use only.
/// @param[in] ref The polygon reference.
/// @see #encodePolyId
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
{
#ifdef DT_POLYREF64
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
return (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
#else
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
return (unsigned int)((ref >> m_polyBits) & tileMask);
#endif
}
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
/// @note This function is generally meant for internal use only.
/// @param[in] ref The polygon reference.
/// @see #encodePolyId
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
{
#ifdef DT_POLYREF64
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
return (unsigned int)(ref & polyMask);
#else
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
return (unsigned int)(ref & polyMask);
#endif
}
/// @}
private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNavMesh(const dtNavMesh&);
dtNavMesh& operator=(const dtNavMesh&);
/// Returns pointer to tile in the tile array.
dtMeshTile* getTile(int i);
/// Returns neighbour tile based on side.
int getTilesAt(const int x, const int y,
dtMeshTile** tiles, const int maxTiles) const;
/// Returns neighbour tile based on side.
int getNeighbourTilesAt(const int x, const int y, const int side,
dtMeshTile** tiles, const int maxTiles) const;
/// Returns all polygons in neighbour tile based on portal defined by the segment.
int findConnectingPolys(const float* va, const float* vb,
const dtMeshTile* tile, int side,
dtPolyRef* con, float* conarea, int maxcon) const;
/// Builds internal polygons links for a tile.
void connectIntLinks(dtMeshTile* tile);
/// Builds internal polygons links for a tile.
void baseOffMeshLinks(dtMeshTile* tile);
/// Builds external polygon links for a tile.
void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
/// Builds external polygon links for a tile.
void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
/// Removes external links at specified side.
void unconnectLinks(dtMeshTile* tile, dtMeshTile* target);
// TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
/// Queries polygons within a tile.
int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
dtPolyRef* polys, const int maxPolys) const;
/// Find nearest polygon within a tile.
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
const float* halfExtents, float* nearestPt) const;
/// Returns whether position is over the poly and the height at the position if so.
bool getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const;
/// Returns closest point on polygon.
void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice.
float m_orig[3]; ///< Origin of the tile (0,0)
float m_tileWidth, m_tileHeight; ///< Dimensions of each tile.
int m_maxTiles; ///< Max number of tiles.
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
int m_tileLutMask; ///< Tile hash lookup mask.
dtMeshTile** m_posLookup; ///< Tile hash lookup.
dtMeshTile* m_nextFree; ///< Freelist of tiles.
dtMeshTile* m_tiles; ///< List of tiles.
#ifndef DT_POLYREF64
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
unsigned int m_polyBits; ///< Number of poly bits in the tile ID.
#endif
friend class dtNavMeshQuery;
};
/// Allocates a navigation mesh object using the Detour allocator.
/// @return A navigation mesh that is ready for initialization, or null on failure.
/// @ingroup detour
dtNavMesh* dtAllocNavMesh();
/// Frees the specified navigation mesh object using the Detour allocator.
/// @param[in] navmesh A navigation mesh allocated using #dtAllocNavMesh
/// @ingroup detour
void dtFreeNavMesh(dtNavMesh* navmesh);
#endif // DETOURNAVMESH_H
///////////////////////////////////////////////////////////////////////////
// This section contains detailed documentation for members that don't have
// a source file. It reduces clutter in the main section of the header.
/**
@typedef dtPolyRef
@par
Polygon references are subject to the same invalidate/preserve/restore
rules that apply to #dtTileRef's. If the #dtTileRef for the polygon's
tile changes, the polygon reference becomes invalid.
Changing a polygon's flags, area id, etc. does not impact its polygon
reference.
@typedef dtTileRef
@par
The following changes will invalidate a tile reference:
- The referenced tile has been removed from the navigation mesh.
- The navigation mesh has been initialized using a different set
of #dtNavMeshParams.
A tile reference is preserved/restored if the tile is added to a navigation
mesh initialized with the original #dtNavMeshParams and is added at the
original reference location. (E.g. The lastRef parameter is used with
dtNavMesh::addTile.)
Basically, if the storage structure of a tile changes, its associated
tile reference changes.
@var unsigned short dtPoly::neis[DT_VERTS_PER_POLYGON]
@par
Each entry represents data for the edge starting at the vertex of the same index.
E.g. The entry at index n represents the edge data for vertex[n] to vertex[n+1].
A value of zero indicates the edge has no polygon connection. (It makes up the
border of the navigation mesh.)
The information can be extracted as follows:
@code
neighborRef = neis[n] & 0xff; // Get the neighbor polygon reference.
if (neis[n] & #DT_EX_LINK)
{
// The edge is an external (portal) edge.
}
@endcode
@var float dtMeshHeader::bvQuantFactor
@par
This value is used for converting between world and bounding volume coordinates.
For example:
@code
const float cs = 1.0f / tile->header->bvQuantFactor;
const dtBVNode* n = &tile->bvTree[i];
if (n->i >= 0)
{
// This is a leaf node.
float worldMinX = tile->header->bmin[0] + n->bmin[0]*cs;
float worldMinY = tile->header->bmin[0] + n->bmin[1]*cs;
// Etc...
}
@endcode
@struct dtMeshTile
@par
Tiles generally only exist within the context of a dtNavMesh object.
Some tile content is optional. For example, a tile may not contain any
off-mesh connections. In this case the associated pointer will be null.
If a detail mesh exists it will share vertices with the base polygon mesh.
Only the vertices unique to the detail mesh will be stored in #detailVerts.
@warning Tiles returned by a dtNavMesh object are not guarenteed to be populated.
For example: The tile at a location might not have been loaded yet, or may have been removed.
In this case, pointers will be null. So if in doubt, check the polygon count in the
tile's header to determine if a tile has polygons defined.
@var float dtOffMeshConnection::pos[6]
@par
For a properly built navigation mesh, vertex A will always be within the bounds of the mesh.
Vertex B is not required to be within the bounds of the mesh.
*/

View file

@ -0,0 +1,155 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURNAVMESHBUILDER_H
#define DETOURNAVMESHBUILDER_H
#include "DetourAlloc.h"
#include "DetourNavMesh.h"
#include <vector>
/// Represents the source data used to build an navigation mesh tile.
/// @ingroup detour
struct dtNavMeshCreateParams
{
/// @name Polygon Mesh Attributes
/// Used to create the base navigation graph.
/// See #rcPolyMesh for details related to these attributes.
/// @{
const unsigned short* verts; ///< The polygon mesh vertices. [(x, y, z) * #vertCount] [Unit: vx]
int vertCount; ///< The number vertices in the polygon mesh. [Limit: >= 3]
const unsigned short* polys; ///< The polygon data. [Size: #polyCount * 2 * #nvp]
const unsigned int* polyFlags; ///< The user defined flags assigned to each polygon. [Size: #polyCount]
const unsigned char* polyAreas; ///< The user defined area ids assigned to each polygon. [Size: #polyCount]
int polyCount; ///< Number of polygons in the mesh. [Limit: >= 1]
int nvp; ///< Number maximum number of vertices per polygon. [Limit: >= 3]
/// @}
/// @name Height Detail Attributes (Optional)
/// See #rcPolyMeshDetail for details related to these attributes.
/// @{
const unsigned int* detailMeshes; ///< The height detail sub-mesh data. [Size: 4 * #polyCount]
const float* detailVerts; ///< The detail mesh vertices. [Size: 3 * #detailVertsCount] [Unit: wu]
int detailVertsCount; ///< The number of vertices in the detail mesh.
const unsigned char* detailTris; ///< The detail mesh triangles. [Size: 4 * #detailTriCount]
int detailTriCount; ///< The number of triangles in the detail mesh.
/// @}
/// @name Off-Mesh Connections Attributes (Optional)
/// Used to define a custom point-to-point edge within the navigation graph, an
/// off-mesh connection is a user defined traversable connection made up to two vertices,
/// at least one of which resides within a navigation mesh polygon.
/// @{
/// Off-mesh connection vertices. [(ax, ay, az, bx, by, bz) * #offMeshConCount] [Unit: wu]
const float* offMeshConVerts;
/// Off-mesh connection radii. [Size: #offMeshConCount] [Unit: wu]
const float* offMeshConRad;
/// User defined flags assigned to the off-mesh connections. [Size: #offMeshConCount]
const unsigned int* offMeshConFlags;
/// User defined area ids assigned to the off-mesh connections. [Size: #offMeshConCount]
const unsigned char* offMeshConAreas;
/// The permitted travel direction of the off-mesh connections. [Size: #offMeshConCount]
///
/// 0 = Travel only from endpoint A to endpoint B.<br/>
/// #DT_OFFMESH_CON_BIDIR = Bidirectional travel.
const unsigned char* offMeshConDir;
/// The user defined ids of the off-mesh connection. [Size: #offMeshConCount]
const unsigned int* offMeshConUserID;
/// The number of off-mesh connections. [Limit: >= 0]
int offMeshConCount;
/// @}
/// @name Tile Attributes
/// @note The tile grid/layer data can be left at zero if the destination is a single tile mesh.
/// @{
unsigned int userId; ///< The user defined id of the tile.
int tileX; ///< The tile's x-grid location within the multi-tile destination mesh. (Along the x-axis.)
int tileY; ///< The tile's y-grid location within the multi-tile desitation mesh. (Along the z-axis.)
int tileLayer; ///< The tile's layer within the layered destination mesh. [Limit: >= 0] (Along the y-axis.)
float bmin[3]; ///< The minimum bounds of the tile. [(x, y, z)] [Unit: wu]
float bmax[3]; ///< The maximum bounds of the tile. [(x, y, z)] [Unit: wu]
/// @}
/// @name General Configuration Attributes
/// @{
float crouchHeight;
float walkableHeight; ///< The agent height. [Unit: wu]
float walkableRadius; ///< The agent radius. [Unit: wu]
float walkableClimb; ///< The agent maximum traversable ledge. (Up/Down) [Unit: wu]
float cs; ///< The xz-plane cell size of the polygon mesh. [Limit: > 0] [Unit: wu]
float ch; ///< The y-axis cell height of the polygon mesh. [Limit: > 0] [Unit: wu]
/// True if a bounding volume tree should be built for the tile.
/// @note The BVTree is not normally needed for layered navigation meshes.
bool buildBvTree;
dtOffMeshConnection* GlobalOffMeshConnections;
int NumOffMeshConnections;
};
/// Builds navigation mesh tile data from the provided tile creation data.
/// @ingroup detour
/// @param[in] params Tile creation data.
/// @param[out] outData The resulting tile data.
/// @param[out] outDataSize The size of the tile data array.
/// @return True if the tile data was successfully created.
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize);
/// Swaps the endianess of the tile data's header (#dtMeshHeader).
/// @param[in,out] data The tile data array.
/// @param[in] dataSize The size of the data array.
bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize);
/// Swaps endianess of the tile data.
/// @param[in,out] data The tile data array.
/// @param[in] dataSize The size of the data array.
bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
#endif // DETOURNAVMESHBUILDER_H
// This section contains detailed documentation for members that don't have
// a source file. It reduces clutter in the main section of the header.
/**
@struct dtNavMeshCreateParams
@par
This structure is used to marshal data between the Recast mesh generation pipeline and Detour navigation components.
See the rcPolyMesh and rcPolyMeshDetail documentation for detailed information related to mesh structure.
Units are usually in voxels (vx) or world units (wu). The units for voxels, grid size, and cell size
are all based on the values of #cs and #ch.
The standard navigation mesh build process is to create tile data using dtCreateNavMeshData, then add the tile
to a navigation mesh using either the dtNavMesh single tile <tt>init()</tt> function or the dtNavMesh::addTile()
function.
@see dtCreateNavMeshData
*/

View file

@ -0,0 +1,600 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURNAVMESHQUERY_H
#define DETOURNAVMESHQUERY_H
#include "DetourNavMesh.h"
#include "DetourStatus.h"
// Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter.
// On certain platforms indirect or virtual function call is expensive. The default
// setting is to use non-virtual functions, the actual implementations of the functions
// are declared as inline for maximum speed.
//#define DT_VIRTUAL_QUERYFILTER 1
/// Defines polygon filtering and traversal costs for navigation mesh query operations.
/// @ingroup detour
class dtQueryFilter
{
float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.)
unsigned int m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.)
unsigned int m_excludeFlags; ///< Flags for polygons that should not be visted. (Used by default implementation.)
public:
dtQueryFilter();
#ifdef DT_VIRTUAL_QUERYFILTER
virtual ~dtQueryFilter() { }
#endif
/// Returns true if the polygon can be visited. (I.e. Is traversable.)
/// @param[in] ref The reference id of the polygon test.
/// @param[in] tile The tile containing the polygon.
/// @param[in] poly The polygon to test.
#ifdef DT_VIRTUAL_QUERYFILTER
virtual bool passFilter(const dtPolyRef ref,
const dtMeshTile* tile,
const dtPoly* poly) const;
#else
bool passFilter(const dtPolyRef ref,
const dtMeshTile* tile,
const dtPoly* poly) const;
#endif
/// Returns cost to move from the beginning to the end of a line segment
/// that is fully contained within a polygon.
/// @param[in] pa The start position on the edge of the previous and current polygon. [(x, y, z)]
/// @param[in] pb The end position on the edge of the current and next polygon. [(x, y, z)]
/// @param[in] prevRef The reference id of the previous polygon. [opt]
/// @param[in] prevTile The tile containing the previous polygon. [opt]
/// @param[in] prevPoly The previous polygon. [opt]
/// @param[in] curRef The reference id of the current polygon.
/// @param[in] curTile The tile containing the current polygon.
/// @param[in] curPoly The current polygon.
/// @param[in] nextRef The refernece id of the next polygon. [opt]
/// @param[in] nextTile The tile containing the next polygon. [opt]
/// @param[in] nextPoly The next polygon. [opt]
#ifdef DT_VIRTUAL_QUERYFILTER
virtual float getCost(const float* pa, const float* pb,
const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
#else
float getCost(const float* pa, const float* pb,
const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
#endif
/// @name Getters and setters for the default implementation data.
///@{
/// Returns the traversal cost of the area.
/// @param[in] i The id of the area.
/// @returns The traversal cost of the area.
inline float getAreaCost(const int i) const { return m_areaCost[i]; }
/// Sets the traversal cost of the area.
/// @param[in] i The id of the area.
/// @param[in] cost The new cost of traversing the area.
inline void setAreaCost(const int i, const float cost) { m_areaCost[i] = cost; }
/// Returns the include flags for the filter.
/// Any polygons that include one or more of these flags will be
/// included in the operation.
inline unsigned int getIncludeFlags() const { return m_includeFlags; }
/// Sets the include flags for the filter.
/// @param[in] flags The new flags.
inline void setIncludeFlags(const unsigned int flags) { m_includeFlags = flags; }
inline void addIncludeFlags(const unsigned int flags) { m_includeFlags |= flags; }
inline void removeIncludeFlags(const unsigned int flags) { m_includeFlags &= ~flags; }
/// Returns the exclude flags for the filter.
/// Any polygons that include one ore more of these flags will be
/// excluded from the operation.
inline unsigned int getExcludeFlags() const { return m_excludeFlags; }
/// Sets the exclude flags for the filter.
/// @param[in] flags The new flags.
inline void setExcludeFlags(const unsigned int flags) { m_excludeFlags = flags; }
inline void addExcludeFlags(const unsigned int flags) { m_excludeFlags |= flags; }
inline void removeExcludeFlags(const unsigned int flags) { m_excludeFlags &= ~flags; }
///@}
};
/// Provides information about raycast hit
/// filled by dtNavMeshQuery::raycast
/// @ingroup detour
struct dtRaycastHit
{
/// The hit parameter. (FLT_MAX if no wall hit.)
float t;
/// hitNormal The normal of the nearest wall hit. [(x, y, z)]
float hitNormal[3];
/// The index of the edge on the final polygon where the wall was hit.
int hitEdgeIndex;
/// Pointer to an array of reference ids of the visited polygons. [opt]
dtPolyRef* path;
/// The number of visited polygons. [opt]
int pathCount;
/// The maximum number of polygons the @p path array can hold.
int maxPath;
/// The cost of the path until hit.
float pathCost;
};
/// Provides custom polygon query behavior.
/// Used by dtNavMeshQuery::queryPolygons.
/// @ingroup detour
class dtPolyQuery
{
public:
virtual ~dtPolyQuery() { }
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
/// This can be called multiple times for a single query.
virtual void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) = 0;
};
/// Provides the ability to perform pathfinding related queries against
/// a navigation mesh.
/// @ingroup detour
class dtNavMeshQuery
{
public:
dtNavMeshQuery();
~dtNavMeshQuery();
/// Initializes the query object.
/// @param[in] nav Pointer to the dtNavMesh object to use for all queries.
/// @param[in] maxNodes Maximum number of search nodes. [Limits: 0 < value <= 65535]
/// @returns The status flags for the query.
dtStatus init(const dtNavMesh* nav, const int maxNodes);
/// @name Standard Pathfinding Functions
// /@{
/// Finds a path from the start polygon to the end polygon.
/// @param[in] startRef The refrence id of the start polygon.
/// @param[in] endRef The reference id of the end polygon.
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
/// [(polyRef) * @p pathCount]
/// @param[out] pathCount The number of polygons returned in the @p path array.
/// @param[in] maxPath The maximum number of polygons the @p path array can hold. [Limit: >= 1]
dtStatus findPath(dtPolyRef startRef, dtPolyRef endRef,
const float* startPos, const float* endPos,
const dtQueryFilter* filter,
dtPolyRef* path, int* pathCount, const int maxPath) const;
/// Finds the straight path from the start to the end position within the polygon corridor.
/// @param[in] startPos Path start position. [(x, y, z)]
/// @param[in] endPos Path end position. [(x, y, z)]
/// @param[in] path An array of polygon references that represent the path corridor.
/// @param[in] pathSize The number of polygons in the @p path array.
/// @param[out] straightPath Points describing the straight path. [(x, y, z) * @p straightPathCount].
/// @param[out] straightPathFlags Flags describing each point. (See: #dtStraightPathFlags) [opt]
/// @param[out] straightPathRefs The reference id of the polygon that is being entered at each point. [opt]
/// @param[out] straightPathCount The number of points in the straight path.
/// @param[in] maxStraightPath The maximum number of points the straight path arrays can hold. [Limit: > 0]
/// @param[in] options Query options. (see: #dtStraightPathOptions)
/// @returns The status flags for the query.
dtStatus findStraightPath(const float* startPos, const float* endPos,
const dtPolyRef* path, const int pathSize,
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
int* straightPathCount, const int maxStraightPath, const int options = 0) const;
///@}
/// @name Sliced Pathfinding Functions
/// Common use case:
/// -# Call initSlicedFindPath() to initialize the sliced path query.
/// -# Call updateSlicedFindPath() until it returns complete.
/// -# Call finalizeSlicedFindPath() to get the path.
///@{
/// Intializes a sliced path query.
/// @param[in] startRef The refrence id of the start polygon.
/// @param[in] endRef The reference id of the end polygon.
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[in] options query options (see: #dtFindPathOptions)
/// @returns The status flags for the query.
dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
const float* startPos, const float* endPos,
const dtQueryFilter* filter, const unsigned int options = 0);
/// Updates an in-progress sliced path query.
/// @param[in] maxIter The maximum number of iterations to perform.
/// @param[out] doneIters The actual number of iterations completed. [opt]
/// @returns The status flags for the query.
dtStatus updateSlicedFindPath(const int maxIter, int* doneIters);
/// Finalizes and returns the results of a sliced path query.
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
/// [(polyRef) * @p pathCount]
/// @param[out] pathCount The number of polygons returned in the @p path array.
/// @param[in] maxPath The max number of polygons the path array can hold. [Limit: >= 1]
/// @returns The status flags for the query.
dtStatus finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath);
/// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest
/// polygon on the existing path that was visited during the search.
/// @param[in] existing An array of polygon references for the existing path.
/// @param[in] existingSize The number of polygon in the @p existing array.
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
/// [(polyRef) * @p pathCount]
/// @param[out] pathCount The number of polygons returned in the @p path array.
/// @param[in] maxPath The max number of polygons the @p path array can hold. [Limit: >= 1]
/// @returns The status flags for the query.
dtStatus finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
dtPolyRef* path, int* pathCount, const int maxPath);
///@}
/// @name Dijkstra Search Functions
/// @{
/// Finds the polygons along the navigation graph that touch the specified circle.
/// @param[in] startRef The reference id of the polygon where the search starts.
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
/// @param[in] radius The radius of the search circle.
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] resultRef The reference ids of the polygons touched by the circle. [opt]
/// @param[out] resultParent The reference ids of the parent polygons for each result.
/// Zero if a result polygon has no parent. [opt]
/// @param[out] resultCost The search cost from @p centerPos to the polygon. [opt]
/// @param[out] resultCount The number of polygons found. [opt]
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
/// @returns The status flags for the query.
dtStatus findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
const dtQueryFilter* filter,
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
int* resultCount, const int maxResult) const;
/// Finds the polygons along the naviation graph that touch the specified convex polygon.
/// @param[in] startRef The reference id of the polygon where the search starts.
/// @param[in] verts The vertices describing the convex polygon. (CCW)
/// [(x, y, z) * @p nverts]
/// @param[in] nverts The number of vertices in the polygon.
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] resultRef The reference ids of the polygons touched by the search polygon. [opt]
/// @param[out] resultParent The reference ids of the parent polygons for each result. Zero if a
/// result polygon has no parent. [opt]
/// @param[out] resultCost The search cost from the centroid point to the polygon. [opt]
/// @param[out] resultCount The number of polygons found.
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
/// @returns The status flags for the query.
dtStatus findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
const dtQueryFilter* filter,
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
int* resultCount, const int maxResult) const;
/// Gets a path from the explored nodes in the previous search.
/// @param[in] endRef The reference id of the end polygon.
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
/// [(polyRef) * @p pathCount]
/// @param[out] pathCount The number of polygons returned in the @p path array.
/// @param[in] maxPath The maximum number of polygons the @p path array can hold. [Limit: >= 0]
/// @returns The status flags. Returns DT_FAILURE | DT_INVALID_PARAM if any parameter is wrong, or if
/// @p endRef was not explored in the previous search. Returns DT_SUCCESS | DT_BUFFER_TOO_SMALL
/// if @p path cannot contain the entire path. In this case it is filled to capacity with a partial path.
/// Otherwise returns DT_SUCCESS.
/// @remarks The result of this function depends on the state of the query object. For that reason it should only
/// be used immediately after one of the two Dijkstra searches, findPolysAroundCircle or findPolysAroundShape.
dtStatus getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const;
/// @}
/// @name Local Query Functions
///@{
/// Finds the polygon nearest to the specified center point.
/// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set.
///
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found.
/// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)]
/// @returns The status flags for the query.
dtStatus findNearestPoly(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* nearestRef, float* nearestPt) const;
/// Finds the polygon nearest to the specified center point.
/// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set.
///
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found.
/// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)]
/// @param[out] isOverPoly Set to true if the point's X/Z coordinate lies inside the polygon, false otherwise. Unchanged if no polygon is found. [opt]
/// @returns The status flags for the query.
dtStatus findNearestPoly(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const;
/// Finds polygons that overlap the search box.
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] polys The reference ids of the polygons that overlap the query box.
/// @param[out] polyCount The number of polygons in the search result.
/// @param[in] maxPolys The maximum number of polygons the search result can hold.
/// @returns The status flags for the query.
dtStatus queryPolygons(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* polys, int* polyCount, const int maxPolys) const;
/// Finds polygons that overlap the search box.
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
dtStatus queryPolygons(const float* center, const float* halfExtents,
const dtQueryFilter* filter, dtPolyQuery* query) const;
/// Finds the non-overlapping navigation polygons in the local neighbourhood around the center position.
/// @param[in] startRef The reference id of the polygon where the search starts.
/// @param[in] centerPos The center of the query circle. [(x, y, z)]
/// @param[in] radius The radius of the query circle.
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] resultRef The reference ids of the polygons touched by the circle.
/// @param[out] resultParent The reference ids of the parent polygons for each result.
/// Zero if a result polygon has no parent. [opt]
/// @param[out] resultCount The number of polygons found.
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
/// @returns The status flags for the query.
dtStatus findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
const dtQueryFilter* filter,
dtPolyRef* resultRef, dtPolyRef* resultParent,
int* resultCount, const int maxResult) const;
/// Moves from the start to the end position constrained to the navigation mesh.
/// @param[in] startRef The reference id of the start polygon.
/// @param[in] startPos A position of the mover within the start polygon. [(x, y, x)]
/// @param[in] endPos The desired end position of the mover. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] resultPos The result position of the mover. [(x, y, z)]
/// @param[out] visited The reference ids of the polygons visited during the move.
/// @param[out] visitedCount The number of polygons visited during the move.
/// @param[in] maxVisitedSize The maximum number of polygons the @p visited array can hold.
/// @returns The status flags for the query.
dtStatus moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
const dtQueryFilter* filter,
float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const;
/// Casts a 'walkability' ray along the surface of the navigation mesh from
/// the start position toward the end position.
/// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility.
/// @param[in] startRef The reference id of the start polygon.
/// @param[in] startPos A position within the start polygon representing
/// the start of the ray. [(x, y, z)]
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
/// @param[out] t The hit parameter. (FLT_MAX if no wall hit.)
/// @param[out] hitNormal The normal of the nearest wall hit. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] path The reference ids of the visited polygons. [opt]
/// @param[out] pathCount The number of visited polygons. [opt]
/// @param[in] maxPath The maximum number of polygons the @p path array can hold.
/// @returns The status flags for the query.
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
const dtQueryFilter* filter,
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
/// Casts a 'walkability' ray along the surface of the navigation mesh from
/// the start position toward the end position.
/// @param[in] startRef The reference id of the start polygon.
/// @param[in] startPos A position within the start polygon representing
/// the start of the ray. [(x, y, z)]
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[in] flags govern how the raycast behaves. See dtRaycastOptions
/// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
/// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
/// @returns The status flags for the query.
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
const dtQueryFilter* filter, const unsigned int options,
dtRaycastHit* hit, dtPolyRef prevRef = 0) const;
/// Finds the distance from the specified position to the nearest polygon wall.
/// @param[in] startRef The reference id of the polygon containing @p centerPos.
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
/// @param[in] maxRadius The radius of the search circle.
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] hitDist The distance to the nearest wall from @p centerPos.
/// @param[out] hitPos The nearest position on the wall that was hit. [(x, y, z)]
/// @param[out] hitNormal The normalized ray formed from the wall point to the
/// source point. [(x, y, z)]
/// @returns The status flags for the query.
dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
const dtQueryFilter* filter,
float* hitDist, float* hitPos, float* hitNormal) const;
/// Returns the segments for the specified polygon, optionally including portals.
/// @param[in] ref The reference id of the polygon.
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] segmentVerts The segments. [(ax, ay, az, bx, by, bz) * segmentCount]
/// @param[out] segmentRefs The reference ids of each segment's neighbor polygon.
/// Or zero if the segment is a wall. [opt] [(parentRef) * @p segmentCount]
/// @param[out] segmentCount The number of segments returned.
/// @param[in] maxSegments The maximum number of segments the result arrays can hold.
/// @returns The status flags for the query.
dtStatus getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
float* segmentVerts, dtPolyRef* segmentRefs, int* segmentCount,
const int maxSegments) const;
/// Returns random location on navmesh.
/// Polygons are chosen weighted by area. The search runs in linear related to number of polygon.
/// @param[in] filter The polygon filter to apply to the query.
/// @param[in] frand Function returning a random number [0..1).
/// @param[out] randomRef The reference id of the random location.
/// @param[out] randomPt The random location.
/// @returns The status flags for the query.
dtStatus findRandomPoint(const dtQueryFilter* filter, float (*frand)(),
dtPolyRef* randomRef, float* randomPt) const;
/// Returns random location on navmesh within the reach of specified location.
/// Polygons are chosen weighted by area. The search runs in linear related to number of polygon.
/// The location is not exactly constrained by the circle, but it limits the visited polygons.
/// @param[in] startRef The reference id of the polygon where the search starts.
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[in] frand Function returning a random number [0..1).
/// @param[out] randomRef The reference id of the random location.
/// @param[out] randomPt The random location. [(x, y, z)]
/// @returns The status flags for the query.
dtStatus findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float maxRadius,
const dtQueryFilter* filter, float (*frand)(),
dtPolyRef* randomRef, float* randomPt) const;
// Same as findRandomPointAroundCircle but ignores whether the point is reachable from startRef or not.
dtStatus findRandomPointAroundCircleIgnoreReachability(dtPolyRef startRef, const float* centerPos, const float maxRadius,
const dtQueryFilter* filter, float (*frand)(),
dtPolyRef* randomRef, float* randomPt) const;
/// Finds the closest point on the specified polygon.
/// @param[in] ref The reference id of the polygon.
/// @param[in] pos The position to check. [(x, y, z)]
/// @param[out] closest The closest point on the polygon. [(x, y, z)]
/// @param[out] posOverPoly True of the position is over the polygon.
/// @returns The status flags for the query.
dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
/// Returns a point on the boundary closest to the source point if the source point is outside the
/// polygon's xz-bounds.
/// @param[in] ref The reference id to the polygon.
/// @param[in] pos The position to check. [(x, y, z)]
/// @param[out] closest The closest point. [(x, y, z)]
/// @returns The status flags for the query.
dtStatus closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const;
/// Gets the height of the polygon at the provided position using the height detail. (Most accurate.)
/// @param[in] ref The reference id of the polygon.
/// @param[in] pos A position within the xz-bounds of the polygon. [(x, y, z)]
/// @param[out] height The height at the surface of the polygon.
/// @returns The status flags for the query.
dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
/// @}
/// @name Miscellaneous Functions
/// @{
/// Returns true if the polygon reference is valid and passes the filter restrictions.
/// @param[in] ref The polygon reference to check.
/// @param[in] filter The filter to apply.
bool isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const;
/// Returns true if the polygon reference is in the closed list.
/// @param[in] ref The reference id of the polygon to check.
/// @returns True if the polygon is in closed list.
bool isInClosedList(dtPolyRef ref) const;
/// Gets the node pool.
/// @returns The node pool.
class dtNodePool* getNodePool() const { return m_nodePool; }
/// Gets the navigation mesh the query object is using.
/// @return The navigation mesh the query object is using.
const dtNavMesh* getAttachedNavMesh() const { return m_nav; }
/// @}
private:
// Explicitly disabled copy constructor and copy assignment operator
dtNavMeshQuery(const dtNavMeshQuery&);
dtNavMeshQuery& operator=(const dtNavMeshQuery&);
/// Queries polygons within a tile.
void queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
const dtQueryFilter* filter, dtPolyQuery* query) const;
/// Returns portal points between two polygons.
dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
unsigned char& fromType, unsigned char& toType) const;
dtStatus getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
float* left, float* right) const;
/// Returns edge mid point between two polygons.
dtStatus getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const;
dtStatus getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
float* mid) const;
// Appends vertex to a straight path
dtStatus appendVertex(const float* pos, const unsigned char flags, const dtPolyRef ref,
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
int* straightPathCount, const int maxStraightPath) const;
// Appends intermediate portal points to a straight path.
dtStatus appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
int* straightPathCount, const int maxStraightPath, const int options) const;
// Gets the path leading to the specified end node.
dtStatus getPathToNode(struct dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const;
const dtNavMesh* m_nav; ///< Pointer to navmesh data.
struct dtQueryData
{
dtStatus status;
struct dtNode* lastBestNode;
float lastBestNodeCost;
dtPolyRef startRef, endRef;
float startPos[3], endPos[3];
const dtQueryFilter* filter;
unsigned int options;
float raycastLimitSqr;
};
dtQueryData m_query; ///< Sliced query state.
class dtNodePool* m_tinyNodePool; ///< Pointer to small node pool.
class dtNodePool* m_nodePool; ///< Pointer to node pool.
class dtNodeQueue* m_openList; ///< Pointer to open list queue.
};
/// Allocates a query object using the Detour allocator.
/// @return An allocated query object, or null on failure.
/// @ingroup detour
dtNavMeshQuery* dtAllocNavMeshQuery();
/// Frees the specified query object using the Detour allocator.
/// @param[in] query A query object allocated using #dtAllocNavMeshQuery
/// @ingroup detour
void dtFreeNavMeshQuery(dtNavMeshQuery* query);
#endif // DETOURNAVMESHQUERY_H

View file

@ -0,0 +1,168 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURNODE_H
#define DETOURNODE_H
#include "DetourNavMesh.h"
enum dtNodeFlags
{
DT_NODE_OPEN = 0x01,
DT_NODE_CLOSED = 0x02,
DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
};
typedef unsigned short dtNodeIndex;
static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0;
static const int DT_NODE_PARENT_BITS = 24;
static const int DT_NODE_STATE_BITS = 2;
struct dtNode
{
float pos[3]; ///< Position of the node.
float cost; ///< Cost from previous node to current node.
float total; ///< Cost up to the node.
unsigned int pidx : DT_NODE_PARENT_BITS; ///< Index to parent node.
unsigned int state : DT_NODE_STATE_BITS; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
dtPolyRef id; ///< Polygon ref the node corresponds to.
};
static const int DT_MAX_STATES_PER_NODE = 1 << DT_NODE_STATE_BITS; // number of extra states per node. See dtNode::state
class dtNodePool
{
public:
dtNodePool(int maxNodes, int hashSize);
~dtNodePool();
void clear();
// Get a dtNode by ref and extra state information. If there is none then - allocate
// There can be more than one node for the same polyRef but with different extra state information
dtNode* getNode(dtPolyRef id, unsigned char state=0);
dtNode* findNode(dtPolyRef id, unsigned char state);
unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes);
inline unsigned int getNodeIdx(const dtNode* node) const
{
if (!node) return 0;
return (unsigned int)(node - m_nodes) + 1;
}
inline dtNode* getNodeAtIdx(unsigned int idx)
{
if (!idx) return 0;
return &m_nodes[idx - 1];
}
inline const dtNode* getNodeAtIdx(unsigned int idx) const
{
if (!idx) return 0;
return &m_nodes[idx - 1];
}
inline int getMemUsed() const
{
return sizeof(*this) +
sizeof(dtNode)*m_maxNodes +
sizeof(dtNodeIndex)*m_maxNodes +
sizeof(dtNodeIndex)*m_hashSize;
}
inline int getMaxNodes() const { return m_maxNodes; }
inline int getHashSize() const { return m_hashSize; }
inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; }
inline dtNodeIndex getNext(int i) const { return m_next[i]; }
inline int getNodeCount() const { return m_nodeCount; }
private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNodePool(const dtNodePool&);
dtNodePool& operator=(const dtNodePool&);
dtNode* m_nodes;
dtNodeIndex* m_first;
dtNodeIndex* m_next;
const int m_maxNodes;
const int m_hashSize;
int m_nodeCount;
};
class dtNodeQueue
{
public:
dtNodeQueue(int n);
~dtNodeQueue();
inline void clear() { m_size = 0; }
inline dtNode* top() { return m_heap[0]; }
inline dtNode* pop()
{
dtNode* result = m_heap[0];
m_size--;
trickleDown(0, m_heap[m_size]);
return result;
}
inline void push(dtNode* node)
{
m_size++;
bubbleUp(m_size-1, node);
}
inline void modify(dtNode* node)
{
for (int i = 0; i < m_size; ++i)
{
if (m_heap[i] == node)
{
bubbleUp(i, node);
return;
}
}
}
inline bool empty() const { return m_size == 0; }
inline int getMemUsed() const
{
return sizeof(*this) +
sizeof(dtNode*) * (m_capacity + 1);
}
inline int getCapacity() const { return m_capacity; }
private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNodeQueue(const dtNodeQueue&);
dtNodeQueue& operator=(const dtNodeQueue&);
void bubbleUp(int i, dtNode* node);
void trickleDown(int i, dtNode* node);
dtNode** m_heap;
const int m_capacity;
int m_size;
};
#endif // DETOURNODE_H

View file

@ -0,0 +1,65 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURSTATUS_H
#define DETOURSTATUS_H
typedef unsigned int dtStatus;
// High level status.
static const unsigned int DT_FAILURE = 1u << 31; // Operation failed.
static const unsigned int DT_SUCCESS = 1u << 30; // Operation succeed.
static const unsigned int DT_IN_PROGRESS = 1u << 29; // Operation still in progress.
// Detail information for status.
static const unsigned int DT_STATUS_DETAIL_MASK = 0x0ffffff;
static const unsigned int DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized.
static const unsigned int DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version.
static const unsigned int DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory.
static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid.
static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results.
static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search.
static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess.
static const unsigned int DT_ALREADY_OCCUPIED = 1 << 7; // A tile has already been assigned to the given x,y coordinate
// Returns true of status is success.
inline bool dtStatusSucceed(dtStatus status)
{
return (status & DT_SUCCESS) != 0;
}
// Returns true of status is failure.
inline bool dtStatusFailed(dtStatus status)
{
return (status & DT_FAILURE) != 0;
}
// Returns true of status is in progress.
inline bool dtStatusInProgress(dtStatus status)
{
return (status & DT_IN_PROGRESS) != 0;
}
// Returns true if specific detail is set.
inline bool dtStatusDetail(dtStatus status, unsigned int detail)
{
return (status & detail) != 0;
}
#endif // DETOURSTATUS_H

View file

@ -0,0 +1,318 @@
#ifndef DETOURTILECACHE_H
#define DETOURTILECACHE_H
#include "DetourStatus.h"
#include "DetourNavMesh.h"
#include <vector>
typedef unsigned int dtObstacleRef;
typedef unsigned int dtOffMeshConnectionRef;
typedef unsigned int dtCompressedTileRef;
/// Flags for addTile
enum dtCompressedTileFlags
{
DT_COMPRESSEDTILE_FREE_DATA = 0x01, ///< Navmesh owns the tile memory and should free it.
};
struct dtCompressedTile
{
unsigned int salt; ///< Counter describing modifications to the tile.
struct dtTileCacheLayerHeader* header;
unsigned char* compressed;
int compressedSize;
unsigned char* data;
int dataSize;
unsigned int flags;
dtCompressedTile* next;
};
enum ObstacleState
{
DT_OBSTACLE_EMPTY,
DT_OBSTACLE_PROCESSING,
DT_OBSTACLE_PROCESSED,
DT_OBSTACLE_REMOVING,
};
enum ObstacleType
{
DT_OBSTACLE_CYLINDER,
DT_OBSTACLE_BOX, // AABB
DT_OBSTACLE_ORIENTED_BOX, // OBB
};
struct dtObstacleCylinder
{
float pos[ 3 ];
float radius;
float height;
int area;
};
struct dtObstacleBox
{
float bmin[ 3 ];
float bmax[ 3 ];
int area;
};
struct dtObstacleOrientedBox
{
float center[ 3 ];
float halfExtents[ 3 ];
float rotAux[ 2 ]; //{ cos(0.5f*angle)*sin(-0.5f*angle); cos(0.5f*angle)*cos(0.5f*angle) - 0.5 }
int area;
};
static const int DT_MAX_TOUCHED_TILES = 8;
struct dtTileCacheObstacle
{
union
{
dtObstacleCylinder cylinder;
dtObstacleBox box;
dtObstacleOrientedBox orientedBox;
};
dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
dtCompressedTileRef pending[DT_MAX_TOUCHED_TILES];
unsigned short salt;
unsigned char type;
unsigned char state;
unsigned char ntouched;
unsigned char npending;
dtTileCacheObstacle* next;
};
struct dtTileCacheParams
{
float orig[3];
float cs, ch;
int width, height;
float walkableHeight;
float walkableRadius;
float walkableClimb;
float walkableSlope;
float maxSimplificationError;
int maxTiles;
int maxObstacles;
int maxOffMeshConnections;
};
struct dtTileCacheMeshProcess
{
virtual ~dtTileCacheMeshProcess() { }
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned int* polyFlags) = 0;
};
class dtTileCache
{
public:
dtTileCache();
~dtTileCache();
struct dtTileCacheAlloc* getAlloc() { return m_talloc; }
struct dtTileCacheCompressor* getCompressor() { return m_tcomp; }
struct dtTileCacheMeshProcess* getMeshProcess() { return m_tmproc; }
const dtTileCacheParams* getParams() const { return &m_params; }
inline int getTileCount() const { return m_params.maxTiles; }
inline const dtCompressedTile* getTile(const int i) const { return &m_tiles[i]; }
inline int getOffMeshCount() const { return m_params.maxOffMeshConnections; }
inline int getObstacleCount() const { return m_params.maxObstacles; }
inline const dtTileCacheObstacle* getObstacle(const int i) const { return &m_obstacles[i]; }
inline const dtOffMeshConnection* getOffMeshConnection(const int i) const { return &m_offMeshConnections[i]; }
const dtTileCacheObstacle* getObstacleByRef(dtObstacleRef ref);
dtOffMeshConnection* getOffMeshConnectionByRef(dtOffMeshConnectionRef ref);
dtObstacleRef getObstacleRef(const dtTileCacheObstacle* obmin) const;
dtOffMeshConnectionRef getOffMeshRef(const dtOffMeshConnection* con) const;
dtStatus init(const dtTileCacheParams* params,
struct dtTileCacheAlloc* talloc,
struct dtTileCacheCompressor* tcomp,
struct dtTileCacheMeshProcess* tmproc);
int getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const ;
dtCompressedTile* getTileAt(const int tx, const int ty, const int tlayer);
dtCompressedTileRef getTileRef(const dtCompressedTile* tile) const;
const dtCompressedTile* getTileByRef(dtCompressedTileRef ref) const;
dtStatus addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result);
dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize);
// Cylinder obstacle.
dtStatus addObstacle(const float* pos, const float radius, const float height, const int area, dtObstacleRef* result);
dtStatus addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned int flags, const bool bBiDirectional, dtOffMeshConnectionRef* result);
dtStatus modifyOffMeshConnection(dtOffMeshConnectionRef ConRef, const unsigned int newFlag);
// Aabb obstacle.
dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);
// Box obstacle: can be rotated in Y.
dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result);
dtStatus removeObstacle(const dtObstacleRef ref);
dtStatus removeOffMeshConnection(const dtOffMeshConnectionRef ref);
dtStatus queryTiles(const float* bmin, const float* bmax,
dtCompressedTileRef* results, int* resultCount, const int maxResults) const;
/// Updates the tile cache by rebuilding tiles touched by unfinished obstacle requests.
/// @param[in] dt The time step size. Currently not used.
/// @param[in] navmesh The mesh to affect when rebuilding tiles.
/// @param[out] upToDate Whether the tile cache is fully up to date with obstacle requests and tile rebuilds.
/// If the tile cache is up to date another (immediate) call to update will have no effect;
/// otherwise another call will continue processing obstacle requests and tile rebuilds.
dtStatus update(const float dt, class dtNavMesh* navmesh, bool* upToDate = 0);
dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh);
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh);
void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;
void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
/// Encodes a tile id.
inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const
{
return ((dtCompressedTileRef)salt << m_tileBits) | (dtCompressedTileRef)it;
}
/// Decodes a tile salt.
inline unsigned int decodeTileIdSalt(dtCompressedTileRef ref) const
{
const dtCompressedTileRef saltMask = ((dtCompressedTileRef)1<<m_saltBits)-1;
return (unsigned int)((ref >> m_tileBits) & saltMask);
}
/// Decodes a tile id.
inline unsigned int decodeTileIdTile(dtCompressedTileRef ref) const
{
const dtCompressedTileRef tileMask = ((dtCompressedTileRef)1<<m_tileBits)-1;
return (unsigned int)(ref & tileMask);
}
/// Encodes an obstacle id.
inline dtObstacleRef encodeObstacleId(unsigned int salt, unsigned int it) const
{
return ((dtObstacleRef)salt << 16) | (dtObstacleRef)it;
}
/// Encodes an obstacle id.
inline dtOffMeshConnectionRef encodeOffMeshId(unsigned int salt, unsigned int it) const
{
return ((dtOffMeshConnectionRef)salt << 16) | (dtOffMeshConnectionRef)it;
}
/// Decodes an obstacle salt.
inline unsigned int decodeObstacleIdSalt(dtObstacleRef ref) const
{
const dtObstacleRef saltMask = ((dtObstacleRef)1<<16)-1;
return (unsigned int)((ref >> 16) & saltMask);
}
inline unsigned int decodeOffMeshIdSalt(dtOffMeshConnectionRef ref) const
{
const dtOffMeshConnectionRef saltMask = ((dtOffMeshConnectionRef)1 << 16) - 1;
return (unsigned int)((ref >> 16) & saltMask);
}
/// Decodes an obstacle id.
inline unsigned int decodeObstacleIdObstacle(dtObstacleRef ref) const
{
const dtObstacleRef tileMask = ((dtObstacleRef)1<<16)-1;
return (unsigned int)(ref & tileMask);
}
inline unsigned int decodeOffMeshIdCon(dtOffMeshConnectionRef ref) const
{
const dtOffMeshConnectionRef tileMask = ((dtOffMeshConnectionRef)1 << 16) - 1;
return (unsigned int)(ref & tileMask);
}
private:
// Explicitly disabled copy constructor and copy assignment operator.
dtTileCache(const dtTileCache&);
dtTileCache& operator=(const dtTileCache&);
enum ObstacleRequestAction
{
REQUEST_ADD,
REQUEST_REMOVE,
};
enum OffMeshRequestAction
{
REQUEST_OFFMESH_ADD,
REQUEST_OFFMESH_REFRESH,
REQUEST_OFFMESH_REMOVE
};
struct ObstacleRequest
{
int action;
dtObstacleRef ref;
};
struct OffMeshRequest
{
int action;
dtOffMeshConnectionRef ref;
};
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
int m_tileLutMask; ///< Tile hash lookup mask.
dtCompressedTile** m_posLookup; ///< Tile hash lookup.
dtCompressedTile* m_nextFreeTile; ///< Freelist of tiles.
dtCompressedTile* m_tiles; ///< List of tiles.
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
dtTileCacheParams m_params;
dtTileCacheAlloc* m_talloc;
dtTileCacheCompressor* m_tcomp;
dtTileCacheMeshProcess* m_tmproc;
dtTileCacheObstacle* m_obstacles;
dtTileCacheObstacle* m_nextFreeObstacle;
dtOffMeshConnection* m_offMeshConnections;
dtOffMeshConnection* m_nextFreeOffMeshConnection;
static const int MAX_REQUESTS = 512;
ObstacleRequest m_reqs[MAX_REQUESTS];
int m_nreqs;
OffMeshRequest m_OffMeshReqs[MAX_REQUESTS];
int m_nOffMeshReqs;
static const int MAX_UPDATE = 512;
dtCompressedTileRef m_update[MAX_UPDATE];
int m_nupdate;
};
dtTileCache* dtAllocTileCache();
void dtFreeTileCache(dtTileCache* tc);
#endif

View file

@ -0,0 +1,165 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef DETOURTILECACHEBUILDER_H
#define DETOURTILECACHEBUILDER_H
#include "DetourAlloc.h"
#include "DetourStatus.h"
static const int DT_TILECACHE_MAGIC = 'D'<<24 | 'T'<<16 | 'L'<<8 | 'R'; ///< 'DTLR';
static const int DT_TILECACHE_VERSION = 1;
static const unsigned char DT_TILECACHE_NULL_AREA = 0;
static const unsigned char DT_TILECACHE_CROUCH_AREA = 1;
static const unsigned char DT_TILECACHE_BLOCKED_AREA = 2;
static const unsigned char DT_TILECACHE_WALLCLIMB_AREA = 3;
static const unsigned char DT_TILECACHE_LADDER_AREA = 4;
static const unsigned char DT_TILECACHE_TEAM1STRUCTURE_AREA = 5;
static const unsigned char DT_TILECACHE_TEAM2STRUCTURE_AREA = 6;
static const unsigned char DT_TILECACHE_WELD_AREA = 7;
static const unsigned char DT_TILECACHE_DOOR_AREA = 8;
static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63;
static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff;
struct dtTileCacheLayerHeader
{
int magic; ///< Data magic
int version; ///< Data version
int tx,ty,tlayer;
float bmin[3], bmax[3];
unsigned short hmin, hmax; ///< Height min/max range
unsigned char width, height; ///< Dimension of the layer.
unsigned char minx, maxx, miny, maxy; ///< Usable sub-region.
};
struct dtTileCacheLayer
{
dtTileCacheLayerHeader* header;
unsigned char regCount; ///< Region count.
unsigned char* heights;
unsigned char* areas;
unsigned char* cons;
unsigned char* regs;
};
struct dtTileCacheContour
{
int nverts;
unsigned char* verts;
unsigned char reg;
unsigned char area;
};
struct dtTileCacheContourSet
{
int nconts;
dtTileCacheContour* conts;
};
struct dtTileCachePolyMesh
{
int nvp;
int nverts; ///< Number of vertices.
int npolys; ///< Number of polygons.
unsigned short* verts; ///< Vertices of the mesh, 3 elements per vertex.
unsigned short* polys; ///< Polygons of the mesh, nvp*2 elements per polygon.
unsigned int* flags; ///< Per polygon flags.
unsigned char* areas; ///< Area ID of polygons.
};
struct dtTileCacheAlloc
{
virtual ~dtTileCacheAlloc() {}
virtual void reset() {}
virtual void* alloc(const size_t size)
{
return dtAlloc(size, DT_ALLOC_TEMP);
}
virtual void free(void* ptr)
{
dtFree(ptr);
}
};
struct dtTileCacheCompressor
{
virtual ~dtTileCacheCompressor() { }
virtual int maxCompressedSize(const int bufferSize) = 0;
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
unsigned char* compressed, const int maxCompressedSize, int* compressedSize) = 0;
virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
unsigned char* buffer, const int maxBufferSize, int* bufferSize) = 0;
};
dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
dtTileCacheLayerHeader* header,
const unsigned char* heights,
const unsigned char* areas,
const unsigned char* cons,
unsigned char** outData, int* outDataSize);
void dtFreeTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheLayer* layer);
dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompressor* comp,
unsigned char* compressed, const int compressedSize,
dtTileCacheLayer** layerOut);
dtTileCacheContourSet* dtAllocTileCacheContourSet(dtTileCacheAlloc* alloc);
void dtFreeTileCacheContourSet(dtTileCacheAlloc* alloc, dtTileCacheContourSet* cset);
dtTileCachePolyMesh* dtAllocTileCachePolyMesh(dtTileCacheAlloc* alloc);
void dtFreeTileCachePolyMesh(dtTileCacheAlloc* alloc, dtTileCachePolyMesh* lmesh);
dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
const float* pos, const float radius, const float height, const unsigned char areaId);
dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
const float* bmin, const float* bmax, const unsigned char areaId);
dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId);
dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc,
dtTileCacheLayer& layer,
const int walkableClimb);
dtStatus dtBuildTileCacheContours(dtTileCacheAlloc* alloc,
dtTileCacheLayer& layer,
const int walkableClimb, const float maxError,
dtTileCacheContourSet& lcset);
dtStatus dtBuildTileCachePolyMesh(dtTileCacheAlloc* alloc,
dtTileCacheContourSet& lcset,
dtTileCachePolyMesh& mesh);
/// Swaps the endianess of the compressed tile data's header (#dtTileCacheLayerHeader).
/// Tile layer data does not need endian swapping as it consits only of bytes.
/// @param[in,out] data The tile data array.
/// @param[in] dataSize The size of the data array.
bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize);
#endif // DETOURTILECACHEBUILDER_H

View file

@ -0,0 +1,75 @@
FastLZ - lightning-fast lossless compression library
Author: Ariya Hidayat
Official website: http://www.fastlz.org
FastLZ is distributed using the MIT license, see file LICENSE
for details.
FastLZ consists of two files: fastlz.h and fastlz.c. Just add these
files to your project in order to use FastLZ. For information on
compression and decompression routines, see fastlz.h.
A simple file compressor called 6pack is included as an example
on how to use FastLZ. The corresponding decompressor is 6unpack.
To compile using GCC:
gcc -o 6pack 6pack.c fastlz.c
gcc -o 6unpack 6unpack.c fastlz.c
To compile using MinGW:
mingw32-gcc -o 6pack 6pack.c fastlz.c
mingw32-gcc -o 6unpack 6unpack.c fastlz.c
To compile using Microsoft Visual C++:
cl 6pack.c fastlz.c
cl 6unpack.c fastlz.c
To compile using Borland C++:
bcc32 6pack.c fastlz.c
bcc32 6unpack.c fastlz.c
To compile using OpenWatcom C/C++:
cl386 6pack.c fastlz.c
cl386 6unpack.c fastlz.c
To compile using Intel C++ compiler for Windows:
icl 6pack.c fastlz.c
icl 6unpack.c fastlz.c
To compile using Intel C++ compiler for Linux:
icc -o 6pack 6pack.c fastlz.c
icc -o 6unpack 6unpack.c fastlz.c
To compile 6pack using LCC-Win32:
lc 6pack.c fastlz.c
lc 6unpack.c fastlz.c
To compile 6pack using Pelles C:
pocc 6pack.c
pocc 6unpack.c
pocc fastlz.c
polink 6pack.obj fastlz.obj
polink 6unpack.obj fastlz.obj
For speed optimization, always use proper compile flags for optimization options.
Typical compiler flags are given below:
* GCC (pre 4.2): -march=pentium -O3 -fomit-frame-pointer -mtune=pentium
* GCC 4.2 or later: -march=pentium -O3 -fomit-frame-pointer -mtune=generic
* Digital Mars C/C++: -o+all -5
* Intel C++ (Windows): /O3 /Qipo
* Intel C++ (Linux): -O2 -march=pentium -mtune=pentium
* Borland C++: -O2 -5
* LCC-Win32: -O
* Pelles C: /O2

View file

@ -0,0 +1,556 @@
/*
FastLZ - lightning-fast lossless compression library
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
/*
* Always check for bound when decompressing.
* Generally it is best to leave it defined.
*/
#define FASTLZ_SAFE
/*
* Give hints to the compiler for branch prediction optimization.
*/
#if defined(__GNUC__) && (__GNUC__ > 2)
#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
#else
#define FASTLZ_EXPECT_CONDITIONAL(c) (c)
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
#endif
/*
* Use inlined functions for supported systems.
*/
#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C)
#define FASTLZ_INLINE inline
#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
#define FASTLZ_INLINE __inline
#else
#define FASTLZ_INLINE
#endif
/*
* Prevent accessing more than 8-bit at once, except on x86 architectures.
*/
#if !defined(FASTLZ_STRICT_ALIGN)
#define FASTLZ_STRICT_ALIGN
#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
#undef FASTLZ_STRICT_ALIGN
#elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */
#undef FASTLZ_STRICT_ALIGN
#elif defined(_M_IX86) /* Intel, MSVC */
#undef FASTLZ_STRICT_ALIGN
#elif defined(__386)
#undef FASTLZ_STRICT_ALIGN
#elif defined(_X86_) /* MinGW */
#undef FASTLZ_STRICT_ALIGN
#elif defined(__I86__) /* Digital Mars */
#undef FASTLZ_STRICT_ALIGN
#endif
#endif
/*
* FIXME: use preprocessor magic to set this on different platforms!
*/
typedef unsigned char flzuint8;
typedef unsigned short flzuint16;
typedef unsigned int flzuint32;
/* Disable "conversion from A to B, possible loss of data" warning when using MSVC */
#if defined(_MSC_VER)
#pragma warning(disable: 4244)
#endif
/* prototypes */
int fastlz_compress(const void* input, int length, void* output);
int fastlz_compress_level(int level, const void* input, int length, void* output);
int fastlz_decompress(const void* input, int length, void* output, int maxout);
#define MAX_COPY 32
#define MAX_LEN 264 /* 256 + 8 */
#define MAX_DISTANCE 8192
#if !defined(FASTLZ_STRICT_ALIGN)
#define FASTLZ_READU16(p) *((const flzuint16*)(p))
#else
#define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8)
#endif
#define HASH_LOG 13
#define HASH_SIZE (1<< HASH_LOG)
#define HASH_MASK (HASH_SIZE-1)
#define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; }
#undef FASTLZ_LEVEL
#define FASTLZ_LEVEL 1
#undef FASTLZ_COMPRESSOR
#undef FASTLZ_DECOMPRESSOR
#define FASTLZ_COMPRESSOR fastlz1_compress
#define FASTLZ_DECOMPRESSOR fastlz1_decompress
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
#include "fastlz.c"
#undef FASTLZ_LEVEL
#define FASTLZ_LEVEL 2
#undef MAX_DISTANCE
#define MAX_DISTANCE 8191
#define MAX_FARDISTANCE (65535+MAX_DISTANCE-1)
#undef FASTLZ_COMPRESSOR
#undef FASTLZ_DECOMPRESSOR
#define FASTLZ_COMPRESSOR fastlz2_compress
#define FASTLZ_DECOMPRESSOR fastlz2_decompress
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
#include "fastlz.c"
int fastlz_compress(const void* input, int length, void* output)
{
/* for short block, choose fastlz1 */
if(length < 65536)
return fastlz1_compress(input, length, output);
/* else... */
return fastlz2_compress(input, length, output);
}
int fastlz_decompress(const void* input, int length, void* output, int maxout)
{
/* magic identifier for compression level */
int level = ((*(const flzuint8*)input) >> 5) + 1;
if(level == 1)
return fastlz1_decompress(input, length, output, maxout);
if(level == 2)
return fastlz2_decompress(input, length, output, maxout);
/* unknown level, trigger error */
return 0;
}
int fastlz_compress_level(int level, const void* input, int length, void* output)
{
if(level == 1)
return fastlz1_compress(input, length, output);
if(level == 2)
return fastlz2_compress(input, length, output);
return 0;
}
#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output)
{
const flzuint8* ip = (const flzuint8*) input;
const flzuint8* ip_bound = ip + length - 2;
const flzuint8* ip_limit = ip + length - 12;
flzuint8* op = (flzuint8*) output;
const flzuint8* htab[HASH_SIZE];
const flzuint8** hslot;
flzuint32 hval;
flzuint32 copy;
/* sanity check */
if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4))
{
if(length)
{
/* create literal copy only */
*op++ = length-1;
ip_bound++;
while(ip <= ip_bound)
*op++ = *ip++;
return length+1;
}
else
return 0;
}
/* initializes hash table */
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
*hslot = ip;
/* we start with literal copy */
copy = 2;
*op++ = MAX_COPY-1;
*op++ = *ip++;
*op++ = *ip++;
/* main loop */
while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
{
const flzuint8* ref;
flzuint32 distance;
/* minimum match length */
flzuint32 len = 3;
/* comparison starting-point */
const flzuint8* anchor = ip;
/* check for a run */
#if FASTLZ_LEVEL==2
if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1))
{
distance = 1;
ip += 3;
ref = anchor - 1 + 3;
goto match;
}
#endif
/* find potential match */
HASH_FUNCTION(hval,ip);
hslot = htab + hval;
ref = htab[hval];
/* calculate distance to the match */
distance = anchor - ref;
/* update hash table */
*hslot = anchor;
/* is this a match? check the first 3 bytes */
if(distance==0 ||
#if FASTLZ_LEVEL==1
(distance >= MAX_DISTANCE) ||
#else
(distance >= MAX_FARDISTANCE) ||
#endif
*ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++)
goto literal;
#if FASTLZ_LEVEL==2
/* far, needs at least 5-byte match */
if(distance >= MAX_DISTANCE)
{
if(*ip++ != *ref++ || *ip++!= *ref++)
goto literal;
len += 2;
}
match:
#endif
/* last matched byte */
ip = anchor + len;
/* distance is biased */
distance--;
if(!distance)
{
/* zero distance means a run */
flzuint8 x = ip[-1];
while(ip < ip_bound)
if(*ref++ != x) break; else ip++;
}
else
for(;;)
{
/* safe because the outer check against ip limit */
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
if(*ref++ != *ip++) break;
while(ip < ip_bound)
if(*ref++ != *ip++) break;
break;
}
/* if we have copied something, adjust the copy count */
if(copy)
/* copy is biased, '0' means 1 byte copy */
*(op-copy-1) = copy-1;
else
/* back, to overwrite the copy count */
op--;
/* reset literal counter */
copy = 0;
/* length is biased, '1' means a match of 3 bytes */
ip -= 3;
len = ip - anchor;
/* encode the match */
#if FASTLZ_LEVEL==2
if(distance < MAX_DISTANCE)
{
if(len < 7)
{
*op++ = (len << 5) + (distance >> 8);
*op++ = (distance & 255);
}
else
{
*op++ = (7 << 5) + (distance >> 8);
for(len-=7; len >= 255; len-= 255)
*op++ = 255;
*op++ = len;
*op++ = (distance & 255);
}
}
else
{
/* far away, but not yet in the another galaxy... */
if(len < 7)
{
distance -= MAX_DISTANCE;
*op++ = (len << 5) + 31;
*op++ = 255;
*op++ = distance >> 8;
*op++ = distance & 255;
}
else
{
distance -= MAX_DISTANCE;
*op++ = (7 << 5) + 31;
for(len-=7; len >= 255; len-= 255)
*op++ = 255;
*op++ = len;
*op++ = 255;
*op++ = distance >> 8;
*op++ = distance & 255;
}
}
#else
if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2))
while(len > MAX_LEN-2)
{
*op++ = (7 << 5) + (distance >> 8);
*op++ = MAX_LEN - 2 - 7 -2;
*op++ = (distance & 255);
len -= MAX_LEN-2;
}
if(len < 7)
{
*op++ = (len << 5) + (distance >> 8);
*op++ = (distance & 255);
}
else
{
*op++ = (7 << 5) + (distance >> 8);
*op++ = len - 7;
*op++ = (distance & 255);
}
#endif
/* update the hash at match boundary */
HASH_FUNCTION(hval,ip);
htab[hval] = ip++;
HASH_FUNCTION(hval,ip);
htab[hval] = ip++;
/* assuming literal copy */
*op++ = MAX_COPY-1;
continue;
literal:
*op++ = *anchor++;
ip = anchor;
copy++;
if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY))
{
copy = 0;
*op++ = MAX_COPY-1;
}
}
/* left-over as literal copy */
ip_bound++;
while(ip <= ip_bound)
{
*op++ = *ip++;
copy++;
if(copy == MAX_COPY)
{
copy = 0;
*op++ = MAX_COPY-1;
}
}
/* if we have copied something, adjust the copy length */
if(copy)
*(op-copy-1) = copy-1;
else
op--;
#if FASTLZ_LEVEL==2
/* marker for fastlz2 */
*(flzuint8*)output |= (1 << 5);
#endif
return op - (flzuint8*)output;
}
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout)
{
const flzuint8* ip = (const flzuint8*) input;
const flzuint8* ip_limit = ip + length;
flzuint8* op = (flzuint8*) output;
flzuint8* op_limit = op + maxout;
flzuint32 ctrl = (*ip++) & 31;
int loop = 1;
do
{
const flzuint8* ref = op;
flzuint32 len = ctrl >> 5;
flzuint32 ofs = (ctrl & 31) << 8;
if(ctrl >= 32)
{
#if FASTLZ_LEVEL==2
flzuint8 code;
#endif
len--;
ref -= ofs;
if (len == 7-1)
#if FASTLZ_LEVEL==1
len += *ip++;
ref -= *ip++;
#else
do
{
code = *ip++;
len += code;
} while (code==255);
code = *ip++;
ref -= code;
/* match from 16-bit distance */
if(FASTLZ_UNEXPECT_CONDITIONAL(code==255))
if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8)))
{
ofs = (*ip++) << 8;
ofs += *ip++;
ref = op - ofs - MAX_DISTANCE;
}
#endif
#ifdef FASTLZ_SAFE
if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit))
return 0;
if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output))
return 0;
#endif
if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
ctrl = *ip++;
else
loop = 0;
if(ref == op)
{
/* optimize copy for a run */
flzuint8 b = ref[-1];
*op++ = b;
*op++ = b;
*op++ = b;
for(; len; --len)
*op++ = b;
}
else
{
#if !defined(FASTLZ_STRICT_ALIGN)
const flzuint16* p;
flzuint16* q;
#endif
/* copy from reference */
ref--;
*op++ = *ref++;
*op++ = *ref++;
*op++ = *ref++;
#if !defined(FASTLZ_STRICT_ALIGN)
/* copy a byte, so that now it's word aligned */
if(len & 1)
{
*op++ = *ref++;
len--;
}
/* copy 16-bit at once */
q = (flzuint16*) op;
op += len;
p = (const flzuint16*) ref;
for(len>>=1; len > 4; len-=4)
{
*q++ = *p++;
*q++ = *p++;
*q++ = *p++;
*q++ = *p++;
}
for(; len; --len)
*q++ = *p++;
#else
for(; len; --len)
*op++ = *ref++;
#endif
}
}
else
{
ctrl++;
#ifdef FASTLZ_SAFE
if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
return 0;
if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
return 0;
#endif
*op++ = *ip++;
for(--ctrl; ctrl; ctrl--)
*op++ = *ip++;
loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
if(loop)
ctrl = *ip++;
}
}
while(FASTLZ_EXPECT_CONDITIONAL(loop));
return op - (flzuint8*)output;
}
#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */

View file

@ -0,0 +1,100 @@
/*
FastLZ - lightning-fast lossless compression library
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef FASTLZ_H
#define FASTLZ_H
#define FASTLZ_VERSION 0x000100
#define FASTLZ_VERSION_MAJOR 0
#define FASTLZ_VERSION_MINOR 0
#define FASTLZ_VERSION_REVISION 0
#define FASTLZ_VERSION_STRING "0.1.0"
#if defined (__cplusplus)
extern "C" {
#endif
/**
Compress a block of data in the input buffer and returns the size of
compressed block. The size of input buffer is specified by length. The
minimum input buffer size is 16.
The output buffer must be at least 5% larger than the input buffer
and can not be smaller than 66 bytes.
If the input is not compressible, the return value might be larger than
length (input buffer size).
The input buffer and the output buffer can not overlap.
*/
int fastlz_compress(const void* input, int length, void* output);
/**
Decompress a block of compressed data and returns the size of the
decompressed block. If error occurs, e.g. the compressed data is
corrupted or the output buffer is not large enough, then 0 (zero)
will be returned instead.
The input buffer and the output buffer can not overlap.
Decompression is memory safe and guaranteed not to write the output buffer
more than what is specified in maxout.
*/
int fastlz_decompress(const void* input, int length, void* output, int maxout);
/**
Compress a block of data in the input buffer and returns the size of
compressed block. The size of input buffer is specified by length. The
minimum input buffer size is 16.
The output buffer must be at least 5% larger than the input buffer
and can not be smaller than 66 bytes.
If the input is not compressible, the return value might be larger than
length (input buffer size).
The input buffer and the output buffer can not overlap.
Compression level can be specified in parameter level. At the moment,
only level 1 and level 2 are supported.
Level 1 is the fastest compression and generally useful for short data.
Level 2 is slightly slower but it gives better compression ratio.
Note that the compressed data, regardless of the level, can always be
decompressed using the function fastlz_decompress above.
*/
int fastlz_compress_level(int level, const void* input, int length, void* output);
#if defined (__cplusplus)
}
#endif
#endif /* FASTLZ_H */

View file

@ -0,0 +1,17 @@
Copyright (c) 2009 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View file

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Detour - Debug|Win32">
<Configuration>Detour - Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Detour - Release|Win32">
<Configuration>Detour - Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Include\DetourAlloc.h" />
<ClInclude Include="Include\DetourAssert.h" />
<ClInclude Include="Include\DetourCommon.h" />
<ClInclude Include="Include\DetourMath.h" />
<ClInclude Include="Include\DetourNavMesh.h" />
<ClInclude Include="Include\DetourNavMeshBuilder.h" />
<ClInclude Include="Include\DetourNavMeshQuery.h" />
<ClInclude Include="Include\DetourNode.h" />
<ClInclude Include="Include\DetourStatus.h" />
<ClInclude Include="Include\DetourTileCache.h" />
<ClInclude Include="Include\DetourTileCacheBuilder.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DetourAlloc.cpp" />
<ClCompile Include="DetourAssert.cpp" />
<ClCompile Include="DetourCommon.cpp" />
<ClCompile Include="DetourNavMesh.cpp" />
<ClCompile Include="DetourNavMeshBuilder.cpp" />
<ClCompile Include="DetourNavMeshQuery.cpp" />
<ClCompile Include="DetourNode.cpp" />
<ClCompile Include="DetourTileCache.cpp" />
<ClCompile Include="DetourTileCacheBuilder.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{36e392b2-ea9f-43fe-9700-82dfe548c75d}</ProjectGuid>
<RootNamespace>detour</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Detour - Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Detour - Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Detour - Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Detour - Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Detour - Debug|Win32'">
<OutDir>$(SolutionDir)\Debug\Detour\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Detour - Release|Win32'">
<OutDir>$(SolutionDir)\Release\Detour\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Detour - Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)\detour\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Detour - Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)\detour\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Default</InlineFunctionExpansion>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>false</OmitFramePointers>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<Optimization>Full</Optimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -35,22 +35,6 @@
#define SF_GLOBAL_SET 1 // Set global state to initial state on spawn
class CEnvGlobal : public CPointEntity
{
public:
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
string_t m_globalstate;
int m_triggermode;
int m_initialstate;
};
TYPEDESCRIPTION CEnvGlobal::m_SaveData[] =
{

View file

@ -458,6 +458,22 @@ public:
string_t m_globalstate;
};
class CEnvGlobal : public CPointEntity
{
public:
void Spawn(void);
void KeyValue(KeyValueData* pkvd);
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
virtual int Save(CSave& save);
virtual int Restore(CRestore& restore);
static TYPEDESCRIPTION m_SaveData[];
string_t m_globalstate;
int m_triggermode;
int m_initialstate;
};
//
// generic Delay entity.

View file

@ -145,6 +145,7 @@
#include "../mod/AvHAlienAbilityConstants.h"
#include "../mod/AvHNetworkMessages.h"
#include "../mod/AvHNexusServer.h"
#include "../mod/AvHAIPlayerManager.h"
#include "../game_shared/voice_gamemgr.h"
extern CVoiceGameMgr g_VoiceGameMgr;
@ -353,7 +354,12 @@ void ClientPutInServer( edict_t *pEntity )
// Allocate a CBasePlayer for pev, and call spawn
pPlayer->SetPlayMode(PLAYMODE_READYROOM);
//pPlayer->Spawn();
//if (pev->flags & FL_FAKECLIENT)
//{
// pPlayer->Spawn();
//}
// Reset interpolation during first frame
pPlayer->pev->effects |= EF_NOINTERP;
@ -389,10 +395,14 @@ void Host_Say( edict_t *pEntity, int teamonly )
if ( !stricmp( pcmd, cpSay) || !stricmp( pcmd, cpSayTeam ) )
{
if ( CMD_ARGC() >= 2 )
int NumArgs = CMD_ARGC();
if (NumArgs >= 2 )
{
p = (char *)CMD_ARGS();
bool bMessageParsed = false;
if(GetGameRules()->GetIsTournamentMode() && !GetGameRules()->GetGameStarted())
{
if(!strcmp(CMD_ARGV(1), kReadyNotification))
@ -403,6 +413,8 @@ void Host_Say( edict_t *pEntity, int teamonly )
{
theTeam->SetIsReady();
}
bMessageParsed = true;
}
else if (!strcmp(CMD_ARGV(1), kNotReadyNotification))
{
@ -412,8 +424,33 @@ void Host_Say( edict_t *pEntity, int teamonly )
{
theTeam->SetIsReady(false);
}
bMessageParsed = true;
}
}
// Check to see if we are asking the AI commander for something
if (!bMessageParsed)
{
char shortenedMsg[32];
char* msg = (char*)CMD_ARGV(1);
strncpy(shortenedMsg, msg, 31);
char* pch;
pch = strtok(shortenedMsg, " ");
if (!stricmp(pch, kAICommanderRequest))
{
pch = strtok(NULL, " \n");
if (pch != NULL)
{
AIMGR_ReceiveCommanderRequest((AvHTeamNumber)pEntity->v.team, pEntity, pch);
}
}
}
}
else
{

View file

@ -118,13 +118,15 @@ void WRITE_STRING(const char* inData);
#ifdef AVH_SERVER
#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity)
#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister)
#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat)
#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString)
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat)
#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString)
#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer)
#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity)
#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister)
#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat)
#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString)
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat)
#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString)
#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer)
#define REGISTER_SERVER_FUNCTION (*g_engfuncs.pfnAddServerCommand)
#define RUN_AI_MOVE (*g_engfuncs.pfnRunPlayerMove)
#endif // AVH_SERVER

View file

@ -125,6 +125,22 @@ cvar_t avh_jumpmode = {kvJumpMode, "1", FCVAR_SERVER};
cvar_t avh_version = {kvVersion, "330", FCVAR_SERVER};
cvar_t avh_widescreenclamp = {kvWidescreenClamp, "0", FCVAR_SERVER};
cvar_t avh_randomrfk = {kvRandomRfk, "0", FCVAR_SERVER};
// AI Player Settings
cvar_t avh_botsenabled = { kvBotsEnabled,"0", FCVAR_SERVER }; // Bots can be added to the server Y/N
cvar_t avh_botautomode = { kvBotAutoMode,"0", FCVAR_SERVER }; // Defines automated behaviour for adding/removing bots. 0 = manual (must add via console), 1 = automatic (auto-fills teams), 2 = balance only (only keeps teams even)
cvar_t avh_botminplayers = { kvBotMinPlayers,"0", FCVAR_SERVER }; // If bots are enabled and auto mode == 1 then it will maintain this player count by adding/removing as needed
cvar_t avh_botskill = { kvBotSkill,"1", FCVAR_SERVER }; // Sets the skill for the bots (0 = easiest, 3 = hardest)
cvar_t avh_botusemapdefaults = { kvBotUseMapDefaults,"1", FCVAR_SERVER }; // If bot auto mode == 1 then the min players will be taken from the config
cvar_t avh_botcommandermode = { kvBotCommanderMode,"0", FCVAR_SERVER }; // 0 = Bots never command, 1 = If nobody takes charge, 2 = Only if no humans on team
cvar_t avh_botdebugmode = { kvBotDebugMode,"0", FCVAR_SERVER }; // 0 = Regular play, 1 = Drone mode, 2 = Test Navigation mode
cvar_t avh_botallowlerk = { kvBotAllowLerk,"1", FCVAR_SERVER }; // 0 = Bot will never evolve lerk, 1 = Bot will go lerk when appropriate
cvar_t avh_botallowfade = { kvBotAllowFade,"1", FCVAR_SERVER }; // 0 = Bot will never evolve fade, 1 = Bot will go fade when appropriate
cvar_t avh_botallowonos = { kvBotAllowOnos,"1", FCVAR_SERVER }; // 0 = Bot will never evolve onos, 1 = Bot will go onos when appropriate
cvar_t avh_botcommanderwait = { kvBotCommWait,"10", FCVAR_SERVER }; // If mp_botcommandermode == 1 then how long the bot will wait before taking command, to give a human a chance
cvar_t avh_botlerkcooldown = { kvBotLerkCooldown,"60", FCVAR_SERVER }; // If the team's lerk is killed, how long the bot will wait before going lerk again.
cvar_t avh_botmaxstucktime = { kvBotMaxStuckTime,"15", FCVAR_SERVER }; // If the team's lerk is killed, how long the bot will wait before going lerk again.
//playtest cvars
cvar_t avh_fastjp = {kvfastjp, "0", FCVAR_SERVER};
cvar_t avh_parasiteonmap = {kvParasiteOnMap, "0", FCVAR_SERVER };
@ -210,6 +226,21 @@ void GameDLLInit( void )
CVAR_REGISTER (&defaultteam);
CVAR_REGISTER (&allowmonsters);
CVAR_REGISTER (&mp_chattime);
// Register AI player settings
CVAR_REGISTER(&avh_botsenabled);
CVAR_REGISTER(&avh_botautomode);
CVAR_REGISTER(&avh_botminplayers);
CVAR_REGISTER(&avh_botusemapdefaults);
CVAR_REGISTER(&avh_botskill);
CVAR_REGISTER(&avh_botcommandermode);
CVAR_REGISTER(&avh_botdebugmode);
CVAR_REGISTER(&avh_botallowlerk);
CVAR_REGISTER(&avh_botallowfade);
CVAR_REGISTER(&avh_botallowonos);
CVAR_REGISTER(&avh_botcommanderwait);
CVAR_REGISTER(&avh_botlerkcooldown);
CVAR_REGISTER(&avh_botmaxstucktime);
// Register AvH variables
CVAR_REGISTER (&avh_drawdamage);

View file

@ -97,13 +97,13 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\includes\lua\include;$(SolutionDir)\particles\;$(SolutionDir)\includes\vgui\include;$(SolutionDir)\includes\libcurl-7.50-nossl\include;$(SolutionDir)\common;$(SolutionDir)\public;$(SolutionDir)\util;$(SolutionDir)\engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\includes\lua\include;$(SolutionDir)\particles\;$(SolutionDir)\includes\vgui\include;$(SolutionDir)\includes\libcurl-7.50-nossl\include;$(SolutionDir)\common;$(SolutionDir)\public;$(SolutionDir)\util;$(SolutionDir)\detour\Include;$(SolutionDir)\engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;particles.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;particles.lib;detour.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(SolutionDir)\Debug\Server\$(TargetName).dll</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<AdditionalLibraryDirectories>$(SolutionDir)\Debug\Particles;$(SolutionDir)\includes\lpng1251;$(SolutionDir)\includes\zlib-1.2.8;$(SolutionDir)\includes\vgui\lib\win32_vc6;$(SolutionDir)\lib\public;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(SolutionDir)\Debug\Particles;$(SolutionDir)\includes\lpng1251;$(SolutionDir)\includes\zlib-1.2.8;$(SolutionDir)\includes\vgui\lib\win32_vc6;$(SolutionDir)\Debug\Detour;$(SolutionDir)\lib\public;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreSpecificDefaultLibraries>libcmt;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>.\hl.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -154,10 +154,10 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\includes\lua\include;$(SolutionDir)\particles\;$(SolutionDir)\includes\vgui\include;$(SolutionDir)\includes\libcurl-7.50-nossl\include;$(SolutionDir)\common;$(SolutionDir)\public;$(SolutionDir)\util;$(SolutionDir)\engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\includes\lua\include;$(SolutionDir)\detour\Include;$(SolutionDir)\particles\;$(SolutionDir)\includes\vgui\include;$(SolutionDir)\includes\libcurl-7.50-nossl\include;$(SolutionDir)\common;$(SolutionDir)\public;$(SolutionDir)\util;$(SolutionDir)\engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;particles.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;particles.lib;detour.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>gdi32.lib user32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>.\hl.def</ModuleDefinitionFile>
@ -169,7 +169,7 @@
<SetChecksum>false</SetChecksum>
<TreatLinkerWarningAsErrors>false</TreatLinkerWarningAsErrors>
<AdditionalOptions>/NODEFAULTLIB:LIBCMT %(AdditionalOptions)</AdditionalOptions>
<AdditionalLibraryDirectories>$(SolutionDir)\Release\Particles;$(SolutionDir)\includes\lpng1251;$(SolutionDir)\includes\zlib-1.2.8;$(SolutionDir)\includes\vgui\lib\win32_vc6;$(SolutionDir)\lib\public;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(SolutionDir)\Release\Particles;$(SolutionDir)\includes\lpng1251;$(SolutionDir)\includes\zlib-1.2.8;$(SolutionDir)\includes\vgui\lib\win32_vc6;$(SolutionDir)\Release\Detour;$(SolutionDir)\lib\public;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>false</GenerateDebugInformation>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<OutputFile>$(SolutionDir)\Playtest\Server\$(TargetName).dll</OutputFile>
@ -211,11 +211,11 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\includes\lua\include;$(SolutionDir)\particles\;$(SolutionDir)\includes\vgui\include;$(SolutionDir)\includes\libcurl-7.50-nossl\include;$(SolutionDir)\common;$(SolutionDir)\public;$(SolutionDir)\util;$(SolutionDir)\engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\includes\lua\include;$(SolutionDir)\particles\;$(SolutionDir)\includes\vgui\include;$(SolutionDir)\includes\libcurl-7.50-nossl\include;$(SolutionDir)\common;$(SolutionDir)\public;$(SolutionDir)\util;$(SolutionDir)\detour\Include;$(SolutionDir)\engine;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<OmitFramePointers>true</OmitFramePointers>
</ClCompile>
<Link>
<AdditionalDependencies>winmm.lib;ws2_32.lib;particles.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>winmm.lib;ws2_32.lib;particles.lib;detour.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SuppressStartupBanner>true</SuppressStartupBanner>
<IgnoreSpecificDefaultLibraries>gdi32.lib user32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>.\hl.def</ModuleDefinitionFile>
@ -227,7 +227,7 @@
<SetChecksum>false</SetChecksum>
<TreatLinkerWarningAsErrors>false</TreatLinkerWarningAsErrors>
<AdditionalOptions>/NODEFAULTLIB:LIBCMT %(AdditionalOptions)</AdditionalOptions>
<AdditionalLibraryDirectories>$(SolutionDir)\Release\Particles;$(SolutionDir)\includes\lpng1251;$(SolutionDir)\includes\zlib-1.2.8;$(SolutionDir)\includes\vgui\lib\win32_vc6;$(SolutionDir)\lib\public;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(SolutionDir)\Release\Detour;$(SolutionDir)\Release\Particles;$(SolutionDir)\includes\lpng1251;$(SolutionDir)\includes\zlib-1.2.8;$(SolutionDir)\includes\vgui\lib\win32_vc6;$(SolutionDir)\lib\public;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<OutputFile>$(SolutionDir)\Release\Server\$(TargetName).dll</OutputFile>
@ -248,6 +248,17 @@
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\mod\AvHAICommander.cpp" />
<ClCompile Include="..\mod\AvHAIConfig.cpp" />
<ClCompile Include="..\mod\AvHAIHelper.cpp" />
<ClCompile Include="..\mod\AvHAIMath.cpp" />
<ClCompile Include="..\mod\AvHAINavigation.cpp" />
<ClCompile Include="..\mod\AvHAIPlayer.cpp" />
<ClCompile Include="..\mod\AvHAIPlayerManager.cpp" />
<ClCompile Include="..\mod\AvHAIPlayerUtil.cpp" />
<ClCompile Include="..\mod\AvHAITactical.cpp" />
<ClCompile Include="..\mod\AvHAITask.cpp" />
<ClCompile Include="..\mod\AvHAIWeaponHelper.cpp" />
<ClCompile Include="animating.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Server - Debug|Win32'">Disabled</Optimization>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Server - Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ -1370,6 +1381,18 @@
<ClCompile Include="..\textrep\TRFactory.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\mod\AvHAICommander.h" />
<ClInclude Include="..\mod\AvHAIConfig.h" />
<ClInclude Include="..\mod\AvHAIConstants.h" />
<ClInclude Include="..\mod\AvHAIHelper.h" />
<ClInclude Include="..\mod\AvHAIMath.h" />
<ClInclude Include="..\mod\AvHAINavigation.h" />
<ClInclude Include="..\mod\AvHAIPlayer.h" />
<ClInclude Include="..\mod\AvHAIPlayerManager.h" />
<ClInclude Include="..\mod\AvHAIPlayerUtil.h" />
<ClInclude Include="..\mod\AvHAITactical.h" />
<ClInclude Include="..\mod\AvHAITask.h" />
<ClInclude Include="..\mod\AvHAIWeaponHelper.h" />
<ClInclude Include="activity.h" />
<ClInclude Include="activitymap.h" />
<ClInclude Include="animation.h" />

View file

@ -24,6 +24,9 @@
<Filter Include="textrep">
<UniqueIdentifier>{bdec0058-6589-4e1b-ab38-ecebc623b07f}</UniqueIdentifier>
</Filter>
<Filter Include="mod\bots">
<UniqueIdentifier>{66a5a6b4-18d8-4a1f-b7f7-70ed086d2c06}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="animating.cpp">
@ -509,6 +512,39 @@
<ClCompile Include="compatibility.cpp">
<Filter>util</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIPlayerManager.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIPlayerUtil.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAITactical.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAITask.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIWeaponHelper.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAICommander.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIConfig.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIHelper.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIMath.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAINavigation.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
<ClCompile Include="..\mod\AvHAIPlayer.cpp">
<Filter>mod\bots</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="activity.h">
@ -895,5 +931,41 @@
<ClInclude Include="..\textrep\TRTagValuePair.h">
<Filter>textrep</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIPlayerManager.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIPlayerUtil.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAITactical.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAITask.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIWeaponHelper.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAICommander.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIConfig.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIConstants.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIHelper.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIMath.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAINavigation.h">
<Filter>mod\bots</Filter>
</ClInclude>
<ClInclude Include="..\mod\AvHAIPlayer.h">
<Filter>mod\bots</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -3139,8 +3139,8 @@ void CBasePlayer::Spawn( void )
pev->takedamage = DAMAGE_AIM;
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_WALK;
pev->flags &= FL_PROXY; // keep proxy flag sey by engine
pev->flags |= FL_CLIENT;
pev->flags &= FL_PROXY | FL_FAKECLIENT; // keep proxy and fakeclient flags set by engine
pev->flags |= FL_CLIENT;
pev->air_finished = gpGlobals->time + 12;
pev->dmg = 2; // initial water damage
pev->effects = 0;

View file

@ -259,47 +259,9 @@ void CTriggerRelay::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE
// FLAG: THREAD (create clones when triggered)
// FLAG: CLONE (this is a clone for a threaded execution)
#define SF_MULTIMAN_CLONE 0x80000000
#define SF_MULTIMAN_THREAD 0x00000001
class CMultiManager : public CBaseToggle
{
public:
void KeyValue( KeyValueData *pkvd );
void Spawn ( void );
void EXPORT ManagerThink ( void );
void EXPORT ManagerUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
#if _DEBUG
void EXPORT ManagerReport( void );
#endif
BOOL HasTarget( string_t targetname );
int ObjectCaps( void ) { return CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int m_cTargets; // the total number of targets in this manager's fire list.
int m_index; // Current target
float m_startTime;// Time we started firing
int m_iTargetName [ MAX_MULTI_TARGETS ];// list if indexes into global string array
float m_flTargetDelay [ MAX_MULTI_TARGETS ];// delay (in seconds) from time of manager fire to target fire
private:
inline BOOL IsClone( void ) { return (pev->spawnflags & SF_MULTIMAN_CLONE) ? TRUE : FALSE; }
inline BOOL ShouldClone( void )
{
if ( IsClone() )
return FALSE;
return (pev->spawnflags & SF_MULTIMAN_THREAD) ? TRUE : FALSE;
}
CMultiManager *Clone( void );
};
LINK_ENTITY_TO_CLASS( multi_manager, CMultiManager );
// Global Savedata for multi_manager
@ -2049,27 +2011,6 @@ void CTriggerGravity::GravityTouch( CBaseEntity *pOther )
// this is a really bad idea.
class CTriggerChangeTarget : public CBaseDelay
{
public:
void KeyValue( KeyValueData *pkvd );
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
private:
int m_iszNewTarget;
};
LINK_ENTITY_TO_CLASS( trigger_changetarget, CTriggerChangeTarget );
TYPEDESCRIPTION CTriggerChangeTarget::m_SaveData[] =

View file

@ -1,5 +1,8 @@
// ============================== LADDER =======================================
#define SF_MULTIMAN_CLONE 0x80000000
#define SF_MULTIMAN_THREAD 0x00000001
class CBaseTrigger : public CBaseToggle
{
public:
@ -17,6 +20,65 @@ public:
virtual int ObjectCaps( void ) { return CBaseToggle :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
};
class CMultiManager : public CBaseToggle
{
public:
void KeyValue(KeyValueData* pkvd);
void Spawn(void);
void EXPORT ManagerThink(void);
void EXPORT ManagerUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
#if _DEBUG
void EXPORT ManagerReport(void);
#endif
BOOL HasTarget(string_t targetname);
int ObjectCaps(void) { return CBaseToggle::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save(CSave& save);
virtual int Restore(CRestore& restore);
static TYPEDESCRIPTION m_SaveData[];
int m_cTargets; // the total number of targets in this manager's fire list.
int m_index; // Current target
float m_startTime;// Time we started firing
int m_iTargetName[MAX_MULTI_TARGETS];// list if indexes into global string array
float m_flTargetDelay[MAX_MULTI_TARGETS];// delay (in seconds) from time of manager fire to target fire
private:
inline BOOL IsClone(void) { return (pev->spawnflags & SF_MULTIMAN_CLONE) ? TRUE : FALSE; }
inline BOOL ShouldClone(void)
{
if (IsClone())
return FALSE;
return (pev->spawnflags & SF_MULTIMAN_THREAD) ? TRUE : FALSE;
}
CMultiManager* Clone(void);
};
// this is a really bad idea.
class CTriggerChangeTarget : public CBaseDelay
{
public:
void KeyValue(KeyValueData* pkvd);
void Spawn(void);
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
int GetNewTargetName() { return m_iszNewTarget; }
int ObjectCaps(void) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save(CSave& save);
virtual int Restore(CRestore& restore);
static TYPEDESCRIPTION m_SaveData[];
private:
int m_iszNewTarget;
};
class CLadder : public CBaseTrigger
{

View file

@ -10,6 +10,7 @@ NS_MOD_SRC_DIR=$(SOURCE_DIR)/mod
UTIL_SRC_DIR=$(SOURCE_DIR)/util
TEXTREP_SRC_DIR=$(SOURCE_DIR)/textrep
PM_SHARED_SRC_DIR=$(SOURCE_DIR)/pm_shared
DETOUR_SRC_DIR=$(SOURCE_DIR)/detour
HLDLL_OBJ_DIR=$(BUILD_OBJ_DIR)/hldll
HLWPN_OBJ_DIR=$(HLDLL_OBJ_DIR)/wpn_shared
@ -18,17 +19,20 @@ GAME_SHARED_OBJ_DIR=$(HLDLL_OBJ_DIR)/game_shared
NS_MOD_OBJ_DIR=$(HLDLL_OBJ_DIR)/mod
UTIL_OBJ_DIR=$(HLDLL_OBJ_DIR)/util
TEXTREP_OBJ_DIR=$(HLDLL_OBJ_DIR)/textrep
DETOUR_OBJ_DIR=$(HLDLL_OBJ_DIR)/detour
#CFLAGS=$(BASE_CFLAGS) $(ARCH_CFLAGS) $(SHLIBCFLAGS) -DCLIENT_WEAPONS
CFLAGS=$(BASE_CFLAGS) $(ARCH_CFLAGS) -Dstricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -DAVH_SERVER -DLINUX -DVALVE_DLL -DQUIVER -DVOXEL -DQUAKE2 -DDEDICATED -DSWDS -D_STLP_USE_GLIBC -DUSE_OLDAUTH -Dsprintf_s=snprintf -D_stricmp=strcasecmp -DDISABLE_VEC_FUNCS -D_FORTIFY_SOURCE=0
#-O3 -ffast-math -fno-strength-reduce -DCURL_STATICLIB
DETOUR_CFLAGS=$(filter-out -O3,$(ARCH_CFLAGS)) -O1
HLDLL_INCLUDEDIRS=-I$(HLDLL_SRC_DIR) -I../ -I$(ENGINE_SRC_DIR) -I$(COMMON_SRC_DIR) -I$(PM_SRC_DIR) -I$(GAME_SHARED_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(UTIL_SRC_DIR) -I$(NS_MOD_SRC_DIR) -I$(PM_OBJ_DIR)
HLDLL_INCLUDEDIRS=-I$(HLDLL_SRC_DIR) -I../ -I$(ENGINE_SRC_DIR) -I$(COMMON_SRC_DIR) -I$(PM_SRC_DIR) -I$(GAME_SHARED_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(UTIL_SRC_DIR) -I$(NS_MOD_SRC_DIR) -I$(PM_OBJ_DIR) -I$(DETOUR_SRC_DIR) -I$(DETOUR_SRC_DIR)/Include
PM_INCLUDEDIRS=-I$(COMMON_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(PM_SHARED_SRC_DIR)
GAME_SHARED_INCLUDEDIRS=-I$(HLDLL_SRC_DIR) -I$(ENGINE_SRC_DIR) -I$(COMMON_SRC_DIR) -I$(PM_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(COMMON_SRC_DIR)
MOD_INCLUDEDIRS=-I$(UTIL_SRC_DIR) -I$(GAME_SHARED_SRC_DIR) -I$(HLDLL_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(COMMON_SRC_DIR) -I$(ENGINE_SRC_DIR) -I../particles -I../cdll/
MOD_INCLUDEDIRS=-I$(UTIL_SRC_DIR) -I$(GAME_SHARED_SRC_DIR) -I$(HLDLL_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(COMMON_SRC_DIR) -I$(ENGINE_SRC_DIR) -I$(DETOUR_SRC_DIR) -I$(DETOUR_SRC_DIR)/Include -I../particles -I../cdll/
UTIL_INCLUDEDIRS = -I../ -I../util
TEXTREP_INCLUDEDIRS=..
DETOUR_INCLUDEDIRS=-I$(DETOUR_SRC_DIR) -I$(DETOUR_SRC_DIR)/Include
ifeq ($(OS),Darwin)
LDFLAGS=-lm -lstdc++ -L. libparticleMP.a
@ -44,6 +48,8 @@ DO_GAME_SHARED_CC=$(CC) $(GAME_SHARED_INCLUDEDIRS) $(CFLAGS) -o $@ -c $<
DO_MOD_CC=$(CC) $(MOD_INCLUDEDIRS) $(CFLAGS) -o $@ -c $<
DO_UTIL_CC=$(CC) $(UTIL_INCLUDEDIRS) $(CFLAGS) -o $@ -c $<
DO_TEXTREP_CC=$(CC) $(TEXTREP_INCLUDEDIRS) $(CFLAGS) -o $@ -c $<
DO_DETOUR_CC=$(CC) $(DETOUR_INCLUDEDIRS) $(DETOUR_CFLAGS) -o $@ -c $<
#####################################################################
HLDLL_OBJS = \
@ -100,6 +106,17 @@ HLDLL_OBJS = \
MOD_OBJS = \
$(NS_MOD_OBJ_DIR)/AnimationUtil.o \
$(NS_MOD_OBJ_DIR)/AvHAcidRocketGun.o \
$(NS_MOD_OBJ_DIR)/AvHAICommander.o \
$(NS_MOD_OBJ_DIR)/AvHAIConfig.o \
$(NS_MOD_OBJ_DIR)/AvHAIHelper.o \
$(NS_MOD_OBJ_DIR)/AvHAIMath.o \
$(NS_MOD_OBJ_DIR)/AvHAINavigation.o \
$(NS_MOD_OBJ_DIR)/AvHAIPlayer.o \
$(NS_MOD_OBJ_DIR)/AvHAIPlayerManager.o \
$(NS_MOD_OBJ_DIR)/AvHAIPlayerUtil.o \
$(NS_MOD_OBJ_DIR)/AvHAITactical.o \
$(NS_MOD_OBJ_DIR)/AvHAITask.o \
$(NS_MOD_OBJ_DIR)/AvHAIWeaponHelper.o \
$(NS_MOD_OBJ_DIR)/AvHAlienAbilities.o \
$(NS_MOD_OBJ_DIR)/AvHAlienEquipment.o \
$(NS_MOD_OBJ_DIR)/AvHAlienTurret.o \
@ -215,6 +232,17 @@ TEXTREP_OBJS = \
$(TEXTREP_OBJ_DIR)/TRDescription.o \
$(TEXTREP_OBJ_DIR)/TRFactory.o \
DETOUR_OBJS = \
$(DETOUR_OBJ_DIR)/DetourAlloc.o \
$(DETOUR_OBJ_DIR)/DetourAssert.o \
$(DETOUR_OBJ_DIR)/DetourCommon.o \
$(DETOUR_OBJ_DIR)/DetourNavMesh.o \
$(DETOUR_OBJ_DIR)/DetourNavMeshBuilder.o \
$(DETOUR_OBJ_DIR)/DetourNavMeshQuery.o \
$(DETOUR_OBJ_DIR)/DetourNode.o \
$(DETOUR_OBJ_DIR)/DetourTileCache.o \
$(DETOUR_OBJ_DIR)/DetourTileCacheBuilder.o \
all: dirs ns.$(SHLIBEXT)
dirs:
@ -226,9 +254,10 @@ dirs:
-mkdir $(UTIL_OBJ_DIR)
-mkdir $(TEXTREP_OBJ_DIR)
-mkdir $(GAME_SHARED_OBJ_DIR)
-mkdir $(DETOUR_OBJ_DIR)
ns.$(SHLIBEXT): $(HLDLL_OBJS) $(MOD_OBJS) $(PM_OBJS) $(GAME_SHARED_OBJS) $(UTIL_OBJS) $(TEXTREP_OBJS)
$(CC) $(SHLIBLDFLAGS) -o $(BUILD_DIR)/$@ $(HLDLL_OBJS) $(MOD_OBJS) $(PM_OBJS) $(GAME_SHARED_OBJS) $(UTIL_OBJS) $(TEXTREP_OBJS) $(LDFLAGS)
ns.$(SHLIBEXT): $(HLDLL_OBJS) $(MOD_OBJS) $(PM_OBJS) $(GAME_SHARED_OBJS) $(UTIL_OBJS) $(TEXTREP_OBJS) $(DETOUR_OBJS)
$(CC) $(SHLIBLDFLAGS) -o $(BUILD_DIR)/$@ $(HLDLL_OBJS) $(MOD_OBJS) $(PM_OBJS) $(GAME_SHARED_OBJS) $(UTIL_OBJS) $(TEXTREP_OBJS) $(DETOUR_OBJS) $(LDFLAGS)
@echo "Done..."
$(NS_MOD_OBJ_DIR)/%.o: $(NS_MOD_SRC_DIR)/%.cpp
@ -249,6 +278,9 @@ $(UTIL_OBJ_DIR)/%.o : $(UTIL_SRC_DIR)/%.cpp
$(TEXTREP_OBJ_DIR)/%.o : $(TEXTREP_SRC_DIR)/%.cpp
$(DO_TEXTREP_CC)
$(DETOUR_OBJ_DIR)/%.o : $(DETOUR_SRC_DIR)/%.cpp
$(DO_DETOUR_CC)
clean:
-rm -rf $(GAME_SHARED_OBJ_DIR)
-rm -rf $(PM_OBJ_DIR)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
//
// EvoBot - Neoptolemus' Natural Selection bot, based on Botman's HPB bot template
//
// bot_navigation.h
//
// Handles all bot path finding and movement
//
#pragma once
#ifndef AVH_AI_COMMANDER_H
#define AVH_AI_COMMANDER_H
#include "AvHAIConstants.h"
static const float MIN_COMMANDER_REMIND_TIME = 20.0f; // How frequently the commander can nag a player to do something, if they don't think they're doing it
bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location, StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE);
bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDeploy, const Vector Location);
bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToUpgrade);
bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research);
bool AICOMM_RecycleStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToRecycle);
bool AICOMM_IssueMovementOrder(AvHAIPlayer* pBot, edict_t* Recipient, const Vector MoveLocation);
bool AICOMM_IssueBuildOrder(AvHAIPlayer* pBot, edict_t* Recipient, edict_t* TargetStructuree);
bool AICOMM_IssueSecureHiveOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIHiveDefinition* HiveToSecure);
bool AICOMM_IssueSiegeHiveOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIHiveDefinition* HiveToSiege, const Vector SiegePosition);
bool AICOMM_IssueSecureResNodeOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIResourceNode* ResNode);
void AICOMM_AssignNewPlayerOrder(AvHAIPlayer* pBot, edict_t* Assignee, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose);
int AICOMM_GetNumPlayersAssignedToOrder(AvHAIPlayer* pBot, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose);
bool AICOMM_IsOrderStillValid(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot);
edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation);
bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_ClearAction(commander_action* Action);
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot);
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDeployStructureAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableStructureType StructureToBuild, const Vector Location, bool bIsUrgent);
void AICOMM_SetDeployItemAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableItemType ItemToBuild, const Vector Location, bool bIsUrgent);
void AICOMM_CommanderThink(AvHAIPlayer* pBot);
const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation);
bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair);
bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege);
bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure);
ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edict_t* Requestor);
void AICOMM_CheckNewRequests(AvHAIPlayer* pBot);
bool AICOMM_IsRequestValid(ai_commander_request* Request);
bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinition* Hive, bool bIncludeElectrical);
bool AICOMM_ShouldCommanderLeaveChair(AvHAIPlayer* pBot);
const AvHAIResourceNode* AICOMM_GetNearestResourceNodeCapOpportunity(const AvHTeamNumber Team, const Vector SearchLocation);
const AvHAIHiveDefinition* AICOMM_GetHiveSiegeOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation);
bool AICOMM_ShouldCommanderPrioritiseNodes(AvHAIPlayer* pBot);
bool AICOMM_ShouldBeacon(AvHAIPlayer* pBot);
void AICOMM_ReceiveChatRequest(AvHAIPlayer* Commander, edict_t* Requestor, const char* Request);
#endif // AVH_AI_COMMANDER_H

View file

@ -0,0 +1,545 @@
#include "AvHAIConfig.h"
#include "AvHAIMath.h"
#include "AvHServerUtil.h"
#include <unordered_map>
BotFillTiming CurrentBotFillTiming = FILLTIMING_ALLHUMANS;
std::unordered_map<std::string, TeamSizeDefinitions> TeamSizeMap;
bot_skill BotSkillLevels[4];
AvHMessageID ChamberSequence[3] = { ALIEN_BUILD_DEFENSE_CHAMBER, ALIEN_BUILD_MOVEMENT_CHAMBER, ALIEN_BUILD_SENSORY_CHAMBER };
char BotPrefix[32] = "";
extern cvar_t avh_botskill;
extern cvar_t avh_botallowlerk;
extern cvar_t avh_botallowfade;
extern cvar_t avh_botallowonos;
extern cvar_t avh_botcommanderwait;
extern cvar_t avh_botlerkcooldown;
extern cvar_t avh_botmaxstucktime;
float CONFIG_GetCommanderWaitTime()
{
return avh_botcommanderwait.value;
}
float CONFIG_GetLerkCooldown()
{
return avh_botlerkcooldown.value;
}
bool CONFIG_IsLerkAllowed()
{
return avh_botallowlerk.value > 0;
}
bool CONFIG_IsFadeAllowed()
{
return avh_botallowfade.value > 0;
}
bool CONFIG_IsOnosAllowed()
{
return avh_botallowonos.value > 0;
}
float CONFIG_GetMaxStuckTime()
{
return avh_botmaxstucktime.value;
}
string CONFIG_GetBotPrefix()
{
return string(BotPrefix);
}
int CONFIG_GetTeamASizeForMap(const char* MapName)
{
std::string s = MapName;
std::unordered_map<std::string, TeamSizeDefinitions>::const_iterator got = TeamSizeMap.find(s);
if (got == TeamSizeMap.end())
{
return TeamSizeMap["default"].TeamASize;
}
else
{
return got->second.TeamASize;
}
}
int CONFIG_GetTeamBSizeForMap(const char* MapName)
{
std::string s = MapName;
std::unordered_map<std::string, TeamSizeDefinitions>::const_iterator got = TeamSizeMap.find(s);
if (got == TeamSizeMap.end())
{
return TeamSizeMap["default"].TeamBSize;
}
else
{
return got->second.TeamBSize;
}
}
AvHMessageID CONFIG_GetHiveTechAtIndex(const int Index)
{
if (Index < 0 || Index > 2) { return MESSAGE_NULL; }
return ChamberSequence[Index];
}
bot_skill CONFIG_GetBotSkillLevel()
{
int index = clampi((int)avh_botskill.value, 0, 3);
return BotSkillLevels[index];
}
void CONFIG_ParseConfigFile()
{
BotSkillLevels[0].marine_bot_reaction_time = 0.4f;
BotSkillLevels[0].marine_bot_aim_skill = 0.1f;
BotSkillLevels[0].marine_bot_motion_tracking_skill = 0.1f;
BotSkillLevels[0].marine_bot_view_speed = 0.5f;
BotSkillLevels[0].alien_bot_reaction_time = 0.4f;
BotSkillLevels[0].alien_bot_aim_skill = 0.2f;
BotSkillLevels[0].alien_bot_motion_tracking_skill = 0.2f;
BotSkillLevels[0].alien_bot_view_speed = 0.75f;
BotSkillLevels[1].marine_bot_reaction_time = 0.2f;
BotSkillLevels[1].marine_bot_aim_skill = 0.5f;
BotSkillLevels[1].marine_bot_motion_tracking_skill = 0.4f;
BotSkillLevels[1].marine_bot_view_speed = 1.0f;
BotSkillLevels[1].alien_bot_reaction_time = 0.2f;
BotSkillLevels[1].alien_bot_aim_skill = 0.5f;
BotSkillLevels[1].alien_bot_motion_tracking_skill = 0.5f;
BotSkillLevels[1].alien_bot_view_speed = 1.3f;
BotSkillLevels[2].marine_bot_reaction_time = 0.2f;
BotSkillLevels[2].marine_bot_aim_skill = 0.6f;
BotSkillLevels[2].marine_bot_motion_tracking_skill = 0.6f;
BotSkillLevels[2].marine_bot_view_speed = 1.5f;
BotSkillLevels[2].alien_bot_reaction_time = 0.2f;
BotSkillLevels[2].alien_bot_aim_skill = 0.8f;
BotSkillLevels[2].alien_bot_motion_tracking_skill = 0.8f;
BotSkillLevels[2].alien_bot_view_speed = 1.5f;
BotSkillLevels[3].marine_bot_reaction_time = 0.1f;
BotSkillLevels[3].marine_bot_aim_skill = 1.0f;
BotSkillLevels[3].marine_bot_motion_tracking_skill = 1.0f;
BotSkillLevels[3].marine_bot_view_speed = 2.0f;
BotSkillLevels[3].alien_bot_reaction_time = 0.1f;
BotSkillLevels[3].alien_bot_aim_skill = 1.0f;
BotSkillLevels[3].alien_bot_motion_tracking_skill = 1.0f;
BotSkillLevels[3].alien_bot_view_speed = 2.0f;
string BotConfigFile = string(getModDirectory()) + "/nsbots.ini";
const char* filename = BotConfigFile.c_str();
std::ifstream cFile(filename);
if (cFile.is_open())
{
std::string line;
int CurrSkillIndex = 0;
while (getline(cFile, line))
{
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
line.end());
if (line[0] == '#' || line.empty())
continue;
auto delimiterPos = line.find("=");
auto key = line.substr(0, delimiterPos);
auto value = line.substr(delimiterPos + 1);
if (key.compare("TeamSize") == 0)
{
auto mapDelimiterPos = value.find(":");
if (mapDelimiterPos == std::string::npos)
{
continue;
}
auto mapName = value.substr(0, mapDelimiterPos);
auto teamSizes = value.substr(mapDelimiterPos + 1);
auto sizeDelimiterPos = teamSizes.find("/");
if (sizeDelimiterPos == std::string::npos)
{
continue;
}
auto marineSize = teamSizes.substr(0, sizeDelimiterPos);
auto alienSize = teamSizes.substr(sizeDelimiterPos + 1);
int iMarineSize = atoi(marineSize.c_str());
int iAlienSize = atoi(alienSize.c_str());
if (iMarineSize >= 0 && iMarineSize <= 32 && iAlienSize >= 0 && iAlienSize <= 32)
{
TeamSizeMap[mapName].TeamASize = atoi(marineSize.c_str());
TeamSizeMap[mapName].TeamBSize = atoi(alienSize.c_str());
}
continue;
}
if (key.compare("prefix") == 0)
{
sprintf(BotPrefix, value.c_str());
continue;
}
if (key.compare("BotFillTiming") == 0)
{
int FillSetting = atoi(value.c_str());
FillSetting = clampi(FillSetting, 0, 2);
CurrentBotFillTiming = (BotFillTiming)FillSetting;
continue;
}
if (key.compare("BotSkillLevel") == 0)
{
CurrSkillIndex = std::stoi(value.c_str());
CurrSkillIndex = clampi(CurrSkillIndex, 0, 3);
continue;
}
if (key.compare("MarineReactionTime") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].marine_bot_reaction_time = clampf(NewValue, 0.0f, 1.0f);
continue;
}
if (key.compare("AlienReactionTime") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].alien_bot_reaction_time = clampf(NewValue, 0.0f, 1.0f);
continue;
}
if (key.compare("MarineAimSkill") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].marine_bot_aim_skill = clampf(NewValue, 0.0f, 1.0f);
continue;
}
if (key.compare("AlienAimSkill") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].alien_bot_aim_skill = clampf(NewValue, 0.0f, 1.0f);
continue;
}
if (key.compare("MarineMovementTracking") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].marine_bot_motion_tracking_skill = clampf(NewValue, 0.0f, 1.0f);
continue;
}
if (key.compare("AlienMovementTracking") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].alien_bot_motion_tracking_skill = clampf(NewValue, 0.0f, 1.0f);
continue;
}
if (key.compare("MarineViewSpeed") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].marine_bot_view_speed = clampf(NewValue, 0.0f, 5.0f);
continue;
}
if (key.compare("AlienViewSpeed") == 0)
{
float NewValue = std::stof(value.c_str());
BotSkillLevels[CurrSkillIndex].alien_bot_view_speed = clampf(NewValue, 0.0f, 5.0f);
continue;
}
if (key.compare("ChamberSequence") == 0)
{
AvHMessageID HiveOneTech = MESSAGE_NULL;
AvHMessageID HiveTwoTech = MESSAGE_NULL;
AvHMessageID HiveThreeTech = MESSAGE_NULL;
std::vector<AvHMessageID> AvailableTechs = { ALIEN_BUILD_DEFENSE_CHAMBER, ALIEN_BUILD_MOVEMENT_CHAMBER, ALIEN_BUILD_SENSORY_CHAMBER };
auto firstTechDelimiter = value.find("/");
if (firstTechDelimiter == std::string::npos)
{
continue;
}
auto FirstTech = value.substr(0, firstTechDelimiter);
auto NextTechs = value.substr(firstTechDelimiter + 1);
auto SecondTechDelimiter = NextTechs.find("/");
if (SecondTechDelimiter == std::string::npos)
{
continue;
}
auto SecondTech = NextTechs.substr(0, SecondTechDelimiter);
auto ThirdTech = NextTechs.substr(SecondTechDelimiter + 1);
if (FirstTech.compare("movement") == 0)
{
HiveOneTech = ALIEN_BUILD_MOVEMENT_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER), AvailableTechs.end());
}
else if (FirstTech.compare("defense") == 0)
{
HiveOneTech = ALIEN_BUILD_DEFENSE_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER), AvailableTechs.end());
}
else if (FirstTech.compare("sensory") == 0)
{
HiveOneTech = ALIEN_BUILD_SENSORY_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER), AvailableTechs.end());
}
if (SecondTech.compare("movement") == 0)
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER) != AvailableTechs.end())
{
HiveTwoTech = ALIEN_BUILD_MOVEMENT_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER), AvailableTechs.end());
}
}
else if (SecondTech.compare("defense") == 0)
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER) != AvailableTechs.end())
{
HiveTwoTech = ALIEN_BUILD_DEFENSE_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER), AvailableTechs.end());
}
}
else if (SecondTech.compare("sensory") == 0)
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER) != AvailableTechs.end())
{
HiveTwoTech = ALIEN_BUILD_SENSORY_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER), AvailableTechs.end());
}
}
if (ThirdTech.compare("movement") == 0)
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER) != AvailableTechs.end())
{
HiveThreeTech = ALIEN_BUILD_MOVEMENT_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_MOVEMENT_CHAMBER), AvailableTechs.end());
}
}
else if (ThirdTech.compare("defense") == 0)
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER) != AvailableTechs.end())
{
HiveThreeTech = ALIEN_BUILD_DEFENSE_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_DEFENSE_CHAMBER), AvailableTechs.end());
}
}
else if (ThirdTech.compare("sensory") == 0)
{
if (std::find(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER) != AvailableTechs.end())
{
HiveThreeTech = ALIEN_BUILD_SENSORY_CHAMBER;
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), ALIEN_BUILD_SENSORY_CHAMBER), AvailableTechs.end());
}
}
if (HiveOneTech == MESSAGE_NULL)
{
int random = rand() % AvailableTechs.size();
HiveOneTech = AvailableTechs[random];
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), HiveOneTech), AvailableTechs.end());
}
if (HiveTwoTech == MESSAGE_NULL)
{
int random = rand() % AvailableTechs.size();
HiveTwoTech = AvailableTechs[random];
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), HiveTwoTech), AvailableTechs.end());
}
if (HiveThreeTech == MESSAGE_NULL)
{
int random = rand() % AvailableTechs.size();
HiveThreeTech = AvailableTechs[random];
AvailableTechs.erase(std::remove(AvailableTechs.begin(), AvailableTechs.end(), HiveTwoTech), AvailableTechs.end());
}
ChamberSequence[0] = HiveOneTech;
ChamberSequence[1] = HiveTwoTech;
ChamberSequence[2] = HiveThreeTech;
continue;
}
}
}
else
{
ALERT(at_console, "nsbots.ini was not found in the NS mod folder. You can regenerate it with the console command 'sv_regenbotini'");
}
}
BotFillTiming CONFIG_GetBotFillTiming()
{
return CurrentBotFillTiming;
}
void CONFIG_RegenerateIniFile()
{
string BotConfigFile = string(getModDirectory()) + "/nsbots.ini";
const char* filename = BotConfigFile.c_str();
FILE* NewConfigFile = fopen(filename, "w+");
if (!NewConfigFile)
{
ALERT(at_console, "Unable to write to %s, please ensure the user has privileges\n", filename);
return;
}
fprintf(NewConfigFile, "### General bot settings ###\n\n");
fprintf(NewConfigFile, "# What prefix to put in front of a bot's name (can leave blank)\n");
fprintf(NewConfigFile, "prefix=[BOT]\n\n");
fprintf(NewConfigFile, "# When should the server start adding bots? Note: bots will always be added after round start regardless\n");
fprintf(NewConfigFile, "# 0 = On map load (after 5 second grace period)\n");
fprintf(NewConfigFile, "# 1 = When all humans have joined a team (i.e. no more humans left in ready room)\n");
fprintf(NewConfigFile, "# 2 = When the round has started (after countdown)\n");
fprintf(NewConfigFile, "BotFillTiming = 1\n\n\n");
fprintf(NewConfigFile, "### Skill Settings ###\n\n");
fprintf(NewConfigFile, "# Bot skill settings. You can define as many settings as you like and reference them by name\n");
fprintf(NewConfigFile, "# Format is BotSkillName = name, followed by one of the following:\n");
fprintf(NewConfigFile, "# ReactionTime = How quickly in seconds the bot will react to sighting enemies\n");
fprintf(NewConfigFile, "# AimSkill = How accurately the bot can lock sights on you after seeing you (0.0 - 1.0)\n");
fprintf(NewConfigFile, "# MovementTracking = How accurately the bot can follow a moving target (0.0 - 1.0)\n");
fprintf(NewConfigFile, "# ViewSpeed = How fast the bot can swivel its view (0.1 - 2.0)\n");
fprintf(NewConfigFile, "# Set the difficulty using the 'mp_botskill' cvar (0 - 3)\n\n");
fprintf(NewConfigFile, "BotSkillLevel=0\n");
fprintf(NewConfigFile, "MarineReactionTime=0.5\n");
fprintf(NewConfigFile, "MarineAimSkill=0.1\n");
fprintf(NewConfigFile, "MarineMovementTracking=0.1\n");
fprintf(NewConfigFile, "MarineViewSpeed=0.5\n");
fprintf(NewConfigFile, "AlienReactionTime=0.5\n");
fprintf(NewConfigFile, "AlienAimSkill=0.2\n");
fprintf(NewConfigFile, "AlienMovementTracking=0.2\n");
fprintf(NewConfigFile, "AlienViewSpeed=0.75\n\n");
fprintf(NewConfigFile, "BotSkillLevel=1\n");
fprintf(NewConfigFile, "MarineReactionTime=0.2\n");
fprintf(NewConfigFile, "MarineAimSkill=0.5\n");
fprintf(NewConfigFile, "MarineMovementTracking=0.4\n");
fprintf(NewConfigFile, "MarineViewSpeed=1.0\n");
fprintf(NewConfigFile, "AlienReactionTime=0.2\n");
fprintf(NewConfigFile, "AlienAimSkill=0.5\n");
fprintf(NewConfigFile, "AlienMovementTracking=0.5\n");
fprintf(NewConfigFile, "AlienViewSpeed=1.3\n\n");
fprintf(NewConfigFile, "BotSkillLevel=2\n");
fprintf(NewConfigFile, "MarineReactionTime=0.2\n");
fprintf(NewConfigFile, "MarineAimSkill=0.6\n");
fprintf(NewConfigFile, "MarineMovementTracking=0.6\n");
fprintf(NewConfigFile, "MarineViewSpeed=1.5\n");
fprintf(NewConfigFile, "AlienReactionTime=0.2\n");
fprintf(NewConfigFile, "AlienAimSkill=0.8\n");
fprintf(NewConfigFile, "AlienMovementTracking=0.8\n");
fprintf(NewConfigFile, "AlienViewSpeed=1.5\n\n");
fprintf(NewConfigFile, "BotSkillLevel=3\n");
fprintf(NewConfigFile, "MarineReactionTime=0.1\n");
fprintf(NewConfigFile, "MarineAimSkill=1.0\n");
fprintf(NewConfigFile, "MarineMovementTracking=1.0\n");
fprintf(NewConfigFile, "MarineViewSpeed=2.0\n");
fprintf(NewConfigFile, "AlienReactionTime=0.1\n");
fprintf(NewConfigFile, "AlienAimSkill=1.0\n");
fprintf(NewConfigFile, "AlienMovementTracking=1.0\n");
fprintf(NewConfigFile, "AlienViewSpeed=2.0\n\n");
fprintf(NewConfigFile, "# Desired team sizes. Only used if bot fill mode is 'fillteams'\n");
fprintf(NewConfigFile, "# Format is TeamSize=mapname:nummarines/numaliens\n");
fprintf(NewConfigFile, "# 'default' will be used if playing a map not listed below\n");
fprintf(NewConfigFile, "TeamSize=default:7/7\n");
fprintf(NewConfigFile, "TeamSize=ns_machina:8/8\n");
fprintf(NewConfigFile, "TeamSize=ns_ragnarok:8/8\n");
fprintf(NewConfigFile, "TeamSize=co_faceoff:4/4\n");
fprintf(NewConfigFile, "TeamSize=co_core:4/4\n");
fprintf(NewConfigFile, "TeamSize=co_pulse:6/6\n");
fprintf(NewConfigFile, "TeamSize=co_ulysses:6/6\n");
fprintf(NewConfigFile, "TeamSize=co_niveus:5/5\n");
fprintf(NewConfigFile, "TeamSize=co_kestrel:5/5\n\n\n");
fprintf(NewConfigFile, "### Alien Settings ###\n\n");
fprintf(NewConfigFile, "# Preferred chamber sequence. Valid entries are 'defense', 'movement' and 'sensory'. Separate sequence with forward slash\n");
fprintf(NewConfigFile, "# You can also use ? for random, so if you want movement always first but then defense and sensory at random, use\n");
fprintf(NewConfigFile, "# ChamberSequence:movement/?/?\n");
fprintf(NewConfigFile, "# Or if you want sensory always last, but movement and defence random, use\n");
fprintf(NewConfigFile, "# ChamberSequence=?/?/sensory\n");
fprintf(NewConfigFile, "ChamberSequence=defense/movement/sensory\n");
fflush(NewConfigFile);
fclose(NewConfigFile);
}

View file

@ -0,0 +1,63 @@
#pragma once
#ifndef AVH_AI_CONFIG_H
#define AVH_AI_CONFIG_H
#include "AvHAIConstants.h"
// Bot fill mode determines how bots should be automatically added/removed from teams
typedef enum _BOTFILLMODE
{
BOTFILL_MANUAL = 0, // Manual, no automatic adding or removal of bots
BOTFILL_BALANCEONLY, // Bots are automatically added/removed to ensure teams remain balanced
BOTFILL_FILLTEAMS // Bots are automatically added/removed to ensure teams maintain a certain number of players (see TeamSizeDefinitions)
} BotFillMode;
// Each map can have a desired marine and alien team size
typedef struct _TEAMSIZEDEFINITIONS
{
int TeamASize = 6;
int TeamBSize = 6;
} TeamSizeDefinitions;
typedef enum _BOTFILLTIMING
{
FILLTIMING_MAPLOAD = 0, // Bots will start filling teams after map load (after grace period)
FILLTIMING_ALLHUMANS, // Bots will only start filling teams once all humans in the ready room have joined a team
FILLTIMING_ROUNDSTART // Bots will only start filling teams after round start
} BotFillTiming;
// Reads evobot.cfg in addons/evobot and populates all the settings from it
void CONFIG_ParseConfigFile();
string CONFIG_GetBotPrefix();
// Returns the current commander wait time is COMMANDERMODE_ALWAYS (see CONFIG_GetCommanderMode())
float CONFIG_GetCommanderWaitTime();
// Returns the current lerk cooldown (how long aliens wait before evolving another lerk after the last one died)
float CONFIG_GetLerkCooldown();
bool CONFIG_IsLerkAllowed();
bool CONFIG_IsFadeAllowed();
bool CONFIG_IsOnosAllowed();
// Returns the max time a bot is allowed to be stuck before suiciding (0 means forever)
float CONFIG_GetMaxStuckTime();
// Returns the desired marine team size for the given map, indexes into TeamSizeMap
int CONFIG_GetTeamASizeForMap(const char* MapName);
// Returns the desired alien team size for the given map, indexes into TeamSizeMap
int CONFIG_GetTeamBSizeForMap(const char* MapName);
// Returns the configured hive tech at that index (chamber build sequence)
AvHMessageID CONFIG_GetHiveTechAtIndex(const int Index);
bot_skill CONFIG_GetBotSkillLevel();
BotFillTiming CONFIG_GetBotFillTiming();
void CONFIG_RegenerateIniFile();
#endif

View file

@ -0,0 +1,807 @@
#pragma once
#ifndef AVH_AI_CONSTANTS_H
#define AVH_AI_CONSTANTS_H
#include "DetourStatus.h"
#include "DetourNavMeshQuery.h"
#include "AvHHive.h"
#include "AvHEntities.h"
static const float commander_action_cooldown = 1.0f;
static const float min_request_spam_time = 10.0f;
constexpr auto MAX_AI_PATH_SIZE = 512; // Maximum number of points allowed in a path (this should be enough for any sized map)
static const int MAX_NAV_MESHES = 8; // Max number of nav meshes allowed. Currently 3 are used (one for building placement, one for the onos, and a regular one for everyone else)
// NS weapon types. Each number refers to the GoldSrc weapon index
typedef enum
{
WEAPON_INVALID = 0,
WEAPON_LERK_SPIKE = 4, // I think this is an early NS weapon, replaced by primal scream
// Marine Weapons
WEAPON_MARINE_KNIFE = 13,
WEAPON_MARINE_PISTOL = 14,
WEAPON_MARINE_MG = 15,
WEAPON_MARINE_SHOTGUN = 16,
WEAPON_MARINE_HMG = 17,
WEAPON_MARINE_WELDER = 18,
WEAPON_MARINE_MINES = 19,
WEAPON_MARINE_GL = 20,
WEAPON_MARINE_GRENADE = 28,
// Alien Abilities
WEAPON_SKULK_BITE = 5,
WEAPON_SKULK_PARASITE = 10,
WEAPON_SKULK_LEAP = 21,
WEAPON_SKULK_XENOCIDE = 12,
WEAPON_GORGE_SPIT = 2,
WEAPON_GORGE_HEALINGSPRAY = 27,
WEAPON_GORGE_BILEBOMB = 25,
WEAPON_GORGE_WEB = 8,
WEAPON_LERK_BITE = 6,
WEAPON_LERK_SPORES = 3,
WEAPON_LERK_UMBRA = 23,
WEAPON_LERK_PRIMALSCREAM = 24,
WEAPON_FADE_SWIPE = 7,
WEAPON_FADE_BLINK = 11,
WEAPON_FADE_METABOLIZE = 9,
WEAPON_FADE_ACIDROCKET = 26,
WEAPON_ONOS_GORE = 1,
WEAPON_ONOS_DEVOUR = 30,
WEAPON_ONOS_STOMP = 29,
WEAPON_ONOS_CHARGE = 22,
WEAPON_MAX = 31
}
AvHAIWeapon;
// Hives can either be unbuilt ("ghost" hive), in progress or fully built (active)
typedef enum
{
HIVE_STATUS_UNBUILT = 0,
HIVE_STATUS_BUILDING = 1,
HIVE_STATUS_BUILT = 2
} HiveStatusType;
// All tech statuses that can be assigned to a hive
typedef enum
{
HIVE_TECH_NONE = 0, // Hive doesn't have any tech assigned to it yet (no chambers built for it)
HIVE_TECH_DEFENCE = 1,
HIVE_TECH_SENSORY = 2,
HIVE_TECH_MOVEMENT = 3
} HiveTechStatus;
typedef enum _AI_REACHABILITY_STATUS
{
AI_REACHABILITY_NONE = 0,
AI_REACHABILITY_MARINE = 1u << 0,
AI_REACHABILITY_SKULK = 1u << 1,
AI_REACHABILITY_GORGE = 1u << 2,
AI_REACHABILITY_ONOS = 1u << 3,
AI_REACHABILITY_WELDER = 1u << 4,
AI_REACHABILITY_UNREACHABLE = 1u << 5,
AI_REACHABILITY_ALL = -1
} AvHAIReachabilityStatus;
typedef enum
{
STRUCTURE_STATUS_NONE = 0, // No filters, all buildings will be returned
STRUCTURE_STATUS_COMPLETED = 1, // Structure is fully built
STRUCTURE_STATUS_ELECTRIFIED = 1 << 1,
STRUCTURE_STATUS_RECYCLING = 1 << 2,
STRUCTURE_STATUS_PARASITED = 1 << 3,
STRUCTURE_STATUS_UNDERATTACK = 1 << 4,
STRUCTURE_STATUS_RESEARCHING = 1 << 5,
STRUCTURE_STATUS_DAMAGED = 1 << 6,
STRUCTURE_STATUS_DISABLED = 1 << 7, // For marine turrets when there's no TF
STRUCTURE_STATUS_ALL = -1
} AvHAIStructureStatus;
typedef enum
{
STRUCTURE_NONE = 0,
STRUCTURE_MARINE_RESTOWER = 1u,
STRUCTURE_MARINE_INFANTRYPORTAL = 1u << 1,
STRUCTURE_MARINE_TURRETFACTORY = 1u << 2,
STRUCTURE_MARINE_ADVTURRETFACTORY = 1u << 3,
STRUCTURE_MARINE_ARMOURY = 1u << 4,
STRUCTURE_MARINE_ADVARMOURY = 1u << 5,
STRUCTURE_MARINE_ARMSLAB = 1u << 6,
STRUCTURE_MARINE_PROTOTYPELAB = 1u << 7,
STRUCTURE_MARINE_OBSERVATORY = 1u << 8,
STRUCTURE_MARINE_PHASEGATE = 1u << 9,
STRUCTURE_MARINE_TURRET = 1u << 10,
STRUCTURE_MARINE_SIEGETURRET = 1u << 11,
STRUCTURE_MARINE_COMMCHAIR = 1u << 12,
STRUCTURE_MARINE_DEPLOYEDMINE = 1u << 13,
STRUCTURE_ALIEN_HIVE = 1u << 14,
STRUCTURE_ALIEN_RESTOWER = 1u << 15,
STRUCTURE_ALIEN_DEFENCECHAMBER = 1u << 16,
STRUCTURE_ALIEN_SENSORYCHAMBER = 1u << 17,
STRUCTURE_ALIEN_MOVEMENTCHAMBER = 1u << 18,
STRUCTURE_ALIEN_OFFENCECHAMBER = 1u << 19,
SEARCH_ALL_MARINE_STRUCTURES = 0x3FFF,
SEARCH_ALL_ALIEN_STRUCTURES = 0xFC000,
SEARCH_ANY_RES_TOWER = (STRUCTURE_MARINE_RESTOWER | STRUCTURE_ALIEN_RESTOWER),
SEARCH_ALL_STRUCTURES = -1
} AvHAIDeployableStructureType;
typedef enum
{
DEPLOYABLE_ITEM_NONE = 0,
DEPLOYABLE_ITEM_RESUPPLY = 1u, // For combat mode
DEPLOYABLE_ITEM_HEAVYARMOUR = 1u << 1,
DEPLOYABLE_ITEM_JETPACK = 1u << 2,
DEPLOYABLE_ITEM_CATALYSTS = 1u << 3,
DEPLOYABLE_ITEM_SCAN = 1u << 4,
DEPLOYABLE_ITEM_HEALTHPACK = 1u << 5,
DEPLOYABLE_ITEM_AMMO = 1u << 6,
DEPLOYABLE_ITEM_MINES = 1u << 7,
DEPLOYABLE_ITEM_WELDER = 1u << 8,
DEPLOYABLE_ITEM_SHOTGUN = 1u << 9,
DEPLOYABLE_ITEM_HMG = 1u << 10,
DEPLOYABLE_ITEM_GRENADELAUNCHER = 1u << 11,
DEPLOYABLE_ITEM_WEAPONS = 0xF80,
DEPLOYABLE_ITEM_EQUIPMENT = 0x6,
DEPLOYABLE_ITEM_ALL = -1
} AvHAIDeployableItemType;
// Type of goal the commander wants to achieve
typedef enum _STRUCTUREPURPOSE
{
STRUCTURE_PURPOSE_NONE = 0,
STRUCTURE_PURPOSE_SIEGE,
STRUCTURE_PURPOSE_FORTIFY
} StructurePurpose;
typedef enum _AVHAICOMMANDERMODE
{
COMMANDERMODE_DISABLED, // AI Commander not allowed
COMMANDERMODE_IFNOHUMAN, // AI Commander only allowed if no humans are on the marine team
COMMANDERMODE_ENABLED // AI Commander allowed if no human takes charge (following grace period)
} AvHAICommanderMode;
// Bot's role on the team. For marines, this only governs what they do when left to their own devices.
// Marine bots will always listen to orders from the commander regardless of role.
typedef enum _AVHAIBOTROLE
{
BOT_ROLE_NONE, // No defined role
// General Roles
BOT_ROLE_FIND_RESOURCES, // Will hunt for uncapped resource nodes and cap them. Will attack enemy resource towers
BOT_ROLE_SWEEPER, // Defensive role to protect infrastructure and build at base. Will patrol to keep outposts secure
BOT_ROLE_ASSAULT, // Will go to attack the enemy base. In combat mode, used for Fade-focus aliens
// Marine-only Roles
BOT_ROLE_COMMAND, // Will attempt to take command
BOT_ROLE_BOMBARDIER, // Bot is armed with a GL and wants to wreck your shit. In combat mode, used for Onos-focus aliens
// Alien-only roles
BOT_ROLE_BUILDER, // Will focus on building chambers and hives. Stays gorge most of the time
BOT_ROLE_HARASS // Focuses on taking down enemy resource nodes and hunting the enemy
} AvHAIBotRole;
typedef enum _AVHAICOMBATSTRATEGY
{
COMBAT_STRATEGY_IGNORE = 0, // Don't engage this enemy
COMBAT_STRATEGY_AMBUSH, // Set up an ambush for this enemy
COMBAT_STRATEGY_RETREAT, // Retreat and find health
COMBAT_STRATEGY_SKIRMISH, // Maintain distance, whittle down their health from range and generally be a pain the arse
COMBAT_STRATEGY_ATTACK // Attack the enemy
} AvHAICombatStrategy;
typedef enum _AVHAINAVMESHSTATUS
{
NAVMESH_STATUS_PENDING = 0, // Waiting to try loading the navmesh
NAVMESH_STATUS_FAILED, // Failed to load the navmesh
NAVMESH_STATUS_SUCCESS // Successfully loaded the navmesh
} AvHAINavMeshStatus;
typedef struct _OFF_MESH_CONN
{
unsigned int ConnectionRefs[2];
unsigned int ConnectionFlags = 0;
unsigned int DefaultConnectionFlags = 0;
Vector FromLocation = g_vecZero;
Vector ToLocation = g_vecZero;
edict_t* TargetObject = nullptr;
} AvHAIOffMeshConnection;
typedef struct _STRUCTURE_OBSTACLE
{
unsigned int NavMeshIndex = 0;
unsigned int ObstacleRef = 0;
} AvHAITempObstacle;
// Data structure used to track resource nodes in the map
typedef struct _RESOURCE_NODE
{
AvHFuncResource* ResourceEntity = nullptr; // The func_resource edict reference
edict_t* ResourceEdict = nullptr;
Vector Location = g_vecZero; // origin of the func_resource edict (not the tower itself)
bool bIsOccupied = false; // True if there is any resource tower on it
AvHTeamNumber OwningTeam = TEAM_IND; // The team that has currently capped this node (TEAM_IND if none)
edict_t* ActiveTowerEntity = nullptr; // Reference to the resource tower edict (if capped)
bool bIsBaseNode = false; // Is this a node in the marine base or active alien hive?
edict_t* ParentHive = nullptr;
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Who on team A can reach this node?
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Who on team B can reach this node?
bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated
float NextReachabilityRefreshTime = 0.0f;
} AvHAIResourceNode;
// Data structure to hold information about each hive in the map
typedef struct _HIVE_DEFINITION_T
{
AvHHive* HiveEntity = nullptr; // Hive entity reference
edict_t* HiveEdict = nullptr; // Hive edict reference
Vector Location = g_vecZero; // Origin of the hive
Vector FloorLocation = g_vecZero; // Some hives are suspended in the air, this is the floor location directly beneath it
HiveStatusType Status = HIVE_STATUS_UNBUILT; // Can be unbuilt, in progress, or fully built
AvHMessageID TechStatus = MESSAGE_NULL; // What tech (if any) is assigned to this hive right now
bool bIsUnderAttack = false; // Is the hive currently under attack? Becomes false if not taken damage for more than 10 seconds
float HealthPercent = 0.0f; // If the hive is built and active, what its health currently is
AvHAIResourceNode* HiveResNodeRef = nullptr; // Which resource node (indexes into ResourceNodes array) belongs to this hive?
unsigned int ObstacleRefs[MAX_NAV_MESHES]; // When in progress or built, will place an obstacle so bots don't try to walk through it
float NextFloorLocationCheck = 0.0f; // When should the closest navigable point to the hive be calculated? Used to delay the check after a hive is built
AvHTeamNumber OwningTeam = TEAM_IND; // Which team owns this hive currently (TEAM_IND if empty)
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Who on team A can reach this node?
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Who on team B can reach this node?
char HiveName[64];
} AvHAIHiveDefinition;
// A nav profile combines a nav mesh reference (indexed into NavMeshes) and filters to determine how a bot should find paths
typedef struct _NAV_PROFILE
{
int NavMeshIndex = -1;
dtQueryFilter Filters;
bool bFlyingProfile = false;
AvHAIReachabilityStatus ReachabilityFlag = AI_REACHABILITY_NONE;
} nav_profile;
typedef struct _LOAD_NAV_HINT
{
unsigned int id = 0;
unsigned int hintType = 0;
float position[3] = { 0.0f, 0.0f, 0.0f };
} LoadNavHint;
typedef struct _NAV_HINT
{
unsigned int hintType = 0;
Vector Position = g_vecZero;
edict_t* OccupyingBuilding = nullptr;
} NavHint;
typedef struct _DEPLOYABLE_SEARCH_FILTER
{
unsigned int DeployableTypes = SEARCH_ALL_STRUCTURES;
unsigned int IncludeStatusFlags = STRUCTURE_STATUS_NONE;
unsigned int ExcludeStatusFlags = STRUCTURE_STATUS_NONE;
unsigned int ReachabilityFlags = AI_REACHABILITY_NONE;
float MinSearchRadius = 0.0f;
float MaxSearchRadius = 0.0f;
bool bConsiderPhaseDistance = false;
AvHTeamNumber DeployableTeam = TEAM_IND;
AvHTeamNumber ReachabilityTeam = TEAM_IND;
} DeployableSearchFilter;
// Pending message a bot wants to say. Allows for a delay in sending a message to simulate typing, or prevent too many messages on the same frame
typedef struct _BOT_MSG
{
char msg[64]; // Message to send
float SendTime = 0.0f; // When the bot should send this message
bool bIsPending = false; // Represents a valid pending message
bool bIsTeamSay = false; // Is this a team-only message?
} bot_msg;
typedef struct _BOT_GUARD_INFO
{
Vector GuardLocation = g_vecZero; // What position are we guarding?
Vector GuardStandPosition = g_vecZero; // Where the bot should stand to guard position (moves around a bit)
Vector GuardPoints[8]; // All potential areas to watch that an enemy could approach from
int NumGuardPoints = 0; // How many watch areas there are for the current location
Vector GuardLookLocation = g_vecZero; // Which area are we currently watching?
float GuardStartLookTime = 0.0f; // When did we start watching the current area?
float ThisGuardLookTime = 0.0f; // How long should we watch this area for?
float ThisGuardStandTime = 0.0f; // How long should we watch this area for?
float GuardStartStandTime = 0.0f; // How long should we watch this area for?
} AvHAIGuardInfo;
// Data structure to hold information on any kind of buildable structure (hive, resource tower, chamber, marine building etc)
typedef struct _AVH_AI_BUILDABLE_STRUCTURE
{
AvHBaseBuildable* EntityRef = nullptr;
edict_t* edict = nullptr; // Reference to structure edict
Vector Location = g_vecZero; // origin of the structure edict
float healthPercent = 0.0f; // Current health of the building
float lastDamagedTime = 0.0f; // When it was last damaged by something. Used by bots to determine if still needs defending
AvHAIDeployableStructureType StructureType = STRUCTURE_NONE; // Type of structure it is (e.g. hive, comm chair, infantry portal, defence chamber etc.)
unsigned int StructureStatusFlags = STRUCTURE_STATUS_NONE;
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE;
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE;
int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the building has been removed from play
vector< AvHAITempObstacle> Obstacles;
vector<AvHAIOffMeshConnection> OffMeshConnections; // References to any off-mesh connections this structure is associated with
Vector LastSuccessfulCommanderLocation = g_vecZero; // Tracks the last commander view location where it successfully placed or selected the building
Vector LastSuccessfulCommanderAngle = g_vecZero; // Tracks the last commander input angle ("click" location) used to successfully place or select building
StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE;
bool bReachabilityMarkedDirty = false; // If true, reachability flags will be recalculated for this structure
bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; }
bool IsCompleted() { return (StructureStatusFlags & STRUCTURE_STATUS_COMPLETED); }
} AvHAIBuildableStructure;
// Any kind of pickup that has been dropped either by the commander or by a player
typedef struct _DROPPED_MARINE_ITEM
{
edict_t* edict = nullptr; // Reference to the item edict
Vector Location = g_vecZero; // Origin of the entity
AvHAIDeployableItemType ItemType = DEPLOYABLE_ITEM_NONE; // Is it a weapon, health pack, ammo pack etc?
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE;
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE;
bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated
int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the item has been removed from play
} AvHAIDroppedItem;
// How far a bot can be from a useable object when trying to interact with it. Used also for melee attacks. We make it slightly less than actual to avoid edge cases
static const float max_ai_use_reach = 55.0f;
// Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down)
static const float min_ai_use_interval = 0.5f;
// Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down)
static const float max_ai_jump_height = 62.0f;
// Affects the bot's pathfinding choices
enum BotMoveStyle
{
MOVESTYLE_NORMAL, // Most direct route to target
MOVESTYLE_AMBUSH, // Prefer wall climbing and vents
MOVESTYLE_HIDE // Prefer crouched areas like vents
};
// The list of potential task types for the bot_task structure
typedef enum
{
TASK_NONE,
TASK_GET_HEALTH,
TASK_GET_AMMO,
TASK_GET_WEAPON,
TASK_GET_EQUIPMENT,
TASK_BUILD,
TASK_ATTACK,
TASK_MOVE,
TASK_CAP_RESNODE,
TASK_DEFEND,
TASK_GUARD,
TASK_HEAL,
TASK_WELD,
TASK_RESUPPLY,
TASK_EVOLVE,
TASK_COMMAND,
TASK_USE,
TASK_TOUCH,
TASK_REINFORCE_STRUCTURE,
TASK_SECURE_HIVE,
TASK_PLACE_MINE
}
BotTaskType;
//
typedef enum
{
ATTACK_SUCCESS,
ATTACK_BLOCKED,
ATTACK_OUTOFRANGE,
ATTACK_INVALIDTARGET,
ATTACK_NOWEAPON
}
BotAttackResult;
typedef enum
{
BUILD_ATTEMPT_NONE = 0,
BUILD_ATTEMPT_PENDING,
BUILD_ATTEMPT_SUCCESS,
BUILD_ATTEMPT_FAILED
} BotBuildAttemptStatus;
typedef enum
{
MOVE_TASK_NONE = 0,
MOVE_TASK_MOVE,
MOVE_TASK_USE,
MOVE_TASK_BREAK,
MOVE_TASK_TOUCH,
MOVE_TASK_PICKUP,
MOVE_TASK_WELD
} BotMovementTaskType;
// Door type. Not currently used, future feature so bots know how to open a door
enum DoorActivationType
{
DOOR_NONE, // No type, cannot be activated (permanently open/shut)
DOOR_USE, // Door activated by using it directly
DOOR_TRIGGER,// Door activated by touching a trigger_once or trigger_multiple
DOOR_BUTTON, // Door activated by pressing a button
DOOR_WELD, // Door activated by welding something
DOOR_SHOOT, // Door activated by being shot
DOOR_BREAK // Door activated by breaking something
};
// Door type. Not currently used, future feature so bots know how to open a door
enum NavDoorType
{
DOORTYPE_DOOR, // No type, cannot be activated (permanently open/shut)
DOORTYPE_PLAT, // Door activated by using it directly
DOORTYPE_TRAIN // Door activated by touching a trigger_once or trigger_multiple
};
// Bot path node. A path will be several of these strung together to lead the bot to its destination
typedef struct _BOT_PATH_NODE
{
Vector FromLocation = g_vecZero; // Location to move from
Vector Location = g_vecZero; // Location to move to
float requiredZ = 0.0f; // If climbing a up ladder or wall, how high should they aim to get before dismounting.
unsigned int flag = 0; // Is this a ladder movement, wall climb, walk etc
unsigned char area = 0; // Is this a crouch area, normal walking area etc
unsigned int poly = 0; // The nav mesh poly this point resides on
} bot_path_node;
// Represents a bot's current understanding of an enemy player's status
typedef struct _ENEMY_STATUS
{
AvHPlayer* EnemyPlayer = nullptr;
edict_t* EnemyEdict = nullptr; // Reference to the enemy player edict
Vector LastVisibleLocation = g_vecZero; // The last point the bot saw the target
Vector LastSeenLocation = g_vecZero; // The last visibly-confirmed location of the player or tracked location (if parasited / motion tracked)
Vector LastFloorPosition = g_vecZero; // Nearest point on the floor where the enemy was (for moving towards it)
Vector LastSeenVelocity = g_vecZero; // Last visibly-confirmed movement direction of the player
Vector PendingSeenLocation = g_vecZero; // The last visibly-confirmed location of the player
Vector PendingSeenVelocity = g_vecZero; // Last visibly-confirmed movement direction of the player
Vector LastLOSPosition = g_vecZero; // The last position where the bot has LOS to the enemy
Vector LastHiddenPosition = g_vecZero; // The last position where the bot did NOT have LOS to the enemy
float LastSeenTime = 0.0f; // Last time the bot saw the player (not tracked)
float LastTrackedTime = 0.0f; // Last time the bot saw the player (tracked position)
//bool bInFOV = false; // Is the player in the bot's FOV
bool bHasLOS = false; // Does the bot have LOS to the target
bool bIsVisible = false; // Enemy is in FOV and has LOS
bool bIsAwareOfPlayer = false; // Is the bot aware of this player's presence?
float NextUpdateTime = 0.0f; // When the bot can next react to a change in target's state
float NextVelocityUpdateTime = 0.0f; // When the bot can next react to a change in target's state
float EndTrackingTime = 0.0f; // When to stop "sensing" enemy movement after losing LOS
} enemy_status;
// Tracks what orders have been given to which players
typedef struct _BOT_SKILL
{
float marine_bot_reaction_time = 0.2f; // How quickly the bot will react to seeing an enemy
float marine_bot_aim_skill = 0.5f; // How quickly the bot can lock on to an enemy
float marine_bot_motion_tracking_skill = 0.5f; // How well the bot can follow an enemy target's motion
float marine_bot_view_speed = 1.0f; // How fast a bot can spin its view to aim in a given direction
float alien_bot_reaction_time = 0.2f; // How quickly the bot will react to seeing an enemy
float alien_bot_aim_skill = 0.5f; // How quickly the bot can lock on to an enemy
float alien_bot_motion_tracking_skill = 0.5f; // How well the bot can follow an enemy target's motion
float alien_bot_view_speed = 0.5f; // How fast a bot can spin its view to aim in a given direction
} bot_skill;
typedef struct _AVH_AI_BUILD_ATTEMPT
{
AvHAIDeployableStructureType AttemptedStructureType = STRUCTURE_NONE;
Vector AttemptedLocation = g_vecZero;
int NumAttempts = 0;
BotBuildAttemptStatus BuildStatus = BUILD_ATTEMPT_NONE;
float BuildAttemptTime = 0.0f;
AvHAIBuildableStructure* LinkedStructure = nullptr;
} AvHAIBuildAttempt;
// A bot task is a goal the bot wants to perform, such as attacking a structure, placing a structure etc. NOT USED BY COMMANDER
typedef struct _AVH_AI_PLAYER_TASK
{
BotTaskType TaskType = TASK_NONE; // Task Type (e.g. build, attack, defend, heal etc)
Vector TaskLocation = g_vecZero; // Task location, if task needs one (e.g. where to place structure for TASK_BUILD)
edict_t* TaskTarget = nullptr; // Reference to a target, if task needs one (e.g. TASK_ATTACK)
edict_t* TaskSecondaryTarget = nullptr; // Secondary target, if task needs one (e.g. TASK_REINFORCE)
AvHAIDeployableStructureType StructureType = STRUCTURE_NONE; // For Gorges, what structure to build (if TASK_BUILD)
float TaskStartedTime = 0.0f; // When the bot started this task. Helps time-out if the bot gets stuck trying to complete it
bool bIssuedByCommander = false; // Was this task issued by the commander? Top priority if so
bool bTargetIsPlayer = false; // Is the TaskTarget a player?
bool bTaskIsUrgent = false; // Determines whether this task is prioritised over others if bot has multiple
bool bIsWaitingForBuildLink = false; // If true, Gorge has sent the build impulse and is waiting to see if the building materialised
float LastBuildAttemptTime = 0.0f; // When did the Gorge last try to place a structure?
int BuildAttempts = 0; // How many attempts the Gorge has tried to place it, so it doesn't keep trying forever
AvHMessageID Evolution = MESSAGE_NULL; // Used by TASK_EVOLVE to determine what to evolve into
float TaskLength = 0.0f; // If a task has gone on longer than this time, it will be considered completed
AvHAIBuildAttempt ActiveBuildInfo; // If gorge, the current status of any recent attempt to place a structure
} AvHAIPlayerTask;
typedef struct _DOOR_TRIGGER
{
CBaseEntity* Entity = nullptr;
CBaseToggle* ToggleEnt = nullptr;
edict_t* Edict = nullptr;
DoorActivationType TriggerType = DOOR_NONE;
bool bIsActivated = false;
CBaseEntity* TriggerChangeTargetRef = nullptr;
float ActivationDelay = 0.0f;
float LastActivatedTime = 0.0f;
TOGGLE_STATE LastToggleState = TS_AT_BOTTOM;
float LastNextThink = 0.0f;
float NextActivationTime = 0.0f;
} DoorTrigger;
typedef struct _AVH_AI_PLAYER_MOVE_TASK
{
BotMovementTaskType TaskType = MOVE_TASK_NONE;
Vector TaskLocation = g_vecZero;
edict_t* TaskTarget = nullptr;
DoorTrigger* TriggerToActivate = nullptr;
bool bPathGenerated = false;
} AvHAIPlayerMoveTask;
typedef struct _AVH_AI_STUCK_TRACKER
{
Vector LastBotPosition = g_vecZero;
Vector MoveDestination = g_vecZero;
float TotalStuckTime = 0.0f; // Total time the bot has spent stuck
bool bPathFollowFailed = false;
} AvHAIPlayerStuckTracker;
// Contains the bot's current navigation info, such as current path
typedef struct _NAV_STATUS
{
vector<bot_path_node> CurrentPath; // Bot's path nodes
unsigned int CurrentPathPoint = 0;
Vector TargetDestination = g_vecZero; // Desired destination
Vector ActualMoveDestination = g_vecZero; // Actual destination on nav mesh
Vector PathDestination = g_vecZero; // Where the path is currently headed to
Vector LastNavMeshCheckPosition = g_vecZero;
Vector LastNavMeshPosition = g_vecZero; // Tracks the last place the bot was on the nav mesh. Useful if accidentally straying off it
Vector LastOpenLocation = g_vecZero; // Tracks the last place the bot had enough room to move around people. Useful if in a vent and need to back up somewhere to let another player past.
int CurrentMoveType = MOVETYPE_NONE; // Tracks the edict's current movement type
unsigned int CurrentPoly = 0; // Which nav mesh poly the bot is currently on
float LastStuckCheckTime = 0.0f; // Last time the bot checked if it had successfully moved
float TotalStuckTime = 0.0f; // Total time the bot has spent stuck
float LastDistanceFromDestination = 0.0f; // How far from its destination was it last stuck check
Vector StuckCheckMoveLocation = g_vecZero; // Where is the bot trying to go that we're checking if they're stuck?
Vector UnstuckMoveLocation = g_vecZero; // If the bot is unable to find a path, blindly move here to try and fix the problem
float LandedTime = 0.0f; // When the bot last landed after a fall/jump.
float LeapAttemptedTime = 0.0f; // When the bot last attempted to leap/blink. Avoid spam that sends it flying around too fast
bool bIsJumping = false; // Is the bot in the air from a jump? Will duck so it can duck-jump
bool IsOnGround = true; // Is the bot currently on the ground, or on a ladder?
bool bHasAttemptedJump = false; // Last frame, the bot tried a jump. If the bot is still on the ground, it probably tried to jump in a vent or something
float LastFlapTime = 0.0f; // When the bot last flapped its wings (if Lerk). Prevents per-frame spam draining adrenaline
bool bShouldWalk = false; // Should the bot walk at this point?
BotMoveStyle PreviousMoveStyle = MOVESTYLE_NORMAL; // Previous desired move style (e.g. normal, ambush, hide). Will trigger new path calculations if this changes
BotMoveStyle MoveStyle = MOVESTYLE_NORMAL; // Current desired move style (e.g. normal, ambush, hide). Will trigger new path calculations if this changes
float LastPathCalcTime = 0.0f; // When the bot last calculated a path, to limit how frequently it can recalculate
float NextForceRecalc = 0.0f; // If set, then the bot will force-recalc its current path
bool bZig; // Is the bot zigging, or zagging?
float NextZigTime; // Controls how frequently they zig or zag
nav_profile NavProfile;
bool bNavProfileChanged = false;
AvHAIPlayerStuckTracker StuckInfo;
unsigned int SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.)
AvHAIPlayerMoveTask MovementTask;
} nav_status;
// Type of goal the commander wants to achieve
typedef enum _COMMANDERACTIONTYPE
{
ACTION_NONE = 0,
ACTION_UPGRADE,
ACTION_RESEARCH,
ACTION_RECYCLE,
ACTION_GIVEORDER,
ACTION_DEPLOY // Deploy a structure or item into the map
} CommanderActionType;
// Some commander actions are multi-step (e.g. click to select building, release to complete selection, input recycle command etc). Tracks where the commander is in the process
typedef enum _COMMANDERACTIONSTEP
{
ACTION_STEP_NONE = 0,
ACTION_STEP_BEGIN_SELECT, // Click mouse button down to start select
ACTION_STEP_END_SELECT, // Release mouse button to complete select
} CommanderActionStep;
// Used by the AI commander instead of bot_task. Has data specifically to handle commander-specific stuff
typedef struct _COMMANDER_ACTION
{
bool bIsActive = false;
CommanderActionType ActionType = ACTION_NONE; // What action to perform (e.g. build, recycle, drop item etc)
CommanderActionStep ActionStep = ACTION_STEP_NONE; // Used for multi-stage processes such as selecting a building, issuing recycle command etc.
AvHAIDeployableStructureType StructureToBuild = STRUCTURE_NONE; // What structure to build if build action
AvHAIDeployableItemType ItemToPlace = DEPLOYABLE_ITEM_NONE;
int NumInstances = 0;
int NumDesiredInstances = 0;
StructurePurpose ActionPurpose = STRUCTURE_PURPOSE_NONE;
Vector BuildLocation = g_vecZero; // Where to build the structure
Vector DesiredCommanderLocation = g_vecZero; // To perform this action, where does the commander's view need to be? For building, usually directly above location, but could be off to side if obstructed by geometry
Vector LastAttemptedCommanderLocation = g_vecZero; // The position of the commander's view at the last action attempt
Vector LastAttemptedCommanderAngle = g_vecZero; // The click angle of the last action attempt
int AssignedPlayer = 0; // Which player index is assigned to perform the action (e.g. build structure)? Will send orders to that player (move here, build this structure etc.)
edict_t* StructureOrItem = nullptr; // Reference the structure edict. If a structure has been successfully placed but not yet fully built, it will be referenced here
edict_t* ActionTarget = nullptr; // Mostly used for dropping health packs and ammo for players where the drop location might be moving around
bool bHasAttemptedAction = false; // Has the commander tried placing a structure or item at the build location? If so, and it didn't appear, will try to adjust view around until it works
float StructureBuildAttemptTime = 0.0f; // When the commander tried placing a structure. Commander will wait a short while to confirm if the building appeared or if it should try again
int NumActionAttempts = 0; // Commander will give up after a certain number of attempts to place structure/item
AvHMessageID ResearchId = MESSAGE_NULL; // What research to perform if research action
bool bIsAwaitingBuildLink = false; // The AI has tried placing a structure or item and is waiting to confirm it worked or not
bool bIsActionUrgent = false;
} commander_action;
typedef enum
{
ORDERPURPOSE_NONE,
ORDERPURPOSE_SECURE_HIVE,
ORDERPURPOSE_SIEGE_HIVE,
ORDERPURPOSE_SECURE_RESNODE
} AvHAIOrderPurpose;
typedef struct _AI_COMMANDER_ORDER
{
edict_t* Assignee = nullptr;
AvHAIOrderPurpose OrderPurpose = ORDERPURPOSE_NONE;
edict_t* OrderTarget = nullptr;
Vector OrderLocation = g_vecZero;
float LastReminderTime = 0.0f;
float LastPlayerDistance = 0.0f;
} ai_commander_order;
typedef struct _AI_COMMANDER_REQUEST
{
bool bNewRequest = false; // Is this a new request just come in?
edict_t* Requestor = nullptr; // Who sent the request?
AvHMessageID RequestType = MESSAGE_NULL; // What did they request?
bool bAcknowledged = false; // If we can't satisfy the request right now, have we at least acknowledged it?
bool bResponded = false; // Have we already responded to this request?
float RequestTime = 0.0f; // When the request came in
int ResponseAttempts = 0; // How many times have we tried to respond to this request?
Vector RequestLocation = g_vecZero; // Where was the request raised? Ideal drop location for stuff
} ai_commander_request;
typedef struct AVH_AI_PLAYER
{
AvHPlayer* Player = nullptr;
edict_t* Edict = nullptr;
AvHTeamNumber Team = TEAM_IND;
float ForwardMove = 0.0f;
float SideMove = 0.0f;
float UpMove = 0.0f;
int Button = 0.0f;
int Impulse = 0.0f;
byte AdjustedMsec = 0;
bool bIsPendingKill = false;
bool bIsInactive = false;
float LastUseTime = 0.0f;
float f_previous_command_time = 0.0f;
Vector desiredMovementDir = g_vecZero;
Vector CurrentLadderNormal = g_vecZero;
Vector CurrentEyePosition = g_vecZero;
Vector CurrentFloorPosition = g_vecZero;
Vector LastPosition = g_vecZero;
Vector CollisionHullBottomLocation = g_vecZero;
Vector CollisionHullTopLocation = g_vecZero;
float TimeSinceLastMovement = 0.0f;
AvHAIWeapon DesiredMoveWeapon = WEAPON_INVALID;
AvHAIWeapon DesiredCombatWeapon = WEAPON_INVALID;
enemy_status TrackedEnemies[32];
int CurrentEnemy = -1;
AvHAICombatStrategy CurrentCombatStrategy = COMBAT_STRATEGY_ATTACK;
edict_t* CurrentEnemyRef = nullptr;
vector<AvHAIBuildableStructure> DangerTurrets;
AvHAIPlayerTask* CurrentTask = nullptr; // Bot's current task they're performing
AvHAIPlayerTask PrimaryBotTask;
AvHAIPlayerTask SecondaryBotTask;
AvHAIPlayerTask WantsAndNeedsTask;
AvHAIPlayerTask CommanderTask; // Task assigned by the commander
float BotNextTaskEvaluationTime = 0.0f;
bot_skill BotSkillSettings;
char PathStatus[128]; // Debug used to help figure out what's going on with a bot's path finding
char MoveStatus[128]; // Debug used to help figure out what's going on with a bot's steering
nav_status BotNavInfo; // Bot's movement information, their current path, where in the path they are etc.
vector<ai_commander_request> ActiveRequests;
vector<ai_commander_order> ActiveOrders;
float next_commander_action_time = 0.0f;
bot_msg ChatMessages[5]; // Bot can have up to 5 chat messages pending
float LastCombatTime = 0.0f;
AvHAIGuardInfo GuardInfo;
float LastRequestTime = 0.0f; // When bot last used a voice line to request something. Prevents spam
Vector DesiredLookDirection = g_vecZero; // What view angle is the bot currently turning towards
Vector InterpolatedLookDirection = g_vecZero; // Used to smoothly interpolate the bot's view rather than snap instantly like an aimbot
edict_t* LookTarget = nullptr; // Used to work out what view angle is needed to look at the desired entity
Vector LookTargetLocation = g_vecZero; // This is the bot's current desired look target. Could be an enemy (see LookTarget), or point of interest
Vector MoveLookLocation = g_vecZero; // If the bot has to look somewhere specific for movement (e.g. up for a ladder or wall-climb), this will override LookTargetLocation so the bot doesn't get distracted and mess the move up
float LastTargetTrackUpdate = 0.0f; // Add a delay to how frequently a bot can track a target's movements
float ViewInterpolationSpeed = 0.0f; // How fast should the bot turn its view? Depends on distance to turn
float ViewInterpStartedTime = 0.0f; // Used for interpolation
float ViewUpdateRate = 0.2f; // How frequently the bot can react to new sightings of enemies etc.
float LastViewUpdateTime = 0.0f; // Used to throttle view updates based on ViewUpdateRate
Vector ViewForwardVector = g_vecZero; // Bot's current forward unit vector
Vector LastSafeLocation = g_vecZero;
AvHAIBotRole BotRole = BOT_ROLE_NONE;
int ExperiencePointsAvailable = 0; // How much experience the bot has to spend
AvHMessageID NextCombatModeUpgrade = MESSAGE_NULL;
float ThinkDelta = 0.0f; // How long since this bot last ran AIPlayerThink
float LastThinkTime = 0.0f; // When the bot last ran AIPlayerThink
float ServerUpdateDelta = 0.0f; // How long since we last called RunPlayerMove
float LastServerUpdateTime = 0.0f; // When we last called RunPlayerMove
} AvHAIPlayer;
#endif

View file

@ -0,0 +1,643 @@
#include "AvHAIHelper.h"
#include "AvHAIMath.h"
#include "AvHAIPlayerUtil.h"
#include "AvHAITactical.h"
#include "AvHAINavigation.h"
#include "AvHGamerules.h"
#include <unordered_map>
int m_spriteTexture;
std::unordered_map<const char*, std::string> LocalizedLocationsMap;
bool UTIL_CommanderTrace(const edict_t* pEdict, const Vector& start, const Vector& end)
{
TraceResult hit;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
UTIL_TraceLine(start, end, ignore_monsters, ignore_glass, IgnoreEdict, &hit);
return (hit.flFraction >= 1.0f);
}
bool UTIL_QuickTrace(const edict_t* pEdict, const Vector& start, const Vector& end, bool bAllowStartSolid)
{
TraceResult hit;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
UTIL_TraceLine(start, end, ignore_monsters, ignore_glass, IgnoreEdict, &hit);
return (hit.flFraction >= 1.0f && !hit.fAllSolid && (bAllowStartSolid || !hit.fStartSolid));
}
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, bool bAllowStartSolid)
{
int hullNum = (!FNullEnt(pEdict)) ? GetPlayerHullIndex(pEdict) : point_hull;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
TraceResult hit;
UTIL_TraceHull(start, end, ignore_monsters, hullNum, IgnoreEdict, &hit);
return (hit.flFraction >= 1.0f && !hit.fAllSolid && (bAllowStartSolid || !hit.fStartSolid));
}
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, int hullNum, bool bAllowStartSolid)
{
TraceResult hit;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
UTIL_TraceHull(start, end, ignore_monsters, hullNum, IgnoreEdict, &hit);
return (hit.flFraction >= 1.0f && !hit.fAllSolid && (bAllowStartSolid || !hit.fStartSolid));
}
edict_t* UTIL_TraceEntity(const edict_t* pEdict, const Vector& start, const Vector& end)
{
TraceResult hit;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
UTIL_TraceLine(start, end, dont_ignore_monsters, dont_ignore_glass, IgnoreEdict, &hit);
return hit.pHit;
}
Vector UTIL_GetTraceHitLocation(const Vector Start, const Vector End)
{
TraceResult hit;
UTIL_TraceHull(Start, End, ignore_monsters, point_hull, NULL, &hit);
if (hit.flFraction < 1.0f && !hit.fAllSolid)
{
return hit.vecEndPos;
}
return Start;
}
Vector UTIL_GetHullTraceHitLocation(const Vector Start, const Vector End, int HullNum)
{
TraceResult hit;
UTIL_TraceHull(Start, End, ignore_monsters, HullNum, NULL, &hit);
if (hit.flFraction < 1.0f && !hit.fAllSolid)
{
return hit.vecEndPos;
}
return Start;
}
Vector UTIL_GetGroundLocation(const Vector CheckLocation)
{
if (vIsZero(CheckLocation)) { return g_vecZero; }
TraceResult hit;
UTIL_TraceHull(CheckLocation, (CheckLocation - Vector(0.0f, 0.0f, 1000.0f)), ignore_monsters, head_hull, nullptr, &hit);
if (hit.flFraction < 1.0f)
{
return hit.vecEndPos;
}
return CheckLocation;
}
Vector UTIL_GetEntityGroundLocation(const edict_t* pEntity)
{
if (FNullEnt(pEntity)) { return g_vecZero; }
bool bIsPlayer = IsEdictPlayer(pEntity);
if (bIsPlayer)
{
if (IsPlayerOnLadder(pEntity))
{
return UTIL_GetFloorUnderEntity(pEntity);
}
if (pEntity->v.flags & FL_ONGROUND)
{
if (FNullEnt(pEntity->v.groundentity))
{
return GetPlayerBottomOfCollisionHull(pEntity);
}
if (!IsEdictPlayer(pEntity->v.groundentity) && GetDeployableObjectTypeFromEdict(pEntity->v.groundentity) == STRUCTURE_NONE)
{
return GetPlayerBottomOfCollisionHull(pEntity);
}
}
return UTIL_GetFloorUnderEntity(pEntity);
}
if (GetDeployableObjectTypeFromEdict(pEntity) == STRUCTURE_ALIEN_HIVE)
{
return UTIL_GetFloorUnderEntity(pEntity);
const AvHAIHiveDefinition* Hive = AITAC_GetHiveFromEdict(pEntity);
if (Hive)
{
return Hive->FloorLocation;
}
else
{
return UTIL_GetFloorUnderEntity(pEntity);
}
}
Vector Centre = UTIL_GetCentreOfEntity(pEntity);
Centre.z = pEntity->v.absmin.z + 1.0f;
return Centre;
}
Vector UTIL_GetCentreOfEntity(const edict_t* Entity)
{
if (!Entity) { return g_vecZero; }
return (Entity->v.absmin + (Entity->v.size * 0.5f));
}
Vector UTIL_GetFloorUnderEntity(const edict_t* Edict)
{
if (FNullEnt(Edict)) { return g_vecZero; }
TraceResult hit;
Vector EntityCentre = UTIL_GetCentreOfEntity(Edict) + Vector(0.0f, 0.0f, 1.0f);
Vector TraceEnd = (EntityCentre - Vector(0.0f, 0.0f, 1000.0f));
UTIL_TraceHull(EntityCentre, TraceEnd, ignore_monsters, head_hull, Edict->v.pContainingEntity, &hit);
if (hit.flFraction < 1.0f)
{
return (hit.vecEndPos + Vector(0.0f, 0.0f, 1.0f));
}
return Edict->v.origin;
}
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector Location, const edict_t* Entity)
{
return Vector(clampf(Location.x, Entity->v.absmin.x, Entity->v.absmax.x), clampf(Location.y, Entity->v.absmin.y, Entity->v.absmax.y), clampf(Location.z, Entity->v.absmin.z, Entity->v.absmax.z));
}
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector Location, const edict_t* Entity, const Vector EntityLocation)
{
Vector MinVec = EntityLocation - (Entity->v.size * 0.5f);
Vector MaxVec = EntityLocation + (Entity->v.size * 0.5f);
return Vector(clampf(Location.x, MinVec.x, MaxVec.x), clampf(Location.y, MinVec.y, MaxVec.y), clampf(Location.z, MinVec.z, MaxVec.z));
}
AvHAIDeployableStructureType IUSER3ToStructureType(const int inIUSER3)
{
if (inIUSER3 == AVH_USER3_COMMANDER_STATION) { return STRUCTURE_MARINE_COMMCHAIR; }
if (inIUSER3 == AVH_USER3_RESTOWER) { return STRUCTURE_MARINE_RESTOWER; }
if (inIUSER3 == AVH_USER3_INFANTRYPORTAL) { return STRUCTURE_MARINE_INFANTRYPORTAL; }
if (inIUSER3 == AVH_USER3_ARMORY) { return STRUCTURE_MARINE_ARMOURY; }
if (inIUSER3 == AVH_USER3_ADVANCED_ARMORY) { return STRUCTURE_MARINE_ADVARMOURY; }
if (inIUSER3 == AVH_USER3_TURRET_FACTORY) { return STRUCTURE_MARINE_TURRETFACTORY; }
if (inIUSER3 == AVH_USER3_ADVANCED_TURRET_FACTORY) { return STRUCTURE_MARINE_ADVTURRETFACTORY; }
if (inIUSER3 == AVH_USER3_TURRET) { return STRUCTURE_MARINE_TURRET; }
if (inIUSER3 == AVH_USER3_SIEGETURRET) { return STRUCTURE_MARINE_SIEGETURRET; }
if (inIUSER3 == AVH_USER3_ARMSLAB) { return STRUCTURE_MARINE_ARMSLAB; }
if (inIUSER3 == AVH_USER3_PROTOTYPE_LAB) { return STRUCTURE_MARINE_PROTOTYPELAB; }
if (inIUSER3 == AVH_USER3_OBSERVATORY) { return STRUCTURE_MARINE_OBSERVATORY; }
if (inIUSER3 == AVH_USER3_PHASEGATE) { return STRUCTURE_MARINE_PHASEGATE; }
if (inIUSER3 == AVH_USER3_MINE) { return STRUCTURE_MARINE_DEPLOYEDMINE; }
if (inIUSER3 == AVH_USER3_HIVE) { return STRUCTURE_ALIEN_HIVE; }
if (inIUSER3 == AVH_USER3_ALIENRESTOWER) { return STRUCTURE_ALIEN_RESTOWER; }
if (inIUSER3 == AVH_USER3_DEFENSE_CHAMBER) { return STRUCTURE_ALIEN_DEFENCECHAMBER; }
if (inIUSER3 == AVH_USER3_SENSORY_CHAMBER) { return STRUCTURE_ALIEN_SENSORYCHAMBER; }
if (inIUSER3 == AVH_USER3_MOVEMENT_CHAMBER) { return STRUCTURE_ALIEN_MOVEMENTCHAMBER; }
if (inIUSER3 == AVH_USER3_OFFENSE_CHAMBER) { return STRUCTURE_ALIEN_OFFENCECHAMBER; }
return STRUCTURE_NONE;
}
AvHAIDeployableStructureType GetDeployableObjectTypeFromEdict(const edict_t* StructureEdict)
{
if (FNullEnt(StructureEdict)) { return STRUCTURE_NONE; }
return IUSER3ToStructureType(StructureEdict->v.iuser3);
}
bool IsEdictStructure(const edict_t* edict)
{
return (GetDeployableObjectTypeFromEdict(edict) != STRUCTURE_NONE);
}
bool IsEdictHive(const edict_t* edict)
{
if (FNullEnt(edict)) { return false; }
return (edict->v.iuser3 == AVH_USER3_HIVE);
}
bool IsDamagingStructure(const edict_t* StructureEdict)
{
return IsDamagingStructure(GetStructureTypeFromEdict(StructureEdict));
}
bool IsDamagingStructure(AvHAIDeployableStructureType StructureType)
{
switch (StructureType)
{
case STRUCTURE_ALIEN_OFFENCECHAMBER:
case STRUCTURE_MARINE_TURRET:
return true;
default:
return false;
}
return false;
}
AvHAIDeployableStructureType GetStructureTypeFromEdict(const edict_t* StructureEdict)
{
if (FNullEnt(StructureEdict)) { return STRUCTURE_NONE; }
return IUSER3ToStructureType(StructureEdict->v.iuser3);
}
bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation)
{
bool theSuccess = false;
const AvHBaseInfoLocationListType& inLocations = GetGameRules()->GetInfoLocations();
bool bFoundNearest = false;
float MinDist = 0.0f;
// Look at our current position, and see if we lie within of the map locations
for (AvHBaseInfoLocationListType::const_iterator theIter = inLocations.begin(); theIter != inLocations.end(); theIter++)
{
if (theIter->GetIsPointInRegion(SearchLocation))
{
outLocation = theIter->GetLocationName();
return true;
}
float NearestX = clampf(SearchLocation.x, theIter->GetMinExtent().x, theIter->GetMaxExtent().x);
float NearestY = clampf(SearchLocation.y, theIter->GetMinExtent().y, theIter->GetMaxExtent().y);
float ThisDist = vDist2DSq(SearchLocation, Vector(NearestX, NearestY, 0.0f));
if (!bFoundNearest || ThisDist < MinDist)
{
outLocation = theIter->GetLocationName();
bFoundNearest = true;
theSuccess = true;
MinDist = ThisDist;
}
}
return theSuccess;
}
void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot, float DrawTime)
{
AIDEBUG_DrawPath(pBot->BotNavInfo.CurrentPath, DrawTime);
}
void AIDEBUG_DrawPath(vector<bot_path_node>& path, float DrawTime)
{
if (path.size() == 0) { return; }
for (auto it = path.begin(); it != path.end(); it++)
{
Vector FromLoc = it->FromLocation;
Vector ToLoc = it->Location;
switch (it->flag)
{
case SAMPLE_POLYFLAGS_WELD:
case SAMPLE_POLYFLAGS_DOOR:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 255, 0, 0);
break;
case SAMPLE_POLYFLAGS_JUMP:
case SAMPLE_POLYFLAGS_DUCKJUMP:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 255, 255, 0);
break;
case SAMPLE_POLYFLAGS_LADDER:
case SAMPLE_POLYFLAGS_LIFT:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 0, 0, 255);
break;
case SAMPLE_POLYFLAGS_WALLCLIMB:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 0, 128, 0);
break;
case SAMPLE_POLYFLAGS_BLOCKED:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 128, 128, 128);
break;
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime, 255, 128, 128);
break;
default:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, DrawTime);
break;
}
}
}
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
WRITE_COORD(start.z);
WRITE_COORD(end.x);
WRITE_COORD(end.y);
WRITE_COORD(end.z);
WRITE_SHORT(m_spriteTexture);
WRITE_BYTE(1); // framestart
WRITE_BYTE(10); // framerate
WRITE_BYTE(1); // life in 0.1's
WRITE_BYTE(5); // width
WRITE_BYTE(0); // noise
WRITE_BYTE(255); // r, g, b
WRITE_BYTE(255); // r, g, b
WRITE_BYTE(255); // r, g, b
WRITE_BYTE(250); // brightness
WRITE_BYTE(5); // speed
MESSAGE_END();
}
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
int timeTenthSeconds = (int)floorf(drawTimeSeconds * 10.0f);
timeTenthSeconds = fmaxf(timeTenthSeconds, 1);
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
WRITE_COORD(start.z);
WRITE_COORD(end.x);
WRITE_COORD(end.y);
WRITE_COORD(end.z);
WRITE_SHORT(m_spriteTexture);
WRITE_BYTE(1); // framestart
WRITE_BYTE(10); // framerate
WRITE_BYTE(timeTenthSeconds); // life in 0.1's
WRITE_BYTE(5); // width
WRITE_BYTE(0); // noise
WRITE_BYTE(255); // r, g, b
WRITE_BYTE(255); // r, g, b
WRITE_BYTE(255); // r, g, b
WRITE_BYTE(250); // brightness
WRITE_BYTE(5); // speed
MESSAGE_END();
}
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds, int r, int g, int b)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
int timeTenthSeconds = (int)floorf(drawTimeSeconds * 10.0f);
timeTenthSeconds = fmaxf(timeTenthSeconds, 1);
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
WRITE_COORD(start.z);
WRITE_COORD(end.x);
WRITE_COORD(end.y);
WRITE_COORD(end.z);
WRITE_SHORT(m_spriteTexture);
WRITE_BYTE(1); // framestart
WRITE_BYTE(10); // framerate
WRITE_BYTE(timeTenthSeconds); // life in 0.1's
WRITE_BYTE(5); // width
WRITE_BYTE(0); // noise
WRITE_BYTE(r); // r, g, b
WRITE_BYTE(g); // r, g, b
WRITE_BYTE(b); // r, g, b
WRITE_BYTE(250); // brightness
WRITE_BYTE(5); // speed
MESSAGE_END();
}
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int b)
{
if (FNullEnt(pEntity) || pEntity->free) { return; }
MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_BEAMPOINTS);
WRITE_COORD(start.x);
WRITE_COORD(start.y);
WRITE_COORD(start.z);
WRITE_COORD(end.x);
WRITE_COORD(end.y);
WRITE_COORD(end.z);
WRITE_SHORT(m_spriteTexture);
WRITE_BYTE(1); // framestart
WRITE_BYTE(10); // framerate
WRITE_BYTE(1); // life in 0.1's
WRITE_BYTE(5); // width
WRITE_BYTE(0); // noise
WRITE_BYTE(r); // r, g, b
WRITE_BYTE(g); // r, g, b
WRITE_BYTE(b); // r, g, b
WRITE_BYTE(250); // brightness
WRITE_BYTE(5); // speed
MESSAGE_END();
}
void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds)
{
Vector LowerBottomLeftCorner = bMin;
Vector LowerTopLeftCorner = Vector(bMin.x, bMax.y, bMin.z);
Vector LowerTopRightCorner = Vector(bMax.x, bMax.y, bMin.z);
Vector LowerBottomRightCorner = Vector(bMax.x, bMin.y, bMin.z);
Vector UpperBottomLeftCorner = Vector(bMin.x, bMin.y, bMax.z);
Vector UpperTopLeftCorner = Vector(bMin.x, bMax.y, bMax.z);
Vector UpperTopRightCorner = Vector(bMax.x, bMax.y, bMax.z);
Vector UpperBottomRightCorner = Vector(bMax.x, bMin.y, bMax.z);
UTIL_DrawLine(pEntity, LowerTopLeftCorner, LowerTopRightCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, LowerTopRightCorner, LowerBottomRightCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, LowerBottomRightCorner, LowerBottomLeftCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, UpperBottomLeftCorner, UpperTopLeftCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, UpperTopLeftCorner, UpperTopRightCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, UpperTopRightCorner, UpperBottomRightCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, UpperBottomRightCorner, UpperBottomLeftCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, LowerBottomLeftCorner, UpperBottomLeftCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, LowerTopLeftCorner, UpperTopLeftCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, LowerTopRightCorner, UpperTopRightCorner, drawTimeSeconds, 255, 255, 255);
UTIL_DrawLine(pEntity, LowerBottomRightCorner, UpperBottomRightCorner, drawTimeSeconds, 255, 255, 255);
}
void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds, int r, int g, int b)
{
Vector LowerBottomLeftCorner = bMin;
Vector LowerTopLeftCorner = Vector(bMin.x, bMax.y, bMin.z);
Vector LowerTopRightCorner = Vector(bMax.x, bMax.y, bMin.z);
Vector LowerBottomRightCorner = Vector(bMax.x, bMin.y, bMin.z);
Vector UpperBottomLeftCorner = Vector(bMin.x, bMin.y, bMax.z);
Vector UpperTopLeftCorner = Vector(bMin.x, bMax.y, bMax.z);
Vector UpperTopRightCorner = Vector(bMax.x, bMax.y, bMax.z);
Vector UpperBottomRightCorner = Vector(bMax.x, bMin.y, bMax.z);
UTIL_DrawLine(pEntity, LowerTopLeftCorner, LowerTopRightCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, LowerTopRightCorner, LowerBottomRightCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, LowerBottomRightCorner, LowerBottomLeftCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, UpperBottomLeftCorner, UpperTopLeftCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, UpperTopLeftCorner, UpperTopRightCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, UpperTopRightCorner, UpperBottomRightCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, UpperBottomRightCorner, UpperBottomLeftCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, LowerBottomLeftCorner, UpperBottomLeftCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, LowerTopLeftCorner, UpperTopLeftCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, LowerTopRightCorner, UpperTopRightCorner, drawTimeSeconds, r, g, b);
UTIL_DrawLine(pEntity, LowerBottomRightCorner, UpperBottomRightCorner, drawTimeSeconds, r, g, b);
}
void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string)
{
// higher level wrapper for hudtextparms TE_TEXTMESSAGEs. This function is meant to be called
// every frame, since the duration of the display is roughly worth the duration of a video
// frame. The X and Y coordinates are unary fractions which are bound to this rule:
// 0: top of the screen (Y) or left of the screen (X), left aligned text
// 1: bottom of the screen (Y) or right of the screen (X), right aligned text
// -1(only one negative value possible): center of the screen (X and Y), centered text
// Any value ranging from 0 to 1 will represent a valid position on the screen.
//static short duration;
if (FNullEnt(pEntity)) { return; }
//duration = (int)GAME_GetServerMSecVal() * 256 / 750; // compute text message duration
//if (duration < 5)
// duration = 5;
MESSAGE_BEGIN(MSG_ONE_UNRELIABLE, SVC_TEMPENTITY, NULL, pEntity);
WRITE_BYTE(TE_TEXTMESSAGE);
WRITE_BYTE(channel); // channel
WRITE_SHORT((int)(x * 8192.0f)); // x coordinates * 8192
WRITE_SHORT((int)(y * 8192.0f)); // y coordinates * 8192
WRITE_BYTE(0); // effect (fade in/out)
WRITE_BYTE(r); // initial RED
WRITE_BYTE(g); // initial GREEN
WRITE_BYTE(b); // initial BLUE
WRITE_BYTE(1); // initial ALPHA
WRITE_BYTE(r); // effect RED
WRITE_BYTE(g); // effect GREEN
WRITE_BYTE(b); // effect BLUE
WRITE_BYTE(1); // effect ALPHA
WRITE_SHORT(0); // fade-in time in seconds * 256
WRITE_SHORT(0); // fade-out time in seconds * 256
WRITE_SHORT(5); // hold time in seconds * 256
WRITE_STRING(string);//string); // send the string
MESSAGE_END(); // end
return;
}
void UTIL_ClearLocalizations()
{
LocalizedLocationsMap.clear();
}
void UTIL_LocalizeText(const char* InputText, string& OutputText)
{
// Don't localize empty strings
if (!strcmp(InputText, ""))
{
OutputText = "";
}
char theInputString[1024];
sprintf(theInputString, "%s", InputText);
std::unordered_map<const char*, std::string>::const_iterator FoundLocalization = LocalizedLocationsMap.find(theInputString);
if (FoundLocalization != LocalizedLocationsMap.end())
{
OutputText = FoundLocalization->second;
return;
}
char filename[256];
std::string localizedString(theInputString);
string titlesPath = string(getModDirectory()) + "/titles.txt";
strcpy(filename, titlesPath.c_str());
std::ifstream cFile(filename);
if (cFile.is_open())
{
std::string line;
while (getline(cFile, line))
{
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
line.end());
if (line[0] == '/' || line.empty())
continue;
if (line.compare(theInputString) == 0)
{
getline(cFile, line);
getline(cFile, localizedString);
break;
}
}
}
char theOutputString[1024];
sprintf(theOutputString, "%s", localizedString.c_str());
string Delimiter = "Hive -";
auto delimiterPos = localizedString.find(Delimiter);
if (delimiterPos == std::string::npos)
{
Delimiter = "Hive Location -";
delimiterPos = localizedString.find(Delimiter);
}
if (delimiterPos == std::string::npos)
{
Delimiter = "Hive Location -";
delimiterPos = localizedString.find("Hive Location -");
}
if (delimiterPos != std::string::npos)
{
auto AreaName = localizedString.substr(delimiterPos + Delimiter.length());
AreaName.erase(0, AreaName.find_first_not_of(" \r\n\t\v\f"));
sprintf(theOutputString, "%s", AreaName.c_str());
}
OutputText = theOutputString;
LocalizedLocationsMap[InputText] = OutputText;
}

View file

@ -0,0 +1,61 @@
#pragma once
#ifndef AVH_AI_HELPER_H
#define AVH_AI_HELPER_H
#include "AvHPlayer.h"
#include "AvHAIConstants.h"
bool UTIL_CommanderTrace(const edict_t* pEdict, const Vector& start, const Vector& end);
bool UTIL_QuickTrace(const edict_t* pEdict, const Vector& start, const Vector& end, bool bAllowStartSolid = false);
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, bool bAllowStartSolid = false);
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, int hullNum, bool bAllowStartSolid = false);
edict_t* UTIL_TraceEntity(const edict_t* pEdict, const Vector& start, const Vector& end);
Vector UTIL_GetTraceHitLocation(const Vector Start, const Vector End);
Vector UTIL_GetHullTraceHitLocation(const Vector Start, const Vector End, int HullNum);
Vector UTIL_GetGroundLocation(const Vector CheckLocation);
Vector UTIL_GetEntityGroundLocation(const edict_t* pEntity);
Vector UTIL_GetCentreOfEntity(const edict_t* Entity);
Vector UTIL_GetFloorUnderEntity(const edict_t* Edict);
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector UserLocation, const edict_t* Entity);
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector Location, const edict_t* Entity, const Vector EntityLocation);
AvHAIDeployableStructureType IUSER3ToStructureType(const int inIUSER3);
bool IsEdictStructure(const edict_t* edict);
bool IsEdictHive(const edict_t* edict);
AvHAIDeployableStructureType GetStructureTypeFromEdict(const edict_t* StructureEdict);
// Returns true if this structure shoots back (turret or offence chamber)
bool IsDamagingStructure(const edict_t* StructureEdict);
// Returns true if this structure shoots back (turret or offence chamber)
bool IsDamagingStructure(AvHAIDeployableStructureType StructureType);
bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation);
AvHAIDeployableStructureType GetDeployableObjectTypeFromEdict(const edict_t* StructureEdict);
void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot, float DrawTime = 0.0f);
void AIDEBUG_DrawPath(vector<bot_path_node>& path, float DrawTime = 0.0f);
// Draws a white line between start and end for the given player (pEntity) for 0.1s
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end);
// Draws a white line between start and end for the given player (pEntity) for given number of seconds
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds);
// Draws a coloured line using RGB input, between start and end for the given player (pEntity) for 0.1s
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, int r, int g, int b);
// Draws a coloured line using RGB input, between start and end for the given player (pEntity) for given number of seconds
void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end, float drawTimeSeconds, int r, int g, int b);
void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds);
void UTIL_DrawBox(edict_t* pEntity, Vector bMin, Vector bMax, float drawTimeSeconds, int r, int g, int b);
void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned char r, unsigned char g, unsigned char b, const char* string);
void UTIL_ClearLocalizations();
void UTIL_LocalizeText(const char* InputText, string& OutputText);
#endif

View file

@ -0,0 +1,813 @@
//
// EvoBot - Neoptolemus' Natural Selection bot, based on Botman's HPB bot template
//
// bot_math.cpp
//
// Contains all useful math functions for bot stuff
//
#include "AvHAIMath.h"
#include "../dlls/util.h"
#include <time.h>
#include <string>
#include <sstream>
extern enginefuncs_t g_engfuncs;
bool isNumber(const char* line)
{
char* p;
strtol(line, &p, 10);
return *p == 0;
}
bool isFloat(const char* line)
{
std::string myString = line;
std::istringstream iss(myString);
float f;
iss >> std::noskipws >> f; // noskipws considers leading whitespace invalid
// Check the entire string was consumed and if either failbit or badbit is set
return iss.eof() && !iss.fail();
}
// Returns the normalized surface normal of a triangle defined by v1,v2,v3. Assumes clockwise indices.
Vector UTIL_GetSurfaceNormal(const Vector v1, const Vector v2, const Vector v3)
{
Vector normal(((v2.y - v1.y) * (v3.z - v1.z)) - ((v2.z - v1.z) * (v3.y - v1.y)),
((v2.z - v1.z) * (v3.x - v1.x)) - ((v2.x - v1.x) * (v3.z - v1.z)),
((v2.x - v1.x) * (v3.y - v1.y)) - ((v2.y - v1.y) * (v3.x - v1.x)));
UTIL_NormalizeVector(&normal);
return normal;
}
bool vPointOverlaps3D(const Vector Point, const Vector MinBB, const Vector MaxBB)
{
return (Point.x >= MinBB.x && Point.x <= MaxBB.x
&& Point.y >= MinBB.y && Point.y <= MaxBB.y
&& Point.z >= MinBB.z && Point.z <= MaxBB.z);
}
bool vPointOverlaps2D(const Vector Point, const Vector MinBB, const Vector MaxBB)
{
return (Point.x >= MinBB.x && Point.x <= MaxBB.x
&& Point.y >= MinBB.y && Point.y <= MaxBB.y);
}
bool vBBOverlaps2D(const Vector MinBBA, const Vector MaxBBA, const Vector MinBBB, const Vector MaxBBB)
{
return ( (MinBBA.x < MaxBBB.x && MaxBBA.x > MinBBB.x)
&& (MinBBA.y < MaxBBB.y && MaxBBA.y > MinBBB.y));
}
// Given three collinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Vector p, Vector q, Vector r)
{
if (q.x <= fmaxf(p.x, r.x) && q.x >= fminf(p.x, r.x) &&
q.y <= fmaxf(p.y, r.y) && q.y >= fminf(p.y, r.y))
return true;
return false;
}
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are collinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Vector p, Vector q, Vector r)
{
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for details of below formula.
int val = (q.y - p.y) * (r.x - q.x) -
(q.x - p.x) * (r.y - q.y);
if (val == 0) return 0; // collinear
return (val > 0) ? 1 : 2; // clock or counterclock wise
}
bool vIntersects2D(const Vector LineAStart, const Vector LineAEnd, const Vector LineBStart, const Vector LineBEnd)
{
int o1 = orientation(LineAStart, LineAEnd, LineBStart);
int o2 = orientation(LineAStart, LineAEnd, LineBEnd);
int o3 = orientation(LineBStart, LineBEnd, LineAStart);
int o4 = orientation(LineBStart, LineBEnd, LineAEnd);
// General case
if (o1 != o2 && o3 != o4)
return true;
// Special Cases
// p1, q1 and p2 are collinear and p2 lies on segment p1q1
if (o1 == 0 && onSegment(LineAStart, LineBStart, LineAEnd)) return true;
// p1, q1 and q2 are collinear and q2 lies on segment p1q1
if (o2 == 0 && onSegment(LineAStart, LineBEnd, LineAEnd)) return true;
// p2, q2 and p1 are collinear and p1 lies on segment p2q2
if (o3 == 0 && onSegment(LineBStart, LineAStart, LineBEnd)) return true;
// p2, q2 and q1 are collinear and q1 lies on segment p2q2
if (o4 == 0 && onSegment(LineBStart, LineAEnd, LineBEnd)) return true;
return false; // Doesn't fall in any of the above cases
}
Vector vClosestPointOnBB(const Vector Point, const Vector MinBB, const Vector MaxBB)
{
return Vector(clampf(Point.x, MinBB.x, MaxBB.x), clampf(Point.y, MinBB.y, MaxBB.y), clampf(Point.z, MinBB.z, MaxBB.z));
}
void vScaleBB(Vector& MinBB, Vector& MaxBB, const float Scale)
{
Vector Centre = MinBB + ((MaxBB - MinBB) * 0.5f);
float SizeX = MaxBB.x - MinBB.x;
float SizeY = MaxBB.y - MinBB.y;
float SizeZ = MaxBB.z - MinBB.z;
MinBB.x = Centre.x - (SizeX * Scale);
MinBB.y = Centre.y - (SizeY * Scale);
MinBB.z = Centre.z - (SizeZ * Scale);
MaxBB.x = Centre.x + (SizeX * Scale);
MaxBB.y = Centre.y + (SizeY * Scale);
MaxBB.z = Centre.z + (SizeZ * Scale);
}
// Returns the 3D distance of point from a line defined between lineFrom and lineTo
float vDistanceFromLine3D(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint)
{
Vector nearestToLine = vClosestPointOnLine(lineFrom, lineTo, CheckPoint);
return vDist3D(CheckPoint, nearestToLine);
}
// Returns the 2D distance (Z axis ignored) of point from a line defined between lineFrom and lineTo
float vDistanceFromLine2D(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint)
{
Vector nearestToLine = vClosestPointOnLine2D(lineFrom, lineTo, CheckPoint);
return vDist2D(CheckPoint, nearestToLine);
}
// Returns the 2D distance (Z axis ignored) of point from a line defined between lineFrom and lineTo
float vDistanceFromLine2DSq(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint)
{
Vector nearestToLine = vClosestPointOnLine2D(lineFrom, lineTo, CheckPoint);
return vDist2DSq(CheckPoint, nearestToLine);
}
Vector vClosestPointOnInfiniteLine3D(const Vector PointOnLine, const Vector NormalisedLineDir, const Vector TestPoint)
{
Vector DirectionToPoint = UTIL_GetVectorNormal(PointOnLine - TestPoint);
float DirectionDot = UTIL_GetDotProduct(DirectionToPoint, NormalisedLineDir);
return TestPoint + (NormalisedLineDir * DirectionDot);
}
Vector vClosestPointOnInfiniteLine2D(const Vector PointOnLine, const Vector NormalisedLineDir, const Vector TestPoint)
{
Vector NormalisedLineDir2D = UTIL_GetVectorNormal2D(NormalisedLineDir);
Vector DirectionToPoint = UTIL_GetVectorNormal2D(PointOnLine - TestPoint);
float DirectionDot = UTIL_GetDotProduct2D(DirectionToPoint, NormalisedLineDir2D);
return TestPoint + (NormalisedLineDir * DirectionDot);
}
// Returns 0 if point sits right on the line defined by lineFrom and lineTo, -1 if it sits to the left, 1 if it sits to the right. Ignores Z axis
int vPointOnLine(const Vector lineFrom, const Vector lineTo, const Vector point)
{
float value = ((lineTo.x - lineFrom.x) * (point.y - lineFrom.y)) - ((point.x - lineFrom.x) * (lineTo.y - lineFrom.y));
if (value > 1.0f)
return 1;
if (value < -1.0f)
return -1;
return 0;
}
// For given line lineFrom -> lineTo, returns the point along that line closest to point
Vector vClosestPointOnLine(const Vector lineFrom, const Vector lineTo, const Vector point)
{
Vector vVector1 = point - lineFrom;
Vector vVector2 = UTIL_GetVectorNormal(lineTo - lineFrom);
float d = vDist3D(lineFrom, lineTo);
float t = UTIL_GetDotProduct(vVector2, vVector1);
if (t <= 0)
return lineFrom;
if (t >= d)
return lineTo;
Vector vVector3 = vVector2 * t;
Vector vClosestPoint = lineFrom + vVector3;
return vClosestPoint;
}
// For given line lineFrom -> lineTo, returns the 2D point (Z axis ignored) along that line closest to point
Vector vClosestPointOnLine2D(const Vector lineFrom, const Vector lineTo, const Vector point)
{
Vector lineFrom2D = Vector(lineFrom.x, lineFrom.y, 0.0f);
Vector lineTo2D = Vector(lineTo.x, lineTo.y, 0.0f);
Vector point2D = Vector(point.x, point.y, 0.0f);
Vector vVector1 = point2D - lineFrom2D;
Vector vVector2 = UTIL_GetVectorNormal2D(lineTo2D - lineFrom2D);
float d = vDist2D(lineFrom2D, lineTo2D);
float t = UTIL_GetDotProduct2D(vVector2, vVector1);
if (t <= 0)
return lineFrom2D;
if (t >= d)
return lineTo2D;
Vector vVector3 = vVector2 * t;
Vector vClosestPoint = lineFrom2D + vVector3;
return vClosestPoint;
}
// Normalizes the supplied vector, overwriting it with the normalized value in the process
void UTIL_NormalizeVector(Vector* vec)
{
float len = sqrt((vec->x * vec->x) + (vec->y * vec->y) + (vec->z * vec->z));
float div = 1.0f / len;
vec->x *= div;
vec->y *= div;
vec->z *= div;
}
void UTIL_NormalizeVector2D(Vector* vec)
{
float len = sqrt((vec->x * vec->x) + (vec->y * vec->y));
float div = 1.0f / len;
vec->x *= div;
vec->y *= div;
vec->z = 0.0f;
}
// Returns a normalized copy of the supplied Vector. Original value is unmodified
Vector UTIL_GetVectorNormal(const Vector vec)
{
return vec.Normalize();
}
// Returns a 2D (Z axis is 0) normalized copy of the supplied Vector. Original value is unmodified
Vector UTIL_GetVectorNormal2D(const Vector vec)
{
if (vec.x == 0.0f && vec.y == 0.0f) { return ZERO_VECTOR; }
Vector result;
float len = sqrt((vec.x * vec.x) + (vec.y * vec.y));
float div = 1.0f / len;
result.x = vec.x * div;
result.y = vec.y * div;
result.z = 0.0f;
return result;
}
// Returns the cross product of v1 and v2.
Vector UTIL_GetCrossProduct(const Vector v1, const Vector v2)
{
Vector result;
result.x = v1.y * v2.z - v1.z * v2.y;
result.y = v1.z * v2.x - v1.x * v2.z;
result.z = v1.x * v2.y - v1.y * v2.x;
return result;
}
// Returns the 2D (ignoring Z axis) distance between the two vectors
float vDist2D(const Vector v1, const Vector v2)
{
return (float)sqrt((v2.x - v1.x) * (v2.x - v1.x)
+ (v2.y - v1.y) * (v2.y - v1.y));
}
// Returns the 3D distance between the two vectors
float vDist3D(const Vector v1, const Vector v2)
{
return (float)sqrt((v2.x - v1.x) * (v2.x - v1.x)
+ (v2.y - v1.y) * (v2.y - v1.y)
+ (v2.z - v1.z) * (v2.z - v1.z));
}
// Returns the 2D (ignoring Z axis) squared distance between the two vectors
float vDist2DSq(const Vector v1, const Vector v2)
{
return ((v2.x - v1.x) * (v2.x - v1.x)
+ (v2.y - v1.y) * (v2.y - v1.y));
}
// Returns the 3D squared distance between the two vectors
float vDist3DSq(const Vector v1, const Vector v2)
{
return ((v2.x - v1.x) * (v2.x - v1.x)
+ (v2.y - v1.y) * (v2.y - v1.y)
+ (v2.z - v1.z) * (v2.z - v1.z));
}
float vSize3DSq(const Vector V)
{
return (V.x * V.x) + (V.y * V.y) + (V.z * V.z);
}
float vSize2DSq(const Vector V)
{
return (V.x * V.x) + (V.y * V.y);
}
float vSize3D(const Vector V)
{
return sqrtf((V.x * V.x) + (V.y * V.y) + (V.z * V.z));
}
float vSize2D(const Vector V)
{
return sqrtf((V.x * V.x) + (V.y * V.y));
}
// Returns true if the two vectors are the same (all components are within 0.0001f of each other)
bool vEquals(const Vector v1, const Vector v2)
{
return fabsf(v1.x - v2.x) <= 0.0001f && fabsf(v1.y - v2.y) <= 0.0001f && fabsf(v1.z - v2.z) <= 0.0001f;
}
bool vEquals2D(const Vector v1, const Vector v2)
{
return fabsf(v1.x - v2.x) <= 0.01f && fabsf(v1.y - v2.y) <= 0.01f;
}
// Returns true if the two vectors are the same (all components are within epsilon of each other)
bool vEquals(const Vector v1, const Vector v2, const float epsilon)
{
return fabsf(v1.x - v2.x) <= epsilon && fabsf(v1.y - v2.y) <= epsilon && fabsf(v1.z - v2.z) <= epsilon;
}
bool vEquals2D(const Vector v1, const Vector v2, const float epsilon)
{
return fabsf(v1.x - v2.x) <= epsilon && fabsf(v1.y - v2.y) <= epsilon;
}
bool vIsZero(const Vector v1)
{
return (fabsf(v1.x) < 0.0001f && fabsf(v1.y) < 0.0001f && fabsf(v1.z) < 0.0001f);
}
bool fNearlyEqual(const float f1, const float f2)
{
return fabsf(f1 - f2) < 0.001f;
}
// Returns the dot product of two vectors (1.0f if both vectors pointing exactly the same direction, -1.0f if opposites, 0.0f if perpendicular)
//float UTIL_GetDotProduct(const Vector v1, const Vector v2)
//{
// return ((v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z));
//}
// Returns the 2D dot product (Z axis ignored) of two vectors (1.0f if both vectors pointing exactly the same direction, -1.0f if opposites, 0.0f if perpendicular)
float UTIL_GetDotProduct2D(const Vector v1, const Vector v2)
{
return ((v1.x * v2.x) + (v1.y * v2.y));
}
// Returns a random point along the circumference of a circle defined with the supplied origin and radius
Vector UTIL_RandomPointOnCircle(const Vector origin, const float radius)
{
Vector result;
float random = ((float)rand()) / (float)RAND_MAX;
float a = random * (MATH_PI * 2);
result.x = origin.x + (radius * cos(a));
result.y = origin.y + (radius * sin(a));
result.z = origin.z;
return result;
}
// For given plane, determine if the given point sits within the plane or not
bool UTIL_PointInsidePlane(const frustum_plane_t* plane, const Vector point)
{
float distance = plane->d + (plane->normal.x * point.x + plane->normal.y * point.y + plane->normal.z * point.z);
return distance >= 0.0f;
}
bool UTIL_CylinderInsidePlane(const frustum_plane_t* plane, const Vector centre, float height, float radius)
{
Vector testNormal = plane->normal;
testNormal.z = 0;
Vector topPoint = centre + Vector(0.0f, 0.0f, height * 0.5f) + (testNormal * radius);
Vector bottomPoint = centre - Vector(0.0f, 0.0f, height * 0.5f) + (testNormal * radius);
return (UTIL_PointInsidePlane(plane, topPoint) || UTIL_PointInsidePlane(plane, bottomPoint));
}
void UTIL_SetFrustumPlane(frustum_plane_t* plane, Vector v1, Vector v2, Vector v3)
{
Vector normal = UTIL_GetSurfaceNormal(v1, v2, v3);
plane->normal.x = normal.x;
plane->normal.y = normal.y;
plane->normal.z = normal.z;
plane->point.x = v2.x;
plane->point.y = v2.y;
plane->point.z = v2.z;
plane->d = -(plane->normal.x * plane->point.x + plane->normal.y * plane->point.y + plane->normal.z * plane->point.z);
}
float UTIL_MetresToGoldSrcUnits(const float Metres)
{
return Metres * 52.4934f;
}
float UTIL_GoldSrcUnitsToMetres(const float GoldSrcUnits)
{
return GoldSrcUnits * 0.01905f;
}
float sqrf(float input)
{
return (input * input);
}
int imaxi(const int a, const int b)
{
return (a > b) ? a : b;
}
int imini(const int a, const int b)
{
return (a < b) ? a : b;
}
float clampf(float input, float inMin, float inMax)
{
return fmaxf(fminf(input, inMax), inMin);
}
float clampi(int input, int inMin, int inMax)
{
return imaxi(imini(input, inMax), inMin);
}
Vector GetPitchForProjectile(Vector LaunchPoint, Vector TargetPoint, const float ProjectileSpeed, const float Gravity)
{
double start_x = LaunchPoint.x;
double start_y = LaunchPoint.y;
double start_z = LaunchPoint.z;
double target_x = TargetPoint.x;
double target_y = TargetPoint.y;
double target_z = TargetPoint.z;
double x = target_x - start_x;
double y = target_y - start_y;
double z = target_z - start_z;
double range = sqrt(x * x + y * y);
double discriminant = pow(ProjectileSpeed, 4) - Gravity * (Gravity * pow(range, 2) + 2 * z * pow(ProjectileSpeed, 2));
if (discriminant < 0)
{
return ZERO_VECTOR;
}
double launch_angle = atan((pow(ProjectileSpeed, 2) - sqrt(discriminant)) / (Gravity * range));
// Calculate the components of the unit vector
double unit_vector_x = cos(launch_angle) * cos(atan2(y, x));
double unit_vector_y = cos(launch_angle) * sin(atan2(y, x));
double unit_vector_z = sin(launch_angle);
Vector LaunchVector = Vector(unit_vector_x, unit_vector_y, unit_vector_z);
return LaunchVector;
}
void UTIL_AnglesToVector(const Vector angles, Vector* fwd, Vector* right, Vector* up)
{
g_engfuncs.pfnAngleVectors(angles, (float*)fwd, (float*)right, (float*)up);
}
float UTIL_WrapAngle(float angle)
{
// check for wraparound of angle
if (angle > 180)
angle -= 360;
else if (angle < -180)
angle += 360;
return (angle);
}
Vector UTIL_WrapAngles(Vector angles)
{
// check for wraparound of angles
if (angles.x > 180)
angles.x -= 360;
else if (angles.x < -180)
angles.x += 360;
if (angles.y > 180)
angles.y -= 360;
else if (angles.y < -180)
angles.y += 360;
if (angles.z > 180)
angles.z -= 360;
else if (angles.z < -180)
angles.z += 360;
return (angles);
}
float signf(float input)
{
return (input == 0.0f) ? 0.0f : ((input > 0.0f) ? 1.0f : -1.0f);
}
Vector ViewInterpTo(const Vector CurrentViewAngles, const Vector& TargetDirection, const float DeltaTime, const float InterpSpeed)
{
if (DeltaTime == 0.f)
{
return CurrentViewAngles;
}
// If no interp speed, jump to target value
if (InterpSpeed <= 0.f)
{
return UTIL_VecToAngles(TargetDirection);
}
Vector TargetAngles = UTIL_VecToAngles(TargetDirection);
const float DeltaInterpSpeed = InterpSpeed * DeltaTime;
const Vector Delta = UTIL_GetVectorNormal(TargetAngles - CurrentViewAngles);
// If steps are too small, just return Target and assume we have reached our destination.
if (vEquals(CurrentViewAngles, TargetAngles))
{
return TargetAngles;
}
// Delta Move, Clamp so we do not over shoot.
const Vector DeltaMove = Delta * clampf(DeltaInterpSpeed, 0.f, 1.f);
Vector NewAngle = (CurrentViewAngles + DeltaMove);
if (NewAngle.y > 180)
NewAngle.y -= 360;
// Paulo-La-Frite - START bot aiming bug fix
if (NewAngle.x > 180)
NewAngle.x -= 360;
NewAngle.z = 0.0f;
return NewAngle;
}
float fInterpTo(float start, float end, float DeltaTime, float InterpSpeed)
{
// If no interp speed, jump to target value
if (InterpSpeed <= 0.f)
{
return end;
}
// Distance to reach
float Dist = end - start;
// If distance is too small, just set the desired location
if (fabsf(sqrf(Dist)) < 0.0001f)
{
return end;
}
// Delta Move, Clamp so we do not over shoot.
float DeltaMove = Dist * clampf((DeltaTime * InterpSpeed), 0.f, 1.f);
float NewVal = start + DeltaMove;
return NewVal;
}
float fInterpConstantTo(float start, float end, float DeltaTime, float InterpSpeed)
{
float Dist = end - start;
// If distance is too small, just set the desired location
if (fabsf(sqrf(Dist)) < 0.0001f)
{
return end;
}
const float Step = InterpSpeed * DeltaTime;
float NewVal = start + clampf(Dist, -Step, Step);
return NewVal;
}
float frandrange(float MinValue, float MaxValue)
{
return ((float(rand()) / float(RAND_MAX)) * (MaxValue - MinValue)) + MinValue;
}
int irandrange(int MinValue, int MaxValue)
{
if (MinValue == MaxValue) { return MinValue; }
return MinValue + rand() / (RAND_MAX / (MaxValue - MinValue + 1) + 1);
}
bool randbool()
{
return (rand() & 1);
}
Vector random_unit_vector_within_cone(const Vector Direction, double cone_half_angle)
{
Vector Result = ZERO_VECTOR;
double u = ((double)rand() / RAND_MAX) * 2 - 1; // random number in [-1, 1]
double phi = ((double)rand() / RAND_MAX) * 2 * M_PI; // random number in [0, 2*pi]
double r = sqrt(1 - u * u);
double x = r * cos(phi);
double y = r * sin(phi);
double z = u;
// Rotate the random point to the cone direction using a quaternion.
// First, compute the quaternion that rotates the z-axis to the cone direction.
double cos_half_angle = cos(cone_half_angle / 2);
double sin_half_angle = sin(cone_half_angle / 2);
double qx = Direction.x * sin_half_angle;
double qy = Direction.y * sin_half_angle;
double qz = Direction.z * sin_half_angle;
double qw = cos_half_angle;
// Then, apply the quaternion rotation to the random point.
double t = qw * z - qx * x - qy * y - qz * z;
Result.x = qx * z + qw * x - qz * y + qy * t;
Result.y = qy * z + qz * x + qw * y - qx * t;
Result.z = qz * z - qy * x + qx * y + qw * t;
return UTIL_GetVectorNormal(Result);
}
Vector UTIL_GetRandomUnitVectorInCone(const Vector ConeDirection, const float HalfAngleRadians)
{
Vector P = UTIL_GetVectorNormal(UTIL_GetCrossProduct(ConeDirection, UP_VECTOR));
Vector Q = UTIL_GetVectorNormal(UTIL_GetCrossProduct(ConeDirection, P));
float RMax = tanf(HalfAngleRadians);
float Theta = frandrange(0.0f, 2.0f) * MATH_PI;
float u = frandrange(cos(HalfAngleRadians), 1.0f);
float r = RMax * sqrt(1.0f - sqrf(u));
return UTIL_GetVectorNormal((r * (P * cos(Theta) + Q * sin(Theta))));
}
float fDegreesToRadians(const float Degrees)
{
return Degrees * DEGREES_RADIANS_CONV;
}
Vector UTIL_GetForwardVector(const Vector angles)
{
Vector fwd, right, up;
UTIL_AnglesToVector(angles, &fwd, &right, &up);
return fwd;
}
Vector UTIL_GetForwardVector2D(const Vector angles)
{
Vector fwd, right, up;
UTIL_AnglesToVector(angles, &fwd, &right, &up);
return UTIL_GetVectorNormal2D(fwd);
}
float UTIL_GetDistanceToPolygon2DSq(const Vector TestPoint, const Vector* Points, const int NumPoints)
{
float minDist = -1.0f;
for (int i = 0; i < NumPoints; i++)
{
Vector destPoint = (i <= (NumPoints - 2)) ? Points[i + 1] : Points[0];
float thisDist = vDistanceFromLine2DSq(Points[i], destPoint, TestPoint);
if (minDist < 0.0f)
{
minDist = thisDist;
}
else
{
minDist = fminf(minDist, thisDist);
}
}
return minDist;
}
Vector UTIL_GetAimLocationToLeadTarget(const Vector ShooterLocation, const Vector TargetLocation, const Vector TargetVelocity, const float ProjectileVelocity)
{
// We interpret a speed of 0.0f to mean hitscan, i.e. infinitely fast
if (ProjectileVelocity == 0.0f) { return TargetLocation; }
Vector totarget = TargetLocation - ShooterLocation;
float a = UTIL_GetDotProduct(TargetVelocity, TargetVelocity) - (ProjectileVelocity * ProjectileVelocity);
float b = 2.0f * UTIL_GetDotProduct(TargetVelocity, totarget);
float c = UTIL_GetDotProduct(totarget, totarget);
float p = -b / (2.0f * a);
float q = (float)sqrt((b * b) - 4.0f * a * c) / (2.0f * a);
float t1 = p - q;
float t2 = p + q;
float t;
if (t1 > t2 && t2 > 0)
{
t = t2;
}
else
{
t = t1;
}
return (TargetLocation + TargetVelocity * t);
}
float UTIL_GetVelocityRequiredToReachTarget(const Vector StartLocation, const Vector TargetLocation, float Gravity)
{
// Calculate the distance between the start and target positions
double distance = vDist3D(StartLocation, TargetLocation);
// Calculate the initial velocity required to reach the target with no air resistance
double velocity = sqrt(2.0f * Gravity * distance);
return velocity;
}
Vector UTIL_GetRandomPointInBoundingBox(const Vector BoxMin, const Vector BoxMax)
{
float RandX = frandrange(BoxMin.x, BoxMax.x);
float RandY = frandrange(BoxMin.y, BoxMax.y);
float RandZ = frandrange(BoxMin.z, BoxMax.z);
return Vector(RandX, RandY, RandZ);
}
/* Function to get no of set bits in binary
representation of positive integer n */
unsigned int UTIL_CountSetBitsInInteger(unsigned int n)
{
unsigned int count = 0;
while (n)
{
count += n & 1;
n >>= 1;
}
return count;
}
float UTIL_CalculateSlopeAngleBetweenPoints(const Vector StartPoint, const Vector EndPoint)
{
float Run = vDist2DSq(StartPoint, EndPoint);
float Rise = fabsf(StartPoint.z - EndPoint.z);
return atanf(Rise / Run);
}

208
main/source/mod/AvHAIMath.h Normal file
View file

@ -0,0 +1,208 @@
//
// EvoBot - Neoptolemus' Natural Selection bot, based on Botman's HPB bot template
//
// bot_math.h
//
// Contains all useful math functions for bot stuff
//
#pragma once
#ifndef AVH_AI_MATH_H
#define AVH_AI_MATH_H
#include "../dlls/extdll.h"
static const Vector ZERO_VECTOR = Vector(0.0f, 0.0f, 0.0f);
static const Vector UP_VECTOR = Vector(0.0f, 0.0f, 1.0f); // Normalized "up" direction
static const Vector RIGHT_VECTOR = Vector(1.0f, 0.0f, 0.0f); // Normalized "right" direction
static const Vector FWD_VECTOR = Vector(0.0f, 1.0f, 0.0f); // Normalized "forward" direction
static const float MATH_PI = 3.141592654f;
static const float DEGREES_RADIANS_CONV = (MATH_PI / 180.0f);
static const float GOLDSRC_GRAVITY = 400.0f; // Default speed of gravity in GoldSrc units per second squared
// Defines a frustum plane
typedef struct _FRUSTUM_PLANE_T
{
Vector normal;
Vector point;
float d;
} frustum_plane_t;
// GENERAL MATH
// Is the input string a valid integer?
bool isNumber(const char* line);
// Is the input string a valid floating point number?
bool isFloat(const char* line);
// Square the input
float sqrf(float input);
// Return the sign (-1 if number is negative, 1 if positive, 0 if 0)
float signf(float input);
// Clamp float value between min and max
float clampf(float input, float inMin, float inMax);
// Clamp int value between min and max
float clampi(int input, int inMin, int inMax);
// For any given view angle, ensure that the angle is expressed as a value between -180 and 180
float UTIL_WrapAngle(float angle);
// For view angles, ensures that the angles do not exceed -180 or 180
Vector UTIL_WrapAngles(Vector angles);
// Spherical linear interpolation of float from start to end at interp speed
float fInterpTo(float start, float end, float DeltaTime, float InterpSpeed);
// Linear interpolation of float from start to end at interp speed
float fInterpConstantTo(float start, float end, float DeltaTime, float InterpSpeed);
// Random float between min value and max value
float frandrange(float MinValue, float MaxValue);
// Random integer between min and max values
int irandrange(int MinValue, int MaxValue);
// Convert degrees to radians
float fDegreesToRadians(const float Degrees);
// Return random boolean
bool randbool();
// Returns the max of two integers
int imaxi(const int a, const int b);
// Returns the min of two integers
int imini(const int a, const int b);
// VECTOR MATH
// 2D (ignore Z axis) distance between v1 and v2
float vDist2D(const Vector v1, const Vector v2);
// 3D distance between v1 and v2
float vDist3D(const Vector v1, const Vector v2);
// Squared (no sqrt) 2D distance (ignore Z axis) between v1 and v2
float vDist2DSq(const Vector v1, const Vector v2);
// Squared (no sqrt) 3D distance between v1 and v2
float vDist3DSq(const Vector v1, const Vector v2);
// 3D length of vector
float vSize3D(const Vector V);
// 2D length (no Z axis) of vector
float vSize2D(const Vector V);
// Squared 3D length of vector
float vSize3DSq(const Vector V);
// Squared 2D length (no Z axis) of vector
float vSize2DSq(const Vector V);
// Are two vectors equal, using default epsilon of 0.1f
bool vEquals(const Vector v1, const Vector v2);
bool vEquals2D(const Vector v1, const Vector v2);
// Are two vectors equal, using custom epsilon
bool vEquals(const Vector v1, const Vector v2, const float epsilon);
bool vEquals2D(const Vector v1, const Vector v2, const float epsilon);
bool vIsZero(const Vector v1);
bool fNearlyEqual(const float f1, const float f2);
// Returns the dot product of two unit vectors
inline float UTIL_GetDotProduct(const Vector v1, const Vector v2) { return ((v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z)); }
// Returns the dot product of two unit vectors excluding Z axis
float UTIL_GetDotProduct2D(const Vector v1, const Vector v2);
// Normalize the unit vector (modifies input)
void UTIL_NormalizeVector(Vector* vec);
// Normalize the unit vector without the Z axis (modifies input)
void UTIL_NormalizeVector2D(Vector* vec);
// Returns a normalized copy of the input unit vector
Vector UTIL_GetVectorNormal(const Vector vec);
// Returns a normalized 2D copy of the input vector without Z axis
Vector UTIL_GetVectorNormal2D(const Vector vec);
//Returns the cross product of v1 and v2
Vector UTIL_GetCrossProduct(const Vector v1, const Vector v2);
// Returns the surface normal of a poly defined at points v1, v2 and v3 (clockwise)
Vector UTIL_GetSurfaceNormal(const Vector v1, const Vector v2, const Vector v3);
bool vPointOverlaps3D(const Vector Point, const Vector MinBB, const Vector MaxBB);
bool vPointOverlaps2D(const Vector Point, const Vector MinBB, const Vector MaxBB);
bool vBBOverlaps2D(const Vector MinBBA, const Vector MaxBBA, const Vector MinBBB, const Vector MaxBBB);
// For the two lines provided, returns true if they cross each other on the X and Y axis
bool vIntersects2D(const Vector LineAStart, const Vector LineAEnd, const Vector LineBStart, const Vector LineBEnd);
Vector vClosestPointOnBB(const Vector Point, const Vector MinBB, const Vector MaxBB);
void vScaleBB(Vector& MinBB, Vector& MaxBB, const float Scale);
// WIP: Trying to get a working random unit vector in cone. Not currently used
Vector UTIL_GetRandomUnitVectorInCone(const Vector ConeDirection, const float HalfAngleRadians);
Vector random_unit_vector_within_cone(const Vector Direction, double HalfAngleRadians);
// Takes in a bot's current view angle and a target direction to look in, and returns the appropriate view angles. Eases into the target
Vector ViewInterpTo(const Vector CurrentViewAngles, const Vector& TargetDirection, const float DeltaTime, const float InterpSpeed);
// LINE MATH
// For given line defined by lineFrom -> lineTo, how far away from that line is CheckPoint?
float vDistanceFromLine3D(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint);
// For given line defined by lineFrom -> lineTo, how far away from that line is CheckPoint? Ignores Z axis
float vDistanceFromLine2D(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint);
// For given line defined by lineFrom -> lineTo, get squared distance from that line of CheckPoint. Ignores Z axis
float vDistanceFromLine2DSq(const Vector lineFrom, const Vector lineTo, const Vector CheckPoint);
// Returns 0 if point sits right on the line defined by lineFrom -> lineTo, -1 if it sits to the left, 1 if it sits to the right. Ignores Z axis
int vPointOnLine(const Vector lineFrom, const Vector lineTo, const Vector point);
// Finds the closest point along a line to the input point
Vector vClosestPointOnLine(const Vector lineFrom, const Vector lineTo, const Vector point);
// Finds the closest point along a line to the input point, ignores Z axis
Vector vClosestPointOnLine2D(const Vector lineFrom, const Vector lineTo, const Vector point);
// Finds the closest point along a line to the input point, assumes infinite line
Vector vClosestPointOnInfiniteLine3D(const Vector PointOnLine, const Vector NormalisedLineDir, const Vector TestPoint);
// Finds the closest point along a line to the input point, assumes infinite line. Ignores Z axis
Vector vClosestPointOnInfiniteLine2D(const Vector PointOnLine, const Vector NormalisedLineDir, const Vector TestPoint);
// CONVERSIONS
// Converts input metres to GoldSrc units (approx 1 metres = 52.5 units)
float UTIL_MetresToGoldSrcUnits(const float Metres);
// Converts input GoldSrc units to metres (approx 52.5 units = 1 metre)
float UTIL_GoldSrcUnitsToMetres(const float GoldSrcUnits);
// Converts angles to unit vector
void UTIL_AnglesToVector(const Vector angles, Vector* fwd, Vector* right, Vector* up);
// Returns unit vector pointing in direction of input angles
Vector UTIL_GetForwardVector(const Vector angles);
// Returns 2D unit vector pointing in direction of input view angles, ignoring Z axis
Vector UTIL_GetForwardVector2D(const Vector angles);
// GEOMETRY STUFF
// Returns random point on a circle, assuming circle normal is (0, 0, 1)
Vector UTIL_RandomPointOnCircle(const Vector origin, const float radius);
// Returns the required pitch needed to hit the target point from launch point, taking projectile speed and gravity into account
Vector GetPitchForProjectile(Vector LaunchPoint, Vector TargetPoint, const float ProjectileSpeed, const float Gravity);
// Confirms if the given point is on the inside of a frustum plane or not
bool UTIL_PointInsidePlane(const frustum_plane_t* plane, const Vector point);
/* Tests to see if the defined cylinder is intersecting with the supplied frustum plane.
Since players are always upright, it is reasonable to assume that it is impossible for both the
top and bottom of the cylinder to be outside the plane if it is intersecting, therefore
we only need to test the top and bottom cylinder at the closest point to the plane.*/
bool UTIL_CylinderInsidePlane(const frustum_plane_t* plane, const Vector centre, float height, float radius);
// Set the normal and position for the plane based on the 3 points defining it
void UTIL_SetFrustumPlane(frustum_plane_t* plane, Vector v1, Vector v2, Vector v3);
// Finds the closest point to the polygon, defined by segments (edges)
float UTIL_GetDistanceToPolygon2DSq(const Vector TestPoint, const Vector* Segments, const int NumSegments);
// Based on the target's motion and the weapon's projectile speed, where should the bot aim to lead the target and hit them?
Vector UTIL_GetAimLocationToLeadTarget(const Vector ShooterLocation, const Vector TargetLocation, const Vector TargetVelocity, const float ProjectileVelocity);
// If flying through the air (e.g. blink), what velocity does the bot need to land on the target spot?
float UTIL_GetVelocityRequiredToReachTarget(const Vector StartLocation, const Vector TargetLocation, float Gravity);
Vector UTIL_GetRandomPointInBoundingBox(const Vector BoxMin, const Vector BoxMax);
// OTHER STUFF
// Function to get number of set bits in a positive integer n
unsigned int UTIL_CountSetBitsInInteger(unsigned int n);
float UTIL_CalculateSlopeAngleBetweenPoints(const Vector StartPoint, const Vector EndPoint);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,508 @@
//
// EvoBot - Neoptolemus' Natural Selection bot, based on Botman's HPB bot template
//
// bot_navigation.h
//
// Handles all bot path finding and movement
//
#pragma once
#ifndef AVH_AI_NAVIGATION_H
#define AVH_AI_NAVIGATION_H
#include "DetourStatus.h"
#include "DetourNavMeshQuery.h"
#include "DetourTileCache.h"
#include "AvHAIPlayer.h"
/* Navigation profiles determine which nav mesh (regular, onos, building) is used for queries, and what
types of movement are allowed and their costs. For example, marine nav profile uses regular nav mesh,
cannot wall climb, and has a higher cost for crouch movement since it's slower.
*/
constexpr auto MIN_PATH_RECALC_TIME = 0.33f; // How frequently can a bot recalculate its path? Default to max 3 times per second
constexpr auto MAX_BOT_STUCK_TIME = 30.0f; // How long a bot can be stuck, unable to move, before giving up and suiciding
constexpr auto MARINE_BASE_NAV_PROFILE = 0;
constexpr auto SKULK_BASE_NAV_PROFILE = 1;
constexpr auto GORGE_BASE_NAV_PROFILE = 2;
constexpr auto LERK_BASE_NAV_PROFILE = 3;
constexpr auto FADE_BASE_NAV_PROFILE = 4;
constexpr auto ONOS_BASE_NAV_PROFILE = 5;
constexpr auto STRUCTURE_BASE_NAV_PROFILE = 6;
constexpr auto ALL_NAV_PROFILE = 7;
constexpr auto MAX_PATH_POLY = 512; // Max nav mesh polys that can be traversed in a path. This should be sufficient for any sized map.
// Possible area types. Water, Road, Door and Grass are not used (left-over from Detour library)
enum SamplePolyAreas
{
SAMPLE_POLYAREA_GROUND = 0, // Regular ground movement
SAMPLE_POLYAREA_CROUCH = 1, // Requires crouched movement
SAMPLE_POLYAREA_BLOCKED = 2, // Requires a jump to get over
SAMPLE_POLYAREA_FALLDAMAGE = 3, // Requires taking fall damage (if not immune to it)
SAMPLE_POLYAREA_WALLCLIMB = 4, // Requires the ability to wall-stick, fly or blink
SAMPLE_POLYAREA_OBSTRUCTION = 5, // There is a door or weldable object in the way
SAMPLE_POLYAREA_STRUCTUREBLOCK = 6, // An enemy structure is blocking the way that must be destroyed
SAMPLE_POLYAREA_PHASEGATE = 7, // Phase gate area, for area cost calculation
SAMPLE_POLYAREA_LADDER = 8, // Phase gate area, for area cost calculation
SAMPLE_POLYAREA_LIFT = 9, // Phase gate area, for area cost calculation
};
// Possible movement types. Swim and door are not used
enum SamplePolyFlags
{
SAMPLE_POLYFLAGS_WALK = 1 << 0, // Simple walk to traverse
SAMPLE_POLYFLAGS_FALL = 1 << 1, // Required dropping down
SAMPLE_POLYFLAGS_BLOCKED = 1 << 2, // Blocked by an obstruction, but can be jumped over
SAMPLE_POLYFLAGS_WALLCLIMB = 1 << 3, // Requires climbing a wall to traverse
SAMPLE_POLYFLAGS_LADDER = 1 << 4, // Requires climbing a ladder to traverse
SAMPLE_POLYFLAGS_JUMP = 1 << 5, // Requires a regular jump to traverse
SAMPLE_POLYFLAGS_DUCKJUMP = 1 << 6, // Requires a duck-jump to traverse
SAMPLE_POLYFLAGS_FLY = 1 << 7, // Requires lerk or jetpack to traverse
SAMPLE_POLYFLAGS_NOONOS = 1 << 8, // This movement is not allowed by onos
SAMPLE_POLYFLAGS_TEAM1PHASEGATE = 1 << 9, // Requires using a phase gate to traverse (team 1 only)
SAMPLE_POLYFLAGS_TEAM2PHASEGATE = 1 << 10, // Requires using a phase gate to traverse (team 2 only)
SAMPLE_POLYFLAGS_TEAM1STRUCTURE = 1 << 11, // A team 1 structure is in the way that cannot be jumped over. Impassable to team 1 players (assume cannot teamkill own structures)
SAMPLE_POLYFLAGS_TEAM2STRUCTURE = 1 << 12, // A team 2 structure is in the way that cannot be jumped over. Impassable to team 2 players (assume cannot teamkill own structures)
SAMPLE_POLYFLAGS_WELD = 1 << 13, // Requires a welder to get through here
SAMPLE_POLYFLAGS_DOOR = 1 << 14, // Requires a welder to get through here
SAMPLE_POLYFLAGS_LIFT = 1 << 15, // Requires using a lift or moving platform
SAMPLE_POLYFLAGS_DISABLED = 1 << 16, // Disabled, not usable by anyone
SAMPLE_POLYFLAGS_ALL = -1 // All abilities.
};
// Door reference. Not used, but is a future feature to allow bots to track if a door is open or not, and how to open it etc.
typedef struct _NAV_DOOR
{
CBaseToggle* DoorEntity = nullptr;
edict_t* DoorEdict = nullptr; // Reference to the func_door
unsigned int ObstacleRefs[32][MAX_NAV_MESHES] = {}; // Dynamic obstacle ref. Used to add/remove the obstacle as the door is opened/closed
int NumObstacles = 0;
vector<DoorTrigger> TriggerEnts; // Reference to the trigger edicts (e.g. func_trigger, func_button etc.)
DoorActivationType ActivationType = DOOR_NONE; // How the door should be opened
TOGGLE_STATE CurrentState = TS_AT_BOTTOM;
float OpenDelay = 0.0f; // How long the door takes to start opening after activation
vector<Vector> StopPoints; // Where does this door/platform stop when triggered?
NavDoorType DoorType = DOORTYPE_DOOR;
vector<AvHAIOffMeshConnection*> AffectedConnections;
} nav_door;
typedef struct _NAV_WELDABLE
{
edict_t* WeldableEdict = nullptr;
unsigned int ObstacleRefs[32][MAX_NAV_MESHES];
int NumObstacles = 0;
} nav_weldable;
// Door reference. Not used, but is a future feature to allow bots to track if a door is open or not, and how to open it etc.
typedef struct _NAV_HITRESULT
{
float flFraction = 0.0f;
bool bStartOffMesh = false;
Vector TraceEndPoint = g_vecZero;
} nav_hitresult;
// Links together a tile cache, nav query and the nav mesh into one handy structure for all your querying needs
typedef struct _NAV_MESH
{
class dtTileCache* tileCache = nullptr;
class dtNavMeshQuery* navQuery = nullptr;
class dtNavMesh* navMesh = nullptr;
} nav_mesh;
static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET', used to confirm the nav mesh we're loading is compatible;
static const int NAVMESHSET_VERSION = 1;
static const int TILECACHESET_MAGIC = 'T' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'TSET', used to confirm the tile cache we're loading is compatible;
static const int TILECACHESET_VERSION = 2;
static const float pExtents[3] = { 400.0f, 50.0f, 400.0f }; // Default extents (in GoldSrc units) to find the nearest spot on the nav mesh
static const float pReachableExtents[3] = { max_ai_use_reach, max_ai_use_reach, max_ai_use_reach }; // Extents (in GoldSrc units) to determine if something is on the nav mesh
static const int MAX_NAV_PROFILES = 16; // Max number of possible nav profiles. Currently 9 are used (see top of this header file)
static const int REGULAR_NAV_MESH = 0; // Nav mesh used by all players except Onos and the AI commander
static const int ONOS_NAV_MESH = 1; // Nav mesh used by Onos (due to larger hitbox)
static const int BUILDING_NAV_MESH = 2; // Nav mesh used by commander for building placement. Must be the last nav mesh index (see UTIL_AddStructureTemporaryObstacles)
static const int DT_AREA_NULL = 0; // Represents a null area on the nav mesh. Not traversable and considered not on the nav mesh
static const int DT_AREA_BLOCKED = 3; // Area occupied by an obstruction (e.g. building). Not traversable, but considered to be on the nav mesh
static const int MAX_OFFMESH_CONNS = 1024; // Max number of dynamic connections that can be placed. Not currently used (connections are baked into the nav mesh using the external tool)
static const int DOOR_USE_ONLY = 256; // Flag used by GoldSrc to determine if a door entity can only be used to open (i.e. can't be triggered)
static const int DOOR_START_OPEN = 1;
static const float CHECK_STUCK_INTERVAL = 0.1f; // How frequently should the bot check if it's stuck?
// Returns true if a valid nav mesh has been loaded into memory
bool NavmeshLoaded();
// Unloads all data, including loaded nav meshes, nav profiles, all the map data such as buildable structure maps and hive locations.
void UnloadNavigationData();
// Unloads only the nav meshes, but not map data such as doors, hives and locations
void UnloadNavMeshes();
// Searches for the corresponding .nav file for the input map name, and loads/initiatialises the nav meshes and nav profiles.
bool loadNavigationData(const char* mapname);
// Loads the nav mesh only. Map data such as hive locations, doors etc are not loaded
bool LoadNavMesh(const char* mapname);
// Unloads the nav meshes (UnloadNavMeshes()) and then reloads them (LoadNavMesh). Map data such as doors, hives, locations are not touched.
void ReloadNavMeshes();
AvHAINavMeshStatus NAV_GetNavMeshStatus();
void SetBaseNavProfile(AvHAIPlayer* pBot);
void UpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
void MarineUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
void SkulkUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
void GorgeUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
void LerkUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
void FadeUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
void OnosUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle);
// Finds any random point on the navmesh that is relevant for the bot. Returns ZERO_VECTOR if none found
Vector UTIL_GetRandomPointOnNavmesh(const AvHAIPlayer* pBot);
/* Finds any random point on the navmesh that is relevant for the bot within a given radius of the origin point,
taking reachability into account(will not return impossible to reach location).
Returns ZERO_VECTOR if none found
*/
Vector UTIL_GetRandomPointOnNavmeshInRadius(const nav_profile& NavProfile, const Vector origin, const float MaxRadius);
/* Finds any random point on the navmesh that is relevant for the bot within a given radius of the origin point,
ignores reachability (could return a location that isn't actually reachable for the bot).
Returns ZERO_VECTOR if none found
*/
Vector UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(const nav_profile& NavProfile, const Vector origin, const float MaxRadius);
/* Finds any random point on the navmesh of the area type (e.g. crouch area) that is relevant for the bot within a given radius of the origin point,
taking reachability into account(will not return impossible to reach location).
Returns ZERO_VECTOR if none found
*/
Vector UTIL_GetRandomPointOnNavmeshInRadiusOfAreaType(SamplePolyFlags Flag, const Vector origin, const float MaxRadius);
/* Finds any random point on the navmesh of the area type (e.g. crouch area) that is relevant for the bot within the min and max radius of the origin point,
taking reachability into account(will not return impossible to reach location).
Returns ZERO_VECTOR if none found
*/
Vector UTIL_GetRandomPointOnNavmeshInDonut(const nav_profile& NavProfile, const Vector origin, const float MinRadius, const float MaxRadius);
/* Finds any random point on the navmesh of the area type (e.g. crouch area) that is relevant for the bot within the min and max radius of the origin point,
ignores reachability (could return a location that isn't actually reachable for the bot).
Returns ZERO_VECTOR if none found
*/
Vector UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(const nav_profile& NavProfile, const Vector origin, const float MinRadius, const float MaxRadius);
// Roughly estimates the movement cost to move between FromLocation and ToLocation. Uses simple formula of distance between points x cost modifier for that movement
float UTIL_GetPathCostBetweenLocations(const nav_profile &NavProfile, const Vector FromLocation, const Vector ToLocation);
// Returns true is the bot is grounded, on the nav mesh, and close enough to the Destination to be considered at that point
bool BotIsAtLocation(const AvHAIPlayer* pBot, const Vector Destination);
// Sets the bot's desired movement direction and performs jumps/crouch/etc. to traverse the current path point
void NewMove(AvHAIPlayer* pBot);
// Returns true if the bot has completed the current movement along their path
bool HasBotReachedPathPoint(const AvHAIPlayer* pBot);
bool HasBotCompletedLadderMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedWalkMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedFallMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedClimbMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, float RequiredClimbHeight, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedJumpMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedPhaseGateMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedLiftMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool HasBotCompletedObstacleMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
// Returns true if the bot is considered to have strayed off the path (e.g. missed a jump and fallen)
bool IsBotOffPath(const AvHAIPlayer* pBot);
bool IsBotOffLadderNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffWalkNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffFallNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffClimbNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffJumpNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffPhaseGateNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffLiftNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
bool IsBotOffObstacleNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag);
// Called by NewMove, determines the movement direction and inputs required to walk/crouch between start and end points
void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines the movement direction and inputs required to jump between start and end points
void JumpMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines movement direction and jump inputs to hop over obstructions (structures)
void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines which structure is in the way and attacks it
void StructureBlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines the movement direction and inputs required to drop down from start to end points
void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint
void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea);
// Called by NewMove, determines the movement direction and inputs required to climb a wall to reach endpoint
void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight);
void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight);
// Called by NewMove, determines the movement direction and inputs required to use a phase gate to reach end point
void PhaseGateMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines the movement direction and inputs required to use a lift to reach an end point
void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
DoorTrigger* UTIL_GetDoorTriggerByEntity(edict_t* TriggerEntity);
bool UTIL_TriggerHasBeenRecentlyActivated(edict_t* TriggerEntity);
// Will check for any func_breakable which might be in the way (e.g. window, vent) and make the bot aim and attack it to break it. Marines will switch to knife to break it.
void CheckAndHandleBreakableObstruction(AvHAIPlayer* pBot, const Vector MoveFrom, const Vector MoveTo, unsigned int MovementFlags);
void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot);
DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, CBaseEntity* IgnoreTrigger, bool bCheckBlockedByDoor);
DoorTrigger* UTIL_GetNearestDoorTriggerFromLift(edict_t* LiftEdict, nav_door* Door, CBaseEntity* IgnoreTrigger);
bool UTIL_IsPathBlockedByDoor(const Vector StartLoc, const Vector EndLoc, edict_t* SearchDoor);
edict_t* UTIL_GetDoorBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchDoor);
edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector ToLocation, const unsigned int MovementFlag, edict_t* SearchDoor);
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchBreakable);
edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector FromLocation, const Vector ToLocation, const unsigned int MovementFlag, edict_t* SearchBreakable);
Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdict);
// Clears all tracking of a bot's stuck status
void ClearBotStuck(AvHAIPlayer* pBot);
// Clears all bot movement data, including the current path, their stuck status. Effectively stops all movement the bot is performing.
void ClearBotMovement(AvHAIPlayer* pBot);
// Called every bot frame (default is 60fps). Ensures the tile cache is updated after obstacles are placed
bool UTIL_UpdateTileCache();
void AIDEBUG_DrawOffMeshConnections(float DrawTime);
void AIDEBUG_DrawTemporaryObstacles(float DrawTime);
Vector UTIL_GetNearestPointOnNavWall(AvHAIPlayer* pBot, const float MaxRadius);
Vector UTIL_GetNearestPointOnNavWall(const nav_profile& NavProfile, const Vector Location, const float MaxRadius);
/* Places a temporary obstacle of the given height and radius on the mesh.Will modify that part of the nav mesh to be the given area.
An example use case is to place an obstacle of area type SAMPLE_POLYAREA_OBSTRUCTION to mark where buildings are.
Using DT_AREA_NULL will effectively cut a hole in the nav mesh, meaning it's no longer considered a valid mesh position.
*/
unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector Location, float Radius, float Height, int area);
void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Height, int area, unsigned int* ObstacleRefArray);
void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure);
void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure);
unsigned int UTIL_AddTemporaryBoxObstacle(Vector bMin, Vector bMax, int area);
/* Removes the temporary obstacle from the mesh. The area will return to its default type (either walk or crouch).
Removing a DT_AREA_NULL obstacle will "fill in" the hole again, making it traversable and considered a valid mesh position.
*/
void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef);
void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs);
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned int flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef);
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef);
/*
Safely aborts the current movement the bot is performing. Returns true if the bot has successfully aborted, and is ready to calculate a new path.
The purpose of this is to avoid sudden path changes while on a ladder or performing a wall climb, which can cause the bot to get confused.
NewDestination is where the bot wants to go now, so it can figure out how best to abort the current move.
*/
bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination);
// Will clear current path and recalculate it for the supplied destination
bool BotRecalcPath(AvHAIPlayer* pBot, const Vector Destination);
/* Main function for instructing a bot to move to the destination, and what type of movement to favour. Should be called every frame you want the bot to move.
Will handle path calculation, following the path, detecting if stuck and trying to unstick itself.
Will only recalculate paths if it decides it needs to, so is safe to call every frame.
*/
bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle MoveStyle, const float MaxAcceptableDist = max_ai_use_reach);
void UpdateBotStuck(AvHAIPlayer* pBot);
// Used by the MoveTo command, handles the bot's movement and inputs to follow a path it has calculated for itself
void BotFollowPath(AvHAIPlayer* pBot);
void BotFollowFlightPath(AvHAIPlayer* pBot);
void BotFollowSwimPath(AvHAIPlayer* pBot);
void SkipAheadInFlightPath(AvHAIPlayer* pBot);
// Walks directly towards the destination. No path finding, just raw movement input. Will detect obstacles and try to jump/duck under them.
void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination);
void MoveToWithoutNav(AvHAIPlayer* pBot, const Vector Destination);
// Check if there are any players in our way and try to move around them. If we can't, then back up to let them through
void HandlePlayerAvoidance(AvHAIPlayer* pBot, const Vector MoveDestination);
Vector AdjustPointForPathfinding(const Vector Point);
Vector AdjustPointForPathfinding(const Vector Point, const nav_profile& NavProfile);
// Special path finding that takes the presence of phase gates into account
dtStatus FindFlightPathToPoint(const nav_profile& NavProfile, Vector FromLocation, Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
Vector UTIL_FindHighestSuccessfulTracePoint(const Vector TraceFrom, const Vector TargetPoint, const Vector NextPoint, const float IterationStep, const float MinIdealHeight, const float MaxHeight);
// Similar to FindPathToPoint, but you can specify a max acceptable distance for partial results. Will return a failure if it can't reach at least MaxAcceptableDistance away from the ToLocation
dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
dtStatus DEBUG_TestFindPath(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
// If the bot is stuck and off the path or nav mesh, this will try to find a point it can directly move towards to get it back on track
Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot);
Vector FindClosestNavigablePointToDestination(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, float MaxAcceptableDistance);
// Will attempt to move directly towards MoveDestination while jumping/ducking as needed, and avoiding obstacles in the way
void PerformUnstuckMove(AvHAIPlayer* pBot, const Vector MoveDestination);
// Used by Detour for the FindRandomPointInCircle type functions
static float frand();
// Finds the appropriate nav mesh for the requested profile
const dtNavMesh* UTIL_GetNavMeshForProfile(const nav_profile & NavProfile);
// Finds the appropriate nav mesh query for the requested profile
const dtNavMeshQuery* UTIL_GetNavMeshQueryForProfile(const nav_profile& NavProfile);
// Finds the appropriatetile cache for the requested profile
const dtTileCache* UTIL_GetTileCacheForProfile(const nav_profile& NavProfile);
float UTIL_PointIsDirectlyReachable_DEBUG(const Vector start, const Vector target);
/*
Point is directly reachable:
Determines if the bot can walk directly between the two points.
Ignores map geometry, so will return true even if stairs are in the way, provided the bot can walk up/down them unobstructed
*/
bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetPoint);
bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector start, const Vector target);
bool UTIL_PointIsDirectlyReachable(const Vector start, const Vector target);
bool UTIL_PointIsDirectlyReachable(const nav_profile& NavProfile, const Vector start, const Vector target);
// Will trace along the nav mesh from start to target and return true if the trace reaches within MaxAcceptableDistance
bool UTIL_TraceNav(const nav_profile& NavProfile, const Vector start, const Vector target, const float MaxAcceptableDistance);
void UTIL_TraceNavLine(const nav_profile& NavProfile, const Vector Start, const Vector End, nav_hitresult* HitResult);
/*
Project point to navmesh:
Takes the supplied location in the game world, and returns the nearest point on the nav mesh within the supplied extents.
Uses pExtents by default if not supplying one.
Returns ZERO_VECTOR if not projected successfully
*/
Vector UTIL_ProjectPointToNavmesh(const Vector Location);
Vector UTIL_ProjectPointToNavmesh(const Vector Location, const Vector Extents);
Vector UTIL_ProjectPointToNavmesh(const Vector Location, const nav_profile& NavProfile);
Vector UTIL_ProjectPointToNavmesh(const Vector Location, const Vector Extents, const nav_profile& NavProfile);
/*
Point is on navmesh:
Returns true if it was able to project the point to the navmesh (see UTIL_ProjectPointToNavmesh())
*/
bool UTIL_PointIsOnNavmesh(const Vector Location, const nav_profile& NavProfile);
bool UTIL_PointIsOnNavmesh(const nav_profile& NavProfile, const Vector Location, const Vector SearchExtents);
// Sets the BotNavInfo so the bot can track if it's on the ground, in the air, climbing a wall, on a ladder etc.
void UTIL_UpdateBotMovementStatus(AvHAIPlayer* pBot);
// Returns true if a path could be found between From and To location. Cheaper than full path finding, only a rough check to confirm it can be done.
bool UTIL_PointIsReachable(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, const float MaxAcceptableDistance);
// If the bot has a path, it will work out how far along the path it can see and return the furthest point. Used so that the bot looks ahead along the path rather than just at its next path point
Vector UTIL_GetFurthestVisiblePointOnPath(const AvHAIPlayer* pBot);
// For the given viewer location and path, will return the furthest point along the path the viewer could see
Vector UTIL_GetFurthestVisiblePointOnPath(const Vector ViewerLocation, vector<bot_path_node>& path, bool bPrecise);
Vector UTIL_GetFurthestVisiblePointOnLineWithHull(const Vector ViewerLocation, const Vector LineStart, const Vector LineEnd, int HullNumber);
// Returns the nearest nav mesh poly reference for the edict's current world position
dtPolyRef UTIL_GetNearestPolyRefForEntity(const edict_t* Edict);
dtPolyRef UTIL_GetNearestPolyRefForLocation(const Vector Location);
dtPolyRef UTIL_GetNearestPolyRefForLocation(const nav_profile& NavProfile, const Vector Location);
// Returns the area for the nearest nav mesh poly to the given location. Returns BLOCKED if none found
unsigned char UTIL_GetNavAreaAtLocation(const Vector Location);
unsigned char UTIL_GetNavAreaAtLocation(const nav_profile& NavProfile, const Vector Location);
// For printing out human-readable nav mesh areas
const char* UTIL_NavmeshAreaToChar(const unsigned char Area);
// From the given start point, determine how high up the bot needs to climb to get to climb end. Will allow the bot to climb over railings
float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEnd, const int HullNum);
// Clears the bot's path and sets the path size to 0
void ClearBotPath(AvHAIPlayer* pBot);
// Clears just the bot's current stuck movement attempt (see PerformUnstuckMove())
void ClearBotStuckMovement(AvHAIPlayer* pBot);
void UTIL_ClearDoorData();
void UTIL_ClearWeldablesData();
const nav_profile GetBaseNavProfile(const int index);
// Based on the direction the bot wants to move and it's current facing angle, sets the forward and side move, and the directional buttons to make the bot actually move
void BotMovementInputs(AvHAIPlayer* pBot);
// Event called when a bot starts climbing a ladder
void OnBotStartLadder(AvHAIPlayer* pBot);
// Event called when a bot leaves a ladder
void OnBotEndLadder(AvHAIPlayer* pBot);
// Tracks all doors and their current status
void UTIL_PopulateDoors();
void UTIL_PopulateTrainStopPoints(nav_door* TrainDoor);
void UTIL_UpdateWeldableObstacles();
void UTIL_UpdateDoors(bool bInitial = false);
void UTIL_UpdateDoorTriggers(nav_door* Door);
void UTIL_ModifyOffMeshConnectionFlag(AvHAIOffMeshConnection* Connection, const unsigned int NewFlag);
bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, CBaseEntity* Door);
void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& TriggerList);
void UTIL_PopulateAffectedConnectionsForDoor(nav_door* Door);
void UTIL_PopulateWeldableObstacles();
void UTIL_ApplyTempObstaclesToDoor(nav_door* DoorRef, const int Area);
nav_door* UTIL_GetNavDoorByEdict(const edict_t* DoorEdict);
nav_door* UTIL_GetClosestLiftToPoints(const Vector StartPoint, const Vector EndPoint);
Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall);
void UTIL_PopulateBaseNavProfiles();
void OnOffMeshConnectionAdded(dtOffMeshConnection* NewConnection);
const dtOffMeshConnection* DEBUG_FindNearestOffMeshConnectionToPoint(const Vector Point, unsigned int FilterFlags);
void NAV_SetPickupMovementTask(AvHAIPlayer* pBot, edict_t* ThingToPickup, DoorTrigger* TriggerToActivate);
void NAV_SetMoveMovementTask(AvHAIPlayer* pBot, Vector MoveLocation, DoorTrigger* TriggerToActivate);
void NAV_SetTouchMovementTask(AvHAIPlayer* pBot, edict_t* EntityToTouch, DoorTrigger* TriggerToActivate);
void NAV_SetUseMovementTask(AvHAIPlayer* pBot, edict_t* EntityToUse, DoorTrigger* TriggerToActivate);
void NAV_SetBreakMovementTask(AvHAIPlayer* pBot, edict_t* EntityToBreak, DoorTrigger* TriggerToActivate);
void NAV_SetWeldMovementTask(AvHAIPlayer* pBot, edict_t* EntityToWeld, DoorTrigger* TriggerToActivate);
void NAV_ClearMovementTask(AvHAIPlayer* pBot);
void NAV_ProgressMovementTask(AvHAIPlayer* pBot);
bool NAV_IsMovementTaskStillValid(AvHAIPlayer* pBot);
vector<NavHint*> NAV_GetHintsOfType(unsigned int HintType, bool bUnoccupiedOnly = false);
vector<NavHint*> NAV_GetHintsOfTypeInRadius(unsigned int HintType, Vector SearchLocation, float Radius, bool bUnoccupiedOnly = false);
#endif // BOT_NAVIGATION_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,160 @@
#ifndef AVH_AI_PLAYER_H
#define AVH_AI_PLAYER_H
#include "AvHPlayer.h"
#include "AvHAIConstants.h"
void BotJump(AvHAIPlayer* pBot);
void BotSuicide(AvHAIPlayer* pBot);
void BotLookAt(AvHAIPlayer* pBot, Vector NewLocation);
void BotLookAt(AvHAIPlayer* pBot, edict_t* target);
void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target);
void BotDirectLookAt(AvHAIPlayer* pBot, Vector target);
bool BotUseObject(AvHAIPlayer* pBot, edict_t* Target, bool bContinuous);
bool CanBotLeap(AvHAIPlayer* pBot);
void BotLeap(AvHAIPlayer* pBot, const Vector TargetLocation);
float GetLeapCost(AvHAIPlayer* pBot);
// Returns true if the bot needs to reload
bool BotReloadWeapons(AvHAIPlayer* pBot);
// Make the bot type something in either global or team chat
void BotSay(AvHAIPlayer* pBot, bool bTeamSay, float Delay, char* textToSay);
bot_msg* GetAvailableBotMsgSlot(AvHAIPlayer* pBot);
void BotDropWeapon(AvHAIPlayer* pBot);
void BotAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotMarineAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotAlienAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotShootTarget(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, edict_t* Target);
void BotShootLocation(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, const Vector TargetLocation);
void BombardierAttackTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetLifeform);
void BotEvolveUpgrade(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetUpgrade);
enemy_status* GetTrackedEnemyRefForTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot);
void BotUpdateViewRotation(AvHAIPlayer* pBot, float DeltaTime);
void BotUpdateView(AvHAIPlayer* pBot);
void BotClearEnemyTrackingInfo(enemy_status* TrackingInfo);
bool IsPlayerInBotFOV(AvHAIPlayer* Observer, edict_t* TargetPlayer);
Vector GetVisiblePointOnPlayerFromObserver(edict_t* Observer, edict_t* TargetPlayer);
void UpdateBotChat(AvHAIPlayer* pBot);
void ClearBotInputs(AvHAIPlayer* pBot);
void StartNewBotFrame(AvHAIPlayer* pBot);
void EndBotFrame(AvHAIPlayer* pBot);
void AIPlayerThink(AvHAIPlayer* pBot);
// Think routine for regular NS game mode
void AIPlayerNSThink(AvHAIPlayer* pBot);
void AIPlayerNSMarineThink(AvHAIPlayer* pBot);
void AIPlayerNSAlienThink(AvHAIPlayer* pBot);
// Think routine for the combat game mode
void AIPlayerCOThink(AvHAIPlayer* pBot);
void AIPlayerCOMarineThink(AvHAIPlayer* pBot);
void AIPlayerCOAlienThink(AvHAIPlayer* pBot);
// Think routine for the deathmatch game mode (e.g. when playing CS maps)
void AIPlayerDMThink(AvHAIPlayer* pBot);
void TestNavThink(AvHAIPlayer* pBot);
void DroneThink(AvHAIPlayer* pBot);
void CustomThink(AvHAIPlayer* pBot);
AvHMessageID GetNextAIPlayerCOUpgrade(AvHAIPlayer* pBot);
AvHMessageID GetNextAIPlayerCOMarineUpgrade(AvHAIPlayer* pBot);
AvHMessageID GetNextAIPlayerCOAlienUpgrade(AvHAIPlayer* pBot);
bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
AvHAIPlayerTask* AIPlayerGetNextTask(AvHAIPlayer* pBot);
void AIPlayerSetPrimaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetMarineCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetPrimaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetWantsAndNeedsAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerSetSecondaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot);
bool ShouldBotThink(AvHAIPlayer* pBot);
void BotResumePlay(AvHAIPlayer* pBot);
void UpdateCommanderOrders(AvHAIPlayer* pBot);
void AIPlayerReceiveMoveOrder(AvHAIPlayer* pBot, Vector Destination);
void AIPlayerReceiveBuildOrder(AvHAIPlayer* pBot, edict_t* BuildTarget);
void AIPlayerRequestHealth(AvHAIPlayer* pBot);
void AIPlayerRequestAmmo(AvHAIPlayer* pBot);
void AIPlayerRequestOrder(AvHAIPlayer* pBot);
void BotStopCommanderMode(AvHAIPlayer* pBot);
void SetNewAIPlayerRole(AvHAIPlayer* pBot, AvHAIBotRole NewRole);
void UpdateAIMarinePlayerNSRole(AvHAIPlayer* pBot);
void UpdateAIAlienPlayerNSRole(AvHAIPlayer* pBot);
void UpdateAIPlayerCORole(AvHAIPlayer* pBot);
void UpdateAIPlayerDMRole(AvHAIPlayer* pBot);
bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot);
void AIPlayerTakeDamage(AvHAIPlayer* pBot, int damageTaken, edict_t* aggressor);
int BotGetNextEnemyTarget(AvHAIPlayer* pBot);
AvHMessageID AlienGetDesiredUpgrade(AvHAIPlayer* pBot, HiveTechStatus DesiredTech);
AvHAICombatStrategy GetBotCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetAlienCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetSkulkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetGorgeCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetLerkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetFadeCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetOnosCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetMarineCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
bool MarineCombatThink(AvHAIPlayer* pBot);
void MarineHuntEnemy(AvHAIPlayer* pBot, enemy_status* TrackedEnemy);
void BotThrowGrenadeAtTarget(AvHAIPlayer* pBot, const Vector TargetPoint);
bool AlienCombatThink(AvHAIPlayer* pBot);
bool SkulkCombatThink(AvHAIPlayer* pBot);
bool GorgeCombatThink(AvHAIPlayer* pBot);
bool LerkCombatThink(AvHAIPlayer* pBot);
bool FadeCombatThink(AvHAIPlayer* pBot);
bool OnosCombatThink(AvHAIPlayer* pBot);
bool BombardierCombatThink(AvHAIPlayer* pBot);
bool RegularMarineCombatThink(AvHAIPlayer* pBot);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,116 @@
#ifndef AVH_AI_PLAYER_MANAGER_H
#define AVH_AI_PLAYER_MANAGER_H
#include "AvHConstants.h"
#include "AvHAIPlayer.h"
// The rate at which the bot will call RunPlayerMove in, default is 100hz. WARNING: Increasing the rate past 100hz causes bots to move and turn slowly due to GoldSrc limits!
static const double BOT_SERVER_UPDATE_RATE = (1.0 / 100.0);
// The rate in hz (times per second) at which the bot will call AIPlayerThink, default is 10 times per second.
static const int BOT_THINK_RATE_HZ = 10;
// Once the first human player has joined the game, how long to wait before adding bots
static const float AI_GRACE_PERIOD = 5.0f;
// Max time to wait before spawning players if none connect (e.g. empty dedicated server)
static const float AI_MAX_START_TIMEOUT = 20.0f;
void AIMGR_BotPrecache();
// Called when the round restarts. Clears all tactical information but keeps navigation data.
void AIMGR_ResetRound();
// Called when a new map is loaded. Clears all tactical information AND loads new navmesh.
void AIMGR_NewMap();
// Called when the match begins (countdown finished). Populates initial tactical information.
void AIMGR_RoundStarted();
// Adds a new AI player to a team (0 = Auto-assign, 1 = Team A, 2 = Team B)
void AIMGR_AddAIPlayerToTeam(int Team);
// Removed an AI player from the team (0 = Auto-select team, 1 = Team A, 2 = Team B)
void AIMGR_RemoveAIPlayerFromTeam(int Team);
// Run AI player logic
void AIMGR_UpdateAIPlayers();
// Kicks all bots in the ready room (used at round end when everyone is booted back to the ready room)
void AIMGR_RemoveBotsInReadyRoom();
// Called every 0.2s to determine if bots need to be added/removed. Calls UpdateTeamBalance or UpdateFillTeams depending on auto-mode
void AIMGR_UpdateAIPlayerCounts();
// Called by UpdateAIPlayerCounts. If auto-mode is for balance only, will add/remove bots needed to keep teams even
void AIMGR_UpdateTeamBalance();
// Called by UpdateAIPlayerCounts. If auto-mode is fill teams, will add/remove bots needed to maintain minimum player counts and balance
void AIMGR_UpdateFillTeams();
vector<AvHPlayer*> AIMGR_GetAllPlayersOnTeam(AvHTeamNumber Team);
// Convenient helper function to get total number of players (human and AI) on a team
int AIMGR_GetNumPlayersOnTeam(AvHTeamNumber Team);
// How many AI players are in the game (does NOT include third-party bots like RCBot/Whichbot)
int AIMGR_GetNumAIPlayers();
// How many bot commanders we have (across both teams)
int AIMGR_GetNumAICommanders();
// Returns true if an AI player is on the requested team (does NOT include third-party bots like RCBot/Whichbot)
int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team);
void AIMGR_RegenBotIni();
void AIMGR_UpdateAIMapData();
bool AIMGR_ShouldStartPlayerBalancing();
AvHAICommanderMode AIMGR_GetCommanderMode();
void AIMGR_SetCommanderAllowedTime(AvHTeamNumber Team, float NewValue);
float AIMGR_GetCommanderAllowedTime(AvHTeamNumber Team);
Vector AIDEBUG_GetDebugVector1();
Vector AIDEBUG_GetDebugVector2();
void AIDEBUG_SetDebugVector1(const Vector NewVector);
void AIDEBUG_SetDebugVector2(const Vector NewVector);
void AIDEBUG_TestPathFind();
int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team);
int AIMGR_GetNumHumanPlayersOnTeam(AvHTeamNumber Team);
int AIMGR_GetNumAIPlayersWithRoleOnTeam(AvHTeamNumber Team, AvHAIBotRole Role, AvHAIPlayer* IgnoreAIPlayer);
int AIMGR_GetNumHumansOfClassOnTeam(AvHTeamNumber Team, AvHUser3 PlayerType);
bool AIMGR_IsNavmeshLoaded();
AvHAINavMeshStatus AIMGR_GetNavMeshStatus();
bool AIMGR_IsBotEnabled();
void AIMGR_LoadNavigationData();
void AIMGR_ReloadNavigationData();
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);
AvHAIPlayer* AIMGR_GetBotRefFromPlayer(AvHPlayer* PlayerRef);
AvHTeamNumber AIMGR_GetEnemyTeam(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetEnemyTeamType(const AvHTeamNumber FriendlyTeam);
AvHClassType AIMGR_GetTeamType(const AvHTeamNumber Team);
AvHTeamNumber AIMGR_GetTeamANumber();
AvHTeamNumber AIMGR_GetTeamBNumber();
AvHTeam* AIMGR_GetTeamRef(const AvHTeamNumber Team);
// Returns all NS AI players. Does not include third-party bots
vector<AvHAIPlayer*> AIMGR_GetAllAIPlayers();
// Returns all NS AI players on the requested team. Does not include third-party bots
vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team);
// Returns all active players (i.e. not dead, commanding, spectating or in the ready room)
vector<AvHPlayer*> AIMGR_GetAllActivePlayers();
// Returns all players on a team which are not an internal NS bot. Will still include third party bots such as Whichbot and RCBot
vector<AvHPlayer*> AIMGR_GetNonAIPlayersOnTeam(AvHTeamNumber Team);
void AIMGR_ClearBotData();
AvHAIPlayer* AIMGR_GetDebugAIPlayer();
void AIMGR_SetDebugAIPlayer(edict_t* AIPlayer);
void AIMGR_ReceiveCommanderRequest(AvHTeamNumber Team, edict_t* Requestor, const char* Request);
void AIMGR_ClientConnected(edict_t* NewClient);
void AIMGR_PlayerSpawned();
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
#pragma once
#ifndef AVH_AI_PLAYER_HELPER_H
#define AVH_AI_PLAYER_HELPER_H
#include "AvHPlayer.h"
#include "AvHAIConstants.h"
// How far a bot can be from a useable object when trying to interact with it. Used also for melee attacks
static const float max_player_use_reach = 55.0f;
// Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down)
static const float min_player_use_interval = 0.5f;
// Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down)
static const float max_player_jump_height = 62.0f;
/****************
Player Status Checks
*****************/
// Is the player currently a Skulk?
bool IsPlayerSkulk(const edict_t* Player);
// Is the player currently a Gorge?
bool IsPlayerGorge(const edict_t* Player);
// Is the player currently a Lerk?
bool IsPlayerLerk(const edict_t* Player);
// Is the player currently a Fade?
bool IsPlayerFade(const edict_t* Player);
// Is the player currently an Onos?
bool IsPlayerOnos(const edict_t* Player);
// Is the player currently a Marine (not commander)? Includes both light and heavy marines
bool IsPlayerMarine(const edict_t* Player);
bool IsPlayerMarine(const AvHPlayer* Player);
// Is the player currently a Marine (not commander)? Includes both light and heavy marines
bool IsPlayerAlien(const edict_t* Player);
// Is the player the commander?
bool IsPlayerCommander(const edict_t* Player);
// Is the player currently climbing a wall?
bool IsPlayerClimbingWall(const edict_t* Player);
// Is the player in the ready room (i.e. not in the map proper)?
bool IsPlayerInReadyRoom(const edict_t* Player);
// Returns true if the player is not in the ready room, is on a team, is alive, is not being digested, and is not commander
bool IsPlayerActiveInGame(const edict_t* Player);
// Is the player a human?
bool IsPlayerHuman(const edict_t* Player);
// Is the player a bot (includes non-EvoBot fake clients)?
bool IsPlayerBot(const edict_t* Player);
// Is the player dead and waiting to respawn?
bool IsPlayerDead(const edict_t* Player);
// Is player stunned by Onos stomp?
bool IsPlayerStunned(const edict_t* Player);
// Is the player currently spectating?
bool IsPlayerSpectator(const edict_t* Player);
// Is the player currently being digested by an Onos?
bool IsPlayerBeingDigested(const edict_t* Player);
// Is the player an Onos and currently digesting someone?
bool IsPlayerDigesting(const edict_t* Player);
// Is the player currently gestating?
bool IsPlayerGestating(const edict_t* Player);
// Is the player affected by parasite?
bool IsPlayerParasited(const edict_t* Player);
// Is the player being marked through walls to enemies through being sighted by an ally or affected by motion tracking?
bool IsPlayerMotionTracked(const edict_t* Player);
// Is the player currently on a ladder? Always false for Skulks and Lerks as they can't climb ladders
bool IsPlayerOnLadder(const edict_t* Player);
// Is the player an onos under the effect of charge?
bool IsPlayerCharging(const edict_t* Player);
// Is the player buffed by catalysts (for marines) or primal scream (for aliens)?
bool IsPlayerBuffed(const edict_t* Player);
void AIPlayer_Say(edict_t* pEntity, int teamonly, const char* Msg);
// Returns the player's max armour, based on armour research levels (marines) or class and carapace level (aliens)
int GetPlayerMaxArmour(const edict_t* Player);
// Returns the player's current energy (between 0.0 and 1.0)
float GetPlayerEnergy(const edict_t* Player);
// Can the player duck? Skulks, gorges and lerks cannot
bool CanPlayerCrouch(const edict_t* Player);
// Returns player resources (for marines will be team resources)
int GetPlayerResources(const edict_t* Player);
// For combat mode, returns the player's current experience
int GetPlayerCombatExperience(const edict_t* Player);
// For combat mode, returns the player's current level
int GetPlayerCombatLevel(const AvHPlayer* Player);
// Returns the player radius based on their current state
float GetPlayerRadius(const AvHPlayer* Player);
float GetPlayerRadius(const edict_t* Player);
// Returns the hull index that should be used for this player when performing hull traces. Depends on if player is crouching right now or not
int GetPlayerHullIndex(const edict_t* Player);
// Returns the hull index that should be used for this player when performing hull traces, can manually specify if it's their crouching hull index or not
int GetPlayerHullIndex(const edict_t* Player, const bool bIsCrouching);
// How fast the player current regenerates energy, taking adrenaline upgrades into account
float GetPlayerEnergyRegenPerSecond(edict_t* Player);
// Expresses the combined health and armour of a player vs max
float GetPlayerOverallHealthPercent(const edict_t* Player);
// Gets the world position of the player's viewpoint (origin + view_ofs)
Vector GetPlayerEyePosition(const edict_t* Player);
// Player's current height based on their player class, can manually specify if you want the crouched height or not
float GetPlayerHeight(const edict_t* Player, const bool bIsCrouching);
// The origin offset from the bottom of the player's collision box, depending on crouching or not
Vector GetPlayerOriginOffsetFromFloor(const edict_t* pEdict, const bool bIsCrouching);
// The bottom-centre of the player's collision box
Vector GetPlayerBottomOfCollisionHull(const edict_t* pEdict);
// The top-centre of the player's collision box based on their player class and if they're currently crouching
Vector GetPlayerTopOfCollisionHull(const edict_t* pEdict);
// The top-centre of the player's collision box based on their player class, manually specifying if they're crouching or not
Vector GetPlayerTopOfCollisionHull(const edict_t* pEdict, const bool bIsCrouching);
// Based on current movement inputs from the player, what direction are they trying to move? Ignores actual velocity.
Vector GetPlayerAttemptedMoveDirection(const edict_t* Player);
// The player's index into the clients array (see game_state.h)
int GetPlayerIndex(AvHPlayer* Player);
// Returns true if the supplied edict is a player (bot or human)
bool IsEdictPlayer(const edict_t* edict);
bool IsPlayerTouchingEntity(const edict_t* Player, const edict_t* TargetEntity);
bool IsPlayerInUseRange(const edict_t* Player, const edict_t* Target);
bool PlayerHasHeavyArmour(const edict_t* Player);
bool PlayerHasJetpack(edict_t* Player);
bool PlayerHasWeapon(const AvHPlayer* Player, const AvHAIWeapon DesiredCombatWeapon);
bool PlayerHasEquipment(edict_t* Player);
bool PlayerHasSpecialWeapon(const AvHPlayer* Player);
bool UTIL_PlayerHasLOSToEntity(const edict_t* Player, const edict_t* Target, const float MaxRange, const bool bUseHullSweep);
bool UTIL_PlayerHasLOSToLocation(const edict_t* Player, const Vector Target, const float MaxRange);
bool PlayerHasAlienUpgradeOfType(const edict_t* Player, const HiveTechStatus TechType);
float GetPlayerCloakAmount(const edict_t* Player);
bool IsPlayerReloading(const AvHPlayer* Player);
bool IsPlayerStandingOnPlayer(const edict_t* Player);
// If the player is gestating, will return the target evolution. Otherwise, returns the iuser3
AvHUser3 GetPlayerActiveClass(const AvHPlayer* Player);
edict_t* UTIL_GetNearestLadderAtPoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderNormal(edict_t* pEdict);
Vector UTIL_GetNearestLadderNormal(Vector SearchLocation);
Vector UTIL_GetNearestLadderCentrePoint(edict_t* pEdict);
Vector UTIL_GetNearestLadderCentrePoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderTopPoint(edict_t* pEdict);
Vector UTIL_GetNearestLadderTopPoint(const Vector SearchLocation);
Vector UTIL_GetNearestLadderBottomPoint(edict_t* pEdict);
Vector UTIL_GetNearestSurfaceNormal(Vector SearchLocation);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,207 @@
//
// EvoBot - Neoptolemus' Natural Selection bot, based on Botman's HPB bot template
//
// bot_tactical.h
//
// Contains all helper functions for making tactical decisions
//
#pragma once
#ifndef AVH_AI_TACTICAL_H
#define AVH_AI_TACTICAL_H
#include "AvHAIPlayer.h"
#include "AvHAIConstants.h"
// How frequently to update the global list of built structures (in seconds). 0 = every frame
static const float structure_inventory_refresh_rate = 0.2f;
// How frequently to update the global list of dropped marine items (in seconds). 0 = every frame
static const float item_inventory_refresh_rate = 0.2f;
bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSearchFilter* Filter);
std::vector<AvHAIBuildableStructure> AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter);
std::vector<AvHAIBuildableStructure*> AITAC_FindAllDeployablesByRef(const Vector& Location, const DeployableSearchFilter* Filter);
AvHAIBuildableStructure AITAC_FindClosestDeployableToLocation(const Vector& Location, const DeployableSearchFilter* Filter);
AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocationByRef(const Vector& Location, const DeployableSearchFilter* Filter);
AvHAIBuildableStructure AITAC_FindFurthestDeployableFromLocation(const Vector& Location, const DeployableSearchFilter* Filter);
AvHAIBuildableStructure* AITAC_FindFurthestDeployableFromLocationByRef(const Vector& Location, const DeployableSearchFilter* Filter);
AvHAIBuildableStructure AITAC_GetDeployableFromEdict(const edict_t* Structure);
AvHAIBuildableStructure* AITAC_GetDeployableRefFromEdict(const edict_t* Structure);
AvHAIBuildableStructure AITAC_GetNearestDeployableDirectlyReachable(AvHAIPlayer* pBot, const Vector Location, const DeployableSearchFilter* Filter);
AvHAIBuildableStructure* AITAC_GetNearestDeployableDirectlyReachableByRef(AvHAIPlayer* pBot, const Vector Location, const DeployableSearchFilter* Filter);
int AITAC_GetNumDeployablesNearLocation(const Vector& Location, const DeployableSearchFilter* Filter);
void AITAC_PopulateHiveData();
void AITAC_RefreshHiveData();
void AITAC_PopulateResourceNodes();
void AITAC_RefreshResourceNodes();
void AITAC_UpdateMapAIData();
void AITAC_CheckNavMeshModified();
void AITAC_RefreshBuildableStructures();
AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure);
void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure);
void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode);
void AITAC_RefreshReachabilityForHive(AvHAIHiveDefinition* Hive);
void AITAC_RefreshAllResNodeReachability();
void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item);
void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure);
void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure);
void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure);
void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure);
void AITAC_LinkDeployedItemToAction(AvHAIPlayer* CommanderBot, const AvHAIDroppedItem* NewItem);
void AITAC_LinkStructureToPlayer(AvHAIBuildableStructure* NewStructure);
float AITAC_GetPhaseDistanceBetweenPoints(const Vector StartPoint, const Vector EndPoint);
const AvHAIHiveDefinition* AITAC_GetHiveAtIndex(int Index);
const AvHAIHiveDefinition* AITAC_GetHiveNearestLocation(const Vector SearchLocation);
const AvHAIHiveDefinition* AITAC_GetActiveHiveNearestLocation(AvHTeamNumber Team, const Vector SearchLocation);
const AvHAIHiveDefinition* AITAC_GetNonEmptyHiveNearestLocation(const Vector SearchLocation);
Vector AITAC_GetCommChairLocation(AvHTeamNumber Team);
edict_t* AITAC_GetCommChair(AvHTeamNumber Team);
Vector AITAC_GetTeamStartingLocation(AvHTeamNumber Team);
AvHAIResourceNode* AITAC_GetRandomResourceNode(AvHTeamNumber SearchingTeam, const unsigned int ReachabilityFlags);
AvHAIDroppedItem* AITAC_FindClosestItemToLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, AvHTeamNumber SearchingTeam, const unsigned int ReachabilityFlags, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance);
bool AITAC_ItemExistsInLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, AvHTeamNumber SearchingTeam, const unsigned int ReachabilityFlags, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance);
int AITAC_GetNumItemsInLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, AvHTeamNumber SearchingTeam, const unsigned int ReachabilityFlags, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance);
AvHAIDroppedItem* AITAC_GetDroppedItemRefFromEdict(edict_t* ItemEdict);
Vector AITAC_GetRandomBuildHintInLocation(const unsigned int StructureType, const Vector SearchLocation, const float SearchRadius);
Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive);
int AITAC_GetNumHives();
int AITAC_GetNumTeamHives(AvHTeamNumber Team, bool bFullyCompletedOnly);
void AITAC_OnNavMeshModified();
AvHMessageID UTIL_StructureTypeToImpulseCommand(const AvHAIDeployableStructureType StructureType);
AvHMessageID UTIL_ItemTypeToImpulseCommand(const AvHAIDeployableItemType ItemType);
edict_t* AITAC_GetClosestPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
bool AITAC_AnyPlayerOnTeamHasLOSToLocation(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
int AITAC_GetNumPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius, edict_t* IgnorePlayer);
bool AITAC_ShouldBotBeCautious(AvHAIPlayer* pBot);
// Clears out the marine and alien buildable structure maps, resource node and hive lists, and the marine item list
void AITAC_ClearMapAIData(bool bInitialMapLoad = false);
// Clear out all the hive information
void AITAC_ClearHiveInfo();
void AITAC_RefreshTeamStartingLocations();
void AITAC_ClearStructureNavData();
bool AITAC_AlienHiveNeedsReinforcing(const AvHAIHiveDefinition* Hive);
void AITAC_RefreshMarineItems();
void AITAC_UpdateMarineItem(CBaseEntity* Item, AvHAIDeployableItemType ItemType);
void AITAC_OnItemDropped(const AvHAIDroppedItem* NewItem);
AvHAIDeployableStructureType UTIL_IUSER3ToStructureType(const int inIUSER3);
bool UTIL_ShouldStructureCollide(AvHAIDeployableStructureType StructureType);
float UTIL_GetStructureRadiusForObstruction(AvHAIDeployableStructureType StructureType);
unsigned char UTIL_GetAreaForObstruction(AvHAIDeployableStructureType StructureType, const edict_t* BuildingEdict);
bool UTIL_IsStructureElectrified(edict_t* Structure);
bool UTIL_StructureIsFullyBuilt(edict_t* Structure);
bool UTIL_StructureIsRecycling(edict_t* Structure);
bool AITAC_StructureCanBeUpgraded(edict_t* Structure);
AvHAIHiveDefinition* AITAC_GetHiveFromEdict(const edict_t* Edict);
AvHAIResourceNode* AITAC_GetResourceNodeFromEdict(const edict_t* Edict);
// What percentage of all viable (can be reached by the requested team) resource nodes does the team currently own? Expressed as 0.0 - 1.0
float AITAC_GetTeamResNodeOwnership(const AvHTeamNumber Team, bool bIncludeBaseNodes);
int AITAC_GetNumResourceNodesNearLocation(const Vector Location, const DeployableSearchFilter* Filter);
AvHAIResourceNode* AITAC_FindNearestResourceNodeToLocation(const Vector Location, const DeployableSearchFilter* Filter);
AvHAIResourceNode* AITAC_GetNearestResourceNodeToLocation(const Vector Location);
vector<AvHAIResourceNode*> AITAC_GetAllMatchingResourceNodes(const Vector Location, const DeployableSearchFilter* Filter);
bool UTIL_IsBuildableStructureStillReachable(AvHAIPlayer* pBot, const edict_t* Structure);
bool UTIL_IsDroppedItemStillReachable(AvHAIPlayer* pBot, const edict_t* Item);
AvHAIWeapon UTIL_GetWeaponTypeFromEdict(const edict_t* ItemEdict);
int AITAC_GetNumActivePlayersOnTeam(const AvHTeamNumber Team);
int AITAC_GetNumPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 IgnoreClass);
bool AITAC_AnyPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 IgnoreClass);
vector<AvHPlayer*> AITAC_GetAllPlayersOfTeamInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 IgnoreClass);
int AITAC_GetNumPlayersOfTeamAndClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
int AITAC_GetNumPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer);
vector<AvHPlayer*> AITAC_GetAllPlayersOnTeamOfClass(const AvHTeamNumber Team, const AvHUser3 SearchClass, const edict_t* IgnorePlayer);
edict_t* AITAC_GetNearestPlayerOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
vector<edict_t*> AITAC_GetAllPlayersOfClassInArea(const AvHTeamNumber Team, const Vector SearchLocation, const float SearchRadius, const bool bConsiderPhaseDist, const edict_t* IgnorePlayer, const AvHUser3 SearchClass);
AvHAIHiveDefinition* AITAC_GetTeamHiveWithTech(const AvHTeamNumber Team, const AvHMessageID Tech);
bool AITAC_TeamHiveWithTechExists(const AvHTeamNumber Team, const AvHMessageID Tech);
AvHAIDeployableItemType UTIL_GetItemTypeFromEdict(const edict_t* ItemEdict);
bool UTIL_DroppedItemIsPrimaryWeapon(const AvHAIDeployableItemType ItemType);
AvHAIWeapon UTIL_GetWeaponTypeFromDroppedItem(const AvHAIDeployableItemType ItemType);
bool UTIL_StructureIsResearching(edict_t* Structure);
bool UTIL_StructureIsResearching(edict_t* Structure, const AvHMessageID Research);
bool UTIL_StructureIsUpgrading(edict_t* Structure);
bool AITAC_MarineResearchIsAvailable(const AvHTeamNumber Team, const AvHMessageID Research);
bool AITAC_ElectricalResearchIsAvailable(edict_t* Structure);
Vector UTIL_GetNextMinePosition(edict_t* StructureToMine);
Vector UTIL_GetNextMinePosition2(edict_t* StructureToMine);
int UTIL_GetCostOfStructureType(AvHAIDeployableStructureType StructureType);
edict_t* AITAC_GetNearestHumanAtLocation(const AvHTeamNumber Team, const Vector Location, const float MaxSearchRadius);
AvHAIDeployableStructureType UTIL_GetChamberTypeForHiveTech(AvHMessageID HiveTech);
bool AITAC_ResearchIsComplete(const AvHTeamNumber Team, const AvHTechID Research);
bool AITAC_PhaseGatesAvailable(const AvHTeamNumber Team);
int AITAC_GetNumDeadPlayersOnTeam(const AvHTeamNumber Team);
const AvHAIHiveDefinition* AITAC_GetNearestHiveUnderActiveSiege(AvHTeamNumber SiegingTeam, const Vector SearchLocation);
edict_t* AITAC_GetMarineEligibleToBuildSiege(AvHTeamNumber Team, const AvHAIHiveDefinition* Hive);
edict_t* AITAC_GetNearestHiddenPlayerInLocation(AvHTeamNumber Team, const Vector Location, const float MaxRadius);
const vector<AvHAIResourceNode*> AITAC_GetAllResourceNodes();
const vector<AvHAIResourceNode*> AITAC_GetAllReachableResourceNodes(AvHTeamNumber Team);
const vector<AvHAIHiveDefinition*> AITAC_GetAllHives();
const vector<AvHAIHiveDefinition*> AITAC_GetAllTeamHives(AvHTeamNumber Team, bool bFullyBuiltOnly);
const AvHAIHiveDefinition* AITAC_GetNearestTeamHive(AvHTeamNumber Team, const Vector SearchLocation, bool bFullyBuiltOnly);
bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius);
bool AITAC_IsAlienBuilderNeeded(AvHAIPlayer* pBot);
bool AITAC_IsAlienCapperNeeded(AvHAIPlayer* pBot);
bool AITAC_IsAlienHarasserNeeded(AvHAIPlayer* pBot);
bool AITAC_ShouldBotBuildHive(AvHAIPlayer* pBot, AvHAIHiveDefinition** EligibleHive);
AvHAIDeployableStructureType AITAC_GetNextMissingUpgradeChamberForTeam(AvHTeamNumber Team, int& NumMissing);
void AITAC_OnTeamStartsModified();
edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLocation, edict_t* SearchingPlayer, bool bIncludeGorges);
bool AITAC_IsAlienUpgradeAvailableForTeam(AvHTeamNumber Team, HiveTechStatus DesiredTech);
int AITAC_GetNumWeaponsInPlay(AvHTeamNumber Team, AvHAIWeapon WeaponType);
edict_t* AITAC_GetLastSeenLerkForTeam(AvHTeamNumber Team, float& LastSeenTime);
bool AITAC_IsCompletedStructureOfTypeNearLocation(AvHTeamNumber Team, unsigned int StructureType, Vector SearchLocation, float SearchRadius);
bool AITAC_IsStructureOfTypeNearLocation(AvHTeamNumber Team, unsigned int StructureType, Vector SearchLocation, float SearchRadius);
#endif

File diff suppressed because it is too large Load diff

121
main/source/mod/AvHAITask.h Normal file
View file

@ -0,0 +1,121 @@
#pragma once
#ifndef AVH_AI_TASK_H
#define AVH_AI_TASK_H
#include "AvHAIPlayer.h"
void AITASK_ClearAllBotTasks(AvHAIPlayer* pBot);
void AITASK_ClearBotTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsTaskCompleted(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsTaskUrgent(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsMoveTaskUrgent(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsBuildTaskUrgent(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsGuardTaskUrgent(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AITASK_OnCompleteCommanderTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AITASK_BotUpdateAndClearTasks(AvHAIPlayer* pBot);
bool AITASK_IsMoveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsTouchTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAmmoPickupTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsHealthPickupTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsEquipmentPickupTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsWeaponPickupTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsWeldTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAttackTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsResupplyTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsGuardTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsMineStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsUseTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienBuildTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsMarineBuildTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsMarineCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsDefendTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsEvolveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsMarineSecureHiveTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienGetHealthTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
bool AITASK_IsAlienHealTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
char* AITASK_TaskTypeToChar(const BotTaskType TaskType);
void AITASK_SetAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetMoveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const Vector Location, const bool bIsUrgent);
void AITASK_SetBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const AvHAIDeployableStructureType StructureType, const Vector Location, const bool bIsUrgent);
void AITASK_SetBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* StructureToBuild, const bool bIsUrgent);
void AITASK_SetCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const AvHAIResourceNode* NodeRef, const bool bIsUrgent);
void AITASK_SetDefendTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetEvolveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const Vector EvolveLocation, const AvHMessageID EvolveImpulse, const bool bIsUrgent);
void AITASK_SetEvolveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* EvolveHive, const AvHMessageID EvolveImpulse, const bool bIsUrgent);
void AITASK_SetUseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetUseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const Vector UseLocation, const bool bIsUrgent);
void AITASK_SetTouchTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, bool bIsUrgent);
void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, bool bIsUrgent);
void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const AvHAIDeployableStructureType FirstStructureType, bool bIsUrgent);
void AITASK_SetSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const Vector WaitLocation, bool bIsUrgent);
void AITASK_SetMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, bool bIsUrgent);
void AITASK_SetWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void AITASK_SetGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* HealingSource, const bool bIsUrgent);
void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressMoveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressUseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressTouchTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressGuardTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressResupplyTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressDefendTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressTakeCommandTask(AvHAIPlayer* pBot);
void BotProgressEvolveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AIPlayerBuildStructure(AvHAIPlayer* pBot, edict_t* BuildTarget);
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressGetHealthTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressHealTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void AlienProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotGuardLocation(AvHAIPlayer* pBot, const Vector GuardLocation);
void AITASK_GenerateGuardWatchPoints(AvHAIPlayer* pBot, const Vector& GuardLocation);
bool BotWithBuildTaskExists(AvHTeamNumber Team, AvHAIDeployableStructureType StructureType);
int AITASK_GetNumBotsWithBuildTask(AvHTeamNumber Team, AvHAIDeployableStructureType StructureType, edict_t* IgnorePlayer);
AvHAIPlayer* GetFirstBotWithBuildTask(AvHTeamNumber Team, AvHAIDeployableStructureType StructureType, edict_t* IgnorePlayer);
AvHAIPlayer* GetFirstBotWithReinforceTask(AvHTeamNumber Team, edict_t* ReinforceStructure, edict_t* IgnorePlayer);
void UTIL_ClearGuardInfo(AvHAIPlayer* pBot);
void BotAlienPlaceChamber(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, AvHAIDeployableStructureType DesiredStructure);
void BotAlienBuildHive(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const AvHAIHiveDefinition* HiveToBuild);
void BotAlienBuildResTower(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, const AvHAIResourceNode* NodeToCap);
void BotAlienHealTarget(AvHAIPlayer* pBot, edict_t* HealTarget);
void RegisterBotAlienBuildAttempt(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, Vector PlacementLocation, AvHAIDeployableStructureType DesiredStructure);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,78 @@
#pragma once
#ifndef AVH_AI_WEAPON_HELPER_H
#define AVH_AI_WEAPON_HELPER_H
#include "AvHAIPlayer.h"
int GetPlayerCurrentWeaponClipAmmo(const AvHPlayer* Player);
int GetPlayerCurrentWeaponMaxClipAmmo(const AvHPlayer* Player);
int GetPlayerCurrentWeaponReserveAmmo(const AvHPlayer* Player);
AvHAIWeapon GetPlayerCurrentWeapon(const AvHPlayer* Player);
AvHBasePlayerWeapon* GetPlayerCurrentWeaponReference(const AvHPlayer* Player);
AvHAIWeapon UTIL_GetPlayerPrimaryWeapon(const AvHPlayer* Player);
AvHAIWeapon UTIL_GetPlayerSecondaryWeapon(const AvHPlayer* Player);
int UTIL_GetPlayerPrimaryWeaponClipAmmo(const AvHPlayer* Player);
int UTIL_GetPlayerPrimaryWeaponMaxClipSize(const AvHPlayer* Player);
int UTIL_GetPlayerPrimaryAmmoReserve(AvHPlayer* Player);
int UTIL_GetPlayerPrimaryMaxAmmoReserve(AvHPlayer* Player);
int UTIL_GetPlayerSecondaryWeaponClipAmmo(const AvHPlayer* Player);
int UTIL_GetPlayerSecondaryWeaponMaxClipSize(const AvHPlayer* Player);
int UTIL_GetPlayerSecondaryAmmoReserve(AvHPlayer* Player);
int UTIL_GetPlayerSecondaryMaxAmmoReserve(AvHPlayer* Player);
AvHAIWeapon GetBotMarineSecondaryWeapon(const AvHAIPlayer* pBot);
int BotGetSecondaryWeaponClipAmmo(const AvHAIPlayer* pBot);
int BotGetSecondaryWeaponMaxClipSize(const AvHAIPlayer* pBot);
int BotGetSecondaryWeaponAmmoReserve(AvHAIPlayer* pBot);
int BotGetSecondaryWeaponMaxAmmoReserve(AvHAIPlayer* pBot);
float GetEnergyCostForWeapon(const AvHAIWeapon Weapon);
float GetProjectileVelocityForWeapon(const AvHAIWeapon Weapon);
float GetMaxIdealWeaponRange(const AvHAIWeapon Weapon);
float GetMinIdealWeaponRange(const AvHAIWeapon Weapon);
bool WeaponCanBeReloaded(const AvHAIWeapon CheckWeapon);
bool IsMeleeWeapon(const AvHAIWeapon Weapon);
Vector UTIL_GetGrenadeThrowTarget(edict_t* Player, const Vector TargetLocation, const float ExplosionRadius, bool bPrecise);
AvHAIWeapon BotMarineChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* target);
AvHAIWeapon BotAlienChooseBestWeaponForStructure(AvHAIPlayer* pBot, edict_t* target);
// Helper function to pick the best weapon for any given situation and target type.
AvHAIWeapon BotMarineChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target);
AvHAIWeapon BotAlienChooseBestWeapon(AvHAIPlayer* pBot, edict_t* target);
AvHAIWeapon FadeGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);
AvHAIWeapon OnosGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);
AvHAIWeapon SkulkGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);
AvHAIWeapon GorgeGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);
AvHAIWeapon LerkGetBestWeaponForCombatTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotReloadCurrentWeapon(AvHAIPlayer* pBot);
float GetReloadTimeForWeapon(AvHAIWeapon Weapon);
bool CanInterruptWeaponReload(AvHAIWeapon Weapon);
void InterruptReload(AvHAIPlayer* pBot);
bool IsHitscanWeapon(AvHAIWeapon Weapon);
float GetTimeUntilPlayerNextRefire(const AvHPlayer* Player);
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const edict_t* Target);
BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapon, const Vector TargetLocation, const edict_t* Target);
BotAttackResult PerformAttackLOSCheck(const Vector Location, const AvHAIWeapon Weapon, const edict_t* Target);
float UTIL_GetProjectileVelocityForWeapon(const AvHAIWeapon Weapon);
bool IsAreaAffectedBySpores(const Vector Location);
char* UTIL_WeaponTypeToClassname(const AvHAIWeapon WeaponType);
#endif

View file

@ -273,6 +273,15 @@ BOOL AvHAlienWeapon::IsUseable(void)
// : 991 -- added latency-based prediction for the ammount of energy available to the alien
net_status_s current_status;
gEngfuncs.pNetAPI->Status(&current_status);
/* svencoop compatibility change. Commented out to avoid conflicts with SDK TOS
net_status_s current_status;
char TestStruct[256]; // Allocate lots of memory to anticipate Svengine's larger size
gEngfuncs.pNetAPI->Status((net_status_s*)&TestStruct);
memcpy(&current_status, TestStruct, sizeof(net_status_s)); // Now copy back the original struct size. This only works if Svengine added to the struct rather than changed it completely
*/
theLatency = max(0.0f, current_status.latency);
int theNumLevels = AvHGetAlienUpgradeLevel(this->m_pPlayer->pev->iuser4, MASK_UPGRADE_5);

View file

@ -146,6 +146,8 @@ public:
virtual void SetResearching(bool inState);
virtual bool GetIsResearching() { return this->mIsResearching; }
virtual bool GetIsPersistent() const;
virtual AvHTeamNumber GetTeamNumber() const;

Some files were not shown because too many files have changed in this diff Show more