diff --git a/src/botlib/ai_move/bot_move.c b/src/botlib/ai_move/bot_move.c index 2b3d722..054f703 100644 --- a/src/botlib/ai_move/bot_move.c +++ b/src/botlib/ai_move/bot_move.c @@ -519,6 +519,31 @@ static float VectorNormalizeTo(const vec3_t src, vec3_t dst) return VectorNormalizeInline(dst); } +/* +============= +BotMove_VisiblePosition + +Checks if a point is visible from a viewpoint. +============= +*/ +static bool BotMove_VisiblePosition(int ent, const vec3_t eye, const vec3_t target) +{ + if (eye == NULL || target == NULL) + { + return false; + } + + vec3_t start; + vec3_t end; + VectorCopy(eye, start); + VectorCopy(target, end); + + vec3_t mins = {0.0f, 0.0f, 0.0f}; + vec3_t maxs = {0.0f, 0.0f, 0.0f}; + bsp_trace_t trace = Q2_Trace(start, mins, maxs, end, ent, CONTENTS_SOLID | CONTENTS_PLAYERCLIP); + return trace.fraction >= 1.0f; +} + static int BotMove_FindAreaForPoint(const vec3_t origin) { if (!aasworld.loaded || aasworld.areas == NULL || aasworld.numAreas <= 0) @@ -1511,3 +1536,59 @@ void BotMove_ResetAvoidReach(int movestate) memset(ms->avoidreachtries, 0, sizeof(ms->avoidreachtries)); } +/* +============= +BotPredictVisiblePosition + +Predicts a reachability point that remains visible from the goal. +============= +*/ +int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target) +{ + if (goal == NULL || target == NULL) + { + return false; + } + + if (!areanum || !goal->areanum) + { + return false; + } + + bot_movestate_t temp_state; + memset(&temp_state, 0, sizeof(temp_state)); + + for (int i = 0; i < 20 && areanum != goal->areanum; ++i) + { + aas_reachability_t reach; + temp_state.areanum = areanum; + + int reachnum = BotGetReachabilityToGoal(&temp_state, goal, travelflags, &reach, NULL); + if (!reachnum) + { + return false; + } + + if (BotMove_VisiblePosition(goal->entitynum, goal->origin, reach.start)) + { + VectorCopy(reach.start, target); + return true; + } + + if (BotMove_VisiblePosition(goal->entitynum, goal->origin, reach.end)) + { + VectorCopy(reach.end, target); + return true; + } + + if (reach.areanum == goal->areanum) + { + VectorCopy(reach.end, target); + return true; + } + + areanum = reach.areanum; + } + + return false; +} diff --git a/src/botlib/ai_move/bot_move.h b/src/botlib/ai_move/bot_move.h index 8f2600c..45b5746 100644 --- a/src/botlib/ai_move/bot_move.h +++ b/src/botlib/ai_move/bot_move.h @@ -166,6 +166,8 @@ int BotMoveInDirection(int movestate, const vec3_t dir, float speed, int type); void BotMove_ResetAvoidReach(int movestate); +int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target); + void AI_MoveFrame(bot_moveresult_t *result, int movestate, const bot_goal_t *goal, @@ -176,4 +178,3 @@ bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, const struct aas_reachab #ifdef __cplusplus } /* extern "C" */ #endif - diff --git a/src/botlib/interface/bot_interface.c b/src/botlib/interface/bot_interface.c index c3d1bba..f35a8f0 100644 --- a/src/botlib/interface/bot_interface.c +++ b/src/botlib/interface/bot_interface.c @@ -3170,6 +3170,27 @@ static void BotInterface_BotResetAvoidReach(int movestate) BotMove_ResetAvoidReach(movestate); } +/* +============= +BotInterface_BotPredictVisiblePosition + +Routes the visibility prediction request to the bot movement module. +============= +*/ +static int BotInterface_BotPredictVisiblePosition(vec3_t origin, + int areanum, + bot_goal_t *goal, + int travelflags, + vec3_t target) +{ + if (!BotInterface_EnsureLibraryReady("BotPredictVisiblePosition")) + { + return 0; + } + + return BotPredictVisiblePosition(origin, areanum, goal, travelflags, target); +} + static int BotInterface_BotAllocWeaponState(void) { if (!BotInterface_EnsureLibraryReady("BotAllocWeaponState")) @@ -3454,6 +3475,7 @@ GLADIATOR_API bot_export_t *GetBotAPI(bot_import_t *import) exportTable.BotMoveToGoal = BotInterface_BotMoveToGoal; exportTable.BotMoveInDirection = BotInterface_BotMoveInDirection; exportTable.BotResetAvoidReach = BotInterface_BotResetAvoidReach; + exportTable.BotPredictVisiblePosition = BotInterface_BotPredictVisiblePosition; exportTable.BotLoadCharacter = BotLoadCharacter; exportTable.BotFreeCharacter = BotFreeCharacter; exportTable.BotLoadCharacterSkill = BotLoadCharacterSkill; @@ -3485,4 +3507,3 @@ GLADIATOR_API bot_export_t *GetBotAPI(bot_import_t *import) return &exportTable; } - diff --git a/src/q2bridge/botlib.h b/src/q2bridge/botlib.h index c5e92c3..a7d59d3 100644 --- a/src/q2bridge/botlib.h +++ b/src/q2bridge/botlib.h @@ -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 (*BotPredictVisiblePosition)(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target); int (*BotLoadCharacter)(const char *character_file, float skill); void (*BotFreeCharacter)(int handle); int (*BotLoadCharacterSkill)(const char *character_file, float skill);