Okay, I've managed to write a pottable BotWalkPath() function. It's just in alpha stage but I'd need your comments on its layout (generally speaking, since you're not supposed to know all the details of my code yet). It would be great if we could agree on the best layout for such a function since it is a very common problem for all bots, how to reach their next waypoint efficiently. Also I have a problem. But first things first, here's the function :
Code:
void BotWalkPath (player_t *pPlayer)
{
// this function makes the bot pointed to by pBot follow recursively the series of navlinks
// that made up the path it wants to follow, until it either reaches its end, or that it finds
// out that the distance to the next navlink in the list is increasing instead of decreasing,
// indicating that the path is failing (either the bot falled down a cliff or whatever) ; in
// this case, the bot is told to start thinking of a new path immediately.
pathmachine_t *pathmachine = &pPlayer->Bot.BotBrain.PathMachine; // quick access to pathmachine
bot_move_t *BotMove = &pPlayer->Bot.BotMove; // quick access to bot's legs
int path_index;
navlink_t *current_navlink;
Vector v_bot2currentlink;
float currentlink_distance;
navlink_t *next_navlink;
Vector v_bot2nextlink;
float nextlink_distance;
path_index = BotMove->path_index; // quick access to path index
// first off, has the bot reached its destination ?
if (path_index == pathmachine->path_count)
{
pPlayer->Bot.bot_task = BOT_TASK_IDLE; // bot doesn't need to follow a path anymore
pathmachine->path_count = 0; // reset the path
BotMove->path_index = 0;
return;
}
// bot isn't arrived yet
// bot needs to know where it's heading to, that is, it needs to know both the link it's
// currently reaching, AND the link after this one, in order to know when to skip to it. So
// get a quick access to current and next navlink (also check for end of path on next navlink)
current_navlink = pathmachine->path[path_index];
next_navlink = pathmachine->path[(path_index + 1 < pathmachine->path_count ? path_index + 1 : path_index)];
v_bot2currentlink = current_navlink->v_origin - pPlayer->v_origin; // get vector from bot to current link
v_bot2nextlink = next_navlink->v_origin - pPlayer->v_origin; // get vector from bot to next link
currentlink_distance = v_bot2currentlink.Length (); // get distance from bot to next link
nextlink_distance = v_bot2nextlink.Length (); // get distance from bot to link after next link
// does the current navlink involve a ladder ?
if (current_navlink->reachability & REACHABILITY_LADDER)
{
// has the bot NOT identified the ladder yet ?
if (FNullEnt (pPlayer->Bot.pTransportEntity))
BotFindTransportEntity (pPlayer, REACHABILITY_LADDER); // if so, find the involved ladder
}
// does the link involve a fall ?
if (current_navlink->reachability & REACHABILITY_FALLEDGE)
{
// a priori, nothing to do but to let ourselves fall down the pit...
}
// does the link involve an elevator ?
if (current_navlink->reachability & REACHABILITY_ELEVATOR)
{
// has the bot NOT identified the elevator yet ?
if (FNullEnt (pPlayer->Bot.pTransportEntity))
BotFindTransportEntity (pPlayer, REACHABILITY_ELEVATOR); // if so, find the involved elevator
// TODO: check for a button
// TODO: wait for the elevator to stop before getting on it
// TODO: wait for the elevator to stop before leaving it
}
// does the link involve a bobbing platform ?
if (current_navlink->reachability & REACHABILITY_PLATFORM)
{
// has the bot NOT identified the platform yet ?
if (FNullEnt (pPlayer->Bot.pTransportEntity))
BotFindTransportEntity (pPlayer, REACHABILITY_PLATFORM); // if so, find the involved platform
// TODO: check for a button
// TODO: wait for the platform to be close enough to us before getting on it
// TODO: wait to be close enough to destination before leaving the platform
}
// does the link involve a conveyor ribbon ?
if (current_navlink->reachability & REACHABILITY_CONVEYOR)
{
// has the bot NOT identified the conveyor yet ?
if (FNullEnt (pPlayer->Bot.pTransportEntity))
BotFindTransportEntity (pPlayer, REACHABILITY_CONVEYOR); // if so, find the involved conveyor
// TODO: check for a button
// TODO: wait to be close enough to destination before leaving the conveyor
}
// does the link involve a train ?
if (current_navlink->reachability & REACHABILITY_TRAIN)
{
// has the bot NOT identified the train yet ?
if (FNullEnt (pPlayer->Bot.pTransportEntity))
BotFindTransportEntity (pPlayer, REACHABILITY_TRAIN); // if so, find the involved train
// TODO: check for the presence of the train
// TODO: wait for the train to be close enough to us before getting on it
// TODO: wait to be close enough to destination before leaving the train
}
// if time to, update the bot's distance to its next link
if (BotMove->nextlink_distance_updatetime < server.time)
{
BotMove->nextlink_distance = currentlink_distance; // update distance
BotMove->nextlink_distance_updatetime = server.time + 1.0; // next update in 1s
}
// is the path failing ?
/* if ((currentlink_distance > BotMove->nextlink_distance + 16) || pPlayer->Bot.is_stuck)
{
if (ListenserverIsWatching (pPlayer))
printf ("PATH FAILED!!! CAN'T WALK PATH!!!\n");
current_navlink->reachability |= REACHABILITY_BAD; // then destroy this link, it's a bad one...
pathmachine->path_count = 0; // reset the path
BotMove->path_index = 0;
BotMove->nextlink_distance_updatetime = 0;
pPlayer->Bot.bot_task = BOT_TASK_FINDPATH; // tell the bot to figure out a new path
return;
}*/
// if we are watching this bot, display the path it is following
if (ListenserverIsWatching (pPlayer))
{
//UTIL_DrawPath (pListenserverPlayer, pathmachine, 600);
UTIL_DrawLine (pListenserverPlayer, pPlayer->v_origin, current_navlink->v_origin, 1, 255, 255, 255);
UTIL_DrawLine (pListenserverPlayer, current_navlink->v_origin, next_navlink->v_origin, 1, 0, 0, 255);
}
// walk the path, Neo.
BotMoveTowardsPosition (pPlayer, current_navlink->v_origin);
// avoid any obstacles while we're at it
BotAvoidObstacles (pPlayer);
// can the bot look around while wandering ? (don't do so for ladders...)
if (!(current_navlink->reachability & REACHABILITY_LADDER))
BotLookAround (pPlayer); // yes, look around
else
BotSetIdealAngles (pPlayer, UTIL_VecToAngles (v_bot2currentlink)); // should rather look at destination
// has the bot reached its current link OR has the bot bypassed it already ?
if (((current_navlink->v_origin - pPlayer->v_origin).Length () < 60)
|| (AngleOfVectors (v_bot2currentlink, v_bot2nextlink) > 60))
BotMove->path_index++; // skip to the next one in the list
return; // enough for this frame
}
To me the best layout of WalkPath() functions are:
1°) identify the link
2°) identify extra entities that the bot can use to reach the next link
3°) decide of the action to take regarding to these
4°) if not explicitly told not to, walk towards the next waypoint
Commentaries ?
Note that in BotMoveTowardsPosition(), only forward/strafe keys are involved. The bot does NOT make its body angle face the waypoint while the view angles are looking elsewhere (it's unnatural). Which brings me to the problem: I need a reliable way to tell that the path is failing. How do you guys do for detecting that your bot's next waypoint has become unreachable ?