The server loads a waypoint lookup along with the map, adds information gained by observing players and monsters at runtime, and saves it for reuse. The information is basically an adjacency graph based on a spatial subdivision hidden to the Game DLL, and additional information stored in edges and nodes.
Advantage:
Disadvantage:
Moderate memory and performance impact, maybe even compensated by faster AI. If Q3 uses no spatial subdivision at all, this would be obsolete.
Implementation:
In full, this is an overhaul of the current AI, trailing, and the Area/PHS code. However, there are intermediate steps that could be used for a smooth migration on the way to Trinity. Comments below:
int (*getAreaID)( vec3_t pos )This would hide the actual details of the areas from the DLL. This is already present in the Q2 DLL. There are areanum fields in the edict_t.
g_ai.c: if (!gi.AreasConnected(self->areanum, client->areanum))
#define PATH_FASTEST #define PATH_EASIEST #define PATH_SHORTEST #define PATH_SAFEST #define PATH_NO_WATER_THROUGH #define PATH_NO_WATER_CLOSE #define PATH_NO_LAVA_CLOSE // etc. #define PATH_FLYER #define PATH_NO_STEPS #define PATH_NO_FALLS #define PATH_IN_DARK // stealthy #define PATH_NARROW_SPACES #define PATH_OPEN_SPACES // Get a path suggestion, depending on flags. int (*getPathID)( vec3_t start, vec3_t end, int flags ); // Get the overall costs, whatever units. float (*getPathCosts)( int path_id ); // Get waypoints to travel along. vec3_t* (*getPath)( int path_id ); // Get total path length lookahead. // Mask if you want under water path etc. float (*getPathLength)( ind path_id, int flags );The AI could get a path with some preferences, get the costs, reject them, get another one with changed preferences, and if acceptable, get the waypoints and start walking.
vec3_t (*getAmbush)( vec3_t me, vec3_t target );to get obviously good kill positions from past events for my and the targets current position.
vec3_t* (getPointsInArea)( vec3_t pos ); vec3_t* (getAreaPoints)( int areaID );returns a list of valid points in the current area, or any area, reacheable, relatively close to the current position, but measuring the extent of the area: close to the walls, to obstacles. Application:
Monster A sees player. Monster A is a melee attacker (berserk), or low on health, or a coward - for whatever reason, monster A wants to hide out of sight. So you get the points, and check them with trace to find one to hide out of sight. You move towards one picked, and stand still till the player gets in sight again.
int* (getAreasAdjacent)( vec3_t pos );Gives the areaID's of all ajacent areas.
// get number of recent kills - a runtime counter // might distinguis deaths and gibs, // for level of violence deaths_in_area = gi.getAreaKills( areaID ); // Here is your sense of smell. time_since_last = gi.getAreaKillLastWhen( areaID ); pos_of_last = gi.getAreaKillLastWhere( areaID ); // get number of monsters, players // different from gi.BoxEdicts entities_in_area = gi.getAreaEntities( areaID ); // get items/powerups in that area items_in_area = gi.getAreaItems( areaID );You could even return lists. You could cheat, or clip against LOS, once you got all the items. As objects are stored per BSP leaf, the data should be readily available. You could also lookup small, compact bit flags.
// any movements/noises in this area? qboolean gi.getAreaMoves( areaID ); qboolean gi.getAreaNoises( areaID );
Related Proposals:
This proposal complemetes the "Map Markup" one. Ideally, the same internal data structure should be used here. It was inspired by discussions on the q2bots mailing list as well as AI improvement ideas mentioned on the q3suggest mailing list.
Submitted 980420, contact bk@gamers.org.