Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions src/botlib/ai_move/bot_move.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ static bot_movestate_t *g_botMoveStates[MAX_CLIENTS + 1];
static float VectorNormalizeInline(vec3_t v);
static float VectorNormalizeTo(const vec3_t src, vec3_t dst);
static int BotMove_FindAreaForPoint(const vec3_t origin);
static bool BotMove_AreaHasReachability(int areanum);
static int BotMove_FuzzyPointReachabilityArea(const vec3_t origin);
static void BotMove_GetClientBounds(int client, vec3_t mins, vec3_t maxs);
static float BotMove_TravelTimeout(int traveltype);

static const char *BotMove_DefaultGrappleModel(void)
Expand Down Expand Up @@ -547,6 +550,153 @@ static int BotMove_FindAreaForPoint(const vec3_t origin)
return 0;
}

/*
=============
BotMove_AreaHasReachability

Check whether an area has reachability data attached.
=============
*/
static bool BotMove_AreaHasReachability(int areanum)
{
if (areanum <= 0)
{
return false;
}

if (aasworld.reachability == NULL || aasworld.numReachability <= 0)
{
return false;
}

if (aasworld.reachabilityFromArea != NULL)
{
for (int index = 0; index < aasworld.numReachability; ++index)
{
if (aasworld.reachabilityFromArea[index] == areanum)
{
return true;
}
}

return false;
}

for (int index = 0; index < aasworld.numReachability; ++index)
{
if (aasworld.reachability[index].facenum == areanum)
{
return true;
}
}

return false;
}

/*
=============
BotMove_FuzzyPointReachabilityArea

Resolve an area near the origin using small offsets when needed.
=============
*/
static int BotMove_FuzzyPointReachabilityArea(const vec3_t origin)
{
if (origin == NULL)
{
return 0;
}

int area = BotMove_FindAreaForPoint(origin);
if (area > 0)
{
return area;
}

int firstArea = 0;
int bestArea = 0;
float bestDist = FLT_MAX;

for (int z = 1; z >= -1; --z)
{
for (int x = 1; x >= -1; --x)
{
for (int y = 1; y >= -1; --y)
{
vec3_t offset;
VectorCopy(origin, offset);
offset[0] += (float)(x * 8);
offset[1] += (float)(y * 8);
offset[2] += (float)(z * 12);

area = BotMove_FindAreaForPoint(offset);
if (area <= 0)
{
continue;
}

if (firstArea == 0)
{
firstArea = area;
}

vec3_t delta;
VectorSubtract(offset, origin, delta);
float dist = VectorLength(delta);
if (dist < bestDist)
{
bestDist = dist;
bestArea = area;
}
}
}

if (bestArea > 0)
{
return bestArea;
}
}

return firstArea;
}

/*
=============
BotMove_GetClientBounds

Gather the client bounding box for traces.
=============
*/
static void BotMove_GetClientBounds(int client, vec3_t mins, vec3_t maxs)
{
if (mins == NULL || maxs == NULL)
{
return;
}

VectorClear(mins);
VectorClear(maxs);

if (aasworld.entities == NULL || aasworld.maxEntities <= 0)
{
return;
}

if (client < 0 || client >= aasworld.maxEntities)
{
return;
}

const aas_entity_t *entity = &aasworld.entities[client];
if (entity == NULL || !entity->inuse)
{
return;
}

VectorCopy(entity->mins, mins);
VectorCopy(entity->maxs, maxs);
}

static int BotMove_TravelFlagsForType(int traveltype)
{
if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES)
Expand Down Expand Up @@ -1546,6 +1696,81 @@ void BotMove_ResetAvoidReach(int movestate)
memset(ms->avoidreachtries, 0, sizeof(ms->avoidreachtries));
}

