1
Coding / Re: the AI discussion
« on: July 07, 2013, 07:08:54 am »
Sooo, after three failures that really should have worked, and now that i've cooled down from the resulting tantrum, i figure i'll just post what i wanted to do and hope someone can point out the best way to actually do it.
Here is some code that does not work for various reasons.
The most obvious reason this does not work, is that std::list doesn't know how to deal with a pos3_t. That's easily solved by defining a new 3-pos struct to use here and converting to and fro, but it's not the main problem.
The main problem is that it seems this combination of things i want to do is unable to be called together from anywhere in the codebase. Specifically G_EdictsGetNextLivingActor and Grid_GetTUsForDirection. In grid.cpp it can't call the G_ function, and in g_ai.cpp it can't call the Grid_ function.
Maybe i'm wrong about that, and there's some way i can easily do this, but in the end i still have no idea where this function should go.
Probably it wants to be called from G_ClientEndRound? And the enemy_distance_t must be made accessible to the AI somehow. The routing table needs to be accessible to whatever is calling this function, and enemy_distance_t needs to be declared somewhere everyone can see it.
It's possible there's a better way to organize things, but i've already tried integrating this in three different places unsuccessfuly, so i really hope someone can look at this and tell me where and how it can fit in.
Here is some code that does not work for various reasons.
Code: [Select]
typedef struct enemy_distance_s {
/* Approximate distance in TUs to the nearest enemy (capped at 255) */
byte distance[PATHFINDING_HEIGHT][PATHFINDING_WIDTH][PATHFINDING_WIDTH];
inline byte getDist(const pos3_t pos) const {
return distance[pos[2]][pos[1]][pos[0]];
}
inline void setDist(const int x, const int y, const int z, const byte val) {
distance[z][y][x] = val;
}
inline void setDist(const pos3_t pos, const byte val) {
setDist(pos[0], pos[1], pos[2], val);
}
} enemy_distance_t;
/**
* @brief Calculate the distance to the nearest enemy for every point on the map.
* @param[in] routing The routing map (either server or client map).
* @param[out] enemy_dist The map of nearest-enemy distances.
* @param[in] team The AI team we're calculating this for.
*/
void CalcEnemyDistances(const Routing &routing, enemy_distance_t *enemy_dist, const int team)
{
/* Clear the distance grid: set all distances to max.
* (this is probably about 512KiB of data to maxify) */
for (int z = 0; z < PATHFINDING_HEIGHT; z++) {
for (int y = 0; y < PATHFINDING_WIDTH; y++) {
for (int x = 0; x < PATHFINDING_WIDTH; x++) {
enemy_dist->setDist(x, y, z, (byte)(-1));
}
}
}
/* Maintain a list of cells to be checked. */
std::list<pos3_t> cells_to_check;
/* Start with distance 0 at all enemy positions. */
edict_t *ent = nullptr;
while ((ent = G_EdictsGetNextLivingActor(ent))) {
/* Assume we are hostile to members of all other teams.
* (ReactionFire::isEnemy seems to work this way also) */
if (ent->team == team) {
continue;
}
enemy_dist->setDist(ent->pos, 0);
cells_to_check.push_back(ent->pos);
}
int updatedCells = 0;
/* Check the surroundings of each cell,
* to see if we found a shorter path.
* If so, update the distance map, and recurse. */
while (!cells_to_check.empty()) {
std::list<pos3_t>::iterator cell;
for (cell = cells_to_check.begin(); cell != cells_to_check.end(); cell++) {
int dist = enemy_dist->getDist(*cell);
for (int dir = 0; dir < PATHFINDING_DIRECTIONS; dir++) {
/* Get the cell leading to this cell in this direction. */
pos3_t from_cell;
int crouching = 0;
VectorCopy(*cell, from_cell);
PosSubDV(from_cell, crouching, dir);
/* Is it connected?
* For now don't take into account size etc,
* just if there is a grid connection. */
if (!routing.getConn(ACTOR_SIZE_NORMAL, from_cell, dir)) {
continue;
}
/* How many TUs does it take to move from there to here? */
int tustep = Grid_GetTUsForDirection(dir, 0);
/* Have we found a shorter distance? */
if (tustep + dist < enemy_dist->getDist(from_cell)) {
enemy_dist->setDist(from_cell, tustep + dist);
/* Add to the list to be checked next cycle. */
cells_to_check.push_front(from_cell);
updatedCells++;
}
}
cell = cells_to_check.erase(cell);
}
}
}
The most obvious reason this does not work, is that std::list doesn't know how to deal with a pos3_t. That's easily solved by defining a new 3-pos struct to use here and converting to and fro, but it's not the main problem.
The main problem is that it seems this combination of things i want to do is unable to be called together from anywhere in the codebase. Specifically G_EdictsGetNextLivingActor and Grid_GetTUsForDirection. In grid.cpp it can't call the G_ function, and in g_ai.cpp it can't call the Grid_ function.
Maybe i'm wrong about that, and there's some way i can easily do this, but in the end i still have no idea where this function should go.
Probably it wants to be called from G_ClientEndRound? And the enemy_distance_t must be made accessible to the AI somehow. The routing table needs to be accessible to whatever is calling this function, and enemy_distance_t needs to be declared somewhere everyone can see it.
It's possible there's a better way to organize things, but i've already tried integrating this in three different places unsuccessfuly, so i really hope someone can look at this and tell me where and how it can fit in.