mirror of
https://github.com/UberGames/lilium-voyager.git
synced 2024-12-13 13:40:56 +00:00
3572 lines
110 KiB
C
3572 lines
110 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Foobar; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
* name: be_ai_move.c
|
|
*
|
|
* desc: bot movement AI
|
|
*
|
|
* $Archive: /MissionPack/code/botlib/be_ai_move.c $
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "../game/q_shared.h"
|
|
#include "l_memory.h"
|
|
#include "l_libvar.h"
|
|
#include "l_utils.h"
|
|
#include "l_script.h"
|
|
#include "l_precomp.h"
|
|
#include "l_struct.h"
|
|
#include "aasfile.h"
|
|
#include "../game/botlib.h"
|
|
#include "../game/be_aas.h"
|
|
#include "be_aas_funcs.h"
|
|
#include "be_interface.h"
|
|
|
|
#include "../game/be_ea.h"
|
|
#include "../game/be_ai_goal.h"
|
|
#include "../game/be_ai_move.h"
|
|
|
|
|
|
//#define DEBUG_AI_MOVE
|
|
//#define DEBUG_ELEVATOR
|
|
//#define DEBUG_GRAPPLE
|
|
|
|
// bk001204 - redundant bot_avoidspot_t, see ../game/be_ai_move.h
|
|
|
|
//movement state
|
|
//NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and
|
|
// MFL_GRAPPLEPULL must be set outside the movement code
|
|
typedef struct bot_movestate_s
|
|
{
|
|
//input vars (all set outside the movement code)
|
|
vec3_t origin; //origin of the bot
|
|
vec3_t velocity; //velocity of the bot
|
|
vec3_t viewoffset; //view offset
|
|
int entitynum; //entity number of the bot
|
|
int client; //client number of the bot
|
|
float thinktime; //time the bot thinks
|
|
int presencetype; //presencetype of the bot
|
|
vec3_t viewangles; //view angles of the bot
|
|
//state vars
|
|
int areanum; //area the bot is in
|
|
int lastareanum; //last area the bot was in
|
|
int lastgoalareanum; //last goal area number
|
|
int lastreachnum; //last reachability number
|
|
vec3_t lastorigin; //origin previous cycle
|
|
int reachareanum; //area number of the reachabilty
|
|
int moveflags; //movement flags
|
|
int jumpreach; //set when jumped
|
|
float grapplevisible_time; //last time the grapple was visible
|
|
float lastgrappledist; //last distance to the grapple end
|
|
float reachability_time; //time to use current reachability
|
|
int avoidreach[MAX_AVOIDREACH]; //reachabilities to avoid
|
|
float avoidreachtimes[MAX_AVOIDREACH]; //times to avoid the reachabilities
|
|
int avoidreachtries[MAX_AVOIDREACH]; //number of tries before avoiding
|
|
//
|
|
bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid
|
|
int numavoidspots;
|
|
} bot_movestate_t;
|
|
|
|
//used to avoid reachability links for some time after being used
|
|
#define AVOIDREACH
|
|
#define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use
|
|
#define AVOIDREACH_TRIES 4
|
|
//prediction times
|
|
#define PREDICTIONTIME_JUMP 3 //in seconds
|
|
#define PREDICTIONTIME_MOVE 2 //in seconds
|
|
//weapon indexes for weapon jumping
|
|
#define WEAPONINDEX_ROCKET_LAUNCHER 5
|
|
#define WEAPONINDEX_BFG 9
|
|
|
|
#define MODELTYPE_FUNC_PLAT 1
|
|
#define MODELTYPE_FUNC_BOB 2
|
|
#define MODELTYPE_FUNC_DOOR 3
|
|
#define MODELTYPE_FUNC_STATIC 4
|
|
|
|
libvar_t *sv_maxstep;
|
|
libvar_t *sv_maxbarrier;
|
|
libvar_t *sv_gravity;
|
|
libvar_t *weapindex_rocketlauncher;
|
|
libvar_t *weapindex_bfg10k;
|
|
libvar_t *weapindex_grapple;
|
|
libvar_t *entitytypemissile;
|
|
libvar_t *offhandgrapple;
|
|
libvar_t *cmd_grappleoff;
|
|
libvar_t *cmd_grappleon;
|
|
//type of model, func_plat or func_bobbing
|
|
int modeltypes[MAX_MODELS];
|
|
|
|
bot_movestate_t *botmovestates[MAX_CLIENTS+1];
|
|
|
|
//========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//========================================================================
|
|
int BotAllocMoveState(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i <= MAX_CLIENTS; i++)
|
|
{
|
|
if (!botmovestates[i])
|
|
{
|
|
botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t));
|
|
return i;
|
|
} //end if
|
|
} //end for
|
|
return 0;
|
|
} //end of the function BotAllocMoveState
|
|
//========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//========================================================================
|
|
void BotFreeMoveState(int handle)
|
|
{
|
|
if (handle <= 0 || handle > MAX_CLIENTS)
|
|
{
|
|
botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
|
|
return;
|
|
} //end if
|
|
if (!botmovestates[handle])
|
|
{
|
|
botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
|
|
return;
|
|
} //end if
|
|
FreeMemory(botmovestates[handle]);
|
|
botmovestates[handle] = NULL;
|
|
} //end of the function BotFreeMoveState
|
|
//========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//========================================================================
|
|
bot_movestate_t *BotMoveStateFromHandle(int handle)
|
|
{
|
|
if (handle <= 0 || handle > MAX_CLIENTS)
|
|
{
|
|
botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
|
|
return NULL;
|
|
} //end if
|
|
if (!botmovestates[handle])
|
|
{
|
|
botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
|
|
return NULL;
|
|
} //end if
|
|
return botmovestates[handle];
|
|
} //end of the function BotMoveStateFromHandle
|
|
//========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//========================================================================
|
|
void BotInitMoveState(int handle, bot_initmove_t *initmove)
|
|
{
|
|
bot_movestate_t *ms;
|
|
|
|
ms = BotMoveStateFromHandle(handle);
|
|
if (!ms) return;
|
|
VectorCopy(initmove->origin, ms->origin);
|
|
VectorCopy(initmove->velocity, ms->velocity);
|
|
VectorCopy(initmove->viewoffset, ms->viewoffset);
|
|
ms->entitynum = initmove->entitynum;
|
|
ms->client = initmove->client;
|
|
ms->thinktime = initmove->thinktime;
|
|
ms->presencetype = initmove->presencetype;
|
|
VectorCopy(initmove->viewangles, ms->viewangles);
|
|
//
|
|
ms->moveflags &= ~MFL_ONGROUND;
|
|
if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND;
|
|
ms->moveflags &= ~MFL_TELEPORTED;
|
|
if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED;
|
|
ms->moveflags &= ~MFL_WATERJUMP;
|
|
if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP;
|
|
ms->moveflags &= ~MFL_WALK;
|
|
if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK;
|
|
ms->moveflags &= ~MFL_GRAPPLEPULL;
|
|
if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL;
|
|
} //end of the function BotInitMoveState
|
|
//========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//========================================================================
|
|
float AngleDiff(float ang1, float ang2)
|
|
{
|
|
float diff;
|
|
|
|
diff = ang1 - ang2;
|
|
if (ang1 > ang2)
|
|
{
|
|
if (diff > 180.0) diff -= 360.0;
|
|
} //end if
|
|
else
|
|
{
|
|
if (diff < -180.0) diff += 360.0;
|
|
} //end else
|
|
return diff;
|
|
} //end of the function AngleDiff
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotFuzzyPointReachabilityArea(vec3_t origin)
|
|
{
|
|
int firstareanum, j, x, y, z;
|
|
int areas[10], numareas, areanum, bestareanum;
|
|
float dist, bestdist;
|
|
vec3_t points[10], v, end;
|
|
|
|
firstareanum = 0;
|
|
areanum = AAS_PointAreaNum(origin);
|
|
if (areanum)
|
|
{
|
|
firstareanum = areanum;
|
|
if (AAS_AreaReachability(areanum)) return areanum;
|
|
} //end if
|
|
VectorCopy(origin, end);
|
|
end[2] += 4;
|
|
numareas = AAS_TraceAreas(origin, end, areas, points, 10);
|
|
for (j = 0; j < numareas; j++)
|
|
{
|
|
if (AAS_AreaReachability(areas[j])) return areas[j];
|
|
} //end for
|
|
bestdist = 999999;
|
|
bestareanum = 0;
|
|
for (z = 1; z >= -1; z -= 1)
|
|
{
|
|
for (x = 1; x >= -1; x -= 1)
|
|
{
|
|
for (y = 1; y >= -1; y -= 1)
|
|
{
|
|
VectorCopy(origin, end);
|
|
end[0] += x * 8;
|
|
end[1] += y * 8;
|
|
end[2] += z * 12;
|
|
numareas = AAS_TraceAreas(origin, end, areas, points, 10);
|
|
for (j = 0; j < numareas; j++)
|
|
{
|
|
if (AAS_AreaReachability(areas[j]))
|
|
{
|
|
VectorSubtract(points[j], origin, v);
|
|
dist = VectorLength(v);
|
|
if (dist < bestdist)
|
|
{
|
|
bestareanum = areas[j];
|
|
bestdist = dist;
|
|
} //end if
|
|
} //end if
|
|
if (!firstareanum) firstareanum = areas[j];
|
|
} //end for
|
|
} //end for
|
|
} //end for
|
|
if (bestareanum) return bestareanum;
|
|
} //end for
|
|
return firstareanum;
|
|
} //end of the function BotFuzzyPointReachabilityArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotReachabilityArea(vec3_t origin, int client)
|
|
{
|
|
int modelnum, modeltype, reachnum, areanum;
|
|
aas_reachability_t reach;
|
|
vec3_t org, end, mins, maxs, up = {0, 0, 1};
|
|
bsp_trace_t bsptrace;
|
|
aas_trace_t trace;
|
|
|
|
//check if the bot is standing on something
|
|
AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
|
|
VectorMA(origin, -3, up, end);
|
|
bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
|
|
if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE)
|
|
{
|
|
//if standing on the world the bot should be in a valid area
|
|
if (bsptrace.ent == ENTITYNUM_WORLD)
|
|
{
|
|
return BotFuzzyPointReachabilityArea(origin);
|
|
} //end if
|
|
|
|
modelnum = AAS_EntityModelindex(bsptrace.ent);
|
|
modeltype = modeltypes[modelnum];
|
|
|
|
//if standing on a func_plat or func_bobbing then the bot is assumed to be
|
|
//in the area the reachability points to
|
|
if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB)
|
|
{
|
|
reachnum = AAS_NextModelReachability(0, modelnum);
|
|
if (reachnum)
|
|
{
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
return reach.areanum;
|
|
} //end if
|
|
} //end else if
|
|
|
|
//if the bot is swimming the bot should be in a valid area
|
|
if (AAS_Swimming(origin))
|
|
{
|
|
return BotFuzzyPointReachabilityArea(origin);
|
|
} //end if
|
|
//
|
|
areanum = BotFuzzyPointReachabilityArea(origin);
|
|
//if the bot is in an area with reachabilities
|
|
if (areanum && AAS_AreaReachability(areanum)) return areanum;
|
|
//trace down till the ground is hit because the bot is standing on some other entity
|
|
VectorCopy(origin, org);
|
|
VectorCopy(org, end);
|
|
end[2] -= 800;
|
|
trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1);
|
|
if (!trace.startsolid)
|
|
{
|
|
VectorCopy(trace.endpos, org);
|
|
} //end if
|
|
//
|
|
return BotFuzzyPointReachabilityArea(org);
|
|
} //end if
|
|
//
|
|
return BotFuzzyPointReachabilityArea(origin);
|
|
} //end of the function BotReachabilityArea
|
|
//===========================================================================
|
|
// returns the reachability area the bot is in
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
/*
|
|
int BotReachabilityArea(vec3_t origin, int testground)
|
|
{
|
|
int firstareanum, i, j, x, y, z;
|
|
int areas[10], numareas, areanum, bestareanum;
|
|
float dist, bestdist;
|
|
vec3_t org, end, points[10], v;
|
|
aas_trace_t trace;
|
|
|
|
firstareanum = 0;
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
VectorCopy(origin, org);
|
|
//if test at the ground (used when bot is standing on an entity)
|
|
if (i > 0)
|
|
{
|
|
VectorCopy(origin, end);
|
|
end[2] -= 800;
|
|
trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);
|
|
if (!trace.startsolid)
|
|
{
|
|
VectorCopy(trace.endpos, org);
|
|
} //end if
|
|
} //end if
|
|
|
|
firstareanum = 0;
|
|
areanum = AAS_PointAreaNum(org);
|
|
if (areanum)
|
|
{
|
|
firstareanum = areanum;
|
|
if (AAS_AreaReachability(areanum)) return areanum;
|
|
} //end if
|
|
bestdist = 999999;
|
|
bestareanum = 0;
|
|
for (z = 1; z >= -1; z -= 1)
|
|
{
|
|
for (x = 1; x >= -1; x -= 1)
|
|
{
|
|
for (y = 1; y >= -1; y -= 1)
|
|
{
|
|
VectorCopy(org, end);
|
|
end[0] += x * 8;
|
|
end[1] += y * 8;
|
|
end[2] += z * 12;
|
|
numareas = AAS_TraceAreas(org, end, areas, points, 10);
|
|
for (j = 0; j < numareas; j++)
|
|
{
|
|
if (AAS_AreaReachability(areas[j]))
|
|
{
|
|
VectorSubtract(points[j], org, v);
|
|
dist = VectorLength(v);
|
|
if (dist < bestdist)
|
|
{
|
|
bestareanum = areas[j];
|
|
bestdist = dist;
|
|
} //end if
|
|
} //end if
|
|
} //end for
|
|
} //end for
|
|
} //end for
|
|
if (bestareanum) return bestareanum;
|
|
} //end for
|
|
if (!testground) break;
|
|
} //end for
|
|
//#ifdef DEBUG
|
|
//botimport.Print(PRT_MESSAGE, "no reachability area\n");
|
|
//#endif //DEBUG
|
|
return firstareanum;
|
|
} //end of the function BotReachabilityArea*/
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach)
|
|
{
|
|
int i, modelnum;
|
|
vec3_t mins, maxs, modelorigin, org, end;
|
|
vec3_t angles = {0, 0, 0};
|
|
vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8};
|
|
bsp_trace_t trace;
|
|
|
|
modelnum = reach->facenum & 0x0000FFFF;
|
|
//get some bsp model info
|
|
AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
|
|
//
|
|
if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin))
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
|
|
return qfalse;
|
|
} //end if
|
|
//
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse;
|
|
if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse;
|
|
} //end for
|
|
//
|
|
VectorCopy(origin, org);
|
|
org[2] += 24;
|
|
VectorCopy(origin, end);
|
|
end[2] -= 48;
|
|
//
|
|
trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
|
|
if (!trace.startsolid && !trace.allsolid)
|
|
{
|
|
//NOTE: the reachability face number is the model number of the elevator
|
|
if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum)
|
|
{
|
|
return qtrue;
|
|
} //end if
|
|
} //end if
|
|
return qfalse;
|
|
} //end of the function BotOnMover
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int MoverDown(aas_reachability_t *reach)
|
|
{
|
|
int modelnum;
|
|
vec3_t mins, maxs, origin;
|
|
vec3_t angles = {0, 0, 0};
|
|
|
|
modelnum = reach->facenum & 0x0000FFFF;
|
|
//get some bsp model info
|
|
AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
|
|
//
|
|
if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
|
|
return qfalse;
|
|
} //end if
|
|
//if the top of the plat is below the reachability start point
|
|
if (origin[2] + maxs[2] < reach->start[2]) return qtrue;
|
|
return qfalse;
|
|
} //end of the function MoverDown
|
|
//========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//========================================================================
|
|
void BotSetBrushModelTypes(void)
|
|
{
|
|
int ent, modelnum;
|
|
char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
|
|
|
|
Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int));
|
|
//
|
|
for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
|
|
{
|
|
if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
|
|
if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue;
|
|
if (model[0]) modelnum = atoi(model+1);
|
|
else modelnum = 0;
|
|
|
|
if (modelnum < 0 || modelnum > MAX_MODELS)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname);
|
|
continue;
|
|
} //end if
|
|
|
|
if (!Q_stricmp(classname, "func_bobbing"))
|
|
modeltypes[modelnum] = MODELTYPE_FUNC_BOB;
|
|
else if (!Q_stricmp(classname, "func_plat"))
|
|
modeltypes[modelnum] = MODELTYPE_FUNC_PLAT;
|
|
else if (!Q_stricmp(classname, "func_door"))
|
|
modeltypes[modelnum] = MODELTYPE_FUNC_DOOR;
|
|
else if (!Q_stricmp(classname, "func_static"))
|
|
modeltypes[modelnum] = MODELTYPE_FUNC_STATIC;
|
|
} //end for
|
|
} //end of the function BotSetBrushModelTypes
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotOnTopOfEntity(bot_movestate_t *ms)
|
|
{
|
|
vec3_t mins, maxs, end, up = {0, 0, 1};
|
|
bsp_trace_t trace;
|
|
|
|
AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
|
|
VectorMA(ms->origin, -3, up, end);
|
|
trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
|
|
if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
|
|
{
|
|
return trace.ent;
|
|
} //end if
|
|
return -1;
|
|
} //end of the function BotOnTopOfEntity
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags)
|
|
{
|
|
//if the reachability uses an unwanted travel type
|
|
if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse;
|
|
//don't go into areas with bad travel types
|
|
if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse;
|
|
return qtrue;
|
|
} //end of the function BotValidTravel
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_AVOIDREACH; i++)
|
|
{
|
|
if (ms->avoidreach[i] == number)
|
|
{
|
|
if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++;
|
|
else ms->avoidreachtries[i] = 1;
|
|
ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
|
|
return;
|
|
} //end if
|
|
} //end for
|
|
//add the reachability to the reachabilities to avoid for a while
|
|
for (i = 0; i < MAX_AVOIDREACH; i++)
|
|
{
|
|
if (ms->avoidreachtimes[i] < AAS_Time())
|
|
{
|
|
ms->avoidreach[i] = number;
|
|
ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
|
|
ms->avoidreachtries[i] = 1;
|
|
return;
|
|
} //end if
|
|
} //end for
|
|
} //end of the function BotAddToAvoidReach
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2)
|
|
{
|
|
vec3_t proj, dir;
|
|
int j;
|
|
|
|
AAS_ProjectPointOntoVector(p, lp1, lp2, proj);
|
|
for (j = 0; j < 3; j++)
|
|
if ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||
|
|
(proj[j] < lp1[j] && proj[j] < lp2[j]))
|
|
break;
|
|
if (j < 3) {
|
|
if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
|
|
VectorSubtract(p, lp1, dir);
|
|
else
|
|
VectorSubtract(p, lp2, dir);
|
|
return VectorLengthSquared(dir);
|
|
}
|
|
VectorSubtract(p, proj, dir);
|
|
return VectorLengthSquared(dir);
|
|
} //end of the function DistanceFromLineSquared
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
float VectorDistanceSquared(vec3_t p1, vec3_t p2)
|
|
{
|
|
vec3_t dir;
|
|
VectorSubtract(p2, p1, dir);
|
|
return VectorLengthSquared(dir);
|
|
} //end of the function VectorDistanceSquared
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots)
|
|
{
|
|
int checkbetween, i, type;
|
|
float squareddist, squaredradius;
|
|
|
|
switch(reach->traveltype & TRAVELTYPE_MASK)
|
|
{
|
|
case TRAVEL_WALK: checkbetween = qtrue; break;
|
|
case TRAVEL_CROUCH: checkbetween = qtrue; break;
|
|
case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break;
|
|
case TRAVEL_LADDER: checkbetween = qtrue; break;
|
|
case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break;
|
|
case TRAVEL_JUMP: checkbetween = qfalse; break;
|
|
case TRAVEL_SWIM: checkbetween = qtrue; break;
|
|
case TRAVEL_WATERJUMP: checkbetween = qtrue; break;
|
|
case TRAVEL_TELEPORT: checkbetween = qfalse; break;
|
|
case TRAVEL_ELEVATOR: checkbetween = qfalse; break;
|
|
case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break;
|
|
case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break;
|
|
case TRAVEL_BFGJUMP: checkbetween = qfalse; break;
|
|
case TRAVEL_JUMPPAD: checkbetween = qfalse; break;
|
|
case TRAVEL_FUNCBOB: checkbetween = qfalse; break;
|
|
default: checkbetween = qtrue; break;
|
|
} //end switch
|
|
|
|
type = AVOID_CLEAR;
|
|
for (i = 0; i < numavoidspots; i++)
|
|
{
|
|
squaredradius = Square(avoidspots[i].radius);
|
|
squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start);
|
|
// if moving towards the avoid spot
|
|
if (squareddist < squaredradius &&
|
|
VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist)
|
|
{
|
|
type = avoidspots[i].type;
|
|
} //end if
|
|
else if (checkbetween) {
|
|
squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end);
|
|
// if moving towards the avoid spot
|
|
if (squareddist < squaredradius &&
|
|
VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
|
|
{
|
|
type = avoidspots[i].type;
|
|
} //end if
|
|
} //end if
|
|
else
|
|
{
|
|
VectorDistanceSquared(avoidspots[i].origin, reach->end);
|
|
// if the reachability leads closer to the avoid spot
|
|
if (squareddist < squaredradius &&
|
|
VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
|
|
{
|
|
type = avoidspots[i].type;
|
|
} //end if
|
|
} //end else
|
|
if (type == AVOID_ALWAYS)
|
|
return type;
|
|
} //end for
|
|
return type;
|
|
} //end of the function BotAvoidSpots
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type)
|
|
{
|
|
bot_movestate_t *ms;
|
|
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return;
|
|
if (type == AVOID_CLEAR)
|
|
{
|
|
ms->numavoidspots = 0;
|
|
return;
|
|
} //end if
|
|
|
|
if (ms->numavoidspots >= MAX_AVOIDSPOTS)
|
|
return;
|
|
VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin);
|
|
ms->avoidspots[ms->numavoidspots].radius = radius;
|
|
ms->avoidspots[ms->numavoidspots].type = type;
|
|
ms->numavoidspots++;
|
|
} //end of the function BotAddAvoidSpot
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotGetReachabilityToGoal(vec3_t origin, int areanum,
|
|
int lastgoalareanum, int lastareanum,
|
|
int *avoidreach, float *avoidreachtimes, int *avoidreachtries,
|
|
bot_goal_t *goal, int travelflags, int movetravelflags,
|
|
struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags)
|
|
{
|
|
int i, t, besttime, bestreachnum, reachnum;
|
|
aas_reachability_t reach;
|
|
|
|
//if not in a valid area
|
|
if (!areanum) return 0;
|
|
//
|
|
if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum))
|
|
{
|
|
travelflags |= TFL_DONOTENTER;
|
|
movetravelflags |= TFL_DONOTENTER;
|
|
} //end if
|
|
//use the routing to find the next area to go to
|
|
besttime = 0;
|
|
bestreachnum = 0;
|
|
//
|
|
for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum;
|
|
reachnum = AAS_NextAreaReachability(areanum, reachnum))
|
|
{
|
|
#ifdef AVOIDREACH
|
|
//check if it isn't an reachability to avoid
|
|
for (i = 0; i < MAX_AVOIDREACH; i++)
|
|
{
|
|
if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break;
|
|
} //end for
|
|
if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES)
|
|
{
|
|
#ifdef DEBUG
|
|
if (bot_developer)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]);
|
|
} //end if
|
|
#endif //DEBUG
|
|
continue;
|
|
} //end if
|
|
#endif //AVOIDREACH
|
|
//get the reachability from the number
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
//NOTE: do not go back to the previous area if the goal didn't change
|
|
//NOTE: is this actually avoidance of local routing minima between two areas???
|
|
if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue;
|
|
//if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue;
|
|
//if the travel isn't valid
|
|
if (!BotValidTravel(origin, &reach, movetravelflags)) continue;
|
|
//get the travel time
|
|
t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags);
|
|
//if the goal area isn't reachable from the reachable area
|
|
if (!t) continue;
|
|
//if the bot should not use this reachability to avoid bad spots
|
|
if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) {
|
|
if (flags) {
|
|
*flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT;
|
|
}
|
|
continue;
|
|
}
|
|
//add the travel time towards the area
|
|
t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start);
|
|
//if the travel time is better than the ones already found
|
|
if (!besttime || t < besttime)
|
|
{
|
|
besttime = t;
|
|
bestreachnum = reachnum;
|
|
} //end if
|
|
} //end for
|
|
//
|
|
return bestreachnum;
|
|
} //end of the function BotGetReachabilityToGoal
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target)
|
|
{
|
|
vec3_t dir;
|
|
float curdist;
|
|
|
|
VectorSubtract(end, start, dir);
|
|
curdist = VectorNormalize(dir);
|
|
if (*dist + curdist < maxdist)
|
|
{
|
|
VectorCopy(end, target);
|
|
*dist += curdist;
|
|
return qfalse;
|
|
} //end if
|
|
else
|
|
{
|
|
VectorMA(start, maxdist - *dist, dir, target);
|
|
*dist = maxdist;
|
|
return qtrue;
|
|
} //end else
|
|
} //end of the function BotAddToTarget
|
|
|
|
int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target)
|
|
{
|
|
aas_reachability_t reach;
|
|
int reachnum, lastareanum;
|
|
bot_movestate_t *ms;
|
|
vec3_t end;
|
|
float dist;
|
|
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return qfalse;
|
|
reachnum = 0;
|
|
//if the bot has no goal or no last reachability
|
|
if (!ms->lastreachnum || !goal) return qfalse;
|
|
|
|
reachnum = ms->lastreachnum;
|
|
VectorCopy(ms->origin, end);
|
|
lastareanum = ms->lastareanum;
|
|
dist = 0;
|
|
while(reachnum && dist < lookahead)
|
|
{
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue;
|
|
//never look beyond teleporters
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue;
|
|
//never look beyond the weapon jump point
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue;
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue;
|
|
//don't add jump pad distances
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD &&
|
|
(reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR &&
|
|
(reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB)
|
|
{
|
|
if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue;
|
|
} //end if
|
|
reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum,
|
|
ms->lastgoalareanum, lastareanum,
|
|
ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
|
|
goal, travelflags, travelflags, NULL, 0, NULL);
|
|
VectorCopy(reach.end, end);
|
|
lastareanum = reach.areanum;
|
|
if (lastareanum == goal->areanum)
|
|
{
|
|
BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target);
|
|
return qtrue;
|
|
} //end if
|
|
} //end while
|
|
//
|
|
return qfalse;
|
|
} //end of the function BotMovementViewTarget
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotVisible(int ent, vec3_t eye, vec3_t target)
|
|
{
|
|
bsp_trace_t trace;
|
|
|
|
trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
|
|
if (trace.fraction >= 1) return qtrue;
|
|
return qfalse;
|
|
} //end of the function BotVisible
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target)
|
|
{
|
|
aas_reachability_t reach;
|
|
int reachnum, lastgoalareanum, lastareanum, i;
|
|
int avoidreach[MAX_AVOIDREACH];
|
|
float avoidreachtimes[MAX_AVOIDREACH];
|
|
int avoidreachtries[MAX_AVOIDREACH];
|
|
vec3_t end;
|
|
|
|
//if the bot has no goal or no last reachability
|
|
if (!goal) return qfalse;
|
|
//if the areanum is not valid
|
|
if (!areanum) return qfalse;
|
|
//if the goal areanum is not valid
|
|
if (!goal->areanum) return qfalse;
|
|
|
|
Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
|
|
lastgoalareanum = goal->areanum;
|
|
lastareanum = areanum;
|
|
VectorCopy(origin, end);
|
|
//only do 20 hops
|
|
for (i = 0; i < 20 && (areanum != goal->areanum); i++)
|
|
{
|
|
//
|
|
reachnum = BotGetReachabilityToGoal(end, areanum,
|
|
lastgoalareanum, lastareanum,
|
|
avoidreach, avoidreachtimes, avoidreachtries,
|
|
goal, travelflags, travelflags, NULL, 0, NULL);
|
|
if (!reachnum) return qfalse;
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
//
|
|
if (BotVisible(goal->entitynum, goal->origin, reach.start))
|
|
{
|
|
VectorCopy(reach.start, target);
|
|
return qtrue;
|
|
} //end if
|
|
//
|
|
if (BotVisible(goal->entitynum, goal->origin, reach.end))
|
|
{
|
|
VectorCopy(reach.end, target);
|
|
return qtrue;
|
|
} //end if
|
|
//
|
|
if (reach.areanum == goal->areanum)
|
|
{
|
|
VectorCopy(reach.end, target);
|
|
return qtrue;
|
|
} //end if
|
|
//
|
|
lastareanum = areanum;
|
|
areanum = reach.areanum;
|
|
VectorCopy(reach.end, end);
|
|
//
|
|
} //end while
|
|
//
|
|
return qfalse;
|
|
} //end of the function BotPredictVisiblePosition
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter)
|
|
{
|
|
int modelnum;
|
|
vec3_t mins, maxs, origin, mids;
|
|
vec3_t angles = {0, 0, 0};
|
|
|
|
modelnum = reach->facenum & 0x0000FFFF;
|
|
//get some bsp model info
|
|
AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
|
|
//
|
|
if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
|
|
} //end if
|
|
//get a point just above the plat in the bottom position
|
|
VectorAdd(mins, maxs, mids);
|
|
VectorMA(origin, 0.5, mids, bottomcenter);
|
|
bottomcenter[2] = reach->start[2];
|
|
} //end of the function MoverBottomCenter
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum)
|
|
{
|
|
float dist, startz;
|
|
vec3_t start, end;
|
|
aas_trace_t trace;
|
|
|
|
//do gap checking
|
|
startz = origin[2];
|
|
//this enables walking down stairs more fluidly
|
|
{
|
|
VectorCopy(origin, start);
|
|
VectorCopy(origin, end);
|
|
end[2] -= 60;
|
|
trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
|
|
if (trace.fraction >= 1) return 1;
|
|
startz = trace.endpos[2] + 1;
|
|
}
|
|
//
|
|
for (dist = 8; dist <= 100; dist += 8)
|
|
{
|
|
VectorMA(origin, dist, hordir, start);
|
|
start[2] = startz + 24;
|
|
VectorCopy(start, end);
|
|
end[2] -= 48 + sv_maxbarrier->value;
|
|
trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
|
|
//if solid is found the bot can't walk any further and fall into a gap
|
|
if (!trace.startsolid)
|
|
{
|
|
//if it is a gap
|
|
if (trace.endpos[2] < startz - sv_maxstep->value - 8)
|
|
{
|
|
VectorCopy(trace.endpos, end);
|
|
end[2] -= 20;
|
|
if (AAS_PointContents(end) & CONTENTS_WATER) break;
|
|
//if a gap is found slow down
|
|
//botimport.Print(PRT_MESSAGE, "gap at %f\n", dist);
|
|
return dist;
|
|
} //end if
|
|
startz = trace.endpos[2];
|
|
} //end if
|
|
} //end for
|
|
return 0;
|
|
} //end of the function BotGapDistance
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed)
|
|
{
|
|
vec3_t start, hordir, end;
|
|
aas_trace_t trace;
|
|
|
|
VectorCopy(ms->origin, end);
|
|
end[2] += sv_maxbarrier->value;
|
|
//trace right up
|
|
trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum);
|
|
//this shouldn't happen... but we check anyway
|
|
if (trace.startsolid) return qfalse;
|
|
//if very low ceiling it isn't possible to jump up to a barrier
|
|
if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
|
|
//
|
|
hordir[0] = dir[0];
|
|
hordir[1] = dir[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end);
|
|
VectorCopy(trace.endpos, start);
|
|
end[2] = trace.endpos[2];
|
|
//trace from previous trace end pos horizontally in the move direction
|
|
trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
|
|
//again this shouldn't happen
|
|
if (trace.startsolid) return qfalse;
|
|
//
|
|
VectorCopy(trace.endpos, start);
|
|
VectorCopy(trace.endpos, end);
|
|
end[2] = ms->origin[2];
|
|
//trace down from the previous trace end pos
|
|
trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
|
|
//if solid
|
|
if (trace.startsolid) return qfalse;
|
|
//if no obstacle at all
|
|
if (trace.fraction >= 1.0) return qfalse;
|
|
//if less than the maximum step height
|
|
if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
|
|
//
|
|
EA_Jump(ms->client);
|
|
EA_Move(ms->client, hordir, speed);
|
|
ms->moveflags |= MFL_BARRIERJUMP;
|
|
//there is a barrier
|
|
return qtrue;
|
|
} //end of the function BotCheckBarrierJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
|
|
{
|
|
vec3_t normdir;
|
|
|
|
VectorCopy(dir, normdir);
|
|
VectorNormalize(normdir);
|
|
EA_Move(ms->client, normdir, speed);
|
|
return qtrue;
|
|
} //end of the function BotSwimInDirection
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
|
|
{
|
|
vec3_t hordir, cmdmove, velocity, tmpdir, origin;
|
|
int presencetype, maxframes, cmdframes, stopevent;
|
|
aas_clientmove_t move;
|
|
float dist;
|
|
|
|
if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
|
|
//if the bot is on the ground
|
|
if (ms->moveflags & MFL_ONGROUND)
|
|
{
|
|
//if there is a barrier the bot can jump on
|
|
if (BotCheckBarrierJump(ms, dir, speed)) return qtrue;
|
|
//remove barrier jump flag
|
|
ms->moveflags &= ~MFL_BARRIERJUMP;
|
|
//get the presence type for the movement
|
|
if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH;
|
|
else presencetype = PRESENCE_NORMAL;
|
|
//horizontal direction
|
|
hordir[0] = dir[0];
|
|
hordir[1] = dir[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//if the bot is not supposed to jump
|
|
if (!(type & MOVE_JUMP))
|
|
{
|
|
//if there is a gap, try to jump over it
|
|
if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP;
|
|
} //end if
|
|
//get command movement
|
|
VectorScale(hordir, speed, cmdmove);
|
|
VectorCopy(ms->velocity, velocity);
|
|
//
|
|
if (type & MOVE_JUMP)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "trying jump\n");
|
|
cmdmove[2] = 400;
|
|
maxframes = PREDICTIONTIME_JUMP / 0.1;
|
|
cmdframes = 1;
|
|
stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE|
|
|
SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
|
|
} //end if
|
|
else
|
|
{
|
|
maxframes = 2;
|
|
cmdframes = 2;
|
|
stopevent = SE_HITGROUNDDAMAGE|
|
|
SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
|
|
} //end else
|
|
//AAS_ClearShownDebugLines();
|
|
//
|
|
VectorCopy(ms->origin, origin);
|
|
origin[2] += 0.5;
|
|
AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue,
|
|
velocity, cmdmove, cmdframes, maxframes, 0.1f,
|
|
stopevent, 0, qfalse);//qtrue);
|
|
//if prediction time wasn't enough to fully predict the movement
|
|
if (move.frames >= maxframes && (type & MOVE_JUMP))
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client);
|
|
return qfalse;
|
|
} //end if
|
|
//don't enter slime or lava and don't fall from too high
|
|
if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client);
|
|
//if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n");
|
|
//if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n");
|
|
//if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n");
|
|
return qfalse;
|
|
} //end if
|
|
//if ground was hit
|
|
if (move.stopevent & SE_HITGROUND)
|
|
{
|
|
//check for nearby gap
|
|
VectorNormalize2(move.velocity, tmpdir);
|
|
dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum);
|
|
if (dist > 0) return qfalse;
|
|
//
|
|
dist = BotGapDistance(move.endpos, hordir, ms->entitynum);
|
|
if (dist > 0) return qfalse;
|
|
} //end if
|
|
//get horizontal movement
|
|
tmpdir[0] = move.endpos[0] - ms->origin[0];
|
|
tmpdir[1] = move.endpos[1] - ms->origin[1];
|
|
tmpdir[2] = 0;
|
|
//
|
|
//AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE);
|
|
//the bot is blocked by something
|
|
if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse;
|
|
//perform the movement
|
|
if (type & MOVE_JUMP) EA_Jump(ms->client);
|
|
if (type & MOVE_CROUCH) EA_Crouch(ms->client);
|
|
EA_Move(ms->client, hordir, speed);
|
|
//movement was succesfull
|
|
return qtrue;
|
|
} //end if
|
|
else
|
|
{
|
|
if (ms->moveflags & MFL_BARRIERJUMP)
|
|
{
|
|
//if near the top or going down
|
|
if (ms->velocity[2] < 50)
|
|
{
|
|
EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
} //end if
|
|
//FIXME: do air control to avoid hazards
|
|
return qtrue;
|
|
} //end else
|
|
} //end of the function BotWalkInDirection
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type)
|
|
{
|
|
bot_movestate_t *ms;
|
|
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return qfalse;
|
|
//if swimming
|
|
if (AAS_Swimming(ms->origin))
|
|
{
|
|
return BotSwimInDirection(ms, dir, speed, type);
|
|
} //end if
|
|
else
|
|
{
|
|
return BotWalkInDirection(ms, dir, speed, type);
|
|
} //end else
|
|
} //end of the function BotMoveInDirection
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out)
|
|
{
|
|
float x1, dx1, dy1, x2, dx2, dy2, d;
|
|
|
|
dx1 = p2[0] - p1[0];
|
|
dy1 = p2[1] - p1[1];
|
|
dx2 = p4[0] - p3[0];
|
|
dy2 = p4[1] - p3[1];
|
|
|
|
d = dy1 * dx2 - dx1 * dy2;
|
|
if (d != 0)
|
|
{
|
|
x1 = p1[1] * dx1 - p1[0] * dy1;
|
|
x2 = p3[1] * dx2 - p3[0] * dy2;
|
|
out[0] = (int) ((dx1 * x2 - dx2 * x1) / d);
|
|
out[1] = (int) ((dy1 * x2 - dy2 * x1) / d);
|
|
return qtrue;
|
|
} //end if
|
|
else
|
|
{
|
|
return qfalse;
|
|
} //end else
|
|
} //end of the function Intersection
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result)
|
|
{
|
|
vec3_t mins, maxs, end, up = {0, 0, 1};
|
|
bsp_trace_t trace;
|
|
|
|
//test for entities obstructing the bot's path
|
|
AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
|
|
//
|
|
if (fabs(DotProduct(dir, up)) < 0.7)
|
|
{
|
|
mins[2] += sv_maxstep->value; //if the bot can step on
|
|
maxs[2] -= 10; //a little lower to avoid low ceiling
|
|
} //end if
|
|
VectorMA(ms->origin, 3, dir, end);
|
|
trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY);
|
|
//if not started in solid and not hitting the world entity
|
|
if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
|
|
{
|
|
result->blocked = qtrue;
|
|
result->blockentity = trace.ent;
|
|
#ifdef DEBUG
|
|
//botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client);
|
|
#endif //DEBUG
|
|
} //end if
|
|
//if not in an area with reachability
|
|
else if (checkbottom && !AAS_AreaReachability(ms->areanum))
|
|
{
|
|
//check if the bot is standing on something
|
|
AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
|
|
VectorMA(ms->origin, -3, up, end);
|
|
trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
|
|
if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
|
|
{
|
|
result->blocked = qtrue;
|
|
result->blockentity = trace.ent;
|
|
result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
|
|
#ifdef DEBUG
|
|
//botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client);
|
|
#endif //DEBUG
|
|
} //end if
|
|
} //end else
|
|
} //end of the function BotCheckBlocked
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
float dist, speed;
|
|
vec3_t hordir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//first walk straight to the reachability start
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
//
|
|
if (dist < 10)
|
|
{
|
|
//walk straight to the reachability end
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
} //end if
|
|
//if going towards a crouch area
|
|
if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL))
|
|
{
|
|
//if pretty close to the reachable area
|
|
if (dist < 20) EA_Crouch(ms->client);
|
|
} //end if
|
|
//
|
|
dist = BotGapDistance(ms->origin, hordir, ms->entitynum);
|
|
//
|
|
if (ms->moveflags & MFL_WALK)
|
|
{
|
|
if (dist > 0) speed = 200 - (180 - 1 * dist);
|
|
else speed = 200;
|
|
EA_Walk(ms->client);
|
|
} //end if
|
|
else
|
|
{
|
|
if (dist > 0) speed = 400 - (360 - 2 * dist);
|
|
else speed = 400;
|
|
} //end else
|
|
//elemantary action move in direction
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Walk
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir;
|
|
float dist, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
//if not on the ground and changed areas... don't walk back!!
|
|
//(doesn't seem to help)
|
|
/*
|
|
ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
|
|
if (ms->areanum == reach->areanum)
|
|
{
|
|
#ifdef DEBUG
|
|
botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n");
|
|
#endif //DEBUG
|
|
return result;
|
|
} //end if*/
|
|
//go straight to the reachability end
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
if (dist > 100) dist = 100;
|
|
speed = 400 - (400 - 3 * dist);
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_Walk
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
float speed;
|
|
vec3_t hordir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
speed = 400;
|
|
//walk straight to reachability end
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
//elemantary actions
|
|
EA_Crouch(ms->client);
|
|
EA_Move(ms->client, hordir, speed);
|
|
//
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Crouch
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
float dist, speed;
|
|
vec3_t hordir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//walk straight to reachability start
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
//if pretty close to the barrier
|
|
if (dist < 9)
|
|
{
|
|
EA_Jump(ms->client);
|
|
} //end if
|
|
else
|
|
{
|
|
if (dist > 60) dist = 60;
|
|
speed = 360 - (360 - 6 * dist);
|
|
EA_Move(ms->client, hordir, speed);
|
|
} //end else
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_BarrierJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
float dist;
|
|
vec3_t hordir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//if near the top or going down
|
|
if (ms->velocity[2] < 250)
|
|
{
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
//
|
|
EA_Move(ms->client, hordir, 400);
|
|
VectorCopy(hordir, result.movedir);
|
|
} //end if
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_BarrierJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t dir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//swim straight to reachability end
|
|
VectorSubtract(reach->start, ms->origin, dir);
|
|
VectorNormalize(dir);
|
|
//
|
|
BotCheckBlocked(ms, dir, qtrue, &result);
|
|
//elemantary actions
|
|
EA_Move(ms->client, dir, 400);
|
|
//
|
|
VectorCopy(dir, result.movedir);
|
|
Vector2Angles(dir, result.ideal_viewangles);
|
|
result.flags |= MOVERESULT_SWIMVIEW;
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Swim
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t dir, hordir;
|
|
float dist;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//swim straight to reachability end
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
VectorCopy(dir, hordir);
|
|
hordir[2] = 0;
|
|
dir[2] += 15 + crandom() * 40;
|
|
//botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]);
|
|
VectorNormalize(dir);
|
|
dist = VectorNormalize(hordir);
|
|
//elemantary actions
|
|
//EA_Move(ms->client, dir, 400);
|
|
EA_MoveForward(ms->client);
|
|
//move up if close to the actual out of water jump spot
|
|
if (dist < 40) EA_MoveUp(ms->client);
|
|
//set the ideal view angles
|
|
Vector2Angles(dir, result.ideal_viewangles);
|
|
result.flags |= MOVERESULT_MOVEMENTVIEW;
|
|
//
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_WaterJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t dir, pnt;
|
|
float dist;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n");
|
|
//if waterjumping there's nothing to do
|
|
if (ms->moveflags & MFL_WATERJUMP) return result;
|
|
//if not touching any water anymore don't do anything
|
|
//otherwise the bot sometimes keeps jumping?
|
|
VectorCopy(ms->origin, pnt);
|
|
pnt[2] -= 32; //extra for q2dm4 near red armor/mega health
|
|
if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result;
|
|
//swim straight to reachability end
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
dir[0] += crandom() * 10;
|
|
dir[1] += crandom() * 10;
|
|
dir[2] += 70 + crandom() * 10;
|
|
dist = VectorNormalize(dir);
|
|
//elemantary actions
|
|
EA_Move(ms->client, dir, 400);
|
|
//set the ideal view angles
|
|
Vector2Angles(dir, result.ideal_viewangles);
|
|
result.flags |= MOVERESULT_MOVEMENTVIEW;
|
|
//
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_WaterJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir, dir;
|
|
float dist, speed, reachhordist;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//check if the bot is blocked by anything
|
|
VectorSubtract(reach->start, ms->origin, dir);
|
|
VectorNormalize(dir);
|
|
BotCheckBlocked(ms, dir, qtrue, &result);
|
|
//if the reachability start and end are practially above each other
|
|
VectorSubtract(reach->end, reach->start, dir);
|
|
dir[2] = 0;
|
|
reachhordist = VectorLength(dir);
|
|
//walk straight to the reachability start
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//if pretty close to the start focus on the reachability end
|
|
if (dist < 48)
|
|
{
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//
|
|
if (reachhordist < 20)
|
|
{
|
|
speed = 100;
|
|
} //end if
|
|
else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed))
|
|
{
|
|
speed = 400;
|
|
} //end if
|
|
} //end if
|
|
else
|
|
{
|
|
if (reachhordist < 20)
|
|
{
|
|
if (dist > 64) dist = 64;
|
|
speed = 400 - (256 - 4 * dist);
|
|
} //end if
|
|
else
|
|
{
|
|
speed = 400;
|
|
} //end else
|
|
} //end else
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
//elemantary action
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_WalkOffLedge
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed)
|
|
{
|
|
vec3_t org, vel;
|
|
float dist;
|
|
int i;
|
|
|
|
VectorCopy(origin, org);
|
|
VectorScale(velocity, 0.1, vel);
|
|
for (i = 0; i < 50; i++)
|
|
{
|
|
vel[2] -= sv_gravity->value * 0.01;
|
|
//if going down and next position would be below the goal
|
|
if (vel[2] < 0 && org[2] + vel[2] < goal[2])
|
|
{
|
|
VectorScale(vel, (goal[2] - org[2]) / vel[2], vel);
|
|
VectorAdd(org, vel, org);
|
|
VectorSubtract(goal, org, dir);
|
|
dist = VectorNormalize(dir);
|
|
if (dist > 32) dist = 32;
|
|
*speed = 400 - (400 - 13 * dist);
|
|
return qtrue;
|
|
} //end if
|
|
else
|
|
{
|
|
VectorAdd(org, vel, org);
|
|
} //end else
|
|
} //end for
|
|
VectorSet(dir, 0, 0, 0);
|
|
*speed = 400;
|
|
return qfalse;
|
|
} //end of the function BotAirControl
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t dir, hordir, end, v;
|
|
float dist, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
BotCheckBlocked(ms, dir, qtrue, &result);
|
|
//
|
|
VectorSubtract(reach->end, ms->origin, v);
|
|
v[2] = 0;
|
|
dist = VectorNormalize(v);
|
|
if (dist > 16) VectorMA(reach->end, 16, v, end);
|
|
else VectorCopy(reach->end, end);
|
|
//
|
|
if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed))
|
|
{
|
|
//go straight to the reachability end
|
|
VectorCopy(dir, hordir);
|
|
hordir[2] = 0;
|
|
//
|
|
dist = VectorNormalize(hordir);
|
|
speed = 400;
|
|
} //end if
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_WalkOffLedge
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
/*
|
|
bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir;
|
|
float dist, gapdist, speed, horspeed, sv_jumpvel;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
sv_jumpvel = botlibglobals.sv_jumpvel->value;
|
|
//walk straight to the reachability start
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
speed = 350;
|
|
//
|
|
gapdist = BotGapDistance(ms, hordir, ms->entitynum);
|
|
//if pretty close to the start focus on the reachability end
|
|
if (dist < 50 || (gapdist && gapdist < 50))
|
|
{
|
|
//NOTE: using max speed (400) works best
|
|
//if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed))
|
|
//{
|
|
// speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;
|
|
//} //end if
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
VectorNormalize(hordir);
|
|
//elemantary action jump
|
|
EA_Jump(ms->client);
|
|
//
|
|
ms->jumpreach = ms->lastreachnum;
|
|
speed = 600;
|
|
} //end if
|
|
else
|
|
{
|
|
if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed))
|
|
{
|
|
speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;
|
|
} //end if
|
|
} //end else
|
|
//elemantary action
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Jump*/
|
|
/*
|
|
bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir, dir1, dir2, mins, maxs, start, end;
|
|
float dist1, dist2, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
bsp_trace_t trace;
|
|
|
|
//
|
|
hordir[0] = reach->start[0] - reach->end[0];
|
|
hordir[1] = reach->start[1] - reach->end[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//
|
|
VectorCopy(reach->start, start);
|
|
start[2] += 1;
|
|
//minus back the bouding box size plus 16
|
|
VectorMA(reach->start, 80, hordir, end);
|
|
//
|
|
AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs);
|
|
//check for solids
|
|
trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID);
|
|
if (trace.startsolid) VectorCopy(start, trace.endpos);
|
|
//check for a gap
|
|
for (dist1 = 0; dist1 < 80; dist1 += 10)
|
|
{
|
|
VectorMA(start, dist1+10, hordir, end);
|
|
end[2] += 1;
|
|
if (AAS_PointAreaNum(end) != ms->reachareanum) break;
|
|
} //end for
|
|
if (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos);
|
|
// dist1 = BotGapDistance(start, hordir, ms->entitynum);
|
|
// if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos);
|
|
//
|
|
VectorSubtract(ms->origin, reach->start, dir1);
|
|
dir1[2] = 0;
|
|
dist1 = VectorNormalize(dir1);
|
|
VectorSubtract(ms->origin, trace.endpos, dir2);
|
|
dir2[2] = 0;
|
|
dist2 = VectorNormalize(dir2);
|
|
//if just before the reachability start
|
|
if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "between jump start and run to point\n");
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//elemantary action jump
|
|
if (dist1 < 24) EA_Jump(ms->client);
|
|
else if (dist1 < 32) EA_DelayedJump(ms->client);
|
|
EA_Move(ms->client, hordir, 600);
|
|
//
|
|
ms->jumpreach = ms->lastreachnum;
|
|
} //end if
|
|
else
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "going towards run to point\n");
|
|
hordir[0] = trace.endpos[0] - ms->origin[0];
|
|
hordir[1] = trace.endpos[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//
|
|
if (dist2 > 80) dist2 = 80;
|
|
speed = 400 - (400 - 5 * dist2);
|
|
EA_Move(ms->client, hordir, speed);
|
|
} //end else
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Jump*/
|
|
//*
|
|
bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir, dir1, dir2, start, end, runstart;
|
|
// vec3_t runstart, dir1, dir2, hordir;
|
|
float dist1, dist2, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
AAS_JumpReachRunStart(reach, runstart);
|
|
//*
|
|
hordir[0] = runstart[0] - reach->start[0];
|
|
hordir[1] = runstart[1] - reach->start[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//
|
|
VectorCopy(reach->start, start);
|
|
start[2] += 1;
|
|
VectorMA(reach->start, 80, hordir, runstart);
|
|
//check for a gap
|
|
for (dist1 = 0; dist1 < 80; dist1 += 10)
|
|
{
|
|
VectorMA(start, dist1+10, hordir, end);
|
|
end[2] += 1;
|
|
if (AAS_PointAreaNum(end) != ms->reachareanum) break;
|
|
} //end for
|
|
if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart);
|
|
//
|
|
VectorSubtract(ms->origin, reach->start, dir1);
|
|
dir1[2] = 0;
|
|
dist1 = VectorNormalize(dir1);
|
|
VectorSubtract(ms->origin, runstart, dir2);
|
|
dir2[2] = 0;
|
|
dist2 = VectorNormalize(dir2);
|
|
//if just before the reachability start
|
|
if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
|
|
{
|
|
// botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//elemantary action jump
|
|
if (dist1 < 24) EA_Jump(ms->client);
|
|
else if (dist1 < 32) EA_DelayedJump(ms->client);
|
|
EA_Move(ms->client, hordir, 600);
|
|
//
|
|
ms->jumpreach = ms->lastreachnum;
|
|
} //end if
|
|
else
|
|
{
|
|
// botimport.Print(PRT_MESSAGE, "going towards run start point\n");
|
|
hordir[0] = runstart[0] - ms->origin[0];
|
|
hordir[1] = runstart[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//
|
|
if (dist2 > 80) dist2 = 80;
|
|
speed = 400 - (400 - 5 * dist2);
|
|
EA_Move(ms->client, hordir, speed);
|
|
} //end else
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Jump*/
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir, hordir2;
|
|
float speed, dist;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//if not jumped yet
|
|
if (!ms->jumpreach) return result;
|
|
//go straight to the reachability end
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
hordir2[0] = reach->end[0] - reach->start[0];
|
|
hordir2[1] = reach->end[1] - reach->start[1];
|
|
hordir2[2] = 0;
|
|
VectorNormalize(hordir2);
|
|
//
|
|
if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result;
|
|
//always use max speed when traveling through the air
|
|
speed = 800;
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_Jump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
//float dist, speed;
|
|
vec3_t dir, viewdir;//, hordir;
|
|
vec3_t origin = {0, 0, 0};
|
|
// vec3_t up = {0, 0, 1};
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
// if ((ms->moveflags & MFL_AGAINSTLADDER))
|
|
//NOTE: not a good idea for ladders starting in water
|
|
// || !(ms->moveflags & MFL_ONGROUND))
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n");
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
VectorNormalize(dir);
|
|
//set the ideal view angles, facing the ladder up or down
|
|
viewdir[0] = dir[0];
|
|
viewdir[1] = dir[1];
|
|
viewdir[2] = 3 * dir[2];
|
|
Vector2Angles(viewdir, result.ideal_viewangles);
|
|
//elemantary action
|
|
EA_Move(ms->client, origin, 0);
|
|
EA_MoveForward(ms->client);
|
|
//set movement view flag so the AI can see the view is focussed
|
|
result.flags |= MOVERESULT_MOVEMENTVIEW;
|
|
} //end if
|
|
/* else
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "moving towards ladder\n");
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
//make sure the horizontal movement is large anough
|
|
VectorCopy(dir, hordir);
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
dir[0] = hordir[0];
|
|
dir[1] = hordir[1];
|
|
if (dir[2] > 0) dir[2] = 1;
|
|
else dir[2] = -1;
|
|
if (dist > 50) dist = 50;
|
|
speed = 400 - (200 - 4 * dist);
|
|
EA_Move(ms->client, dir, speed);
|
|
} //end else*/
|
|
//save the movement direction
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_Ladder
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir;
|
|
float dist;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//if the bot is being teleported
|
|
if (ms->moveflags & MFL_TELEPORTED) return result;
|
|
|
|
//walk straight to center of the teleporter
|
|
VectorSubtract(reach->start, ms->origin, hordir);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
|
|
if (dist < 30) EA_Move(ms->client, hordir, 200);
|
|
else EA_Move(ms->client, hordir, 400);
|
|
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
|
|
VectorCopy(hordir, result.movedir);
|
|
return result;
|
|
} //end of the function BotTravel_Teleport
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t dir, dir1, dir2, hordir, bottomcenter;
|
|
float dist, dist1, dist2, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//if standing on the plat
|
|
if (BotOnMover(ms->origin, ms->entitynum, reach))
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "bot on elevator\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
//if vertically not too far from the end point
|
|
if (abs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value)
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "bot moving to end\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
//move to the end point
|
|
VectorSubtract(reach->end, ms->origin, hordir);
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
if (!BotCheckBarrierJump(ms, hordir, 100))
|
|
{
|
|
EA_Move(ms->client, hordir, 400);
|
|
} //end if
|
|
VectorCopy(hordir, result.movedir);
|
|
} //end else
|
|
//if not really close to the center of the elevator
|
|
else
|
|
{
|
|
MoverBottomCenter(reach, bottomcenter);
|
|
VectorSubtract(bottomcenter, ms->origin, hordir);
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
if (dist > 10)
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "bot moving to center\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
//move to the center of the plat
|
|
if (dist > 100) dist = 100;
|
|
speed = 400 - (400 - 4 * dist);
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
} //end if
|
|
} //end else
|
|
} //end if
|
|
else
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "bot not on elevator\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
//if very near the reachability end
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
dist = VectorLength(dir);
|
|
if (dist < 64)
|
|
{
|
|
if (dist > 60) dist = 60;
|
|
speed = 360 - (360 - 6 * dist);
|
|
//
|
|
if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
|
|
{
|
|
if (speed > 5) EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
//stop using this reachability
|
|
ms->reachability_time = 0;
|
|
return result;
|
|
} //end if
|
|
//get direction and distance to reachability start
|
|
VectorSubtract(reach->start, ms->origin, dir1);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
|
|
dist1 = VectorNormalize(dir1);
|
|
//if the elevator isn't down
|
|
if (!MoverDown(reach))
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "elevator not down\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
dist = dist1;
|
|
VectorCopy(dir1, dir);
|
|
//
|
|
BotCheckBlocked(ms, dir, qfalse, &result);
|
|
//
|
|
if (dist > 60) dist = 60;
|
|
speed = 360 - (360 - 6 * dist);
|
|
//
|
|
if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
|
|
{
|
|
if (speed > 5) EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
//this isn't a failure... just wait till the elevator comes down
|
|
result.type = RESULTTYPE_ELEVATORUP;
|
|
result.flags |= MOVERESULT_WAITING;
|
|
return result;
|
|
} //end if
|
|
//get direction and distance to elevator bottom center
|
|
MoverBottomCenter(reach, bottomcenter);
|
|
VectorSubtract(bottomcenter, ms->origin, dir2);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
|
|
dist2 = VectorNormalize(dir2);
|
|
//if very close to the reachability start or
|
|
//closer to the elevator center or
|
|
//between reachability start and elevator center
|
|
if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "bot moving to center\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
dist = dist2;
|
|
VectorCopy(dir2, dir);
|
|
} //end if
|
|
else //closer to the reachability start
|
|
{
|
|
#ifdef DEBUG_ELEVATOR
|
|
botimport.Print(PRT_MESSAGE, "bot moving to start\n");
|
|
#endif //DEBUG_ELEVATOR
|
|
dist = dist1;
|
|
VectorCopy(dir1, dir);
|
|
} //end else
|
|
//
|
|
BotCheckBlocked(ms, dir, qfalse, &result);
|
|
//
|
|
if (dist > 60) dist = 60;
|
|
speed = 400 - (400 - 6 * dist);
|
|
//
|
|
if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
|
|
{
|
|
EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
} //end else
|
|
return result;
|
|
} //end of the function BotTravel_Elevator
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t bottomcenter, bottomdir, topdir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
MoverBottomCenter(reach, bottomcenter);
|
|
VectorSubtract(bottomcenter, ms->origin, bottomdir);
|
|
//
|
|
VectorSubtract(reach->end, ms->origin, topdir);
|
|
//
|
|
if (fabs(bottomdir[2]) < fabs(topdir[2]))
|
|
{
|
|
VectorNormalize(bottomdir);
|
|
EA_Move(ms->client, bottomdir, 300);
|
|
} //end if
|
|
else
|
|
{
|
|
VectorNormalize(topdir);
|
|
EA_Move(ms->client, topdir, 300);
|
|
} //end else
|
|
return result;
|
|
} //end of the function BotFinishTravel_Elevator
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin)
|
|
{
|
|
int spawnflags, modelnum;
|
|
vec3_t mins, maxs, mid, angles = {0, 0, 0};
|
|
int num0, num1;
|
|
|
|
modelnum = reach->facenum & 0x0000FFFF;
|
|
if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum);
|
|
VectorSet(start, 0, 0, 0);
|
|
VectorSet(end, 0, 0, 0);
|
|
return;
|
|
} //end if
|
|
AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
|
|
VectorAdd(mins, maxs, mid);
|
|
VectorScale(mid, 0.5, mid);
|
|
VectorCopy(mid, start);
|
|
VectorCopy(mid, end);
|
|
spawnflags = reach->facenum >> 16;
|
|
num0 = reach->edgenum >> 16;
|
|
if (num0 > 0x00007FFF) num0 |= 0xFFFF0000;
|
|
num1 = reach->edgenum & 0x0000FFFF;
|
|
if (num1 > 0x00007FFF) num1 |= 0xFFFF0000;
|
|
if (spawnflags & 1)
|
|
{
|
|
start[0] = num0;
|
|
end[0] = num1;
|
|
//
|
|
origin[0] += mid[0];
|
|
origin[1] = mid[1];
|
|
origin[2] = mid[2];
|
|
} //end if
|
|
else if (spawnflags & 2)
|
|
{
|
|
start[1] = num0;
|
|
end[1] = num1;
|
|
//
|
|
origin[0] = mid[0];
|
|
origin[1] += mid[1];
|
|
origin[2] = mid[2];
|
|
} //end else if
|
|
else
|
|
{
|
|
start[2] = num0;
|
|
end[2] = num1;
|
|
//
|
|
origin[0] = mid[0];
|
|
origin[1] = mid[1];
|
|
origin[2] += mid[2];
|
|
} //end else
|
|
} //end of the function BotFuncBobStartEnd
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin;
|
|
float dist, dist1, dist2, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//
|
|
BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
|
|
//if standing ontop of the func_bobbing
|
|
if (BotOnMover(ms->origin, ms->entitynum, reach))
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n");
|
|
#endif
|
|
//if near end point of reachability
|
|
VectorSubtract(bob_origin, bob_end, dir);
|
|
if (VectorLength(dir) < 24)
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n");
|
|
#endif
|
|
//move to the end point
|
|
VectorSubtract(reach->end, ms->origin, hordir);
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
if (!BotCheckBarrierJump(ms, hordir, 100))
|
|
{
|
|
EA_Move(ms->client, hordir, 400);
|
|
} //end if
|
|
VectorCopy(hordir, result.movedir);
|
|
} //end else
|
|
//if not really close to the center of the elevator
|
|
else
|
|
{
|
|
MoverBottomCenter(reach, bottomcenter);
|
|
VectorSubtract(bottomcenter, ms->origin, hordir);
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
if (dist > 10)
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
|
|
#endif
|
|
//move to the center of the plat
|
|
if (dist > 100) dist = 100;
|
|
speed = 400 - (400 - 4 * dist);
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
} //end if
|
|
} //end else
|
|
} //end if
|
|
else
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n");
|
|
#endif
|
|
//if very near the reachability end
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
dist = VectorLength(dir);
|
|
if (dist < 64)
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot moving to end\n");
|
|
#endif
|
|
if (dist > 60) dist = 60;
|
|
speed = 360 - (360 - 6 * dist);
|
|
//if swimming or no barrier jump
|
|
if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
|
|
{
|
|
if (speed > 5) EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
//stop using this reachability
|
|
ms->reachability_time = 0;
|
|
return result;
|
|
} //end if
|
|
//get direction and distance to reachability start
|
|
VectorSubtract(reach->start, ms->origin, dir1);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
|
|
dist1 = VectorNormalize(dir1);
|
|
//if func_bobbing is Not it's start position
|
|
VectorSubtract(bob_origin, bob_start, dir);
|
|
if (VectorLength(dir) > 16)
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n");
|
|
#endif
|
|
dist = dist1;
|
|
VectorCopy(dir1, dir);
|
|
//
|
|
BotCheckBlocked(ms, dir, qfalse, &result);
|
|
//
|
|
if (dist > 60) dist = 60;
|
|
speed = 360 - (360 - 6 * dist);
|
|
//
|
|
if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
|
|
{
|
|
if (speed > 5) EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
//this isn't a failure... just wait till the func_bobbing arrives
|
|
result.type = RESULTTYPE_WAITFORFUNCBOBBING;
|
|
result.flags |= MOVERESULT_WAITING;
|
|
return result;
|
|
} //end if
|
|
//get direction and distance to func_bob bottom center
|
|
MoverBottomCenter(reach, bottomcenter);
|
|
VectorSubtract(bottomcenter, ms->origin, dir2);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
|
|
dist2 = VectorNormalize(dir2);
|
|
//if very close to the reachability start or
|
|
//closer to the elevator center or
|
|
//between reachability start and func_bobbing center
|
|
if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
|
|
#endif
|
|
dist = dist2;
|
|
VectorCopy(dir2, dir);
|
|
} //end if
|
|
else //closer to the reachability start
|
|
{
|
|
#ifdef DEBUG_FUNCBOB
|
|
botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n");
|
|
#endif
|
|
dist = dist1;
|
|
VectorCopy(dir1, dir);
|
|
} //end else
|
|
//
|
|
BotCheckBlocked(ms, dir, qfalse, &result);
|
|
//
|
|
if (dist > 60) dist = 60;
|
|
speed = 400 - (400 - 6 * dist);
|
|
//
|
|
if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
|
|
{
|
|
EA_Move(ms->client, dir, speed);
|
|
} //end if
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
} //end else
|
|
return result;
|
|
} //end of the function BotTravel_FuncBobbing
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter;
|
|
bot_moveresult_t_cleared( result );
|
|
float dist, speed;
|
|
|
|
//
|
|
BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
|
|
//
|
|
VectorSubtract(bob_origin, bob_end, dir);
|
|
dist = VectorLength(dir);
|
|
//if the func_bobbing is near the end
|
|
if (dist < 16)
|
|
{
|
|
VectorSubtract(reach->end, ms->origin, hordir);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
if (dist > 60) dist = 60;
|
|
speed = 360 - (360 - 6 * dist);
|
|
//
|
|
if (speed > 5) EA_Move(ms->client, dir, speed);
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
|
|
} //end if
|
|
else
|
|
{
|
|
MoverBottomCenter(reach, bottomcenter);
|
|
VectorSubtract(bottomcenter, ms->origin, hordir);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
if (dist > 5)
|
|
{
|
|
//move to the center of the plat
|
|
if (dist > 100) dist = 100;
|
|
speed = 400 - (400 - 4 * dist);
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
} //end if
|
|
} //end else
|
|
return result;
|
|
} //end of the function BotFinishTravel_FuncBobbing
|
|
//===========================================================================
|
|
// 0 no valid grapple hook visible
|
|
// 1 the grapple hook is still flying
|
|
// 2 the grapple hooked into a wall
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
int i;
|
|
aas_entityinfo_t entinfo;
|
|
|
|
//if the grapple hook is pulling
|
|
if (ms->moveflags & MFL_GRAPPLEPULL)
|
|
return 2;
|
|
//check for a visible grapple missile entity
|
|
//or visible grapple entity
|
|
for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i))
|
|
{
|
|
if (AAS_EntityType(i) == (int) entitytypemissile->value)
|
|
{
|
|
AAS_EntityInfo(i, &entinfo);
|
|
if (entinfo.weapon == (int) weapindex_grapple->value)
|
|
{
|
|
return 1;
|
|
} //end if
|
|
} //end if
|
|
} //end for
|
|
//no valid grapple at all
|
|
return 0;
|
|
} //end of the function GrappleState
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotResetGrapple(bot_movestate_t *ms)
|
|
{
|
|
aas_reachability_t reach;
|
|
|
|
AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
|
|
//if not using the grapple hook reachability anymore
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK)
|
|
{
|
|
if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time)
|
|
{
|
|
if (offhandgrapple->value)
|
|
EA_Command(ms->client, cmd_grappleoff->string);
|
|
ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
|
|
ms->grapplevisible_time = 0;
|
|
#ifdef DEBUG_GRAPPLE
|
|
botimport.Print(PRT_MESSAGE, "reset grapple\n");
|
|
#endif //DEBUG_GRAPPLE
|
|
} //end if
|
|
} //end if
|
|
} //end of the function BotResetGrapple
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
bot_moveresult_t_cleared( result );
|
|
float dist, speed;
|
|
vec3_t dir, viewdir, org;
|
|
int state, areanum;
|
|
bsp_trace_t trace;
|
|
|
|
#ifdef DEBUG_GRAPPLE
|
|
static int debugline;
|
|
if (!debugline) debugline = botimport.DebugLineCreate();
|
|
botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE);
|
|
#endif //DEBUG_GRAPPLE
|
|
|
|
//
|
|
if (ms->moveflags & MFL_GRAPPLERESET)
|
|
{
|
|
if (offhandgrapple->value)
|
|
EA_Command(ms->client, cmd_grappleoff->string);
|
|
ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
|
|
return result;
|
|
} //end if
|
|
//
|
|
if (!(int) offhandgrapple->value)
|
|
{
|
|
result.weapon = weapindex_grapple->value;
|
|
result.flags |= MOVERESULT_MOVEMENTWEAPON;
|
|
} //end if
|
|
//
|
|
if (ms->moveflags & MFL_ACTIVEGRAPPLE)
|
|
{
|
|
#ifdef DEBUG_GRAPPLE
|
|
botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n");
|
|
#endif //DEBUG_GRAPPLE
|
|
//
|
|
state = GrappleState(ms, reach);
|
|
//
|
|
VectorSubtract(reach->end, ms->origin, dir);
|
|
dir[2] = 0;
|
|
dist = VectorLength(dir);
|
|
//if very close to the grapple end or the grappled is hooked and
|
|
//the bot doesn't get any closer
|
|
if (state && dist < 48)
|
|
{
|
|
if (ms->lastgrappledist - dist < 1)
|
|
{
|
|
#ifdef DEBUG_GRAPPLE
|
|
botimport.Print(PRT_ERROR, "grapple normal end\n");
|
|
#endif //DEBUG_GRAPPLE
|
|
if (offhandgrapple->value)
|
|
EA_Command(ms->client, cmd_grappleoff->string);
|
|
ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
|
|
ms->moveflags |= MFL_GRAPPLERESET;
|
|
ms->reachability_time = 0; //end the reachability
|
|
return result;
|
|
} //end if
|
|
} //end if
|
|
//if no valid grapple at all, or the grapple hooked and the bot
|
|
//isn't moving anymore
|
|
else if (!state || (state == 2 && dist > ms->lastgrappledist - 2))
|
|
{
|
|
if (ms->grapplevisible_time < AAS_Time() - 0.4)
|
|
{
|
|
#ifdef DEBUG_GRAPPLE
|
|
botimport.Print(PRT_ERROR, "grapple not visible\n");
|
|
#endif //DEBUG_GRAPPLE
|
|
if (offhandgrapple->value)
|
|
EA_Command(ms->client, cmd_grappleoff->string);
|
|
ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
|
|
ms->moveflags |= MFL_GRAPPLERESET;
|
|
ms->reachability_time = 0; //end the reachability
|
|
return result;
|
|
} //end if
|
|
} //end if
|
|
else
|
|
{
|
|
ms->grapplevisible_time = AAS_Time();
|
|
} //end else
|
|
//
|
|
if (!(int) offhandgrapple->value)
|
|
{
|
|
EA_Attack(ms->client);
|
|
} //end if
|
|
//remember the current grapple distance
|
|
ms->lastgrappledist = dist;
|
|
} //end if
|
|
else
|
|
{
|
|
#ifdef DEBUG_GRAPPLE
|
|
botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n");
|
|
#endif //DEBUG_GRAPPLE
|
|
//
|
|
ms->grapplevisible_time = AAS_Time();
|
|
//
|
|
VectorSubtract(reach->start, ms->origin, dir);
|
|
if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0;
|
|
VectorAdd(ms->origin, ms->viewoffset, org);
|
|
VectorSubtract(reach->end, org, viewdir);
|
|
//
|
|
dist = VectorNormalize(dir);
|
|
Vector2Angles(viewdir, result.ideal_viewangles);
|
|
result.flags |= MOVERESULT_MOVEMENTVIEW;
|
|
//
|
|
if (dist < 5 &&
|
|
fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 &&
|
|
fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2)
|
|
{
|
|
#ifdef DEBUG_GRAPPLE
|
|
botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n");
|
|
#endif //DEBUG_GRAPPLE
|
|
//check if the grapple missile path is clear
|
|
VectorAdd(ms->origin, ms->viewoffset, org);
|
|
trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID);
|
|
VectorSubtract(reach->end, trace.endpos, dir);
|
|
if (VectorLength(dir) > 16)
|
|
{
|
|
result.failure = qtrue;
|
|
return result;
|
|
} //end if
|
|
//activate the grapple
|
|
if (offhandgrapple->value)
|
|
{
|
|
EA_Command(ms->client, cmd_grappleon->string);
|
|
} //end if
|
|
else
|
|
{
|
|
EA_Attack(ms->client);
|
|
} //end else
|
|
ms->moveflags |= MFL_ACTIVEGRAPPLE;
|
|
ms->lastgrappledist = 999999;
|
|
} //end if
|
|
else
|
|
{
|
|
if (dist < 70) speed = 300 - (300 - 4 * dist);
|
|
else speed = 400;
|
|
//
|
|
BotCheckBlocked(ms, dir, qtrue, &result);
|
|
//elemantary action move in direction
|
|
EA_Move(ms->client, dir, speed);
|
|
VectorCopy(dir, result.movedir);
|
|
} //end else
|
|
//if in another area before actually grappling
|
|
areanum = AAS_PointAreaNum(ms->origin);
|
|
if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0;
|
|
} //end else
|
|
return result;
|
|
} //end of the function BotTravel_Grapple
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir;
|
|
float dist, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n");
|
|
//
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
//
|
|
dist = VectorNormalize(hordir);
|
|
//look in the movement direction
|
|
Vector2Angles(hordir, result.ideal_viewangles);
|
|
//look straight down
|
|
result.ideal_viewangles[PITCH] = 90;
|
|
//
|
|
if (dist < 5 &&
|
|
fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
|
|
fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//elemantary action jump
|
|
EA_Jump(ms->client);
|
|
EA_Attack(ms->client);
|
|
EA_Move(ms->client, hordir, 800);
|
|
//
|
|
ms->jumpreach = ms->lastreachnum;
|
|
} //end if
|
|
else
|
|
{
|
|
if (dist > 80) dist = 80;
|
|
speed = 400 - (400 - 5 * dist);
|
|
EA_Move(ms->client, hordir, speed);
|
|
} //end else
|
|
//look in the movement direction
|
|
Vector2Angles(hordir, result.ideal_viewangles);
|
|
//look straight down
|
|
result.ideal_viewangles[PITCH] = 90;
|
|
//set the view angles directly
|
|
EA_View(ms->client, result.ideal_viewangles);
|
|
//view is important for the movment
|
|
result.flags |= MOVERESULT_MOVEMENTVIEWSET;
|
|
//select the rocket launcher
|
|
EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value);
|
|
//weapon is used for movement
|
|
result.weapon = (int) weapindex_rocketlauncher->value;
|
|
result.flags |= MOVERESULT_MOVEMENTWEAPON;
|
|
//
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_RocketJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir;
|
|
float dist, speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n");
|
|
//
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
//
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
if (dist < 5 &&
|
|
fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
|
|
fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//elemantary action jump
|
|
EA_Jump(ms->client);
|
|
EA_Attack(ms->client);
|
|
EA_Move(ms->client, hordir, 800);
|
|
//
|
|
ms->jumpreach = ms->lastreachnum;
|
|
} //end if
|
|
else
|
|
{
|
|
if (dist > 80) dist = 80;
|
|
speed = 400 - (400 - 5 * dist);
|
|
EA_Move(ms->client, hordir, speed);
|
|
} //end else
|
|
//look in the movement direction
|
|
Vector2Angles(hordir, result.ideal_viewangles);
|
|
//look straight down
|
|
result.ideal_viewangles[PITCH] = 90;
|
|
//set the view angles directly
|
|
EA_View(ms->client, result.ideal_viewangles);
|
|
//view is important for the movment
|
|
result.flags |= MOVERESULT_MOVEMENTVIEWSET;
|
|
//select the rocket launcher
|
|
EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value);
|
|
//weapon is used for movement
|
|
result.weapon = (int) weapindex_bfg10k->value;
|
|
result.flags |= MOVERESULT_MOVEMENTWEAPON;
|
|
//
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_BFGJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
vec3_t hordir;
|
|
float speed;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//if not jumped yet
|
|
if (!ms->jumpreach) return result;
|
|
/*
|
|
//go straight to the reachability end
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
//always use max speed when traveling through the air
|
|
EA_Move(ms->client, hordir, 800);
|
|
VectorCopy(hordir, result.movedir);
|
|
*/
|
|
//
|
|
if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
|
|
{
|
|
//go straight to the reachability end
|
|
VectorSubtract(reach->end, ms->origin, hordir);
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
speed = 400;
|
|
} //end if
|
|
//
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_WeaponJump
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
float dist, speed;
|
|
vec3_t hordir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
//first walk straight to the reachability start
|
|
hordir[0] = reach->start[0] - ms->origin[0];
|
|
hordir[1] = reach->start[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
dist = VectorNormalize(hordir);
|
|
//
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
speed = 400;
|
|
//elemantary action move in direction
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotTravel_JumpPad
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
|
|
{
|
|
float speed;
|
|
vec3_t hordir;
|
|
bot_moveresult_t_cleared( result );
|
|
|
|
if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
|
|
{
|
|
hordir[0] = reach->end[0] - ms->origin[0];
|
|
hordir[1] = reach->end[1] - ms->origin[1];
|
|
hordir[2] = 0;
|
|
VectorNormalize(hordir);
|
|
speed = 400;
|
|
} //end if
|
|
BotCheckBlocked(ms, hordir, qtrue, &result);
|
|
//elemantary action move in direction
|
|
EA_Move(ms->client, hordir, speed);
|
|
VectorCopy(hordir, result.movedir);
|
|
//
|
|
return result;
|
|
} //end of the function BotFinishTravel_JumpPad
|
|
//===========================================================================
|
|
// time before the reachability times out
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotReachabilityTime(aas_reachability_t *reach)
|
|
{
|
|
switch(reach->traveltype & TRAVELTYPE_MASK)
|
|
{
|
|
case TRAVEL_WALK: return 5;
|
|
case TRAVEL_CROUCH: return 5;
|
|
case TRAVEL_BARRIERJUMP: return 5;
|
|
case TRAVEL_LADDER: return 6;
|
|
case TRAVEL_WALKOFFLEDGE: return 5;
|
|
case TRAVEL_JUMP: return 5;
|
|
case TRAVEL_SWIM: return 5;
|
|
case TRAVEL_WATERJUMP: return 5;
|
|
case TRAVEL_TELEPORT: return 5;
|
|
case TRAVEL_ELEVATOR: return 10;
|
|
case TRAVEL_GRAPPLEHOOK: return 8;
|
|
case TRAVEL_ROCKETJUMP: return 6;
|
|
case TRAVEL_BFGJUMP: return 6;
|
|
case TRAVEL_JUMPPAD: return 10;
|
|
case TRAVEL_FUNCBOB: return 10;
|
|
default:
|
|
{
|
|
botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype);
|
|
return 8;
|
|
} //end case
|
|
} //end switch
|
|
} //end of the function BotReachabilityTime
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal)
|
|
{
|
|
bot_moveresult_t_cleared( result );
|
|
vec3_t dir;
|
|
float dist, speed;
|
|
|
|
#ifdef DEBUG
|
|
//botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1));
|
|
//AAS_ClearShownDebugLines();
|
|
//AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED);
|
|
#endif //DEBUG
|
|
//walk straight to the goal origin
|
|
dir[0] = goal->origin[0] - ms->origin[0];
|
|
dir[1] = goal->origin[1] - ms->origin[1];
|
|
if (ms->moveflags & MFL_SWIMMING)
|
|
{
|
|
dir[2] = goal->origin[2] - ms->origin[2];
|
|
result.traveltype = TRAVEL_SWIM;
|
|
} //end if
|
|
else
|
|
{
|
|
dir[2] = 0;
|
|
result.traveltype = TRAVEL_WALK;
|
|
} //endif
|
|
//
|
|
dist = VectorNormalize(dir);
|
|
if (dist > 100) dist = 100;
|
|
speed = 400 - (400 - 4 * dist);
|
|
if (speed < 10) speed = 0;
|
|
//
|
|
BotCheckBlocked(ms, dir, qtrue, &result);
|
|
//elemantary action move in direction
|
|
EA_Move(ms->client, dir, speed);
|
|
VectorCopy(dir, result.movedir);
|
|
//
|
|
if (ms->moveflags & MFL_SWIMMING)
|
|
{
|
|
Vector2Angles(dir, result.ideal_viewangles);
|
|
result.flags |= MOVERESULT_SWIMVIEW;
|
|
} //end if
|
|
//if (!debugline) debugline = botimport.DebugLineCreate();
|
|
//botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE);
|
|
//
|
|
ms->lastreachnum = 0;
|
|
ms->lastareanum = 0;
|
|
ms->lastgoalareanum = goal->areanum;
|
|
VectorCopy(ms->origin, ms->lastorigin);
|
|
//
|
|
return result;
|
|
} //end of the function BotMoveInGoalArea
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags)
|
|
{
|
|
int reachnum, lastreachnum, foundjumppad, ent, resultflags;
|
|
aas_reachability_t reach, lastreach;
|
|
bot_movestate_t *ms;
|
|
//vec3_t mins, maxs, up = {0, 0, 1};
|
|
//bsp_trace_t trace;
|
|
//static int debugline;
|
|
|
|
result->failure = qfalse;
|
|
result->type = 0;
|
|
result->blocked = qfalse;
|
|
result->blockentity = 0;
|
|
result->traveltype = 0;
|
|
result->flags = 0;
|
|
|
|
//
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return;
|
|
//reset the grapple before testing if the bot has a valid goal
|
|
//because the bot could loose all it's goals when stuck to a wall
|
|
BotResetGrapple(ms);
|
|
//
|
|
if (!goal)
|
|
{
|
|
#ifdef DEBUG
|
|
botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client);
|
|
#endif //DEBUG
|
|
result->failure = qtrue;
|
|
return;
|
|
} //end if
|
|
//botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach);
|
|
//remove some of the move flags
|
|
ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER);
|
|
//set some of the move flags
|
|
//NOTE: the MFL_ONGROUND flag is also set in the higher AI
|
|
if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
|
|
//
|
|
if (ms->moveflags & MFL_ONGROUND)
|
|
{
|
|
int modeltype, modelnum;
|
|
|
|
ent = BotOnTopOfEntity(ms);
|
|
|
|
if (ent != -1)
|
|
{
|
|
modelnum = AAS_EntityModelindex(ent);
|
|
if (modelnum >= 0 && modelnum < MAX_MODELS)
|
|
{
|
|
modeltype = modeltypes[modelnum];
|
|
|
|
if (modeltype == MODELTYPE_FUNC_PLAT)
|
|
{
|
|
AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
|
|
//if the bot is Not using the elevator
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR ||
|
|
//NOTE: the face number is the plat model number
|
|
(reach.facenum & 0x0000FFFF) != modelnum)
|
|
{
|
|
reachnum = AAS_NextModelReachability(0, modelnum);
|
|
if (reachnum)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client);
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
ms->lastreachnum = reachnum;
|
|
ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
|
|
} //end if
|
|
else
|
|
{
|
|
if (bot_developer)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client);
|
|
} //end if
|
|
result->blocked = qtrue;
|
|
result->blockentity = ent;
|
|
result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
|
|
return;
|
|
} //end else
|
|
} //end if
|
|
result->flags |= MOVERESULT_ONTOPOF_ELEVATOR;
|
|
} //end if
|
|
else if (modeltype == MODELTYPE_FUNC_BOB)
|
|
{
|
|
AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
|
|
//if the bot is Not using the func bobbing
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB ||
|
|
//NOTE: the face number is the func_bobbing model number
|
|
(reach.facenum & 0x0000FFFF) != modelnum)
|
|
{
|
|
reachnum = AAS_NextModelReachability(0, modelnum);
|
|
if (reachnum)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client);
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
ms->lastreachnum = reachnum;
|
|
ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
|
|
} //end if
|
|
else
|
|
{
|
|
if (bot_developer)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client);
|
|
} //end if
|
|
result->blocked = qtrue;
|
|
result->blockentity = ent;
|
|
result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
|
|
return;
|
|
} //end else
|
|
} //end if
|
|
result->flags |= MOVERESULT_ONTOPOF_FUNCBOB;
|
|
} //end if
|
|
else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR)
|
|
{
|
|
// check if ontop of a door bridge ?
|
|
ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
|
|
// if not in a reachability area
|
|
if (!AAS_AreaReachability(ms->areanum))
|
|
{
|
|
result->blocked = qtrue;
|
|
result->blockentity = ent;
|
|
result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
|
|
return;
|
|
} //end if
|
|
} //end else if
|
|
else
|
|
{
|
|
result->blocked = qtrue;
|
|
result->blockentity = ent;
|
|
result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
|
|
return;
|
|
} //end else
|
|
} //end if
|
|
} //end if
|
|
} //end if
|
|
//if swimming
|
|
if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING;
|
|
//if against a ladder
|
|
if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER;
|
|
//if the bot is on the ground, swimming or against a ladder
|
|
if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER))
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1));
|
|
//
|
|
AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach);
|
|
//reachability area the bot is in
|
|
//ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR));
|
|
ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
|
|
//
|
|
if ( !ms->areanum )
|
|
{
|
|
result->failure = qtrue;
|
|
result->blocked = qtrue;
|
|
result->blockentity = 0;
|
|
result->type = RESULTTYPE_INSOLIDAREA;
|
|
return;
|
|
} //end if
|
|
//if the bot is in the goal area
|
|
if (ms->areanum == goal->areanum)
|
|
{
|
|
*result = BotMoveInGoalArea(ms, goal);
|
|
return;
|
|
} //end if
|
|
//assume we can use the reachability from the last frame
|
|
reachnum = ms->lastreachnum;
|
|
//if there is a last reachability
|
|
if (reachnum)
|
|
{
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
//check if the reachability is still valid
|
|
if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags))
|
|
{
|
|
reachnum = 0;
|
|
} //end if
|
|
//special grapple hook case
|
|
else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK)
|
|
{
|
|
if (ms->reachability_time < AAS_Time() ||
|
|
(ms->moveflags & MFL_GRAPPLERESET))
|
|
{
|
|
reachnum = 0;
|
|
} //end if
|
|
} //end if
|
|
//special elevator case
|
|
else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR ||
|
|
(reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
|
|
{
|
|
if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) ||
|
|
(result->flags & MOVERESULT_ONTOPOF_FUNCBOB))
|
|
{
|
|
ms->reachability_time = AAS_Time() + 5;
|
|
} //end if
|
|
//if the bot was going for an elevator and reached the reachability area
|
|
if (ms->areanum == reach.areanum ||
|
|
ms->reachability_time < AAS_Time())
|
|
{
|
|
reachnum = 0;
|
|
} //end if
|
|
} //end if
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
if (bot_developer)
|
|
{
|
|
if (ms->reachability_time < AAS_Time())
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client);
|
|
AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
|
|
botimport.Print(PRT_MESSAGE, "\n");
|
|
} //end if
|
|
/*
|
|
if (ms->lastareanum != ms->areanum)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum);
|
|
} //end if*/
|
|
} //end if
|
|
#endif //DEBUG
|
|
//if the goal area changed or the reachability timed out
|
|
//or the area changed
|
|
if (ms->lastgoalareanum != goal->areanum ||
|
|
ms->reachability_time < AAS_Time() ||
|
|
ms->lastareanum != ms->areanum)
|
|
{
|
|
reachnum = 0;
|
|
//botimport.Print(PRT_MESSAGE, "area change or timeout\n");
|
|
} //end else if
|
|
} //end else
|
|
} //end if
|
|
resultflags = 0;
|
|
//if the bot needs a new reachability
|
|
if (!reachnum)
|
|
{
|
|
//if the area has no reachability links
|
|
if (!AAS_AreaReachability(ms->areanum))
|
|
{
|
|
#ifdef DEBUG
|
|
if (bot_developer)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum);
|
|
} //end if
|
|
#endif //DEBUG
|
|
} //end if
|
|
//get a new reachability leading towards the goal
|
|
reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum,
|
|
ms->lastgoalareanum, ms->lastareanum,
|
|
ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
|
|
goal, travelflags, travelflags,
|
|
ms->avoidspots, ms->numavoidspots, &resultflags);
|
|
//the area number the reachability starts in
|
|
ms->reachareanum = ms->areanum;
|
|
//reset some state variables
|
|
ms->jumpreach = 0; //for TRAVEL_JUMP
|
|
ms->moveflags &= ~MFL_GRAPPLERESET; //for TRAVEL_GRAPPLEHOOK
|
|
//if there is a reachability to the goal
|
|
if (reachnum)
|
|
{
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
//set a timeout for this reachability
|
|
ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
|
|
//
|
|
#ifdef AVOIDREACH
|
|
//add the reachability to the reachabilities to avoid for a while
|
|
BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME);
|
|
#endif //AVOIDREACH
|
|
} //end if
|
|
#ifdef DEBUG
|
|
|
|
else if (bot_developer)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "goal not reachable\n");
|
|
Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy
|
|
} //end else
|
|
if (bot_developer)
|
|
{
|
|
//if still going for the same goal
|
|
if (ms->lastgoalareanum == goal->areanum)
|
|
{
|
|
if (ms->lastareanum == reach.areanum)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n");
|
|
} //end if
|
|
} //end if
|
|
} //end if
|
|
#endif //DEBUG
|
|
} //end else
|
|
//
|
|
ms->lastreachnum = reachnum;
|
|
ms->lastgoalareanum = goal->areanum;
|
|
ms->lastareanum = ms->areanum;
|
|
//if the bot has a reachability
|
|
if (reachnum)
|
|
{
|
|
//get the reachability from the number
|
|
AAS_ReachabilityFromNum(reachnum, &reach);
|
|
result->traveltype = reach.traveltype;
|
|
//
|
|
#ifdef DEBUG_AI_MOVE
|
|
AAS_ClearShownDebugLines();
|
|
AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
|
|
AAS_ShowReachability(&reach);
|
|
#endif //DEBUG_AI_MOVE
|
|
//
|
|
#ifdef DEBUG
|
|
//botimport.Print(PRT_MESSAGE, "client %d: ", ms->client);
|
|
//AAS_PrintTravelType(reach.traveltype);
|
|
//botimport.Print(PRT_MESSAGE, "\n");
|
|
#endif //DEBUG
|
|
switch(reach.traveltype & TRAVELTYPE_MASK)
|
|
{
|
|
case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;
|
|
case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break;
|
|
case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break;
|
|
case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
|
|
case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break;
|
|
case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break;
|
|
case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
|
|
case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break;
|
|
case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break;
|
|
case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break;
|
|
case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
|
|
case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break;
|
|
case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break;
|
|
case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break;
|
|
case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break;
|
|
default:
|
|
{
|
|
botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
|
|
break;
|
|
} //end case
|
|
} //end switch
|
|
result->traveltype = reach.traveltype;
|
|
result->flags |= resultflags;
|
|
} //end if
|
|
else
|
|
{
|
|
result->failure = qtrue;
|
|
result->flags |= resultflags;
|
|
Com_Memset(&reach, 0, sizeof(aas_reachability_t));
|
|
} //end else
|
|
#ifdef DEBUG
|
|
if (bot_developer)
|
|
{
|
|
if (result->failure)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client);
|
|
AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
|
|
botimport.Print(PRT_MESSAGE, "\n");
|
|
} //end if
|
|
} //end if
|
|
#endif //DEBUG
|
|
} //end if
|
|
else
|
|
{
|
|
int i, numareas, areas[16];
|
|
vec3_t end;
|
|
|
|
//special handling of jump pads when the bot uses a jump pad without knowing it
|
|
foundjumppad = qfalse;
|
|
VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end);
|
|
numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16);
|
|
for (i = numareas-1; i >= 0; i--)
|
|
{
|
|
if (AAS_AreaJumpPad(areas[i]))
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]);
|
|
foundjumppad = qtrue;
|
|
lastreachnum = BotGetReachabilityToGoal(end, areas[i],
|
|
ms->lastgoalareanum, ms->lastareanum,
|
|
ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
|
|
goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL);
|
|
if (lastreachnum)
|
|
{
|
|
ms->lastreachnum = lastreachnum;
|
|
ms->lastareanum = areas[i];
|
|
//botimport.Print(PRT_MESSAGE, "found jumppad reachability\n");
|
|
break;
|
|
} //end if
|
|
else
|
|
{
|
|
for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum;
|
|
lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum))
|
|
{
|
|
//get the reachability from the number
|
|
AAS_ReachabilityFromNum(lastreachnum, &reach);
|
|
if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)
|
|
{
|
|
ms->lastreachnum = lastreachnum;
|
|
ms->lastareanum = areas[i];
|
|
//botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n");
|
|
break;
|
|
} //end if
|
|
} //end for
|
|
if (lastreachnum) break;
|
|
} //end else
|
|
} //end if
|
|
} //end for
|
|
if (bot_developer)
|
|
{
|
|
//if a jumppad is found with the trace but no reachability is found
|
|
if (foundjumppad && !ms->lastreachnum)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client);
|
|
} //end if
|
|
} //end if
|
|
//
|
|
if (ms->lastreachnum)
|
|
{
|
|
//botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1));
|
|
AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
|
|
result->traveltype = reach.traveltype;
|
|
#ifdef DEBUG
|
|
//botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client);
|
|
//AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
|
|
//botimport.Print(PRT_MESSAGE, "\n");
|
|
#endif //DEBUG
|
|
//
|
|
switch(reach.traveltype & TRAVELTYPE_MASK)
|
|
{
|
|
case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break;
|
|
case TRAVEL_CROUCH: /*do nothing*/ break;
|
|
case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break;
|
|
case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
|
|
case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break;
|
|
case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break;
|
|
case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
|
|
case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break;
|
|
case TRAVEL_TELEPORT: /*do nothing*/ break;
|
|
case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break;
|
|
case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
|
|
case TRAVEL_ROCKETJUMP:
|
|
case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break;
|
|
case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break;
|
|
case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break;
|
|
default:
|
|
{
|
|
botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
|
|
break;
|
|
} //end case
|
|
} //end switch
|
|
result->traveltype = reach.traveltype;
|
|
#ifdef DEBUG
|
|
if (bot_developer)
|
|
{
|
|
if (result->failure)
|
|
{
|
|
botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client);
|
|
AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
|
|
botimport.Print(PRT_MESSAGE, "\n");
|
|
} //end if
|
|
} //end if
|
|
#endif //DEBUG
|
|
} //end if
|
|
} //end else
|
|
//FIXME: is it right to do this here?
|
|
if (result->blocked) ms->reachability_time -= 10 * ms->thinktime;
|
|
//copy the last origin
|
|
VectorCopy(ms->origin, ms->lastorigin);
|
|
//return the movement result
|
|
return;
|
|
} //end of the function BotMoveToGoal
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotResetAvoidReach(int movestate)
|
|
{
|
|
bot_movestate_t *ms;
|
|
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return;
|
|
Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
|
|
Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float));
|
|
Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int));
|
|
} //end of the function BotResetAvoidReach
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotResetLastAvoidReach(int movestate)
|
|
{
|
|
int i, latest;
|
|
float latesttime;
|
|
bot_movestate_t *ms;
|
|
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return;
|
|
latesttime = 0;
|
|
latest = 0;
|
|
for (i = 0; i < MAX_AVOIDREACH; i++)
|
|
{
|
|
if (ms->avoidreachtimes[i] > latesttime)
|
|
{
|
|
latesttime = ms->avoidreachtimes[i];
|
|
latest = i;
|
|
} //end if
|
|
} //end for
|
|
if (latesttime)
|
|
{
|
|
ms->avoidreachtimes[latest] = 0;
|
|
if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--;
|
|
} //end if
|
|
} //end of the function BotResetLastAvoidReach
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotResetMoveState(int movestate)
|
|
{
|
|
bot_movestate_t *ms;
|
|
|
|
ms = BotMoveStateFromHandle(movestate);
|
|
if (!ms) return;
|
|
Com_Memset(ms, 0, sizeof(bot_movestate_t));
|
|
} //end of the function BotResetMoveState
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
int BotSetupMoveAI(void)
|
|
{
|
|
BotSetBrushModelTypes();
|
|
sv_maxstep = LibVar("sv_step", "18");
|
|
sv_maxbarrier = LibVar("sv_maxbarrier", "32");
|
|
sv_gravity = LibVar("sv_gravity", "800");
|
|
weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5");
|
|
weapindex_bfg10k = LibVar("weapindex_bfg10k", "9");
|
|
weapindex_grapple = LibVar("weapindex_grapple", "10");
|
|
entitytypemissile = LibVar("entitytypemissile", "3");
|
|
offhandgrapple = LibVar("offhandgrapple", "0");
|
|
cmd_grappleon = LibVar("cmd_grappleon", "grappleon");
|
|
cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff");
|
|
return BLERR_NOERROR;
|
|
} //end of the function BotSetupMoveAI
|
|
//===========================================================================
|
|
//
|
|
// Parameter: -
|
|
// Returns: -
|
|
// Changes Globals: -
|
|
//===========================================================================
|
|
void BotShutdownMoveAI(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i <= MAX_CLIENTS; i++)
|
|
{
|
|
if (botmovestates[i])
|
|
{
|
|
FreeMemory(botmovestates[i]);
|
|
botmovestates[i] = NULL;
|
|
} //end if
|
|
} //end for
|
|
} //end of the function BotShutdownMoveAI
|
|
|
|
|