/*
=============
BotReachabilityArea

Resolve the reachability area for an origin with mover/solid handling.
=============
*/
int BotReachabilityArea(const vec3_t origin, int client)
{
if (origin == NULL)
{
return 0;
}

vec3_t mins;
vec3_t maxs;
BotMove_GetClientBounds(client, mins, maxs);

vec3_t start;
vec3_t end;
VectorCopy(origin, start);
VectorCopy(origin, end);
end[2] -= 3.0f;

bsp_trace_t trace = Q2_Trace(start, mins, maxs, end, client, CONTENTS_SOLID | CONTENTS_PLAYERCLIP);
if (!trace.startsolid && trace.fraction < 1.0f)
{
int entnum = trace.ent;
if (entnum > 0 && aasworld.entities != NULL && entnum < aasworld.maxEntities)
{
const aas_entity_t *entity = &aasworld.entities[entnum];
if (entity != NULL && entity->inuse)
{
int modelnum = AAS_ModelNumForEntity(entnum);
if (modelnum > 0)
{
const bot_mover_catalogue_entry_t *entry = BotMove_MoverCatalogueFindByModel(modelnum);
if (entry != NULL &&
(entry->kind == BOT_MOVER_KIND_FUNC_PLAT ||
entry->kind == BOT_MOVER_KIND_FUNC_BOB))
{
aas_reachability_t reach;
int reachnum = AAS_NextModelReachability(0, modelnum);
if (reachnum > 0 && BotMove_LoadReachability(reachnum, &reach))
{
return reach.areanum;
}
}
}
}
}

if (Q2_PointContents(start) & MASK_WATER)
{
return BotMove_FuzzyPointReachabilityArea(start);
}

int areanum = BotMove_FuzzyPointReachabilityArea(start);
if (areanum > 0 && BotMove_AreaHasReachability(areanum))
{
return areanum;
}

VectorCopy(origin, end);
end[2] -= 800.0f;
trace = Q2_Trace(start, mins, maxs, end, client, CONTENTS_SOLID | CONTENTS_PLAYERCLIP);
if (!trace.startsolid)
{
return BotMove_FuzzyPointReachabilityArea(trace.endpos);
}
}

return BotMove_FuzzyPointReachabilityArea(start);
}

/*
=============
BotMovementViewTarget
Expand Down
1 change: 1 addition & 0 deletions src/botlib/ai_move/bot_move.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ void BotMoveToGoal(bot_moveresult_t *result,
int BotMoveInDirection(int movestate, const vec3_t dir, float speed, int type);

void BotMove_ResetAvoidReach(int movestate);
int BotReachabilityArea(const vec3_t origin, int client);
int BotMovementViewTarget(int movestate,
const bot_goal_t *goal,
int travelflags,
Expand Down
18 changes: 18 additions & 0 deletions src/botlib/interface/bot_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -3170,6 +3170,23 @@ static void BotInterface_BotResetAvoidReach(int movestate)
BotMove_ResetAvoidReach(movestate);
}

/*
=============
BotInterface_BotReachabilityArea

Bridge reachability area lookup through the botlib API.
=============
*/
static int BotInterface_BotReachabilityArea(vec3_t origin, int client)
{
if (!BotInterface_EnsureLibraryReady("BotReachabilityArea"))
{
return 0;
}

return BotReachabilityArea(origin, client);
}

/*
=============
BotInterface_BotMovementViewTarget
Expand Down Expand Up @@ -3475,6 +3492,7 @@ GLADIATOR_API bot_export_t *GetBotAPI(bot_import_t *import)
exportTable.BotMoveToGoal = BotInterface_BotMoveToGoal;
exportTable.BotMoveInDirection = BotInterface_BotMoveInDirection;
exportTable.BotResetAvoidReach = BotInterface_BotResetAvoidReach;
exportTable.BotReachabilityArea = BotInterface_BotReachabilityArea;
exportTable.BotMovementViewTarget = BotInterface_BotMovementViewTarget;
exportTable.BotLoadCharacter = BotLoadCharacter;
exportTable.BotFreeCharacter = BotFreeCharacter;
Expand Down
1 change: 1 addition & 0 deletions src/q2bridge/botlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ typedef struct bot_export_s {
void (*BotMoveToGoal)(bot_moveresult_t *result, int movestate, const bot_goal_t *goal, int travelflags);
int (*BotMoveInDirection)(int movestate, const vec3_t dir, float speed, int type);
void (*BotResetAvoidReach)(int movestate);
int (*BotReachabilityArea)(vec3_t origin, int client);
int (*BotMovementViewTarget)(int movestate, const bot_goal_t *goal, int travelflags, float lookahead, vec3_t target);
int (*BotLoadCharacter)(const char *character_file, float skill);
void (*BotFreeCharacter)(int handle);
Expand Down
Loading