diff options
author | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
---|---|---|
committer | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
commit | 6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch) | |
tree | e3eda937a05d7db42de725b7013bd0344b987f34 /code/botlib | |
parent | 872d4d7f55af706737ffb361bb76ad13e7496770 (diff) | |
download | ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip |
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/botlib')
56 files changed, 36396 insertions, 36396 deletions
diff --git a/code/botlib/aasfile.h b/code/botlib/aasfile.h index 5b37fbe..7fdc26a 100755 --- a/code/botlib/aasfile.h +++ b/code/botlib/aasfile.h @@ -1,267 +1,267 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-
-//NOTE: int = default signed
-// default long
-
-#define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E')
-#define AASVERSION_OLD 4
-#define AASVERSION 5
-
-//presence types
-#define PRESENCE_NONE 1
-#define PRESENCE_NORMAL 2
-#define PRESENCE_CROUCH 4
-
-//travel types
-#define MAX_TRAVELTYPES 32
-#define TRAVEL_INVALID 1 //temporary not possible
-#define TRAVEL_WALK 2 //walking
-#define TRAVEL_CROUCH 3 //crouching
-#define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier
-#define TRAVEL_JUMP 5 //jumping
-#define TRAVEL_LADDER 6 //climbing a ladder
-#define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge
-#define TRAVEL_SWIM 8 //swimming
-#define TRAVEL_WATERJUMP 9 //jump out of the water
-#define TRAVEL_TELEPORT 10 //teleportation
-#define TRAVEL_ELEVATOR 11 //travel by elevator
-#define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel
-#define TRAVEL_BFGJUMP 13 //bfg jumping required for travel
-#define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel
-#define TRAVEL_DOUBLEJUMP 15 //double jump
-#define TRAVEL_RAMPJUMP 16 //ramp jump
-#define TRAVEL_STRAFEJUMP 17 //strafe jump
-#define TRAVEL_JUMPPAD 18 //jump pad
-#define TRAVEL_FUNCBOB 19 //func bob
-
-//additional travel flags
-#define TRAVELTYPE_MASK 0xFFFFFF
-#define TRAVELFLAG_NOTTEAM1 (1 << 24)
-#define TRAVELFLAG_NOTTEAM2 (2 << 24)
-
-//face flags
-#define FACE_SOLID 1 //just solid at the other side
-#define FACE_LADDER 2 //ladder
-#define FACE_GROUND 4 //standing on ground when in this face
-#define FACE_GAP 8 //gap in the ground
-#define FACE_LIQUID 16 //face seperating two areas with liquid
-#define FACE_LIQUIDSURFACE 32 //face seperating liquid and air
-#define FACE_BRIDGE 64 //can walk over this face if bridge is closed
-
-//area contents
-#define AREACONTENTS_WATER 1
-#define AREACONTENTS_LAVA 2
-#define AREACONTENTS_SLIME 4
-#define AREACONTENTS_CLUSTERPORTAL 8
-#define AREACONTENTS_TELEPORTAL 16
-#define AREACONTENTS_ROUTEPORTAL 32
-#define AREACONTENTS_TELEPORTER 64
-#define AREACONTENTS_JUMPPAD 128
-#define AREACONTENTS_DONOTENTER 256
-#define AREACONTENTS_VIEWPORTAL 512
-#define AREACONTENTS_MOVER 1024
-#define AREACONTENTS_NOTTEAM1 2048
-#define AREACONTENTS_NOTTEAM2 4096
-//number of model of the mover inside this area
-#define AREACONTENTS_MODELNUMSHIFT 24
-#define AREACONTENTS_MAXMODELNUM 0xFF
-#define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT)
-
-//area flags
-#define AREA_GROUNDED 1 //bot can stand on the ground
-#define AREA_LADDER 2 //area contains one or more ladder faces
-#define AREA_LIQUID 4 //area contains a liquid
-#define AREA_DISABLED 8 //area is disabled for routing when set
-#define AREA_BRIDGE 16 //area ontop of a bridge
-
-//aas file header lumps
-#define AAS_LUMPS 14
-#define AASLUMP_BBOXES 0
-#define AASLUMP_VERTEXES 1
-#define AASLUMP_PLANES 2
-#define AASLUMP_EDGES 3
-#define AASLUMP_EDGEINDEX 4
-#define AASLUMP_FACES 5
-#define AASLUMP_FACEINDEX 6
-#define AASLUMP_AREAS 7
-#define AASLUMP_AREASETTINGS 8
-#define AASLUMP_REACHABILITY 9
-#define AASLUMP_NODES 10
-#define AASLUMP_PORTALS 11
-#define AASLUMP_PORTALINDEX 12
-#define AASLUMP_CLUSTERS 13
-
-//========== bounding box =========
-
-//bounding box
-typedef struct aas_bbox_s
-{
- int presencetype;
- int flags;
- vec3_t mins, maxs;
-} aas_bbox_t;
-
-//============ settings ===========
-
-//reachability to another area
-typedef struct aas_reachability_s
-{
- int areanum; //number of the reachable area
- int facenum; //number of the face towards the other area
- int edgenum; //number of the edge towards the other area
- vec3_t start; //start point of inter area movement
- vec3_t end; //end point of inter area movement
- int traveltype; //type of travel required to get to the area
- unsigned short int traveltime;//travel time of the inter area movement
-} aas_reachability_t;
-
-//area settings
-typedef struct aas_areasettings_s
-{
- //could also add all kind of statistic fields
- int contents; //contents of the area
- int areaflags; //several area flags
- int presencetype; //how a bot can be present in this area
- int cluster; //cluster the area belongs to, if negative it's a portal
- int clusterareanum; //number of the area in the cluster
- int numreachableareas; //number of reachable areas from this one
- int firstreachablearea; //first reachable area in the reachable area index
-} aas_areasettings_t;
-
-//cluster portal
-typedef struct aas_portal_s
-{
- int areanum; //area that is the actual portal
- int frontcluster; //cluster at front of portal
- int backcluster; //cluster at back of portal
- int clusterareanum[2]; //number of the area in the front and back cluster
-} aas_portal_t;
-
-//cluster portal index
-typedef int aas_portalindex_t;
-
-//cluster
-typedef struct aas_cluster_s
-{
- int numareas; //number of areas in the cluster
- int numreachabilityareas; //number of areas with reachabilities
- int numportals; //number of cluster portals
- int firstportal; //first cluster portal in the index
-} aas_cluster_t;
-
-//============ 3d definition ============
-
-typedef vec3_t aas_vertex_t;
-
-//just a plane in the third dimension
-typedef struct aas_plane_s
-{
- vec3_t normal; //normal vector of the plane
- float dist; //distance of the plane (normal vector * distance = point in plane)
- int type;
-} aas_plane_t;
-
-//edge
-typedef struct aas_edge_s
-{
- int v[2]; //numbers of the vertexes of this edge
-} aas_edge_t;
-
-//edge index, negative if vertexes are reversed
-typedef int aas_edgeindex_t;
-
-//a face bounds an area, often it will also seperate two areas
-typedef struct aas_face_s
-{
- int planenum; //number of the plane this face is in
- int faceflags; //face flags (no use to create face settings for just this field)
- int numedges; //number of edges in the boundary of the face
- int firstedge; //first edge in the edge index
- int frontarea; //area at the front of this face
- int backarea; //area at the back of this face
-} aas_face_t;
-
-//face index, stores a negative index if backside of face
-typedef int aas_faceindex_t;
-
-//area with a boundary of faces
-typedef struct aas_area_s
-{
- int areanum; //number of this area
- //3d definition
- int numfaces; //number of faces used for the boundary of the area
- int firstface; //first face in the face index used for the boundary of the area
- vec3_t mins; //mins of the area
- vec3_t maxs; //maxs of the area
- vec3_t center; //'center' of the area
-} aas_area_t;
-
-//nodes of the bsp tree
-typedef struct aas_node_s
-{
- int planenum;
- int children[2]; //child nodes of this node, or areas as leaves when negative
- //when a child is zero it's a solid leaf
-} aas_node_t;
-
-//=========== aas file ===============
-
-//header lump
-typedef struct
-{
- int fileofs;
- int filelen;
-} aas_lump_t;
-
-//aas file header
-typedef struct aas_header_s
-{
- int ident;
- int version;
- int bspchecksum;
- //data entries
- aas_lump_t lumps[AAS_LUMPS];
-} aas_header_t;
-
-
-//====== additional information ======
-/*
-
-- when a node child is a solid leaf the node child number is zero
-- two adjacent areas (sharing a plane at opposite sides) share a face
- this face is a portal between the areas
-- when an area uses a face from the faceindex with a positive index
- then the face plane normal points into the area
-- the face edges are stored counter clockwise using the edgeindex
-- two adjacent convex areas (sharing a face) only share One face
- this is a simple result of the areas being convex
-- the areas can't have a mixture of ground and gap faces
- other mixtures of faces in one area are allowed
-- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have
- the cluster number set to the negative portal number
-- edge zero is a dummy
-- face zero is a dummy
-- area zero is a dummy
-- node zero is a dummy
-*/
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +//NOTE: int = default signed +// default long + +#define AASID (('S'<<24)+('A'<<16)+('A'<<8)+'E') +#define AASVERSION_OLD 4 +#define AASVERSION 5 + +//presence types +#define PRESENCE_NONE 1 +#define PRESENCE_NORMAL 2 +#define PRESENCE_CROUCH 4 + +//travel types +#define MAX_TRAVELTYPES 32 +#define TRAVEL_INVALID 1 //temporary not possible +#define TRAVEL_WALK 2 //walking +#define TRAVEL_CROUCH 3 //crouching +#define TRAVEL_BARRIERJUMP 4 //jumping onto a barrier +#define TRAVEL_JUMP 5 //jumping +#define TRAVEL_LADDER 6 //climbing a ladder +#define TRAVEL_WALKOFFLEDGE 7 //walking of a ledge +#define TRAVEL_SWIM 8 //swimming +#define TRAVEL_WATERJUMP 9 //jump out of the water +#define TRAVEL_TELEPORT 10 //teleportation +#define TRAVEL_ELEVATOR 11 //travel by elevator +#define TRAVEL_ROCKETJUMP 12 //rocket jumping required for travel +#define TRAVEL_BFGJUMP 13 //bfg jumping required for travel +#define TRAVEL_GRAPPLEHOOK 14 //grappling hook required for travel +#define TRAVEL_DOUBLEJUMP 15 //double jump +#define TRAVEL_RAMPJUMP 16 //ramp jump +#define TRAVEL_STRAFEJUMP 17 //strafe jump +#define TRAVEL_JUMPPAD 18 //jump pad +#define TRAVEL_FUNCBOB 19 //func bob + +//additional travel flags +#define TRAVELTYPE_MASK 0xFFFFFF +#define TRAVELFLAG_NOTTEAM1 (1 << 24) +#define TRAVELFLAG_NOTTEAM2 (2 << 24) + +//face flags +#define FACE_SOLID 1 //just solid at the other side +#define FACE_LADDER 2 //ladder +#define FACE_GROUND 4 //standing on ground when in this face +#define FACE_GAP 8 //gap in the ground +#define FACE_LIQUID 16 //face seperating two areas with liquid +#define FACE_LIQUIDSURFACE 32 //face seperating liquid and air +#define FACE_BRIDGE 64 //can walk over this face if bridge is closed + +//area contents +#define AREACONTENTS_WATER 1 +#define AREACONTENTS_LAVA 2 +#define AREACONTENTS_SLIME 4 +#define AREACONTENTS_CLUSTERPORTAL 8 +#define AREACONTENTS_TELEPORTAL 16 +#define AREACONTENTS_ROUTEPORTAL 32 +#define AREACONTENTS_TELEPORTER 64 +#define AREACONTENTS_JUMPPAD 128 +#define AREACONTENTS_DONOTENTER 256 +#define AREACONTENTS_VIEWPORTAL 512 +#define AREACONTENTS_MOVER 1024 +#define AREACONTENTS_NOTTEAM1 2048 +#define AREACONTENTS_NOTTEAM2 4096 +//number of model of the mover inside this area +#define AREACONTENTS_MODELNUMSHIFT 24 +#define AREACONTENTS_MAXMODELNUM 0xFF +#define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) + +//area flags +#define AREA_GROUNDED 1 //bot can stand on the ground +#define AREA_LADDER 2 //area contains one or more ladder faces +#define AREA_LIQUID 4 //area contains a liquid +#define AREA_DISABLED 8 //area is disabled for routing when set +#define AREA_BRIDGE 16 //area ontop of a bridge + +//aas file header lumps +#define AAS_LUMPS 14 +#define AASLUMP_BBOXES 0 +#define AASLUMP_VERTEXES 1 +#define AASLUMP_PLANES 2 +#define AASLUMP_EDGES 3 +#define AASLUMP_EDGEINDEX 4 +#define AASLUMP_FACES 5 +#define AASLUMP_FACEINDEX 6 +#define AASLUMP_AREAS 7 +#define AASLUMP_AREASETTINGS 8 +#define AASLUMP_REACHABILITY 9 +#define AASLUMP_NODES 10 +#define AASLUMP_PORTALS 11 +#define AASLUMP_PORTALINDEX 12 +#define AASLUMP_CLUSTERS 13 + +//========== bounding box ========= + +//bounding box +typedef struct aas_bbox_s +{ + int presencetype; + int flags; + vec3_t mins, maxs; +} aas_bbox_t; + +//============ settings =========== + +//reachability to another area +typedef struct aas_reachability_s +{ + int areanum; //number of the reachable area + int facenum; //number of the face towards the other area + int edgenum; //number of the edge towards the other area + vec3_t start; //start point of inter area movement + vec3_t end; //end point of inter area movement + int traveltype; //type of travel required to get to the area + unsigned short int traveltime;//travel time of the inter area movement +} aas_reachability_t; + +//area settings +typedef struct aas_areasettings_s +{ + //could also add all kind of statistic fields + int contents; //contents of the area + int areaflags; //several area flags + int presencetype; //how a bot can be present in this area + int cluster; //cluster the area belongs to, if negative it's a portal + int clusterareanum; //number of the area in the cluster + int numreachableareas; //number of reachable areas from this one + int firstreachablearea; //first reachable area in the reachable area index +} aas_areasettings_t; + +//cluster portal +typedef struct aas_portal_s +{ + int areanum; //area that is the actual portal + int frontcluster; //cluster at front of portal + int backcluster; //cluster at back of portal + int clusterareanum[2]; //number of the area in the front and back cluster +} aas_portal_t; + +//cluster portal index +typedef int aas_portalindex_t; + +//cluster +typedef struct aas_cluster_s +{ + int numareas; //number of areas in the cluster + int numreachabilityareas; //number of areas with reachabilities + int numportals; //number of cluster portals + int firstportal; //first cluster portal in the index +} aas_cluster_t; + +//============ 3d definition ============ + +typedef vec3_t aas_vertex_t; + +//just a plane in the third dimension +typedef struct aas_plane_s +{ + vec3_t normal; //normal vector of the plane + float dist; //distance of the plane (normal vector * distance = point in plane) + int type; +} aas_plane_t; + +//edge +typedef struct aas_edge_s +{ + int v[2]; //numbers of the vertexes of this edge +} aas_edge_t; + +//edge index, negative if vertexes are reversed +typedef int aas_edgeindex_t; + +//a face bounds an area, often it will also seperate two areas +typedef struct aas_face_s +{ + int planenum; //number of the plane this face is in + int faceflags; //face flags (no use to create face settings for just this field) + int numedges; //number of edges in the boundary of the face + int firstedge; //first edge in the edge index + int frontarea; //area at the front of this face + int backarea; //area at the back of this face +} aas_face_t; + +//face index, stores a negative index if backside of face +typedef int aas_faceindex_t; + +//area with a boundary of faces +typedef struct aas_area_s +{ + int areanum; //number of this area + //3d definition + int numfaces; //number of faces used for the boundary of the area + int firstface; //first face in the face index used for the boundary of the area + vec3_t mins; //mins of the area + vec3_t maxs; //maxs of the area + vec3_t center; //'center' of the area +} aas_area_t; + +//nodes of the bsp tree +typedef struct aas_node_s +{ + int planenum; + int children[2]; //child nodes of this node, or areas as leaves when negative + //when a child is zero it's a solid leaf +} aas_node_t; + +//=========== aas file =============== + +//header lump +typedef struct +{ + int fileofs; + int filelen; +} aas_lump_t; + +//aas file header +typedef struct aas_header_s +{ + int ident; + int version; + int bspchecksum; + //data entries + aas_lump_t lumps[AAS_LUMPS]; +} aas_header_t; + + +//====== additional information ====== +/* + +- when a node child is a solid leaf the node child number is zero +- two adjacent areas (sharing a plane at opposite sides) share a face + this face is a portal between the areas +- when an area uses a face from the faceindex with a positive index + then the face plane normal points into the area +- the face edges are stored counter clockwise using the edgeindex +- two adjacent convex areas (sharing a face) only share One face + this is a simple result of the areas being convex +- the areas can't have a mixture of ground and gap faces + other mixtures of faces in one area are allowed +- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have + the cluster number set to the negative portal number +- edge zero is a dummy +- face zero is a dummy +- area zero is a dummy +- node zero is a dummy +*/ diff --git a/code/botlib/be_aas_bsp.h b/code/botlib/be_aas_bsp.h index d55353c..aa05e4b 100755 --- a/code/botlib/be_aas_bsp.h +++ b/code/botlib/be_aas_bsp.h @@ -1,89 +1,89 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_bsp.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_bsp.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-//loads the given BSP file
-int AAS_LoadBSPFile(void);
-//dump the loaded BSP data
-void AAS_DumpBSPData(void);
-//unlink the given entity from the bsp tree leaves
-void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves);
-//link the given entity to the bsp tree leaves of the given model
-bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins,
- vec3_t absmaxs,
- int entnum,
- int modelnum);
-
-//calculates collision with given entity
-qboolean AAS_EntityCollision(int entnum,
- vec3_t start,
- vec3_t boxmins,
- vec3_t boxmaxs,
- vec3_t end,
- int contentmask,
- bsp_trace_t *trace);
-//for debugging
-void AAS_PrintFreeBSPLinks(char *str);
-//
-#endif //AASINTERN
-
-#define MAX_EPAIRKEY 128
-
-//trace through the world
-bsp_trace_t AAS_Trace( vec3_t start,
- vec3_t mins,
- vec3_t maxs,
- vec3_t end,
- int passent,
- int contentmask);
-//returns the contents at the given point
-int AAS_PointContents(vec3_t point);
-//returns true when p2 is in the PVS of p1
-qboolean AAS_inPVS(vec3_t p1, vec3_t p2);
-//returns true when p2 is in the PHS of p1
-qboolean AAS_inPHS(vec3_t p1, vec3_t p2);
-//returns true if the given areas are connected
-qboolean AAS_AreasConnected(int area1, int area2);
-//creates a list with entities totally or partly within the given box
-int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount);
-//gets the mins, maxs and origin of a BSP model
-void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin);
-//handle to the next bsp entity
-int AAS_NextBSPEntity(int ent);
-//return the value of the BSP epair key
-int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size);
-//get a vector for the BSP epair key
-int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v);
-//get a float for the BSP epair key
-int AAS_FloatForBSPEpairKey(int ent, char *key, float *value);
-//get an integer for the BSP epair key
-int AAS_IntForBSPEpairKey(int ent, char *key, int *value);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_bsp.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_bsp.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//loads the given BSP file +int AAS_LoadBSPFile(void); +//dump the loaded BSP data +void AAS_DumpBSPData(void); +//unlink the given entity from the bsp tree leaves +void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves); +//link the given entity to the bsp tree leaves of the given model +bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, + vec3_t absmaxs, + int entnum, + int modelnum); + +//calculates collision with given entity +qboolean AAS_EntityCollision(int entnum, + vec3_t start, + vec3_t boxmins, + vec3_t boxmaxs, + vec3_t end, + int contentmask, + bsp_trace_t *trace); +//for debugging +void AAS_PrintFreeBSPLinks(char *str); +// +#endif //AASINTERN + +#define MAX_EPAIRKEY 128 + +//trace through the world +bsp_trace_t AAS_Trace( vec3_t start, + vec3_t mins, + vec3_t maxs, + vec3_t end, + int passent, + int contentmask); +//returns the contents at the given point +int AAS_PointContents(vec3_t point); +//returns true when p2 is in the PVS of p1 +qboolean AAS_inPVS(vec3_t p1, vec3_t p2); +//returns true when p2 is in the PHS of p1 +qboolean AAS_inPHS(vec3_t p1, vec3_t p2); +//returns true if the given areas are connected +qboolean AAS_AreasConnected(int area1, int area2); +//creates a list with entities totally or partly within the given box +int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount); +//gets the mins, maxs and origin of a BSP model +void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin); +//handle to the next bsp entity +int AAS_NextBSPEntity(int ent); +//return the value of the BSP epair key +int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size); +//get a vector for the BSP epair key +int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v); +//get a float for the BSP epair key +int AAS_FloatForBSPEpairKey(int ent, char *key, float *value); +//get an integer for the BSP epair key +int AAS_IntForBSPEpairKey(int ent, char *key, int *value); + diff --git a/code/botlib/be_aas_bspq3.c b/code/botlib/be_aas_bspq3.c index a5e0d44..97c286d 100755 --- a/code/botlib/be_aas_bspq3.c +++ b/code/botlib/be_aas_bspq3.c @@ -1,487 +1,487 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_bspq3.c
- *
- * desc: BSP, Environment Sampling
- *
- * $Archive: /MissionPack/code/botlib/be_aas_bspq3.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-
-extern botlib_import_t botimport;
-
-//#define TRACE_DEBUG
-
-#define ON_EPSILON 0.005
-//#define DEG2RAD( a ) (( a * M_PI ) / 180.0F)
-
-#define MAX_BSPENTITIES 2048
-
-typedef struct rgb_s
-{
- int red;
- int green;
- int blue;
-} rgb_t;
-
-//bsp entity epair
-typedef struct bsp_epair_s
-{
- char *key;
- char *value;
- struct bsp_epair_s *next;
-} bsp_epair_t;
-
-//bsp data entity
-typedef struct bsp_entity_s
-{
- bsp_epair_t *epairs;
-} bsp_entity_t;
-
-//id Sofware BSP data
-typedef struct bsp_s
-{
- //true when bsp file is loaded
- int loaded;
- //entity data
- int entdatasize;
- char *dentdata;
- //bsp entities
- int numentities;
- bsp_entity_t entities[MAX_BSPENTITIES];
-} bsp_t;
-
-//global bsp
-bsp_t bspworld;
-
-
-#ifdef BSP_DEBUG
-typedef struct cname_s
-{
- int value;
- char *name;
-} cname_t;
-
-cname_t contentnames[] =
-{
- {CONTENTS_SOLID,"CONTENTS_SOLID"},
- {CONTENTS_WINDOW,"CONTENTS_WINDOW"},
- {CONTENTS_AUX,"CONTENTS_AUX"},
- {CONTENTS_LAVA,"CONTENTS_LAVA"},
- {CONTENTS_SLIME,"CONTENTS_SLIME"},
- {CONTENTS_WATER,"CONTENTS_WATER"},
- {CONTENTS_MIST,"CONTENTS_MIST"},
- {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"},
-
- {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"},
- {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"},
- {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"},
- {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"},
- {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"},
- {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"},
- {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"},
- {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"},
- {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"},
- {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"},
- {CONTENTS_MONSTER,"CONTENTS_MONSTER"},
- {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"},
- {CONTENTS_DETAIL,"CONTENTS_DETAIL"},
- {CONTENTS_TRANSLUCENT,"CONTENTS_TRANSLUCENT"},
- {CONTENTS_LADDER,"CONTENTS_LADDER"},
- {0, 0}
-};
-
-void PrintContents(int contents)
-{
- int i;
-
- for (i = 0; contentnames[i].value; i++)
- {
- if (contents & contentnames[i].value)
- {
- botimport.Print(PRT_MESSAGE, "%s\n", contentnames[i].name);
- } //end if
- } //end for
-} //end of the function PrintContents
-
-#endif // BSP_DEBUG
-//===========================================================================
-// traces axial boxes of any size through the world
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask)
-{
- bsp_trace_t bsptrace;
- botimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask);
- return bsptrace;
-} //end of the function AAS_Trace
-//===========================================================================
-// returns the contents at the given point
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PointContents(vec3_t point)
-{
- return botimport.PointContents(point);
-} //end of the function AAS_PointContents
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_EntityCollision(int entnum,
- vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end,
- int contentmask, bsp_trace_t *trace)
-{
- bsp_trace_t enttrace;
-
- botimport.EntityTrace(&enttrace, start, boxmins, boxmaxs, end, entnum, contentmask);
- if (enttrace.fraction < trace->fraction)
- {
- Com_Memcpy(trace, &enttrace, sizeof(bsp_trace_t));
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function AAS_EntityCollision
-//===========================================================================
-// returns true if in Potentially Hearable Set
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_inPVS(vec3_t p1, vec3_t p2)
-{
- return botimport.inPVS(p1, p2);
-} //end of the function AAS_InPVS
-//===========================================================================
-// returns true if in Potentially Visible Set
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_inPHS(vec3_t p1, vec3_t p2)
-{
- return qtrue;
-} //end of the function AAS_inPHS
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin)
-{
- botimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
-} //end of the function AAS_BSPModelMinsMaxs
-//===========================================================================
-// unlinks the entity from all leaves
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves)
-{
-} //end of the function AAS_UnlinkFromBSPLeaves
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum)
-{
- return NULL;
-} //end of the function AAS_BSPLinkEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount)
-{
- return 0;
-} //end of the function AAS_BoxEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NextBSPEntity(int ent)
-{
- ent++;
- if (ent >= 1 && ent < bspworld.numentities) return ent;
- return 0;
-} //end of the function AAS_NextBSPEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BSPEntityInRange(int ent)
-{
- if (ent <= 0 || ent >= bspworld.numentities)
- {
- botimport.Print(PRT_MESSAGE, "bsp entity out of range\n");
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function AAS_BSPEntityInRange
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size)
-{
- bsp_epair_t *epair;
-
- value[0] = '\0';
- if (!AAS_BSPEntityInRange(ent)) return qfalse;
- for (epair = bspworld.entities[ent].epairs; epair; epair = epair->next)
- {
- if (!strcmp(epair->key, key))
- {
- strncpy(value, epair->value, size-1);
- value[size-1] = '\0';
- return qtrue;
- } //end if
- } //end for
- return qfalse;
-} //end of the function AAS_FindBSPEpair
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v)
-{
- char buf[MAX_EPAIRKEY];
- double v1, v2, v3;
-
- VectorClear(v);
- if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse;
- //scanf into doubles, then assign, so it is vec_t size independent
- v1 = v2 = v3 = 0;
- sscanf(buf, "%lf %lf %lf", &v1, &v2, &v3);
- v[0] = v1;
- v[1] = v2;
- v[2] = v3;
- return qtrue;
-} //end of the function AAS_VectorForBSPEpairKey
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_FloatForBSPEpairKey(int ent, char *key, float *value)
-{
- char buf[MAX_EPAIRKEY];
-
- *value = 0;
- if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse;
- *value = atof(buf);
- return qtrue;
-} //end of the function AAS_FloatForBSPEpairKey
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_IntForBSPEpairKey(int ent, char *key, int *value)
-{
- char buf[MAX_EPAIRKEY];
-
- *value = 0;
- if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse;
- *value = atoi(buf);
- return qtrue;
-} //end of the function AAS_IntForBSPEpairKey
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeBSPEntities(void)
-{
- int i;
- bsp_entity_t *ent;
- bsp_epair_t *epair, *nextepair;
-
- for (i = 1; i < bspworld.numentities; i++)
- {
- ent = &bspworld.entities[i];
- for (epair = ent->epairs; epair; epair = nextepair)
- {
- nextepair = epair->next;
- //
- if (epair->key) FreeMemory(epair->key);
- if (epair->value) FreeMemory(epair->value);
- FreeMemory(epair);
- } //end for
- } //end for
- bspworld.numentities = 0;
-} //end of the function AAS_FreeBSPEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ParseBSPEntities(void)
-{
- script_t *script;
- token_t token;
- bsp_entity_t *ent;
- bsp_epair_t *epair;
-
- script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata");
- SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE);
-
- bspworld.numentities = 1;
-
- while(PS_ReadToken(script, &token))
- {
- if (strcmp(token.string, "{"))
- {
- ScriptError(script, "invalid %s\n", token.string);
- AAS_FreeBSPEntities();
- FreeScript(script);
- return;
- } //end if
- if (bspworld.numentities >= MAX_BSPENTITIES)
- {
- botimport.Print(PRT_MESSAGE, "too many entities in BSP file\n");
- break;
- } //end if
- ent = &bspworld.entities[bspworld.numentities];
- bspworld.numentities++;
- ent->epairs = NULL;
- while(PS_ReadToken(script, &token))
- {
- if (!strcmp(token.string, "}")) break;
- epair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t));
- epair->next = ent->epairs;
- ent->epairs = epair;
- if (token.type != TT_STRING)
- {
- ScriptError(script, "invalid %s\n", token.string);
- AAS_FreeBSPEntities();
- FreeScript(script);
- return;
- } //end if
- StripDoubleQuotes(token.string);
- epair->key = (char *) GetHunkMemory(strlen(token.string) + 1);
- strcpy(epair->key, token.string);
- if (!PS_ExpectTokenType(script, TT_STRING, 0, &token))
- {
- AAS_FreeBSPEntities();
- FreeScript(script);
- return;
- } //end if
- StripDoubleQuotes(token.string);
- epair->value = (char *) GetHunkMemory(strlen(token.string) + 1);
- strcpy(epair->value, token.string);
- } //end while
- if (strcmp(token.string, "}"))
- {
- ScriptError(script, "missing }\n");
- AAS_FreeBSPEntities();
- FreeScript(script);
- return;
- } //end if
- } //end while
- FreeScript(script);
-} //end of the function AAS_ParseBSPEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BSPTraceLight(vec3_t start, vec3_t end, vec3_t endpos, int *red, int *green, int *blue)
-{
- return 0;
-} //end of the function AAS_BSPTraceLight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DumpBSPData(void)
-{
- AAS_FreeBSPEntities();
-
- if (bspworld.dentdata) FreeMemory(bspworld.dentdata);
- bspworld.dentdata = NULL;
- bspworld.entdatasize = 0;
- //
- bspworld.loaded = qfalse;
- Com_Memset( &bspworld, 0, sizeof(bspworld) );
-} //end of the function AAS_DumpBSPData
-//===========================================================================
-// load an bsp file
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_LoadBSPFile(void)
-{
- AAS_DumpBSPData();
- bspworld.entdatasize = strlen(botimport.BSPEntityData()) + 1;
- bspworld.dentdata = (char *) GetClearedHunkMemory(bspworld.entdatasize);
- Com_Memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize);
- AAS_ParseBSPEntities();
- bspworld.loaded = qtrue;
- return BLERR_NOERROR;
-} //end of the function AAS_LoadBSPFile
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_bspq3.c + * + * desc: BSP, Environment Sampling + * + * $Archive: /MissionPack/code/botlib/be_aas_bspq3.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +//#define TRACE_DEBUG + +#define ON_EPSILON 0.005 +//#define DEG2RAD( a ) (( a * M_PI ) / 180.0F) + +#define MAX_BSPENTITIES 2048 + +typedef struct rgb_s +{ + int red; + int green; + int blue; +} rgb_t; + +//bsp entity epair +typedef struct bsp_epair_s +{ + char *key; + char *value; + struct bsp_epair_s *next; +} bsp_epair_t; + +//bsp data entity +typedef struct bsp_entity_s +{ + bsp_epair_t *epairs; +} bsp_entity_t; + +//id Sofware BSP data +typedef struct bsp_s +{ + //true when bsp file is loaded + int loaded; + //entity data + int entdatasize; + char *dentdata; + //bsp entities + int numentities; + bsp_entity_t entities[MAX_BSPENTITIES]; +} bsp_t; + +//global bsp +bsp_t bspworld; + + +#ifdef BSP_DEBUG +typedef struct cname_s +{ + int value; + char *name; +} cname_t; + +cname_t contentnames[] = +{ + {CONTENTS_SOLID,"CONTENTS_SOLID"}, + {CONTENTS_WINDOW,"CONTENTS_WINDOW"}, + {CONTENTS_AUX,"CONTENTS_AUX"}, + {CONTENTS_LAVA,"CONTENTS_LAVA"}, + {CONTENTS_SLIME,"CONTENTS_SLIME"}, + {CONTENTS_WATER,"CONTENTS_WATER"}, + {CONTENTS_MIST,"CONTENTS_MIST"}, + {LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"}, + + {CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"}, + {CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"}, + {CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"}, + {CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"}, + {CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"}, + {CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"}, + {CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"}, + {CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"}, + {CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"}, + {CONTENTS_ORIGIN,"CONTENTS_ORIGIN"}, + {CONTENTS_MONSTER,"CONTENTS_MONSTER"}, + {CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"}, + {CONTENTS_DETAIL,"CONTENTS_DETAIL"}, + {CONTENTS_TRANSLUCENT,"CONTENTS_TRANSLUCENT"}, + {CONTENTS_LADDER,"CONTENTS_LADDER"}, + {0, 0} +}; + +void PrintContents(int contents) +{ + int i; + + for (i = 0; contentnames[i].value; i++) + { + if (contents & contentnames[i].value) + { + botimport.Print(PRT_MESSAGE, "%s\n", contentnames[i].name); + } //end if + } //end for +} //end of the function PrintContents + +#endif // BSP_DEBUG +//=========================================================================== +// traces axial boxes of any size through the world +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) +{ + bsp_trace_t bsptrace; + botimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask); + return bsptrace; +} //end of the function AAS_Trace +//=========================================================================== +// returns the contents at the given point +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointContents(vec3_t point) +{ + return botimport.PointContents(point); +} //end of the function AAS_PointContents +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_EntityCollision(int entnum, + vec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end, + int contentmask, bsp_trace_t *trace) +{ + bsp_trace_t enttrace; + + botimport.EntityTrace(&enttrace, start, boxmins, boxmaxs, end, entnum, contentmask); + if (enttrace.fraction < trace->fraction) + { + Com_Memcpy(trace, &enttrace, sizeof(bsp_trace_t)); + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_EntityCollision +//=========================================================================== +// returns true if in Potentially Hearable Set +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_inPVS(vec3_t p1, vec3_t p2) +{ + return botimport.inPVS(p1, p2); +} //end of the function AAS_InPVS +//=========================================================================== +// returns true if in Potentially Visible Set +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_inPHS(vec3_t p1, vec3_t p2) +{ + return qtrue; +} //end of the function AAS_inPHS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin) +{ + botimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); +} //end of the function AAS_BSPModelMinsMaxs +//=========================================================================== +// unlinks the entity from all leaves +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves) +{ +} //end of the function AAS_UnlinkFromBSPLeaves +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum) +{ + return NULL; +} //end of the function AAS_BSPLinkEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount) +{ + return 0; +} //end of the function AAS_BoxEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextBSPEntity(int ent) +{ + ent++; + if (ent >= 1 && ent < bspworld.numentities) return ent; + return 0; +} //end of the function AAS_NextBSPEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BSPEntityInRange(int ent) +{ + if (ent <= 0 || ent >= bspworld.numentities) + { + botimport.Print(PRT_MESSAGE, "bsp entity out of range\n"); + return qfalse; + } //end if + return qtrue; +} //end of the function AAS_BSPEntityInRange +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size) +{ + bsp_epair_t *epair; + + value[0] = '\0'; + if (!AAS_BSPEntityInRange(ent)) return qfalse; + for (epair = bspworld.entities[ent].epairs; epair; epair = epair->next) + { + if (!strcmp(epair->key, key)) + { + strncpy(value, epair->value, size-1); + value[size-1] = '\0'; + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function AAS_FindBSPEpair +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v) +{ + char buf[MAX_EPAIRKEY]; + double v1, v2, v3; + + VectorClear(v); + if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; + //scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = v3 = 0; + sscanf(buf, "%lf %lf %lf", &v1, &v2, &v3); + v[0] = v1; + v[1] = v2; + v[2] = v3; + return qtrue; +} //end of the function AAS_VectorForBSPEpairKey +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FloatForBSPEpairKey(int ent, char *key, float *value) +{ + char buf[MAX_EPAIRKEY]; + + *value = 0; + if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; + *value = atof(buf); + return qtrue; +} //end of the function AAS_FloatForBSPEpairKey +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_IntForBSPEpairKey(int ent, char *key, int *value) +{ + char buf[MAX_EPAIRKEY]; + + *value = 0; + if (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse; + *value = atoi(buf); + return qtrue; +} //end of the function AAS_IntForBSPEpairKey +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeBSPEntities(void) +{ + int i; + bsp_entity_t *ent; + bsp_epair_t *epair, *nextepair; + + for (i = 1; i < bspworld.numentities; i++) + { + ent = &bspworld.entities[i]; + for (epair = ent->epairs; epair; epair = nextepair) + { + nextepair = epair->next; + // + if (epair->key) FreeMemory(epair->key); + if (epair->value) FreeMemory(epair->value); + FreeMemory(epair); + } //end for + } //end for + bspworld.numentities = 0; +} //end of the function AAS_FreeBSPEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ParseBSPEntities(void) +{ + script_t *script; + token_t token; + bsp_entity_t *ent; + bsp_epair_t *epair; + + script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); + SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE); + + bspworld.numentities = 1; + + while(PS_ReadToken(script, &token)) + { + if (strcmp(token.string, "{")) + { + ScriptError(script, "invalid %s\n", token.string); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + if (bspworld.numentities >= MAX_BSPENTITIES) + { + botimport.Print(PRT_MESSAGE, "too many entities in BSP file\n"); + break; + } //end if + ent = &bspworld.entities[bspworld.numentities]; + bspworld.numentities++; + ent->epairs = NULL; + while(PS_ReadToken(script, &token)) + { + if (!strcmp(token.string, "}")) break; + epair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t)); + epair->next = ent->epairs; + ent->epairs = epair; + if (token.type != TT_STRING) + { + ScriptError(script, "invalid %s\n", token.string); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + StripDoubleQuotes(token.string); + epair->key = (char *) GetHunkMemory(strlen(token.string) + 1); + strcpy(epair->key, token.string); + if (!PS_ExpectTokenType(script, TT_STRING, 0, &token)) + { + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + StripDoubleQuotes(token.string); + epair->value = (char *) GetHunkMemory(strlen(token.string) + 1); + strcpy(epair->value, token.string); + } //end while + if (strcmp(token.string, "}")) + { + ScriptError(script, "missing }\n"); + AAS_FreeBSPEntities(); + FreeScript(script); + return; + } //end if + } //end while + FreeScript(script); +} //end of the function AAS_ParseBSPEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BSPTraceLight(vec3_t start, vec3_t end, vec3_t endpos, int *red, int *green, int *blue) +{ + return 0; +} //end of the function AAS_BSPTraceLight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DumpBSPData(void) +{ + AAS_FreeBSPEntities(); + + if (bspworld.dentdata) FreeMemory(bspworld.dentdata); + bspworld.dentdata = NULL; + bspworld.entdatasize = 0; + // + bspworld.loaded = qfalse; + Com_Memset( &bspworld, 0, sizeof(bspworld) ); +} //end of the function AAS_DumpBSPData +//=========================================================================== +// load an bsp file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadBSPFile(void) +{ + AAS_DumpBSPData(); + bspworld.entdatasize = strlen(botimport.BSPEntityData()) + 1; + bspworld.dentdata = (char *) GetClearedHunkMemory(bspworld.entdatasize); + Com_Memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize); + AAS_ParseBSPEntities(); + bspworld.loaded = qtrue; + return BLERR_NOERROR; +} //end of the function AAS_LoadBSPFile diff --git a/code/botlib/be_aas_cluster.c b/code/botlib/be_aas_cluster.c index 2fd569b..118dd0a 100755 --- a/code/botlib/be_aas_cluster.c +++ b/code/botlib/be_aas_cluster.c @@ -1,1545 +1,1545 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_cluster.c
- *
- * desc: area clustering
- *
- * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_log.h"
-#include "l_memory.h"
-#include "l_libvar.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-
-extern botlib_import_t botimport;
-
-#define AAS_MAX_PORTALS 65536
-#define AAS_MAX_PORTALINDEXSIZE 65536
-#define AAS_MAX_CLUSTERS 65536
-//
-#define MAX_PORTALAREAS 1024
-
-// do not flood through area faces, only use reachabilities
-int nofaceflood = qtrue;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemoveClusterAreas(void)
-{
- int i;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- aasworld.areasettings[i].cluster = 0;
- } //end for
-} //end of the function AAS_RemoveClusterAreas
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ClearCluster(int clusternum)
-{
- int i;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (aasworld.areasettings[i].cluster == clusternum)
- {
- aasworld.areasettings[i].cluster = 0;
- } //end if
- } //end for
-} //end of the function AAS_ClearCluster
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemovePortalsClusterReference(int clusternum)
-{
- int portalnum;
-
- for (portalnum = 1; portalnum < aasworld.numportals; portalnum++)
- {
- if (aasworld.portals[portalnum].frontcluster == clusternum)
- {
- aasworld.portals[portalnum].frontcluster = 0;
- } //end if
- if (aasworld.portals[portalnum].backcluster == clusternum)
- {
- aasworld.portals[portalnum].backcluster = 0;
- } //end if
- } //end for
-} //end of the function AAS_RemovePortalsClusterReference
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_UpdatePortal(int areanum, int clusternum)
-{
- int portalnum;
- aas_portal_t *portal;
- aas_cluster_t *cluster;
-
- //find the portal of the area
- for (portalnum = 1; portalnum < aasworld.numportals; portalnum++)
- {
- if (aasworld.portals[portalnum].areanum == areanum) break;
- } //end for
- //
- if (portalnum == aasworld.numportals)
- {
- AAS_Error("no portal of area %d", areanum);
- return qtrue;
- } //end if
- //
- portal = &aasworld.portals[portalnum];
- //if the portal is already fully updated
- if (portal->frontcluster == clusternum) return qtrue;
- if (portal->backcluster == clusternum) return qtrue;
- //if the portal has no front cluster yet
- if (!portal->frontcluster)
- {
- portal->frontcluster = clusternum;
- } //end if
- //if the portal has no back cluster yet
- else if (!portal->backcluster)
- {
- portal->backcluster = clusternum;
- } //end else if
- else
- {
- //remove the cluster portal flag contents
- aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- Log_Write("portal area %d is seperating more than two clusters\r\n", areanum);
- return qfalse;
- } //end else
- if (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE)
- {
- AAS_Error("AAS_MAX_PORTALINDEXSIZE");
- return qtrue;
- } //end if
- //set the area cluster number to the negative portal number
- aasworld.areasettings[areanum].cluster = -portalnum;
- //add the portal to the cluster using the portal index
- cluster = &aasworld.clusters[clusternum];
- aasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum;
- aasworld.portalindexsize++;
- cluster->numportals++;
- return qtrue;
-} //end of the function AAS_UpdatePortal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_FloodClusterAreas_r(int areanum, int clusternum)
-{
- aas_area_t *area;
- aas_face_t *face;
- int facenum, i;
-
- //
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- AAS_Error("AAS_FloodClusterAreas_r: areanum out of range");
- return qfalse;
- } //end if
- //if the area is already part of a cluster
- if (aasworld.areasettings[areanum].cluster > 0)
- {
- if (aasworld.areasettings[areanum].cluster == clusternum) return qtrue;
- //
- //there's a reachability going from one cluster to another only in one direction
- //
- AAS_Error("cluster %d touched cluster %d at area %d\r\n",
- clusternum, aasworld.areasettings[areanum].cluster, areanum);
- return qfalse;
- } //end if
- //don't add the cluster portal areas to the clusters
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- return AAS_UpdatePortal(areanum, clusternum);
- } //end if
- //set the area cluster number
- aasworld.areasettings[areanum].cluster = clusternum;
- aasworld.areasettings[areanum].clusterareanum =
- aasworld.clusters[clusternum].numareas;
- //the cluster has an extra area
- aasworld.clusters[clusternum].numareas++;
-
- area = &aasworld.areas[areanum];
- //use area faces to flood into adjacent areas
- if (!nofaceflood)
- {
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- face = &aasworld.faces[facenum];
- if (face->frontarea == areanum)
- {
- if (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse;
- } //end if
- else
- {
- if (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse;
- } //end else
- } //end for
- } //end if
- //use the reachabilities to flood into other areas
- for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++)
- {
- if (!aasworld.reachability[
- aasworld.areasettings[areanum].firstreachablearea + i].areanum)
- {
- continue;
- } //end if
- if (!AAS_FloodClusterAreas_r(aasworld.reachability[
- aasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse;
- } //end for
- return qtrue;
-} //end of the function AAS_FloodClusterAreas_r
-//===========================================================================
-// try to flood from all areas without cluster into areas with a cluster set
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_FloodClusterAreasUsingReachabilities(int clusternum)
-{
- int i, j, areanum;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- //if this area already has a cluster set
- if (aasworld.areasettings[i].cluster)
- continue;
- //if this area is a cluster portal
- if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
- continue;
- //loop over the reachable areas from this area
- for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
- {
- //the reachable area
- areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
- //if this area is a cluster portal
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL)
- continue;
- //if this area has a cluster set
- if (aasworld.areasettings[areanum].cluster)
- {
- if (!AAS_FloodClusterAreas_r(i, clusternum))
- return qfalse;
- i = 0;
- break;
- } //end if
- } //end for
- } //end for
- return qtrue;
-} //end of the function AAS_FloodClusterAreasUsingReachabilities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_NumberClusterPortals(int clusternum)
-{
- int i, portalnum;
- aas_cluster_t *cluster;
- aas_portal_t *portal;
-
- cluster = &aasworld.clusters[clusternum];
- for (i = 0; i < cluster->numportals; i++)
- {
- portalnum = aasworld.portalindex[cluster->firstportal + i];
- portal = &aasworld.portals[portalnum];
- if (portal->frontcluster == clusternum)
- {
- portal->clusterareanum[0] = cluster->numareas++;
- } //end if
- else
- {
- portal->clusterareanum[1] = cluster->numareas++;
- } //end else
- } //end for
-} //end of the function AAS_NumberClusterPortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_NumberClusterAreas(int clusternum)
-{
- int i, portalnum;
- aas_cluster_t *cluster;
- aas_portal_t *portal;
-
- aasworld.clusters[clusternum].numareas = 0;
- aasworld.clusters[clusternum].numreachabilityareas = 0;
- //number all areas in this cluster WITH reachabilities
- for (i = 1; i < aasworld.numareas; i++)
- {
- //
- if (aasworld.areasettings[i].cluster != clusternum) continue;
- //
- if (!AAS_AreaReachability(i)) continue;
- //
- aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas;
- //the cluster has an extra area
- aasworld.clusters[clusternum].numareas++;
- aasworld.clusters[clusternum].numreachabilityareas++;
- } //end for
- //number all portals in this cluster WITH reachabilities
- cluster = &aasworld.clusters[clusternum];
- for (i = 0; i < cluster->numportals; i++)
- {
- portalnum = aasworld.portalindex[cluster->firstportal + i];
- portal = &aasworld.portals[portalnum];
- if (!AAS_AreaReachability(portal->areanum)) continue;
- if (portal->frontcluster == clusternum)
- {
- portal->clusterareanum[0] = cluster->numareas++;
- aasworld.clusters[clusternum].numreachabilityareas++;
- } //end if
- else
- {
- portal->clusterareanum[1] = cluster->numareas++;
- aasworld.clusters[clusternum].numreachabilityareas++;
- } //end else
- } //end for
- //number all areas in this cluster WITHOUT reachabilities
- for (i = 1; i < aasworld.numareas; i++)
- {
- //
- if (aasworld.areasettings[i].cluster != clusternum) continue;
- //
- if (AAS_AreaReachability(i)) continue;
- //
- aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas;
- //the cluster has an extra area
- aasworld.clusters[clusternum].numareas++;
- } //end for
- //number all portals in this cluster WITHOUT reachabilities
- cluster = &aasworld.clusters[clusternum];
- for (i = 0; i < cluster->numportals; i++)
- {
- portalnum = aasworld.portalindex[cluster->firstportal + i];
- portal = &aasworld.portals[portalnum];
- if (AAS_AreaReachability(portal->areanum)) continue;
- if (portal->frontcluster == clusternum)
- {
- portal->clusterareanum[0] = cluster->numareas++;
- } //end if
- else
- {
- portal->clusterareanum[1] = cluster->numareas++;
- } //end else
- } //end for
-} //end of the function AAS_NumberClusterAreas
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_FindClusters(void)
-{
- int i;
- aas_cluster_t *cluster;
-
- AAS_RemoveClusterAreas();
- //
- for (i = 1; i < aasworld.numareas; i++)
- {
- //if the area is already part of a cluster
- if (aasworld.areasettings[i].cluster)
- continue;
- // if not flooding through faces only use areas that have reachabilities
- if (nofaceflood)
- {
- if (!aasworld.areasettings[i].numreachableareas)
- continue;
- } //end if
- //if the area is a cluster portal
- if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
- continue;
- if (aasworld.numclusters >= AAS_MAX_CLUSTERS)
- {
- AAS_Error("AAS_MAX_CLUSTERS");
- return qfalse;
- } //end if
- cluster = &aasworld.clusters[aasworld.numclusters];
- cluster->numareas = 0;
- cluster->numreachabilityareas = 0;
- cluster->firstportal = aasworld.portalindexsize;
- cluster->numportals = 0;
- //flood the areas in this cluster
- if (!AAS_FloodClusterAreas_r(i, aasworld.numclusters))
- return qfalse;
- if (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters))
- return qfalse;
- //number the cluster areas
- //AAS_NumberClusterPortals(aasworld.numclusters);
- AAS_NumberClusterAreas(aasworld.numclusters);
- //Log_Write("cluster %d has %d areas\r\n", aasworld.numclusters, cluster->numareas);
- aasworld.numclusters++;
- } //end for
- return qtrue;
-} //end of the function AAS_FindClusters
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_CreatePortals(void)
-{
- int i;
- aas_portal_t *portal;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- //if the area is a cluster portal
- if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- if (aasworld.numportals >= AAS_MAX_PORTALS)
- {
- AAS_Error("AAS_MAX_PORTALS");
- return;
- } //end if
- portal = &aasworld.portals[aasworld.numportals];
- portal->areanum = i;
- portal->frontcluster = 0;
- portal->backcluster = 0;
- aasworld.numportals++;
- } //end if
- } //end for
-} //end of the function AAS_CreatePortals
-/*
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_MapContainsTeleporters(void)
-{
- bsp_entity_t *entities, *ent;
- char *classname;
-
- entities = AAS_ParseBSPEntities();
-
- for (ent = entities; ent; ent = ent->next)
- {
- classname = AAS_ValueForBSPEpairKey(ent, "classname");
- if (classname && !strcmp(classname, "misc_teleporter"))
- {
- AAS_FreeBSPEntities(entities);
- return qtrue;
- } //end if
- } //end for
- return qfalse;
-} //end of the function AAS_MapContainsTeleporters
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2)
-{
- int i, j, edgenum;
- aas_plane_t *plane1, *plane2;
- aas_edge_t *edge;
-
-
- plane1 = &aasworld.planes[face1->planenum ^ side1];
- plane2 = &aasworld.planes[face2->planenum ^ side2];
-
- //check if one of the points of face1 is at the back of the plane of face2
- for (i = 0; i < face1->numedges; i++)
- {
- edgenum = abs(aasworld.edgeindex[face1->firstedge + i]);
- edge = &aasworld.edges[edgenum];
- for (j = 0; j < 2; j++)
- {
- if (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) -
- plane2->dist < -0.01) return qtrue;
- } //end for
- } //end for
- for (i = 0; i < face2->numedges; i++)
- {
- edgenum = abs(aasworld.edgeindex[face2->firstedge + i]);
- edge = &aasworld.edges[edgenum];
- for (j = 0; j < 2; j++)
- {
- if (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) -
- plane1->dist < -0.01) return qtrue;
- } //end for
- } //end for
-
- return qfalse;
-} //end of the function AAS_NonConvexFaces
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_CanMergeAreas(int *areanums, int numareas)
-{
- int i, j, s, face1num, face2num, side1, side2, fn1, fn2;
- aas_face_t *face1, *face2;
- aas_area_t *area1, *area2;
-
- for (i = 0; i < numareas; i++)
- {
- area1 = &aasworld.areas[areanums[i]];
- for (fn1 = 0; fn1 < area1->numfaces; fn1++)
- {
- face1num = abs(aasworld.faceindex[area1->firstface + fn1]);
- face1 = &aasworld.faces[face1num];
- side1 = face1->frontarea != areanums[i];
- //check if the face isn't a shared one with one of the other areas
- for (s = 0; s < numareas; s++)
- {
- if (s == i) continue;
- if (face1->frontarea == s || face1->backarea == s) break;
- } //end for
- //if the face was a shared one
- if (s != numareas) continue;
- //
- for (j = 0; j < numareas; j++)
- {
- if (j == i) continue;
- area2 = &aasworld.areas[areanums[j]];
- for (fn2 = 0; fn2 < area2->numfaces; fn2++)
- {
- face2num = abs(aasworld.faceindex[area2->firstface + fn2]);
- face2 = &aasworld.faces[face2num];
- side2 = face2->frontarea != areanums[j];
- //check if the face isn't a shared one with one of the other areas
- for (s = 0; s < numareas; s++)
- {
- if (s == j) continue;
- if (face2->frontarea == s || face2->backarea == s) break;
- } //end for
- //if the face was a shared one
- if (s != numareas) continue;
- //
- if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse;
- } //end for
- } //end for
- } //end for
- } //end for
- return qtrue;
-} //end of the function AAS_CanMergeAreas
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum)
-{
- int i;
- vec3_t edgevec1, edgevec2, normal1, normal2;
- float dist1, dist2;
- aas_plane_t *plane;
-
- plane = &aasworld.planes[planenum];
- VectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1);
- VectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2);
- if (side1) VectorInverse(edgevec1);
- if (side2) VectorInverse(edgevec2);
- //
- CrossProduct(edgevec1, plane->normal, normal1);
- dist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]);
- CrossProduct(edgevec2, plane->normal, normal2);
- dist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]);
-
- for (i = 0; i < 2; i++)
- {
- if (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse;
- } //end for
- for (i = 0; i < 2; i++)
- {
- if (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse;
- } //end for
- return qtrue;
-} //end of the function AAS_NonConvexEdges
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum)
-{
- int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens;
- aas_face_t *face1, *face2, *otherface;
- aas_edge_t *edge1, *edge2;
-
- for (i = 0; i < numfaces; i++)
- {
- face1 = &aasworld.faces[facenums[i]];
- for (en1 = 0; en1 < face1->numedges; en1++)
- {
- edgenum1 = aasworld.edgeindex[face1->firstedge + en1];
- side1 = (edgenum1 < 0) ^ (face1->planenum != planenum);
- edgenum1 = abs(edgenum1);
- edge1 = &aasworld.edges[edgenum1];
- //check if the edge is shared with another face
- for (s = 0; s < numfaces; s++)
- {
- if (s == i) continue;
- otherface = &aasworld.faces[facenums[s]];
- for (ens = 0; ens < otherface->numedges; ens++)
- {
- if (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break;
- } //end for
- if (ens != otherface->numedges) break;
- } //end for
- //if the edge was shared
- if (s != numfaces) continue;
- //
- for (j = 0; j < numfaces; j++)
- {
- if (j == i) continue;
- face2 = &aasworld.faces[facenums[j]];
- for (en2 = 0; en2 < face2->numedges; en2++)
- {
- edgenum2 = aasworld.edgeindex[face2->firstedge + en2];
- side2 = (edgenum2 < 0) ^ (face2->planenum != planenum);
- edgenum2 = abs(edgenum2);
- edge2 = &aasworld.edges[edgenum2];
- //check if the edge is shared with another face
- for (s = 0; s < numfaces; s++)
- {
- if (s == i) continue;
- otherface = &aasworld.faces[facenums[s]];
- for (ens = 0; ens < otherface->numedges; ens++)
- {
- if (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break;
- } //end for
- if (ens != otherface->numedges) break;
- } //end for
- //if the edge was shared
- if (s != numfaces) continue;
- //
- if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse;
- } //end for
- } //end for
- } //end for
- } //end for
- return qtrue;
-} //end of the function AAS_CanMergeFaces*/
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea)
-{
- int i, j, otherareanum, facenum;
- aas_area_t *area;
- aas_face_t *face;
-
- connectedareas[curarea] = qtrue;
- area = &aasworld.areas[areanums[curarea]];
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- face = &aasworld.faces[facenum];
- //if the face is solid
- if (face->faceflags & FACE_SOLID) continue;
- //get the area at the other side of the face
- if (face->frontarea != areanums[curarea]) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //check if the face is leading to one of the other areas
- for (j = 0; j < numareas; j++)
- {
- if (areanums[j] == otherareanum) break;
- } //end for
- //if the face isn't leading to one of the other areas
- if (j == numareas) continue;
- //if the other area is already connected
- if (connectedareas[j]) continue;
- //recursively proceed with the other area
- AAS_ConnectedAreas_r(areanums, numareas, connectedareas, j);
- } //end for
-} //end of the function AAS_ConnectedAreas_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_ConnectedAreas(int *areanums, int numareas)
-{
- int connectedareas[MAX_PORTALAREAS], i;
-
- Com_Memset(connectedareas, 0, sizeof(connectedareas));
- if (numareas < 1) return qfalse;
- if (numareas == 1) return qtrue;
- AAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0);
- for (i = 0; i < numareas; i++)
- {
- if (!connectedareas[i]) return qfalse;
- } //end for
- return qtrue;
-} //end of the function AAS_ConnectedAreas
-//===========================================================================
-// gets adjacent areas with less presence types recursively
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum)
-{
- int i, j, presencetype, otherpresencetype, otherareanum, facenum;
- aas_area_t *area;
- aas_face_t *face;
-
- areanums[numareas++] = curareanum;
- area = &aasworld.areas[curareanum];
- presencetype = aasworld.areasettings[curareanum].presencetype;
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- face = &aasworld.faces[facenum];
- //if the face is solid
- if (face->faceflags & FACE_SOLID) continue;
- //the area at the other side of the face
- if (face->frontarea != curareanum) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //
- otherpresencetype = aasworld.areasettings[otherareanum].presencetype;
- //if the other area has less presence types
- if ((presencetype & ~otherpresencetype) &&
- !(otherpresencetype & ~presencetype))
- {
- //check if the other area isn't already in the list
- for (j = 0; j < numareas; j++)
- {
- if (otherareanum == areanums[j]) break;
- } //end for
- //if the other area isn't already in the list
- if (j == numareas)
- {
- if (numareas >= MAX_PORTALAREAS)
- {
- AAS_Error("MAX_PORTALAREAS");
- return numareas;
- } //end if
- numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum);
- } //end if
- } //end if
- } //end for
- return numareas;
-} //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_CheckAreaForPossiblePortals(int areanum)
-{
- int i, j, k, fen, ben, frontedgenum, backedgenum, facenum;
- int areanums[MAX_PORTALAREAS], numareas, otherareanum;
- int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS];
- int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS];
- int numfrontfaces, numbackfaces;
- int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS];
- int numfrontareas, numbackareas;
- int frontplanenum, backplanenum, faceplanenum;
- aas_area_t *area;
- aas_face_t *frontface, *backface, *face;
-
- //if it isn't already a portal
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0;
- //it must be a grounded area
- if (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0;
- //
- Com_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces));
- Com_Memset(numareabackfaces, 0, sizeof(numareabackfaces));
- numareas = numfrontfaces = numbackfaces = 0;
- numfrontareas = numbackareas = 0;
- frontplanenum = backplanenum = -1;
- //add any adjacent areas with less presence types
- numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum);
- //
- for (i = 0; i < numareas; i++)
- {
- area = &aasworld.areas[areanums[i]];
- for (j = 0; j < area->numfaces; j++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + j]);
- face = &aasworld.faces[facenum];
- //if the face is solid
- if (face->faceflags & FACE_SOLID) continue;
- //check if the face is shared with one of the other areas
- for (k = 0; k < numareas; k++)
- {
- if (k == i) continue;
- if (face->frontarea == areanums[k] || face->backarea == areanums[k]) break;
- } //end for
- //if the face is shared
- if (k != numareas) continue;
- //the number of the area at the other side of the face
- if (face->frontarea == areanums[i]) otherareanum = face->backarea;
- else otherareanum = face->frontarea;
- //if the other area already is a cluter portal
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0;
- //number of the plane of the area
- faceplanenum = face->planenum & ~1;
- //
- if (frontplanenum < 0 || faceplanenum == frontplanenum)
- {
- frontplanenum = faceplanenum;
- frontfacenums[numfrontfaces++] = facenum;
- for (k = 0; k < numfrontareas; k++)
- {
- if (frontareanums[k] == otherareanum) break;
- } //end for
- if (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum;
- numareafrontfaces[i]++;
- } //end if
- else if (backplanenum < 0 || faceplanenum == backplanenum)
- {
- backplanenum = faceplanenum;
- backfacenums[numbackfaces++] = facenum;
- for (k = 0; k < numbackareas; k++)
- {
- if (backareanums[k] == otherareanum) break;
- } //end for
- if (k == numbackareas) backareanums[numbackareas++] = otherareanum;
- numareabackfaces[i]++;
- } //end else
- else
- {
- return 0;
- } //end else
- } //end for
- } //end for
- //every area should have at least one front face and one back face
- for (i = 0; i < numareas; i++)
- {
- if (!numareafrontfaces[i] || !numareabackfaces[i]) return 0;
- } //end for
- //the front areas should all be connected
- if (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0;
- //the back areas should all be connected
- if (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0;
- //none of the front faces should have a shared edge with a back face
- for (i = 0; i < numfrontfaces; i++)
- {
- frontface = &aasworld.faces[frontfacenums[i]];
- for (fen = 0; fen < frontface->numedges; fen++)
- {
- frontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]);
- for (j = 0; j < numbackfaces; j++)
- {
- backface = &aasworld.faces[backfacenums[j]];
- for (ben = 0; ben < backface->numedges; ben++)
- {
- backedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]);
- if (frontedgenum == backedgenum) break;
- } //end for
- if (ben != backface->numedges) break;
- } //end for
- if (j != numbackfaces) break;
- } //end for
- if (fen != frontface->numedges) break;
- } //end for
- if (i != numfrontfaces) return 0;
- //set the cluster portal contents
- for (i = 0; i < numareas; i++)
- {
- aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL;
- //this area can be used as a route portal
- aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL;
- Log_Write("possible portal: %d\r\n", areanums[i]);
- } //end for
- //
- return numareas;
-} //end of the function AAS_CheckAreaForPossiblePortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FindPossiblePortals(void)
-{
- int i, numpossibleportals;
-
- numpossibleportals = 0;
- for (i = 1; i < aasworld.numareas; i++)
- {
- numpossibleportals += AAS_CheckAreaForPossiblePortals(i);
- } //end for
- botimport.Print(PRT_MESSAGE, "\r%6d possible portal areas\n", numpossibleportals);
-} //end of the function AAS_FindPossiblePortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemoveAllPortals(void)
-{
- int i;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- } //end for
-} //end of the function AAS_RemoveAllPortals
-
-#if 0
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FloodCluster_r(int areanum, int clusternum)
-{
- int i, otherareanum;
- aas_face_t *face;
- aas_area_t *area;
-
- //set cluster mark
- aasworld.areasettings[areanum].cluster = clusternum;
- //if the area is a portal
- //if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return;
- //
- area = &aasworld.areas[areanum];
- //use area faces to flood into adjacent areas
- for (i = 0; i < area->numfaces; i++)
- {
- face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
- //
- if (face->frontarea != areanum) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //if there's no area at the other side
- if (!otherareanum) continue;
- //if the area is a portal
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
- //if the area is already marked
- if (aasworld.areasettings[otherareanum].cluster) continue;
- //
- AAS_FloodCluster_r(otherareanum, clusternum);
- } //end for
- //use the reachabilities to flood into other areas
- for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++)
- {
- otherareanum = aasworld.reachability[
- aasworld.areasettings[areanum].firstreachablearea + i].areanum;
- if (!otherareanum)
- {
- continue;
- AAS_Error("reachability %d has zero area\n", aasworld.areasettings[areanum].firstreachablearea + i);
- } //end if
- //if the area is a portal
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
- //if the area is already marked
- if (aasworld.areasettings[otherareanum].cluster) continue;
- //
- AAS_FloodCluster_r(otherareanum, clusternum);
- } //end for
-} //end of the function AAS_FloodCluster_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemoveTeleporterPortals(void)
-{
- int i, j, areanum;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
- {
- areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
- if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT)
- {
- aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- break;
- } //end if
- } //end for
- } //end for
-} //end of the function AAS_RemoveTeleporterPortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FloodClusterReachabilities(int clusternum)
-{
- int i, j, areanum;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- //if this area already has a cluster set
- if (aasworld.areasettings[i].cluster) continue;
- //if this area is a cluster portal
- if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue;
- //loop over the reachable areas from this area
- for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
- {
- //the reachable area
- areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
- //if this area is a cluster portal
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
- //if this area has a cluster set
- if (aasworld.areasettings[areanum].cluster == clusternum)
- {
- AAS_FloodCluster_r(i, clusternum);
- i = 0;
- break;
- } //end if
- } //end for
- } //end for
-} //end of the function AAS_FloodClusterReachabilities
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemoveNotClusterClosingPortals(void)
-{
- int i, j, k, facenum, otherareanum, nonclosingportals;
- aas_area_t *area;
- aas_face_t *face;
-
- AAS_RemoveTeleporterPortals();
- //
- nonclosingportals = 0;
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue;
- //find a non-portal area adjacent to the portal area and flood
- //the cluster from there
- area = &aasworld.areas[i];
- for (j = 0; j < area->numfaces; j++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + j]);
- face = &aasworld.faces[facenum];
- //
- if (face->frontarea != i) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //
- if (!otherareanum) continue;
- //
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- continue;
- } //end if
- //reset all cluster fields
- AAS_RemoveClusterAreas();
- //
- AAS_FloodCluster_r(otherareanum, 1);
- AAS_FloodClusterReachabilities(1);
- //check if all adjacent non-portal areas have a cluster set
- for (k = 0; k < area->numfaces; k++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + k]);
- face = &aasworld.faces[facenum];
- //
- if (face->frontarea != i) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //
- if (!otherareanum) continue;
- //
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- continue;
- } //end if
- //
- if (!aasworld.areasettings[otherareanum].cluster) break;
- } //end for
- //if all adjacent non-portal areas have a cluster set then the portal
- //didn't seal a cluster
- if (k >= area->numfaces)
- {
- aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- nonclosingportals++;
- //recheck all the other portals again
- i = 0;
- break;
- } //end if
- } //end for
- } //end for
- botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals);
-} //end of the function AAS_RemoveNotClusterClosingPortals
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-
-void AAS_RemoveNotClusterClosingPortals(void)
-{
- int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters;
- aas_area_t *area;
- aas_face_t *face;
-
- AAS_RemoveTeleporterPortals();
- //
- nonclosingportals = 0;
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue;
- //
- numseperatedclusters = 0;
- //reset all cluster fields
- AAS_RemoveClusterAreas();
- //find a non-portal area adjacent to the portal area and flood
- //the cluster from there
- area = &aasworld.areas[i];
- for (j = 0; j < area->numfaces; j++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + j]);
- face = &aasworld.faces[facenum];
- //
- if (face->frontarea != i) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //if not solid at the other side of the face
- if (!otherareanum) continue;
- //don't flood into other portals
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
- //if the area already has a cluster set
- if (aasworld.areasettings[otherareanum].cluster) continue;
- //another cluster is seperated by this portal
- numseperatedclusters++;
- //flood the cluster
- AAS_FloodCluster_r(otherareanum, numseperatedclusters);
- AAS_FloodClusterReachabilities(numseperatedclusters);
- } //end for
- //use the reachabilities to flood into other areas
- for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
- {
- otherareanum = aasworld.reachability[
- aasworld.areasettings[i].firstreachablearea + j].areanum;
- //this should never be qtrue but we check anyway
- if (!otherareanum) continue;
- //don't flood into other portals
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
- //if the area already has a cluster set
- if (aasworld.areasettings[otherareanum].cluster) continue;
- //another cluster is seperated by this portal
- numseperatedclusters++;
- //flood the cluster
- AAS_FloodCluster_r(otherareanum, numseperatedclusters);
- AAS_FloodClusterReachabilities(numseperatedclusters);
- } //end for
- //a portal must seperate no more and no less than 2 clusters
- if (numseperatedclusters != 2)
- {
- aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- nonclosingportals++;
- //recheck all the other portals again
- i = 0;
- } //end if
- } //end for
- botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals);
-} //end of the function AAS_RemoveNotClusterClosingPortals
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-
-void AAS_AddTeleporterPortals(void)
-{
- int j, area2num, facenum, otherareanum;
- char *target, *targetname, *classname;
- bsp_entity_t *entities, *ent, *dest;
- vec3_t origin, destorigin, mins, maxs, end;
- vec3_t bbmins, bbmaxs;
- aas_area_t *area;
- aas_face_t *face;
- aas_trace_t trace;
- aas_link_t *areas, *link;
-
- entities = AAS_ParseBSPEntities();
-
- for (ent = entities; ent; ent = ent->next)
- {
- classname = AAS_ValueForBSPEpairKey(ent, "classname");
- if (classname && !strcmp(classname, "misc_teleporter"))
- {
- if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
- {
- botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target);
- continue;
- } //end if
- //
- target = AAS_ValueForBSPEpairKey(ent, "target");
- if (!target)
- {
- botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target);
- continue;
- } //end if
- for (dest = entities; dest; dest = dest->next)
- {
- classname = AAS_ValueForBSPEpairKey(dest, "classname");
- if (classname && !strcmp(classname, "misc_teleporter_dest"))
- {
- targetname = AAS_ValueForBSPEpairKey(dest, "targetname");
- if (targetname && !strcmp(targetname, target))
- {
- break;
- } //end if
- } //end if
- } //end for
- if (!dest)
- {
- botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target);
- continue;
- } //end if
- if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin))
- {
- botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target);
- continue;
- } //end if
- destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground
- VectorCopy(destorigin, end);
- end[2] -= 100;
- trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);
- if (trace.startsolid)
- {
- botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target);
- continue;
- } //end if
- VectorCopy(trace.endpos, destorigin);
- area2num = AAS_PointAreaNum(destorigin);
- //reset all cluster fields
- for (j = 0; j < aasworld.numareas; j++)
- {
- aasworld.areasettings[j].cluster = 0;
- } //end for
- //
- VectorSet(mins, -8, -8, 8);
- VectorSet(maxs, 8, 8, 24);
- //
- AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs);
- //
- VectorAdd(origin, mins, mins);
- VectorAdd(origin, maxs, maxs);
- //add bounding box size
- VectorSubtract(mins, bbmaxs, mins);
- VectorSubtract(maxs, bbmins, maxs);
- //link an invalid (-1) entity
- areas = AAS_AASLinkEntity(mins, maxs, -1);
- //
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaGrounded(link->areanum)) continue;
- //add the teleporter portal mark
- aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL |
- AREACONTENTS_TELEPORTAL;
- } //end for
- //
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaGrounded(link->areanum)) continue;
- //find a non-portal area adjacent to the portal area and flood
- //the cluster from there
- area = &aasworld.areas[link->areanum];
- for (j = 0; j < area->numfaces; j++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + j]);
- face = &aasworld.faces[facenum];
- //
- if (face->frontarea != link->areanum) otherareanum = face->frontarea;
- else otherareanum = face->backarea;
- //
- if (!otherareanum) continue;
- //
- if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- continue;
- } //end if
- //
- AAS_FloodCluster_r(otherareanum, 1);
- } //end for
- } //end for
- //if the teleport destination IS in the same cluster
- if (aasworld.areasettings[area2num].cluster)
- {
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaGrounded(link->areanum)) continue;
- //add the teleporter portal mark
- aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL |
- AREACONTENTS_TELEPORTAL);
- } //end for
- } //end if
- } //end if
- } //end for
- AAS_FreeBSPEntities(entities);
-} //end of the function AAS_AddTeleporterPortals
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_AddTeleporterPortals(void)
-{
- int i, j, areanum;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
- {
- if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT) continue;
- areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
- aasworld.areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL;
- } //end for
- } //end for
-} //end of the function AAS_AddTeleporterPortals
-
-#endif
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_TestPortals(void)
-{
- int i;
- aas_portal_t *portal;
-
- for (i = 1; i < aasworld.numportals; i++)
- {
- portal = &aasworld.portals[i];
- if (!portal->frontcluster)
- {
- aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- Log_Write("portal area %d has no front cluster\r\n", portal->areanum);
- return qfalse;
- } //end if
- if (!portal->backcluster)
- {
- aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
- Log_Write("portal area %d has no back cluster\r\n", portal->areanum);
- return qfalse;
- } //end if
- } //end for
- return qtrue;
-} //end of the function
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_CountForcedClusterPortals(void)
-{
- int num, i;
-
- num = 0;
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- Log_Write("area %d is a forced portal area\r\n", i);
- num++;
- } //end if
- } //end for
- botimport.Print(PRT_MESSAGE, "%6d forced portal areas\n", num);
-} //end of the function AAS_CountForcedClusterPortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_CreateViewPortals(void)
-{
- int i;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- aasworld.areasettings[i].contents |= AREACONTENTS_VIEWPORTAL;
- } //end if
- } //end for
-} //end of the function AAS_CreateViewPortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_SetViewPortalsAsClusterPortals(void)
-{
- int i;
-
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL)
- {
- aasworld.areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL;
- } //end if
- } //end for
-} //end of the function AAS_SetViewPortalsAsClusterPortals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitClustering(void)
-{
- int i, removedPortalAreas;
- int n, total, numreachabilityareas;
-
- if (!aasworld.loaded) return;
- //if there are clusters
- if (aasworld.numclusters >= 1)
- {
-#ifndef BSPC
- //if clustering isn't forced
- if (!((int)LibVarGetValue("forceclustering")) &&
- !((int)LibVarGetValue("forcereachability"))) return;
-#endif
- } //end if
- //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach)
- AAS_SetViewPortalsAsClusterPortals();
- //count the number of forced cluster portals
- AAS_CountForcedClusterPortals();
- //remove all area cluster marks
- AAS_RemoveClusterAreas();
- //find possible cluster portals
- AAS_FindPossiblePortals();
- //craete portals to for the bot view
- AAS_CreateViewPortals();
- //remove all portals that are not closing a cluster
- //AAS_RemoveNotClusterClosingPortals();
- //initialize portal memory
- if (aasworld.portals) FreeMemory(aasworld.portals);
- aasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t));
- //initialize portal index memory
- if (aasworld.portalindex) FreeMemory(aasworld.portalindex);
- aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t));
- //initialize cluster memory
- if (aasworld.clusters) FreeMemory(aasworld.clusters);
- aasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t));
- //
- removedPortalAreas = 0;
- botimport.Print(PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas);
- while(1)
- {
- botimport.Print(PRT_MESSAGE, "\r%6d", removedPortalAreas);
- //initialize the number of portals and clusters
- aasworld.numportals = 1; //portal 0 is a dummy
- aasworld.portalindexsize = 0;
- aasworld.numclusters = 1; //cluster 0 is a dummy
- //create the portals from the portal areas
- AAS_CreatePortals();
- //
- removedPortalAreas++;
- //find the clusters
- if (!AAS_FindClusters())
- continue;
- //test the portals
- if (!AAS_TestPortals())
- continue;
- //
- break;
- } //end while
- botimport.Print(PRT_MESSAGE, "\n");
- //the AAS file should be saved
- aasworld.savefile = qtrue;
- //write the portal areas to the log file
- for (i = 1; i < aasworld.numportals; i++)
- {
- Log_Write("portal %d: area %d\r\n", i, aasworld.portals[i].areanum);
- } //end for
- // report cluster info
- botimport.Print(PRT_MESSAGE, "%6d portals created\n", aasworld.numportals);
- botimport.Print(PRT_MESSAGE, "%6d clusters created\n", aasworld.numclusters);
- for (i = 1; i < aasworld.numclusters; i++)
- {
- botimport.Print(PRT_MESSAGE, "cluster %d has %d reachability areas\n", i,
- aasworld.clusters[i].numreachabilityareas);
- } //end for
- // report AAS file efficiency
- numreachabilityareas = 0;
- total = 0;
- for (i = 0; i < aasworld.numclusters; i++) {
- n = aasworld.clusters[i].numreachabilityareas;
- numreachabilityareas += n;
- total += n * n;
- }
- total += numreachabilityareas * aasworld.numportals;
- //
- botimport.Print(PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas);
- botimport.Print(PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3);
-} //end of the function AAS_InitClustering
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_cluster.c + * + * desc: area clustering + * + * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +#define AAS_MAX_PORTALS 65536 +#define AAS_MAX_PORTALINDEXSIZE 65536 +#define AAS_MAX_CLUSTERS 65536 +// +#define MAX_PORTALAREAS 1024 + +// do not flood through area faces, only use reachabilities +int nofaceflood = qtrue; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveClusterAreas(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + aasworld.areasettings[i].cluster = 0; + } //end for +} //end of the function AAS_RemoveClusterAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearCluster(int clusternum) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].cluster == clusternum) + { + aasworld.areasettings[i].cluster = 0; + } //end if + } //end for +} //end of the function AAS_ClearCluster +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemovePortalsClusterReference(int clusternum) +{ + int portalnum; + + for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) + { + if (aasworld.portals[portalnum].frontcluster == clusternum) + { + aasworld.portals[portalnum].frontcluster = 0; + } //end if + if (aasworld.portals[portalnum].backcluster == clusternum) + { + aasworld.portals[portalnum].backcluster = 0; + } //end if + } //end for +} //end of the function AAS_RemovePortalsClusterReference +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_UpdatePortal(int areanum, int clusternum) +{ + int portalnum; + aas_portal_t *portal; + aas_cluster_t *cluster; + + //find the portal of the area + for (portalnum = 1; portalnum < aasworld.numportals; portalnum++) + { + if (aasworld.portals[portalnum].areanum == areanum) break; + } //end for + // + if (portalnum == aasworld.numportals) + { + AAS_Error("no portal of area %d", areanum); + return qtrue; + } //end if + // + portal = &aasworld.portals[portalnum]; + //if the portal is already fully updated + if (portal->frontcluster == clusternum) return qtrue; + if (portal->backcluster == clusternum) return qtrue; + //if the portal has no front cluster yet + if (!portal->frontcluster) + { + portal->frontcluster = clusternum; + } //end if + //if the portal has no back cluster yet + else if (!portal->backcluster) + { + portal->backcluster = clusternum; + } //end else if + else + { + //remove the cluster portal flag contents + aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + Log_Write("portal area %d is seperating more than two clusters\r\n", areanum); + return qfalse; + } //end else + if (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE) + { + AAS_Error("AAS_MAX_PORTALINDEXSIZE"); + return qtrue; + } //end if + //set the area cluster number to the negative portal number + aasworld.areasettings[areanum].cluster = -portalnum; + //add the portal to the cluster using the portal index + cluster = &aasworld.clusters[clusternum]; + aasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum; + aasworld.portalindexsize++; + cluster->numportals++; + return qtrue; +} //end of the function AAS_UpdatePortal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FloodClusterAreas_r(int areanum, int clusternum) +{ + aas_area_t *area; + aas_face_t *face; + int facenum, i; + + // + if (areanum <= 0 || areanum >= aasworld.numareas) + { + AAS_Error("AAS_FloodClusterAreas_r: areanum out of range"); + return qfalse; + } //end if + //if the area is already part of a cluster + if (aasworld.areasettings[areanum].cluster > 0) + { + if (aasworld.areasettings[areanum].cluster == clusternum) return qtrue; + // + //there's a reachability going from one cluster to another only in one direction + // + AAS_Error("cluster %d touched cluster %d at area %d\r\n", + clusternum, aasworld.areasettings[areanum].cluster, areanum); + return qfalse; + } //end if + //don't add the cluster portal areas to the clusters + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + return AAS_UpdatePortal(areanum, clusternum); + } //end if + //set the area cluster number + aasworld.areasettings[areanum].cluster = clusternum; + aasworld.areasettings[areanum].clusterareanum = + aasworld.clusters[clusternum].numareas; + //the cluster has an extra area + aasworld.clusters[clusternum].numareas++; + + area = &aasworld.areas[areanum]; + //use area faces to flood into adjacent areas + if (!nofaceflood) + { + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + if (face->frontarea == areanum) + { + if (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse; + } //end if + else + { + if (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse; + } //end else + } //end for + } //end if + //use the reachabilities to flood into other areas + for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) + { + if (!aasworld.reachability[ + aasworld.areasettings[areanum].firstreachablearea + i].areanum) + { + continue; + } //end if + if (!AAS_FloodClusterAreas_r(aasworld.reachability[ + aasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_FloodClusterAreas_r +//=========================================================================== +// try to flood from all areas without cluster into areas with a cluster set +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FloodClusterAreasUsingReachabilities(int clusternum) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + //if this area already has a cluster set + if (aasworld.areasettings[i].cluster) + continue; + //if this area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + continue; + //loop over the reachable areas from this area + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + //the reachable area + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + //if this area is a cluster portal + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) + continue; + //if this area has a cluster set + if (aasworld.areasettings[areanum].cluster) + { + if (!AAS_FloodClusterAreas_r(i, clusternum)) + return qfalse; + i = 0; + break; + } //end if + } //end for + } //end for + return qtrue; +} //end of the function AAS_FloodClusterAreasUsingReachabilities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_NumberClusterPortals(int clusternum) +{ + int i, portalnum; + aas_cluster_t *cluster; + aas_portal_t *portal; + + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + if (portal->frontcluster == clusternum) + { + portal->clusterareanum[0] = cluster->numareas++; + } //end if + else + { + portal->clusterareanum[1] = cluster->numareas++; + } //end else + } //end for +} //end of the function AAS_NumberClusterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_NumberClusterAreas(int clusternum) +{ + int i, portalnum; + aas_cluster_t *cluster; + aas_portal_t *portal; + + aasworld.clusters[clusternum].numareas = 0; + aasworld.clusters[clusternum].numreachabilityareas = 0; + //number all areas in this cluster WITH reachabilities + for (i = 1; i < aasworld.numareas; i++) + { + // + if (aasworld.areasettings[i].cluster != clusternum) continue; + // + if (!AAS_AreaReachability(i)) continue; + // + aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; + //the cluster has an extra area + aasworld.clusters[clusternum].numareas++; + aasworld.clusters[clusternum].numreachabilityareas++; + } //end for + //number all portals in this cluster WITH reachabilities + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + if (!AAS_AreaReachability(portal->areanum)) continue; + if (portal->frontcluster == clusternum) + { + portal->clusterareanum[0] = cluster->numareas++; + aasworld.clusters[clusternum].numreachabilityareas++; + } //end if + else + { + portal->clusterareanum[1] = cluster->numareas++; + aasworld.clusters[clusternum].numreachabilityareas++; + } //end else + } //end for + //number all areas in this cluster WITHOUT reachabilities + for (i = 1; i < aasworld.numareas; i++) + { + // + if (aasworld.areasettings[i].cluster != clusternum) continue; + // + if (AAS_AreaReachability(i)) continue; + // + aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; + //the cluster has an extra area + aasworld.clusters[clusternum].numareas++; + } //end for + //number all portals in this cluster WITHOUT reachabilities + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + if (AAS_AreaReachability(portal->areanum)) continue; + if (portal->frontcluster == clusternum) + { + portal->clusterareanum[0] = cluster->numareas++; + } //end if + else + { + portal->clusterareanum[1] = cluster->numareas++; + } //end else + } //end for +} //end of the function AAS_NumberClusterAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FindClusters(void) +{ + int i; + aas_cluster_t *cluster; + + AAS_RemoveClusterAreas(); + // + for (i = 1; i < aasworld.numareas; i++) + { + //if the area is already part of a cluster + if (aasworld.areasettings[i].cluster) + continue; + // if not flooding through faces only use areas that have reachabilities + if (nofaceflood) + { + if (!aasworld.areasettings[i].numreachableareas) + continue; + } //end if + //if the area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + continue; + if (aasworld.numclusters >= AAS_MAX_CLUSTERS) + { + AAS_Error("AAS_MAX_CLUSTERS"); + return qfalse; + } //end if + cluster = &aasworld.clusters[aasworld.numclusters]; + cluster->numareas = 0; + cluster->numreachabilityareas = 0; + cluster->firstportal = aasworld.portalindexsize; + cluster->numportals = 0; + //flood the areas in this cluster + if (!AAS_FloodClusterAreas_r(i, aasworld.numclusters)) + return qfalse; + if (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters)) + return qfalse; + //number the cluster areas + //AAS_NumberClusterPortals(aasworld.numclusters); + AAS_NumberClusterAreas(aasworld.numclusters); + //Log_Write("cluster %d has %d areas\r\n", aasworld.numclusters, cluster->numareas); + aasworld.numclusters++; + } //end for + return qtrue; +} //end of the function AAS_FindClusters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreatePortals(void) +{ + int i; + aas_portal_t *portal; + + for (i = 1; i < aasworld.numareas; i++) + { + //if the area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + { + if (aasworld.numportals >= AAS_MAX_PORTALS) + { + AAS_Error("AAS_MAX_PORTALS"); + return; + } //end if + portal = &aasworld.portals[aasworld.numportals]; + portal->areanum = i; + portal->frontcluster = 0; + portal->backcluster = 0; + aasworld.numportals++; + } //end if + } //end for +} //end of the function AAS_CreatePortals +/* +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_MapContainsTeleporters(void) +{ + bsp_entity_t *entities, *ent; + char *classname; + + entities = AAS_ParseBSPEntities(); + + for (ent = entities; ent; ent = ent->next) + { + classname = AAS_ValueForBSPEpairKey(ent, "classname"); + if (classname && !strcmp(classname, "misc_teleporter")) + { + AAS_FreeBSPEntities(entities); + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function AAS_MapContainsTeleporters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2) +{ + int i, j, edgenum; + aas_plane_t *plane1, *plane2; + aas_edge_t *edge; + + + plane1 = &aasworld.planes[face1->planenum ^ side1]; + plane2 = &aasworld.planes[face2->planenum ^ side2]; + + //check if one of the points of face1 is at the back of the plane of face2 + for (i = 0; i < face1->numedges; i++) + { + edgenum = abs(aasworld.edgeindex[face1->firstedge + i]); + edge = &aasworld.edges[edgenum]; + for (j = 0; j < 2; j++) + { + if (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) - + plane2->dist < -0.01) return qtrue; + } //end for + } //end for + for (i = 0; i < face2->numedges; i++) + { + edgenum = abs(aasworld.edgeindex[face2->firstedge + i]); + edge = &aasworld.edges[edgenum]; + for (j = 0; j < 2; j++) + { + if (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) - + plane1->dist < -0.01) return qtrue; + } //end for + } //end for + + return qfalse; +} //end of the function AAS_NonConvexFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_CanMergeAreas(int *areanums, int numareas) +{ + int i, j, s, face1num, face2num, side1, side2, fn1, fn2; + aas_face_t *face1, *face2; + aas_area_t *area1, *area2; + + for (i = 0; i < numareas; i++) + { + area1 = &aasworld.areas[areanums[i]]; + for (fn1 = 0; fn1 < area1->numfaces; fn1++) + { + face1num = abs(aasworld.faceindex[area1->firstface + fn1]); + face1 = &aasworld.faces[face1num]; + side1 = face1->frontarea != areanums[i]; + //check if the face isn't a shared one with one of the other areas + for (s = 0; s < numareas; s++) + { + if (s == i) continue; + if (face1->frontarea == s || face1->backarea == s) break; + } //end for + //if the face was a shared one + if (s != numareas) continue; + // + for (j = 0; j < numareas; j++) + { + if (j == i) continue; + area2 = &aasworld.areas[areanums[j]]; + for (fn2 = 0; fn2 < area2->numfaces; fn2++) + { + face2num = abs(aasworld.faceindex[area2->firstface + fn2]); + face2 = &aasworld.faces[face2num]; + side2 = face2->frontarea != areanums[j]; + //check if the face isn't a shared one with one of the other areas + for (s = 0; s < numareas; s++) + { + if (s == j) continue; + if (face2->frontarea == s || face2->backarea == s) break; + } //end for + //if the face was a shared one + if (s != numareas) continue; + // + if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse; + } //end for + } //end for + } //end for + } //end for + return qtrue; +} //end of the function AAS_CanMergeAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum) +{ + int i; + vec3_t edgevec1, edgevec2, normal1, normal2; + float dist1, dist2; + aas_plane_t *plane; + + plane = &aasworld.planes[planenum]; + VectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1); + VectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2); + if (side1) VectorInverse(edgevec1); + if (side2) VectorInverse(edgevec2); + // + CrossProduct(edgevec1, plane->normal, normal1); + dist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]); + CrossProduct(edgevec2, plane->normal, normal2); + dist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]); + + for (i = 0; i < 2; i++) + { + if (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse; + } //end for + for (i = 0; i < 2; i++) + { + if (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_NonConvexEdges +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum) +{ + int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens; + aas_face_t *face1, *face2, *otherface; + aas_edge_t *edge1, *edge2; + + for (i = 0; i < numfaces; i++) + { + face1 = &aasworld.faces[facenums[i]]; + for (en1 = 0; en1 < face1->numedges; en1++) + { + edgenum1 = aasworld.edgeindex[face1->firstedge + en1]; + side1 = (edgenum1 < 0) ^ (face1->planenum != planenum); + edgenum1 = abs(edgenum1); + edge1 = &aasworld.edges[edgenum1]; + //check if the edge is shared with another face + for (s = 0; s < numfaces; s++) + { + if (s == i) continue; + otherface = &aasworld.faces[facenums[s]]; + for (ens = 0; ens < otherface->numedges; ens++) + { + if (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; + } //end for + if (ens != otherface->numedges) break; + } //end for + //if the edge was shared + if (s != numfaces) continue; + // + for (j = 0; j < numfaces; j++) + { + if (j == i) continue; + face2 = &aasworld.faces[facenums[j]]; + for (en2 = 0; en2 < face2->numedges; en2++) + { + edgenum2 = aasworld.edgeindex[face2->firstedge + en2]; + side2 = (edgenum2 < 0) ^ (face2->planenum != planenum); + edgenum2 = abs(edgenum2); + edge2 = &aasworld.edges[edgenum2]; + //check if the edge is shared with another face + for (s = 0; s < numfaces; s++) + { + if (s == i) continue; + otherface = &aasworld.faces[facenums[s]]; + for (ens = 0; ens < otherface->numedges; ens++) + { + if (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break; + } //end for + if (ens != otherface->numedges) break; + } //end for + //if the edge was shared + if (s != numfaces) continue; + // + if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse; + } //end for + } //end for + } //end for + } //end for + return qtrue; +} //end of the function AAS_CanMergeFaces*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea) +{ + int i, j, otherareanum, facenum; + aas_area_t *area; + aas_face_t *face; + + connectedareas[curarea] = qtrue; + area = &aasworld.areas[areanums[curarea]]; + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + //if the face is solid + if (face->faceflags & FACE_SOLID) continue; + //get the area at the other side of the face + if (face->frontarea != areanums[curarea]) otherareanum = face->frontarea; + else otherareanum = face->backarea; + //check if the face is leading to one of the other areas + for (j = 0; j < numareas; j++) + { + if (areanums[j] == otherareanum) break; + } //end for + //if the face isn't leading to one of the other areas + if (j == numareas) continue; + //if the other area is already connected + if (connectedareas[j]) continue; + //recursively proceed with the other area + AAS_ConnectedAreas_r(areanums, numareas, connectedareas, j); + } //end for +} //end of the function AAS_ConnectedAreas_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_ConnectedAreas(int *areanums, int numareas) +{ + int connectedareas[MAX_PORTALAREAS], i; + + Com_Memset(connectedareas, 0, sizeof(connectedareas)); + if (numareas < 1) return qfalse; + if (numareas == 1) return qtrue; + AAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0); + for (i = 0; i < numareas; i++) + { + if (!connectedareas[i]) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_ConnectedAreas +//=========================================================================== +// gets adjacent areas with less presence types recursively +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum) +{ + int i, j, presencetype, otherpresencetype, otherareanum, facenum; + aas_area_t *area; + aas_face_t *face; + + areanums[numareas++] = curareanum; + area = &aasworld.areas[curareanum]; + presencetype = aasworld.areasettings[curareanum].presencetype; + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + //if the face is solid + if (face->faceflags & FACE_SOLID) continue; + //the area at the other side of the face + if (face->frontarea != curareanum) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + otherpresencetype = aasworld.areasettings[otherareanum].presencetype; + //if the other area has less presence types + if ((presencetype & ~otherpresencetype) && + !(otherpresencetype & ~presencetype)) + { + //check if the other area isn't already in the list + for (j = 0; j < numareas; j++) + { + if (otherareanum == areanums[j]) break; + } //end for + //if the other area isn't already in the list + if (j == numareas) + { + if (numareas >= MAX_PORTALAREAS) + { + AAS_Error("MAX_PORTALAREAS"); + return numareas; + } //end if + numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum); + } //end if + } //end if + } //end for + return numareas; +} //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_CheckAreaForPossiblePortals(int areanum) +{ + int i, j, k, fen, ben, frontedgenum, backedgenum, facenum; + int areanums[MAX_PORTALAREAS], numareas, otherareanum; + int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS]; + int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS]; + int numfrontfaces, numbackfaces; + int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS]; + int numfrontareas, numbackareas; + int frontplanenum, backplanenum, faceplanenum; + aas_area_t *area; + aas_face_t *frontface, *backface, *face; + + //if it isn't already a portal + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; + //it must be a grounded area + if (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0; + // + Com_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces)); + Com_Memset(numareabackfaces, 0, sizeof(numareabackfaces)); + numareas = numfrontfaces = numbackfaces = 0; + numfrontareas = numbackareas = 0; + frontplanenum = backplanenum = -1; + //add any adjacent areas with less presence types + numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum); + // + for (i = 0; i < numareas; i++) + { + area = &aasworld.areas[areanums[i]]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + //if the face is solid + if (face->faceflags & FACE_SOLID) continue; + //check if the face is shared with one of the other areas + for (k = 0; k < numareas; k++) + { + if (k == i) continue; + if (face->frontarea == areanums[k] || face->backarea == areanums[k]) break; + } //end for + //if the face is shared + if (k != numareas) continue; + //the number of the area at the other side of the face + if (face->frontarea == areanums[i]) otherareanum = face->backarea; + else otherareanum = face->frontarea; + //if the other area already is a cluter portal + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0; + //number of the plane of the area + faceplanenum = face->planenum & ~1; + // + if (frontplanenum < 0 || faceplanenum == frontplanenum) + { + frontplanenum = faceplanenum; + frontfacenums[numfrontfaces++] = facenum; + for (k = 0; k < numfrontareas; k++) + { + if (frontareanums[k] == otherareanum) break; + } //end for + if (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum; + numareafrontfaces[i]++; + } //end if + else if (backplanenum < 0 || faceplanenum == backplanenum) + { + backplanenum = faceplanenum; + backfacenums[numbackfaces++] = facenum; + for (k = 0; k < numbackareas; k++) + { + if (backareanums[k] == otherareanum) break; + } //end for + if (k == numbackareas) backareanums[numbackareas++] = otherareanum; + numareabackfaces[i]++; + } //end else + else + { + return 0; + } //end else + } //end for + } //end for + //every area should have at least one front face and one back face + for (i = 0; i < numareas; i++) + { + if (!numareafrontfaces[i] || !numareabackfaces[i]) return 0; + } //end for + //the front areas should all be connected + if (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0; + //the back areas should all be connected + if (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0; + //none of the front faces should have a shared edge with a back face + for (i = 0; i < numfrontfaces; i++) + { + frontface = &aasworld.faces[frontfacenums[i]]; + for (fen = 0; fen < frontface->numedges; fen++) + { + frontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]); + for (j = 0; j < numbackfaces; j++) + { + backface = &aasworld.faces[backfacenums[j]]; + for (ben = 0; ben < backface->numedges; ben++) + { + backedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]); + if (frontedgenum == backedgenum) break; + } //end for + if (ben != backface->numedges) break; + } //end for + if (j != numbackfaces) break; + } //end for + if (fen != frontface->numedges) break; + } //end for + if (i != numfrontfaces) return 0; + //set the cluster portal contents + for (i = 0; i < numareas; i++) + { + aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL; + //this area can be used as a route portal + aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL; + Log_Write("possible portal: %d\r\n", areanums[i]); + } //end for + // + return numareas; +} //end of the function AAS_CheckAreaForPossiblePortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FindPossiblePortals(void) +{ + int i, numpossibleportals; + + numpossibleportals = 0; + for (i = 1; i < aasworld.numareas; i++) + { + numpossibleportals += AAS_CheckAreaForPossiblePortals(i); + } //end for + botimport.Print(PRT_MESSAGE, "\r%6d possible portal areas\n", numpossibleportals); +} //end of the function AAS_FindPossiblePortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveAllPortals(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + } //end for +} //end of the function AAS_RemoveAllPortals + +#if 0 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FloodCluster_r(int areanum, int clusternum) +{ + int i, otherareanum; + aas_face_t *face; + aas_area_t *area; + + //set cluster mark + aasworld.areasettings[areanum].cluster = clusternum; + //if the area is a portal + //if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return; + // + area = &aasworld.areas[areanum]; + //use area faces to flood into adjacent areas + for (i = 0; i < area->numfaces; i++) + { + face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; + // + if (face->frontarea != areanum) otherareanum = face->frontarea; + else otherareanum = face->backarea; + //if there's no area at the other side + if (!otherareanum) continue; + //if the area is a portal + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area is already marked + if (aasworld.areasettings[otherareanum].cluster) continue; + // + AAS_FloodCluster_r(otherareanum, clusternum); + } //end for + //use the reachabilities to flood into other areas + for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++) + { + otherareanum = aasworld.reachability[ + aasworld.areasettings[areanum].firstreachablearea + i].areanum; + if (!otherareanum) + { + continue; + AAS_Error("reachability %d has zero area\n", aasworld.areasettings[areanum].firstreachablearea + i); + } //end if + //if the area is a portal + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area is already marked + if (aasworld.areasettings[otherareanum].cluster) continue; + // + AAS_FloodCluster_r(otherareanum, clusternum); + } //end for +} //end of the function AAS_FloodCluster_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveTeleporterPortals(void) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + break; + } //end if + } //end for + } //end for +} //end of the function AAS_RemoveTeleporterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FloodClusterReachabilities(int clusternum) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + //if this area already has a cluster set + if (aasworld.areasettings[i].cluster) continue; + //if this area is a cluster portal + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //loop over the reachable areas from this area + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + //the reachable area + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + //if this area is a cluster portal + if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if this area has a cluster set + if (aasworld.areasettings[areanum].cluster == clusternum) + { + AAS_FloodCluster_r(i, clusternum); + i = 0; + break; + } //end if + } //end for + } //end for +} //end of the function AAS_FloodClusterReachabilities + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveNotClusterClosingPortals(void) +{ + int i, j, k, facenum, otherareanum, nonclosingportals; + aas_area_t *area; + aas_face_t *face; + + AAS_RemoveTeleporterPortals(); + // + nonclosingportals = 0; + for (i = 1; i < aasworld.numareas; i++) + { + if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; + //find a non-portal area adjacent to the portal area and flood + //the cluster from there + area = &aasworld.areas[i]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != i) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + if (!otherareanum) continue; + // + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + continue; + } //end if + //reset all cluster fields + AAS_RemoveClusterAreas(); + // + AAS_FloodCluster_r(otherareanum, 1); + AAS_FloodClusterReachabilities(1); + //check if all adjacent non-portal areas have a cluster set + for (k = 0; k < area->numfaces; k++) + { + facenum = abs(aasworld.faceindex[area->firstface + k]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != i) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + if (!otherareanum) continue; + // + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + continue; + } //end if + // + if (!aasworld.areasettings[otherareanum].cluster) break; + } //end for + //if all adjacent non-portal areas have a cluster set then the portal + //didn't seal a cluster + if (k >= area->numfaces) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + nonclosingportals++; + //recheck all the other portals again + i = 0; + break; + } //end if + } //end for + } //end for + botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); +} //end of the function AAS_RemoveNotClusterClosingPortals + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +void AAS_RemoveNotClusterClosingPortals(void) +{ + int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters; + aas_area_t *area; + aas_face_t *face; + + AAS_RemoveTeleporterPortals(); + // + nonclosingportals = 0; + for (i = 1; i < aasworld.numareas; i++) + { + if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; + // + numseperatedclusters = 0; + //reset all cluster fields + AAS_RemoveClusterAreas(); + //find a non-portal area adjacent to the portal area and flood + //the cluster from there + area = &aasworld.areas[i]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != i) otherareanum = face->frontarea; + else otherareanum = face->backarea; + //if not solid at the other side of the face + if (!otherareanum) continue; + //don't flood into other portals + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area already has a cluster set + if (aasworld.areasettings[otherareanum].cluster) continue; + //another cluster is seperated by this portal + numseperatedclusters++; + //flood the cluster + AAS_FloodCluster_r(otherareanum, numseperatedclusters); + AAS_FloodClusterReachabilities(numseperatedclusters); + } //end for + //use the reachabilities to flood into other areas + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + otherareanum = aasworld.reachability[ + aasworld.areasettings[i].firstreachablearea + j].areanum; + //this should never be qtrue but we check anyway + if (!otherareanum) continue; + //don't flood into other portals + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; + //if the area already has a cluster set + if (aasworld.areasettings[otherareanum].cluster) continue; + //another cluster is seperated by this portal + numseperatedclusters++; + //flood the cluster + AAS_FloodCluster_r(otherareanum, numseperatedclusters); + AAS_FloodClusterReachabilities(numseperatedclusters); + } //end for + //a portal must seperate no more and no less than 2 clusters + if (numseperatedclusters != 2) + { + aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; + nonclosingportals++; + //recheck all the other portals again + i = 0; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); +} //end of the function AAS_RemoveNotClusterClosingPortals + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +void AAS_AddTeleporterPortals(void) +{ + int j, area2num, facenum, otherareanum; + char *target, *targetname, *classname; + bsp_entity_t *entities, *ent, *dest; + vec3_t origin, destorigin, mins, maxs, end; + vec3_t bbmins, bbmaxs; + aas_area_t *area; + aas_face_t *face; + aas_trace_t trace; + aas_link_t *areas, *link; + + entities = AAS_ParseBSPEntities(); + + for (ent = entities; ent; ent = ent->next) + { + classname = AAS_ValueForBSPEpairKey(ent, "classname"); + if (classname && !strcmp(classname, "misc_teleporter")) + { + if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target); + continue; + } //end if + // + target = AAS_ValueForBSPEpairKey(ent, "target"); + if (!target) + { + botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target); + continue; + } //end if + for (dest = entities; dest; dest = dest->next) + { + classname = AAS_ValueForBSPEpairKey(dest, "classname"); + if (classname && !strcmp(classname, "misc_teleporter_dest")) + { + targetname = AAS_ValueForBSPEpairKey(dest, "targetname"); + if (targetname && !strcmp(targetname, target)) + { + break; + } //end if + } //end if + } //end for + if (!dest) + { + botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target); + continue; + } //end if + if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); + continue; + } //end if + destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground + VectorCopy(destorigin, end); + end[2] -= 100; + trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); + continue; + } //end if + VectorCopy(trace.endpos, destorigin); + area2num = AAS_PointAreaNum(destorigin); + //reset all cluster fields + for (j = 0; j < aasworld.numareas; j++) + { + aasworld.areasettings[j].cluster = 0; + } //end for + // + VectorSet(mins, -8, -8, 8); + VectorSet(maxs, 8, 8, 24); + // + AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); + // + VectorAdd(origin, mins, mins); + VectorAdd(origin, maxs, maxs); + //add bounding box size + VectorSubtract(mins, bbmaxs, mins); + VectorSubtract(maxs, bbmins, maxs); + //link an invalid (-1) entity + areas = AAS_AASLinkEntity(mins, maxs, -1); + // + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaGrounded(link->areanum)) continue; + //add the teleporter portal mark + aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL | + AREACONTENTS_TELEPORTAL; + } //end for + // + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaGrounded(link->areanum)) continue; + //find a non-portal area adjacent to the portal area and flood + //the cluster from there + area = &aasworld.areas[link->areanum]; + for (j = 0; j < area->numfaces; j++) + { + facenum = abs(aasworld.faceindex[area->firstface + j]); + face = &aasworld.faces[facenum]; + // + if (face->frontarea != link->areanum) otherareanum = face->frontarea; + else otherareanum = face->backarea; + // + if (!otherareanum) continue; + // + if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) + { + continue; + } //end if + // + AAS_FloodCluster_r(otherareanum, 1); + } //end for + } //end for + //if the teleport destination IS in the same cluster + if (aasworld.areasettings[area2num].cluster) + { + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaGrounded(link->areanum)) continue; + //add the teleporter portal mark + aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL | + AREACONTENTS_TELEPORTAL); + } //end for + } //end if + } //end if + } //end for + AAS_FreeBSPEntities(entities); +} //end of the function AAS_AddTeleporterPortals + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AddTeleporterPortals(void) +{ + int i, j, areanum; + + for (i = 1; i < aasworld.numareas; i++) + { + for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) + { + if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT) continue; + areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum; + aasworld.areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL; + } //end for + } //end for +} //end of the function AAS_AddTeleporterPortals + +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TestPortals(void) +{ + int i; + aas_portal_t *portal; + + for (i = 1; i < aasworld.numportals; i++) + { + portal = &aasworld.portals[i]; + if (!portal->frontcluster) + { + aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + Log_Write("portal area %d has no front cluster\r\n", portal->areanum); + return qfalse; + } //end if + if (!portal->backcluster) + { + aasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL; + Log_Write("portal area %d has no back cluster\r\n", portal->areanum); + return qfalse; + } //end if + } //end for + return qtrue; +} //end of the function +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CountForcedClusterPortals(void) +{ + int num, i; + + num = 0; + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + { + Log_Write("area %d is a forced portal area\r\n", i); + num++; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "%6d forced portal areas\n", num); +} //end of the function AAS_CountForcedClusterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateViewPortals(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) + { + aasworld.areasettings[i].contents |= AREACONTENTS_VIEWPORTAL; + } //end if + } //end for +} //end of the function AAS_CreateViewPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetViewPortalsAsClusterPortals(void) +{ + int i; + + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL) + { + aasworld.areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL; + } //end if + } //end for +} //end of the function AAS_SetViewPortalsAsClusterPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitClustering(void) +{ + int i, removedPortalAreas; + int n, total, numreachabilityareas; + + if (!aasworld.loaded) return; + //if there are clusters + if (aasworld.numclusters >= 1) + { +#ifndef BSPC + //if clustering isn't forced + if (!((int)LibVarGetValue("forceclustering")) && + !((int)LibVarGetValue("forcereachability"))) return; +#endif + } //end if + //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) + AAS_SetViewPortalsAsClusterPortals(); + //count the number of forced cluster portals + AAS_CountForcedClusterPortals(); + //remove all area cluster marks + AAS_RemoveClusterAreas(); + //find possible cluster portals + AAS_FindPossiblePortals(); + //craete portals to for the bot view + AAS_CreateViewPortals(); + //remove all portals that are not closing a cluster + //AAS_RemoveNotClusterClosingPortals(); + //initialize portal memory + if (aasworld.portals) FreeMemory(aasworld.portals); + aasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t)); + //initialize portal index memory + if (aasworld.portalindex) FreeMemory(aasworld.portalindex); + aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t)); + //initialize cluster memory + if (aasworld.clusters) FreeMemory(aasworld.clusters); + aasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t)); + // + removedPortalAreas = 0; + botimport.Print(PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas); + while(1) + { + botimport.Print(PRT_MESSAGE, "\r%6d", removedPortalAreas); + //initialize the number of portals and clusters + aasworld.numportals = 1; //portal 0 is a dummy + aasworld.portalindexsize = 0; + aasworld.numclusters = 1; //cluster 0 is a dummy + //create the portals from the portal areas + AAS_CreatePortals(); + // + removedPortalAreas++; + //find the clusters + if (!AAS_FindClusters()) + continue; + //test the portals + if (!AAS_TestPortals()) + continue; + // + break; + } //end while + botimport.Print(PRT_MESSAGE, "\n"); + //the AAS file should be saved + aasworld.savefile = qtrue; + //write the portal areas to the log file + for (i = 1; i < aasworld.numportals; i++) + { + Log_Write("portal %d: area %d\r\n", i, aasworld.portals[i].areanum); + } //end for + // report cluster info + botimport.Print(PRT_MESSAGE, "%6d portals created\n", aasworld.numportals); + botimport.Print(PRT_MESSAGE, "%6d clusters created\n", aasworld.numclusters); + for (i = 1; i < aasworld.numclusters; i++) + { + botimport.Print(PRT_MESSAGE, "cluster %d has %d reachability areas\n", i, + aasworld.clusters[i].numreachabilityareas); + } //end for + // report AAS file efficiency + numreachabilityareas = 0; + total = 0; + for (i = 0; i < aasworld.numclusters; i++) { + n = aasworld.clusters[i].numreachabilityareas; + numreachabilityareas += n; + total += n * n; + } + total += numreachabilityareas * aasworld.numportals; + // + botimport.Print(PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas); + botimport.Print(PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3); +} //end of the function AAS_InitClustering diff --git a/code/botlib/be_aas_cluster.h b/code/botlib/be_aas_cluster.h index 3f52a46..86a4bfd 100755 --- a/code/botlib/be_aas_cluster.h +++ b/code/botlib/be_aas_cluster.h @@ -1,38 +1,38 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_cluster.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_cluster.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-//initialize the AAS clustering
-void AAS_InitClustering(void);
-//
-void AAS_SetViewPortalsAsClusterPortals(void);
-#endif //AASINTERN
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_cluster.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_cluster.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize the AAS clustering +void AAS_InitClustering(void); +// +void AAS_SetViewPortalsAsClusterPortals(void); +#endif //AASINTERN + diff --git a/code/botlib/be_aas_debug.c b/code/botlib/be_aas_debug.c index 6271677..a665c0c 100755 --- a/code/botlib/be_aas_debug.c +++ b/code/botlib/be_aas_debug.c @@ -1,777 +1,777 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_debug.c
- *
- * desc: AAS debug code
- *
- * $Archive: /MissionPack/code/botlib/be_aas_debug.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_libvar.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_interface.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-
-#define MAX_DEBUGLINES 1024
-#define MAX_DEBUGPOLYGONS 8192
-
-int debuglines[MAX_DEBUGLINES];
-int debuglinevisible[MAX_DEBUGLINES];
-int numdebuglines;
-
-static int debugpolygons[MAX_DEBUGPOLYGONS];
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ClearShownPolygons(void)
-{
- int i;
-//*
- for (i = 0; i < MAX_DEBUGPOLYGONS; i++)
- {
- if (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]);
- debugpolygons[i] = 0;
- } //end for
-//*/
-/*
- for (i = 0; i < MAX_DEBUGPOLYGONS; i++)
- {
- botimport.DebugPolygonDelete(i);
- debugpolygons[i] = 0;
- } //end for
-*/
-} //end of the function AAS_ClearShownPolygons
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowPolygon(int color, int numpoints, vec3_t *points)
-{
- int i;
-
- for (i = 0; i < MAX_DEBUGPOLYGONS; i++)
- {
- if (!debugpolygons[i])
- {
- debugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points);
- break;
- } //end if
- } //end for
-} //end of the function AAS_ShowPolygon
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ClearShownDebugLines(void)
-{
- int i;
-
- //make all lines invisible
- for (i = 0; i < MAX_DEBUGLINES; i++)
- {
- if (debuglines[i])
- {
- //botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE);
- botimport.DebugLineDelete(debuglines[i]);
- debuglines[i] = 0;
- debuglinevisible[i] = qfalse;
- } //end if
- } //end for
-} //end of the function AAS_ClearShownDebugLines
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DebugLine(vec3_t start, vec3_t end, int color)
-{
- int line;
-
- for (line = 0; line < MAX_DEBUGLINES; line++)
- {
- if (!debuglines[line])
- {
- debuglines[line] = botimport.DebugLineCreate();
- debuglinevisible[line] = qfalse;
- numdebuglines++;
- } //end if
- if (!debuglinevisible[line])
- {
- botimport.DebugLineShow(debuglines[line], start, end, color);
- debuglinevisible[line] = qtrue;
- return;
- } //end else
- } //end for
-} //end of the function AAS_DebugLine
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_PermanentLine(vec3_t start, vec3_t end, int color)
-{
- int line;
-
- line = botimport.DebugLineCreate();
- botimport.DebugLineShow(line, start, end, color);
-} //end of the function AAS_PermenentLine
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DrawPermanentCross(vec3_t origin, float size, int color)
-{
- int i, debugline;
- vec3_t start, end;
-
- for (i = 0; i < 3; i++)
- {
- VectorCopy(origin, start);
- start[i] += size;
- VectorCopy(origin, end);
- end[i] -= size;
- AAS_DebugLine(start, end, color);
- debugline = botimport.DebugLineCreate();
- botimport.DebugLineShow(debugline, start, end, color);
- } //end for
-} //end of the function AAS_DrawPermanentCross
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color)
-{
- int n0, n1, n2, j, line, lines[2];
- vec3_t start1, end1, start2, end2;
-
- //make a cross in the hit plane at the hit point
- VectorCopy(point, start1);
- VectorCopy(point, end1);
- VectorCopy(point, start2);
- VectorCopy(point, end2);
-
- n0 = type % 3;
- n1 = (type + 1) % 3;
- n2 = (type + 2) % 3;
- start1[n1] -= 6;
- start1[n2] -= 6;
- end1[n1] += 6;
- end1[n2] += 6;
- start2[n1] += 6;
- start2[n2] -= 6;
- end2[n1] -= 6;
- end2[n2] += 6;
-
- start1[n0] = (dist - (start1[n1] * normal[n1] +
- start1[n2] * normal[n2])) / normal[n0];
- end1[n0] = (dist - (end1[n1] * normal[n1] +
- end1[n2] * normal[n2])) / normal[n0];
- start2[n0] = (dist - (start2[n1] * normal[n1] +
- start2[n2] * normal[n2])) / normal[n0];
- end2[n0] = (dist - (end2[n1] * normal[n1] +
- end2[n2] * normal[n2])) / normal[n0];
-
- for (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++)
- {
- if (!debuglines[line])
- {
- debuglines[line] = botimport.DebugLineCreate();
- lines[j++] = debuglines[line];
- debuglinevisible[line] = qtrue;
- numdebuglines++;
- } //end if
- else if (!debuglinevisible[line])
- {
- lines[j++] = debuglines[line];
- debuglinevisible[line] = qtrue;
- } //end else
- } //end for
- botimport.DebugLineShow(lines[0], start1, end1, color);
- botimport.DebugLineShow(lines[1], start2, end2, color);
-} //end of the function AAS_DrawPlaneCross
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs)
-{
- vec3_t bboxcorners[8];
- int lines[3];
- int i, j, line;
-
- //upper corners
- bboxcorners[0][0] = origin[0] + maxs[0];
- bboxcorners[0][1] = origin[1] + maxs[1];
- bboxcorners[0][2] = origin[2] + maxs[2];
- //
- bboxcorners[1][0] = origin[0] + mins[0];
- bboxcorners[1][1] = origin[1] + maxs[1];
- bboxcorners[1][2] = origin[2] + maxs[2];
- //
- bboxcorners[2][0] = origin[0] + mins[0];
- bboxcorners[2][1] = origin[1] + mins[1];
- bboxcorners[2][2] = origin[2] + maxs[2];
- //
- bboxcorners[3][0] = origin[0] + maxs[0];
- bboxcorners[3][1] = origin[1] + mins[1];
- bboxcorners[3][2] = origin[2] + maxs[2];
- //lower corners
- Com_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4);
- for (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2];
- //draw bounding box
- for (i = 0; i < 4; i++)
- {
- for (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++)
- {
- if (!debuglines[line])
- {
- debuglines[line] = botimport.DebugLineCreate();
- lines[j++] = debuglines[line];
- debuglinevisible[line] = qtrue;
- numdebuglines++;
- } //end if
- else if (!debuglinevisible[line])
- {
- lines[j++] = debuglines[line];
- debuglinevisible[line] = qtrue;
- } //end else
- } //end for
- //top plane
- botimport.DebugLineShow(lines[0], bboxcorners[i],
- bboxcorners[(i+1)&3], LINECOLOR_RED);
- //bottom plane
- botimport.DebugLineShow(lines[1], bboxcorners[4+i],
- bboxcorners[4+((i+1)&3)], LINECOLOR_RED);
- //vertical lines
- botimport.DebugLineShow(lines[2], bboxcorners[i],
- bboxcorners[4+i], LINECOLOR_RED);
- } //end for
-} //end of the function AAS_ShowBoundingBox
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowFace(int facenum)
-{
- int i, color, edgenum;
- aas_edge_t *edge;
- aas_face_t *face;
- aas_plane_t *plane;
- vec3_t start, end;
-
- color = LINECOLOR_YELLOW;
- //check if face number is in range
- if (facenum >= aasworld.numfaces)
- {
- botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum);
- } //end if
- face = &aasworld.faces[facenum];
- //walk through the edges of the face
- for (i = 0; i < face->numedges; i++)
- {
- //edge number
- edgenum = abs(aasworld.edgeindex[face->firstedge + i]);
- //check if edge number is in range
- if (edgenum >= aasworld.numedges)
- {
- botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum);
- } //end if
- edge = &aasworld.edges[edgenum];
- if (color == LINECOLOR_RED) color = LINECOLOR_GREEN;
- else if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE;
- else if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW;
- else color = LINECOLOR_RED;
- AAS_DebugLine(aasworld.vertexes[edge->v[0]],
- aasworld.vertexes[edge->v[1]],
- color);
- } //end for
- plane = &aasworld.planes[face->planenum];
- edgenum = abs(aasworld.edgeindex[face->firstedge]);
- edge = &aasworld.edges[edgenum];
- VectorCopy(aasworld.vertexes[edge->v[0]], start);
- VectorMA(start, 20, plane->normal, end);
- AAS_DebugLine(start, end, LINECOLOR_RED);
-} //end of the function AAS_ShowFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowFacePolygon(int facenum, int color, int flip)
-{
- int i, edgenum, numpoints;
- vec3_t points[128];
- aas_edge_t *edge;
- aas_face_t *face;
-
- //check if face number is in range
- if (facenum >= aasworld.numfaces)
- {
- botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum);
- } //end if
- face = &aasworld.faces[facenum];
- //walk through the edges of the face
- numpoints = 0;
- if (flip)
- {
- for (i = face->numedges-1; i >= 0; i--)
- {
- //edge number
- edgenum = aasworld.edgeindex[face->firstedge + i];
- edge = &aasworld.edges[abs(edgenum)];
- VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]);
- numpoints++;
- } //end for
- } //end if
- else
- {
- for (i = 0; i < face->numedges; i++)
- {
- //edge number
- edgenum = aasworld.edgeindex[face->firstedge + i];
- edge = &aasworld.edges[abs(edgenum)];
- VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]);
- numpoints++;
- } //end for
- } //end else
- AAS_ShowPolygon(color, numpoints, points);
-} //end of the function AAS_ShowFacePolygon
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowArea(int areanum, int groundfacesonly)
-{
- int areaedges[MAX_DEBUGLINES];
- int numareaedges, i, j, n, color = 0, line;
- int facenum, edgenum;
- aas_area_t *area;
- aas_face_t *face;
- aas_edge_t *edge;
-
- //
- numareaedges = 0;
- //
- if (areanum < 0 || areanum >= aasworld.numareas)
- {
- botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n",
- areanum, aasworld.numareas);
- return;
- } //end if
- //pointer to the convex area
- area = &aasworld.areas[areanum];
- //walk through the faces of the area
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- //check if face number is in range
- if (facenum >= aasworld.numfaces)
- {
- botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum);
- } //end if
- face = &aasworld.faces[facenum];
- //ground faces only
- if (groundfacesonly)
- {
- if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue;
- } //end if
- //walk through the edges of the face
- for (j = 0; j < face->numedges; j++)
- {
- //edge number
- edgenum = abs(aasworld.edgeindex[face->firstedge + j]);
- //check if edge number is in range
- if (edgenum >= aasworld.numedges)
- {
- botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum);
- } //end if
- //check if the edge is stored already
- for (n = 0; n < numareaedges; n++)
- {
- if (areaedges[n] == edgenum) break;
- } //end for
- if (n == numareaedges && numareaedges < MAX_DEBUGLINES)
- {
- areaedges[numareaedges++] = edgenum;
- } //end if
- } //end for
- //AAS_ShowFace(facenum);
- } //end for
- //draw all the edges
- for (n = 0; n < numareaedges; n++)
- {
- for (line = 0; line < MAX_DEBUGLINES; line++)
- {
- if (!debuglines[line])
- {
- debuglines[line] = botimport.DebugLineCreate();
- debuglinevisible[line] = qfalse;
- numdebuglines++;
- } //end if
- if (!debuglinevisible[line])
- {
- break;
- } //end else
- } //end for
- if (line >= MAX_DEBUGLINES) return;
- edge = &aasworld.edges[areaedges[n]];
- if (color == LINECOLOR_RED) color = LINECOLOR_BLUE;
- else if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN;
- else if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW;
- else color = LINECOLOR_RED;
- botimport.DebugLineShow(debuglines[line],
- aasworld.vertexes[edge->v[0]],
- aasworld.vertexes[edge->v[1]],
- color);
- debuglinevisible[line] = qtrue;
- } //end for*/
-} //end of the function AAS_ShowArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly)
-{
- int i, facenum;
- aas_area_t *area;
- aas_face_t *face;
-
- //
- if (areanum < 0 || areanum >= aasworld.numareas)
- {
- botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n",
- areanum, aasworld.numareas);
- return;
- } //end if
- //pointer to the convex area
- area = &aasworld.areas[areanum];
- //walk through the faces of the area
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- //check if face number is in range
- if (facenum >= aasworld.numfaces)
- {
- botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum);
- } //end if
- face = &aasworld.faces[facenum];
- //ground faces only
- if (groundfacesonly)
- {
- if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue;
- } //end if
- AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum);
- } //end for
-} //end of the function AAS_ShowAreaPolygons
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DrawCross(vec3_t origin, float size, int color)
-{
- int i;
- vec3_t start, end;
-
- for (i = 0; i < 3; i++)
- {
- VectorCopy(origin, start);
- start[i] += size;
- VectorCopy(origin, end);
- end[i] -= size;
- AAS_DebugLine(start, end, color);
- } //end for
-} //end of the function AAS_DrawCross
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_PrintTravelType(int traveltype)
-{
-#ifdef DEBUG
- char *str;
- //
- switch(traveltype & TRAVELTYPE_MASK)
- {
- case TRAVEL_INVALID: str = "TRAVEL_INVALID"; break;
- case TRAVEL_WALK: str = "TRAVEL_WALK"; break;
- case TRAVEL_CROUCH: str = "TRAVEL_CROUCH"; break;
- case TRAVEL_BARRIERJUMP: str = "TRAVEL_BARRIERJUMP"; break;
- case TRAVEL_JUMP: str = "TRAVEL_JUMP"; break;
- case TRAVEL_LADDER: str = "TRAVEL_LADDER"; break;
- case TRAVEL_WALKOFFLEDGE: str = "TRAVEL_WALKOFFLEDGE"; break;
- case TRAVEL_SWIM: str = "TRAVEL_SWIM"; break;
- case TRAVEL_WATERJUMP: str = "TRAVEL_WATERJUMP"; break;
- case TRAVEL_TELEPORT: str = "TRAVEL_TELEPORT"; break;
- case TRAVEL_ELEVATOR: str = "TRAVEL_ELEVATOR"; break;
- case TRAVEL_ROCKETJUMP: str = "TRAVEL_ROCKETJUMP"; break;
- case TRAVEL_BFGJUMP: str = "TRAVEL_BFGJUMP"; break;
- case TRAVEL_GRAPPLEHOOK: str = "TRAVEL_GRAPPLEHOOK"; break;
- case TRAVEL_JUMPPAD: str = "TRAVEL_JUMPPAD"; break;
- case TRAVEL_FUNCBOB: str = "TRAVEL_FUNCBOB"; break;
- default: str = "UNKNOWN TRAVEL TYPE"; break;
- } //end switch
- botimport.Print(PRT_MESSAGE, "%s", str);
-#endif
-} //end of the function AAS_PrintTravelType
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor)
-{
- vec3_t dir, cross, p1, p2, up = {0, 0, 1};
- float dot;
-
- VectorSubtract(end, start, dir);
- VectorNormalize(dir);
- dot = DotProduct(dir, up);
- if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
- else CrossProduct(dir, up, cross);
-
- VectorMA(end, -6, dir, p1);
- VectorCopy(p1, p2);
- VectorMA(p1, 6, cross, p1);
- VectorMA(p2, -6, cross, p2);
-
- AAS_DebugLine(start, end, linecolor);
- AAS_DebugLine(p1, end, arrowcolor);
- AAS_DebugLine(p2, end, arrowcolor);
-} //end of the function AAS_DrawArrow
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowReachability(aas_reachability_t *reach)
-{
- vec3_t dir, cmdmove, velocity;
- float speed, zvel;
- aas_clientmove_t move;
-
- AAS_ShowAreaPolygons(reach->areanum, 5, qtrue);
- //AAS_ShowArea(reach->areanum, qtrue);
- AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW);
- //
- if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP ||
- (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE)
- {
- AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed);
- //
- VectorSubtract(reach->end, reach->start, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- //set the velocity
- VectorScale(dir, speed, velocity);
- //set the command movement
- VectorClear(cmdmove);
- cmdmove[2] = aassettings.phys_jumpvel;
- //
- AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue,
- velocity, cmdmove, 3, 30, 0.1f,
- SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue);
- //
- if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
- {
- AAS_JumpReachRunStart(reach, dir);
- AAS_DrawCross(dir, 4, LINECOLOR_BLUE);
- } //end if
- } //end if
- else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP)
- {
- zvel = AAS_RocketJumpZVelocity(reach->start);
- AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed);
- //
- VectorSubtract(reach->end, reach->start, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- //get command movement
- VectorScale(dir, speed, cmdmove);
- VectorSet(velocity, 0, 0, zvel);
- //
- AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue,
- velocity, cmdmove, 30, 30, 0.1f,
- SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
- SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue);
- } //end else if
- else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)
- {
- VectorSet(cmdmove, 0, 0, 0);
- //
- VectorSubtract(reach->end, reach->start, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- //set the velocity
- //NOTE: the edgenum is the horizontal velocity
- VectorScale(dir, reach->edgenum, velocity);
- //NOTE: the facenum is the Z velocity
- velocity[2] = reach->facenum;
- //
- AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue,
- velocity, cmdmove, 30, 30, 0.1f,
- SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
- SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue);
- } //end else if
-} //end of the function AAS_ShowReachability
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShowReachableAreas(int areanum)
-{
- aas_areasettings_t *settings;
- static aas_reachability_t reach;
- static int index, lastareanum;
- static float lasttime;
-
- if (areanum != lastareanum)
- {
- index = 0;
- lastareanum = areanum;
- } //end if
- settings = &aasworld.areasettings[areanum];
- //
- if (!settings->numreachableareas) return;
- //
- if (index >= settings->numreachableareas) index = 0;
- //
- if (AAS_Time() - lasttime > 1.5)
- {
- Com_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t));
- index++;
- lasttime = AAS_Time();
- AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
- botimport.Print(PRT_MESSAGE, "\n");
- } //end if
- AAS_ShowReachability(&reach);
-} //end of the function ShowReachableAreas
-
-void AAS_FloodAreas_r(int areanum, int cluster, int *done)
-{
- int nextareanum, i, facenum;
- aas_area_t *area;
- aas_face_t *face;
- aas_areasettings_t *settings;
- aas_reachability_t *reach;
-
- AAS_ShowAreaPolygons(areanum, 1, qtrue);
- //pointer to the convex area
- area = &aasworld.areas[areanum];
- settings = &aasworld.areasettings[areanum];
- //walk through the faces of the area
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- face = &aasworld.faces[facenum];
- if (face->frontarea == areanum)
- nextareanum = face->backarea;
- else
- nextareanum = face->frontarea;
- if (!nextareanum)
- continue;
- if (done[nextareanum])
- continue;
- done[nextareanum] = qtrue;
- if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL)
- continue;
- if (AAS_AreaCluster(nextareanum) != cluster)
- continue;
- AAS_FloodAreas_r(nextareanum, cluster, done);
- } //end for
- //
- for (i = 0; i < settings->numreachableareas; i++)
- {
- reach = &aasworld.reachability[settings->firstreachablearea + i];
- nextareanum = reach->areanum;
- if (!nextareanum)
- continue;
- if (done[nextareanum])
- continue;
- done[nextareanum] = qtrue;
- if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL)
- continue;
- if (AAS_AreaCluster(nextareanum) != cluster)
- continue;
- /*
- if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE)
- {
- AAS_DebugLine(reach->start, reach->end, 1);
- }
- */
- AAS_FloodAreas_r(nextareanum, cluster, done);
- }
-}
-
-void AAS_FloodAreas(vec3_t origin)
-{
- int areanum, cluster, *done;
-
- done = (int *) GetClearedMemory(aasworld.numareas * sizeof(int));
- areanum = AAS_PointAreaNum(origin);
- cluster = AAS_AreaCluster(areanum);
- AAS_FloodAreas_r(areanum, cluster, done);
-}
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_debug.c + * + * desc: AAS debug code + * + * $Archive: /MissionPack/code/botlib/be_aas_debug.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_interface.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +#define MAX_DEBUGLINES 1024 +#define MAX_DEBUGPOLYGONS 8192 + +int debuglines[MAX_DEBUGLINES]; +int debuglinevisible[MAX_DEBUGLINES]; +int numdebuglines; + +static int debugpolygons[MAX_DEBUGPOLYGONS]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearShownPolygons(void) +{ + int i; +//* + for (i = 0; i < MAX_DEBUGPOLYGONS; i++) + { + if (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]); + debugpolygons[i] = 0; + } //end for +//*/ +/* + for (i = 0; i < MAX_DEBUGPOLYGONS; i++) + { + botimport.DebugPolygonDelete(i); + debugpolygons[i] = 0; + } //end for +*/ +} //end of the function AAS_ClearShownPolygons +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowPolygon(int color, int numpoints, vec3_t *points) +{ + int i; + + for (i = 0; i < MAX_DEBUGPOLYGONS; i++) + { + if (!debugpolygons[i]) + { + debugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points); + break; + } //end if + } //end for +} //end of the function AAS_ShowPolygon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearShownDebugLines(void) +{ + int i; + + //make all lines invisible + for (i = 0; i < MAX_DEBUGLINES; i++) + { + if (debuglines[i]) + { + //botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE); + botimport.DebugLineDelete(debuglines[i]); + debuglines[i] = 0; + debuglinevisible[i] = qfalse; + } //end if + } //end for +} //end of the function AAS_ClearShownDebugLines +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DebugLine(vec3_t start, vec3_t end, int color) +{ + int line; + + for (line = 0; line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + debuglinevisible[line] = qfalse; + numdebuglines++; + } //end if + if (!debuglinevisible[line]) + { + botimport.DebugLineShow(debuglines[line], start, end, color); + debuglinevisible[line] = qtrue; + return; + } //end else + } //end for +} //end of the function AAS_DebugLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PermanentLine(vec3_t start, vec3_t end, int color) +{ + int line; + + line = botimport.DebugLineCreate(); + botimport.DebugLineShow(line, start, end, color); +} //end of the function AAS_PermenentLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawPermanentCross(vec3_t origin, float size, int color) +{ + int i, debugline; + vec3_t start, end; + + for (i = 0; i < 3; i++) + { + VectorCopy(origin, start); + start[i] += size; + VectorCopy(origin, end); + end[i] -= size; + AAS_DebugLine(start, end, color); + debugline = botimport.DebugLineCreate(); + botimport.DebugLineShow(debugline, start, end, color); + } //end for +} //end of the function AAS_DrawPermanentCross +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color) +{ + int n0, n1, n2, j, line, lines[2]; + vec3_t start1, end1, start2, end2; + + //make a cross in the hit plane at the hit point + VectorCopy(point, start1); + VectorCopy(point, end1); + VectorCopy(point, start2); + VectorCopy(point, end2); + + n0 = type % 3; + n1 = (type + 1) % 3; + n2 = (type + 2) % 3; + start1[n1] -= 6; + start1[n2] -= 6; + end1[n1] += 6; + end1[n2] += 6; + start2[n1] += 6; + start2[n2] -= 6; + end2[n1] -= 6; + end2[n2] += 6; + + start1[n0] = (dist - (start1[n1] * normal[n1] + + start1[n2] * normal[n2])) / normal[n0]; + end1[n0] = (dist - (end1[n1] * normal[n1] + + end1[n2] * normal[n2])) / normal[n0]; + start2[n0] = (dist - (start2[n1] * normal[n1] + + start2[n2] * normal[n2])) / normal[n0]; + end2[n0] = (dist - (end2[n1] * normal[n1] + + end2[n2] * normal[n2])) / normal[n0]; + + for (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + numdebuglines++; + } //end if + else if (!debuglinevisible[line]) + { + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + } //end else + } //end for + botimport.DebugLineShow(lines[0], start1, end1, color); + botimport.DebugLineShow(lines[1], start2, end2, color); +} //end of the function AAS_DrawPlaneCross +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs) +{ + vec3_t bboxcorners[8]; + int lines[3]; + int i, j, line; + + //upper corners + bboxcorners[0][0] = origin[0] + maxs[0]; + bboxcorners[0][1] = origin[1] + maxs[1]; + bboxcorners[0][2] = origin[2] + maxs[2]; + // + bboxcorners[1][0] = origin[0] + mins[0]; + bboxcorners[1][1] = origin[1] + maxs[1]; + bboxcorners[1][2] = origin[2] + maxs[2]; + // + bboxcorners[2][0] = origin[0] + mins[0]; + bboxcorners[2][1] = origin[1] + mins[1]; + bboxcorners[2][2] = origin[2] + maxs[2]; + // + bboxcorners[3][0] = origin[0] + maxs[0]; + bboxcorners[3][1] = origin[1] + mins[1]; + bboxcorners[3][2] = origin[2] + maxs[2]; + //lower corners + Com_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4); + for (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2]; + //draw bounding box + for (i = 0; i < 4; i++) + { + for (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + numdebuglines++; + } //end if + else if (!debuglinevisible[line]) + { + lines[j++] = debuglines[line]; + debuglinevisible[line] = qtrue; + } //end else + } //end for + //top plane + botimport.DebugLineShow(lines[0], bboxcorners[i], + bboxcorners[(i+1)&3], LINECOLOR_RED); + //bottom plane + botimport.DebugLineShow(lines[1], bboxcorners[4+i], + bboxcorners[4+((i+1)&3)], LINECOLOR_RED); + //vertical lines + botimport.DebugLineShow(lines[2], bboxcorners[i], + bboxcorners[4+i], LINECOLOR_RED); + } //end for +} //end of the function AAS_ShowBoundingBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowFace(int facenum) +{ + int i, color, edgenum; + aas_edge_t *edge; + aas_face_t *face; + aas_plane_t *plane; + vec3_t start, end; + + color = LINECOLOR_YELLOW; + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //walk through the edges of the face + for (i = 0; i < face->numedges; i++) + { + //edge number + edgenum = abs(aasworld.edgeindex[face->firstedge + i]); + //check if edge number is in range + if (edgenum >= aasworld.numedges) + { + botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); + } //end if + edge = &aasworld.edges[edgenum]; + if (color == LINECOLOR_RED) color = LINECOLOR_GREEN; + else if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE; + else if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW; + else color = LINECOLOR_RED; + AAS_DebugLine(aasworld.vertexes[edge->v[0]], + aasworld.vertexes[edge->v[1]], + color); + } //end for + plane = &aasworld.planes[face->planenum]; + edgenum = abs(aasworld.edgeindex[face->firstedge]); + edge = &aasworld.edges[edgenum]; + VectorCopy(aasworld.vertexes[edge->v[0]], start); + VectorMA(start, 20, plane->normal, end); + AAS_DebugLine(start, end, LINECOLOR_RED); +} //end of the function AAS_ShowFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowFacePolygon(int facenum, int color, int flip) +{ + int i, edgenum, numpoints; + vec3_t points[128]; + aas_edge_t *edge; + aas_face_t *face; + + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //walk through the edges of the face + numpoints = 0; + if (flip) + { + for (i = face->numedges-1; i >= 0; i--) + { + //edge number + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); + numpoints++; + } //end for + } //end if + else + { + for (i = 0; i < face->numedges; i++) + { + //edge number + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + VectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]); + numpoints++; + } //end for + } //end else + AAS_ShowPolygon(color, numpoints, points); +} //end of the function AAS_ShowFacePolygon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowArea(int areanum, int groundfacesonly) +{ + int areaedges[MAX_DEBUGLINES]; + int numareaedges, i, j, n, color = 0, line; + int facenum, edgenum; + aas_area_t *area; + aas_face_t *face; + aas_edge_t *edge; + + // + numareaedges = 0; + // + if (areanum < 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", + areanum, aasworld.numareas); + return; + } //end if + //pointer to the convex area + area = &aasworld.areas[areanum]; + //walk through the faces of the area + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //ground faces only + if (groundfacesonly) + { + if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; + } //end if + //walk through the edges of the face + for (j = 0; j < face->numedges; j++) + { + //edge number + edgenum = abs(aasworld.edgeindex[face->firstedge + j]); + //check if edge number is in range + if (edgenum >= aasworld.numedges) + { + botimport.Print(PRT_ERROR, "edgenum %d out of range\n", edgenum); + } //end if + //check if the edge is stored already + for (n = 0; n < numareaedges; n++) + { + if (areaedges[n] == edgenum) break; + } //end for + if (n == numareaedges && numareaedges < MAX_DEBUGLINES) + { + areaedges[numareaedges++] = edgenum; + } //end if + } //end for + //AAS_ShowFace(facenum); + } //end for + //draw all the edges + for (n = 0; n < numareaedges; n++) + { + for (line = 0; line < MAX_DEBUGLINES; line++) + { + if (!debuglines[line]) + { + debuglines[line] = botimport.DebugLineCreate(); + debuglinevisible[line] = qfalse; + numdebuglines++; + } //end if + if (!debuglinevisible[line]) + { + break; + } //end else + } //end for + if (line >= MAX_DEBUGLINES) return; + edge = &aasworld.edges[areaedges[n]]; + if (color == LINECOLOR_RED) color = LINECOLOR_BLUE; + else if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN; + else if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW; + else color = LINECOLOR_RED; + botimport.DebugLineShow(debuglines[line], + aasworld.vertexes[edge->v[0]], + aasworld.vertexes[edge->v[1]], + color); + debuglinevisible[line] = qtrue; + } //end for*/ +} //end of the function AAS_ShowArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly) +{ + int i, facenum; + aas_area_t *area; + aas_face_t *face; + + // + if (areanum < 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "area %d out of range [0, %d]\n", + areanum, aasworld.numareas); + return; + } //end if + //pointer to the convex area + area = &aasworld.areas[areanum]; + //walk through the faces of the area + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + //check if face number is in range + if (facenum >= aasworld.numfaces) + { + botimport.Print(PRT_ERROR, "facenum %d out of range\n", facenum); + } //end if + face = &aasworld.faces[facenum]; + //ground faces only + if (groundfacesonly) + { + if (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue; + } //end if + AAS_ShowFacePolygon(facenum, color, face->frontarea != areanum); + } //end for +} //end of the function AAS_ShowAreaPolygons +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawCross(vec3_t origin, float size, int color) +{ + int i; + vec3_t start, end; + + for (i = 0; i < 3; i++) + { + VectorCopy(origin, start); + start[i] += size; + VectorCopy(origin, end); + end[i] -= size; + AAS_DebugLine(start, end, color); + } //end for +} //end of the function AAS_DrawCross +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PrintTravelType(int traveltype) +{ +#ifdef DEBUG + char *str; + // + switch(traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_INVALID: str = "TRAVEL_INVALID"; break; + case TRAVEL_WALK: str = "TRAVEL_WALK"; break; + case TRAVEL_CROUCH: str = "TRAVEL_CROUCH"; break; + case TRAVEL_BARRIERJUMP: str = "TRAVEL_BARRIERJUMP"; break; + case TRAVEL_JUMP: str = "TRAVEL_JUMP"; break; + case TRAVEL_LADDER: str = "TRAVEL_LADDER"; break; + case TRAVEL_WALKOFFLEDGE: str = "TRAVEL_WALKOFFLEDGE"; break; + case TRAVEL_SWIM: str = "TRAVEL_SWIM"; break; + case TRAVEL_WATERJUMP: str = "TRAVEL_WATERJUMP"; break; + case TRAVEL_TELEPORT: str = "TRAVEL_TELEPORT"; break; + case TRAVEL_ELEVATOR: str = "TRAVEL_ELEVATOR"; break; + case TRAVEL_ROCKETJUMP: str = "TRAVEL_ROCKETJUMP"; break; + case TRAVEL_BFGJUMP: str = "TRAVEL_BFGJUMP"; break; + case TRAVEL_GRAPPLEHOOK: str = "TRAVEL_GRAPPLEHOOK"; break; + case TRAVEL_JUMPPAD: str = "TRAVEL_JUMPPAD"; break; + case TRAVEL_FUNCBOB: str = "TRAVEL_FUNCBOB"; break; + default: str = "UNKNOWN TRAVEL TYPE"; break; + } //end switch + botimport.Print(PRT_MESSAGE, "%s", str); +#endif +} //end of the function AAS_PrintTravelType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor) +{ + vec3_t dir, cross, p1, p2, up = {0, 0, 1}; + float dot; + + VectorSubtract(end, start, dir); + VectorNormalize(dir); + dot = DotProduct(dir, up); + if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0); + else CrossProduct(dir, up, cross); + + VectorMA(end, -6, dir, p1); + VectorCopy(p1, p2); + VectorMA(p1, 6, cross, p1); + VectorMA(p2, -6, cross, p2); + + AAS_DebugLine(start, end, linecolor); + AAS_DebugLine(p1, end, arrowcolor); + AAS_DebugLine(p2, end, arrowcolor); +} //end of the function AAS_DrawArrow +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowReachability(aas_reachability_t *reach) +{ + vec3_t dir, cmdmove, velocity; + float speed, zvel; + aas_clientmove_t move; + + AAS_ShowAreaPolygons(reach->areanum, 5, qtrue); + //AAS_ShowArea(reach->areanum, qtrue); + AAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); + // + if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP || + (reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) + { + AAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed); + // + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + VectorNormalize(dir); + //set the velocity + VectorScale(dir, speed, velocity); + //set the command movement + VectorClear(cmdmove); + cmdmove[2] = aassettings.phys_jumpvel; + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 3, 30, 0.1f, + SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue); + // + if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) + { + AAS_JumpReachRunStart(reach, dir); + AAS_DrawCross(dir, 4, LINECOLOR_BLUE); + } //end if + } //end if + else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) + { + zvel = AAS_RocketJumpZVelocity(reach->start); + AAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed); + // + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + VectorNormalize(dir); + //get command movement + VectorScale(dir, speed, cmdmove); + VectorSet(velocity, 0, 0, zvel); + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); + } //end else if + else if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) + { + VectorSet(cmdmove, 0, 0, 0); + // + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + VectorNormalize(dir); + //set the velocity + //NOTE: the edgenum is the horizontal velocity + VectorScale(dir, reach->edgenum, velocity); + //NOTE: the facenum is the Z velocity + velocity[2] = reach->facenum; + // + AAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue); + } //end else if +} //end of the function AAS_ShowReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowReachableAreas(int areanum) +{ + aas_areasettings_t *settings; + static aas_reachability_t reach; + static int index, lastareanum; + static float lasttime; + + if (areanum != lastareanum) + { + index = 0; + lastareanum = areanum; + } //end if + settings = &aasworld.areasettings[areanum]; + // + if (!settings->numreachableareas) return; + // + if (index >= settings->numreachableareas) index = 0; + // + if (AAS_Time() - lasttime > 1.5) + { + Com_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t)); + index++; + lasttime = AAS_Time(); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + AAS_ShowReachability(&reach); +} //end of the function ShowReachableAreas + +void AAS_FloodAreas_r(int areanum, int cluster, int *done) +{ + int nextareanum, i, facenum; + aas_area_t *area; + aas_face_t *face; + aas_areasettings_t *settings; + aas_reachability_t *reach; + + AAS_ShowAreaPolygons(areanum, 1, qtrue); + //pointer to the convex area + area = &aasworld.areas[areanum]; + settings = &aasworld.areasettings[areanum]; + //walk through the faces of the area + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + if (face->frontarea == areanum) + nextareanum = face->backarea; + else + nextareanum = face->frontarea; + if (!nextareanum) + continue; + if (done[nextareanum]) + continue; + done[nextareanum] = qtrue; + if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) + continue; + if (AAS_AreaCluster(nextareanum) != cluster) + continue; + AAS_FloodAreas_r(nextareanum, cluster, done); + } //end for + // + for (i = 0; i < settings->numreachableareas; i++) + { + reach = &aasworld.reachability[settings->firstreachablearea + i]; + nextareanum = reach->areanum; + if (!nextareanum) + continue; + if (done[nextareanum]) + continue; + done[nextareanum] = qtrue; + if (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL) + continue; + if (AAS_AreaCluster(nextareanum) != cluster) + continue; + /* + if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE) + { + AAS_DebugLine(reach->start, reach->end, 1); + } + */ + AAS_FloodAreas_r(nextareanum, cluster, done); + } +} + +void AAS_FloodAreas(vec3_t origin) +{ + int areanum, cluster, *done; + + done = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); + areanum = AAS_PointAreaNum(origin); + cluster = AAS_AreaCluster(areanum); + AAS_FloodAreas_r(areanum, cluster, done); +} diff --git a/code/botlib/be_aas_debug.h b/code/botlib/be_aas_debug.h index f0147c1..ad0f2a2 100755 --- a/code/botlib/be_aas_debug.h +++ b/code/botlib/be_aas_debug.h @@ -1,62 +1,62 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_debug.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_debug.h $
- *
- *****************************************************************************/
-
-//clear the shown debug lines
-void AAS_ClearShownDebugLines(void);
-//
-void AAS_ClearShownPolygons(void);
-//show a debug line
-void AAS_DebugLine(vec3_t start, vec3_t end, int color);
-//show a permenent line
-void AAS_PermanentLine(vec3_t start, vec3_t end, int color);
-//show a permanent cross
-void AAS_DrawPermanentCross(vec3_t origin, float size, int color);
-//draw a cross in the plane
-void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color);
-//show a bounding box
-void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs);
-//show a face
-void AAS_ShowFace(int facenum);
-//show an area
-void AAS_ShowArea(int areanum, int groundfacesonly);
-//
-void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly);
-//draw a cros
-void AAS_DrawCross(vec3_t origin, float size, int color);
-//print the travel type
-void AAS_PrintTravelType(int traveltype);
-//draw an arrow
-void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor);
-//visualize the given reachability
-void AAS_ShowReachability(struct aas_reachability_s *reach);
-//show the reachable areas from the given area
-void AAS_ShowReachableAreas(int areanum);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_debug.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_debug.h $ + * + *****************************************************************************/ + +//clear the shown debug lines +void AAS_ClearShownDebugLines(void); +// +void AAS_ClearShownPolygons(void); +//show a debug line +void AAS_DebugLine(vec3_t start, vec3_t end, int color); +//show a permenent line +void AAS_PermanentLine(vec3_t start, vec3_t end, int color); +//show a permanent cross +void AAS_DrawPermanentCross(vec3_t origin, float size, int color); +//draw a cross in the plane +void AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color); +//show a bounding box +void AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs); +//show a face +void AAS_ShowFace(int facenum); +//show an area +void AAS_ShowArea(int areanum, int groundfacesonly); +// +void AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly); +//draw a cros +void AAS_DrawCross(vec3_t origin, float size, int color); +//print the travel type +void AAS_PrintTravelType(int traveltype); +//draw an arrow +void AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor); +//visualize the given reachability +void AAS_ShowReachability(struct aas_reachability_s *reach); +//show the reachable areas from the given area +void AAS_ShowReachableAreas(int areanum); + diff --git a/code/botlib/be_aas_def.h b/code/botlib/be_aas_def.h index 22ebbc5..72bb3e6 100755 --- a/code/botlib/be_aas_def.h +++ b/code/botlib/be_aas_def.h @@ -1,306 +1,306 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_def.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_def.h $
- *
- *****************************************************************************/
-
-//debugging on
-#define AAS_DEBUG
-
-#define MAX_CLIENTS 64
-#define MAX_MODELS 256 // these are sent over the net as 8 bits
-#define MAX_SOUNDS 256 // so they cannot be blindly increased
-#define MAX_CONFIGSTRINGS 1024
-
-#define CS_SCORES 32
-#define CS_MODELS (CS_SCORES+MAX_CLIENTS)
-#define CS_SOUNDS (CS_MODELS+MAX_MODELS)
-
-#define DF_AASENTNUMBER(x) (x - aasworld.entities)
-#define DF_NUMBERAASENT(x) (&aasworld.entities[x])
-#define DF_AASENTCLIENT(x) (x - aasworld.entities - 1)
-#define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1])
-
-#ifndef MAX_PATH
- #define MAX_PATH MAX_QPATH
-#endif
-
-//string index (for model, sound and image index)
-typedef struct aas_stringindex_s
-{
- int numindexes;
- char **index;
-} aas_stringindex_t;
-
-//structure to link entities to areas and areas to entities
-typedef struct aas_link_s
-{
- int entnum;
- int areanum;
- struct aas_link_s *next_ent, *prev_ent;
- struct aas_link_s *next_area, *prev_area;
-} aas_link_t;
-
-//structure to link entities to leaves and leaves to entities
-typedef struct bsp_link_s
-{
- int entnum;
- int leafnum;
- struct bsp_link_s *next_ent, *prev_ent;
- struct bsp_link_s *next_leaf, *prev_leaf;
-} bsp_link_t;
-
-typedef struct bsp_entdata_s
-{
- vec3_t origin;
- vec3_t angles;
- vec3_t absmins;
- vec3_t absmaxs;
- int solid;
- int modelnum;
-} bsp_entdata_t;
-
-//entity
-typedef struct aas_entity_s
-{
- //entity info
- aas_entityinfo_t i;
- //links into the AAS areas
- aas_link_t *areas;
- //links into the BSP leaves
- bsp_link_t *leaves;
-} aas_entity_t;
-
-typedef struct aas_settings_s
-{
- vec3_t phys_gravitydirection;
- float phys_friction;
- float phys_stopspeed;
- float phys_gravity;
- float phys_waterfriction;
- float phys_watergravity;
- float phys_maxvelocity;
- float phys_maxwalkvelocity;
- float phys_maxcrouchvelocity;
- float phys_maxswimvelocity;
- float phys_walkaccelerate;
- float phys_airaccelerate;
- float phys_swimaccelerate;
- float phys_maxstep;
- float phys_maxsteepness;
- float phys_maxwaterjump;
- float phys_maxbarrier;
- float phys_jumpvel;
- float phys_falldelta5;
- float phys_falldelta10;
- float rs_waterjump;
- float rs_teleport;
- float rs_barrierjump;
- float rs_startcrouch;
- float rs_startgrapple;
- float rs_startwalkoffledge;
- float rs_startjump;
- float rs_rocketjump;
- float rs_bfgjump;
- float rs_jumppad;
- float rs_aircontrolledjumppad;
- float rs_funcbob;
- float rs_startelevator;
- float rs_falldamage5;
- float rs_falldamage10;
- float rs_maxfallheight;
- float rs_maxjumpfallheight;
-} aas_settings_t;
-
-#define CACHETYPE_PORTAL 0
-#define CACHETYPE_AREA 1
-
-//routing cache
-typedef struct aas_routingcache_s
-{
- byte type; //portal or area cache
- float time; //last time accessed or updated
- int size; //size of the routing cache
- int cluster; //cluster the cache is for
- int areanum; //area the cache is created for
- vec3_t origin; //origin within the area
- float starttraveltime; //travel time to start with
- int travelflags; //combinations of the travel flags
- struct aas_routingcache_s *prev, *next;
- struct aas_routingcache_s *time_prev, *time_next;
- unsigned char *reachabilities; //reachabilities used for routing
- unsigned short int traveltimes[1]; //travel time for every area (variable sized)
-} aas_routingcache_t;
-
-//fields for the routing algorithm
-typedef struct aas_routingupdate_s
-{
- int cluster;
- int areanum; //area number of the update
- vec3_t start; //start point the area was entered
- unsigned short int tmptraveltime; //temporary travel time
- unsigned short int *areatraveltimes; //travel times within the area
- qboolean inlist; //true if the update is in the list
- struct aas_routingupdate_s *next;
- struct aas_routingupdate_s *prev;
-} aas_routingupdate_t;
-
-//reversed reachability link
-typedef struct aas_reversedlink_s
-{
- int linknum; //the aas_areareachability_t
- int areanum; //reachable from this area
- struct aas_reversedlink_s *next; //next link
-} aas_reversedlink_t;
-
-//reversed area reachability
-typedef struct aas_reversedreachability_s
-{
- int numlinks;
- aas_reversedlink_t *first;
-} aas_reversedreachability_t;
-
-//areas a reachability goes through
-typedef struct aas_reachabilityareas_s
-{
- int firstarea, numareas;
-} aas_reachabilityareas_t;
-
-typedef struct aas_s
-{
- int loaded; //true when an AAS file is loaded
- int initialized; //true when AAS has been initialized
- int savefile; //set true when file should be saved
- int bspchecksum;
- //current time
- float time;
- int numframes;
- //name of the aas file
- char filename[MAX_PATH];
- char mapname[MAX_PATH];
- //bounding boxes
- int numbboxes;
- aas_bbox_t *bboxes;
- //vertexes
- int numvertexes;
- aas_vertex_t *vertexes;
- //planes
- int numplanes;
- aas_plane_t *planes;
- //edges
- int numedges;
- aas_edge_t *edges;
- //edge index
- int edgeindexsize;
- aas_edgeindex_t *edgeindex;
- //faces
- int numfaces;
- aas_face_t *faces;
- //face index
- int faceindexsize;
- aas_faceindex_t *faceindex;
- //convex areas
- int numareas;
- aas_area_t *areas;
- //convex area settings
- int numareasettings;
- aas_areasettings_t *areasettings;
- //reachablity list
- int reachabilitysize;
- aas_reachability_t *reachability;
- //nodes of the bsp tree
- int numnodes;
- aas_node_t *nodes;
- //cluster portals
- int numportals;
- aas_portal_t *portals;
- //cluster portal index
- int portalindexsize;
- aas_portalindex_t *portalindex;
- //clusters
- int numclusters;
- aas_cluster_t *clusters;
- //
- int numreachabilityareas;
- float reachabilitytime;
- //enities linked in the areas
- aas_link_t *linkheap; //heap with link structures
- int linkheapsize; //size of the link heap
- aas_link_t *freelinks; //first free link
- aas_link_t **arealinkedentities; //entities linked into areas
- //entities
- int maxentities;
- int maxclients;
- aas_entity_t *entities;
- //string indexes
- char *configstrings[MAX_CONFIGSTRINGS];
- int indexessetup;
- //index to retrieve travel flag for a travel type
- int travelflagfortype[MAX_TRAVELTYPES];
- //travel flags for each area based on contents
- int *areacontentstravelflags;
- //routing update
- aas_routingupdate_t *areaupdate;
- aas_routingupdate_t *portalupdate;
- //number of routing updates during a frame (reset every frame)
- int frameroutingupdates;
- //reversed reachability links
- aas_reversedreachability_t *reversedreachability;
- //travel times within the areas
- unsigned short ***areatraveltimes;
- //array of size numclusters with cluster cache
- aas_routingcache_t ***clusterareacache;
- aas_routingcache_t **portalcache;
- //cache list sorted on time
- aas_routingcache_t *oldestcache; // start of cache list sorted on time
- aas_routingcache_t *newestcache; // end of cache list sorted on time
- //maximum travel time through portal areas
- int *portalmaxtraveltimes;
- //areas the reachabilities go through
- int *reachabilityareaindex;
- aas_reachabilityareas_t *reachabilityareas;
-} aas_t;
-
-#define AASINTERN
-
-#ifndef BSPCINCLUDE
-
-#include "be_aas_main.h"
-#include "be_aas_entity.h"
-#include "be_aas_sample.h"
-#include "be_aas_cluster.h"
-#include "be_aas_reach.h"
-#include "be_aas_route.h"
-#include "be_aas_routealt.h"
-#include "be_aas_debug.h"
-#include "be_aas_file.h"
-#include "be_aas_optimize.h"
-#include "be_aas_bsp.h"
-#include "be_aas_move.h"
-
-#endif //BSPCINCLUDE
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_def.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_def.h $ + * + *****************************************************************************/ + +//debugging on +#define AAS_DEBUG + +#define MAX_CLIENTS 64 +#define MAX_MODELS 256 // these are sent over the net as 8 bits +#define MAX_SOUNDS 256 // so they cannot be blindly increased +#define MAX_CONFIGSTRINGS 1024 + +#define CS_SCORES 32 +#define CS_MODELS (CS_SCORES+MAX_CLIENTS) +#define CS_SOUNDS (CS_MODELS+MAX_MODELS) + +#define DF_AASENTNUMBER(x) (x - aasworld.entities) +#define DF_NUMBERAASENT(x) (&aasworld.entities[x]) +#define DF_AASENTCLIENT(x) (x - aasworld.entities - 1) +#define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1]) + +#ifndef MAX_PATH + #define MAX_PATH MAX_QPATH +#endif + +//string index (for model, sound and image index) +typedef struct aas_stringindex_s +{ + int numindexes; + char **index; +} aas_stringindex_t; + +//structure to link entities to areas and areas to entities +typedef struct aas_link_s +{ + int entnum; + int areanum; + struct aas_link_s *next_ent, *prev_ent; + struct aas_link_s *next_area, *prev_area; +} aas_link_t; + +//structure to link entities to leaves and leaves to entities +typedef struct bsp_link_s +{ + int entnum; + int leafnum; + struct bsp_link_s *next_ent, *prev_ent; + struct bsp_link_s *next_leaf, *prev_leaf; +} bsp_link_t; + +typedef struct bsp_entdata_s +{ + vec3_t origin; + vec3_t angles; + vec3_t absmins; + vec3_t absmaxs; + int solid; + int modelnum; +} bsp_entdata_t; + +//entity +typedef struct aas_entity_s +{ + //entity info + aas_entityinfo_t i; + //links into the AAS areas + aas_link_t *areas; + //links into the BSP leaves + bsp_link_t *leaves; +} aas_entity_t; + +typedef struct aas_settings_s +{ + vec3_t phys_gravitydirection; + float phys_friction; + float phys_stopspeed; + float phys_gravity; + float phys_waterfriction; + float phys_watergravity; + float phys_maxvelocity; + float phys_maxwalkvelocity; + float phys_maxcrouchvelocity; + float phys_maxswimvelocity; + float phys_walkaccelerate; + float phys_airaccelerate; + float phys_swimaccelerate; + float phys_maxstep; + float phys_maxsteepness; + float phys_maxwaterjump; + float phys_maxbarrier; + float phys_jumpvel; + float phys_falldelta5; + float phys_falldelta10; + float rs_waterjump; + float rs_teleport; + float rs_barrierjump; + float rs_startcrouch; + float rs_startgrapple; + float rs_startwalkoffledge; + float rs_startjump; + float rs_rocketjump; + float rs_bfgjump; + float rs_jumppad; + float rs_aircontrolledjumppad; + float rs_funcbob; + float rs_startelevator; + float rs_falldamage5; + float rs_falldamage10; + float rs_maxfallheight; + float rs_maxjumpfallheight; +} aas_settings_t; + +#define CACHETYPE_PORTAL 0 +#define CACHETYPE_AREA 1 + +//routing cache +typedef struct aas_routingcache_s +{ + byte type; //portal or area cache + float time; //last time accessed or updated + int size; //size of the routing cache + int cluster; //cluster the cache is for + int areanum; //area the cache is created for + vec3_t origin; //origin within the area + float starttraveltime; //travel time to start with + int travelflags; //combinations of the travel flags + struct aas_routingcache_s *prev, *next; + struct aas_routingcache_s *time_prev, *time_next; + unsigned char *reachabilities; //reachabilities used for routing + unsigned short int traveltimes[1]; //travel time for every area (variable sized) +} aas_routingcache_t; + +//fields for the routing algorithm +typedef struct aas_routingupdate_s +{ + int cluster; + int areanum; //area number of the update + vec3_t start; //start point the area was entered + unsigned short int tmptraveltime; //temporary travel time + unsigned short int *areatraveltimes; //travel times within the area + qboolean inlist; //true if the update is in the list + struct aas_routingupdate_s *next; + struct aas_routingupdate_s *prev; +} aas_routingupdate_t; + +//reversed reachability link +typedef struct aas_reversedlink_s +{ + int linknum; //the aas_areareachability_t + int areanum; //reachable from this area + struct aas_reversedlink_s *next; //next link +} aas_reversedlink_t; + +//reversed area reachability +typedef struct aas_reversedreachability_s +{ + int numlinks; + aas_reversedlink_t *first; +} aas_reversedreachability_t; + +//areas a reachability goes through +typedef struct aas_reachabilityareas_s +{ + int firstarea, numareas; +} aas_reachabilityareas_t; + +typedef struct aas_s +{ + int loaded; //true when an AAS file is loaded + int initialized; //true when AAS has been initialized + int savefile; //set true when file should be saved + int bspchecksum; + //current time + float time; + int numframes; + //name of the aas file + char filename[MAX_PATH]; + char mapname[MAX_PATH]; + //bounding boxes + int numbboxes; + aas_bbox_t *bboxes; + //vertexes + int numvertexes; + aas_vertex_t *vertexes; + //planes + int numplanes; + aas_plane_t *planes; + //edges + int numedges; + aas_edge_t *edges; + //edge index + int edgeindexsize; + aas_edgeindex_t *edgeindex; + //faces + int numfaces; + aas_face_t *faces; + //face index + int faceindexsize; + aas_faceindex_t *faceindex; + //convex areas + int numareas; + aas_area_t *areas; + //convex area settings + int numareasettings; + aas_areasettings_t *areasettings; + //reachablity list + int reachabilitysize; + aas_reachability_t *reachability; + //nodes of the bsp tree + int numnodes; + aas_node_t *nodes; + //cluster portals + int numportals; + aas_portal_t *portals; + //cluster portal index + int portalindexsize; + aas_portalindex_t *portalindex; + //clusters + int numclusters; + aas_cluster_t *clusters; + // + int numreachabilityareas; + float reachabilitytime; + //enities linked in the areas + aas_link_t *linkheap; //heap with link structures + int linkheapsize; //size of the link heap + aas_link_t *freelinks; //first free link + aas_link_t **arealinkedentities; //entities linked into areas + //entities + int maxentities; + int maxclients; + aas_entity_t *entities; + //string indexes + char *configstrings[MAX_CONFIGSTRINGS]; + int indexessetup; + //index to retrieve travel flag for a travel type + int travelflagfortype[MAX_TRAVELTYPES]; + //travel flags for each area based on contents + int *areacontentstravelflags; + //routing update + aas_routingupdate_t *areaupdate; + aas_routingupdate_t *portalupdate; + //number of routing updates during a frame (reset every frame) + int frameroutingupdates; + //reversed reachability links + aas_reversedreachability_t *reversedreachability; + //travel times within the areas + unsigned short ***areatraveltimes; + //array of size numclusters with cluster cache + aas_routingcache_t ***clusterareacache; + aas_routingcache_t **portalcache; + //cache list sorted on time + aas_routingcache_t *oldestcache; // start of cache list sorted on time + aas_routingcache_t *newestcache; // end of cache list sorted on time + //maximum travel time through portal areas + int *portalmaxtraveltimes; + //areas the reachabilities go through + int *reachabilityareaindex; + aas_reachabilityareas_t *reachabilityareas; +} aas_t; + +#define AASINTERN + +#ifndef BSPCINCLUDE + +#include "be_aas_main.h" +#include "be_aas_entity.h" +#include "be_aas_sample.h" +#include "be_aas_cluster.h" +#include "be_aas_reach.h" +#include "be_aas_route.h" +#include "be_aas_routealt.h" +#include "be_aas_debug.h" +#include "be_aas_file.h" +#include "be_aas_optimize.h" +#include "be_aas_bsp.h" +#include "be_aas_move.h" + +#endif //BSPCINCLUDE diff --git a/code/botlib/be_aas_entity.c b/code/botlib/be_aas_entity.c index e3ed85e..e11356b 100755 --- a/code/botlib/be_aas_entity.c +++ b/code/botlib/be_aas_entity.c @@ -1,437 +1,437 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_entity.c
- *
- * desc: AAS entities
- *
- * $Archive: /MissionPack/code/botlib/be_aas_entity.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_utils.h"
-#include "l_log.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_aas_def.h"
-
-#define MASK_SOLID CONTENTS_PLAYERCLIP
-
-//FIXME: these might change
-enum {
- ET_GENERAL,
- ET_PLAYER,
- ET_ITEM,
- ET_MISSILE,
- ET_MOVER
-};
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_UpdateEntity(int entnum, bot_entitystate_t *state)
-{
- int relink;
- aas_entity_t *ent;
- vec3_t absmins, absmaxs;
-
- if (!aasworld.loaded)
- {
- botimport.Print(PRT_MESSAGE, "AAS_UpdateEntity: not loaded\n");
- return BLERR_NOAASFILE;
- } //end if
-
- ent = &aasworld.entities[entnum];
-
- if (!state) {
- //unlink the entity
- AAS_UnlinkFromAreas(ent->areas);
- //unlink the entity from the BSP leaves
- AAS_UnlinkFromBSPLeaves(ent->leaves);
- //
- ent->areas = NULL;
- //
- ent->leaves = NULL;
- return BLERR_NOERROR;
- }
-
- ent->i.update_time = AAS_Time() - ent->i.ltime;
- ent->i.type = state->type;
- ent->i.flags = state->flags;
- ent->i.ltime = AAS_Time();
- VectorCopy(ent->i.origin, ent->i.lastvisorigin);
- VectorCopy(state->old_origin, ent->i.old_origin);
- ent->i.solid = state->solid;
- ent->i.groundent = state->groundent;
- ent->i.modelindex = state->modelindex;
- ent->i.modelindex2 = state->modelindex2;
- ent->i.frame = state->frame;
- ent->i.event = state->event;
- ent->i.eventParm = state->eventParm;
- ent->i.powerups = state->powerups;
- ent->i.weapon = state->weapon;
- ent->i.legsAnim = state->legsAnim;
- ent->i.torsoAnim = state->torsoAnim;
- //number of the entity
- ent->i.number = entnum;
- //updated so set valid flag
- ent->i.valid = qtrue;
- //link everything the first frame
- if (aasworld.numframes == 1) relink = qtrue;
- else relink = qfalse;
- //
- if (ent->i.solid == SOLID_BSP)
- {
- //if the angles of the model changed
- if (!VectorCompare(state->angles, ent->i.angles))
- {
- VectorCopy(state->angles, ent->i.angles);
- relink = qtrue;
- } //end if
- //get the mins and maxs of the model
- //FIXME: rotate mins and maxs
- AAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL);
- } //end if
- else if (ent->i.solid == SOLID_BBOX)
- {
- //if the bounding box size changed
- if (!VectorCompare(state->mins, ent->i.mins) ||
- !VectorCompare(state->maxs, ent->i.maxs))
- {
- VectorCopy(state->mins, ent->i.mins);
- VectorCopy(state->maxs, ent->i.maxs);
- relink = qtrue;
- } //end if
- VectorCopy(state->angles, ent->i.angles);
- } //end if
- //if the origin changed
- if (!VectorCompare(state->origin, ent->i.origin))
- {
- VectorCopy(state->origin, ent->i.origin);
- relink = qtrue;
- } //end if
- //if the entity should be relinked
- if (relink)
- {
- //don't link the world model
- if (entnum != ENTITYNUM_WORLD)
- {
- //absolute mins and maxs
- VectorAdd(ent->i.mins, ent->i.origin, absmins);
- VectorAdd(ent->i.maxs, ent->i.origin, absmaxs);
- //unlink the entity
- AAS_UnlinkFromAreas(ent->areas);
- //relink the entity to the AAS areas (use the larges bbox)
- ent->areas = AAS_LinkEntityClientBBox(absmins, absmaxs, entnum, PRESENCE_NORMAL);
- //unlink the entity from the BSP leaves
- AAS_UnlinkFromBSPLeaves(ent->leaves);
- //link the entity to the world BSP tree
- ent->leaves = AAS_BSPLinkEntity(absmins, absmaxs, entnum, 0);
- } //end if
- } //end if
- return BLERR_NOERROR;
-} //end of the function AAS_UpdateEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_EntityInfo(int entnum, aas_entityinfo_t *info)
-{
- if (!aasworld.initialized)
- {
- botimport.Print(PRT_FATAL, "AAS_EntityInfo: aasworld not initialized\n");
- Com_Memset(info, 0, sizeof(aas_entityinfo_t));
- return;
- } //end if
-
- if (entnum < 0 || entnum >= aasworld.maxentities)
- {
- botimport.Print(PRT_FATAL, "AAS_EntityInfo: entnum %d out of range\n", entnum);
- Com_Memset(info, 0, sizeof(aas_entityinfo_t));
- return;
- } //end if
-
- Com_Memcpy(info, &aasworld.entities[entnum].i, sizeof(aas_entityinfo_t));
-} //end of the function AAS_EntityInfo
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_EntityOrigin(int entnum, vec3_t origin)
-{
- if (entnum < 0 || entnum >= aasworld.maxentities)
- {
- botimport.Print(PRT_FATAL, "AAS_EntityOrigin: entnum %d out of range\n", entnum);
- VectorClear(origin);
- return;
- } //end if
-
- VectorCopy(aasworld.entities[entnum].i.origin, origin);
-} //end of the function AAS_EntityOrigin
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_EntityModelindex(int entnum)
-{
- if (entnum < 0 || entnum >= aasworld.maxentities)
- {
- botimport.Print(PRT_FATAL, "AAS_EntityModelindex: entnum %d out of range\n", entnum);
- return 0;
- } //end if
- return aasworld.entities[entnum].i.modelindex;
-} //end of the function AAS_EntityModelindex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_EntityType(int entnum)
-{
- if (!aasworld.initialized) return 0;
-
- if (entnum < 0 || entnum >= aasworld.maxentities)
- {
- botimport.Print(PRT_FATAL, "AAS_EntityType: entnum %d out of range\n", entnum);
- return 0;
- } //end if
- return aasworld.entities[entnum].i.type;
-} //end of the AAS_EntityType
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_EntityModelNum(int entnum)
-{
- if (!aasworld.initialized) return 0;
-
- if (entnum < 0 || entnum >= aasworld.maxentities)
- {
- botimport.Print(PRT_FATAL, "AAS_EntityModelNum: entnum %d out of range\n", entnum);
- return 0;
- } //end if
- return aasworld.entities[entnum].i.modelindex;
-} //end of the function AAS_EntityModelNum
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin)
-{
- int i;
- aas_entity_t *ent;
-
- for (i = 0; i < aasworld.maxentities; i++)
- {
- ent = &aasworld.entities[i];
- if (ent->i.type == ET_MOVER)
- {
- if (ent->i.modelindex == modelnum)
- {
- VectorCopy(ent->i.origin, origin);
- return qtrue;
- } //end if
- } //end if
- } //end for
- return qfalse;
-} //end of the function AAS_OriginOfMoverWithModelNum
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs)
-{
- aas_entity_t *ent;
-
- if (!aasworld.initialized) return;
-
- if (entnum < 0 || entnum >= aasworld.maxentities)
- {
- botimport.Print(PRT_FATAL, "AAS_EntitySize: entnum %d out of range\n", entnum);
- return;
- } //end if
-
- ent = &aasworld.entities[entnum];
- VectorCopy(ent->i.mins, mins);
- VectorCopy(ent->i.maxs, maxs);
-} //end of the function AAS_EntitySize
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata)
-{
- aas_entity_t *ent;
-
- ent = &aasworld.entities[entnum];
- VectorCopy(ent->i.origin, entdata->origin);
- VectorCopy(ent->i.angles, entdata->angles);
- VectorAdd(ent->i.origin, ent->i.mins, entdata->absmins);
- VectorAdd(ent->i.origin, ent->i.maxs, entdata->absmaxs);
- entdata->solid = ent->i.solid;
- entdata->modelnum = ent->i.modelindex - 1;
-} //end of the function AAS_EntityBSPData
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ResetEntityLinks(void)
-{
- int i;
- for (i = 0; i < aasworld.maxentities; i++)
- {
- aasworld.entities[i].areas = NULL;
- aasworld.entities[i].leaves = NULL;
- } //end for
-} //end of the function AAS_ResetEntityLinks
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InvalidateEntities(void)
-{
- int i;
- for (i = 0; i < aasworld.maxentities; i++)
- {
- aasworld.entities[i].i.valid = qfalse;
- aasworld.entities[i].i.number = i;
- } //end for
-} //end of the function AAS_InvalidateEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UnlinkInvalidEntities(void)
-{
- int i;
- aas_entity_t *ent;
-
- for (i = 0; i < aasworld.maxentities; i++)
- {
- ent = &aasworld.entities[i];
- if (!ent->i.valid)
- {
- AAS_UnlinkFromAreas( ent->areas );
- ent->areas = NULL;
- AAS_UnlinkFromBSPLeaves( ent->leaves );
- ent->leaves = NULL;
- } //end for
- } //end for
-} //end of the function AAS_UnlinkInvalidEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NearestEntity(vec3_t origin, int modelindex)
-{
- int i, bestentnum;
- float dist, bestdist;
- aas_entity_t *ent;
- vec3_t dir;
-
- bestentnum = 0;
- bestdist = 99999;
- for (i = 0; i < aasworld.maxentities; i++)
- {
- ent = &aasworld.entities[i];
- if (ent->i.modelindex != modelindex) continue;
- VectorSubtract(ent->i.origin, origin, dir);
- if (abs(dir[0]) < 40)
- {
- if (abs(dir[1]) < 40)
- {
- dist = VectorLength(dir);
- if (dist < bestdist)
- {
- bestdist = dist;
- bestentnum = i;
- } //end if
- } //end if
- } //end if
- } //end for
- return bestentnum;
-} //end of the function AAS_NearestEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BestReachableEntityArea(int entnum)
-{
- aas_entity_t *ent;
-
- ent = &aasworld.entities[entnum];
- return AAS_BestReachableLinkArea(ent->areas);
-} //end of the function AAS_BestReachableEntityArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NextEntity(int entnum)
-{
- if (!aasworld.loaded) return 0;
-
- if (entnum < 0) entnum = -1;
- while(++entnum < aasworld.maxentities)
- {
- if (aasworld.entities[entnum].i.valid) return entnum;
- } //end while
- return 0;
-} //end of the function AAS_NextEntity
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_entity.c + * + * desc: AAS entities + * + * $Archive: /MissionPack/code/botlib/be_aas_entity.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_utils.h" +#include "l_log.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +#define MASK_SOLID CONTENTS_PLAYERCLIP + +//FIXME: these might change +enum { + ET_GENERAL, + ET_PLAYER, + ET_ITEM, + ET_MISSILE, + ET_MOVER +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_UpdateEntity(int entnum, bot_entitystate_t *state) +{ + int relink; + aas_entity_t *ent; + vec3_t absmins, absmaxs; + + if (!aasworld.loaded) + { + botimport.Print(PRT_MESSAGE, "AAS_UpdateEntity: not loaded\n"); + return BLERR_NOAASFILE; + } //end if + + ent = &aasworld.entities[entnum]; + + if (!state) { + //unlink the entity + AAS_UnlinkFromAreas(ent->areas); + //unlink the entity from the BSP leaves + AAS_UnlinkFromBSPLeaves(ent->leaves); + // + ent->areas = NULL; + // + ent->leaves = NULL; + return BLERR_NOERROR; + } + + ent->i.update_time = AAS_Time() - ent->i.ltime; + ent->i.type = state->type; + ent->i.flags = state->flags; + ent->i.ltime = AAS_Time(); + VectorCopy(ent->i.origin, ent->i.lastvisorigin); + VectorCopy(state->old_origin, ent->i.old_origin); + ent->i.solid = state->solid; + ent->i.groundent = state->groundent; + ent->i.modelindex = state->modelindex; + ent->i.modelindex2 = state->modelindex2; + ent->i.frame = state->frame; + ent->i.event = state->event; + ent->i.eventParm = state->eventParm; + ent->i.powerups = state->powerups; + ent->i.weapon = state->weapon; + ent->i.legsAnim = state->legsAnim; + ent->i.torsoAnim = state->torsoAnim; + //number of the entity + ent->i.number = entnum; + //updated so set valid flag + ent->i.valid = qtrue; + //link everything the first frame + if (aasworld.numframes == 1) relink = qtrue; + else relink = qfalse; + // + if (ent->i.solid == SOLID_BSP) + { + //if the angles of the model changed + if (!VectorCompare(state->angles, ent->i.angles)) + { + VectorCopy(state->angles, ent->i.angles); + relink = qtrue; + } //end if + //get the mins and maxs of the model + //FIXME: rotate mins and maxs + AAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL); + } //end if + else if (ent->i.solid == SOLID_BBOX) + { + //if the bounding box size changed + if (!VectorCompare(state->mins, ent->i.mins) || + !VectorCompare(state->maxs, ent->i.maxs)) + { + VectorCopy(state->mins, ent->i.mins); + VectorCopy(state->maxs, ent->i.maxs); + relink = qtrue; + } //end if + VectorCopy(state->angles, ent->i.angles); + } //end if + //if the origin changed + if (!VectorCompare(state->origin, ent->i.origin)) + { + VectorCopy(state->origin, ent->i.origin); + relink = qtrue; + } //end if + //if the entity should be relinked + if (relink) + { + //don't link the world model + if (entnum != ENTITYNUM_WORLD) + { + //absolute mins and maxs + VectorAdd(ent->i.mins, ent->i.origin, absmins); + VectorAdd(ent->i.maxs, ent->i.origin, absmaxs); + //unlink the entity + AAS_UnlinkFromAreas(ent->areas); + //relink the entity to the AAS areas (use the larges bbox) + ent->areas = AAS_LinkEntityClientBBox(absmins, absmaxs, entnum, PRESENCE_NORMAL); + //unlink the entity from the BSP leaves + AAS_UnlinkFromBSPLeaves(ent->leaves); + //link the entity to the world BSP tree + ent->leaves = AAS_BSPLinkEntity(absmins, absmaxs, entnum, 0); + } //end if + } //end if + return BLERR_NOERROR; +} //end of the function AAS_UpdateEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntityInfo(int entnum, aas_entityinfo_t *info) +{ + if (!aasworld.initialized) + { + botimport.Print(PRT_FATAL, "AAS_EntityInfo: aasworld not initialized\n"); + Com_Memset(info, 0, sizeof(aas_entityinfo_t)); + return; + } //end if + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityInfo: entnum %d out of range\n", entnum); + Com_Memset(info, 0, sizeof(aas_entityinfo_t)); + return; + } //end if + + Com_Memcpy(info, &aasworld.entities[entnum].i, sizeof(aas_entityinfo_t)); +} //end of the function AAS_EntityInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntityOrigin(int entnum, vec3_t origin) +{ + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityOrigin: entnum %d out of range\n", entnum); + VectorClear(origin); + return; + } //end if + + VectorCopy(aasworld.entities[entnum].i.origin, origin); +} //end of the function AAS_EntityOrigin +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityModelindex(int entnum) +{ + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityModelindex: entnum %d out of range\n", entnum); + return 0; + } //end if + return aasworld.entities[entnum].i.modelindex; +} //end of the function AAS_EntityModelindex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityType(int entnum) +{ + if (!aasworld.initialized) return 0; + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityType: entnum %d out of range\n", entnum); + return 0; + } //end if + return aasworld.entities[entnum].i.type; +} //end of the AAS_EntityType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EntityModelNum(int entnum) +{ + if (!aasworld.initialized) return 0; + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntityModelNum: entnum %d out of range\n", entnum); + return 0; + } //end if + return aasworld.entities[entnum].i.modelindex; +} //end of the function AAS_EntityModelNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin) +{ + int i; + aas_entity_t *ent; + + for (i = 0; i < aasworld.maxentities; i++) + { + ent = &aasworld.entities[i]; + if (ent->i.type == ET_MOVER) + { + if (ent->i.modelindex == modelnum) + { + VectorCopy(ent->i.origin, origin); + return qtrue; + } //end if + } //end if + } //end for + return qfalse; +} //end of the function AAS_OriginOfMoverWithModelNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs) +{ + aas_entity_t *ent; + + if (!aasworld.initialized) return; + + if (entnum < 0 || entnum >= aasworld.maxentities) + { + botimport.Print(PRT_FATAL, "AAS_EntitySize: entnum %d out of range\n", entnum); + return; + } //end if + + ent = &aasworld.entities[entnum]; + VectorCopy(ent->i.mins, mins); + VectorCopy(ent->i.maxs, maxs); +} //end of the function AAS_EntitySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata) +{ + aas_entity_t *ent; + + ent = &aasworld.entities[entnum]; + VectorCopy(ent->i.origin, entdata->origin); + VectorCopy(ent->i.angles, entdata->angles); + VectorAdd(ent->i.origin, ent->i.mins, entdata->absmins); + VectorAdd(ent->i.origin, ent->i.maxs, entdata->absmaxs); + entdata->solid = ent->i.solid; + entdata->modelnum = ent->i.modelindex - 1; +} //end of the function AAS_EntityBSPData +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ResetEntityLinks(void) +{ + int i; + for (i = 0; i < aasworld.maxentities; i++) + { + aasworld.entities[i].areas = NULL; + aasworld.entities[i].leaves = NULL; + } //end for +} //end of the function AAS_ResetEntityLinks +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InvalidateEntities(void) +{ + int i; + for (i = 0; i < aasworld.maxentities; i++) + { + aasworld.entities[i].i.valid = qfalse; + aasworld.entities[i].i.number = i; + } //end for +} //end of the function AAS_InvalidateEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkInvalidEntities(void) +{ + int i; + aas_entity_t *ent; + + for (i = 0; i < aasworld.maxentities; i++) + { + ent = &aasworld.entities[i]; + if (!ent->i.valid) + { + AAS_UnlinkFromAreas( ent->areas ); + ent->areas = NULL; + AAS_UnlinkFromBSPLeaves( ent->leaves ); + ent->leaves = NULL; + } //end for + } //end for +} //end of the function AAS_UnlinkInvalidEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NearestEntity(vec3_t origin, int modelindex) +{ + int i, bestentnum; + float dist, bestdist; + aas_entity_t *ent; + vec3_t dir; + + bestentnum = 0; + bestdist = 99999; + for (i = 0; i < aasworld.maxentities; i++) + { + ent = &aasworld.entities[i]; + if (ent->i.modelindex != modelindex) continue; + VectorSubtract(ent->i.origin, origin, dir); + if (abs(dir[0]) < 40) + { + if (abs(dir[1]) < 40) + { + dist = VectorLength(dir); + if (dist < bestdist) + { + bestdist = dist; + bestentnum = i; + } //end if + } //end if + } //end if + } //end for + return bestentnum; +} //end of the function AAS_NearestEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableEntityArea(int entnum) +{ + aas_entity_t *ent; + + ent = &aasworld.entities[entnum]; + return AAS_BestReachableLinkArea(ent->areas); +} //end of the function AAS_BestReachableEntityArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextEntity(int entnum) +{ + if (!aasworld.loaded) return 0; + + if (entnum < 0) entnum = -1; + while(++entnum < aasworld.maxentities) + { + if (aasworld.entities[entnum].i.valid) return entnum; + } //end while + return 0; +} //end of the function AAS_NextEntity diff --git a/code/botlib/be_aas_entity.h b/code/botlib/be_aas_entity.h index f5286dd..fc3675b 100755 --- a/code/botlib/be_aas_entity.h +++ b/code/botlib/be_aas_entity.h @@ -1,63 +1,63 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_entity.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_entity.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-//invalidates all entity infos
-void AAS_InvalidateEntities(void);
-//unlink not updated entities
-void AAS_UnlinkInvalidEntities(void);
-//resets the entity AAS and BSP links (sets areas and leaves pointers to NULL)
-void AAS_ResetEntityLinks(void);
-//updates an entity
-int AAS_UpdateEntity(int ent, bot_entitystate_t *state);
-//gives the entity data used for collision detection
-void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata);
-#endif //AASINTERN
-
-//returns the size of the entity bounding box in mins and maxs
-void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs);
-//returns the BSP model number of the entity
-int AAS_EntityModelNum(int entnum);
-//returns the origin of an entity with the given model number
-int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin);
-//returns the best reachable area the entity is situated in
-int AAS_BestReachableEntityArea(int entnum);
-//returns the info of the given entity
-void AAS_EntityInfo(int entnum, aas_entityinfo_t *info);
-//returns the next entity
-int AAS_NextEntity(int entnum);
-//returns the origin of the entity
-void AAS_EntityOrigin(int entnum, vec3_t origin);
-//returns the entity type
-int AAS_EntityType(int entnum);
-//returns the model index of the entity
-int AAS_EntityModelindex(int entnum);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_entity.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_entity.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//invalidates all entity infos +void AAS_InvalidateEntities(void); +//unlink not updated entities +void AAS_UnlinkInvalidEntities(void); +//resets the entity AAS and BSP links (sets areas and leaves pointers to NULL) +void AAS_ResetEntityLinks(void); +//updates an entity +int AAS_UpdateEntity(int ent, bot_entitystate_t *state); +//gives the entity data used for collision detection +void AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata); +#endif //AASINTERN + +//returns the size of the entity bounding box in mins and maxs +void AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs); +//returns the BSP model number of the entity +int AAS_EntityModelNum(int entnum); +//returns the origin of an entity with the given model number +int AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin); +//returns the best reachable area the entity is situated in +int AAS_BestReachableEntityArea(int entnum); +//returns the info of the given entity +void AAS_EntityInfo(int entnum, aas_entityinfo_t *info); +//returns the next entity +int AAS_NextEntity(int entnum); +//returns the origin of the entity +void AAS_EntityOrigin(int entnum, vec3_t origin); +//returns the entity type +int AAS_EntityType(int entnum); +//returns the model index of the entity +int AAS_EntityModelindex(int entnum); + diff --git a/code/botlib/be_aas_file.c b/code/botlib/be_aas_file.c index 35147fc..50d644e 100755 --- a/code/botlib/be_aas_file.c +++ b/code/botlib/be_aas_file.c @@ -1,582 +1,582 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_file.c
- *
- * desc: AAS file loading/writing
- *
- * $Archive: /MissionPack/code/botlib/be_aas_file.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_libvar.h"
-#include "l_utils.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_aas_def.h"
-
-//#define AASFILEDEBUG
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_SwapAASData(void)
-{
- int i, j;
- //bounding boxes
- for (i = 0; i < aasworld.numbboxes; i++)
- {
- aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype);
- aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags);
- for (j = 0; j < 3; j++)
- {
- aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]);
- aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]);
- } //end for
- } //end for
- //vertexes
- for (i = 0; i < aasworld.numvertexes; i++)
- {
- for (j = 0; j < 3; j++)
- aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]);
- } //end for
- //planes
- for (i = 0; i < aasworld.numplanes; i++)
- {
- for (j = 0; j < 3; j++)
- aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]);
- aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist);
- aasworld.planes[i].type = LittleLong(aasworld.planes[i].type);
- } //end for
- //edges
- for (i = 0; i < aasworld.numedges; i++)
- {
- aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]);
- aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]);
- } //end for
- //edgeindex
- for (i = 0; i < aasworld.edgeindexsize; i++)
- {
- aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]);
- } //end for
- //faces
- for (i = 0; i < aasworld.numfaces; i++)
- {
- aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum);
- aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags);
- aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges);
- aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge);
- aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea);
- aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea);
- } //end for
- //face index
- for (i = 0; i < aasworld.faceindexsize; i++)
- {
- aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]);
- } //end for
- //convex areas
- for (i = 0; i < aasworld.numareas; i++)
- {
- aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum);
- aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces);
- aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface);
- for (j = 0; j < 3; j++)
- {
- aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]);
- aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]);
- aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]);
- } //end for
- } //end for
- //area settings
- for (i = 0; i < aasworld.numareasettings; i++)
- {
- aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents);
- aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags);
- aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype);
- aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster);
- aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum);
- aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas);
- aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea);
- } //end for
- //area reachability
- for (i = 0; i < aasworld.reachabilitysize; i++)
- {
- aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum);
- aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum);
- aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum);
- for (j = 0; j < 3; j++)
- {
- aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]);
- aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]);
- } //end for
- aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype);
- aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime);
- } //end for
- //nodes
- for (i = 0; i < aasworld.numnodes; i++)
- {
- aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum);
- aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]);
- aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]);
- } //end for
- //cluster portals
- for (i = 0; i < aasworld.numportals; i++)
- {
- aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum);
- aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster);
- aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster);
- aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]);
- aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]);
- } //end for
- //cluster portal index
- for (i = 0; i < aasworld.portalindexsize; i++)
- {
- aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]);
- } //end for
- //cluster
- for (i = 0; i < aasworld.numclusters; i++)
- {
- aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas);
- aasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas);
- aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals);
- aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal);
- } //end for
-} //end of the function AAS_SwapAASData
-//===========================================================================
-// dump the current loaded aas file
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DumpAASData(void)
-{
- aasworld.numbboxes = 0;
- if (aasworld.bboxes) FreeMemory(aasworld.bboxes);
- aasworld.bboxes = NULL;
- aasworld.numvertexes = 0;
- if (aasworld.vertexes) FreeMemory(aasworld.vertexes);
- aasworld.vertexes = NULL;
- aasworld.numplanes = 0;
- if (aasworld.planes) FreeMemory(aasworld.planes);
- aasworld.planes = NULL;
- aasworld.numedges = 0;
- if (aasworld.edges) FreeMemory(aasworld.edges);
- aasworld.edges = NULL;
- aasworld.edgeindexsize = 0;
- if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);
- aasworld.edgeindex = NULL;
- aasworld.numfaces = 0;
- if (aasworld.faces) FreeMemory(aasworld.faces);
- aasworld.faces = NULL;
- aasworld.faceindexsize = 0;
- if (aasworld.faceindex) FreeMemory(aasworld.faceindex);
- aasworld.faceindex = NULL;
- aasworld.numareas = 0;
- if (aasworld.areas) FreeMemory(aasworld.areas);
- aasworld.areas = NULL;
- aasworld.numareasettings = 0;
- if (aasworld.areasettings) FreeMemory(aasworld.areasettings);
- aasworld.areasettings = NULL;
- aasworld.reachabilitysize = 0;
- if (aasworld.reachability) FreeMemory(aasworld.reachability);
- aasworld.reachability = NULL;
- aasworld.numnodes = 0;
- if (aasworld.nodes) FreeMemory(aasworld.nodes);
- aasworld.nodes = NULL;
- aasworld.numportals = 0;
- if (aasworld.portals) FreeMemory(aasworld.portals);
- aasworld.portals = NULL;
- aasworld.numportals = 0;
- if (aasworld.portalindex) FreeMemory(aasworld.portalindex);
- aasworld.portalindex = NULL;
- aasworld.portalindexsize = 0;
- if (aasworld.clusters) FreeMemory(aasworld.clusters);
- aasworld.clusters = NULL;
- aasworld.numclusters = 0;
- //
- aasworld.loaded = qfalse;
- aasworld.initialized = qfalse;
- aasworld.savefile = qfalse;
-} //end of the function AAS_DumpAASData
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef AASFILEDEBUG
-void AAS_FileInfo(void)
-{
- int i, n, optimized;
-
- botimport.Print(PRT_MESSAGE, "version = %d\n", AASVERSION);
- botimport.Print(PRT_MESSAGE, "numvertexes = %d\n", aasworld.numvertexes);
- botimport.Print(PRT_MESSAGE, "numplanes = %d\n", aasworld.numplanes);
- botimport.Print(PRT_MESSAGE, "numedges = %d\n", aasworld.numedges);
- botimport.Print(PRT_MESSAGE, "edgeindexsize = %d\n", aasworld.edgeindexsize);
- botimport.Print(PRT_MESSAGE, "numfaces = %d\n", aasworld.numfaces);
- botimport.Print(PRT_MESSAGE, "faceindexsize = %d\n", aasworld.faceindexsize);
- botimport.Print(PRT_MESSAGE, "numareas = %d\n", aasworld.numareas);
- botimport.Print(PRT_MESSAGE, "numareasettings = %d\n", aasworld.numareasettings);
- botimport.Print(PRT_MESSAGE, "reachabilitysize = %d\n", aasworld.reachabilitysize);
- botimport.Print(PRT_MESSAGE, "numnodes = %d\n", aasworld.numnodes);
- botimport.Print(PRT_MESSAGE, "numportals = %d\n", aasworld.numportals);
- botimport.Print(PRT_MESSAGE, "portalindexsize = %d\n", aasworld.portalindexsize);
- botimport.Print(PRT_MESSAGE, "numclusters = %d\n", aasworld.numclusters);
- //
- for (n = 0, i = 0; i < aasworld.numareasettings; i++)
- {
- if (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++;
- } //end for
- botimport.Print(PRT_MESSAGE, "num grounded areas = %d\n", n);
- //
- botimport.Print(PRT_MESSAGE, "planes size %d bytes\n", aasworld.numplanes * sizeof(aas_plane_t));
- botimport.Print(PRT_MESSAGE, "areas size %d bytes\n", aasworld.numareas * sizeof(aas_area_t));
- botimport.Print(PRT_MESSAGE, "areasettings size %d bytes\n", aasworld.numareasettings * sizeof(aas_areasettings_t));
- botimport.Print(PRT_MESSAGE, "nodes size %d bytes\n", aasworld.numnodes * sizeof(aas_node_t));
- botimport.Print(PRT_MESSAGE, "reachability size %d bytes\n", aasworld.reachabilitysize * sizeof(aas_reachability_t));
- botimport.Print(PRT_MESSAGE, "portals size %d bytes\n", aasworld.numportals * sizeof(aas_portal_t));
- botimport.Print(PRT_MESSAGE, "clusters size %d bytes\n", aasworld.numclusters * sizeof(aas_cluster_t));
-
- optimized = aasworld.numplanes * sizeof(aas_plane_t) +
- aasworld.numareas * sizeof(aas_area_t) +
- aasworld.numareasettings * sizeof(aas_areasettings_t) +
- aasworld.numnodes * sizeof(aas_node_t) +
- aasworld.reachabilitysize * sizeof(aas_reachability_t) +
- aasworld.numportals * sizeof(aas_portal_t) +
- aasworld.numclusters * sizeof(aas_cluster_t);
- botimport.Print(PRT_MESSAGE, "optimzed size %d KB\n", optimized >> 10);
-} //end of the function AAS_FileInfo
-#endif //AASFILEDEBUG
-//===========================================================================
-// allocate memory and read a lump of a AAS file
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size)
-{
- char *buf;
- //
- if (!length)
- {
- //just alloc a dummy
- return (char *) GetClearedHunkMemory(size+1);
- } //end if
- //seek to the data
- if (offset != *lastoffset)
- {
- botimport.Print(PRT_WARNING, "AAS file not sequentially read\n");
- if (botimport.FS_Seek(fp, offset, FS_SEEK_SET))
- {
- AAS_Error("can't seek to aas lump\n");
- AAS_DumpAASData();
- botimport.FS_FCloseFile(fp);
- return 0;
- } //end if
- } //end if
- //allocate memory
- buf = (char *) GetClearedHunkMemory(length+1);
- //read the data
- if (length)
- {
- botimport.FS_Read(buf, length, fp );
- *lastoffset += length;
- } //end if
- return buf;
-} //end of the function AAS_LoadAASLump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DData(unsigned char *data, int size)
-{
- int i;
-
- for (i = 0; i < size; i++)
- {
- data[i] ^= (unsigned char) i * 119;
- } //end for
-} //end of the function AAS_DData
-//===========================================================================
-// load an aas file
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_LoadAASFile(char *filename)
-{
- fileHandle_t fp;
- aas_header_t header;
- int offset, length, lastoffset;
-
- botimport.Print(PRT_MESSAGE, "trying to load %s\n", filename);
- //dump current loaded aas file
- AAS_DumpAASData();
- //open the file
- botimport.FS_FOpenFile( filename, &fp, FS_READ );
- if (!fp)
- {
- AAS_Error("can't open %s\n", filename);
- return BLERR_CANNOTOPENAASFILE;
- } //end if
- //read the header
- botimport.FS_Read(&header, sizeof(aas_header_t), fp );
- lastoffset = sizeof(aas_header_t);
- //check header identification
- header.ident = LittleLong(header.ident);
- if (header.ident != AASID)
- {
- AAS_Error("%s is not an AAS file\n", filename);
- botimport.FS_FCloseFile(fp);
- return BLERR_WRONGAASFILEID;
- } //end if
- //check the version
- header.version = LittleLong(header.version);
- //
- if (header.version != AASVERSION_OLD && header.version != AASVERSION)
- {
- AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION);
- botimport.FS_FCloseFile(fp);
- return BLERR_WRONGAASFILEVERSION;
- } //end if
- //
- if (header.version == AASVERSION)
- {
- AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);
- } //end if
- //
- aasworld.bspchecksum = atoi(LibVarGetString( "sv_mapChecksum"));
- if (LittleLong(header.bspchecksum) != aasworld.bspchecksum)
- {
- AAS_Error("aas file %s is out of date\n", filename);
- botimport.FS_FCloseFile(fp);
- return BLERR_WRONGAASFILEVERSION;
- } //end if
- //load the lumps:
- //bounding boxes
- offset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs);
- length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen);
- aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t));
- aasworld.numbboxes = length / sizeof(aas_bbox_t);
- if (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP;
- //vertexes
- offset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs);
- length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen);
- aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t));
- aasworld.numvertexes = length / sizeof(aas_vertex_t);
- if (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP;
- //planes
- offset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs);
- length = LittleLong(header.lumps[AASLUMP_PLANES].filelen);
- aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t));
- aasworld.numplanes = length / sizeof(aas_plane_t);
- if (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP;
- //edges
- offset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs);
- length = LittleLong(header.lumps[AASLUMP_EDGES].filelen);
- aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t));
- aasworld.numedges = length / sizeof(aas_edge_t);
- if (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP;
- //edgeindex
- offset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs);
- length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen);
- aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t));
- aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t);
- if (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP;
- //faces
- offset = LittleLong(header.lumps[AASLUMP_FACES].fileofs);
- length = LittleLong(header.lumps[AASLUMP_FACES].filelen);
- aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t));
- aasworld.numfaces = length / sizeof(aas_face_t);
- if (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP;
- //faceindex
- offset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs);
- length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen);
- aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t));
- aasworld.faceindexsize = length / sizeof(aas_faceindex_t);
- if (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP;
- //convex areas
- offset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs);
- length = LittleLong(header.lumps[AASLUMP_AREAS].filelen);
- aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t));
- aasworld.numareas = length / sizeof(aas_area_t);
- if (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP;
- //area settings
- offset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs);
- length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen);
- aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t));
- aasworld.numareasettings = length / sizeof(aas_areasettings_t);
- if (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP;
- //reachability list
- offset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs);
- length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen);
- aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t));
- aasworld.reachabilitysize = length / sizeof(aas_reachability_t);
- if (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP;
- //nodes
- offset = LittleLong(header.lumps[AASLUMP_NODES].fileofs);
- length = LittleLong(header.lumps[AASLUMP_NODES].filelen);
- aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t));
- aasworld.numnodes = length / sizeof(aas_node_t);
- if (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP;
- //cluster portals
- offset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs);
- length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen);
- aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t));
- aasworld.numportals = length / sizeof(aas_portal_t);
- if (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP;
- //cluster portal index
- offset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs);
- length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen);
- aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t));
- aasworld.portalindexsize = length / sizeof(aas_portalindex_t);
- if (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP;
- //clusters
- offset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs);
- length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen);
- aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t));
- aasworld.numclusters = length / sizeof(aas_cluster_t);
- if (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP;
- //swap everything
- AAS_SwapAASData();
- //aas file is loaded
- aasworld.loaded = qtrue;
- //close the file
- botimport.FS_FCloseFile(fp);
- //
-#ifdef AASFILEDEBUG
- AAS_FileInfo();
-#endif //AASFILEDEBUG
- //
- return BLERR_NOERROR;
-} //end of the function AAS_LoadAASFile
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-static int AAS_WriteAASLump_offset;
-
-int AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length)
-{
- aas_lump_t *lump;
-
- lump = &h->lumps[lumpnum];
-
- lump->fileofs = LittleLong(AAS_WriteAASLump_offset); //LittleLong(ftell(fp));
- lump->filelen = LittleLong(length);
-
- if (length > 0)
- {
- botimport.FS_Write(data, length, fp );
- } //end if
-
- AAS_WriteAASLump_offset += length;
-
- return qtrue;
-} //end of the function AAS_WriteAASLump
-//===========================================================================
-// aas data is useless after writing to file because it is byte swapped
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_WriteAASFile(char *filename)
-{
- aas_header_t header;
- fileHandle_t fp;
-
- botimport.Print(PRT_MESSAGE, "writing %s\n", filename);
- //swap the aas data
- AAS_SwapAASData();
- //initialize the file header
- Com_Memset(&header, 0, sizeof(aas_header_t));
- header.ident = LittleLong(AASID);
- header.version = LittleLong(AASVERSION);
- header.bspchecksum = LittleLong(aasworld.bspchecksum);
- //open a new file
- botimport.FS_FOpenFile( filename, &fp, FS_WRITE );
- if (!fp)
- {
- botimport.Print(PRT_ERROR, "error opening %s\n", filename);
- return qfalse;
- } //end if
- //write the header
- botimport.FS_Write(&header, sizeof(aas_header_t), fp);
- AAS_WriteAASLump_offset = sizeof(aas_header_t);
- //add the data lumps to the file
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes,
- aasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes,
- aasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes,
- aasworld.numplanes * sizeof(aas_plane_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges,
- aasworld.numedges * sizeof(aas_edge_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex,
- aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces,
- aasworld.numfaces * sizeof(aas_face_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex,
- aasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas,
- aasworld.numareas * sizeof(aas_area_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings,
- aasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability,
- aasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes,
- aasworld.numnodes * sizeof(aas_node_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals,
- aasworld.numportals * sizeof(aas_portal_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex,
- aasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse;
- if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters,
- aasworld.numclusters * sizeof(aas_cluster_t))) return qfalse;
- //rewrite the header with the added lumps
- botimport.FS_Seek(fp, 0, FS_SEEK_SET);
- AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);
- botimport.FS_Write(&header, sizeof(aas_header_t), fp);
- //close the file
- botimport.FS_FCloseFile(fp);
- return qtrue;
-} //end of the function AAS_WriteAASFile
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_file.c + * + * desc: AAS file loading/writing + * + * $Archive: /MissionPack/code/botlib/be_aas_file.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "l_utils.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +//#define AASFILEDEBUG + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SwapAASData(void) +{ + int i, j; + //bounding boxes + for (i = 0; i < aasworld.numbboxes; i++) + { + aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype); + aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags); + for (j = 0; j < 3; j++) + { + aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]); + aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]); + } //end for + } //end for + //vertexes + for (i = 0; i < aasworld.numvertexes; i++) + { + for (j = 0; j < 3; j++) + aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]); + } //end for + //planes + for (i = 0; i < aasworld.numplanes; i++) + { + for (j = 0; j < 3; j++) + aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]); + aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist); + aasworld.planes[i].type = LittleLong(aasworld.planes[i].type); + } //end for + //edges + for (i = 0; i < aasworld.numedges; i++) + { + aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]); + aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]); + } //end for + //edgeindex + for (i = 0; i < aasworld.edgeindexsize; i++) + { + aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]); + } //end for + //faces + for (i = 0; i < aasworld.numfaces; i++) + { + aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum); + aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags); + aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges); + aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge); + aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea); + aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea); + } //end for + //face index + for (i = 0; i < aasworld.faceindexsize; i++) + { + aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]); + } //end for + //convex areas + for (i = 0; i < aasworld.numareas; i++) + { + aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum); + aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces); + aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface); + for (j = 0; j < 3; j++) + { + aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]); + aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]); + aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]); + } //end for + } //end for + //area settings + for (i = 0; i < aasworld.numareasettings; i++) + { + aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents); + aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags); + aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype); + aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster); + aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum); + aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas); + aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea); + } //end for + //area reachability + for (i = 0; i < aasworld.reachabilitysize; i++) + { + aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum); + aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum); + aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum); + for (j = 0; j < 3; j++) + { + aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]); + aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]); + } //end for + aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype); + aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime); + } //end for + //nodes + for (i = 0; i < aasworld.numnodes; i++) + { + aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum); + aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]); + aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]); + } //end for + //cluster portals + for (i = 0; i < aasworld.numportals; i++) + { + aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum); + aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster); + aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster); + aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]); + aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]); + } //end for + //cluster portal index + for (i = 0; i < aasworld.portalindexsize; i++) + { + aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]); + } //end for + //cluster + for (i = 0; i < aasworld.numclusters; i++) + { + aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas); + aasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas); + aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals); + aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal); + } //end for +} //end of the function AAS_SwapAASData +//=========================================================================== +// dump the current loaded aas file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DumpAASData(void) +{ + aasworld.numbboxes = 0; + if (aasworld.bboxes) FreeMemory(aasworld.bboxes); + aasworld.bboxes = NULL; + aasworld.numvertexes = 0; + if (aasworld.vertexes) FreeMemory(aasworld.vertexes); + aasworld.vertexes = NULL; + aasworld.numplanes = 0; + if (aasworld.planes) FreeMemory(aasworld.planes); + aasworld.planes = NULL; + aasworld.numedges = 0; + if (aasworld.edges) FreeMemory(aasworld.edges); + aasworld.edges = NULL; + aasworld.edgeindexsize = 0; + if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); + aasworld.edgeindex = NULL; + aasworld.numfaces = 0; + if (aasworld.faces) FreeMemory(aasworld.faces); + aasworld.faces = NULL; + aasworld.faceindexsize = 0; + if (aasworld.faceindex) FreeMemory(aasworld.faceindex); + aasworld.faceindex = NULL; + aasworld.numareas = 0; + if (aasworld.areas) FreeMemory(aasworld.areas); + aasworld.areas = NULL; + aasworld.numareasettings = 0; + if (aasworld.areasettings) FreeMemory(aasworld.areasettings); + aasworld.areasettings = NULL; + aasworld.reachabilitysize = 0; + if (aasworld.reachability) FreeMemory(aasworld.reachability); + aasworld.reachability = NULL; + aasworld.numnodes = 0; + if (aasworld.nodes) FreeMemory(aasworld.nodes); + aasworld.nodes = NULL; + aasworld.numportals = 0; + if (aasworld.portals) FreeMemory(aasworld.portals); + aasworld.portals = NULL; + aasworld.numportals = 0; + if (aasworld.portalindex) FreeMemory(aasworld.portalindex); + aasworld.portalindex = NULL; + aasworld.portalindexsize = 0; + if (aasworld.clusters) FreeMemory(aasworld.clusters); + aasworld.clusters = NULL; + aasworld.numclusters = 0; + // + aasworld.loaded = qfalse; + aasworld.initialized = qfalse; + aasworld.savefile = qfalse; +} //end of the function AAS_DumpAASData +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef AASFILEDEBUG +void AAS_FileInfo(void) +{ + int i, n, optimized; + + botimport.Print(PRT_MESSAGE, "version = %d\n", AASVERSION); + botimport.Print(PRT_MESSAGE, "numvertexes = %d\n", aasworld.numvertexes); + botimport.Print(PRT_MESSAGE, "numplanes = %d\n", aasworld.numplanes); + botimport.Print(PRT_MESSAGE, "numedges = %d\n", aasworld.numedges); + botimport.Print(PRT_MESSAGE, "edgeindexsize = %d\n", aasworld.edgeindexsize); + botimport.Print(PRT_MESSAGE, "numfaces = %d\n", aasworld.numfaces); + botimport.Print(PRT_MESSAGE, "faceindexsize = %d\n", aasworld.faceindexsize); + botimport.Print(PRT_MESSAGE, "numareas = %d\n", aasworld.numareas); + botimport.Print(PRT_MESSAGE, "numareasettings = %d\n", aasworld.numareasettings); + botimport.Print(PRT_MESSAGE, "reachabilitysize = %d\n", aasworld.reachabilitysize); + botimport.Print(PRT_MESSAGE, "numnodes = %d\n", aasworld.numnodes); + botimport.Print(PRT_MESSAGE, "numportals = %d\n", aasworld.numportals); + botimport.Print(PRT_MESSAGE, "portalindexsize = %d\n", aasworld.portalindexsize); + botimport.Print(PRT_MESSAGE, "numclusters = %d\n", aasworld.numclusters); + // + for (n = 0, i = 0; i < aasworld.numareasettings; i++) + { + if (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++; + } //end for + botimport.Print(PRT_MESSAGE, "num grounded areas = %d\n", n); + // + botimport.Print(PRT_MESSAGE, "planes size %d bytes\n", aasworld.numplanes * sizeof(aas_plane_t)); + botimport.Print(PRT_MESSAGE, "areas size %d bytes\n", aasworld.numareas * sizeof(aas_area_t)); + botimport.Print(PRT_MESSAGE, "areasettings size %d bytes\n", aasworld.numareasettings * sizeof(aas_areasettings_t)); + botimport.Print(PRT_MESSAGE, "nodes size %d bytes\n", aasworld.numnodes * sizeof(aas_node_t)); + botimport.Print(PRT_MESSAGE, "reachability size %d bytes\n", aasworld.reachabilitysize * sizeof(aas_reachability_t)); + botimport.Print(PRT_MESSAGE, "portals size %d bytes\n", aasworld.numportals * sizeof(aas_portal_t)); + botimport.Print(PRT_MESSAGE, "clusters size %d bytes\n", aasworld.numclusters * sizeof(aas_cluster_t)); + + optimized = aasworld.numplanes * sizeof(aas_plane_t) + + aasworld.numareas * sizeof(aas_area_t) + + aasworld.numareasettings * sizeof(aas_areasettings_t) + + aasworld.numnodes * sizeof(aas_node_t) + + aasworld.reachabilitysize * sizeof(aas_reachability_t) + + aasworld.numportals * sizeof(aas_portal_t) + + aasworld.numclusters * sizeof(aas_cluster_t); + botimport.Print(PRT_MESSAGE, "optimzed size %d KB\n", optimized >> 10); +} //end of the function AAS_FileInfo +#endif //AASFILEDEBUG +//=========================================================================== +// allocate memory and read a lump of a AAS file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size) +{ + char *buf; + // + if (!length) + { + //just alloc a dummy + return (char *) GetClearedHunkMemory(size+1); + } //end if + //seek to the data + if (offset != *lastoffset) + { + botimport.Print(PRT_WARNING, "AAS file not sequentially read\n"); + if (botimport.FS_Seek(fp, offset, FS_SEEK_SET)) + { + AAS_Error("can't seek to aas lump\n"); + AAS_DumpAASData(); + botimport.FS_FCloseFile(fp); + return 0; + } //end if + } //end if + //allocate memory + buf = (char *) GetClearedHunkMemory(length+1); + //read the data + if (length) + { + botimport.FS_Read(buf, length, fp ); + *lastoffset += length; + } //end if + return buf; +} //end of the function AAS_LoadAASLump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DData(unsigned char *data, int size) +{ + int i; + + for (i = 0; i < size; i++) + { + data[i] ^= (unsigned char) i * 119; + } //end for +} //end of the function AAS_DData +//=========================================================================== +// load an aas file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadAASFile(char *filename) +{ + fileHandle_t fp; + aas_header_t header; + int offset, length, lastoffset; + + botimport.Print(PRT_MESSAGE, "trying to load %s\n", filename); + //dump current loaded aas file + AAS_DumpAASData(); + //open the file + botimport.FS_FOpenFile( filename, &fp, FS_READ ); + if (!fp) + { + AAS_Error("can't open %s\n", filename); + return BLERR_CANNOTOPENAASFILE; + } //end if + //read the header + botimport.FS_Read(&header, sizeof(aas_header_t), fp ); + lastoffset = sizeof(aas_header_t); + //check header identification + header.ident = LittleLong(header.ident); + if (header.ident != AASID) + { + AAS_Error("%s is not an AAS file\n", filename); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEID; + } //end if + //check the version + header.version = LittleLong(header.version); + // + if (header.version != AASVERSION_OLD && header.version != AASVERSION) + { + AAS_Error("aas file %s is version %i, not %i\n", filename, header.version, AASVERSION); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEVERSION; + } //end if + // + if (header.version == AASVERSION) + { + AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); + } //end if + // + aasworld.bspchecksum = atoi(LibVarGetString( "sv_mapChecksum")); + if (LittleLong(header.bspchecksum) != aasworld.bspchecksum) + { + AAS_Error("aas file %s is out of date\n", filename); + botimport.FS_FCloseFile(fp); + return BLERR_WRONGAASFILEVERSION; + } //end if + //load the lumps: + //bounding boxes + offset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs); + length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen); + aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t)); + aasworld.numbboxes = length / sizeof(aas_bbox_t); + if (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP; + //vertexes + offset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs); + length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen); + aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t)); + aasworld.numvertexes = length / sizeof(aas_vertex_t); + if (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP; + //planes + offset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs); + length = LittleLong(header.lumps[AASLUMP_PLANES].filelen); + aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t)); + aasworld.numplanes = length / sizeof(aas_plane_t); + if (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP; + //edges + offset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs); + length = LittleLong(header.lumps[AASLUMP_EDGES].filelen); + aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t)); + aasworld.numedges = length / sizeof(aas_edge_t); + if (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP; + //edgeindex + offset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs); + length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen); + aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t)); + aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t); + if (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP; + //faces + offset = LittleLong(header.lumps[AASLUMP_FACES].fileofs); + length = LittleLong(header.lumps[AASLUMP_FACES].filelen); + aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t)); + aasworld.numfaces = length / sizeof(aas_face_t); + if (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP; + //faceindex + offset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs); + length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen); + aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t)); + aasworld.faceindexsize = length / sizeof(aas_faceindex_t); + if (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP; + //convex areas + offset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs); + length = LittleLong(header.lumps[AASLUMP_AREAS].filelen); + aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t)); + aasworld.numareas = length / sizeof(aas_area_t); + if (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP; + //area settings + offset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs); + length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen); + aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t)); + aasworld.numareasettings = length / sizeof(aas_areasettings_t); + if (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP; + //reachability list + offset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs); + length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen); + aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t)); + aasworld.reachabilitysize = length / sizeof(aas_reachability_t); + if (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP; + //nodes + offset = LittleLong(header.lumps[AASLUMP_NODES].fileofs); + length = LittleLong(header.lumps[AASLUMP_NODES].filelen); + aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t)); + aasworld.numnodes = length / sizeof(aas_node_t); + if (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP; + //cluster portals + offset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs); + length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen); + aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t)); + aasworld.numportals = length / sizeof(aas_portal_t); + if (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP; + //cluster portal index + offset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs); + length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen); + aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t)); + aasworld.portalindexsize = length / sizeof(aas_portalindex_t); + if (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP; + //clusters + offset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs); + length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen); + aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t)); + aasworld.numclusters = length / sizeof(aas_cluster_t); + if (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP; + //swap everything + AAS_SwapAASData(); + //aas file is loaded + aasworld.loaded = qtrue; + //close the file + botimport.FS_FCloseFile(fp); + // +#ifdef AASFILEDEBUG + AAS_FileInfo(); +#endif //AASFILEDEBUG + // + return BLERR_NOERROR; +} //end of the function AAS_LoadAASFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static int AAS_WriteAASLump_offset; + +int AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length) +{ + aas_lump_t *lump; + + lump = &h->lumps[lumpnum]; + + lump->fileofs = LittleLong(AAS_WriteAASLump_offset); //LittleLong(ftell(fp)); + lump->filelen = LittleLong(length); + + if (length > 0) + { + botimport.FS_Write(data, length, fp ); + } //end if + + AAS_WriteAASLump_offset += length; + + return qtrue; +} //end of the function AAS_WriteAASLump +//=========================================================================== +// aas data is useless after writing to file because it is byte swapped +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_WriteAASFile(char *filename) +{ + aas_header_t header; + fileHandle_t fp; + + botimport.Print(PRT_MESSAGE, "writing %s\n", filename); + //swap the aas data + AAS_SwapAASData(); + //initialize the file header + Com_Memset(&header, 0, sizeof(aas_header_t)); + header.ident = LittleLong(AASID); + header.version = LittleLong(AASVERSION); + header.bspchecksum = LittleLong(aasworld.bspchecksum); + //open a new file + botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); + if (!fp) + { + botimport.Print(PRT_ERROR, "error opening %s\n", filename); + return qfalse; + } //end if + //write the header + botimport.FS_Write(&header, sizeof(aas_header_t), fp); + AAS_WriteAASLump_offset = sizeof(aas_header_t); + //add the data lumps to the file + if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes, + aasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes, + aasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes, + aasworld.numplanes * sizeof(aas_plane_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges, + aasworld.numedges * sizeof(aas_edge_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex, + aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces, + aasworld.numfaces * sizeof(aas_face_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex, + aasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas, + aasworld.numareas * sizeof(aas_area_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings, + aasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability, + aasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes, + aasworld.numnodes * sizeof(aas_node_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals, + aasworld.numportals * sizeof(aas_portal_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex, + aasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse; + if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters, + aasworld.numclusters * sizeof(aas_cluster_t))) return qfalse; + //rewrite the header with the added lumps + botimport.FS_Seek(fp, 0, FS_SEEK_SET); + AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8); + botimport.FS_Write(&header, sizeof(aas_header_t), fp); + //close the file + botimport.FS_FCloseFile(fp); + return qtrue; +} //end of the function AAS_WriteAASFile diff --git a/code/botlib/be_aas_file.h b/code/botlib/be_aas_file.h index 87a38d0..0d9383e 100755 --- a/code/botlib/be_aas_file.h +++ b/code/botlib/be_aas_file.h @@ -1,42 +1,42 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_file.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_file.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-//loads the AAS file with the given name
-int AAS_LoadAASFile(char *filename);
-//writes an AAS file with the given name
-qboolean AAS_WriteAASFile(char *filename);
-//dumps the loaded AAS data
-void AAS_DumpAASData(void);
-//print AAS file information
-void AAS_FileInfo(void);
-#endif //AASINTERN
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_file.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_file.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//loads the AAS file with the given name +int AAS_LoadAASFile(char *filename); +//writes an AAS file with the given name +qboolean AAS_WriteAASFile(char *filename); +//dumps the loaded AAS data +void AAS_DumpAASData(void); +//print AAS file information +void AAS_FileInfo(void); +#endif //AASINTERN + diff --git a/code/botlib/be_aas_funcs.h b/code/botlib/be_aas_funcs.h index 4dd37e9..e70f348 100755 --- a/code/botlib/be_aas_funcs.h +++ b/code/botlib/be_aas_funcs.h @@ -1,47 +1,47 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_funcs.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_funcs.h $
- *
- *****************************************************************************/
-
-#ifndef BSPCINCLUDE
-
-#include "be_aas_main.h"
-#include "be_aas_entity.h"
-#include "be_aas_sample.h"
-#include "be_aas_cluster.h"
-#include "be_aas_reach.h"
-#include "be_aas_route.h"
-#include "be_aas_routealt.h"
-#include "be_aas_debug.h"
-#include "be_aas_file.h"
-#include "be_aas_optimize.h"
-#include "be_aas_bsp.h"
-#include "be_aas_move.h"
-
-#endif //BSPCINCLUDE
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_funcs.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_funcs.h $ + * + *****************************************************************************/ + +#ifndef BSPCINCLUDE + +#include "be_aas_main.h" +#include "be_aas_entity.h" +#include "be_aas_sample.h" +#include "be_aas_cluster.h" +#include "be_aas_reach.h" +#include "be_aas_route.h" +#include "be_aas_routealt.h" +#include "be_aas_debug.h" +#include "be_aas_file.h" +#include "be_aas_optimize.h" +#include "be_aas_bsp.h" +#include "be_aas_move.h" + +#endif //BSPCINCLUDE diff --git a/code/botlib/be_aas_main.c b/code/botlib/be_aas_main.c index 45a9dc3..dac1ce3 100755 --- a/code/botlib/be_aas_main.c +++ b/code/botlib/be_aas_main.c @@ -1,429 +1,429 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_main.c
- *
- * desc: AAS
- *
- * $Archive: /MissionPack/code/botlib/be_aas_main.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_libvar.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_log.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_aas_def.h"
-
-aas_t aasworld;
-
-libvar_t *saveroutingcache;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void QDECL AAS_Error(char *fmt, ...)
-{
- char str[1024];
- va_list arglist;
-
- va_start(arglist, fmt);
- vsprintf(str, fmt, arglist);
- va_end(arglist);
- botimport.Print(PRT_FATAL, str);
-} //end of the function AAS_Error
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index)
-{
- if (!aasworld.indexessetup)
- {
- botimport.Print(PRT_ERROR, "%s: index %d not setup\n", indexname, index);
- return "";
- } //end if
- if (index < 0 || index >= numindexes)
- {
- botimport.Print(PRT_ERROR, "%s: index %d out of range\n", indexname, index);
- return "";
- } //end if
- if (!stringindex[index])
- {
- if (index)
- {
- botimport.Print(PRT_ERROR, "%s: reference to unused index %d\n", indexname, index);
- } //end if
- return "";
- } //end if
- return stringindex[index];
-} //end of the function AAS_StringFromIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string)
-{
- int i;
- if (!aasworld.indexessetup)
- {
- botimport.Print(PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string);
- return 0;
- } //end if
- for (i = 0; i < numindexes; i++)
- {
- if (!stringindex[i]) continue;
- if (!Q_stricmp(stringindex[i], string)) return i;
- } //end for
- return 0;
-} //end of the function AAS_IndexFromString
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *AAS_ModelFromIndex(int index)
-{
- return AAS_StringFromIndex("ModelFromIndex", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index);
-} //end of the function AAS_ModelFromIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_IndexFromModel(char *modelname)
-{
- return AAS_IndexFromString("IndexFromModel", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname);
-} //end of the function AAS_IndexFromModel
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[])
-{
- int i;
- //set string pointers and copy the strings
- for (i = 0; i < numconfigstrings; i++)
- {
- if (configstrings[i])
- {
- //if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]);
- aasworld.configstrings[i] = (char *) GetMemory(strlen(configstrings[i]) + 1);
- strcpy(aasworld.configstrings[i], configstrings[i]);
- } //end if
- } //end for
- aasworld.indexessetup = qtrue;
-} //end of the function AAS_UpdateStringIndexes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Loaded(void)
-{
- return aasworld.loaded;
-} //end of the function AAS_Loaded
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Initialized(void)
-{
- return aasworld.initialized;
-} //end of the function AAS_Initialized
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_SetInitialized(void)
-{
- aasworld.initialized = qtrue;
- botimport.Print(PRT_MESSAGE, "AAS initialized.\n");
-#ifdef DEBUG
- //create all the routing cache
- //AAS_CreateAllRoutingCache();
- //
- //AAS_RoutingInfo();
-#endif
-} //end of the function AAS_SetInitialized
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ContinueInit(float time)
-{
- //if no AAS file loaded
- if (!aasworld.loaded) return;
- //if AAS is already initialized
- if (aasworld.initialized) return;
- //calculate reachability, if not finished return
- if (AAS_ContinueInitReachability(time)) return;
- //initialize clustering for the new map
- AAS_InitClustering();
- //if reachability has been calculated and an AAS file should be written
- //or there is a forced data optimization
- if (aasworld.savefile || ((int)LibVarGetValue("forcewrite")))
- {
- //optimize the AAS data
- if ((int)LibVarValue("aasoptimize", "0")) AAS_Optimize();
- //save the AAS file
- if (AAS_WriteAASFile(aasworld.filename))
- {
- botimport.Print(PRT_MESSAGE, "%s written succesfully\n", aasworld.filename);
- } //end if
- else
- {
- botimport.Print(PRT_ERROR, "couldn't write %s\n", aasworld.filename);
- } //end else
- } //end if
- //initialize the routing
- AAS_InitRouting();
- //at this point AAS is initialized
- AAS_SetInitialized();
-} //end of the function AAS_ContinueInit
-//===========================================================================
-// called at the start of every frame
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_StartFrame(float time)
-{
- aasworld.time = time;
- //unlink all entities that were not updated last frame
- AAS_UnlinkInvalidEntities();
- //invalidate the entities
- AAS_InvalidateEntities();
- //initialize AAS
- AAS_ContinueInit(time);
- //
- aasworld.frameroutingupdates = 0;
- //
- if (bot_developer)
- {
- if (LibVarGetValue("showcacheupdates"))
- {
- AAS_RoutingInfo();
- LibVarSet("showcacheupdates", "0");
- } //end if
- if (LibVarGetValue("showmemoryusage"))
- {
- PrintUsedMemorySize();
- LibVarSet("showmemoryusage", "0");
- } //end if
- if (LibVarGetValue("memorydump"))
- {
- PrintMemoryLabels();
- LibVarSet("memorydump", "0");
- } //end if
- } //end if
- //
- if (saveroutingcache->value)
- {
- AAS_WriteRouteCache();
- LibVarSet("saveroutingcache", "0");
- } //end if
- //
- aasworld.numframes++;
- return BLERR_NOERROR;
-} //end of the function AAS_StartFrame
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_Time(void)
-{
- return aasworld.time;
-} //end of the function AAS_Time
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
-{
- vec3_t pVec, vec;
-
- VectorSubtract( point, vStart, pVec );
- VectorSubtract( vEnd, vStart, vec );
- VectorNormalize( vec );
- // project onto the directional vector for this segment
- VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
-} //end of the function AAS_ProjectPointOntoVector
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_LoadFiles(const char *mapname)
-{
- int errnum;
- char aasfile[MAX_PATH];
-// char bspfile[MAX_PATH];
-
- strcpy(aasworld.mapname, mapname);
- //NOTE: first reset the entity links into the AAS areas and BSP leaves
- // the AAS link heap and BSP link heap are reset after respectively the
- // AAS file and BSP file are loaded
- AAS_ResetEntityLinks();
- // load bsp info
- AAS_LoadBSPFile();
-
- //load the aas file
- Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname);
- errnum = AAS_LoadAASFile(aasfile);
- if (errnum != BLERR_NOERROR)
- return errnum;
-
- botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile);
- strncpy(aasworld.filename, aasfile, MAX_PATH);
- return BLERR_NOERROR;
-} //end of the function AAS_LoadFiles
-//===========================================================================
-// called everytime a map changes
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_LoadMap(const char *mapname)
-{
- int errnum;
-
- //if no mapname is provided then the string indexes are updated
- if (!mapname)
- {
- return 0;
- } //end if
- //
- aasworld.initialized = qfalse;
- //NOTE: free the routing caches before loading a new map because
- // to free the caches the old number of areas, number of clusters
- // and number of areas in a clusters must be available
- AAS_FreeRoutingCaches();
- //load the map
- errnum = AAS_LoadFiles(mapname);
- if (errnum != BLERR_NOERROR)
- {
- aasworld.loaded = qfalse;
- return errnum;
- } //end if
- //
- AAS_InitSettings();
- //initialize the AAS link heap for the new map
- AAS_InitAASLinkHeap();
- //initialize the AAS linked entities for the new map
- AAS_InitAASLinkedEntities();
- //initialize reachability for the new map
- AAS_InitReachability();
- //initialize the alternative routing
- AAS_InitAlternativeRouting();
- //everything went ok
- return 0;
-} //end of the function AAS_LoadMap
-//===========================================================================
-// called when the library is first loaded
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Setup(void)
-{
- aasworld.maxclients = (int) LibVarValue("maxclients", "128");
- aasworld.maxentities = (int) LibVarValue("maxentities", "1024");
- // as soon as it's set to 1 the routing cache will be saved
- saveroutingcache = LibVar("saveroutingcache", "0");
- //allocate memory for the entities
- if (aasworld.entities) FreeMemory(aasworld.entities);
- aasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t));
- //invalidate all the entities
- AAS_InvalidateEntities();
- //force some recalculations
- //LibVarSet("forceclustering", "1"); //force clustering calculation
- //LibVarSet("forcereachability", "1"); //force reachability calculation
- aasworld.numframes = 0;
- return BLERR_NOERROR;
-} //end of the function AAS_Setup
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Shutdown(void)
-{
- AAS_ShutdownAlternativeRouting();
- //
- AAS_DumpBSPData();
- //free routing caches
- AAS_FreeRoutingCaches();
- //free aas link heap
- AAS_FreeAASLinkHeap();
- //free aas linked entities
- AAS_FreeAASLinkedEntities();
- //free the aas data
- AAS_DumpAASData();
- //free the entities
- if (aasworld.entities) FreeMemory(aasworld.entities);
- //clear the aasworld structure
- Com_Memset(&aasworld, 0, sizeof(aas_t));
- //aas has not been initialized
- aasworld.initialized = qfalse;
- //NOTE: as soon as a new .bsp file is loaded the .bsp file memory is
- // freed an reallocated, so there's no need to free that memory here
- //print shutdown
- botimport.Print(PRT_MESSAGE, "AAS shutdown.\n");
-} //end of the function AAS_Shutdown
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_main.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_main.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_log.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +aas_t aasworld; + +libvar_t *saveroutingcache; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL AAS_Error(char *fmt, ...) +{ + char str[1024]; + va_list arglist; + + va_start(arglist, fmt); + vsprintf(str, fmt, arglist); + va_end(arglist); + botimport.Print(PRT_FATAL, str); +} //end of the function AAS_Error +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index) +{ + if (!aasworld.indexessetup) + { + botimport.Print(PRT_ERROR, "%s: index %d not setup\n", indexname, index); + return ""; + } //end if + if (index < 0 || index >= numindexes) + { + botimport.Print(PRT_ERROR, "%s: index %d out of range\n", indexname, index); + return ""; + } //end if + if (!stringindex[index]) + { + if (index) + { + botimport.Print(PRT_ERROR, "%s: reference to unused index %d\n", indexname, index); + } //end if + return ""; + } //end if + return stringindex[index]; +} //end of the function AAS_StringFromIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string) +{ + int i; + if (!aasworld.indexessetup) + { + botimport.Print(PRT_ERROR, "%s: index not setup \"%s\"\n", indexname, string); + return 0; + } //end if + for (i = 0; i < numindexes; i++) + { + if (!stringindex[i]) continue; + if (!Q_stricmp(stringindex[i], string)) return i; + } //end for + return 0; +} //end of the function AAS_IndexFromString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_ModelFromIndex(int index) +{ + return AAS_StringFromIndex("ModelFromIndex", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index); +} //end of the function AAS_ModelFromIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_IndexFromModel(char *modelname) +{ + return AAS_IndexFromString("IndexFromModel", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname); +} //end of the function AAS_IndexFromModel +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[]) +{ + int i; + //set string pointers and copy the strings + for (i = 0; i < numconfigstrings; i++) + { + if (configstrings[i]) + { + //if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]); + aasworld.configstrings[i] = (char *) GetMemory(strlen(configstrings[i]) + 1); + strcpy(aasworld.configstrings[i], configstrings[i]); + } //end if + } //end for + aasworld.indexessetup = qtrue; +} //end of the function AAS_UpdateStringIndexes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Loaded(void) +{ + return aasworld.loaded; +} //end of the function AAS_Loaded +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Initialized(void) +{ + return aasworld.initialized; +} //end of the function AAS_Initialized +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetInitialized(void) +{ + aasworld.initialized = qtrue; + botimport.Print(PRT_MESSAGE, "AAS initialized.\n"); +#ifdef DEBUG + //create all the routing cache + //AAS_CreateAllRoutingCache(); + // + //AAS_RoutingInfo(); +#endif +} //end of the function AAS_SetInitialized +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ContinueInit(float time) +{ + //if no AAS file loaded + if (!aasworld.loaded) return; + //if AAS is already initialized + if (aasworld.initialized) return; + //calculate reachability, if not finished return + if (AAS_ContinueInitReachability(time)) return; + //initialize clustering for the new map + AAS_InitClustering(); + //if reachability has been calculated and an AAS file should be written + //or there is a forced data optimization + if (aasworld.savefile || ((int)LibVarGetValue("forcewrite"))) + { + //optimize the AAS data + if ((int)LibVarValue("aasoptimize", "0")) AAS_Optimize(); + //save the AAS file + if (AAS_WriteAASFile(aasworld.filename)) + { + botimport.Print(PRT_MESSAGE, "%s written succesfully\n", aasworld.filename); + } //end if + else + { + botimport.Print(PRT_ERROR, "couldn't write %s\n", aasworld.filename); + } //end else + } //end if + //initialize the routing + AAS_InitRouting(); + //at this point AAS is initialized + AAS_SetInitialized(); +} //end of the function AAS_ContinueInit +//=========================================================================== +// called at the start of every frame +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_StartFrame(float time) +{ + aasworld.time = time; + //unlink all entities that were not updated last frame + AAS_UnlinkInvalidEntities(); + //invalidate the entities + AAS_InvalidateEntities(); + //initialize AAS + AAS_ContinueInit(time); + // + aasworld.frameroutingupdates = 0; + // + if (bot_developer) + { + if (LibVarGetValue("showcacheupdates")) + { + AAS_RoutingInfo(); + LibVarSet("showcacheupdates", "0"); + } //end if + if (LibVarGetValue("showmemoryusage")) + { + PrintUsedMemorySize(); + LibVarSet("showmemoryusage", "0"); + } //end if + if (LibVarGetValue("memorydump")) + { + PrintMemoryLabels(); + LibVarSet("memorydump", "0"); + } //end if + } //end if + // + if (saveroutingcache->value) + { + AAS_WriteRouteCache(); + LibVarSet("saveroutingcache", "0"); + } //end if + // + aasworld.numframes++; + return BLERR_NOERROR; +} //end of the function AAS_StartFrame +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_Time(void) +{ + return aasworld.time; +} //end of the function AAS_Time +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ) +{ + vec3_t pVec, vec; + + VectorSubtract( point, vStart, pVec ); + VectorSubtract( vEnd, vStart, vec ); + VectorNormalize( vec ); + // project onto the directional vector for this segment + VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj ); +} //end of the function AAS_ProjectPointOntoVector +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadFiles(const char *mapname) +{ + int errnum; + char aasfile[MAX_PATH]; +// char bspfile[MAX_PATH]; + + strcpy(aasworld.mapname, mapname); + //NOTE: first reset the entity links into the AAS areas and BSP leaves + // the AAS link heap and BSP link heap are reset after respectively the + // AAS file and BSP file are loaded + AAS_ResetEntityLinks(); + // load bsp info + AAS_LoadBSPFile(); + + //load the aas file + Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname); + errnum = AAS_LoadAASFile(aasfile); + if (errnum != BLERR_NOERROR) + return errnum; + + botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile); + strncpy(aasworld.filename, aasfile, MAX_PATH); + return BLERR_NOERROR; +} //end of the function AAS_LoadFiles +//=========================================================================== +// called everytime a map changes +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_LoadMap(const char *mapname) +{ + int errnum; + + //if no mapname is provided then the string indexes are updated + if (!mapname) + { + return 0; + } //end if + // + aasworld.initialized = qfalse; + //NOTE: free the routing caches before loading a new map because + // to free the caches the old number of areas, number of clusters + // and number of areas in a clusters must be available + AAS_FreeRoutingCaches(); + //load the map + errnum = AAS_LoadFiles(mapname); + if (errnum != BLERR_NOERROR) + { + aasworld.loaded = qfalse; + return errnum; + } //end if + // + AAS_InitSettings(); + //initialize the AAS link heap for the new map + AAS_InitAASLinkHeap(); + //initialize the AAS linked entities for the new map + AAS_InitAASLinkedEntities(); + //initialize reachability for the new map + AAS_InitReachability(); + //initialize the alternative routing + AAS_InitAlternativeRouting(); + //everything went ok + return 0; +} //end of the function AAS_LoadMap +//=========================================================================== +// called when the library is first loaded +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Setup(void) +{ + aasworld.maxclients = (int) LibVarValue("maxclients", "128"); + aasworld.maxentities = (int) LibVarValue("maxentities", "1024"); + // as soon as it's set to 1 the routing cache will be saved + saveroutingcache = LibVar("saveroutingcache", "0"); + //allocate memory for the entities + if (aasworld.entities) FreeMemory(aasworld.entities); + aasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t)); + //invalidate all the entities + AAS_InvalidateEntities(); + //force some recalculations + //LibVarSet("forceclustering", "1"); //force clustering calculation + //LibVarSet("forcereachability", "1"); //force reachability calculation + aasworld.numframes = 0; + return BLERR_NOERROR; +} //end of the function AAS_Setup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Shutdown(void) +{ + AAS_ShutdownAlternativeRouting(); + // + AAS_DumpBSPData(); + //free routing caches + AAS_FreeRoutingCaches(); + //free aas link heap + AAS_FreeAASLinkHeap(); + //free aas linked entities + AAS_FreeAASLinkedEntities(); + //free the aas data + AAS_DumpAASData(); + //free the entities + if (aasworld.entities) FreeMemory(aasworld.entities); + //clear the aasworld structure + Com_Memset(&aasworld, 0, sizeof(aas_t)); + //aas has not been initialized + aasworld.initialized = qfalse; + //NOTE: as soon as a new .bsp file is loaded the .bsp file memory is + // freed an reallocated, so there's no need to free that memory here + //print shutdown + botimport.Print(PRT_MESSAGE, "AAS shutdown.\n"); +} //end of the function AAS_Shutdown diff --git a/code/botlib/be_aas_main.h b/code/botlib/be_aas_main.h index 13564a9..d8dc41c 100755 --- a/code/botlib/be_aas_main.h +++ b/code/botlib/be_aas_main.h @@ -1,61 +1,61 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_main.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_main.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-
-extern aas_t aasworld;
-
-//AAS error message
-void QDECL AAS_Error(char *fmt, ...);
-//set AAS initialized
-void AAS_SetInitialized(void);
-//setup AAS with the given number of entities and clients
-int AAS_Setup(void);
-//shutdown AAS
-void AAS_Shutdown(void);
-//start a new map
-int AAS_LoadMap(const char *mapname);
-//start a new time frame
-int AAS_StartFrame(float time);
-#endif //AASINTERN
-
-//returns true if AAS is initialized
-int AAS_Initialized(void);
-//returns true if the AAS file is loaded
-int AAS_Loaded(void);
-//returns the model name from the given index
-char *AAS_ModelFromIndex(int index);
-//returns the index from the given model name
-int AAS_IndexFromModel(char *modelname);
-//returns the current time
-float AAS_Time(void);
-//
-void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj );
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_main.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_main.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN + +extern aas_t aasworld; + +//AAS error message +void QDECL AAS_Error(char *fmt, ...); +//set AAS initialized +void AAS_SetInitialized(void); +//setup AAS with the given number of entities and clients +int AAS_Setup(void); +//shutdown AAS +void AAS_Shutdown(void); +//start a new map +int AAS_LoadMap(const char *mapname); +//start a new time frame +int AAS_StartFrame(float time); +#endif //AASINTERN + +//returns true if AAS is initialized +int AAS_Initialized(void); +//returns true if the AAS file is loaded +int AAS_Loaded(void); +//returns the model name from the given index +char *AAS_ModelFromIndex(int index); +//returns the index from the given model name +int AAS_IndexFromModel(char *modelname); +//returns the current time +float AAS_Time(void); +// +void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj ); diff --git a/code/botlib/be_aas_move.c b/code/botlib/be_aas_move.c index d8b6cfd..3433153 100755 --- a/code/botlib/be_aas_move.c +++ b/code/botlib/be_aas_move.c @@ -1,1101 +1,1101 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_move.c
- *
- * desc: AAS
- *
- * $Archive: /MissionPack/code/botlib/be_aas_move.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_libvar.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-
-extern botlib_import_t botimport;
-
-aas_settings_t aassettings;
-
-//#define AAS_MOVE_DEBUG
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs)
-{
- vec3_t end;
- bsp_trace_t trace;
-
- VectorCopy(origin, end);
- end[2] -= 100;
- trace = AAS_Trace(origin, mins, maxs, end, 0, CONTENTS_SOLID);
- if (trace.startsolid) return qfalse;
- VectorCopy(trace.endpos, origin);
- return qtrue;
-} //end of the function AAS_DropToFloor
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitSettings(void)
-{
- aassettings.phys_gravitydirection[0] = 0;
- aassettings.phys_gravitydirection[1] = 0;
- aassettings.phys_gravitydirection[2] = -1;
- aassettings.phys_friction = LibVarValue("phys_friction", "6");
- aassettings.phys_stopspeed = LibVarValue("phys_stopspeed", "100");
- aassettings.phys_gravity = LibVarValue("phys_gravity", "800");
- aassettings.phys_waterfriction = LibVarValue("phys_waterfriction", "1");
- aassettings.phys_watergravity = LibVarValue("phys_watergravity", "400");
- aassettings.phys_maxvelocity = LibVarValue("phys_maxvelocity", "320");
- aassettings.phys_maxwalkvelocity = LibVarValue("phys_maxwalkvelocity", "320");
- aassettings.phys_maxcrouchvelocity = LibVarValue("phys_maxcrouchvelocity", "100");
- aassettings.phys_maxswimvelocity = LibVarValue("phys_maxswimvelocity", "150");
- aassettings.phys_walkaccelerate = LibVarValue("phys_walkaccelerate", "10");
- aassettings.phys_airaccelerate = LibVarValue("phys_airaccelerate", "1");
- aassettings.phys_swimaccelerate = LibVarValue("phys_swimaccelerate", "4");
- aassettings.phys_maxstep = LibVarValue("phys_maxstep", "19");
- aassettings.phys_maxsteepness = LibVarValue("phys_maxsteepness", "0.7");
- aassettings.phys_maxwaterjump = LibVarValue("phys_maxwaterjump", "18");
- aassettings.phys_maxbarrier = LibVarValue("phys_maxbarrier", "33");
- aassettings.phys_jumpvel = LibVarValue("phys_jumpvel", "270");
- aassettings.phys_falldelta5 = LibVarValue("phys_falldelta5", "40");
- aassettings.phys_falldelta10 = LibVarValue("phys_falldelta10", "60");
- aassettings.rs_waterjump = LibVarValue("rs_waterjump", "400");
- aassettings.rs_teleport = LibVarValue("rs_teleport", "50");
- aassettings.rs_barrierjump = LibVarValue("rs_barrierjump", "100");
- aassettings.rs_startcrouch = LibVarValue("rs_startcrouch", "300");
- aassettings.rs_startgrapple = LibVarValue("rs_startgrapple", "500");
- aassettings.rs_startwalkoffledge = LibVarValue("rs_startwalkoffledge", "70");
- aassettings.rs_startjump = LibVarValue("rs_startjump", "300");
- aassettings.rs_rocketjump = LibVarValue("rs_rocketjump", "500");
- aassettings.rs_bfgjump = LibVarValue("rs_bfgjump", "500");
- aassettings.rs_jumppad = LibVarValue("rs_jumppad", "250");
- aassettings.rs_aircontrolledjumppad = LibVarValue("rs_aircontrolledjumppad", "300");
- aassettings.rs_funcbob = LibVarValue("rs_funcbob", "300");
- aassettings.rs_startelevator = LibVarValue("rs_startelevator", "50");
- aassettings.rs_falldamage5 = LibVarValue("rs_falldamage5", "300");
- aassettings.rs_falldamage10 = LibVarValue("rs_falldamage10", "500");
- aassettings.rs_maxfallheight = LibVarValue("rs_maxfallheight", "0");
- aassettings.rs_maxjumpfallheight = LibVarValue("rs_maxjumpfallheight", "450");
-} //end of the function AAS_InitSettings
-//===========================================================================
-// returns qtrue if the bot is against a ladder
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AgainstLadder(vec3_t origin)
-{
- int areanum, i, facenum, side;
- vec3_t org;
- aas_plane_t *plane;
- aas_face_t *face;
- aas_area_t *area;
-
- VectorCopy(origin, org);
- areanum = AAS_PointAreaNum(org);
- if (!areanum)
- {
- org[0] += 1;
- areanum = AAS_PointAreaNum(org);
- if (!areanum)
- {
- org[1] += 1;
- areanum = AAS_PointAreaNum(org);
- if (!areanum)
- {
- org[0] -= 2;
- areanum = AAS_PointAreaNum(org);
- if (!areanum)
- {
- org[1] -= 2;
- areanum = AAS_PointAreaNum(org);
- } //end if
- } //end if
- } //end if
- } //end if
- //if in solid... wrrr shouldn't happen
- if (!areanum) return qfalse;
- //if not in a ladder area
- if (!(aasworld.areasettings[areanum].areaflags & AREA_LADDER)) return qfalse;
- //if a crouch only area
- if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qfalse;
- //
- area = &aasworld.areas[areanum];
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = aasworld.faceindex[area->firstface + i];
- side = facenum < 0;
- face = &aasworld.faces[abs(facenum)];
- //if the face isn't a ladder face
- if (!(face->faceflags & FACE_LADDER)) continue;
- //get the plane the face is in
- plane = &aasworld.planes[face->planenum ^ side];
- //if the origin is pretty close to the plane
- if (abs(DotProduct(plane->normal, origin) - plane->dist) < 3)
- {
- if (AAS_PointInsideFace(abs(facenum), origin, 0.1f)) return qtrue;
- } //end if
- } //end for
- return qfalse;
-} //end of the function AAS_AgainstLadder
-//===========================================================================
-// returns qtrue if the bot is on the ground
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_OnGround(vec3_t origin, int presencetype, int passent)
-{
- aas_trace_t trace;
- vec3_t end, up = {0, 0, 1};
- aas_plane_t *plane;
-
- VectorCopy(origin, end);
- end[2] -= 10;
-
- trace = AAS_TraceClientBBox(origin, end, presencetype, passent);
-
- //if in solid
- if (trace.startsolid) return qfalse;
- //if nothing hit at all
- if (trace.fraction >= 1.0) return qfalse;
- //if too far from the hit plane
- if (origin[2] - trace.endpos[2] > 10) return qfalse;
- //check if the plane isn't too steep
- plane = AAS_PlaneFromNum(trace.planenum);
- if (DotProduct(plane->normal, up) < aassettings.phys_maxsteepness) return qfalse;
- //the bot is on the ground
- return qtrue;
-} //end of the function AAS_OnGround
-//===========================================================================
-// returns qtrue if a bot at the given position is swimming
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Swimming(vec3_t origin)
-{
- vec3_t testorg;
-
- VectorCopy(origin, testorg);
- testorg[2] -= 2;
- if (AAS_PointContents(testorg) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) return qtrue;
- return qfalse;
-} //end of the function AAS_Swimming
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-static vec3_t VEC_UP = {0, -1, 0};
-static vec3_t MOVEDIR_UP = {0, 0, 1};
-static vec3_t VEC_DOWN = {0, -2, 0};
-static vec3_t MOVEDIR_DOWN = {0, 0, -1};
-
-void AAS_SetMovedir(vec3_t angles, vec3_t movedir)
-{
- if (VectorCompare(angles, VEC_UP))
- {
- VectorCopy(MOVEDIR_UP, movedir);
- } //end if
- else if (VectorCompare(angles, VEC_DOWN))
- {
- VectorCopy(MOVEDIR_DOWN, movedir);
- } //end else if
- else
- {
- AngleVectors(angles, movedir, NULL, NULL);
- } //end else
-} //end of the function AAS_SetMovedir
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_JumpReachRunStart(aas_reachability_t *reach, vec3_t runstart)
-{
- vec3_t hordir, start, cmdmove;
- aas_clientmove_t move;
-
- //
- hordir[0] = reach->start[0] - reach->end[0];
- hordir[1] = reach->start[1] - reach->end[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //start point
- VectorCopy(reach->start, start);
- start[2] += 1;
- //get command movement
- VectorScale(hordir, 400, cmdmove);
- //
- AAS_PredictClientMovement(&move, -1, start, PRESENCE_NORMAL, qtrue,
- vec3_origin, cmdmove, 1, 2, 0.1f,
- SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|
- SE_HITGROUNDDAMAGE|SE_GAP, 0, qfalse);
- VectorCopy(move.endpos, runstart);
- //don't enter slime or lava and don't fall from too high
- if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
- {
- VectorCopy(start, runstart);
- } //end if
-} //end of the function AAS_JumpReachRunStart
-//===========================================================================
-// returns the Z velocity when rocket jumping at the origin
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_WeaponJumpZVelocity(vec3_t origin, float radiusdamage)
-{
- vec3_t kvel, v, start, end, forward, right, viewangles, dir;
- float mass, knockback, points;
- vec3_t rocketoffset = {8, 8, -8};
- vec3_t botmins = {-16, -16, -24};
- vec3_t botmaxs = {16, 16, 32};
- bsp_trace_t bsptrace;
-
- //look down (90 degrees)
- viewangles[PITCH] = 90;
- viewangles[YAW] = 0;
- viewangles[ROLL] = 0;
- //get the start point shooting from
- VectorCopy(origin, start);
- start[2] += 8; //view offset Z
- AngleVectors(viewangles, forward, right, NULL);
- start[0] += forward[0] * rocketoffset[0] + right[0] * rocketoffset[1];
- start[1] += forward[1] * rocketoffset[0] + right[1] * rocketoffset[1];
- start[2] += forward[2] * rocketoffset[0] + right[2] * rocketoffset[1] + rocketoffset[2];
- //end point of the trace
- VectorMA(start, 500, forward, end);
- //trace a line to get the impact point
- bsptrace = AAS_Trace(start, NULL, NULL, end, 1, CONTENTS_SOLID);
- //calculate the damage the bot will get from the rocket impact
- VectorAdd(botmins, botmaxs, v);
- VectorMA(origin, 0.5, v, v);
- VectorSubtract(bsptrace.endpos, v, v);
- //
- points = radiusdamage - 0.5 * VectorLength(v);
- if (points < 0) points = 0;
- //the owner of the rocket gets half the damage
- points *= 0.5;
- //mass of the bot (p_client.c: PutClientInServer)
- mass = 200;
- //knockback is the same as the damage points
- knockback = points;
- //direction of the damage (from trace.endpos to bot origin)
- VectorSubtract(origin, bsptrace.endpos, dir);
- VectorNormalize(dir);
- //damage velocity
- VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); //the rocket jump hack...
- //rocket impact velocity + jump velocity
- return kvel[2] + aassettings.phys_jumpvel;
-} //end of the function AAS_WeaponJumpZVelocity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_RocketJumpZVelocity(vec3_t origin)
-{
- //rocket radius damage is 120 (p_weapon.c: Weapon_RocketLauncher_Fire)
- return AAS_WeaponJumpZVelocity(origin, 120);
-} //end of the function AAS_RocketJumpZVelocity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_BFGJumpZVelocity(vec3_t origin)
-{
- //bfg radius damage is 1000 (p_weapon.c: weapon_bfg_fire)
- return AAS_WeaponJumpZVelocity(origin, 120);
-} //end of the function AAS_BFGJumpZVelocity
-//===========================================================================
-// applies ground friction to the given velocity
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Accelerate(vec3_t velocity, float frametime, vec3_t wishdir, float wishspeed, float accel)
-{
- // q2 style
- int i;
- float addspeed, accelspeed, currentspeed;
-
- currentspeed = DotProduct(velocity, wishdir);
- addspeed = wishspeed - currentspeed;
- if (addspeed <= 0) {
- return;
- }
- accelspeed = accel*frametime*wishspeed;
- if (accelspeed > addspeed) {
- accelspeed = addspeed;
- }
-
- for (i=0 ; i<3 ; i++) {
- velocity[i] += accelspeed*wishdir[i];
- }
-} //end of the function AAS_Accelerate
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_AirControl(vec3_t start, vec3_t end, vec3_t velocity, vec3_t cmdmove)
-{
- vec3_t dir;
-
- VectorSubtract(end, start, dir);
-} //end of the function AAS_AirControl
-//===========================================================================
-// applies ground friction to the given velocity
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ApplyFriction(vec3_t vel, float friction, float stopspeed,
- float frametime)
-{
- float speed, control, newspeed;
-
- //horizontal speed
- speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
- if (speed)
- {
- control = speed < stopspeed ? stopspeed : speed;
- newspeed = speed - frametime * control * friction;
- if (newspeed < 0) newspeed = 0;
- newspeed /= speed;
- vel[0] *= newspeed;
- vel[1] *= newspeed;
- } //end if
-} //end of the function AAS_ApplyFriction
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_ClipToBBox(aas_trace_t *trace, vec3_t start, vec3_t end, int presencetype, vec3_t mins, vec3_t maxs)
-{
- int i, j, side;
- float front, back, frac, planedist;
- vec3_t bboxmins, bboxmaxs, absmins, absmaxs, dir, mid;
-
- AAS_PresenceTypeBoundingBox(presencetype, bboxmins, bboxmaxs);
- VectorSubtract(mins, bboxmaxs, absmins);
- VectorSubtract(maxs, bboxmins, absmaxs);
- //
- VectorCopy(end, trace->endpos);
- trace->fraction = 1;
- for (i = 0; i < 3; i++)
- {
- if (start[i] < absmins[i] && end[i] < absmins[i]) return qfalse;
- if (start[i] > absmaxs[i] && end[i] > absmaxs[i]) return qfalse;
- } //end for
- //check bounding box collision
- VectorSubtract(end, start, dir);
- frac = 1;
- for (i = 0; i < 3; i++)
- {
- //get plane to test collision with for the current axis direction
- if (dir[i] > 0) planedist = absmins[i];
- else planedist = absmaxs[i];
- //calculate collision fraction
- front = start[i] - planedist;
- back = end[i] - planedist;
- frac = front / (front-back);
- //check if between bounding planes of next axis
- side = i + 1;
- if (side > 2) side = 0;
- mid[side] = start[side] + dir[side] * frac;
- if (mid[side] > absmins[side] && mid[side] < absmaxs[side])
- {
- //check if between bounding planes of next axis
- side++;
- if (side > 2) side = 0;
- mid[side] = start[side] + dir[side] * frac;
- if (mid[side] > absmins[side] && mid[side] < absmaxs[side])
- {
- mid[i] = planedist;
- break;
- } //end if
- } //end if
- } //end for
- //if there was a collision
- if (i != 3)
- {
- trace->startsolid = qfalse;
- trace->fraction = frac;
- trace->ent = 0;
- trace->planenum = 0;
- trace->area = 0;
- trace->lastarea = 0;
- //trace endpos
- for (j = 0; j < 3; j++) trace->endpos[j] = start[j] + dir[j] * frac;
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function AAS_ClipToBBox
-//===========================================================================
-// predicts the movement
-// assumes regular bounding box sizes
-// NOTE: out of water jumping is not included
-// NOTE: grappling hook is not included
-//
-// Parameter: origin : origin to start with
-// presencetype : presence type to start with
-// velocity : velocity to start with
-// cmdmove : client command movement
-// cmdframes : number of frame cmdmove is valid
-// maxframes : maximum number of predicted frames
-// frametime : duration of one predicted frame
-// stopevent : events that stop the prediction
-// stopareanum : stop as soon as entered this area
-// Returns: aas_clientmove_t
-// Changes Globals: -
-//===========================================================================
-int AAS_ClientMovementPrediction(struct aas_clientmove_s *move,
- int entnum, vec3_t origin,
- int presencetype, int onground,
- vec3_t velocity, vec3_t cmdmove,
- int cmdframes,
- int maxframes, float frametime,
- int stopevent, int stopareanum,
- vec3_t mins, vec3_t maxs, int visualize)
-{
- float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction;
- float phys_watergravity;
- float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate;
- float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity;
- float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction;
- float gravity, delta, maxvel, wishspeed, accelerate;
- //float velchange, newvel;
- int n, i, j, pc, step, swimming, ax, crouch, event, jump_frame, areanum;
- int areas[20], numareas;
- vec3_t points[20];
- vec3_t org, end, feet, start, stepend, lastorg, wishdir;
- vec3_t frame_test_vel, old_frame_test_vel, left_test_vel;
- vec3_t up = {0, 0, 1};
- aas_plane_t *plane, *plane2;
- aas_trace_t trace, steptrace;
-
- if (frametime <= 0) frametime = 0.1f;
- //
- phys_friction = aassettings.phys_friction;
- phys_stopspeed = aassettings.phys_stopspeed;
- phys_gravity = aassettings.phys_gravity;
- phys_waterfriction = aassettings.phys_waterfriction;
- phys_watergravity = aassettings.phys_watergravity;
- phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime;
- phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime;
- phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime;
- phys_walkaccelerate = aassettings.phys_walkaccelerate;
- phys_airaccelerate = aassettings.phys_airaccelerate;
- phys_swimaccelerate = aassettings.phys_swimaccelerate;
- phys_maxstep = aassettings.phys_maxstep;
- phys_maxsteepness = aassettings.phys_maxsteepness;
- phys_jumpvel = aassettings.phys_jumpvel * frametime;
- //
- Com_Memset(move, 0, sizeof(aas_clientmove_t));
- Com_Memset(&trace, 0, sizeof(aas_trace_t));
- //start at the current origin
- VectorCopy(origin, org);
- org[2] += 0.25;
- //velocity to test for the first frame
- VectorScale(velocity, frametime, frame_test_vel);
- //
- jump_frame = -1;
- //predict a maximum of 'maxframes' ahead
- for (n = 0; n < maxframes; n++)
- {
- swimming = AAS_Swimming(org);
- //get gravity depending on swimming or not
- gravity = swimming ? phys_watergravity : phys_gravity;
- //apply gravity at the START of the frame
- frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime);
- //if on the ground or swimming
- if (onground || swimming)
- {
- friction = swimming ? phys_friction : phys_waterfriction;
- //apply friction
- VectorScale(frame_test_vel, 1/frametime, frame_test_vel);
- AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime);
- VectorScale(frame_test_vel, frametime, frame_test_vel);
- } //end if
- crouch = qfalse;
- //apply command movement
- if (n < cmdframes)
- {
- ax = 0;
- maxvel = phys_maxwalkvelocity;
- accelerate = phys_airaccelerate;
- VectorCopy(cmdmove, wishdir);
- if (onground)
- {
- if (cmdmove[2] < -300)
- {
- crouch = qtrue;
- maxvel = phys_maxcrouchvelocity;
- } //end if
- //if not swimming and upmove is positive then jump
- if (!swimming && cmdmove[2] > 1)
- {
- //jump velocity minus the gravity for one frame + 5 for safety
- frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5;
- jump_frame = n;
- //jumping so air accelerate
- accelerate = phys_airaccelerate;
- } //end if
- else
- {
- accelerate = phys_walkaccelerate;
- } //end else
- ax = 2;
- } //end if
- if (swimming)
- {
- maxvel = phys_maxswimvelocity;
- accelerate = phys_swimaccelerate;
- ax = 3;
- } //end if
- else
- {
- wishdir[2] = 0;
- } //end else
- //
- wishspeed = VectorNormalize(wishdir);
- if (wishspeed > maxvel) wishspeed = maxvel;
- VectorScale(frame_test_vel, 1/frametime, frame_test_vel);
- AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate);
- VectorScale(frame_test_vel, frametime, frame_test_vel);
- /*
- for (i = 0; i < ax; i++)
- {
- velchange = (cmdmove[i] * frametime) - frame_test_vel[i];
- if (velchange > phys_maxacceleration) velchange = phys_maxacceleration;
- else if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration;
- newvel = frame_test_vel[i] + velchange;
- //
- if (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel;
- else if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel;
- else frame_test_vel[i] = newvel;
- } //end for
- */
- } //end if
- if (crouch)
- {
- presencetype = PRESENCE_CROUCH;
- } //end if
- else if (presencetype == PRESENCE_CROUCH)
- {
- if (AAS_PointPresenceType(org) & PRESENCE_NORMAL)
- {
- presencetype = PRESENCE_NORMAL;
- } //end if
- } //end else
- //save the current origin
- VectorCopy(org, lastorg);
- //move linear during one frame
- VectorCopy(frame_test_vel, left_test_vel);
- j = 0;
- do
- {
- VectorAdd(org, left_test_vel, end);
- //trace a bounding box
- trace = AAS_TraceClientBBox(org, end, presencetype, entnum);
- //
-//#ifdef AAS_MOVE_DEBUG
- if (visualize)
- {
- if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n");
- AAS_DebugLine(org, trace.endpos, LINECOLOR_RED);
- } //end if
-//#endif //AAS_MOVE_DEBUG
- //
- if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL))
- {
- numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20);
- for (i = 0; i < numareas; i++)
- {
- if (stopevent & SE_ENTERAREA)
- {
- if (areas[i] == stopareanum)
- {
- VectorCopy(points[i], move->endpos);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->endarea = areas[i];
- move->trace = trace;
- move->stopevent = SE_ENTERAREA;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- //NOTE: if not the first frame
- if ((stopevent & SE_TOUCHJUMPPAD) && n)
- {
- if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD)
- {
- VectorCopy(points[i], move->endpos);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->endarea = areas[i];
- move->trace = trace;
- move->stopevent = SE_TOUCHJUMPPAD;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- if (stopevent & SE_TOUCHTELEPORTER)
- {
- if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER)
- {
- VectorCopy(points[i], move->endpos);
- move->endarea = areas[i];
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_TOUCHTELEPORTER;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- if (stopevent & SE_TOUCHCLUSTERPORTAL)
- {
- if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- VectorCopy(points[i], move->endpos);
- move->endarea = areas[i];
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_TOUCHCLUSTERPORTAL;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- } //end for
- } //end if
- //
- if (stopevent & SE_HITBOUNDINGBOX)
- {
- if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs))
- {
- VectorCopy(trace.endpos, move->endpos);
- move->endarea = AAS_PointAreaNum(move->endpos);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_HITBOUNDINGBOX;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- //move the entity to the trace end point
- VectorCopy(trace.endpos, org);
- //if there was a collision
- if (trace.fraction < 1.0)
- {
- //get the plane the bounding box collided with
- plane = AAS_PlaneFromNum(trace.planenum);
- //
- if (stopevent & SE_HITGROUNDAREA)
- {
- if (DotProduct(plane->normal, up) > phys_maxsteepness)
- {
- VectorCopy(org, start);
- start[2] += 0.5;
- if (AAS_PointAreaNum(start) == stopareanum)
- {
- VectorCopy(start, move->endpos);
- move->endarea = stopareanum;
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_HITGROUNDAREA;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- } //end if
- //assume there's no step
- step = qfalse;
- //if it is a vertical plane and the bot didn't jump recently
- if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2))
- {
- //check for a step
- VectorMA(org, -0.25, plane->normal, start);
- VectorCopy(start, stepend);
- start[2] += phys_maxstep;
- steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum);
- //
- if (!steptrace.startsolid)
- {
- plane2 = AAS_PlaneFromNum(steptrace.planenum);
- if (DotProduct(plane2->normal, up) > phys_maxsteepness)
- {
- VectorSubtract(end, steptrace.endpos, left_test_vel);
- left_test_vel[2] = 0;
- frame_test_vel[2] = 0;
-//#ifdef AAS_MOVE_DEBUG
- if (visualize)
- {
- if (steptrace.endpos[2] - org[2] > 0.125)
- {
- VectorCopy(org, start);
- start[2] = steptrace.endpos[2];
- AAS_DebugLine(org, start, LINECOLOR_BLUE);
- } //end if
- } //end if
-//#endif //AAS_MOVE_DEBUG
- org[2] = steptrace.endpos[2];
- step = qtrue;
- } //end if
- } //end if
- } //end if
- //
- if (!step)
- {
- //velocity left to test for this frame is the projection
- //of the current test velocity into the hit plane
- VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal),
- plane->normal, left_test_vel);
- //store the old velocity for landing check
- VectorCopy(frame_test_vel, old_frame_test_vel);
- //test velocity for the next frame is the projection
- //of the velocity of the current frame into the hit plane
- VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal),
- plane->normal, frame_test_vel);
- //check for a landing on an almost horizontal floor
- if (DotProduct(plane->normal, up) > phys_maxsteepness)
- {
- onground = qtrue;
- } //end if
- if (stopevent & SE_HITGROUNDDAMAGE)
- {
- delta = 0;
- if (old_frame_test_vel[2] < 0 &&
- frame_test_vel[2] > old_frame_test_vel[2] &&
- !onground)
- {
- delta = old_frame_test_vel[2];
- } //end if
- else if (onground)
- {
- delta = frame_test_vel[2] - old_frame_test_vel[2];
- } //end else
- if (delta)
- {
- delta = delta * 10;
- delta = delta * delta * 0.0001;
- if (swimming) delta = 0;
- // never take falling damage if completely underwater
- /*
- if (ent->waterlevel == 3) return;
- if (ent->waterlevel == 2) delta *= 0.25;
- if (ent->waterlevel == 1) delta *= 0.5;
- */
- if (delta > 40)
- {
- VectorCopy(org, move->endpos);
- move->endarea = AAS_PointAreaNum(org);
- VectorCopy(frame_test_vel, move->velocity);
- move->trace = trace;
- move->stopevent = SE_HITGROUNDDAMAGE;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end if
- } //end if
- //extra check to prevent endless loop
- if (++j > 20) return qfalse;
- //while there is a plane hit
- } while(trace.fraction < 1.0);
- //if going down
- if (frame_test_vel[2] <= 10)
- {
- //check for a liquid at the feet of the bot
- VectorCopy(org, feet);
- feet[2] -= 22;
- pc = AAS_PointContents(feet);
- //get event from pc
- event = SE_NONE;
- if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA;
- if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME;
- if (pc & CONTENTS_WATER) event |= SE_ENTERWATER;
- //
- areanum = AAS_PointAreaNum(org);
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA)
- event |= SE_ENTERLAVA;
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME)
- event |= SE_ENTERSLIME;
- if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER)
- event |= SE_ENTERWATER;
- //if in lava or slime
- if (event & stopevent)
- {
- VectorCopy(org, move->endpos);
- move->endarea = areanum;
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->stopevent = event & stopevent;
- move->presencetype = presencetype;
- move->endcontents = pc;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- //
- onground = AAS_OnGround(org, presencetype, entnum);
- //if onground and on the ground for at least one whole frame
- if (onground)
- {
- if (stopevent & SE_HITGROUND)
- {
- VectorCopy(org, move->endpos);
- move->endarea = AAS_PointAreaNum(org);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_HITGROUND;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- else if (stopevent & SE_LEAVEGROUND)
- {
- VectorCopy(org, move->endpos);
- move->endarea = AAS_PointAreaNum(org);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_LEAVEGROUND;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end else if
- else if (stopevent & SE_GAP)
- {
- aas_trace_t gaptrace;
-
- VectorCopy(org, start);
- VectorCopy(start, end);
- end[2] -= 48 + aassettings.phys_maxbarrier;
- gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
- //if solid is found the bot cannot walk any further and will not fall into a gap
- if (!gaptrace.startsolid)
- {
- //if it is a gap (lower than one step height)
- if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1)
- {
- if (!(AAS_PointContents(end) & CONTENTS_WATER))
- {
- VectorCopy(lastorg, move->endpos);
- move->endarea = AAS_PointAreaNum(lastorg);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->trace = trace;
- move->stopevent = SE_GAP;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end else if
- } //end for
- //
- VectorCopy(org, move->endpos);
- move->endarea = AAS_PointAreaNum(org);
- VectorScale(frame_test_vel, 1/frametime, move->velocity);
- move->stopevent = SE_NONE;
- move->presencetype = presencetype;
- move->endcontents = 0;
- move->time = n * frametime;
- move->frames = n;
- //
- return qtrue;
-} //end of the function AAS_ClientMovementPrediction
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PredictClientMovement(struct aas_clientmove_s *move,
- int entnum, vec3_t origin,
- int presencetype, int onground,
- vec3_t velocity, vec3_t cmdmove,
- int cmdframes,
- int maxframes, float frametime,
- int stopevent, int stopareanum, int visualize)
-{
- vec3_t mins, maxs;
- return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground,
- velocity, cmdmove, cmdframes, maxframes,
- frametime, stopevent, stopareanum,
- mins, maxs, visualize);
-} //end of the function AAS_PredictClientMovement
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move,
- int entnum, vec3_t origin,
- int presencetype, int onground,
- vec3_t velocity, vec3_t cmdmove,
- int cmdframes,
- int maxframes, float frametime,
- vec3_t mins, vec3_t maxs, int visualize)
-{
- return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground,
- velocity, cmdmove, cmdframes, maxframes,
- frametime, SE_HITBOUNDINGBOX, 0,
- mins, maxs, visualize);
-} //end of the function AAS_ClientMovementHitBBox
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir)
-{
- vec3_t velocity, cmdmove;
- aas_clientmove_t move;
-
- VectorClear(velocity);
- if (!AAS_Swimming(origin)) dir[2] = 0;
- VectorNormalize(dir);
- VectorScale(dir, 400, cmdmove);
- cmdmove[2] = 224;
- AAS_ClearShownDebugLines();
- AAS_PredictClientMovement(&move, entnum, origin, PRESENCE_NORMAL, qtrue,
- velocity, cmdmove, 13, 13, 0.1f, SE_HITGROUND, 0, qtrue);//SE_LEAVEGROUND);
- if (move.stopevent & SE_LEAVEGROUND)
- {
- botimport.Print(PRT_MESSAGE, "leave ground\n");
- } //end if
-} //end of the function TestMovementPrediction
-//===========================================================================
-// calculates the horizontal velocity needed to perform a jump from start
-// to end
-//
-// Parameter: zvel : z velocity for jump
-// start : start position of jump
-// end : end position of jump
-// *speed : returned speed for jump
-// Returns: qfalse if too high or too far from start to end
-// Changes Globals: -
-//===========================================================================
-int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity)
-{
- float phys_gravity, phys_maxvelocity;
- float maxjump, height2fall, t, top;
- vec3_t dir;
-
- phys_gravity = aassettings.phys_gravity;
- phys_maxvelocity = aassettings.phys_maxvelocity;
-
- //maximum height a player can jump with the given initial z velocity
- maxjump = 0.5 * phys_gravity * (zvel / phys_gravity) * (zvel / phys_gravity);
- //top of the parabolic jump
- top = start[2] + maxjump;
- //height the bot will fall from the top
- height2fall = top - end[2];
- //if the goal is to high to jump to
- if (height2fall < 0)
- {
- *velocity = phys_maxvelocity;
- return 0;
- } //end if
- //time a player takes to fall the height
- t = sqrt(height2fall / (0.5 * phys_gravity));
- //direction from start to end
- VectorSubtract(end, start, dir);
- //
- if ( (t + zvel / phys_gravity) == 0.0f ) {
- *velocity = phys_maxvelocity;
- return 0;
- }
- //calculate horizontal speed
- *velocity = sqrt(dir[0]*dir[0] + dir[1]*dir[1]) / (t + zvel / phys_gravity);
- //the horizontal speed must be lower than the max speed
- if (*velocity > phys_maxvelocity)
- {
- *velocity = phys_maxvelocity;
- return 0;
- } //end if
- return 1;
-} //end of the function AAS_HorizontalVelocityForJump
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_move.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_move.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +aas_settings_t aassettings; + +//#define AAS_MOVE_DEBUG + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs) +{ + vec3_t end; + bsp_trace_t trace; + + VectorCopy(origin, end); + end[2] -= 100; + trace = AAS_Trace(origin, mins, maxs, end, 0, CONTENTS_SOLID); + if (trace.startsolid) return qfalse; + VectorCopy(trace.endpos, origin); + return qtrue; +} //end of the function AAS_DropToFloor +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitSettings(void) +{ + aassettings.phys_gravitydirection[0] = 0; + aassettings.phys_gravitydirection[1] = 0; + aassettings.phys_gravitydirection[2] = -1; + aassettings.phys_friction = LibVarValue("phys_friction", "6"); + aassettings.phys_stopspeed = LibVarValue("phys_stopspeed", "100"); + aassettings.phys_gravity = LibVarValue("phys_gravity", "800"); + aassettings.phys_waterfriction = LibVarValue("phys_waterfriction", "1"); + aassettings.phys_watergravity = LibVarValue("phys_watergravity", "400"); + aassettings.phys_maxvelocity = LibVarValue("phys_maxvelocity", "320"); + aassettings.phys_maxwalkvelocity = LibVarValue("phys_maxwalkvelocity", "320"); + aassettings.phys_maxcrouchvelocity = LibVarValue("phys_maxcrouchvelocity", "100"); + aassettings.phys_maxswimvelocity = LibVarValue("phys_maxswimvelocity", "150"); + aassettings.phys_walkaccelerate = LibVarValue("phys_walkaccelerate", "10"); + aassettings.phys_airaccelerate = LibVarValue("phys_airaccelerate", "1"); + aassettings.phys_swimaccelerate = LibVarValue("phys_swimaccelerate", "4"); + aassettings.phys_maxstep = LibVarValue("phys_maxstep", "19"); + aassettings.phys_maxsteepness = LibVarValue("phys_maxsteepness", "0.7"); + aassettings.phys_maxwaterjump = LibVarValue("phys_maxwaterjump", "18"); + aassettings.phys_maxbarrier = LibVarValue("phys_maxbarrier", "33"); + aassettings.phys_jumpvel = LibVarValue("phys_jumpvel", "270"); + aassettings.phys_falldelta5 = LibVarValue("phys_falldelta5", "40"); + aassettings.phys_falldelta10 = LibVarValue("phys_falldelta10", "60"); + aassettings.rs_waterjump = LibVarValue("rs_waterjump", "400"); + aassettings.rs_teleport = LibVarValue("rs_teleport", "50"); + aassettings.rs_barrierjump = LibVarValue("rs_barrierjump", "100"); + aassettings.rs_startcrouch = LibVarValue("rs_startcrouch", "300"); + aassettings.rs_startgrapple = LibVarValue("rs_startgrapple", "500"); + aassettings.rs_startwalkoffledge = LibVarValue("rs_startwalkoffledge", "70"); + aassettings.rs_startjump = LibVarValue("rs_startjump", "300"); + aassettings.rs_rocketjump = LibVarValue("rs_rocketjump", "500"); + aassettings.rs_bfgjump = LibVarValue("rs_bfgjump", "500"); + aassettings.rs_jumppad = LibVarValue("rs_jumppad", "250"); + aassettings.rs_aircontrolledjumppad = LibVarValue("rs_aircontrolledjumppad", "300"); + aassettings.rs_funcbob = LibVarValue("rs_funcbob", "300"); + aassettings.rs_startelevator = LibVarValue("rs_startelevator", "50"); + aassettings.rs_falldamage5 = LibVarValue("rs_falldamage5", "300"); + aassettings.rs_falldamage10 = LibVarValue("rs_falldamage10", "500"); + aassettings.rs_maxfallheight = LibVarValue("rs_maxfallheight", "0"); + aassettings.rs_maxjumpfallheight = LibVarValue("rs_maxjumpfallheight", "450"); +} //end of the function AAS_InitSettings +//=========================================================================== +// returns qtrue if the bot is against a ladder +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AgainstLadder(vec3_t origin) +{ + int areanum, i, facenum, side; + vec3_t org; + aas_plane_t *plane; + aas_face_t *face; + aas_area_t *area; + + VectorCopy(origin, org); + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[0] += 1; + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[1] += 1; + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[0] -= 2; + areanum = AAS_PointAreaNum(org); + if (!areanum) + { + org[1] -= 2; + areanum = AAS_PointAreaNum(org); + } //end if + } //end if + } //end if + } //end if + //if in solid... wrrr shouldn't happen + if (!areanum) return qfalse; + //if not in a ladder area + if (!(aasworld.areasettings[areanum].areaflags & AREA_LADDER)) return qfalse; + //if a crouch only area + if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qfalse; + // + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + side = facenum < 0; + face = &aasworld.faces[abs(facenum)]; + //if the face isn't a ladder face + if (!(face->faceflags & FACE_LADDER)) continue; + //get the plane the face is in + plane = &aasworld.planes[face->planenum ^ side]; + //if the origin is pretty close to the plane + if (abs(DotProduct(plane->normal, origin) - plane->dist) < 3) + { + if (AAS_PointInsideFace(abs(facenum), origin, 0.1f)) return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function AAS_AgainstLadder +//=========================================================================== +// returns qtrue if the bot is on the ground +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OnGround(vec3_t origin, int presencetype, int passent) +{ + aas_trace_t trace; + vec3_t end, up = {0, 0, 1}; + aas_plane_t *plane; + + VectorCopy(origin, end); + end[2] -= 10; + + trace = AAS_TraceClientBBox(origin, end, presencetype, passent); + + //if in solid + if (trace.startsolid) return qfalse; + //if nothing hit at all + if (trace.fraction >= 1.0) return qfalse; + //if too far from the hit plane + if (origin[2] - trace.endpos[2] > 10) return qfalse; + //check if the plane isn't too steep + plane = AAS_PlaneFromNum(trace.planenum); + if (DotProduct(plane->normal, up) < aassettings.phys_maxsteepness) return qfalse; + //the bot is on the ground + return qtrue; +} //end of the function AAS_OnGround +//=========================================================================== +// returns qtrue if a bot at the given position is swimming +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Swimming(vec3_t origin) +{ + vec3_t testorg; + + VectorCopy(origin, testorg); + testorg[2] -= 2; + if (AAS_PointContents(testorg) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) return qtrue; + return qfalse; +} //end of the function AAS_Swimming +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static vec3_t VEC_UP = {0, -1, 0}; +static vec3_t MOVEDIR_UP = {0, 0, 1}; +static vec3_t VEC_DOWN = {0, -2, 0}; +static vec3_t MOVEDIR_DOWN = {0, 0, -1}; + +void AAS_SetMovedir(vec3_t angles, vec3_t movedir) +{ + if (VectorCompare(angles, VEC_UP)) + { + VectorCopy(MOVEDIR_UP, movedir); + } //end if + else if (VectorCompare(angles, VEC_DOWN)) + { + VectorCopy(MOVEDIR_DOWN, movedir); + } //end else if + else + { + AngleVectors(angles, movedir, NULL, NULL); + } //end else +} //end of the function AAS_SetMovedir +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_JumpReachRunStart(aas_reachability_t *reach, vec3_t runstart) +{ + vec3_t hordir, start, cmdmove; + aas_clientmove_t move; + + // + hordir[0] = reach->start[0] - reach->end[0]; + hordir[1] = reach->start[1] - reach->end[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //start point + VectorCopy(reach->start, start); + start[2] += 1; + //get command movement + VectorScale(hordir, 400, cmdmove); + // + AAS_PredictClientMovement(&move, -1, start, PRESENCE_NORMAL, qtrue, + vec3_origin, cmdmove, 1, 2, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA| + SE_HITGROUNDDAMAGE|SE_GAP, 0, qfalse); + VectorCopy(move.endpos, runstart); + //don't enter slime or lava and don't fall from too high + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + { + VectorCopy(start, runstart); + } //end if +} //end of the function AAS_JumpReachRunStart +//=========================================================================== +// returns the Z velocity when rocket jumping at the origin +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_WeaponJumpZVelocity(vec3_t origin, float radiusdamage) +{ + vec3_t kvel, v, start, end, forward, right, viewangles, dir; + float mass, knockback, points; + vec3_t rocketoffset = {8, 8, -8}; + vec3_t botmins = {-16, -16, -24}; + vec3_t botmaxs = {16, 16, 32}; + bsp_trace_t bsptrace; + + //look down (90 degrees) + viewangles[PITCH] = 90; + viewangles[YAW] = 0; + viewangles[ROLL] = 0; + //get the start point shooting from + VectorCopy(origin, start); + start[2] += 8; //view offset Z + AngleVectors(viewangles, forward, right, NULL); + start[0] += forward[0] * rocketoffset[0] + right[0] * rocketoffset[1]; + start[1] += forward[1] * rocketoffset[0] + right[1] * rocketoffset[1]; + start[2] += forward[2] * rocketoffset[0] + right[2] * rocketoffset[1] + rocketoffset[2]; + //end point of the trace + VectorMA(start, 500, forward, end); + //trace a line to get the impact point + bsptrace = AAS_Trace(start, NULL, NULL, end, 1, CONTENTS_SOLID); + //calculate the damage the bot will get from the rocket impact + VectorAdd(botmins, botmaxs, v); + VectorMA(origin, 0.5, v, v); + VectorSubtract(bsptrace.endpos, v, v); + // + points = radiusdamage - 0.5 * VectorLength(v); + if (points < 0) points = 0; + //the owner of the rocket gets half the damage + points *= 0.5; + //mass of the bot (p_client.c: PutClientInServer) + mass = 200; + //knockback is the same as the damage points + knockback = points; + //direction of the damage (from trace.endpos to bot origin) + VectorSubtract(origin, bsptrace.endpos, dir); + VectorNormalize(dir); + //damage velocity + VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); //the rocket jump hack... + //rocket impact velocity + jump velocity + return kvel[2] + aassettings.phys_jumpvel; +} //end of the function AAS_WeaponJumpZVelocity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_RocketJumpZVelocity(vec3_t origin) +{ + //rocket radius damage is 120 (p_weapon.c: Weapon_RocketLauncher_Fire) + return AAS_WeaponJumpZVelocity(origin, 120); +} //end of the function AAS_RocketJumpZVelocity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_BFGJumpZVelocity(vec3_t origin) +{ + //bfg radius damage is 1000 (p_weapon.c: weapon_bfg_fire) + return AAS_WeaponJumpZVelocity(origin, 120); +} //end of the function AAS_BFGJumpZVelocity +//=========================================================================== +// applies ground friction to the given velocity +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Accelerate(vec3_t velocity, float frametime, vec3_t wishdir, float wishspeed, float accel) +{ + // q2 style + int i; + float addspeed, accelspeed, currentspeed; + + currentspeed = DotProduct(velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) { + return; + } + accelspeed = accel*frametime*wishspeed; + if (accelspeed > addspeed) { + accelspeed = addspeed; + } + + for (i=0 ; i<3 ; i++) { + velocity[i] += accelspeed*wishdir[i]; + } +} //end of the function AAS_Accelerate +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AirControl(vec3_t start, vec3_t end, vec3_t velocity, vec3_t cmdmove) +{ + vec3_t dir; + + VectorSubtract(end, start, dir); +} //end of the function AAS_AirControl +//=========================================================================== +// applies ground friction to the given velocity +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ApplyFriction(vec3_t vel, float friction, float stopspeed, + float frametime) +{ + float speed, control, newspeed; + + //horizontal speed + speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); + if (speed) + { + control = speed < stopspeed ? stopspeed : speed; + newspeed = speed - frametime * control * friction; + if (newspeed < 0) newspeed = 0; + newspeed /= speed; + vel[0] *= newspeed; + vel[1] *= newspeed; + } //end if +} //end of the function AAS_ApplyFriction +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ClipToBBox(aas_trace_t *trace, vec3_t start, vec3_t end, int presencetype, vec3_t mins, vec3_t maxs) +{ + int i, j, side; + float front, back, frac, planedist; + vec3_t bboxmins, bboxmaxs, absmins, absmaxs, dir, mid; + + AAS_PresenceTypeBoundingBox(presencetype, bboxmins, bboxmaxs); + VectorSubtract(mins, bboxmaxs, absmins); + VectorSubtract(maxs, bboxmins, absmaxs); + // + VectorCopy(end, trace->endpos); + trace->fraction = 1; + for (i = 0; i < 3; i++) + { + if (start[i] < absmins[i] && end[i] < absmins[i]) return qfalse; + if (start[i] > absmaxs[i] && end[i] > absmaxs[i]) return qfalse; + } //end for + //check bounding box collision + VectorSubtract(end, start, dir); + frac = 1; + for (i = 0; i < 3; i++) + { + //get plane to test collision with for the current axis direction + if (dir[i] > 0) planedist = absmins[i]; + else planedist = absmaxs[i]; + //calculate collision fraction + front = start[i] - planedist; + back = end[i] - planedist; + frac = front / (front-back); + //check if between bounding planes of next axis + side = i + 1; + if (side > 2) side = 0; + mid[side] = start[side] + dir[side] * frac; + if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) + { + //check if between bounding planes of next axis + side++; + if (side > 2) side = 0; + mid[side] = start[side] + dir[side] * frac; + if (mid[side] > absmins[side] && mid[side] < absmaxs[side]) + { + mid[i] = planedist; + break; + } //end if + } //end if + } //end for + //if there was a collision + if (i != 3) + { + trace->startsolid = qfalse; + trace->fraction = frac; + trace->ent = 0; + trace->planenum = 0; + trace->area = 0; + trace->lastarea = 0; + //trace endpos + for (j = 0; j < 3; j++) trace->endpos[j] = start[j] + dir[j] * frac; + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_ClipToBBox +//=========================================================================== +// predicts the movement +// assumes regular bounding box sizes +// NOTE: out of water jumping is not included +// NOTE: grappling hook is not included +// +// Parameter: origin : origin to start with +// presencetype : presence type to start with +// velocity : velocity to start with +// cmdmove : client command movement +// cmdframes : number of frame cmdmove is valid +// maxframes : maximum number of predicted frames +// frametime : duration of one predicted frame +// stopevent : events that stop the prediction +// stopareanum : stop as soon as entered this area +// Returns: aas_clientmove_t +// Changes Globals: - +//=========================================================================== +int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, + vec3_t mins, vec3_t maxs, int visualize) +{ + float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction; + float phys_watergravity; + float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate; + float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity; + float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction; + float gravity, delta, maxvel, wishspeed, accelerate; + //float velchange, newvel; + int n, i, j, pc, step, swimming, ax, crouch, event, jump_frame, areanum; + int areas[20], numareas; + vec3_t points[20]; + vec3_t org, end, feet, start, stepend, lastorg, wishdir; + vec3_t frame_test_vel, old_frame_test_vel, left_test_vel; + vec3_t up = {0, 0, 1}; + aas_plane_t *plane, *plane2; + aas_trace_t trace, steptrace; + + if (frametime <= 0) frametime = 0.1f; + // + phys_friction = aassettings.phys_friction; + phys_stopspeed = aassettings.phys_stopspeed; + phys_gravity = aassettings.phys_gravity; + phys_waterfriction = aassettings.phys_waterfriction; + phys_watergravity = aassettings.phys_watergravity; + phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime; + phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime; + phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime; + phys_walkaccelerate = aassettings.phys_walkaccelerate; + phys_airaccelerate = aassettings.phys_airaccelerate; + phys_swimaccelerate = aassettings.phys_swimaccelerate; + phys_maxstep = aassettings.phys_maxstep; + phys_maxsteepness = aassettings.phys_maxsteepness; + phys_jumpvel = aassettings.phys_jumpvel * frametime; + // + Com_Memset(move, 0, sizeof(aas_clientmove_t)); + Com_Memset(&trace, 0, sizeof(aas_trace_t)); + //start at the current origin + VectorCopy(origin, org); + org[2] += 0.25; + //velocity to test for the first frame + VectorScale(velocity, frametime, frame_test_vel); + // + jump_frame = -1; + //predict a maximum of 'maxframes' ahead + for (n = 0; n < maxframes; n++) + { + swimming = AAS_Swimming(org); + //get gravity depending on swimming or not + gravity = swimming ? phys_watergravity : phys_gravity; + //apply gravity at the START of the frame + frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime); + //if on the ground or swimming + if (onground || swimming) + { + friction = swimming ? phys_friction : phys_waterfriction; + //apply friction + VectorScale(frame_test_vel, 1/frametime, frame_test_vel); + AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); + VectorScale(frame_test_vel, frametime, frame_test_vel); + } //end if + crouch = qfalse; + //apply command movement + if (n < cmdframes) + { + ax = 0; + maxvel = phys_maxwalkvelocity; + accelerate = phys_airaccelerate; + VectorCopy(cmdmove, wishdir); + if (onground) + { + if (cmdmove[2] < -300) + { + crouch = qtrue; + maxvel = phys_maxcrouchvelocity; + } //end if + //if not swimming and upmove is positive then jump + if (!swimming && cmdmove[2] > 1) + { + //jump velocity minus the gravity for one frame + 5 for safety + frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5; + jump_frame = n; + //jumping so air accelerate + accelerate = phys_airaccelerate; + } //end if + else + { + accelerate = phys_walkaccelerate; + } //end else + ax = 2; + } //end if + if (swimming) + { + maxvel = phys_maxswimvelocity; + accelerate = phys_swimaccelerate; + ax = 3; + } //end if + else + { + wishdir[2] = 0; + } //end else + // + wishspeed = VectorNormalize(wishdir); + if (wishspeed > maxvel) wishspeed = maxvel; + VectorScale(frame_test_vel, 1/frametime, frame_test_vel); + AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate); + VectorScale(frame_test_vel, frametime, frame_test_vel); + /* + for (i = 0; i < ax; i++) + { + velchange = (cmdmove[i] * frametime) - frame_test_vel[i]; + if (velchange > phys_maxacceleration) velchange = phys_maxacceleration; + else if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration; + newvel = frame_test_vel[i] + velchange; + // + if (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel; + else if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel; + else frame_test_vel[i] = newvel; + } //end for + */ + } //end if + if (crouch) + { + presencetype = PRESENCE_CROUCH; + } //end if + else if (presencetype == PRESENCE_CROUCH) + { + if (AAS_PointPresenceType(org) & PRESENCE_NORMAL) + { + presencetype = PRESENCE_NORMAL; + } //end if + } //end else + //save the current origin + VectorCopy(org, lastorg); + //move linear during one frame + VectorCopy(frame_test_vel, left_test_vel); + j = 0; + do + { + VectorAdd(org, left_test_vel, end); + //trace a bounding box + trace = AAS_TraceClientBBox(org, end, presencetype, entnum); + // +//#ifdef AAS_MOVE_DEBUG + if (visualize) + { + if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n"); + AAS_DebugLine(org, trace.endpos, LINECOLOR_RED); + } //end if +//#endif //AAS_MOVE_DEBUG + // + if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL)) + { + numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); + for (i = 0; i < numareas; i++) + { + if (stopevent & SE_ENTERAREA) + { + if (areas[i] == stopareanum) + { + VectorCopy(points[i], move->endpos); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->endarea = areas[i]; + move->trace = trace; + move->stopevent = SE_ENTERAREA; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + //NOTE: if not the first frame + if ((stopevent & SE_TOUCHJUMPPAD) && n) + { + if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD) + { + VectorCopy(points[i], move->endpos); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->endarea = areas[i]; + move->trace = trace; + move->stopevent = SE_TOUCHJUMPPAD; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + if (stopevent & SE_TOUCHTELEPORTER) + { + if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER) + { + VectorCopy(points[i], move->endpos); + move->endarea = areas[i]; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_TOUCHTELEPORTER; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + if (stopevent & SE_TOUCHCLUSTERPORTAL) + { + if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL) + { + VectorCopy(points[i], move->endpos); + move->endarea = areas[i]; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_TOUCHCLUSTERPORTAL; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end for + } //end if + // + if (stopevent & SE_HITBOUNDINGBOX) + { + if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs)) + { + VectorCopy(trace.endpos, move->endpos); + move->endarea = AAS_PointAreaNum(move->endpos); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITBOUNDINGBOX; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + //move the entity to the trace end point + VectorCopy(trace.endpos, org); + //if there was a collision + if (trace.fraction < 1.0) + { + //get the plane the bounding box collided with + plane = AAS_PlaneFromNum(trace.planenum); + // + if (stopevent & SE_HITGROUNDAREA) + { + if (DotProduct(plane->normal, up) > phys_maxsteepness) + { + VectorCopy(org, start); + start[2] += 0.5; + if (AAS_PointAreaNum(start) == stopareanum) + { + VectorCopy(start, move->endpos); + move->endarea = stopareanum; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUNDAREA; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + //assume there's no step + step = qfalse; + //if it is a vertical plane and the bot didn't jump recently + if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2)) + { + //check for a step + VectorMA(org, -0.25, plane->normal, start); + VectorCopy(start, stepend); + start[2] += phys_maxstep; + steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum); + // + if (!steptrace.startsolid) + { + plane2 = AAS_PlaneFromNum(steptrace.planenum); + if (DotProduct(plane2->normal, up) > phys_maxsteepness) + { + VectorSubtract(end, steptrace.endpos, left_test_vel); + left_test_vel[2] = 0; + frame_test_vel[2] = 0; +//#ifdef AAS_MOVE_DEBUG + if (visualize) + { + if (steptrace.endpos[2] - org[2] > 0.125) + { + VectorCopy(org, start); + start[2] = steptrace.endpos[2]; + AAS_DebugLine(org, start, LINECOLOR_BLUE); + } //end if + } //end if +//#endif //AAS_MOVE_DEBUG + org[2] = steptrace.endpos[2]; + step = qtrue; + } //end if + } //end if + } //end if + // + if (!step) + { + //velocity left to test for this frame is the projection + //of the current test velocity into the hit plane + VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal), + plane->normal, left_test_vel); + //store the old velocity for landing check + VectorCopy(frame_test_vel, old_frame_test_vel); + //test velocity for the next frame is the projection + //of the velocity of the current frame into the hit plane + VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal), + plane->normal, frame_test_vel); + //check for a landing on an almost horizontal floor + if (DotProduct(plane->normal, up) > phys_maxsteepness) + { + onground = qtrue; + } //end if + if (stopevent & SE_HITGROUNDDAMAGE) + { + delta = 0; + if (old_frame_test_vel[2] < 0 && + frame_test_vel[2] > old_frame_test_vel[2] && + !onground) + { + delta = old_frame_test_vel[2]; + } //end if + else if (onground) + { + delta = frame_test_vel[2] - old_frame_test_vel[2]; + } //end else + if (delta) + { + delta = delta * 10; + delta = delta * delta * 0.0001; + if (swimming) delta = 0; + // never take falling damage if completely underwater + /* + if (ent->waterlevel == 3) return; + if (ent->waterlevel == 2) delta *= 0.25; + if (ent->waterlevel == 1) delta *= 0.5; + */ + if (delta > 40) + { + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorCopy(frame_test_vel, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUNDDAMAGE; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + } //end if + } //end if + //extra check to prevent endless loop + if (++j > 20) return qfalse; + //while there is a plane hit + } while(trace.fraction < 1.0); + //if going down + if (frame_test_vel[2] <= 10) + { + //check for a liquid at the feet of the bot + VectorCopy(org, feet); + feet[2] -= 22; + pc = AAS_PointContents(feet); + //get event from pc + event = SE_NONE; + if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA; + if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME; + if (pc & CONTENTS_WATER) event |= SE_ENTERWATER; + // + areanum = AAS_PointAreaNum(org); + if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA) + event |= SE_ENTERLAVA; + if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME) + event |= SE_ENTERSLIME; + if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER) + event |= SE_ENTERWATER; + //if in lava or slime + if (event & stopevent) + { + VectorCopy(org, move->endpos); + move->endarea = areanum; + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->stopevent = event & stopevent; + move->presencetype = presencetype; + move->endcontents = pc; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + // + onground = AAS_OnGround(org, presencetype, entnum); + //if onground and on the ground for at least one whole frame + if (onground) + { + if (stopevent & SE_HITGROUND) + { + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_HITGROUND; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + else if (stopevent & SE_LEAVEGROUND) + { + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_LEAVEGROUND; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end else if + else if (stopevent & SE_GAP) + { + aas_trace_t gaptrace; + + VectorCopy(org, start); + VectorCopy(start, end); + end[2] -= 48 + aassettings.phys_maxbarrier; + gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + //if solid is found the bot cannot walk any further and will not fall into a gap + if (!gaptrace.startsolid) + { + //if it is a gap (lower than one step height) + if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1) + { + if (!(AAS_PointContents(end) & CONTENTS_WATER)) + { + VectorCopy(lastorg, move->endpos); + move->endarea = AAS_PointAreaNum(lastorg); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->trace = trace; + move->stopevent = SE_GAP; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + return qtrue; + } //end if + } //end if + } //end if + } //end else if + } //end for + // + VectorCopy(org, move->endpos); + move->endarea = AAS_PointAreaNum(org); + VectorScale(frame_test_vel, 1/frametime, move->velocity); + move->stopevent = SE_NONE; + move->presencetype = presencetype; + move->endcontents = 0; + move->time = n * frametime; + move->frames = n; + // + return qtrue; +} //end of the function AAS_ClientMovementPrediction +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PredictClientMovement(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, int visualize) +{ + vec3_t mins, maxs; + return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, + velocity, cmdmove, cmdframes, maxframes, + frametime, stopevent, stopareanum, + mins, maxs, visualize); +} //end of the function AAS_PredictClientMovement +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + vec3_t mins, vec3_t maxs, int visualize) +{ + return AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground, + velocity, cmdmove, cmdframes, maxframes, + frametime, SE_HITBOUNDINGBOX, 0, + mins, maxs, visualize); +} //end of the function AAS_ClientMovementHitBBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir) +{ + vec3_t velocity, cmdmove; + aas_clientmove_t move; + + VectorClear(velocity); + if (!AAS_Swimming(origin)) dir[2] = 0; + VectorNormalize(dir); + VectorScale(dir, 400, cmdmove); + cmdmove[2] = 224; + AAS_ClearShownDebugLines(); + AAS_PredictClientMovement(&move, entnum, origin, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 13, 13, 0.1f, SE_HITGROUND, 0, qtrue);//SE_LEAVEGROUND); + if (move.stopevent & SE_LEAVEGROUND) + { + botimport.Print(PRT_MESSAGE, "leave ground\n"); + } //end if +} //end of the function TestMovementPrediction +//=========================================================================== +// calculates the horizontal velocity needed to perform a jump from start +// to end +// +// Parameter: zvel : z velocity for jump +// start : start position of jump +// end : end position of jump +// *speed : returned speed for jump +// Returns: qfalse if too high or too far from start to end +// Changes Globals: - +//=========================================================================== +int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity) +{ + float phys_gravity, phys_maxvelocity; + float maxjump, height2fall, t, top; + vec3_t dir; + + phys_gravity = aassettings.phys_gravity; + phys_maxvelocity = aassettings.phys_maxvelocity; + + //maximum height a player can jump with the given initial z velocity + maxjump = 0.5 * phys_gravity * (zvel / phys_gravity) * (zvel / phys_gravity); + //top of the parabolic jump + top = start[2] + maxjump; + //height the bot will fall from the top + height2fall = top - end[2]; + //if the goal is to high to jump to + if (height2fall < 0) + { + *velocity = phys_maxvelocity; + return 0; + } //end if + //time a player takes to fall the height + t = sqrt(height2fall / (0.5 * phys_gravity)); + //direction from start to end + VectorSubtract(end, start, dir); + // + if ( (t + zvel / phys_gravity) == 0.0f ) { + *velocity = phys_maxvelocity; + return 0; + } + //calculate horizontal speed + *velocity = sqrt(dir[0]*dir[0] + dir[1]*dir[1]) / (t + zvel / phys_gravity); + //the horizontal speed must be lower than the max speed + if (*velocity > phys_maxvelocity) + { + *velocity = phys_maxvelocity; + return 0; + } //end if + return 1; +} //end of the function AAS_HorizontalVelocityForJump diff --git a/code/botlib/be_aas_move.h b/code/botlib/be_aas_move.h index 5b491a4..5705e46 100755 --- a/code/botlib/be_aas_move.h +++ b/code/botlib/be_aas_move.h @@ -1,71 +1,71 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_move.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_move.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-extern aas_settings_t aassettings;
-#endif //AASINTERN
-
-//movement prediction
-int AAS_PredictClientMovement(struct aas_clientmove_s *move,
- int entnum, vec3_t origin,
- int presencetype, int onground,
- vec3_t velocity, vec3_t cmdmove,
- int cmdframes,
- int maxframes, float frametime,
- int stopevent, int stopareanum, int visualize);
-//predict movement until bounding box is hit
-int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move,
- int entnum, vec3_t origin,
- int presencetype, int onground,
- vec3_t velocity, vec3_t cmdmove,
- int cmdframes,
- int maxframes, float frametime,
- vec3_t mins, vec3_t maxs, int visualize);
-//returns true if on the ground at the given origin
-int AAS_OnGround(vec3_t origin, int presencetype, int passent);
-//returns true if swimming at the given origin
-int AAS_Swimming(vec3_t origin);
-//returns the jump reachability run start point
-void AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart);
-//returns true if against a ladder at the given origin
-int AAS_AgainstLadder(vec3_t origin);
-//rocket jump Z velocity when rocket-jumping at origin
-float AAS_RocketJumpZVelocity(vec3_t origin);
-//bfg jump Z velocity when bfg-jumping at origin
-float AAS_BFGJumpZVelocity(vec3_t origin);
-//calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated
-int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity);
-//
-void AAS_SetMovedir(vec3_t angles, vec3_t movedir);
-//
-int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs);
-//
-void AAS_InitSettings(void);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_move.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_move.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +extern aas_settings_t aassettings; +#endif //AASINTERN + +//movement prediction +int AAS_PredictClientMovement(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + int stopevent, int stopareanum, int visualize); +//predict movement until bounding box is hit +int AAS_ClientMovementHitBBox(struct aas_clientmove_s *move, + int entnum, vec3_t origin, + int presencetype, int onground, + vec3_t velocity, vec3_t cmdmove, + int cmdframes, + int maxframes, float frametime, + vec3_t mins, vec3_t maxs, int visualize); +//returns true if on the ground at the given origin +int AAS_OnGround(vec3_t origin, int presencetype, int passent); +//returns true if swimming at the given origin +int AAS_Swimming(vec3_t origin); +//returns the jump reachability run start point +void AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart); +//returns true if against a ladder at the given origin +int AAS_AgainstLadder(vec3_t origin); +//rocket jump Z velocity when rocket-jumping at origin +float AAS_RocketJumpZVelocity(vec3_t origin); +//bfg jump Z velocity when bfg-jumping at origin +float AAS_BFGJumpZVelocity(vec3_t origin); +//calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated +int AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity); +// +void AAS_SetMovedir(vec3_t angles, vec3_t movedir); +// +int AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs); +// +void AAS_InitSettings(void); diff --git a/code/botlib/be_aas_optimize.c b/code/botlib/be_aas_optimize.c index f420fd1..605dc4d 100755 --- a/code/botlib/be_aas_optimize.c +++ b/code/botlib/be_aas_optimize.c @@ -1,312 +1,312 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_optimize.c
- *
- * desc: decreases the .aas file size after the reachabilities have
- * been calculated, just dumps all the faces, edges and vertexes
- *
- * $Archive: /MissionPack/code/botlib/be_aas_optimize.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_libvar.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_aas_def.h"
-
-typedef struct optimized_s
-{
- //vertexes
- int numvertexes;
- aas_vertex_t *vertexes;
- //edges
- int numedges;
- aas_edge_t *edges;
- //edge index
- int edgeindexsize;
- aas_edgeindex_t *edgeindex;
- //faces
- int numfaces;
- aas_face_t *faces;
- //face index
- int faceindexsize;
- aas_faceindex_t *faceindex;
- //convex areas
- int numareas;
- aas_area_t *areas;
- //
- int *vertexoptimizeindex;
- int *edgeoptimizeindex;
- int *faceoptimizeindex;
-} optimized_t;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_KeepEdge(aas_edge_t *edge)
-{
- return 1;
-} //end of the function AAS_KeepFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_OptimizeEdge(optimized_t *optimized, int edgenum)
-{
- int i, optedgenum;
- aas_edge_t *edge, *optedge;
-
- edge = &aasworld.edges[abs(edgenum)];
- if (!AAS_KeepEdge(edge)) return 0;
-
- optedgenum = optimized->edgeoptimizeindex[abs(edgenum)];
- if (optedgenum)
- {
- //keep the edge reversed sign
- if (edgenum > 0) return optedgenum;
- else return -optedgenum;
- } //end if
-
- optedge = &optimized->edges[optimized->numedges];
-
- for (i = 0; i < 2; i++)
- {
- if (optimized->vertexoptimizeindex[edge->v[i]])
- {
- optedge->v[i] = optimized->vertexoptimizeindex[edge->v[i]];
- } //end if
- else
- {
- VectorCopy(aasworld.vertexes[edge->v[i]], optimized->vertexes[optimized->numvertexes]);
- optedge->v[i] = optimized->numvertexes;
- optimized->vertexoptimizeindex[edge->v[i]] = optimized->numvertexes;
- optimized->numvertexes++;
- } //end else
- } //end for
- optimized->edgeoptimizeindex[abs(edgenum)] = optimized->numedges;
- optedgenum = optimized->numedges;
- optimized->numedges++;
- //keep the edge reversed sign
- if (edgenum > 0) return optedgenum;
- else return -optedgenum;
-} //end of the function AAS_OptimizeEdge
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_KeepFace(aas_face_t *face)
-{
- if (!(face->faceflags & FACE_LADDER)) return 0;
- else return 1;
-} //end of the function AAS_KeepFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_OptimizeFace(optimized_t *optimized, int facenum)
-{
- int i, edgenum, optedgenum, optfacenum;
- aas_face_t *face, *optface;
-
- face = &aasworld.faces[abs(facenum)];
- if (!AAS_KeepFace(face)) return 0;
-
- optfacenum = optimized->faceoptimizeindex[abs(facenum)];
- if (optfacenum)
- {
- //keep the face side sign
- if (facenum > 0) return optfacenum;
- else return -optfacenum;
- } //end if
-
- optface = &optimized->faces[optimized->numfaces];
- Com_Memcpy(optface, face, sizeof(aas_face_t));
-
- optface->numedges = 0;
- optface->firstedge = optimized->edgeindexsize;
- for (i = 0; i < face->numedges; i++)
- {
- edgenum = aasworld.edgeindex[face->firstedge + i];
- optedgenum = AAS_OptimizeEdge(optimized, edgenum);
- if (optedgenum)
- {
- optimized->edgeindex[optface->firstedge + optface->numedges] = optedgenum;
- optface->numedges++;
- optimized->edgeindexsize++;
- } //end if
- } //end for
- optimized->faceoptimizeindex[abs(facenum)] = optimized->numfaces;
- optfacenum = optimized->numfaces;
- optimized->numfaces++;
- //keep the face side sign
- if (facenum > 0) return optfacenum;
- else return -optfacenum;
-} //end of the function AAS_OptimizeFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_OptimizeArea(optimized_t *optimized, int areanum)
-{
- int i, facenum, optfacenum;
- aas_area_t *area, *optarea;
-
- area = &aasworld.areas[areanum];
- optarea = &optimized->areas[areanum];
- Com_Memcpy(optarea, area, sizeof(aas_area_t));
-
- optarea->numfaces = 0;
- optarea->firstface = optimized->faceindexsize;
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = aasworld.faceindex[area->firstface + i];
- optfacenum = AAS_OptimizeFace(optimized, facenum);
- if (optfacenum)
- {
- optimized->faceindex[optarea->firstface + optarea->numfaces] = optfacenum;
- optarea->numfaces++;
- optimized->faceindexsize++;
- } //end if
- } //end for
-} //end of the function AAS_OptimizeArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_OptimizeAlloc(optimized_t *optimized)
-{
- optimized->vertexes = (aas_vertex_t *) GetClearedMemory(aasworld.numvertexes * sizeof(aas_vertex_t));
- optimized->numvertexes = 0;
- optimized->edges = (aas_edge_t *) GetClearedMemory(aasworld.numedges * sizeof(aas_edge_t));
- optimized->numedges = 1; //edge zero is a dummy
- optimized->edgeindex = (aas_edgeindex_t *) GetClearedMemory(aasworld.edgeindexsize * sizeof(aas_edgeindex_t));
- optimized->edgeindexsize = 0;
- optimized->faces = (aas_face_t *) GetClearedMemory(aasworld.numfaces * sizeof(aas_face_t));
- optimized->numfaces = 1; //face zero is a dummy
- optimized->faceindex = (aas_faceindex_t *) GetClearedMemory(aasworld.faceindexsize * sizeof(aas_faceindex_t));
- optimized->faceindexsize = 0;
- optimized->areas = (aas_area_t *) GetClearedMemory(aasworld.numareas * sizeof(aas_area_t));
- optimized->numareas = aasworld.numareas;
- //
- optimized->vertexoptimizeindex = (int *) GetClearedMemory(aasworld.numvertexes * sizeof(int));
- optimized->edgeoptimizeindex = (int *) GetClearedMemory(aasworld.numedges * sizeof(int));
- optimized->faceoptimizeindex = (int *) GetClearedMemory(aasworld.numfaces * sizeof(int));
-} //end of the function AAS_OptimizeAlloc
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_OptimizeStore(optimized_t *optimized)
-{
- //store the optimized vertexes
- if (aasworld.vertexes) FreeMemory(aasworld.vertexes);
- aasworld.vertexes = optimized->vertexes;
- aasworld.numvertexes = optimized->numvertexes;
- //store the optimized edges
- if (aasworld.edges) FreeMemory(aasworld.edges);
- aasworld.edges = optimized->edges;
- aasworld.numedges = optimized->numedges;
- //store the optimized edge index
- if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);
- aasworld.edgeindex = optimized->edgeindex;
- aasworld.edgeindexsize = optimized->edgeindexsize;
- //store the optimized faces
- if (aasworld.faces) FreeMemory(aasworld.faces);
- aasworld.faces = optimized->faces;
- aasworld.numfaces = optimized->numfaces;
- //store the optimized face index
- if (aasworld.faceindex) FreeMemory(aasworld.faceindex);
- aasworld.faceindex = optimized->faceindex;
- aasworld.faceindexsize = optimized->faceindexsize;
- //store the optimized areas
- if (aasworld.areas) FreeMemory(aasworld.areas);
- aasworld.areas = optimized->areas;
- aasworld.numareas = optimized->numareas;
- //free optimize indexes
- FreeMemory(optimized->vertexoptimizeindex);
- FreeMemory(optimized->edgeoptimizeindex);
- FreeMemory(optimized->faceoptimizeindex);
-} //end of the function AAS_OptimizeStore
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Optimize(void)
-{
- int i, sign;
- optimized_t optimized;
-
- AAS_OptimizeAlloc(&optimized);
- for (i = 1; i < aasworld.numareas; i++)
- {
- AAS_OptimizeArea(&optimized, i);
- } //end for
- //reset the reachability face pointers
- for (i = 0; i < aasworld.reachabilitysize; i++)
- {
- //NOTE: for TRAVEL_ELEVATOR the facenum is the model number of
- // the elevator
- if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) continue;
- //NOTE: for TRAVEL_JUMPPAD the facenum is the Z velocity and the edgenum is the hor velocity
- if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) continue;
- //NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information
- if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) continue;
- //
- sign = aasworld.reachability[i].facenum;
- aasworld.reachability[i].facenum = optimized.faceoptimizeindex[abs(aasworld.reachability[i].facenum)];
- if (sign < 0) aasworld.reachability[i].facenum = -aasworld.reachability[i].facenum;
- sign = aasworld.reachability[i].edgenum;
- aasworld.reachability[i].edgenum = optimized.edgeoptimizeindex[abs(aasworld.reachability[i].edgenum)];
- if (sign < 0) aasworld.reachability[i].edgenum = -aasworld.reachability[i].edgenum;
- } //end for
- //store the optimized AAS data into aasworld
- AAS_OptimizeStore(&optimized);
- //print some nice stuff :)
- botimport.Print(PRT_MESSAGE, "AAS data optimized.\n");
-} //end of the function AAS_Optimize
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_optimize.c + * + * desc: decreases the .aas file size after the reachabilities have + * been calculated, just dumps all the faces, edges and vertexes + * + * $Archive: /MissionPack/code/botlib/be_aas_optimize.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_libvar.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +typedef struct optimized_s +{ + //vertexes + int numvertexes; + aas_vertex_t *vertexes; + //edges + int numedges; + aas_edge_t *edges; + //edge index + int edgeindexsize; + aas_edgeindex_t *edgeindex; + //faces + int numfaces; + aas_face_t *faces; + //face index + int faceindexsize; + aas_faceindex_t *faceindex; + //convex areas + int numareas; + aas_area_t *areas; + // + int *vertexoptimizeindex; + int *edgeoptimizeindex; + int *faceoptimizeindex; +} optimized_t; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_KeepEdge(aas_edge_t *edge) +{ + return 1; +} //end of the function AAS_KeepFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OptimizeEdge(optimized_t *optimized, int edgenum) +{ + int i, optedgenum; + aas_edge_t *edge, *optedge; + + edge = &aasworld.edges[abs(edgenum)]; + if (!AAS_KeepEdge(edge)) return 0; + + optedgenum = optimized->edgeoptimizeindex[abs(edgenum)]; + if (optedgenum) + { + //keep the edge reversed sign + if (edgenum > 0) return optedgenum; + else return -optedgenum; + } //end if + + optedge = &optimized->edges[optimized->numedges]; + + for (i = 0; i < 2; i++) + { + if (optimized->vertexoptimizeindex[edge->v[i]]) + { + optedge->v[i] = optimized->vertexoptimizeindex[edge->v[i]]; + } //end if + else + { + VectorCopy(aasworld.vertexes[edge->v[i]], optimized->vertexes[optimized->numvertexes]); + optedge->v[i] = optimized->numvertexes; + optimized->vertexoptimizeindex[edge->v[i]] = optimized->numvertexes; + optimized->numvertexes++; + } //end else + } //end for + optimized->edgeoptimizeindex[abs(edgenum)] = optimized->numedges; + optedgenum = optimized->numedges; + optimized->numedges++; + //keep the edge reversed sign + if (edgenum > 0) return optedgenum; + else return -optedgenum; +} //end of the function AAS_OptimizeEdge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_KeepFace(aas_face_t *face) +{ + if (!(face->faceflags & FACE_LADDER)) return 0; + else return 1; +} //end of the function AAS_KeepFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_OptimizeFace(optimized_t *optimized, int facenum) +{ + int i, edgenum, optedgenum, optfacenum; + aas_face_t *face, *optface; + + face = &aasworld.faces[abs(facenum)]; + if (!AAS_KeepFace(face)) return 0; + + optfacenum = optimized->faceoptimizeindex[abs(facenum)]; + if (optfacenum) + { + //keep the face side sign + if (facenum > 0) return optfacenum; + else return -optfacenum; + } //end if + + optface = &optimized->faces[optimized->numfaces]; + Com_Memcpy(optface, face, sizeof(aas_face_t)); + + optface->numedges = 0; + optface->firstedge = optimized->edgeindexsize; + for (i = 0; i < face->numedges; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + optedgenum = AAS_OptimizeEdge(optimized, edgenum); + if (optedgenum) + { + optimized->edgeindex[optface->firstedge + optface->numedges] = optedgenum; + optface->numedges++; + optimized->edgeindexsize++; + } //end if + } //end for + optimized->faceoptimizeindex[abs(facenum)] = optimized->numfaces; + optfacenum = optimized->numfaces; + optimized->numfaces++; + //keep the face side sign + if (facenum > 0) return optfacenum; + else return -optfacenum; +} //end of the function AAS_OptimizeFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_OptimizeArea(optimized_t *optimized, int areanum) +{ + int i, facenum, optfacenum; + aas_area_t *area, *optarea; + + area = &aasworld.areas[areanum]; + optarea = &optimized->areas[areanum]; + Com_Memcpy(optarea, area, sizeof(aas_area_t)); + + optarea->numfaces = 0; + optarea->firstface = optimized->faceindexsize; + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + optfacenum = AAS_OptimizeFace(optimized, facenum); + if (optfacenum) + { + optimized->faceindex[optarea->firstface + optarea->numfaces] = optfacenum; + optarea->numfaces++; + optimized->faceindexsize++; + } //end if + } //end for +} //end of the function AAS_OptimizeArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_OptimizeAlloc(optimized_t *optimized) +{ + optimized->vertexes = (aas_vertex_t *) GetClearedMemory(aasworld.numvertexes * sizeof(aas_vertex_t)); + optimized->numvertexes = 0; + optimized->edges = (aas_edge_t *) GetClearedMemory(aasworld.numedges * sizeof(aas_edge_t)); + optimized->numedges = 1; //edge zero is a dummy + optimized->edgeindex = (aas_edgeindex_t *) GetClearedMemory(aasworld.edgeindexsize * sizeof(aas_edgeindex_t)); + optimized->edgeindexsize = 0; + optimized->faces = (aas_face_t *) GetClearedMemory(aasworld.numfaces * sizeof(aas_face_t)); + optimized->numfaces = 1; //face zero is a dummy + optimized->faceindex = (aas_faceindex_t *) GetClearedMemory(aasworld.faceindexsize * sizeof(aas_faceindex_t)); + optimized->faceindexsize = 0; + optimized->areas = (aas_area_t *) GetClearedMemory(aasworld.numareas * sizeof(aas_area_t)); + optimized->numareas = aasworld.numareas; + // + optimized->vertexoptimizeindex = (int *) GetClearedMemory(aasworld.numvertexes * sizeof(int)); + optimized->edgeoptimizeindex = (int *) GetClearedMemory(aasworld.numedges * sizeof(int)); + optimized->faceoptimizeindex = (int *) GetClearedMemory(aasworld.numfaces * sizeof(int)); +} //end of the function AAS_OptimizeAlloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_OptimizeStore(optimized_t *optimized) +{ + //store the optimized vertexes + if (aasworld.vertexes) FreeMemory(aasworld.vertexes); + aasworld.vertexes = optimized->vertexes; + aasworld.numvertexes = optimized->numvertexes; + //store the optimized edges + if (aasworld.edges) FreeMemory(aasworld.edges); + aasworld.edges = optimized->edges; + aasworld.numedges = optimized->numedges; + //store the optimized edge index + if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex); + aasworld.edgeindex = optimized->edgeindex; + aasworld.edgeindexsize = optimized->edgeindexsize; + //store the optimized faces + if (aasworld.faces) FreeMemory(aasworld.faces); + aasworld.faces = optimized->faces; + aasworld.numfaces = optimized->numfaces; + //store the optimized face index + if (aasworld.faceindex) FreeMemory(aasworld.faceindex); + aasworld.faceindex = optimized->faceindex; + aasworld.faceindexsize = optimized->faceindexsize; + //store the optimized areas + if (aasworld.areas) FreeMemory(aasworld.areas); + aasworld.areas = optimized->areas; + aasworld.numareas = optimized->numareas; + //free optimize indexes + FreeMemory(optimized->vertexoptimizeindex); + FreeMemory(optimized->edgeoptimizeindex); + FreeMemory(optimized->faceoptimizeindex); +} //end of the function AAS_OptimizeStore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Optimize(void) +{ + int i, sign; + optimized_t optimized; + + AAS_OptimizeAlloc(&optimized); + for (i = 1; i < aasworld.numareas; i++) + { + AAS_OptimizeArea(&optimized, i); + } //end for + //reset the reachability face pointers + for (i = 0; i < aasworld.reachabilitysize; i++) + { + //NOTE: for TRAVEL_ELEVATOR the facenum is the model number of + // the elevator + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) continue; + //NOTE: for TRAVEL_JUMPPAD the facenum is the Z velocity and the edgenum is the hor velocity + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) continue; + //NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) continue; + // + sign = aasworld.reachability[i].facenum; + aasworld.reachability[i].facenum = optimized.faceoptimizeindex[abs(aasworld.reachability[i].facenum)]; + if (sign < 0) aasworld.reachability[i].facenum = -aasworld.reachability[i].facenum; + sign = aasworld.reachability[i].edgenum; + aasworld.reachability[i].edgenum = optimized.edgeoptimizeindex[abs(aasworld.reachability[i].edgenum)]; + if (sign < 0) aasworld.reachability[i].edgenum = -aasworld.reachability[i].edgenum; + } //end for + //store the optimized AAS data into aasworld + AAS_OptimizeStore(&optimized); + //print some nice stuff :) + botimport.Print(PRT_MESSAGE, "AAS data optimized.\n"); +} //end of the function AAS_Optimize diff --git a/code/botlib/be_aas_optimize.h b/code/botlib/be_aas_optimize.h index 15e44e6..d59bbc9 100755 --- a/code/botlib/be_aas_optimize.h +++ b/code/botlib/be_aas_optimize.h @@ -1,33 +1,33 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_optimize.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_optimize.h $
- *
- *****************************************************************************/
-
-void AAS_Optimize(void);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_optimize.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_optimize.h $ + * + *****************************************************************************/ + +void AAS_Optimize(void); + diff --git a/code/botlib/be_aas_reach.c b/code/botlib/be_aas_reach.c index 465da80..4b69d28 100755 --- a/code/botlib/be_aas_reach.c +++ b/code/botlib/be_aas_reach.c @@ -1,4547 +1,4547 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_reach.c
- *
- * desc: reachability calculations
- *
- * $Archive: /MissionPack/code/botlib/be_aas_reach.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_log.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_libvar.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-
-extern int Sys_MilliSeconds(void);
-
-
-extern botlib_import_t botimport;
-
-//#define REACH_DEBUG
-
-//NOTE: all travel times are in hundreth of a second
-//maximum number of reachability links
-#define AAS_MAX_REACHABILITYSIZE 65536
-//number of areas reachability is calculated for each frame
-#define REACHABILITYAREASPERCYCLE 15
-//number of units reachability points are placed inside the areas
-#define INSIDEUNITS 2
-#define INSIDEUNITS_WALKEND 5
-#define INSIDEUNITS_WALKSTART 0.1
-#define INSIDEUNITS_WATERJUMP 15
-//area flag used for weapon jumping
-#define AREA_WEAPONJUMP 8192 //valid area to weapon jump to
-//number of reachabilities of each type
-int reach_swim; //swim
-int reach_equalfloor; //walk on floors with equal height
-int reach_step; //step up
-int reach_walk; //walk of step
-int reach_barrier; //jump up to a barrier
-int reach_waterjump; //jump out of water
-int reach_walkoffledge; //walk of a ledge
-int reach_jump; //jump
-int reach_ladder; //climb or descent a ladder
-int reach_teleport; //teleport
-int reach_elevator; //use an elevator
-int reach_funcbob; //use a func bob
-int reach_grapple; //grapple hook
-int reach_doublejump; //double jump
-int reach_rampjump; //ramp jump
-int reach_strafejump; //strafe jump (just normal jump but further)
-int reach_rocketjump; //rocket jump
-int reach_bfgjump; //bfg jump
-int reach_jumppad; //jump pads
-//if true grapple reachabilities are skipped
-int calcgrapplereach;
-//linked reachability
-typedef struct aas_lreachability_s
-{
- int areanum; //number of the reachable area
- int facenum; //number of the face towards the other area
- int edgenum; //number of the edge towards the other area
- vec3_t start; //start point of inter area movement
- vec3_t end; //end point of inter area movement
- int traveltype; //type of travel required to get to the area
- unsigned short int traveltime; //travel time of the inter area movement
- //
- struct aas_lreachability_s *next;
-} aas_lreachability_t;
-//temporary reachabilities
-aas_lreachability_t *reachabilityheap; //heap with reachabilities
-aas_lreachability_t *nextreachability; //next free reachability from the heap
-aas_lreachability_t **areareachability; //reachability links for every area
-int numlreachabilities;
-
-//===========================================================================
-// returns the surface area of the given face
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_FaceArea(aas_face_t *face)
-{
- int i, edgenum, side;
- float total;
- vec_t *v;
- vec3_t d1, d2, cross;
- aas_edge_t *edge;
-
- edgenum = aasworld.edgeindex[face->firstedge];
- side = edgenum < 0;
- edge = &aasworld.edges[abs(edgenum)];
- v = aasworld.vertexes[edge->v[side]];
-
- total = 0;
- for (i = 1; i < face->numedges - 1; i++)
- {
- edgenum = aasworld.edgeindex[face->firstedge + i];
- side = edgenum < 0;
- edge = &aasworld.edges[abs(edgenum)];
- VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1);
- VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2);
- CrossProduct(d1, d2, cross);
- total += 0.5 * VectorLength(cross);
- } //end for
- return total;
-} //end of the function AAS_FaceArea
-//===========================================================================
-// returns the volume of an area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_AreaVolume(int areanum)
-{
- int i, edgenum, facenum, side;
- vec_t d, a, volume;
- vec3_t corner;
- aas_plane_t *plane;
- aas_edge_t *edge;
- aas_face_t *face;
- aas_area_t *area;
-
- area = &aasworld.areas[areanum];
- facenum = aasworld.faceindex[area->firstface];
- face = &aasworld.faces[abs(facenum)];
- edgenum = aasworld.edgeindex[face->firstedge];
- edge = &aasworld.edges[abs(edgenum)];
- //
- VectorCopy(aasworld.vertexes[edge->v[0]], corner);
-
- //make tetrahedrons to all other faces
- volume = 0;
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = abs(aasworld.faceindex[area->firstface + i]);
- face = &aasworld.faces[facenum];
- side = face->backarea != areanum;
- plane = &aasworld.planes[face->planenum ^ side];
- d = -(DotProduct (corner, plane->normal) - plane->dist);
- a = AAS_FaceArea(face);
- volume += d * a;
- } //end for
-
- volume /= 3;
- return volume;
-} //end of the function AAS_AreaVolume
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BestReachableLinkArea(aas_link_t *areas)
-{
- aas_link_t *link;
-
- for (link = areas; link; link = link->next_area)
- {
- if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum))
- {
- return link->areanum;
- } //end if
- } //end for
- //
- for (link = areas; link; link = link->next_area)
- {
- if (link->areanum) return link->areanum;
- //FIXME: this is a bad idea when the reachability is not yet
- // calculated when the level items are loaded
- if (AAS_AreaReachability(link->areanum))
- return link->areanum;
- } //end for
- return 0;
-} //end of the function AAS_BestReachableLinkArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity)
-{
- int modelnum, ent2;
- float speed, height, gravity, time, dist, forward;
- vec3_t origin, angles, teststart, ent2origin;
- aas_trace_t trace;
- char model[MAX_EPAIRKEY];
- char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
-
- //
- AAS_FloatForBSPEpairKey(ent, "speed", &speed);
- if (!speed) speed = 1000;
- VectorClear(angles);
- //get the mins, maxs and origin of the model
- AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
- if (model[0]) modelnum = atoi(model+1);
- else modelnum = 0;
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
- VectorAdd(origin, absmins, absmins);
- VectorAdd(origin, absmaxs, absmaxs);
- VectorAdd(absmins, absmaxs, origin);
- VectorScale (origin, 0.5, origin);
-
- //get the start areas
- VectorCopy(origin, teststart);
- teststart[2] += 64;
- trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
- if (trace.startsolid)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
- VectorCopy(origin, areastart);
- } //end if
- else
- {
- VectorCopy(trace.endpos, areastart);
- } //end else
- areastart[2] += 0.125;
- //
- //AAS_DrawPermanentCross(origin, 4, 4);
- //get the target entity
- AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
- for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
- {
- if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
- if (!strcmp(targetname, target)) break;
- } //end for
- if (!ent2)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
- return qfalse;
- } //end if
- AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
- //
- height = ent2origin[2] - origin[2];
- gravity = aassettings.phys_gravity;
- time = sqrt( height / ( 0.5 * gravity ) );
- if (!time)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
- return qfalse;
- } //end if
- // set s.origin2 to the push velocity
- VectorSubtract ( ent2origin, origin, velocity);
- dist = VectorNormalize( velocity);
- forward = dist / time;
- //FIXME: why multiply by 1.1
- forward *= 1.1f;
- VectorScale(velocity, forward, velocity);
- velocity[2] = time * gravity;
- return qtrue;
-} //end of the function AAS_GetJumpPadInfo
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs)
-{
- int area2num, ent, bot_visualizejumppads, bestareanum;
- float volume, bestareavolume;
- vec3_t areastart, cmdmove, bboxmins, bboxmaxs;
- vec3_t absmins, absmaxs, velocity;
- aas_clientmove_t move;
- aas_link_t *areas, *link;
- char classname[MAX_EPAIRKEY];
-
-#ifdef BSPC
- bot_visualizejumppads = 0;
-#else
- bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
-#endif
- VectorAdd(origin, mins, bboxmins);
- VectorAdd(origin, maxs, bboxmaxs);
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (strcmp(classname, "trigger_push")) continue;
- //
- if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
- //get the areas the jump pad brush is in
- areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
- for (link = areas; link; link = link->next_area)
- {
- if (AAS_AreaJumpPad(link->areanum)) break;
- } //end for
- if (!link)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
- AAS_UnlinkFromAreas(areas);
- continue;
- } //end if
- //
- //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
- //
- VectorSet(cmdmove, 0, 0, 0);
- Com_Memset(&move, 0, sizeof(aas_clientmove_t));
- area2num = 0;
- AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
- velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads);
- if (move.frames < 30)
- {
- bestareanum = 0;
- bestareavolume = 0;
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaJumpPad(link->areanum)) continue;
- volume = AAS_AreaVolume(link->areanum);
- if (volume >= bestareavolume)
- {
- bestareanum = link->areanum;
- bestareavolume = volume;
- } //end if
- } //end if
- AAS_UnlinkFromAreas(areas);
- return bestareanum;
- } //end if
- AAS_UnlinkFromAreas(areas);
- } //end for
- return 0;
-} //end of the function AAS_BestReachableFromJumpPadArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin)
-{
- int areanum, i, j, k, l;
- aas_link_t *areas;
- vec3_t absmins, absmaxs;
- //vec3_t bbmins, bbmaxs;
- vec3_t start, end;
- aas_trace_t trace;
-
- if (!aasworld.loaded)
- {
- botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n");
- return 0;
- } //end if
- //find a point in an area
- VectorCopy(origin, start);
- areanum = AAS_PointAreaNum(start);
- //while no area found fudge around a little
- for (i = 0; i < 5 && !areanum; i++)
- {
- for (j = 0; j < 5 && !areanum; j++)
- {
- for (k = -1; k <= 1 && !areanum; k++)
- {
- for (l = -1; l <= 1 && !areanum; l++)
- {
- VectorCopy(origin, start);
- start[0] += (float) j * 4 * k;
- start[1] += (float) j * 4 * l;
- start[2] += (float) i * 4;
- areanum = AAS_PointAreaNum(start);
- } //end for
- } //end for
- } //end for
- } //end for
- //if an area was found
- if (areanum)
- {
- //drop client bbox down and try again
- VectorCopy(start, end);
- start[2] += 0.25;
- end[2] -= 50;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
- if (!trace.startsolid)
- {
- areanum = AAS_PointAreaNum(trace.endpos);
- VectorCopy(trace.endpos, goalorigin);
- //FIXME: cannot enable next line right now because the reachability
- // does not have to be calculated when the level items are loaded
- //if the origin is in an area with reachability
- //if (AAS_AreaReachability(areanum)) return areanum;
- if (areanum) return areanum;
- } //end if
- else
- {
- //it can very well happen that the AAS_PointAreaNum function tells that
- //a point is in an area and that starting a AAS_TraceClientBBox from that
- //point will return trace.startsolid qtrue
-#if 0
- if (AAS_PointAreaNum(start))
- {
- Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum);
- AAS_DrawPermanentCross(start, 4, LINECOLOR_RED);
- } //end if
- botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n");
-#endif
- VectorCopy(start, goalorigin);
- return areanum;
- } //end else
- } //end if
- //
- //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs);
- //NOTE: the goal origin does not have to be in the goal area
- // because the bot will have to move towards the item origin anyway
- VectorCopy(origin, goalorigin);
- //
- VectorAdd(origin, mins, absmins);
- VectorAdd(origin, maxs, absmaxs);
- //add bounding box size
- //VectorSubtract(absmins, bbmaxs, absmins);
- //VectorSubtract(absmaxs, bbmins, absmaxs);
- //link an invalid (-1) entity
- areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
- //get the reachable link arae
- areanum = AAS_BestReachableLinkArea(areas);
- //unlink the invalid entity
- AAS_UnlinkFromAreas(areas);
- //
- return areanum;
-} //end of the function AAS_BestReachableArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_SetupReachabilityHeap(void)
-{
- int i;
-
- reachabilityheap = (aas_lreachability_t *) GetClearedMemory(
- AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t));
- for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++)
- {
- reachabilityheap[i].next = &reachabilityheap[i+1];
- } //end for
- reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL;
- nextreachability = reachabilityheap;
- numlreachabilities = 0;
-} //end of the function AAS_InitReachabilityHeap
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShutDownReachabilityHeap(void)
-{
- FreeMemory(reachabilityheap);
- numlreachabilities = 0;
-} //end of the function AAS_ShutDownReachabilityHeap
-//===========================================================================
-// returns a reachability link
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_lreachability_t *AAS_AllocReachability(void)
-{
- aas_lreachability_t *r;
-
- if (!nextreachability) return NULL;
- //make sure the error message only shows up once
- if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE");
- //
- r = nextreachability;
- nextreachability = nextreachability->next;
- numlreachabilities++;
- return r;
-} //end of the function AAS_AllocReachability
-//===========================================================================
-// frees a reachability link
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeReachability(aas_lreachability_t *lreach)
-{
- Com_Memset(lreach, 0, sizeof(aas_lreachability_t));
-
- lreach->next = nextreachability;
- nextreachability = lreach;
- numlreachabilities--;
-} //end of the function AAS_FreeReachability
-//===========================================================================
-// returns qtrue if the area has reachability links
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaReachability(int areanum)
-{
- if (areanum < 0 || areanum >= aasworld.numareas)
- {
- AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum);
- return 0;
- } //end if
- return aasworld.areasettings[areanum].numreachableareas;
-} //end of the function AAS_AreaReachability
-//===========================================================================
-// returns the surface area of all ground faces together of the area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_AreaGroundFaceArea(int areanum)
-{
- int i;
- float total;
- aas_area_t *area;
- aas_face_t *face;
-
- total = 0;
- area = &aasworld.areas[areanum];
- for (i = 0; i < area->numfaces; i++)
- {
- face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
- if (!(face->faceflags & FACE_GROUND)) continue;
- //
- total += AAS_FaceArea(face);
- } //end for
- return total;
-} //end of the function AAS_AreaGroundFaceArea
-//===========================================================================
-// returns the center of a face
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FaceCenter(int facenum, vec3_t center)
-{
- int i;
- float scale;
- aas_face_t *face;
- aas_edge_t *edge;
-
- face = &aasworld.faces[facenum];
-
- VectorClear(center);
- for (i = 0; i < face->numedges; i++)
- {
- edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])];
- VectorAdd(center, aasworld.vertexes[edge->v[0]], center);
- VectorAdd(center, aasworld.vertexes[edge->v[1]], center);
- } //end for
- scale = 0.5 / face->numedges;
- VectorScale(center, scale, center);
-} //end of the function AAS_FaceCenter
-//===========================================================================
-// returns the maximum distance a player can fall before being damaged
-// damage = deltavelocity*deltavelocity * 0.0001
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_FallDamageDistance(void)
-{
- float maxzvelocity, gravity, t;
-
- maxzvelocity = sqrt(30 * 10000);
- gravity = aassettings.phys_gravity;
- t = maxzvelocity / gravity;
- return 0.5 * gravity * t * t;
-} //end of the function AAS_FallDamageDistance
-//===========================================================================
-// distance = 0.5 * gravity * t * t
-// vel = t * gravity
-// damage = vel * vel * 0.0001
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_FallDelta(float distance)
-{
- float t, delta, gravity;
-
- gravity = aassettings.phys_gravity;
- t = sqrt(fabs(distance) * 2 / gravity);
- delta = t * gravity;
- return delta * delta * 0.0001;
-} //end of the function AAS_FallDelta
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_MaxJumpHeight(float phys_jumpvel)
-{
- float phys_gravity;
-
- phys_gravity = aassettings.phys_gravity;
- //maximum height a player can jump with the given initial z velocity
- return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity);
-} //end of the function MaxJumpHeight
-//===========================================================================
-// returns true if a player can only crouch in the area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float AAS_MaxJumpDistance(float phys_jumpvel)
-{
- float phys_gravity, phys_maxvelocity, t;
-
- phys_gravity = aassettings.phys_gravity;
- phys_maxvelocity = aassettings.phys_maxvelocity;
- //time a player takes to fall the height
- t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity));
- //maximum distance
- return phys_maxvelocity * (t + phys_jumpvel / phys_gravity);
-} //end of the function AAS_MaxJumpDistance
-//===========================================================================
-// returns true if a player can only crouch in the area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaCrouch(int areanum)
-{
- if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue;
- else return qfalse;
-} //end of the function AAS_AreaCrouch
-//===========================================================================
-// returns qtrue if it is possible to swim in the area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaSwim(int areanum)
-{
- if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
- else return qfalse;
-} //end of the function AAS_AreaSwim
-//===========================================================================
-// returns qtrue if the area contains a liquid
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaLiquid(int areanum)
-{
- if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;
- else return qfalse;
-} //end of the function AAS_AreaLiquid
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaLava(int areanum)
-{
- return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA);
-} //end of the function AAS_AreaLava
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaSlime(int areanum)
-{
- return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME);
-} //end of the function AAS_AreaSlime
-//===========================================================================
-// returns qtrue if the area contains ground faces
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaGrounded(int areanum)
-{
- return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED);
-} //end of the function AAS_AreaGround
-//===========================================================================
-// returns true if the area contains ladder faces
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaLadder(int areanum)
-{
- return (aasworld.areasettings[areanum].areaflags & AREA_LADDER);
-} //end of the function AAS_AreaLadder
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaJumpPad(int areanum)
-{
- return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD);
-} //end of the function AAS_AreaJumpPad
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaTeleporter(int areanum)
-{
- return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER);
-} //end of the function AAS_AreaTeleporter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaClusterPortal(int areanum)
-{
- return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL);
-} //end of the function AAS_AreaClusterPortal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaDoNotEnter(int areanum)
-{
- return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER);
-} //end of the function AAS_AreaDoNotEnter
-//===========================================================================
-// returns the time it takes perform a barrier jump
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-unsigned short int AAS_BarrierJumpTravelTime(void)
-{
- return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1);
-} //end op the function AAS_BarrierJumpTravelTime
-//===========================================================================
-// returns true if there already exists a reachability from area1 to area2
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_ReachabilityExists(int area1num, int area2num)
-{
- aas_lreachability_t *r;
-
- for (r = areareachability[area1num]; r; r = r->next)
- {
- if (r->areanum == area2num) return qtrue;
- } //end for
- return qfalse;
-} //end of the function AAS_ReachabilityExists
-//===========================================================================
-// returns true if there is a solid just after the end point when going
-// from start to end
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NearbySolidOrGap(vec3_t start, vec3_t end)
-{
- vec3_t dir, testpoint;
- int areanum;
-
- VectorSubtract(end, start, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- VectorMA(end, 48, dir, testpoint);
-
- areanum = AAS_PointAreaNum(testpoint);
- if (!areanum)
- {
- testpoint[2] += 16;
- areanum = AAS_PointAreaNum(testpoint);
- if (!areanum) return qtrue;
- } //end if
- VectorMA(end, 64, dir, testpoint);
- areanum = AAS_PointAreaNum(testpoint);
- if (areanum)
- {
- if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue;
- } //end if
- return qfalse;
-} //end of the function AAS_SolidGapTime
-//===========================================================================
-// searches for swim reachabilities between adjacent areas
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_Swim(int area1num, int area2num)
-{
- int i, j, face1num, face2num, side1;
- aas_area_t *area1, *area2;
- aas_areasettings_t *areasettings;
- aas_lreachability_t *lreach;
- aas_face_t *face1;
- aas_plane_t *plane;
- vec3_t start;
-
- if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse;
- //if the second area is crouch only
- if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse;
-
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
-
- //if the areas are not near anough
- for (i = 0; i < 3; i++)
- {
- if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
- if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
- } //end for
- //find a shared face and create a reachability link
- for (i = 0; i < area1->numfaces; i++)
- {
- face1num = aasworld.faceindex[area1->firstface + i];
- side1 = face1num < 0;
- face1num = abs(face1num);
- //
- for (j = 0; j < area2->numfaces; j++)
- {
- face2num = abs(aasworld.faceindex[area2->firstface + j]);
- //
- if (face1num == face2num)
- {
- AAS_FaceCenter(face1num, start);
- //
- if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))
- {
- //
- face1 = &aasworld.faces[face1num];
- areasettings = &aasworld.areasettings[area1num];
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = face1num;
- lreach->edgenum = 0;
- VectorCopy(start, lreach->start);
- plane = &aasworld.planes[face1->planenum ^ side1];
- VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end);
- lreach->traveltype = TRAVEL_SWIM;
- lreach->traveltime = 1;
- //if the volume of the area is rather small
- if (AAS_AreaVolume(area2num) < 800)
- lreach->traveltime += 200;
- //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500;
- //link the reachability
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- reach_swim++;
- return qtrue;
- } //end if
- } //end if
- } //end for
- } //end for
- return qfalse;
-} //end of the function AAS_Reachability_Swim
-//===========================================================================
-// searches for reachabilities between adjacent areas with equal floor
-// heights
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_EqualFloorHeight(int area1num, int area2num)
-{
- int i, j, edgenum, edgenum1, edgenum2, foundreach, side;
- float height, bestheight, length, bestlength;
- vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1};
- vec3_t edgevec;
- aas_area_t *area1, *area2;
- aas_face_t *face1, *face2;
- aas_edge_t *edge;
- aas_plane_t *plane2;
- aas_lreachability_t lr, *lreach;
-
- if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
-
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
- //if the areas are not near anough in the x-y direction
- for (i = 0; i < 2; i++)
- {
- if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
- if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
- } //end for
- //if area 2 is too high above area 1
- if (area2->mins[2] > area1->maxs[2]) return qfalse;
- //
- VectorCopy(gravitydirection, invgravity);
- VectorInverse(invgravity);
- //
- bestheight = 99999;
- bestlength = 0;
- foundreach = qfalse;
- Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy
- //
- //check if the areas have ground faces with a common edge
- //if existing use the lowest common edge for a reachability link
- for (i = 0; i < area1->numfaces; i++)
- {
- face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])];
- if (!(face1->faceflags & FACE_GROUND)) continue;
- //
- for (j = 0; j < area2->numfaces; j++)
- {
- face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
- if (!(face2->faceflags & FACE_GROUND)) continue;
- //if there is a common edge
- for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++)
- {
- for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++)
- {
- if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) !=
- abs(aasworld.edgeindex[face2->firstedge + edgenum2]))
- continue;
- edgenum = aasworld.edgeindex[face1->firstedge + edgenum1];
- side = edgenum < 0;
- edge = &aasworld.edges[abs(edgenum)];
- //get the length of the edge
- VectorSubtract(aasworld.vertexes[edge->v[1]],
- aasworld.vertexes[edge->v[0]], dir);
- length = VectorLength(dir);
- //get the start point
- VectorAdd(aasworld.vertexes[edge->v[0]],
- aasworld.vertexes[edge->v[1]], start);
- VectorScale(start, 0.5, start);
- VectorCopy(start, end);
- //get the end point several units inside area2
- //and the start point several units inside area1
- //NOTE: normal is pointing into area2 because the
- //face edges are stored counter clockwise
- VectorSubtract(aasworld.vertexes[edge->v[side]],
- aasworld.vertexes[edge->v[!side]], edgevec);
- plane2 = &aasworld.planes[face2->planenum];
- CrossProduct(edgevec, plane2->normal, normal);
- VectorNormalize(normal);
- //
- //VectorMA(start, -1, normal, start);
- VectorMA(end, INSIDEUNITS_WALKEND, normal, end);
- VectorMA(start, INSIDEUNITS_WALKSTART, normal, start);
- end[2] += 0.125;
- //
- height = DotProduct(invgravity, start);
- //NOTE: if there's nearby solid or a gap area after this area
- //disabled this crap
- //if (AAS_NearbySolidOrGap(start, end)) height += 200;
- //NOTE: disabled because it disables reachabilities to very small areas
- //if (AAS_PointAreaNum(end) != area2num) continue;
- //get the longest lowest edge
- if (height < bestheight ||
- (height < bestheight + 1 && length > bestlength))
- {
- bestheight = height;
- bestlength = length;
- //create a new reachability link
- lr.areanum = area2num;
- lr.facenum = 0;
- lr.edgenum = edgenum;
- VectorCopy(start, lr.start);
- VectorCopy(end, lr.end);
- lr.traveltype = TRAVEL_WALK;
- lr.traveltime = 1;
- foundreach = qtrue;
- } //end if
- } //end for
- } //end for
- } //end for
- } //end for
- if (foundreach)
- {
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = lr.areanum;
- lreach->facenum = lr.facenum;
- lreach->edgenum = lr.edgenum;
- VectorCopy(lr.start, lreach->start);
- VectorCopy(lr.end, lreach->end);
- lreach->traveltype = lr.traveltype;
- lreach->traveltime = lr.traveltime;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //if going into a crouch area
- if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
- {
- lreach->traveltime += aassettings.rs_startcrouch;
- } //end if
- /*
- //NOTE: if there's nearby solid or a gap area after this area
- if (!AAS_NearbySolidOrGap(lreach->start, lreach->end))
- {
- lreach->traveltime += 100;
- } //end if
- */
- //avoid rather small areas
- //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;
- //
- reach_equalfloor++;
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function AAS_Reachability_EqualFloorHeight
-//===========================================================================
-// searches step, barrier, waterjump and walk off ledge reachabilities
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num)
-{
- int i, j, k, l, edge1num, edge2num, areas[10], numareas;
- int ground_bestarea2groundedgenum, ground_foundreach;
- int water_bestarea2groundedgenum, water_foundreach;
- int side1, area1swim, faceside1, groundface1num;
- float dist, dist1, dist2, diff, invgravitydot, ortdot;
- float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
- float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist;
- vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
- vec3_t normal, ort, edgevec, start, end, dir;
- vec3_t ground_beststart, ground_bestend, ground_bestnormal;
- vec3_t water_beststart, water_bestend, water_bestnormal;
- vec3_t invgravity = {0, 0, 1};
- vec3_t testpoint;
- aas_plane_t *plane;
- aas_area_t *area1, *area2;
- aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1;
- aas_edge_t *edge1, *edge2;
- aas_lreachability_t *lreach;
- aas_trace_t trace;
-
- //must be able to walk or swim in the first area
- if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
- //
- if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse;
- //
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
- //if the first area contains a liquid
- area1swim = AAS_AreaSwim(area1num);
- //if the areas are not near anough in the x-y direction
- for (i = 0; i < 2; i++)
- {
- if (area1->mins[i] > area2->maxs[i] + 10) return qfalse;
- if (area1->maxs[i] < area2->mins[i] - 10) return qfalse;
- } //end for
- //
- ground_foundreach = qfalse;
- ground_bestdist = 99999;
- ground_bestlength = 0;
- ground_bestarea2groundedgenum = 0;
- //
- water_foundreach = qfalse;
- water_bestdist = 99999;
- water_bestlength = 0;
- water_bestarea2groundedgenum = 0;
- //
- for (i = 0; i < area1->numfaces; i++)
- {
- groundface1num = aasworld.faceindex[area1->firstface + i];
- faceside1 = groundface1num < 0;
- groundface1 = &aasworld.faces[abs(groundface1num)];
- //if this isn't a ground face
- if (!(groundface1->faceflags & FACE_GROUND))
- {
- //if we can swim in the first area
- if (area1swim)
- {
- //face plane must be more or less horizontal
- plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)];
- if (DotProduct(plane->normal, invgravity) < 0.7) continue;
- } //end if
- else
- {
- //if we can't swim in the area it must be a ground face
- continue;
- } //end else
- } //end if
- //
- for (k = 0; k < groundface1->numedges; k++)
- {
- edge1num = aasworld.edgeindex[groundface1->firstedge + k];
- side1 = (edge1num < 0);
- //NOTE: for water faces we must take the side area 1 is
- // on into account because the face is shared and doesn't
- // have to be oriented correctly
- if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1);
- edge1num = abs(edge1num);
- edge1 = &aasworld.edges[edge1num];
- //vertexes of the edge
- VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1);
- VectorCopy(aasworld.vertexes[edge1->v[side1]], v2);
- //get a vertical plane through the edge
- //NOTE: normal is pointing into area 2 because the
- //face edges are stored counter clockwise
- VectorSubtract(v2, v1, edgevec);
- CrossProduct(edgevec, invgravity, normal);
- VectorNormalize(normal);
- dist = DotProduct(normal, v1);
- //check the faces from the second area
- for (j = 0; j < area2->numfaces; j++)
- {
- groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];
- //must be a ground face
- if (!(groundface2->faceflags & FACE_GROUND)) continue;
- //check the edges of this ground face
- for (l = 0; l < groundface2->numedges; l++)
- {
- edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]);
- edge2 = &aasworld.edges[edge2num];
- //vertexes of the edge
- VectorCopy(aasworld.vertexes[edge2->v[0]], v3);
- VectorCopy(aasworld.vertexes[edge2->v[1]], v4);
- //check the distance between the two points and the vertical plane
- //through the edge of area1
- diff = DotProduct(normal, v3) - dist;
- if (diff < -0.1 || diff > 0.1) continue;
- diff = DotProduct(normal, v4) - dist;
- if (diff < -0.1 || diff > 0.1) continue;
- //
- //project the two ground edges into the step side plane
- //and calculate the shortest distance between the two
- //edges if they overlap in the direction orthogonal to
- //the gravity direction
- CrossProduct(invgravity, normal, ort);
- invgravitydot = DotProduct(invgravity, invgravity);
- ortdot = DotProduct(ort, ort);
- //projection into the step plane
- //NOTE: since gravity is vertical this is just the z coordinate
- y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot;
- y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot;
- y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot;
- y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot;
- //
- x1 = DotProduct(v1, ort) / ortdot;
- x2 = DotProduct(v2, ort) / ortdot;
- x3 = DotProduct(v3, ort) / ortdot;
- x4 = DotProduct(v4, ort) / ortdot;
- //
- if (x1 > x2)
- {
- tmp = x1; x1 = x2; x2 = tmp;
- tmp = y1; y1 = y2; y2 = tmp;
- VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2);
- } //end if
- if (x3 > x4)
- {
- tmp = x3; x3 = x4; x4 = tmp;
- tmp = y3; y3 = y4; y4 = tmp;
- VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4);
- } //end if
- //if the two projected edge lines have no overlap
- if (x2 <= x3 || x4 <= x1)
- {
-// Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num);
- continue;
- } //end if
- //if the two lines fully overlap
- if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) &&
- (x3 - 0.5 < x1 && x2 < x4 + 0.5))
- {
- dist1 = y3 - y1;
- dist2 = y4 - y2;
- VectorCopy(v1, p1area1);
- VectorCopy(v2, p2area1);
- VectorCopy(v3, p1area2);
- VectorCopy(v4, p2area2);
- } //end if
- else
- {
- //if the points are equal
- if (x1 > x3 - 0.1 && x1 < x3 + 0.1)
- {
- dist1 = y3 - y1;
- VectorCopy(v1, p1area1);
- VectorCopy(v3, p1area2);
- } //end if
- else if (x1 < x3)
- {
- y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
- dist1 = y3 - y;
- VectorCopy(v3, p1area1);
- p1area1[2] = y;
- VectorCopy(v3, p1area2);
- } //end if
- else
- {
- y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
- dist1 = y - y1;
- VectorCopy(v1, p1area1);
- VectorCopy(v1, p1area2);
- p1area2[2] = y;
- } //end if
- //if the points are equal
- if (x2 > x4 - 0.1 && x2 < x4 + 0.1)
- {
- dist2 = y4 - y2;
- VectorCopy(v2, p2area1);
- VectorCopy(v4, p2area2);
- } //end if
- else if (x2 < x4)
- {
- y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
- dist2 = y - y2;
- VectorCopy(v2, p2area1);
- VectorCopy(v2, p2area2);
- p2area2[2] = y;
- } //end if
- else
- {
- y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
- dist2 = y4 - y;
- VectorCopy(v4, p2area1);
- p2area1[2] = y;
- VectorCopy(v4, p2area2);
- } //end else
- } //end else
- //if both distances are pretty much equal
- //then we take the middle of the points
- if (dist1 > dist2 - 1 && dist1 < dist2 + 1)
- {
- dist = dist1;
- VectorAdd(p1area1, p2area1, start);
- VectorScale(start, 0.5, start);
- VectorAdd(p1area2, p2area2, end);
- VectorScale(end, 0.5, end);
- } //end if
- else if (dist1 < dist2)
- {
- dist = dist1;
- VectorCopy(p1area1, start);
- VectorCopy(p1area2, end);
- } //end else if
- else
- {
- dist = dist2;
- VectorCopy(p2area1, start);
- VectorCopy(p2area2, end);
- } //end else
- //get the length of the overlapping part of the edges of the two areas
- VectorSubtract(p2area2, p1area2, dir);
- length = VectorLength(dir);
- //
- if (groundface1->faceflags & FACE_GROUND)
- {
- //if the vertical distance is smaller
- if (dist < ground_bestdist ||
- //or the vertical distance is pretty much the same
- //but the overlapping part of the edges is longer
- (dist < ground_bestdist + 1 && length > ground_bestlength))
- {
- ground_bestdist = dist;
- ground_bestlength = length;
- ground_foundreach = qtrue;
- ground_bestarea2groundedgenum = edge1num;
- ground_bestface1 = groundface1;
- //best point towards area1
- VectorCopy(start, ground_beststart);
- //normal is pointing into area2
- VectorCopy(normal, ground_bestnormal);
- //best point towards area2
- VectorCopy(end, ground_bestend);
- } //end if
- } //end if
- else
- {
- //if the vertical distance is smaller
- if (dist < water_bestdist ||
- //or the vertical distance is pretty much the same
- //but the overlapping part of the edges is longer
- (dist < water_bestdist + 1 && length > water_bestlength))
- {
- water_bestdist = dist;
- water_bestlength = length;
- water_foundreach = qtrue;
- water_bestarea2groundedgenum = edge1num;
- water_bestface1 = groundface1;
- //best point towards area1
- VectorCopy(start, water_beststart);
- //normal is pointing into area2
- VectorCopy(normal, water_bestnormal);
- //best point towards area2
- VectorCopy(end, water_bestend);
- } //end if
- } //end else
- } //end for
- } //end for
- } //end for
- } //end for
- //
- // NOTE: swim reachabilities are already filtered out
- //
- // Steps
- //
- // ---------
- // | step height -> TRAVEL_WALK
- //--------|
- //
- // ---------
- //~~~~~~~~| step height and low water -> TRAVEL_WALK
- //--------|
- //
- //~~~~~~~~~~~~~~~~~~
- // ---------
- // | step height and low water up to the step -> TRAVEL_WALK
- //--------|
- //
- //check for a step reachability
- if (ground_foundreach)
- {
- //if area2 is higher but lower than the maximum step height
- //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities
- if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep)
- {
- //create walk reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = ground_bestarea2groundedgenum;
- VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
- VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
- lreach->traveltype = TRAVEL_WALK;
- lreach->traveltime = 0;//1;
- //if going into a crouch area
- if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))
- {
- lreach->traveltime += aassettings.rs_startcrouch;
- } //end if
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //NOTE: if there's nearby solid or a gap area after this area
- /*
- if (!AAS_NearbySolidOrGap(lreach->start, lreach->end))
- {
- lreach->traveltime += 100;
- } //end if
- */
- //avoid rather small areas
- //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;
- //
- reach_step++;
- return qtrue;
- } //end if
- } //end if
- //
- // Water Jumps
- //
- // ---------
- // |
- //~~~~~~~~|
- // |
- // | higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP
- //--------|
- //
- //~~~~~~~~~~~~~~~~~~
- // ---------
- // |
- // |
- // |
- // | higher than step height and low water up to the step -> TRAVEL_WATERJUMP
- //--------|
- //
- //check for a waterjump reachability
- if (water_foundreach)
- {
- //get a test point a little bit towards area1
- VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint);
- //go down the maximum waterjump height
- testpoint[2] -= aassettings.phys_maxwaterjump;
- //if there IS water the sv_maxwaterjump height below the bestend point
- if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID)
- {
- //don't create rediculous water jump reachabilities from areas very far below
- //the water surface
- if (water_bestdist < aassettings.phys_maxwaterjump + 24)
- {
- //waterjumping from or towards a crouch only area is not possible in Quake2
- if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) &&
- (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL))
- {
- //create water jump reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = water_bestarea2groundedgenum;
- VectorCopy(water_beststart, lreach->start);
- VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end);
- lreach->traveltype = TRAVEL_WATERJUMP;
- lreach->traveltime = aassettings.rs_waterjump;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //we've got another waterjump reachability
- reach_waterjump++;
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end if
- //
- // Barrier Jumps
- //
- // ---------
- // |
- // |
- // |
- // | higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP
- //--------|
- //
- // ---------
- // |
- // |
- // |
- //~~~~~~~~| higher than step height lower than barrier height
- //--------| and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP
- //
- //check for a barrier jump reachability
- if (ground_foundreach)
- {
- //if area2 is higher but lower than the maximum barrier jump height
- if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier)
- {
- //if no water in area1 or a very thin layer of water on the ground
- if (!water_foundreach || (ground_bestdist - water_bestdist < 16))
- {
- //cannot perform a barrier jump towards or from a crouch area in Quake2
- if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num))
- {
- //create barrier jump reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = ground_bestarea2groundedgenum;
- VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
- VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
- lreach->traveltype = TRAVEL_BARRIERJUMP;
- lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime();
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //we've got another barrierjump reachability
- reach_barrier++;
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end if
- //
- // Walk and Walk Off Ledge
- //
- //--------|
- // | can walk or step back -> TRAVEL_WALK
- // ---------
- //
- //--------|
- // |
- // |
- // |
- // | cannot walk/step back -> TRAVEL_WALKOFFLEDGE
- // ---------
- //
- //--------|
- // |
- // |~~~~~~~~
- // |
- // | cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE
- // --------- FIXME: create TRAVEL_WALK reach??
- //
- //check for a walk or walk off ledge reachability
- if (ground_foundreach)
- {
- if (ground_bestdist < 0)
- {
- if (ground_bestdist > -aassettings.phys_maxstep)
- {
- //create walk reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = ground_bestarea2groundedgenum;
- VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);
- VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);
- lreach->traveltype = TRAVEL_WALK;
- lreach->traveltime = 1;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //we've got another walk reachability
- reach_walk++;
- return qtrue;
- } //end if
- // if no maximum fall height set or less than the max
- if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) {
- //trace a bounding box vertically to check for solids
- VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend);
- VectorCopy(ground_bestend, start);
- start[2] = ground_beststart[2];
- VectorCopy(ground_bestend, end);
- end[2] += 4;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
- //if no solids were found
- if (!trace.startsolid && trace.fraction >= 1.0)
- {
- //the trace end point must be in the goal area
- trace.endpos[2] += 1;
- if (AAS_PointAreaNum(trace.endpos) == area2num)
- {
- //if not going through a cluster portal
- numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int));
- for (i = 0; i < numareas; i++)
- if (AAS_AreaClusterPortal(areas[i]))
- break;
- if (i >= numareas)
- {
- //create a walk off ledge reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = ground_bestarea2groundedgenum;
- VectorCopy(ground_beststart, lreach->start);
- VectorCopy(ground_bestend, lreach->end);
- lreach->traveltype = TRAVEL_WALKOFFLEDGE;
- lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity;
- //if falling from too high and not falling into water
- if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num))
- {
- if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5)
- {
- lreach->traveltime += aassettings.rs_falldamage5;
- } //end if
- if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10)
- {
- lreach->traveltime += aassettings.rs_falldamage10;
- } //end if
- } //end if
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_walkoffledge++;
- //NOTE: don't create a weapon (rl, bfg) jump reachability here
- //because it interferes with other reachabilities
- //like the ladder reachability
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end if
- } //end else
- } //end if
- return qfalse;
-} //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge
-//===========================================================================
-// returns the distance between the two vectors
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float VectorDistance(vec3_t v1, vec3_t v2)
-{
- vec3_t dir;
-
- VectorSubtract(v2, v1, dir);
- return VectorLength(dir);
-} //end of the function VectorDistance
-//===========================================================================
-// returns true if the first vector is between the last two vectors
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2)
-{
- vec3_t dir1, dir2;
-
- VectorSubtract(v, v1, dir1);
- VectorSubtract(v, v2, dir2);
- return (DotProduct(dir1, dir2) <= 0);
-} //end of the function VectorBetweenVectors
-//===========================================================================
-// returns the mid point between the two vectors
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle)
-{
- VectorAdd(v1, v2, middle);
- VectorScale(middle, 0.5, middle);
-} //end of the function VectorMiddle
-//===========================================================================
-// calculate a range of points closest to each other on both edges
-//
-// Parameter: beststart1 start of the range of points on edge v1-v2
-// beststart2 end of the range of points on edge v1-v2
-// bestend1 start of the range of points on edge v3-v4
-// bestend2 end of the range of points on edge v3-v4
-// bestdist best distance so far
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-/*
-float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
- aas_plane_t *plane1, aas_plane_t *plane2,
- vec3_t beststart, vec3_t bestend, float bestdist)
-{
- vec3_t dir1, dir2, p1, p2, p3, p4;
- float a1, a2, b1, b2, dist;
- int founddist;
-
- //edge vectors
- VectorSubtract(v2, v1, dir1);
- VectorSubtract(v4, v3, dir2);
- //get the horizontal directions
- dir1[2] = 0;
- dir2[2] = 0;
- //
- // p1 = point on an edge vector of area2 closest to v1
- // p2 = point on an edge vector of area2 closest to v2
- // p3 = point on an edge vector of area1 closest to v3
- // p4 = point on an edge vector of area1 closest to v4
- //
- if (dir2[0])
- {
- a2 = dir2[1] / dir2[0];
- b2 = v3[1] - a2 * v3[0];
- //point on the edge vector of area2 closest to v1
- p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
- p1[1] = a2 * p1[0] + b2;
- //point on the edge vector of area2 closest to v2
- p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
- p2[1] = a2 * p2[0] + b2;
- } //end if
- else
- {
- //point on the edge vector of area2 closest to v1
- p1[0] = v3[0];
- p1[1] = v1[1];
- //point on the edge vector of area2 closest to v2
- p2[0] = v3[0];
- p2[1] = v2[1];
- } //end else
- //
- if (dir1[0])
- {
- //
- a1 = dir1[1] / dir1[0];
- b1 = v1[1] - a1 * v1[0];
- //point on the edge vector of area1 closest to v3
- p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
- p3[1] = a1 * p3[0] + b1;
- //point on the edge vector of area1 closest to v4
- p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
- p4[1] = a1 * p4[0] + b1;
- } //end if
- else
- {
- //point on the edge vector of area1 closest to v3
- p3[0] = v1[0];
- p3[1] = v3[1];
- //point on the edge vector of area1 closest to v4
- p4[0] = v1[0];
- p4[1] = v4[1];
- } //end else
- //start with zero z-coordinates
- p1[2] = 0;
- p2[2] = 0;
- p3[2] = 0;
- p4[2] = 0;
- //calculate the z-coordinates from the ground planes
- p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
- p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
- p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
- p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
- //
- founddist = qfalse;
- //
- if (VectorBetweenVectors(p1, v3, v4))
- {
- dist = VectorDistance(v1, p1);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- VectorMiddle(beststart, v1, beststart);
- VectorMiddle(bestend, p1, bestend);
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v1, beststart);
- VectorCopy(p1, bestend);
- } //end if
- founddist = qtrue;
- } //end if
- if (VectorBetweenVectors(p2, v3, v4))
- {
- dist = VectorDistance(v2, p2);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- VectorMiddle(beststart, v2, beststart);
- VectorMiddle(bestend, p2, bestend);
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v2, beststart);
- VectorCopy(p2, bestend);
- } //end if
- founddist = qtrue;
- } //end else if
- if (VectorBetweenVectors(p3, v1, v2))
- {
- dist = VectorDistance(v3, p3);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- VectorMiddle(beststart, p3, beststart);
- VectorMiddle(bestend, v3, bestend);
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(p3, beststart);
- VectorCopy(v3, bestend);
- } //end if
- founddist = qtrue;
- } //end else if
- if (VectorBetweenVectors(p4, v1, v2))
- {
- dist = VectorDistance(v4, p4);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- VectorMiddle(beststart, p4, beststart);
- VectorMiddle(bestend, v4, bestend);
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(p4, beststart);
- VectorCopy(v4, bestend);
- } //end if
- founddist = qtrue;
- } //end else if
- //if no shortest distance was found the shortest distance
- //is between one of the vertexes of edge1 and one of edge2
- if (!founddist)
- {
- dist = VectorDistance(v1, v3);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v1, beststart);
- VectorCopy(v3, bestend);
- } //end if
- dist = VectorDistance(v1, v4);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v1, beststart);
- VectorCopy(v4, bestend);
- } //end if
- dist = VectorDistance(v2, v3);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v2, beststart);
- VectorCopy(v3, bestend);
- } //end if
- dist = VectorDistance(v2, v4);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v2, beststart);
- VectorCopy(v4, bestend);
- } //end if
- } //end if
- return bestdist;
-} //end of the function AAS_ClosestEdgePoints*/
-
-float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,
- aas_plane_t *plane1, aas_plane_t *plane2,
- vec3_t beststart1, vec3_t bestend1,
- vec3_t beststart2, vec3_t bestend2, float bestdist)
-{
- vec3_t dir1, dir2, p1, p2, p3, p4;
- float a1, a2, b1, b2, dist, dist1, dist2;
- int founddist;
-
- //edge vectors
- VectorSubtract(v2, v1, dir1);
- VectorSubtract(v4, v3, dir2);
- //get the horizontal directions
- dir1[2] = 0;
- dir2[2] = 0;
- //
- // p1 = point on an edge vector of area2 closest to v1
- // p2 = point on an edge vector of area2 closest to v2
- // p3 = point on an edge vector of area1 closest to v3
- // p4 = point on an edge vector of area1 closest to v4
- //
- if (dir2[0])
- {
- a2 = dir2[1] / dir2[0];
- b2 = v3[1] - a2 * v3[0];
- //point on the edge vector of area2 closest to v1
- p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
- p1[1] = a2 * p1[0] + b2;
- //point on the edge vector of area2 closest to v2
- p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];
- p2[1] = a2 * p2[0] + b2;
- } //end if
- else
- {
- //point on the edge vector of area2 closest to v1
- p1[0] = v3[0];
- p1[1] = v1[1];
- //point on the edge vector of area2 closest to v2
- p2[0] = v3[0];
- p2[1] = v2[1];
- } //end else
- //
- if (dir1[0])
- {
- //
- a1 = dir1[1] / dir1[0];
- b1 = v1[1] - a1 * v1[0];
- //point on the edge vector of area1 closest to v3
- p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
- p3[1] = a1 * p3[0] + b1;
- //point on the edge vector of area1 closest to v4
- p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];
- p4[1] = a1 * p4[0] + b1;
- } //end if
- else
- {
- //point on the edge vector of area1 closest to v3
- p3[0] = v1[0];
- p3[1] = v3[1];
- //point on the edge vector of area1 closest to v4
- p4[0] = v1[0];
- p4[1] = v4[1];
- } //end else
- //start with zero z-coordinates
- p1[2] = 0;
- p2[2] = 0;
- p3[2] = 0;
- p4[2] = 0;
- //calculate the z-coordinates from the ground planes
- p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];
- p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];
- p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];
- p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];
- //
- founddist = qfalse;
- //
- if (VectorBetweenVectors(p1, v3, v4))
- {
- dist = VectorDistance(v1, p1);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- dist1 = VectorDistance(beststart1, v1);
- dist2 = VectorDistance(beststart2, v1);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1);
- } //end else
- dist1 = VectorDistance(bestend1, p1);
- dist2 = VectorDistance(bestend2, p1);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1);
- } //end else
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v1, beststart1);
- VectorCopy(v1, beststart2);
- VectorCopy(p1, bestend1);
- VectorCopy(p1, bestend2);
- } //end if
- founddist = qtrue;
- } //end if
- if (VectorBetweenVectors(p2, v3, v4))
- {
- dist = VectorDistance(v2, p2);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- dist1 = VectorDistance(beststart1, v2);
- dist2 = VectorDistance(beststart2, v2);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1);
- } //end else
- dist1 = VectorDistance(bestend1, p2);
- dist2 = VectorDistance(bestend2, p2);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1);
- } //end else
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v2, beststart1);
- VectorCopy(v2, beststart2);
- VectorCopy(p2, bestend1);
- VectorCopy(p2, bestend2);
- } //end if
- founddist = qtrue;
- } //end else if
- if (VectorBetweenVectors(p3, v1, v2))
- {
- dist = VectorDistance(v3, p3);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- dist1 = VectorDistance(beststart1, p3);
- dist2 = VectorDistance(beststart2, p3);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1);
- } //end else
- dist1 = VectorDistance(bestend1, v3);
- dist2 = VectorDistance(bestend2, v3);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1);
- } //end else
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(p3, beststart1);
- VectorCopy(p3, beststart2);
- VectorCopy(v3, bestend1);
- VectorCopy(v3, bestend2);
- } //end if
- founddist = qtrue;
- } //end else if
- if (VectorBetweenVectors(p4, v1, v2))
- {
- dist = VectorDistance(v4, p4);
- if (dist > bestdist - 0.5 && dist < bestdist + 0.5)
- {
- dist1 = VectorDistance(beststart1, p4);
- dist2 = VectorDistance(beststart2, p4);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1);
- } //end else
- dist1 = VectorDistance(bestend1, v4);
- dist2 = VectorDistance(bestend2, v4);
- if (dist1 > dist2)
- {
- if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2);
- } //end if
- else
- {
- if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1);
- } //end else
- } //end if
- else if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(p4, beststart1);
- VectorCopy(p4, beststart2);
- VectorCopy(v4, bestend1);
- VectorCopy(v4, bestend2);
- } //end if
- founddist = qtrue;
- } //end else if
- //if no shortest distance was found the shortest distance
- //is between one of the vertexes of edge1 and one of edge2
- if (!founddist)
- {
- dist = VectorDistance(v1, v3);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v1, beststart1);
- VectorCopy(v1, beststart2);
- VectorCopy(v3, bestend1);
- VectorCopy(v3, bestend2);
- } //end if
- dist = VectorDistance(v1, v4);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v1, beststart1);
- VectorCopy(v1, beststart2);
- VectorCopy(v4, bestend1);
- VectorCopy(v4, bestend2);
- } //end if
- dist = VectorDistance(v2, v3);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v2, beststart1);
- VectorCopy(v2, beststart2);
- VectorCopy(v3, bestend1);
- VectorCopy(v3, bestend2);
- } //end if
- dist = VectorDistance(v2, v4);
- if (dist < bestdist)
- {
- bestdist = dist;
- VectorCopy(v2, beststart1);
- VectorCopy(v2, beststart2);
- VectorCopy(v4, bestend1);
- VectorCopy(v4, bestend2);
- } //end if
- } //end if
- return bestdist;
-} //end of the function AAS_ClosestEdgePoints
-//===========================================================================
-// creates possible jump reachabilities between the areas
-//
-// The two closest points on the ground of the areas are calculated
-// One of the points will be on an edge of a ground face of area1 and
-// one on an edge of a ground face of area2.
-// If there is a range of closest points the point in the middle of this range
-// is selected.
-// Between these two points there must be one or more gaps.
-// If the gaps exist a potential jump is predicted.
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_Jump(int area1num, int area2num)
-{
- int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype;
- int stopevent, areas[10], numareas;
- float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed;
- vec_t *v1, *v2, *v3, *v4;
- vec3_t beststart, beststart2, bestend, bestend2;
- vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards;
- aas_area_t *area1, *area2;
- aas_face_t *face1, *face2;
- aas_edge_t *edge1, *edge2;
- aas_plane_t *plane1, *plane2, *plane;
- aas_trace_t trace;
- aas_clientmove_t move;
- aas_lreachability_t *lreach;
-
- if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;
- //cannot jump from or to a crouch area
- if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse;
- //
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
- //
- phys_jumpvel = aassettings.phys_jumpvel;
- //maximum distance a player can jump
- maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel);
- //maximum height a player can jump with the given initial z velocity
- maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
-
- //if the areas are not near anough in the x-y direction
- for (i = 0; i < 2; i++)
- {
- if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse;
- if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse;
- } //end for
- //if area2 is way to high to jump up to
- if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse;
- //
- bestdist = 999999;
- //
- for (i = 0; i < area1->numfaces; i++)
- {
- face1num = aasworld.faceindex[area1->firstface + i];
- face1 = &aasworld.faces[abs(face1num)];
- //if not a ground face
- if (!(face1->faceflags & FACE_GROUND)) continue;
- //
- for (j = 0; j < area2->numfaces; j++)
- {
- face2num = aasworld.faceindex[area2->firstface + j];
- face2 = &aasworld.faces[abs(face2num)];
- //if not a ground face
- if (!(face2->faceflags & FACE_GROUND)) continue;
- //
- for (k = 0; k < face1->numedges; k++)
- {
- edge1num = abs(aasworld.edgeindex[face1->firstedge + k]);
- edge1 = &aasworld.edges[edge1num];
- for (l = 0; l < face2->numedges; l++)
- {
- edge2num = abs(aasworld.edgeindex[face2->firstedge + l]);
- edge2 = &aasworld.edges[edge2num];
- //calculate the minimum distance between the two edges
- v1 = aasworld.vertexes[edge1->v[0]];
- v2 = aasworld.vertexes[edge1->v[1]];
- v3 = aasworld.vertexes[edge2->v[0]];
- v4 = aasworld.vertexes[edge2->v[1]];
- //get the ground planes
- plane1 = &aasworld.planes[face1->planenum];
- plane2 = &aasworld.planes[face2->planenum];
- //
- bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2,
- beststart, bestend,
- beststart2, bestend2, bestdist);
- } //end for
- } //end for
- } //end for
- } //end for
- VectorMiddle(beststart, beststart2, beststart);
- VectorMiddle(bestend, bestend2, bestend);
- if (bestdist > 4 && bestdist < maxjumpdistance)
- {
-// Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist);
- // if very close and almost no height difference then the bot can walk
- if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8)
- {
- speed = 400;
- traveltype = TRAVEL_WALKOFFLEDGE;
- } //end if
- else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed))
- {
- //FIXME: why multiply with 1.2???
- speed *= 1.2f;
- traveltype = TRAVEL_WALKOFFLEDGE;
- } //end else if
- else
- {
- //get the horizontal speed for the jump, if it isn't possible to calculate this
- //speed (the jump is not possible) then there's no jump reachability created
- if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed))
- return qfalse;
- speed *= 1.05f;
- traveltype = TRAVEL_JUMP;
- //
- //NOTE: test if the horizontal distance isn't too small
- VectorSubtract(bestend, beststart, dir);
- dir[2] = 0;
- if (VectorLength(dir) < 10)
- return qfalse;
- } //end if
- //
- VectorSubtract(bestend, beststart, dir);
- VectorNormalize(dir);
- VectorMA(beststart, 1, dir, teststart);
- //
- VectorCopy(teststart, testend);
- testend[2] -= 100;
- trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
- //
- if (trace.startsolid)
- return qfalse;
- if (trace.fraction < 1)
- {
- plane = &aasworld.planes[trace.planenum];
- // if the bot can stand on the surface
- if (DotProduct(plane->normal, up) >= 0.7)
- {
- // if no lava or slime below
- if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
- {
- if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
- return qfalse;
- } //end if
- } //end if
- } //end if
- //
- VectorMA(bestend, -1, dir, teststart);
- //
- VectorCopy(teststart, testend);
- testend[2] -= 100;
- trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);
- //
- if (trace.startsolid)
- return qfalse;
- if (trace.fraction < 1)
- {
- plane = &aasworld.planes[trace.planenum];
- // if the bot can stand on the surface
- if (DotProduct(plane->normal, up) >= 0.7)
- {
- // if no lava or slime below
- if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))
- {
- if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)
- return qfalse;
- } //end if
- } //end if
- } //end if
- //
- // get command movement
- VectorClear(cmdmove);
- if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
- cmdmove[2] = aassettings.phys_jumpvel;
- else
- cmdmove[2] = 0;
- //
- VectorSubtract(bestend, beststart, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- CrossProduct(dir, up, sidewards);
- //
- stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE;
- if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num))
- stopevent |= SE_TOUCHCLUSTERPORTAL;
- //
- for (i = 0; i < 3; i++)
- {
- //
- if (i == 1)
- VectorAdd(testend, sidewards, testend);
- else if (i == 2)
- VectorSubtract(bestend, sidewards, testend);
- else
- VectorCopy(bestend, testend);
- VectorSubtract(testend, beststart, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- VectorScale(dir, speed, velocity);
- //
- AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue,
- velocity, cmdmove, 3, 30, 0.1f,
- stopevent, 0, qfalse);
- // if prediction time wasn't enough to fully predict the movement
- if (move.frames >= 30)
- return qfalse;
- // don't enter slime or lava and don't fall from too high
- if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
- return qfalse;
- // never jump or fall through a cluster portal
- if (move.stopevent & SE_TOUCHCLUSTERPORTAL)
- return qfalse;
- //the end position should be in area2, also test a little bit back
- //because the predicted jump could have rushed through the area
- VectorMA(move.endpos, -64, dir, teststart);
- teststart[2] += 1;
- numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int));
- for (j = 0; j < numareas; j++)
- {
- if (areas[j] == area2num)
- break;
- } //end for
- if (j < numareas)
- break;
- }
- if (i >= 3)
- return qfalse;
- //
-#ifdef REACH_DEBUG
- //create the reachability
- Log_Write("jump reachability between %d and %d\r\n", area1num, area2num);
-#endif //REACH_DEBUG
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = 0;
- VectorCopy(beststart, lreach->start);
- VectorCopy(bestend, lreach->end);
- lreach->traveltype = traveltype;
-
- VectorSubtract(bestend, beststart, dir);
- height = dir[2];
- dir[2] = 0;
- if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir))
- {
- lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity;
- }
- else
- {
- lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity;
- } //end if
- //
- if (!AAS_AreaJumpPad(area2num))
- {
- if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5)
- {
- lreach->traveltime += aassettings.rs_falldamage5;
- } //end if
- else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10)
- {
- lreach->traveltime += aassettings.rs_falldamage10;
- } //end if
- } //end if
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)
- reach_jump++;
- else
- reach_walkoffledge++;
- } //end if
- return qfalse;
-} //end of the function AAS_Reachability_Jump
-//===========================================================================
-// create a possible ladder reachability from area1 to area2
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_Ladder(int area1num, int area2num)
-{
- int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum;
- int face1num, face2num, ladderface1num, ladderface2num;
- int ladderface1vertical, ladderface2vertical, firstv;
- float face1area, face2area, bestface1area, bestface2area;
- float phys_jumpvel, maxjumpheight;
- vec3_t area1point, area2point, v1, v2, up = {0, 0, 1};
- vec3_t mid, lowestpoint, start, end, sharededgevec, dir;
- aas_area_t *area1, *area2;
- aas_face_t *face1, *face2, *ladderface1, *ladderface2;
- aas_plane_t *plane1, *plane2;
- aas_edge_t *sharededge, *edge1;
- aas_lreachability_t *lreach;
- aas_trace_t trace;
-
- if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse;
- //
- phys_jumpvel = aassettings.phys_jumpvel;
- //maximum height a player can jump with the given initial z velocity
- maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);
-
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
- //
- ladderface1 = NULL;
- ladderface2 = NULL;
- ladderface1num = 0; //make compiler happy
- ladderface2num = 0; //make compiler happy
- bestface1area = -9999;
- bestface2area = -9999;
- sharededgenum = 0; //make compiler happy
- lowestedgenum = 0; //make compiler happy
- //
- for (i = 0; i < area1->numfaces; i++)
- {
- face1num = aasworld.faceindex[area1->firstface + i];
- face1 = &aasworld.faces[abs(face1num)];
- //if not a ladder face
- if (!(face1->faceflags & FACE_LADDER)) continue;
- //
- for (j = 0; j < area2->numfaces; j++)
- {
- face2num = aasworld.faceindex[area2->firstface + j];
- face2 = &aasworld.faces[abs(face2num)];
- //if not a ladder face
- if (!(face2->faceflags & FACE_LADDER)) continue;
- //check if the faces share an edge
- for (k = 0; k < face1->numedges; k++)
- {
- edge1num = aasworld.edgeindex[face1->firstedge + k];
- for (l = 0; l < face2->numedges; l++)
- {
- edge2num = aasworld.edgeindex[face2->firstedge + l];
- if (abs(edge1num) == abs(edge2num))
- {
- //get the face with the largest area
- face1area = AAS_FaceArea(face1);
- face2area = AAS_FaceArea(face2);
- if (face1area > bestface1area && face2area > bestface2area)
- {
- bestface1area = face1area;
- bestface2area = face2area;
- ladderface1 = face1;
- ladderface2 = face2;
- ladderface1num = face1num;
- ladderface2num = face2num;
- sharededgenum = edge1num;
- } //end if
- break;
- } //end if
- } //end for
- if (l != face2->numedges) break;
- } //end for
- } //end for
- } //end for
- //
- if (ladderface1 && ladderface2)
- {
- //get the middle of the shared edge
- sharededge = &aasworld.edges[abs(sharededgenum)];
- firstv = sharededgenum < 0;
- //
- VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1);
- VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2);
- VectorAdd(v1, v2, area1point);
- VectorScale(area1point, 0.5, area1point);
- VectorCopy(area1point, area2point);
- //
- //if the face plane in area 1 is pretty much vertical
- plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)];
- plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)];
- //
- //get the points really into the areas
- VectorSubtract(v2, v1, sharededgevec);
- CrossProduct(plane1->normal, sharededgevec, dir);
- VectorNormalize(dir);
- //NOTE: 32 because that's larger than 16 (bot bbox x,y)
- VectorMA(area1point, -32, dir, area1point);
- VectorMA(area2point, 32, dir, area2point);
- //
- ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1;
- ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1;
- //there's only reachability between vertical ladder faces
- if (!ladderface1vertical && !ladderface2vertical) return qfalse;
- //if both vertical ladder faces
- if (ladderface1vertical && ladderface2vertical
- //and the ladder faces do not make a sharp corner
- && DotProduct(plane1->normal, plane2->normal) > 0.7
- //and the shared edge is not too vertical
- && abs(DotProduct(sharededgevec, up)) < 0.7)
- {
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = ladderface1num;
- lreach->edgenum = abs(sharededgenum);
- VectorCopy(area1point, lreach->start);
- //VectorCopy(area2point, lreach->end);
- VectorMA(area2point, -3, plane1->normal, lreach->end);
- lreach->traveltype = TRAVEL_LADDER;
- lreach->traveltime = 10;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_ladder++;
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area1num;
- lreach->facenum = ladderface2num;
- lreach->edgenum = abs(sharededgenum);
- VectorCopy(area2point, lreach->start);
- //VectorCopy(area1point, lreach->end);
- VectorMA(area1point, -3, plane1->normal, lreach->end);
- lreach->traveltype = TRAVEL_LADDER;
- lreach->traveltime = 10;
- lreach->next = areareachability[area2num];
- areareachability[area2num] = lreach;
- //
- reach_ladder++;
- //
- return qtrue;
- } //end if
- //if the second ladder face is also a ground face
- //create ladder end (just ladder) reachability and
- //walk off a ladder (ledge) reachability
- if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND))
- {
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = ladderface1num;
- lreach->edgenum = abs(sharededgenum);
- VectorCopy(area1point, lreach->start);
- VectorCopy(area2point, lreach->end);
- lreach->end[2] += 16;
- VectorMA(lreach->end, -15, plane1->normal, lreach->end);
- lreach->traveltype = TRAVEL_LADDER;
- lreach->traveltime = 10;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_ladder++;
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area1num;
- lreach->facenum = ladderface2num;
- lreach->edgenum = abs(sharededgenum);
- VectorCopy(area2point, lreach->start);
- VectorCopy(area1point, lreach->end);
- lreach->traveltype = TRAVEL_WALKOFFLEDGE;
- lreach->traveltime = 10;
- lreach->next = areareachability[area2num];
- areareachability[area2num] = lreach;
- //
- reach_walkoffledge++;
- //
- return qtrue;
- } //end if
- //
- if (ladderface1vertical)
- {
- //find lowest edge of the ladder face
- lowestpoint[2] = 99999;
- for (i = 0; i < ladderface1->numedges; i++)
- {
- edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]);
- edge1 = &aasworld.edges[edge1num];
- //
- VectorCopy(aasworld.vertexes[edge1->v[0]], v1);
- VectorCopy(aasworld.vertexes[edge1->v[1]], v2);
- //
- VectorAdd(v1, v2, mid);
- VectorScale(mid, 0.5, mid);
- //
- if (mid[2] < lowestpoint[2])
- {
- VectorCopy(mid, lowestpoint);
- lowestedgenum = edge1num;
- } //end if
- } //end for
- //
- plane1 = &aasworld.planes[ladderface1->planenum];
- //trace down in the middle of this edge
- VectorMA(lowestpoint, 5, plane1->normal, start);
- VectorCopy(start, end);
- start[2] += 5;
- end[2] -= 100;
- //trace without entity collision
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
- //
- //
-#ifdef REACH_DEBUG
- if (trace.startsolid)
- {
- Log_Write("trace from area %d started in solid\r\n", area1num);
- } //end if
-#endif //REACH_DEBUG
- //
- trace.endpos[2] += 1;
- area2num = AAS_PointAreaNum(trace.endpos);
- //
- area2 = &aasworld.areas[area2num];
- for (i = 0; i < area2->numfaces; i++)
- {
- face2num = aasworld.faceindex[area2->firstface + i];
- face2 = &aasworld.faces[abs(face2num)];
- //
- if (face2->faceflags & FACE_LADDER)
- {
- plane2 = &aasworld.planes[face2->planenum];
- if (abs(DotProduct(plane2->normal, up)) < 0.1) break;
- } //end if
- } //end for
- //if from another area without vertical ladder faces
- if (i >= area2->numfaces && area2num != area1num &&
- //the reachabilities shouldn't exist already
- !AAS_ReachabilityExists(area1num, area2num) &&
- !AAS_ReachabilityExists(area2num, area1num))
- {
- //if the height is jumpable
- if (start[2] - trace.endpos[2] < maxjumpheight)
- {
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = ladderface1num;
- lreach->edgenum = lowestedgenum;
- VectorCopy(lowestpoint, lreach->start);
- VectorCopy(trace.endpos, lreach->end);
- lreach->traveltype = TRAVEL_LADDER;
- lreach->traveltime = 10;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_ladder++;
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area1num;
- lreach->facenum = ladderface1num;
- lreach->edgenum = lowestedgenum;
- VectorCopy(trace.endpos, lreach->start);
- //get the end point a little bit into the ladder
- VectorMA(lowestpoint, -5, plane1->normal, lreach->end);
- //get the end point a little higher
- lreach->end[2] += 10;
- lreach->traveltype = TRAVEL_JUMP;
- lreach->traveltime = 10;
- lreach->next = areareachability[area2num];
- areareachability[area2num] = lreach;
- //
- reach_jump++;
- //
- return qtrue;
-#ifdef REACH_DEBUG
- Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num);
-#endif //REACH_DEBUG
- } //end if
-#ifdef REACH_DEBUG
- else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num);
-#endif //REACH_DEBUG
- } //end if
- /*//if slime or lava below the ladder
- //try jump reachability from far towards the ladder
- if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME
- | AREACONTENTS_LAVA))
- {
- for (i = 20; i <= 120; i += 20)
- {
- //trace down in the middle of this edge
- VectorMA(lowestpoint, i, plane1->normal, start);
- VectorCopy(start, end);
- start[2] += 5;
- end[2] -= 100;
- //trace without entity collision
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
- //
- if (trace.startsolid) break;
- trace.endpos[2] += 1;
- area2num = AAS_PointAreaNum(trace.endpos);
- if (area2num == area1num) continue;
- //
- if (start[2] - trace.endpos[2] > maxjumpheight) continue;
- if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME
- | AREACONTENTS_LAVA)) continue;
- //
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area1num;
- lreach->facenum = ladderface1num;
- lreach->edgenum = lowestedgenum;
- VectorCopy(trace.endpos, lreach->start);
- VectorCopy(lowestpoint, lreach->end);
- lreach->end[2] += 5;
- lreach->traveltype = TRAVEL_JUMP;
- lreach->traveltime = 10;
- lreach->next = areareachability[area2num];
- areareachability[area2num] = lreach;
- //
- reach_jump++;
- //
- Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num);
- //
- break;
- } //end for
- } //end if*/
- } //end if
- } //end if
- return qfalse;
-} //end of the function AAS_Reachability_Ladder
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_TravelFlagsForTeam(int ent)
-{
- int notteam;
-
- if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team))
- return 0;
- if (notteam == 1)
- return TRAVELFLAG_NOTTEAM1;
- if (notteam == 2)
- return TRAVELFLAG_NOTTEAM2;
- return 0;
-} //end of the function AAS_TravelFlagsForTeam
-//===========================================================================
-// create possible teleporter reachabilities
-// this is very game dependent.... :(
-//
-// classname = trigger_multiple or trigger_teleport
-// target = "t1"
-//
-// classname = target_teleporter
-// targetname = "t1"
-// target = "t2"
-//
-// classname = misc_teleporter_dest
-// targetname = "t2"
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Reachability_Teleport(void)
-{
- int area1num, area2num;
- char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];
- char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
- int ent, dest;
- float angle;
- vec3_t origin, destorigin, mins, maxs, end, angles;
- vec3_t mid, velocity, cmdmove;
- aas_lreachability_t *lreach;
- aas_clientmove_t move;
- aas_trace_t trace;
- aas_link_t *areas, *link;
-
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (!strcmp(classname, "trigger_multiple"))
- {
- AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
-//#ifdef REACH_DEBUG
- botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model);
-//#endif REACH_DEBUG
- VectorClear(angles);
- AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
- //
- if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
- {
- botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n",
- origin[0], origin[1], origin[2]);
- continue;
- } //end if
- for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
- {
- if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue;
- if (!strcmp(classname, "target_teleporter"))
- {
- if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue;
- if (!strcmp(targetname, target))
- {
- break;
- } //end if
- } //end if
- } //end for
- if (!dest)
- {
- continue;
- } //end if
- if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY))
- {
- botimport.Print(PRT_ERROR, "target_teleporter without target\n");
- continue;
- } //end if
- } //end else
- else if (!strcmp(classname, "trigger_teleport"))
- {
- AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
-//#ifdef REACH_DEBUG
- botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model);
-//#endif REACH_DEBUG
- VectorClear(angles);
- AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);
- //
- if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY))
- {
- botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n",
- origin[0], origin[1], origin[2]);
- continue;
- } //end if
- } //end if
- else
- {
- continue;
- } //end else
- //
- for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))
- {
- //classname should be misc_teleporter_dest
- //but I've also seen target_position and actually any
- //entity could be used... burp
- if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY))
- {
- if (!strcmp(targetname, target))
- {
- break;
- } //end if
- } //end if
- } //end for
- if (!dest)
- {
- botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target);
- continue;
- } //end if
- if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin))
- {
- botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target);
- continue;
- } //end if
- //
- area2num = AAS_PointAreaNum(destorigin);
- //if not teleported into a teleporter or into a jumppad
- if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num))
- {
- VectorCopy(destorigin, end);
- end[2] -= 64;
- trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);
- if (trace.startsolid)
- {
- botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target);
- continue;
- } //end if
- area2num = AAS_PointAreaNum(trace.endpos);
- //
- /*
- if (!AAS_AreaTeleporter(area2num) &&
- !AAS_AreaJumpPad(area2num) &&
- !AAS_AreaGrounded(area2num))
- {
- VectorCopy(trace.endpos, destorigin);
- }
- else*/
- {
- //predict where you'll end up
- AAS_FloatForBSPEpairKey(dest, "angle", &angle);
- if (angle)
- {
- VectorSet(angles, 0, angle, 0);
- AngleVectors(angles, velocity, NULL, NULL);
- VectorScale(velocity, 400, velocity);
- } //end if
- else
- {
- VectorClear(velocity);
- } //end else
- VectorClear(cmdmove);
- AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse,
- velocity, cmdmove, 0, 30, 0.1f,
- SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue);
- area2num = AAS_PointAreaNum(move.endpos);
- if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))
- {
- botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target);
- } //end if
- VectorCopy(move.endpos, destorigin);
- } //end else
- } //end if
- //
- //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]);
- //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]);
- //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]);
- VectorAdd(origin, mins, mins);
- VectorAdd(origin, maxs, maxs);
- //
- VectorAdd(mins, maxs, mid);
- VectorScale(mid, 0.5, mid);
- //link an invalid (-1) entity
- areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH);
- if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n");
- //
- for (link = areas; link; link = link->next_area)
- {
- //if (!AAS_AreaGrounded(link->areanum)) continue;
- if (!AAS_AreaTeleporter(link->areanum)) continue;
- //
- area1num = link->areanum;
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) break;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = 0;
- VectorCopy(mid, lreach->start);
- VectorCopy(destorigin, lreach->end);
- lreach->traveltype = TRAVEL_TELEPORT;
- lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
- lreach->traveltime = aassettings.rs_teleport;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_teleport++;
- } //end for
- //unlink the invalid entity
- AAS_UnlinkFromAreas(areas);
- } //end for
-} //end of the function AAS_Reachability_Teleport
-//===========================================================================
-// create possible elevator (func_plat) reachabilities
-// this is very game dependent.... :(
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Reachability_Elevator(void)
-{
- int area1num, area2num, modelnum, i, j, k, l, n, p;
- float lip, height, speed;
- char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY];
- int ent;
- vec3_t mins, maxs, origin, angles = {0, 0, 0};
- vec3_t pos1, pos2, mids, platbottom, plattop;
- vec3_t bottomorg, toporg, start, end, dir;
- vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8];
- aas_lreachability_t *lreach;
- aas_trace_t trace;
-
-#ifdef REACH_DEBUG
- Log_Write("AAS_Reachability_Elevator\r\n");
-#endif //REACH_DEBUG
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (!strcmp(classname, "func_plat"))
- {
-#ifdef REACH_DEBUG
- Log_Write("found func plat\r\n");
-#endif //REACH_DEBUG
- if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
- {
- botimport.Print(PRT_ERROR, "func_plat without model\n");
- continue;
- } //end if
- //get the model number, and skip the leading *
- modelnum = atoi(model+1);
- if (modelnum <= 0)
- {
- botimport.Print(PRT_ERROR, "func_plat with invalid model number\n");
- continue;
- } //end if
- //get the mins, maxs and origin of the model
- //NOTE: the origin is usually (0,0,0) and the mins and maxs
- // are the absolute mins and maxs
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
- //
- AAS_VectorForBSPEpairKey(ent, "origin", origin);
- //pos1 is the top position, pos2 is the bottom
- VectorCopy(origin, pos1);
- VectorCopy(origin, pos2);
- //get the lip of the plat
- AAS_FloatForBSPEpairKey(ent, "lip", &lip);
- if (!lip) lip = 8;
- //get the movement height of the plat
- AAS_FloatForBSPEpairKey(ent, "height", &height);
- if (!height) height = (maxs[2] - mins[2]) - lip;
- //get the speed of the plat
- AAS_FloatForBSPEpairKey(ent, "speed", &speed);
- if (!speed) speed = 200;
- //get bottom position below pos1
- pos2[2] -= height;
- //
- //get a point just above the plat in the bottom position
- VectorAdd(mins, maxs, mids);
- VectorMA(pos2, 0.5, mids, platbottom);
- platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2;
- //get a point just above the plat in the top position
- VectorAdd(mins, maxs, mids);
- VectorMA(pos2, 0.5, mids, plattop);
- plattop[2] = maxs[2] + 2;
- //
- /*if (!area1num)
- {
- Log_Write("no grounded area near plat bottom\r\n");
- continue;
- } //end if*/
- //get the mins and maxs a little larger
- for (i = 0; i < 3; i++)
- {
- mins[i] -= 1;
- maxs[i] += 1;
- } //end for
- //
- //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]);
- //
- VectorAdd(mins, maxs, mids);
- VectorScale(mids, 0.5, mids);
- //
- xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0];
- yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1];
- //
- xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0];
- yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1];
- //find adjacent areas around the bottom of the plat
- for (i = 0; i < 9; i++)
- {
- if (i < 8) //check at the sides of the plat
- {
- bottomorg[0] = origin[0] + xvals[i];
- bottomorg[1] = origin[1] + yvals[i];
- bottomorg[2] = platbottom[2] + 16;
- //get a grounded or swim area near the plat in the bottom position
- area1num = AAS_PointAreaNum(bottomorg);
- for (k = 0; k < 16; k++)
- {
- if (area1num)
- {
- if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break;
- } //end if
- bottomorg[2] += 4;
- area1num = AAS_PointAreaNum(bottomorg);
- } //end if
- //if in solid
- if (k >= 16)
- {
- continue;
- } //end if
- } //end if
- else //at the middle of the plat
- {
- VectorCopy(plattop, bottomorg);
- bottomorg[2] += 24;
- area1num = AAS_PointAreaNum(bottomorg);
- if (!area1num) continue;
- VectorCopy(platbottom, bottomorg);
- bottomorg[2] += 24;
- } //end else
- //look at adjacent areas around the top of the plat
- //make larger steps to outside the plat everytime
- for (n = 0; n < 3; n++)
- {
- for (k = 0; k < 3; k++)
- {
- mins[k] -= 4;
- maxs[k] += 4;
- } //end for
- xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0];
- yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1];
- //
- xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0];
- yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1];
- //
- for (j = 0; j < 8; j++)
- {
- toporg[0] = origin[0] + xvals_top[j];
- toporg[1] = origin[1] + yvals_top[j];
- toporg[2] = plattop[2] + 16;
- //get a grounded or swim area near the plat in the top position
- area2num = AAS_PointAreaNum(toporg);
- for (l = 0; l < 16; l++)
- {
- if (area2num)
- {
- if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num))
- {
- VectorCopy(plattop, start);
- start[2] += 32;
- VectorCopy(toporg, end);
- end[2] += 1;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
- if (trace.fraction >= 1) break;
- } //end if
- } //end if
- toporg[2] += 4;
- area2num = AAS_PointAreaNum(toporg);
- } //end if
- //if in solid
- if (l >= 16) continue;
- //never create a reachability in the same area
- if (area2num == area1num) continue;
- //if the area isn't grounded
- if (!AAS_AreaGrounded(area2num)) continue;
- //if there already exists reachability between the areas
- if (AAS_ReachabilityExists(area1num, area2num)) continue;
- //if the reachability start is within the elevator bounding box
- VectorSubtract(bottomorg, platbottom, dir);
- VectorNormalize(dir);
- dir[0] = bottomorg[0] + 24 * dir[0];
- dir[1] = bottomorg[1] + 24 * dir[1];
- dir[2] = bottomorg[2];
- //
- for (p = 0; p < 3; p++)
- if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break;
- if (p >= 3) continue;
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) continue;
- lreach->areanum = area2num;
- //the facenum is the model number
- lreach->facenum = modelnum;
- //the edgenum is the height
- lreach->edgenum = (int) height;
- //
- VectorCopy(dir, lreach->start);
- VectorCopy(toporg, lreach->end);
- lreach->traveltype = TRAVEL_ELEVATOR;
- lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
- lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //don't go any further to the outside
- n = 9999;
- //
-#ifdef REACH_DEBUG
- Log_Write("elevator reach from %d to %d\r\n", area1num, area2num);
-#endif //REACH_DEBUG
- //
- reach_elevator++;
- } //end for
- } //end for
- } //end for
- } //end if
- } //end for
-} //end of the function AAS_Reachability_Elevator
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface)
-{
- int i, j, k, l;
- int facenum, edgenum, bestfacenum;
- float *v1, *v2, *v3, *v4;
- float bestdist, speed, hordist, dist;
- vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint;
- aas_lreachability_t *lreach, *lreachabilities;
- aas_area_t *area;
- aas_face_t *face;
- aas_edge_t *edge;
- aas_plane_t *faceplane, *bestfaceplane;
-
- //
- lreachabilities = NULL;
- bestfacenum = 0;
- bestfaceplane = NULL;
- //
- for (i = 1; i < aasworld.numareas; i++)
- {
- area = &aasworld.areas[i];
- // get the shortest distance between one of the func_bob start edges and
- // one of the face edges of area1
- bestdist = 999999;
- for (j = 0; j < area->numfaces; j++)
- {
- facenum = aasworld.faceindex[area->firstface + j];
- face = &aasworld.faces[abs(facenum)];
- //if not a ground face
- if (!(face->faceflags & FACE_GROUND)) continue;
- //get the ground planes
- faceplane = &aasworld.planes[face->planenum];
- //
- for (k = 0; k < face->numedges; k++)
- {
- edgenum = abs(aasworld.edgeindex[face->firstedge + k]);
- edge = &aasworld.edges[edgenum];
- //calculate the minimum distance between the two edges
- v1 = aasworld.vertexes[edge->v[0]];
- v2 = aasworld.vertexes[edge->v[1]];
- //
- for (l = 0; l < numpoints; l++)
- {
- v3 = facepoints[l];
- v4 = facepoints[(l+1) % numpoints];
- dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane,
- beststart, bestend,
- beststart2, bestend2, bestdist);
- if (dist < bestdist)
- {
- bestfacenum = facenum;
- bestfaceplane = faceplane;
- bestdist = dist;
- } //end if
- } //end for
- } //end for
- } //end for
- //
- if (bestdist > 192) continue;
- //
- VectorMiddle(beststart, beststart2, beststart);
- VectorMiddle(bestend, bestend2, bestend);
- //
- if (!towardsface)
- {
- VectorCopy(beststart, tmp);
- VectorCopy(bestend, beststart);
- VectorCopy(tmp, bestend);
- } //end if
- //
- VectorSubtract(bestend, beststart, hordir);
- hordir[2] = 0;
- hordist = VectorLength(hordir);
- //
- if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue;
- //the end point should not be significantly higher than the start point
- if (bestend[2] - 32 > beststart[2]) continue;
- //don't fall down too far
- if (bestend[2] < beststart[2] - 128) continue;
- //the distance should not be too far
- if (hordist > 32)
- {
- //check for walk off ledge
- if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue;
- } //end if
- //
- beststart[2] += 1;
- bestend[2] += 1;
- //
- if (towardsface) VectorCopy(bestend, testpoint);
- else VectorCopy(beststart, testpoint);
- testpoint[2] = 0;
- testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2];
- //
- if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f))
- {
- //if the faces are not overlapping then only go down
- if (bestend[2] - 16 > beststart[2]) continue;
- } //end if
- lreach = AAS_AllocReachability();
- if (!lreach) return lreachabilities;
- lreach->areanum = i;
- lreach->facenum = 0;
- lreach->edgenum = 0;
- VectorCopy(beststart, lreach->start);
- VectorCopy(bestend, lreach->end);
- lreach->traveltype = 0;
- lreach->traveltime = 0;
- lreach->next = lreachabilities;
- lreachabilities = lreach;
-#ifndef BSPC
- if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1);
- else AAS_PermanentLine(lreach->start, lreach->end, 2);
-#endif
- } //end for
- return lreachabilities;
-} //end of the function AAS_FindFaceReachabilities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Reachability_FuncBobbing(void)
-{
- int ent, spawnflags, modelnum, axis;
- int i, numareas, areas[10];
- char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
- vec3_t origin, move_end, move_start, move_start_top, move_end_top;
- vec3_t mins, maxs, angles = {0, 0, 0};
- vec3_t start_edgeverts[4], end_edgeverts[4], mid;
- vec3_t org, start, end, dir, points[10];
- float height;
- aas_plane_t start_plane, end_plane;
- aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach;
- aas_lreachability_t *firststartreach, *firstendreach;
-
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (strcmp(classname, "func_bobbing")) continue;
- AAS_FloatForBSPEpairKey(ent, "height", &height);
- if (!height) height = 32;
- //
- if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY))
- {
- botimport.Print(PRT_ERROR, "func_bobbing without model\n");
- continue;
- } //end if
- //get the model number, and skip the leading *
- modelnum = atoi(model+1);
- if (modelnum <= 0)
- {
- botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n");
- continue;
- } //end if
- //if the entity has an origin set then use it
- if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
- VectorSet(origin, 0, 0, 0);
- //
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
- //
- VectorAdd(mins, origin, mins);
- VectorAdd(maxs, origin, maxs);
- //
- VectorAdd(mins, maxs, mid);
- VectorScale(mid, 0.5, mid);
- VectorCopy(mid, origin);
- //
- VectorCopy(origin, move_end);
- VectorCopy(origin, move_start);
- //
- AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
- // set the axis of bobbing
- if (spawnflags & 1) axis = 0;
- else if (spawnflags & 2) axis = 1;
- else axis = 2;
- //
- move_start[axis] -= height;
- move_end[axis] += height;
- //
- Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n",
- modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]);
- //
-#ifndef BSPC
- /*
- AAS_DrawPermanentCross(move_start, 4, 1);
- AAS_DrawPermanentCross(move_end, 4, 2);
- */
-#endif
- //
- for (i = 0; i < 4; i++)
- {
- VectorCopy(move_start, start_edgeverts[i]);
- start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z
- start_edgeverts[i][2] += 24; //+ player origin to ground dist
- } //end for
- start_edgeverts[0][0] += maxs[0] - mid[0];
- start_edgeverts[0][1] += maxs[1] - mid[1];
- start_edgeverts[1][0] += maxs[0] - mid[0];
- start_edgeverts[1][1] += mins[1] - mid[1];
- start_edgeverts[2][0] += mins[0] - mid[0];
- start_edgeverts[2][1] += mins[1] - mid[1];
- start_edgeverts[3][0] += mins[0] - mid[0];
- start_edgeverts[3][1] += maxs[1] - mid[1];
- //
- start_plane.dist = start_edgeverts[0][2];
- VectorSet(start_plane.normal, 0, 0, 1);
- //
- for (i = 0; i < 4; i++)
- {
- VectorCopy(move_end, end_edgeverts[i]);
- end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z
- end_edgeverts[i][2] += 24; //+ player origin to ground dist
- } //end for
- end_edgeverts[0][0] += maxs[0] - mid[0];
- end_edgeverts[0][1] += maxs[1] - mid[1];
- end_edgeverts[1][0] += maxs[0] - mid[0];
- end_edgeverts[1][1] += mins[1] - mid[1];
- end_edgeverts[2][0] += mins[0] - mid[0];
- end_edgeverts[2][1] += mins[1] - mid[1];
- end_edgeverts[3][0] += mins[0] - mid[0];
- end_edgeverts[3][1] += maxs[1] - mid[1];
- //
- end_plane.dist = end_edgeverts[0][2];
- VectorSet(end_plane.normal, 0, 0, 1);
- //
-#ifndef BSPC
-#if 0
- for (i = 0; i < 4; i++)
- {
- AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1);
- AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1);
- } //end for
-#endif
-#endif
- VectorCopy(move_start, move_start_top);
- move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z
- VectorCopy(move_end, move_end_top);
- move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z
- //
- if (!AAS_PointAreaNum(move_start_top)) continue;
- if (!AAS_PointAreaNum(move_end_top)) continue;
- //
- for (i = 0; i < 2; i++)
- {
- firststartreach = firstendreach = NULL;
- //
- if (i == 0)
- {
- firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue);
- firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse);
- } //end if
- else
- {
- firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue);
- firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse);
- } //end else
- //
- //create reachabilities from start to end
- for (startreach = firststartreach; startreach; startreach = nextstartreach)
- {
- nextstartreach = startreach->next;
- //
- //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1);
- //if (trace.fraction < 1) continue;
- //
- for (endreach = firstendreach; endreach; endreach = nextendreach)
- {
- nextendreach = endreach->next;
- //
- //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1);
- //if (trace.fraction < 1) continue;
- //
- Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum);
- //
- //
- if (i == 0) VectorCopy(move_start_top, org);
- else VectorCopy(move_end_top, org);
- VectorSubtract(startreach->start, org, dir);
- dir[2] = 0;
- VectorNormalize(dir);
- VectorCopy(startreach->start, start);
- VectorMA(startreach->start, 1, dir, start);
- start[2] += 1;
- VectorMA(startreach->start, 16, dir, end);
- end[2] += 1;
- //
- numareas = AAS_TraceAreas(start, end, areas, points, 10);
- if (numareas <= 0) continue;
- if (numareas > 1) VectorCopy(points[1], startreach->start);
- else VectorCopy(end, startreach->start);
- //
- if (!AAS_PointAreaNum(startreach->start)) continue;
- if (!AAS_PointAreaNum(endreach->end)) continue;
- //
- lreach = AAS_AllocReachability();
- lreach->areanum = endreach->areanum;
- if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff);
- else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff);
- lreach->facenum = (spawnflags << 16) | modelnum;
- VectorCopy(startreach->start, lreach->start);
- VectorCopy(endreach->end, lreach->end);
-#ifndef BSPC
-// AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW);
-// AAS_PermanentLine(lreach->start, lreach->end, 1);
-#endif
- lreach->traveltype = TRAVEL_FUNCBOB;
- lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
- lreach->traveltime = aassettings.rs_funcbob;
- reach_funcbob++;
- lreach->next = areareachability[startreach->areanum];
- areareachability[startreach->areanum] = lreach;
- //
- } //end for
- } //end for
- for (startreach = firststartreach; startreach; startreach = nextstartreach)
- {
- nextstartreach = startreach->next;
- AAS_FreeReachability(startreach);
- } //end for
- for (endreach = firstendreach; endreach; endreach = nextendreach)
- {
- nextendreach = endreach->next;
- AAS_FreeReachability(endreach);
- } //end for
- //only go up with func_bobbing entities that go up and down
- if (!(spawnflags & 1) && !(spawnflags & 2)) break;
- } //end for
- } //end for
-} //end of the function AAS_Reachability_FuncBobbing
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Reachability_JumpPad(void)
-{
- int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads;
- //int modelnum, ent2;
- //float dist, time, height, gravity, forward;
- float speed, zvel, hordist;
- aas_face_t *face2;
- aas_area_t *area2;
- aas_lreachability_t *lreach;
- vec3_t areastart, facecenter, dir, cmdmove;
- vec3_t velocity, absmins, absmaxs;
- //vec3_t origin, ent2origin, angles, teststart;
- aas_clientmove_t move;
- //aas_trace_t trace;
- aas_link_t *areas, *link;
- //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
- char classname[MAX_EPAIRKEY];
-
-#ifdef BSPC
- bot_visualizejumppads = 0;
-#else
- bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0");
-#endif
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (strcmp(classname, "trigger_push")) continue;
- //
- if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;
- /*
- //
- AAS_FloatForBSPEpairKey(ent, "speed", &speed);
- if (!speed) speed = 1000;
-// AAS_VectorForBSPEpairKey(ent, "angles", angles);
-// AAS_SetMovedir(angles, velocity);
-// VectorScale(velocity, speed, velocity);
- VectorClear(angles);
- //get the mins, maxs and origin of the model
- AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY);
- if (model[0]) modelnum = atoi(model+1);
- else modelnum = 0;
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);
- VectorAdd(origin, absmins, absmins);
- VectorAdd(origin, absmaxs, absmaxs);
- //
-#ifdef REACH_DEBUG
- botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]);
- botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]);
-#endif REACH_DEBUG
- VectorAdd(absmins, absmaxs, origin);
- VectorScale (origin, 0.5, origin);
-
- //get the start areas
- VectorCopy(origin, teststart);
- teststart[2] += 64;
- trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);
- if (trace.startsolid)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push start solid\n");
- VectorCopy(origin, areastart);
- } //end if
- else
- {
- VectorCopy(trace.endpos, areastart);
- } //end else
- areastart[2] += 0.125;
- //
- //AAS_DrawPermanentCross(origin, 4, 4);
- //get the target entity
- AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY);
- for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))
- {
- if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue;
- if (!strcmp(targetname, target)) break;
- } //end for
- if (!ent2)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target);
- continue;
- } //end if
- AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin);
- //
- height = ent2origin[2] - origin[2];
- gravity = aassettings.sv_gravity;
- time = sqrt( height / ( 0.5 * gravity ) );
- if (!time)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push without time\n");
- continue;
- } //end if
- // set s.origin2 to the push velocity
- VectorSubtract ( ent2origin, origin, velocity);
- dist = VectorNormalize( velocity);
- forward = dist / time;
- //FIXME: why multiply by 1.1
- forward *= 1.1;
- VectorScale(velocity, forward, velocity);
- velocity[2] = time * gravity;
- */
- //get the areas the jump pad brush is in
- areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);
- /*
- for (link = areas; link; link = link->next_area)
- {
- if (link->areanum == 563)
- {
- ret = qfalse;
- }
- }
- */
- for (link = areas; link; link = link->next_area)
- {
- if (AAS_AreaJumpPad(link->areanum)) break;
- } //end for
- if (!link)
- {
- botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n");
- AAS_UnlinkFromAreas(areas);
- continue;
- } //end if
- //
- botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]);
- //if there is a horizontal velocity check for a reachability without air control
- if (velocity[0] || velocity[1])
- {
- VectorSet(cmdmove, 0, 0, 0);
- //VectorCopy(velocity, cmdmove);
- //cmdmove[2] = 0;
- Com_Memset(&move, 0, sizeof(aas_clientmove_t));
- area2num = 0;
- for (i = 0; i < 20; i++)
- {
- AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
- velocity, cmdmove, 0, 30, 0.1f,
- SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads);
- area2num = move.endarea;
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaJumpPad(link->areanum)) continue;
- if (link->areanum == area2num) break;
- } //end if
- if (!link) break;
- VectorCopy(move.endpos, areastart);
- VectorCopy(move.velocity, velocity);
- } //end for
- if (area2num && i < 20)
- {
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaJumpPad(link->areanum)) continue;
- if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
- //create a rocket or bfg jump reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach)
- {
- AAS_UnlinkFromAreas(areas);
- return;
- } //end if
- lreach->areanum = area2num;
- //NOTE: the facenum is the Z velocity
- lreach->facenum = velocity[2];
- //NOTE: the edgenum is the horizontal velocity
- lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]);
- VectorCopy(areastart, lreach->start);
- VectorCopy(move.endpos, lreach->end);
- lreach->traveltype = TRAVEL_JUMPPAD;
- lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
- lreach->traveltime = aassettings.rs_jumppad;
- lreach->next = areareachability[link->areanum];
- areareachability[link->areanum] = lreach;
- //
- reach_jumppad++;
- } //end for
- } //end if
- } //end if
- //
- if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue;
- //check for areas we can reach with air control
- for (area2num = 1; area2num < aasworld.numareas; area2num++)
- {
- visualize = qfalse;
- /*
- if (area2num == 3568)
- {
- for (link = areas; link; link = link->next_area)
- {
- if (link->areanum == 3380)
- {
- visualize = qtrue;
- botimport.Print(PRT_MESSAGE, "bah\n");
- } //end if
- } //end for
- } //end if*/
- //never try to go back to one of the original jumppad areas
- //and don't create reachabilities if they already exist
- for (link = areas; link; link = link->next_area)
- {
- if (AAS_ReachabilityExists(link->areanum, area2num)) break;
- if (AAS_AreaJumpPad(link->areanum))
- {
- if (link->areanum == area2num) break;
- } //end if
- } //end if
- if (link) continue;
- //
- area2 = &aasworld.areas[area2num];
- for (i = 0; i < area2->numfaces; i++)
- {
- face2num = aasworld.faceindex[area2->firstface + i];
- face2 = &aasworld.faces[abs(face2num)];
- //if it is not a ground face
- if (!(face2->faceflags & FACE_GROUND)) continue;
- //get the center of the face
- AAS_FaceCenter(face2num, facecenter);
- //only go higher up
- if (facecenter[2] < areastart[2]) continue;
- //get the jumppad jump z velocity
- zvel = velocity[2];
- //get the horizontal speed for the jump, if it isn't possible to calculate this
- //speed
- ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
- if (ret && speed < 150)
- {
- //direction towards the face center
- VectorSubtract(facecenter, areastart, dir);
- dir[2] = 0;
- hordist = VectorNormalize(dir);
- //if (hordist < 1.6 * facecenter[2] - areastart[2])
- {
- //get command movement
- VectorScale(dir, speed, cmdmove);
- //
- AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,
- velocity, cmdmove, 30, 30, 0.1f,
- SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
- SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize);
- //if prediction time wasn't enough to fully predict the movement
- //don't enter slime or lava and don't fall from too high
- if (move.frames < 30 &&
- !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
- && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER)))
- {
- //never go back to the same jumppad
- for (link = areas; link; link = link->next_area)
- {
- if (link->areanum == move.endarea) break;
- }
- if (!link)
- {
- for (link = areas; link; link = link->next_area)
- {
- if (!AAS_AreaJumpPad(link->areanum)) continue;
- if (AAS_ReachabilityExists(link->areanum, area2num)) continue;
- //create a jumppad reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach)
- {
- AAS_UnlinkFromAreas(areas);
- return;
- } //end if
- lreach->areanum = move.endarea;
- //NOTE: the facenum is the Z velocity
- lreach->facenum = velocity[2];
- //NOTE: the edgenum is the horizontal velocity
- lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]);
- VectorCopy(areastart, lreach->start);
- VectorCopy(facecenter, lreach->end);
- lreach->traveltype = TRAVEL_JUMPPAD;
- lreach->traveltype |= AAS_TravelFlagsForTeam(ent);
- lreach->traveltime = aassettings.rs_aircontrolledjumppad;
- lreach->next = areareachability[link->areanum];
- areareachability[link->areanum] = lreach;
- //
- reach_jumppad++;
- } //end for
- }
- } //end if
- } //end if
- } //end for
- } //end for
- } //end for
- AAS_UnlinkFromAreas(areas);
- } //end for
-} //end of the function AAS_Reachability_JumpPad
-//===========================================================================
-// never point at ground faces
-// always a higher and pretty far area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_Grapple(int area1num, int area2num)
-{
- int face2num, i, j, areanum, numareas, areas[20];
- float mingrappleangle, z, hordist;
- bsp_trace_t bsptrace;
- aas_trace_t trace;
- aas_face_t *face2;
- aas_area_t *area1, *area2;
- aas_lreachability_t *lreach;
- vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1};
- vec_t *v;
-
- //only grapple when on the ground or swimming
- if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;
- //don't grapple from a crouch area
- if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse;
- //NOTE: disabled area swim it doesn't work right
- if (AAS_AreaSwim(area1num)) return qfalse;
- //
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
- //don't grapple towards way lower areas
- if (area2->maxs[2] < area1->mins[2]) return qfalse;
- //
- VectorCopy(aasworld.areas[area1num].center, start);
- //if not a swim area
- if (!AAS_AreaSwim(area1num))
- {
- if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
- start[0], start[1], start[2]);
- VectorCopy(start, end);
- end[2] -= 1000;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
- if (trace.startsolid) return qfalse;
- VectorCopy(trace.endpos, areastart);
- } //end if
- else
- {
- if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse;
- } //end else
- //
- //start is now the start point
- //
- for (i = 0; i < area2->numfaces; i++)
- {
- face2num = aasworld.faceindex[area2->firstface + i];
- face2 = &aasworld.faces[abs(face2num)];
- //if it is not a solid face
- if (!(face2->faceflags & FACE_SOLID)) continue;
- //direction towards the first vertex of the face
- v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]];
- VectorSubtract(v, areastart, dir);
- //if the face plane is facing away
- if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue;
- //get the center of the face
- AAS_FaceCenter(face2num, facecenter);
- //only go higher up with the grapple
- if (facecenter[2] < areastart[2] + 64) continue;
- //only use vertical faces or downward facing faces
- if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue;
- //direction towards the face center
- VectorSubtract(facecenter, areastart, dir);
- //
- z = dir[2];
- dir[2] = 0;
- hordist = VectorLength(dir);
- if (!hordist) continue;
- //if too far
- if (hordist > 2000) continue;
- //check the minimal angle of the movement
- mingrappleangle = 15; //15 degrees
- if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue;
- //
- VectorCopy(facecenter, start);
- VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end);
- //
- bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID);
- //the grapple won't stick to the sky and the grapple point should be near the AAS wall
- if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue;
- //trace a full bounding box from the area center on the ground to
- //the center of the face
- VectorSubtract(facecenter, areastart, dir);
- VectorNormalize(dir);
- VectorMA(areastart, 4, dir, start);
- VectorCopy(bsptrace.endpos, end);
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
- VectorSubtract(trace.endpos, facecenter, dir);
- if (VectorLength(dir) > 24) continue;
- //
- VectorCopy(trace.endpos, start);
- VectorCopy(trace.endpos, end);
- end[2] -= AAS_FallDamageDistance();
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);
- if (trace.fraction >= 1) continue;
- //area to end in
- areanum = AAS_PointAreaNum(trace.endpos);
- //if not in lava or slime
- if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA))
- {
- continue;
- } //end if
- //do not go the the source area
- if (areanum == area1num) continue;
- //don't create reachabilities if they already exist
- if (AAS_ReachabilityExists(area1num, areanum)) continue;
- //only end in areas we can stand
- if (!AAS_AreaGrounded(areanum)) continue;
- //never go through cluster portals!!
- numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20);
- if (numareas >= 20) continue;
- for (j = 0; j < numareas; j++)
- {
- if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break;
- } //end for
- if (j < numareas) continue;
- //create a new reachability link
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = areanum;
- lreach->facenum = face2num;
- lreach->edgenum = 0;
- VectorCopy(areastart, lreach->start);
- //VectorCopy(facecenter, lreach->end);
- VectorCopy(bsptrace.endpos, lreach->end);
- lreach->traveltype = TRAVEL_GRAPPLEHOOK;
- VectorSubtract(lreach->end, lreach->start, dir);
- lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25;
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_grapple++;
- } //end for
- //
- return qfalse;
-} //end of the function AAS_Reachability_Grapple
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_SetWeaponJumpAreaFlags(void)
-{
- int ent, i;
- vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15};
- vec3_t origin;
- int areanum, weaponjumpareas, spawnflags;
- char classname[MAX_EPAIRKEY];
-
- weaponjumpareas = 0;
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (
- !strcmp(classname, "item_armor_body") ||
- !strcmp(classname, "item_armor_combat") ||
- !strcmp(classname, "item_health_mega") ||
- !strcmp(classname, "weapon_grenadelauncher") ||
- !strcmp(classname, "weapon_rocketlauncher") ||
- !strcmp(classname, "weapon_lightning") ||
- !strcmp(classname, "weapon_plasmagun") ||
- !strcmp(classname, "weapon_railgun") ||
- !strcmp(classname, "weapon_bfg") ||
- !strcmp(classname, "item_quad") ||
- !strcmp(classname, "item_regen") ||
- !strcmp(classname, "item_invulnerability"))
- {
- if (AAS_VectorForBSPEpairKey(ent, "origin", origin))
- {
- spawnflags = 0;
- AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
- //if not a stationary item
- if (!(spawnflags & 1))
- {
- if (!AAS_DropToFloor(origin, mins, maxs))
- {
- botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
- classname, origin[0], origin[1], origin[2]);
- } //end if
- } //end if
- //areanum = AAS_PointAreaNum(origin);
- areanum = AAS_BestReachableArea(origin, mins, maxs, origin);
- //the bot may rocket jump towards this area
- aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP;
- //
- //if (!AAS_AreaGrounded(areanum))
- // botimport.Print(PRT_MESSAGE, "area not grounded\n");
- //
- weaponjumpareas++;
- } //end if
- } //end if
- } //end for
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
- {
- aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP;
- weaponjumpareas++;
- } //end if
- } //end for
- botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas);
-} //end of the function AAS_SetWeaponJumpAreaFlags
-//===========================================================================
-// create a possible weapon jump reachability from area1 to area2
-//
-// check if there's a cool item in the second area
-// check if area1 is lower than area2
-// check if the bot can rocketjump from area1 to area2
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_Reachability_WeaponJump(int area1num, int area2num)
-{
- int face2num, i, n, ret, visualize;
- float speed, zvel, hordist;
- aas_face_t *face2;
- aas_area_t *area1, *area2;
- aas_lreachability_t *lreach;
- vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart;
- vec3_t velocity;
- aas_clientmove_t move;
- aas_trace_t trace;
-
- visualize = qfalse;
-// if (area1num == 4436 && area2num == 4318)
-// {
-// visualize = qtrue;
-// }
- if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse;
- if (!AAS_AreaGrounded(area2num)) return qfalse;
- //NOTE: only weapon jump towards areas with an interesting item in it??
- if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse;
- //
- area1 = &aasworld.areas[area1num];
- area2 = &aasworld.areas[area2num];
- //don't weapon jump towards way lower areas
- if (area2->maxs[2] < area1->mins[2]) return qfalse;
- //
- VectorCopy(aasworld.areas[area1num].center, start);
- //if not a swim area
- if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num,
- start[0], start[1], start[2]);
- VectorCopy(start, end);
- end[2] -= 1000;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
- if (trace.startsolid) return qfalse;
- VectorCopy(trace.endpos, areastart);
- //
- //areastart is now the start point
- //
- for (i = 0; i < area2->numfaces; i++)
- {
- face2num = aasworld.faceindex[area2->firstface + i];
- face2 = &aasworld.faces[abs(face2num)];
- //if it is not a solid face
- if (!(face2->faceflags & FACE_GROUND)) continue;
- //get the center of the face
- AAS_FaceCenter(face2num, facecenter);
- //only go higher up with weapon jumps
- if (facecenter[2] < areastart[2] + 64) continue;
- //NOTE: set to 2 to allow bfg jump reachabilities
- for (n = 0; n < 1/*2*/; n++)
- {
- //get the rocket jump z velocity
- if (n) zvel = AAS_BFGJumpZVelocity(areastart);
- else zvel = AAS_RocketJumpZVelocity(areastart);
- //get the horizontal speed for the jump, if it isn't possible to calculate this
- //speed (the jump is not possible) then there's no jump reachability created
- ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);
- if (ret && speed < 300)
- {
- //direction towards the face center
- VectorSubtract(facecenter, areastart, dir);
- dir[2] = 0;
- hordist = VectorNormalize(dir);
- //if (hordist < 1.6 * (facecenter[2] - areastart[2]))
- {
- //get command movement
- VectorScale(dir, speed, cmdmove);
- VectorSet(velocity, 0, 0, zvel);
- /*
- //get command movement
- VectorScale(dir, speed, velocity);
- velocity[2] = zvel;
- VectorSet(cmdmove, 0, 0, 0);
- */
- //
- AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue,
- velocity, cmdmove, 30, 30, 0.1f,
- SE_ENTERWATER|SE_ENTERSLIME|
- SE_ENTERLAVA|SE_HITGROUNDDAMAGE|
- SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize);
- //if prediction time wasn't enough to fully predict the movement
- //don't enter slime or lava and don't fall from too high
- if (move.frames < 30 &&
- !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
- && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD)))
- {
- //create a rocket or bfg jump reachability from area1 to area2
- lreach = AAS_AllocReachability();
- if (!lreach) return qfalse;
- lreach->areanum = area2num;
- lreach->facenum = 0;
- lreach->edgenum = 0;
- VectorCopy(areastart, lreach->start);
- VectorCopy(facecenter, lreach->end);
- if (n)
- {
- lreach->traveltype = TRAVEL_BFGJUMP;
- lreach->traveltime = aassettings.rs_bfgjump;
- } //end if
- else
- {
- lreach->traveltype = TRAVEL_ROCKETJUMP;
- lreach->traveltime = aassettings.rs_rocketjump;
- } //end else
- lreach->next = areareachability[area1num];
- areareachability[area1num] = lreach;
- //
- reach_rocketjump++;
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end for
- } //end for
- //
- return qfalse;
-} //end of the function AAS_Reachability_WeaponJump
-//===========================================================================
-// calculates additional walk off ledge reachabilities for the given area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_Reachability_WalkOffLedge(int areanum)
-{
- int i, j, k, l, m, n, p, areas[10], numareas;
- int face1num, face2num, face3num, edge1num, edge2num, edge3num;
- int otherareanum, gap, reachareanum, side;
- aas_area_t *area, *area2;
- aas_face_t *face1, *face2, *face3;
- aas_edge_t *edge;
- aas_plane_t *plane;
- vec_t *v1, *v2;
- vec3_t sharededgevec, mid, dir, testend;
- aas_lreachability_t *lreach;
- aas_trace_t trace;
-
- if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return;
- //
- area = &aasworld.areas[areanum];
- //
- for (i = 0; i < area->numfaces; i++)
- {
- face1num = aasworld.faceindex[area->firstface + i];
- face1 = &aasworld.faces[abs(face1num)];
- //face 1 must be a ground face
- if (!(face1->faceflags & FACE_GROUND)) continue;
- //go through all the edges of this ground face
- for (k = 0; k < face1->numedges; k++)
- {
- edge1num = aasworld.edgeindex[face1->firstedge + k];
- //find another not ground face using this same edge
- for (j = 0; j < area->numfaces; j++)
- {
- face2num = aasworld.faceindex[area->firstface + j];
- face2 = &aasworld.faces[abs(face2num)];
- //face 2 may not be a ground face
- if (face2->faceflags & FACE_GROUND) continue;
- //compare all the edges
- for (l = 0; l < face2->numedges; l++)
- {
- edge2num = aasworld.edgeindex[face2->firstedge + l];
- if (abs(edge1num) == abs(edge2num))
- {
- //get the area at the other side of the face
- if (face2->frontarea == areanum) otherareanum = face2->backarea;
- else otherareanum = face2->frontarea;
- //
- area2 = &aasworld.areas[otherareanum];
- //if the other area is grounded!
- if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED)
- {
- //check for a possible gap
- gap = qfalse;
- for (n = 0; n < area2->numfaces; n++)
- {
- face3num = aasworld.faceindex[area2->firstface + n];
- //may not be the shared face of the two areas
- if (abs(face3num) == abs(face2num)) continue;
- //
- face3 = &aasworld.faces[abs(face3num)];
- //find an edge shared by all three faces
- for (m = 0; m < face3->numedges; m++)
- {
- edge3num = aasworld.edgeindex[face3->firstedge + m];
- //but the edge should be shared by all three faces
- if (abs(edge3num) == abs(edge1num))
- {
- if (!(face3->faceflags & FACE_SOLID))
- {
- gap = qtrue;
- break;
- } //end if
- //
- if (face3->faceflags & FACE_GROUND)
- {
- gap = qfalse;
- break;
- } //end if
- //FIXME: there are more situations to be handled
- gap = qtrue;
- break;
- } //end if
- } //end for
- if (m < face3->numedges) break;
- } //end for
- if (!gap) break;
- } //end if
- //check for a walk off ledge reachability
- edge = &aasworld.edges[abs(edge1num)];
- side = edge1num < 0;
- //
- v1 = aasworld.vertexes[edge->v[side]];
- v2 = aasworld.vertexes[edge->v[!side]];
- //
- plane = &aasworld.planes[face1->planenum];
- //get the points really into the areas
- VectorSubtract(v2, v1, sharededgevec);
- CrossProduct(plane->normal, sharededgevec, dir);
- VectorNormalize(dir);
- //
- VectorAdd(v1, v2, mid);
- VectorScale(mid, 0.5, mid);
- VectorMA(mid, 8, dir, mid);
- //
- VectorCopy(mid, testend);
- testend[2] -= 1000;
- trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1);
- //
- if (trace.startsolid)
- {
- //Log_Write("area %d: trace.startsolid\r\n", areanum);
- break;
- } //end if
- reachareanum = AAS_PointAreaNum(trace.endpos);
- if (reachareanum == areanum)
- {
- //Log_Write("area %d: same area\r\n", areanum);
- break;
- } //end if
- if (AAS_ReachabilityExists(areanum, reachareanum))
- {
- //Log_Write("area %d: reachability already exists\r\n", areanum);
- break;
- } //end if
- if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum))
- {
- //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum);
- break;
- } //end if
- //
- if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME
- | AREACONTENTS_LAVA))
- {
- //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum);
- break;
- } //end if
- //if not going through a cluster portal
- numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int));
- for (p = 0; p < numareas; p++)
- if (AAS_AreaClusterPortal(areas[p]))
- break;
- if (p < numareas)
- break;
- // if a maximum fall height is set and the bot would fall down further
- if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight)
- break;
- //
- lreach = AAS_AllocReachability();
- if (!lreach) break;
- lreach->areanum = reachareanum;
- lreach->facenum = 0;
- lreach->edgenum = edge1num;
- VectorCopy(mid, lreach->start);
- VectorCopy(trace.endpos, lreach->end);
- lreach->traveltype = TRAVEL_WALKOFFLEDGE;
- lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity;
- if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum))
- {
- if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5)
- {
- lreach->traveltime += aassettings.rs_falldamage5;
- } //end if
- else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10)
- {
- lreach->traveltime += aassettings.rs_falldamage10;
- } //end if
- } //end if
- lreach->next = areareachability[areanum];
- areareachability[areanum] = lreach;
- //we've got another walk off ledge reachability
- reach_walkoffledge++;
- } //end if
- } //end for
- } //end for
- } //end for
- } //end for
-} //end of the function AAS_Reachability_WalkOffLedge
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_StoreReachability(void)
-{
- int i;
- aas_areasettings_t *areasettings;
- aas_lreachability_t *lreach;
- aas_reachability_t *reach;
-
- if (aasworld.reachability) FreeMemory(aasworld.reachability);
- aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t));
- aasworld.reachabilitysize = 1;
- for (i = 0; i < aasworld.numareas; i++)
- {
- areasettings = &aasworld.areasettings[i];
- areasettings->firstreachablearea = aasworld.reachabilitysize;
- areasettings->numreachableareas = 0;
- for (lreach = areareachability[i]; lreach; lreach = lreach->next)
- {
- reach = &aasworld.reachability[areasettings->firstreachablearea +
- areasettings->numreachableareas];
- reach->areanum = lreach->areanum;
- reach->facenum = lreach->facenum;
- reach->edgenum = lreach->edgenum;
- VectorCopy(lreach->start, reach->start);
- VectorCopy(lreach->end, reach->end);
- reach->traveltype = lreach->traveltype;
- reach->traveltime = lreach->traveltime;
- //
- areasettings->numreachableareas++;
- } //end for
- aasworld.reachabilitysize += areasettings->numreachableareas;
- } //end for
-} //end of the function AAS_StoreReachability
-//===========================================================================
-//
-// TRAVEL_WALK 100% equal floor height + steps
-// TRAVEL_CROUCH 100%
-// TRAVEL_BARRIERJUMP 100%
-// TRAVEL_JUMP 80%
-// TRAVEL_LADDER 100% + fall down from ladder + jump up to ladder
-// TRAVEL_WALKOFFLEDGE 90% walk off very steep walls?
-// TRAVEL_SWIM 100%
-// TRAVEL_WATERJUMP 100%
-// TRAVEL_TELEPORT 100%
-// TRAVEL_ELEVATOR 100%
-// TRAVEL_GRAPPLEHOOK 100%
-// TRAVEL_DOUBLEJUMP 0%
-// TRAVEL_RAMPJUMP 0%
-// TRAVEL_STRAFEJUMP 0%
-// TRAVEL_ROCKETJUMP 100% (currently limited towards areas with items)
-// TRAVEL_BFGJUMP 0% (currently disabled)
-// TRAVEL_JUMPPAD 100%
-// TRAVEL_FUNCBOB 100%
-//
-// Parameter: -
-// Returns: true if NOT finished
-// Changes Globals: -
-//===========================================================================
-int AAS_ContinueInitReachability(float time)
-{
- int i, j, todo, start_time;
- static float framereachability, reachability_delay;
- static int lastpercentage;
-
- if (!aasworld.loaded) return qfalse;
- //if reachability is calculated for all areas
- if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse;
- //if starting with area 1 (area 0 is a dummy)
- if (aasworld.numreachabilityareas == 1)
- {
- botimport.Print(PRT_MESSAGE, "calculating reachability...\n");
- lastpercentage = 0;
- framereachability = 2000;
- reachability_delay = 1000;
- } //end if
- //number of areas to calculate reachability for this cycle
- todo = aasworld.numreachabilityareas + (int) framereachability;
- start_time = Sys_MilliSeconds();
- //loop over the areas
- for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++)
- {
- aasworld.numreachabilityareas++;
- //only create jumppad reachabilities from jumppad areas
- if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
- {
- continue;
- } //end if
- //loop over the areas
- for (j = 1; j < aasworld.numareas; j++)
- {
- if (i == j) continue;
- //never create reachabilities from teleporter or jumppad areas to regular areas
- if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
- {
- if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)))
- {
- continue;
- } //end if
- } //end if
- //if there already is a reachability link from area i to j
- if (AAS_ReachabilityExists(i, j)) continue;
- //check for a swim reachability
- if (AAS_Reachability_Swim(i, j)) continue;
- //check for a simple walk on equal floor height reachability
- if (AAS_Reachability_EqualFloorHeight(i, j)) continue;
- //check for step, barrier, waterjump and walk off ledge reachabilities
- if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue;
- //check for ladder reachabilities
- if (AAS_Reachability_Ladder(i, j)) continue;
- //check for a jump reachability
- if (AAS_Reachability_Jump(i, j)) continue;
- } //end for
- //never create these reachabilities from teleporter or jumppad areas
- if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))
- {
- continue;
- } //end if
- //loop over the areas
- for (j = 1; j < aasworld.numareas; j++)
- {
- if (i == j) continue;
- //
- if (AAS_ReachabilityExists(i, j)) continue;
- //check for a grapple hook reachability
- if (calcgrapplereach) AAS_Reachability_Grapple(i, j);
- //check for a weapon jump reachability
- AAS_Reachability_WeaponJump(i, j);
- } //end for
- //if the calculation took more time than the max reachability delay
- if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break;
- //
- if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break;
- } //end for
- //
- if (aasworld.numreachabilityareas == aasworld.numareas)
- {
- botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0);
- botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n");
- aasworld.numreachabilityareas++;
- } //end if
- //if this is the last step in the reachability calculations
- else if (aasworld.numreachabilityareas == aasworld.numareas + 1)
- {
- //create additional walk off ledge reachabilities for every area
- for (i = 1; i < aasworld.numareas; i++)
- {
- //only create jumppad reachabilities from jumppad areas
- if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)
- {
- continue;
- } //end if
- AAS_Reachability_WalkOffLedge(i);
- } //end for
- //create jump pad reachabilities
- AAS_Reachability_JumpPad();
- //create teleporter reachabilities
- AAS_Reachability_Teleport();
- //create elevator (func_plat) reachabilities
- AAS_Reachability_Elevator();
- //create func_bobbing reachabilities
- AAS_Reachability_FuncBobbing();
- //
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim);
- botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor);
- botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step);
- botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier);
- botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump);
- botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge);
- botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump);
- botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder);
- botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk);
- botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport);
- botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob);
- botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator);
- botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple);
- botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump);
- botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad);
-#endif
- //*/
- //store all the reachabilities
- AAS_StoreReachability();
- //free the reachability link heap
- AAS_ShutDownReachabilityHeap();
- //
- FreeMemory(areareachability);
- //
- aasworld.numreachabilityareas++;
- //
- botimport.Print(PRT_MESSAGE, "calculating clusters...\n");
- } //end if
- else
- {
- lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas;
- botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10);
- } //end else
- //not yet finished
- return qtrue;
-} //end of the function AAS_ContinueInitReachability
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitReachability(void)
-{
- if (!aasworld.loaded) return;
-
- if (aasworld.reachabilitysize)
- {
-#ifndef BSPC
- if (!((int)LibVarGetValue("forcereachability")))
- {
- aasworld.numreachabilityareas = aasworld.numareas + 2;
- return;
- } //end if
-#else
- aasworld.numreachabilityareas = aasworld.numareas + 2;
- return;
-#endif //BSPC
- } //end if
-#ifndef BSPC
- calcgrapplereach = LibVarGetValue("grapplereach");
-#endif
- aasworld.savefile = qtrue;
- //start with area 1 because area zero is a dummy
- aasworld.numreachabilityareas = 1;
- ////aasworld.numreachabilityareas = aasworld.numareas + 1; //only calculate entity reachabilities
- //setup the heap with reachability links
- AAS_SetupReachabilityHeap();
- //allocate area reachability link array
- areareachability = (aas_lreachability_t **) GetClearedMemory(
- aasworld.numareas * sizeof(aas_lreachability_t *));
- //
- AAS_SetWeaponJumpAreaFlags();
-} //end of the function AAS_InitReachable
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_reach.c + * + * desc: reachability calculations + * + * $Archive: /MissionPack/code/botlib/be_aas_reach.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_libvar.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern int Sys_MilliSeconds(void); + + +extern botlib_import_t botimport; + +//#define REACH_DEBUG + +//NOTE: all travel times are in hundreth of a second +//maximum number of reachability links +#define AAS_MAX_REACHABILITYSIZE 65536 +//number of areas reachability is calculated for each frame +#define REACHABILITYAREASPERCYCLE 15 +//number of units reachability points are placed inside the areas +#define INSIDEUNITS 2 +#define INSIDEUNITS_WALKEND 5 +#define INSIDEUNITS_WALKSTART 0.1 +#define INSIDEUNITS_WATERJUMP 15 +//area flag used for weapon jumping +#define AREA_WEAPONJUMP 8192 //valid area to weapon jump to +//number of reachabilities of each type +int reach_swim; //swim +int reach_equalfloor; //walk on floors with equal height +int reach_step; //step up +int reach_walk; //walk of step +int reach_barrier; //jump up to a barrier +int reach_waterjump; //jump out of water +int reach_walkoffledge; //walk of a ledge +int reach_jump; //jump +int reach_ladder; //climb or descent a ladder +int reach_teleport; //teleport +int reach_elevator; //use an elevator +int reach_funcbob; //use a func bob +int reach_grapple; //grapple hook +int reach_doublejump; //double jump +int reach_rampjump; //ramp jump +int reach_strafejump; //strafe jump (just normal jump but further) +int reach_rocketjump; //rocket jump +int reach_bfgjump; //bfg jump +int reach_jumppad; //jump pads +//if true grapple reachabilities are skipped +int calcgrapplereach; +//linked reachability +typedef struct aas_lreachability_s +{ + int areanum; //number of the reachable area + int facenum; //number of the face towards the other area + int edgenum; //number of the edge towards the other area + vec3_t start; //start point of inter area movement + vec3_t end; //end point of inter area movement + int traveltype; //type of travel required to get to the area + unsigned short int traveltime; //travel time of the inter area movement + // + struct aas_lreachability_s *next; +} aas_lreachability_t; +//temporary reachabilities +aas_lreachability_t *reachabilityheap; //heap with reachabilities +aas_lreachability_t *nextreachability; //next free reachability from the heap +aas_lreachability_t **areareachability; //reachability links for every area +int numlreachabilities; + +//=========================================================================== +// returns the surface area of the given face +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_FaceArea(aas_face_t *face) +{ + int i, edgenum, side; + float total; + vec_t *v; + vec3_t d1, d2, cross; + aas_edge_t *edge; + + edgenum = aasworld.edgeindex[face->firstedge]; + side = edgenum < 0; + edge = &aasworld.edges[abs(edgenum)]; + v = aasworld.vertexes[edge->v[side]]; + + total = 0; + for (i = 1; i < face->numedges - 1; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + side = edgenum < 0; + edge = &aasworld.edges[abs(edgenum)]; + VectorSubtract(aasworld.vertexes[edge->v[side]], v, d1); + VectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2); + CrossProduct(d1, d2, cross); + total += 0.5 * VectorLength(cross); + } //end for + return total; +} //end of the function AAS_FaceArea +//=========================================================================== +// returns the volume of an area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_AreaVolume(int areanum) +{ + int i, edgenum, facenum, side; + vec_t d, a, volume; + vec3_t corner; + aas_plane_t *plane; + aas_edge_t *edge; + aas_face_t *face; + aas_area_t *area; + + area = &aasworld.areas[areanum]; + facenum = aasworld.faceindex[area->firstface]; + face = &aasworld.faces[abs(facenum)]; + edgenum = aasworld.edgeindex[face->firstedge]; + edge = &aasworld.edges[abs(edgenum)]; + // + VectorCopy(aasworld.vertexes[edge->v[0]], corner); + + //make tetrahedrons to all other faces + volume = 0; + for (i = 0; i < area->numfaces; i++) + { + facenum = abs(aasworld.faceindex[area->firstface + i]); + face = &aasworld.faces[facenum]; + side = face->backarea != areanum; + plane = &aasworld.planes[face->planenum ^ side]; + d = -(DotProduct (corner, plane->normal) - plane->dist); + a = AAS_FaceArea(face); + volume += d * a; + } //end for + + volume /= 3; + return volume; +} //end of the function AAS_AreaVolume +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableLinkArea(aas_link_t *areas) +{ + aas_link_t *link; + + for (link = areas; link; link = link->next_area) + { + if (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum)) + { + return link->areanum; + } //end if + } //end for + // + for (link = areas; link; link = link->next_area) + { + if (link->areanum) return link->areanum; + //FIXME: this is a bad idea when the reachability is not yet + // calculated when the level items are loaded + if (AAS_AreaReachability(link->areanum)) + return link->areanum; + } //end for + return 0; +} //end of the function AAS_BestReachableLinkArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity) +{ + int modelnum, ent2; + float speed, height, gravity, time, dist, forward; + vec3_t origin, angles, teststart, ent2origin; + aas_trace_t trace; + char model[MAX_EPAIRKEY]; + char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; + + // + AAS_FloatForBSPEpairKey(ent, "speed", &speed); + if (!speed) speed = 1000; + VectorClear(angles); + //get the mins, maxs and origin of the model + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); + if (model[0]) modelnum = atoi(model+1); + else modelnum = 0; + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); + VectorAdd(origin, absmins, absmins); + VectorAdd(origin, absmaxs, absmaxs); + VectorAdd(absmins, absmaxs, origin); + VectorScale (origin, 0.5, origin); + + //get the start areas + VectorCopy(origin, teststart); + teststart[2] += 64; + trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); + VectorCopy(origin, areastart); + } //end if + else + { + VectorCopy(trace.endpos, areastart); + } //end else + areastart[2] += 0.125; + // + //AAS_DrawPermanentCross(origin, 4, 4); + //get the target entity + AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); + for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) + { + if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; + if (!strcmp(targetname, target)) break; + } //end for + if (!ent2) + { + botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); + return qfalse; + } //end if + AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); + // + height = ent2origin[2] - origin[2]; + gravity = aassettings.phys_gravity; + time = sqrt( height / ( 0.5 * gravity ) ); + if (!time) + { + botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); + return qfalse; + } //end if + // set s.origin2 to the push velocity + VectorSubtract ( ent2origin, origin, velocity); + dist = VectorNormalize( velocity); + forward = dist / time; + //FIXME: why multiply by 1.1 + forward *= 1.1f; + VectorScale(velocity, forward, velocity); + velocity[2] = time * gravity; + return qtrue; +} //end of the function AAS_GetJumpPadInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs) +{ + int area2num, ent, bot_visualizejumppads, bestareanum; + float volume, bestareavolume; + vec3_t areastart, cmdmove, bboxmins, bboxmaxs; + vec3_t absmins, absmaxs, velocity; + aas_clientmove_t move; + aas_link_t *areas, *link; + char classname[MAX_EPAIRKEY]; + +#ifdef BSPC + bot_visualizejumppads = 0; +#else + bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); +#endif + VectorAdd(origin, mins, bboxmins); + VectorAdd(origin, maxs, bboxmaxs); + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (strcmp(classname, "trigger_push")) continue; + // + if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; + //get the areas the jump pad brush is in + areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); + for (link = areas; link; link = link->next_area) + { + if (AAS_AreaJumpPad(link->areanum)) break; + } //end for + if (!link) + { + botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); + AAS_UnlinkFromAreas(areas); + continue; + } //end if + // + //botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); + // + VectorSet(cmdmove, 0, 0, 0); + Com_Memset(&move, 0, sizeof(aas_clientmove_t)); + area2num = 0; + AAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads); + if (move.frames < 30) + { + bestareanum = 0; + bestareavolume = 0; + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + volume = AAS_AreaVolume(link->areanum); + if (volume >= bestareavolume) + { + bestareanum = link->areanum; + bestareavolume = volume; + } //end if + } //end if + AAS_UnlinkFromAreas(areas); + return bestareanum; + } //end if + AAS_UnlinkFromAreas(areas); + } //end for + return 0; +} //end of the function AAS_BestReachableFromJumpPadArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin) +{ + int areanum, i, j, k, l; + aas_link_t *areas; + vec3_t absmins, absmaxs; + //vec3_t bbmins, bbmaxs; + vec3_t start, end; + aas_trace_t trace; + + if (!aasworld.loaded) + { + botimport.Print(PRT_ERROR, "AAS_BestReachableArea: aas not loaded\n"); + return 0; + } //end if + //find a point in an area + VectorCopy(origin, start); + areanum = AAS_PointAreaNum(start); + //while no area found fudge around a little + for (i = 0; i < 5 && !areanum; i++) + { + for (j = 0; j < 5 && !areanum; j++) + { + for (k = -1; k <= 1 && !areanum; k++) + { + for (l = -1; l <= 1 && !areanum; l++) + { + VectorCopy(origin, start); + start[0] += (float) j * 4 * k; + start[1] += (float) j * 4 * l; + start[2] += (float) i * 4; + areanum = AAS_PointAreaNum(start); + } //end for + } //end for + } //end for + } //end for + //if an area was found + if (areanum) + { + //drop client bbox down and try again + VectorCopy(start, end); + start[2] += 0.25; + end[2] -= 50; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + areanum = AAS_PointAreaNum(trace.endpos); + VectorCopy(trace.endpos, goalorigin); + //FIXME: cannot enable next line right now because the reachability + // does not have to be calculated when the level items are loaded + //if the origin is in an area with reachability + //if (AAS_AreaReachability(areanum)) return areanum; + if (areanum) return areanum; + } //end if + else + { + //it can very well happen that the AAS_PointAreaNum function tells that + //a point is in an area and that starting a AAS_TraceClientBBox from that + //point will return trace.startsolid qtrue +#if 0 + if (AAS_PointAreaNum(start)) + { + Log_Write("point %f %f %f in area %d but trace startsolid", start[0], start[1], start[2], areanum); + AAS_DrawPermanentCross(start, 4, LINECOLOR_RED); + } //end if + botimport.Print(PRT_MESSAGE, "AAS_BestReachableArea: start solid\n"); +#endif + VectorCopy(start, goalorigin); + return areanum; + } //end else + } //end if + // + //AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); + //NOTE: the goal origin does not have to be in the goal area + // because the bot will have to move towards the item origin anyway + VectorCopy(origin, goalorigin); + // + VectorAdd(origin, mins, absmins); + VectorAdd(origin, maxs, absmaxs); + //add bounding box size + //VectorSubtract(absmins, bbmaxs, absmins); + //VectorSubtract(absmaxs, bbmins, absmaxs); + //link an invalid (-1) entity + areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); + //get the reachable link arae + areanum = AAS_BestReachableLinkArea(areas); + //unlink the invalid entity + AAS_UnlinkFromAreas(areas); + // + return areanum; +} //end of the function AAS_BestReachableArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetupReachabilityHeap(void) +{ + int i; + + reachabilityheap = (aas_lreachability_t *) GetClearedMemory( + AAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t)); + for (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++) + { + reachabilityheap[i].next = &reachabilityheap[i+1]; + } //end for + reachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL; + nextreachability = reachabilityheap; + numlreachabilities = 0; +} //end of the function AAS_InitReachabilityHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShutDownReachabilityHeap(void) +{ + FreeMemory(reachabilityheap); + numlreachabilities = 0; +} //end of the function AAS_ShutDownReachabilityHeap +//=========================================================================== +// returns a reachability link +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_lreachability_t *AAS_AllocReachability(void) +{ + aas_lreachability_t *r; + + if (!nextreachability) return NULL; + //make sure the error message only shows up once + if (!nextreachability->next) AAS_Error("AAS_MAX_REACHABILITYSIZE"); + // + r = nextreachability; + nextreachability = nextreachability->next; + numlreachabilities++; + return r; +} //end of the function AAS_AllocReachability +//=========================================================================== +// frees a reachability link +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeReachability(aas_lreachability_t *lreach) +{ + Com_Memset(lreach, 0, sizeof(aas_lreachability_t)); + + lreach->next = nextreachability; + nextreachability = lreach; + numlreachabilities--; +} //end of the function AAS_FreeReachability +//=========================================================================== +// returns qtrue if the area has reachability links +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaReachability(int areanum) +{ + if (areanum < 0 || areanum >= aasworld.numareas) + { + AAS_Error("AAS_AreaReachability: areanum %d out of range", areanum); + return 0; + } //end if + return aasworld.areasettings[areanum].numreachableareas; +} //end of the function AAS_AreaReachability +//=========================================================================== +// returns the surface area of all ground faces together of the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_AreaGroundFaceArea(int areanum) +{ + int i; + float total; + aas_area_t *area; + aas_face_t *face; + + total = 0; + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; + if (!(face->faceflags & FACE_GROUND)) continue; + // + total += AAS_FaceArea(face); + } //end for + return total; +} //end of the function AAS_AreaGroundFaceArea +//=========================================================================== +// returns the center of a face +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FaceCenter(int facenum, vec3_t center) +{ + int i; + float scale; + aas_face_t *face; + aas_edge_t *edge; + + face = &aasworld.faces[facenum]; + + VectorClear(center); + for (i = 0; i < face->numedges; i++) + { + edge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])]; + VectorAdd(center, aasworld.vertexes[edge->v[0]], center); + VectorAdd(center, aasworld.vertexes[edge->v[1]], center); + } //end for + scale = 0.5 / face->numedges; + VectorScale(center, scale, center); +} //end of the function AAS_FaceCenter +//=========================================================================== +// returns the maximum distance a player can fall before being damaged +// damage = deltavelocity*deltavelocity * 0.0001 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FallDamageDistance(void) +{ + float maxzvelocity, gravity, t; + + maxzvelocity = sqrt(30 * 10000); + gravity = aassettings.phys_gravity; + t = maxzvelocity / gravity; + return 0.5 * gravity * t * t; +} //end of the function AAS_FallDamageDistance +//=========================================================================== +// distance = 0.5 * gravity * t * t +// vel = t * gravity +// damage = vel * vel * 0.0001 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_FallDelta(float distance) +{ + float t, delta, gravity; + + gravity = aassettings.phys_gravity; + t = sqrt(fabs(distance) * 2 / gravity); + delta = t * gravity; + return delta * delta * 0.0001; +} //end of the function AAS_FallDelta +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_MaxJumpHeight(float phys_jumpvel) +{ + float phys_gravity; + + phys_gravity = aassettings.phys_gravity; + //maximum height a player can jump with the given initial z velocity + return 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity); +} //end of the function MaxJumpHeight +//=========================================================================== +// returns true if a player can only crouch in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float AAS_MaxJumpDistance(float phys_jumpvel) +{ + float phys_gravity, phys_maxvelocity, t; + + phys_gravity = aassettings.phys_gravity; + phys_maxvelocity = aassettings.phys_maxvelocity; + //time a player takes to fall the height + t = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity)); + //maximum distance + return phys_maxvelocity * (t + phys_jumpvel / phys_gravity); +} //end of the function AAS_MaxJumpDistance +//=========================================================================== +// returns true if a player can only crouch in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaCrouch(int areanum) +{ + if (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue; + else return qfalse; +} //end of the function AAS_AreaCrouch +//=========================================================================== +// returns qtrue if it is possible to swim in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaSwim(int areanum) +{ + if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; + else return qfalse; +} //end of the function AAS_AreaSwim +//=========================================================================== +// returns qtrue if the area contains a liquid +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaLiquid(int areanum) +{ + if (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue; + else return qfalse; +} //end of the function AAS_AreaLiquid +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaLava(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA); +} //end of the function AAS_AreaLava +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaSlime(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME); +} //end of the function AAS_AreaSlime +//=========================================================================== +// returns qtrue if the area contains ground faces +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaGrounded(int areanum) +{ + return (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED); +} //end of the function AAS_AreaGround +//=========================================================================== +// returns true if the area contains ladder faces +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaLadder(int areanum) +{ + return (aasworld.areasettings[areanum].areaflags & AREA_LADDER); +} //end of the function AAS_AreaLadder +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaJumpPad(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD); +} //end of the function AAS_AreaJumpPad +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaTeleporter(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER); +} //end of the function AAS_AreaTeleporter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaClusterPortal(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL); +} //end of the function AAS_AreaClusterPortal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaDoNotEnter(int areanum) +{ + return (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER); +} //end of the function AAS_AreaDoNotEnter +//=========================================================================== +// returns the time it takes perform a barrier jump +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short int AAS_BarrierJumpTravelTime(void) +{ + return aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1); +} //end op the function AAS_BarrierJumpTravelTime +//=========================================================================== +// returns true if there already exists a reachability from area1 to area2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_ReachabilityExists(int area1num, int area2num) +{ + aas_lreachability_t *r; + + for (r = areareachability[area1num]; r; r = r->next) + { + if (r->areanum == area2num) return qtrue; + } //end for + return qfalse; +} //end of the function AAS_ReachabilityExists +//=========================================================================== +// returns true if there is a solid just after the end point when going +// from start to end +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NearbySolidOrGap(vec3_t start, vec3_t end) +{ + vec3_t dir, testpoint; + int areanum; + + VectorSubtract(end, start, dir); + dir[2] = 0; + VectorNormalize(dir); + VectorMA(end, 48, dir, testpoint); + + areanum = AAS_PointAreaNum(testpoint); + if (!areanum) + { + testpoint[2] += 16; + areanum = AAS_PointAreaNum(testpoint); + if (!areanum) return qtrue; + } //end if + VectorMA(end, 64, dir, testpoint); + areanum = AAS_PointAreaNum(testpoint); + if (areanum) + { + if (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue; + } //end if + return qfalse; +} //end of the function AAS_SolidGapTime +//=========================================================================== +// searches for swim reachabilities between adjacent areas +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Swim(int area1num, int area2num) +{ + int i, j, face1num, face2num, side1; + aas_area_t *area1, *area2; + aas_areasettings_t *areasettings; + aas_lreachability_t *lreach; + aas_face_t *face1; + aas_plane_t *plane; + vec3_t start; + + if (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse; + //if the second area is crouch only + if (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse; + + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + + //if the areas are not near anough + for (i = 0; i < 3; i++) + { + if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; + if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; + } //end for + //find a shared face and create a reachability link + for (i = 0; i < area1->numfaces; i++) + { + face1num = aasworld.faceindex[area1->firstface + i]; + side1 = face1num < 0; + face1num = abs(face1num); + // + for (j = 0; j < area2->numfaces; j++) + { + face2num = abs(aasworld.faceindex[area2->firstface + j]); + // + if (face1num == face2num) + { + AAS_FaceCenter(face1num, start); + // + if (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) + { + // + face1 = &aasworld.faces[face1num]; + areasettings = &aasworld.areasettings[area1num]; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = face1num; + lreach->edgenum = 0; + VectorCopy(start, lreach->start); + plane = &aasworld.planes[face1->planenum ^ side1]; + VectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end); + lreach->traveltype = TRAVEL_SWIM; + lreach->traveltime = 1; + //if the volume of the area is rather small + if (AAS_AreaVolume(area2num) < 800) + lreach->traveltime += 200; + //if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500; + //link the reachability + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + reach_swim++; + return qtrue; + } //end if + } //end if + } //end for + } //end for + return qfalse; +} //end of the function AAS_Reachability_Swim +//=========================================================================== +// searches for reachabilities between adjacent areas with equal floor +// heights +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_EqualFloorHeight(int area1num, int area2num) +{ + int i, j, edgenum, edgenum1, edgenum2, foundreach, side; + float height, bestheight, length, bestlength; + vec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1}; + vec3_t edgevec; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2; + aas_edge_t *edge; + aas_plane_t *plane2; + aas_lreachability_t lr, *lreach; + + if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; + + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //if the areas are not near anough in the x-y direction + for (i = 0; i < 2; i++) + { + if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; + if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; + } //end for + //if area 2 is too high above area 1 + if (area2->mins[2] > area1->maxs[2]) return qfalse; + // + VectorCopy(gravitydirection, invgravity); + VectorInverse(invgravity); + // + bestheight = 99999; + bestlength = 0; + foundreach = qfalse; + Com_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy + // + //check if the areas have ground faces with a common edge + //if existing use the lowest common edge for a reachability link + for (i = 0; i < area1->numfaces; i++) + { + face1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])]; + if (!(face1->faceflags & FACE_GROUND)) continue; + // + for (j = 0; j < area2->numfaces; j++) + { + face2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; + if (!(face2->faceflags & FACE_GROUND)) continue; + //if there is a common edge + for (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++) + { + for (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++) + { + if (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) != + abs(aasworld.edgeindex[face2->firstedge + edgenum2])) + continue; + edgenum = aasworld.edgeindex[face1->firstedge + edgenum1]; + side = edgenum < 0; + edge = &aasworld.edges[abs(edgenum)]; + //get the length of the edge + VectorSubtract(aasworld.vertexes[edge->v[1]], + aasworld.vertexes[edge->v[0]], dir); + length = VectorLength(dir); + //get the start point + VectorAdd(aasworld.vertexes[edge->v[0]], + aasworld.vertexes[edge->v[1]], start); + VectorScale(start, 0.5, start); + VectorCopy(start, end); + //get the end point several units inside area2 + //and the start point several units inside area1 + //NOTE: normal is pointing into area2 because the + //face edges are stored counter clockwise + VectorSubtract(aasworld.vertexes[edge->v[side]], + aasworld.vertexes[edge->v[!side]], edgevec); + plane2 = &aasworld.planes[face2->planenum]; + CrossProduct(edgevec, plane2->normal, normal); + VectorNormalize(normal); + // + //VectorMA(start, -1, normal, start); + VectorMA(end, INSIDEUNITS_WALKEND, normal, end); + VectorMA(start, INSIDEUNITS_WALKSTART, normal, start); + end[2] += 0.125; + // + height = DotProduct(invgravity, start); + //NOTE: if there's nearby solid or a gap area after this area + //disabled this crap + //if (AAS_NearbySolidOrGap(start, end)) height += 200; + //NOTE: disabled because it disables reachabilities to very small areas + //if (AAS_PointAreaNum(end) != area2num) continue; + //get the longest lowest edge + if (height < bestheight || + (height < bestheight + 1 && length > bestlength)) + { + bestheight = height; + bestlength = length; + //create a new reachability link + lr.areanum = area2num; + lr.facenum = 0; + lr.edgenum = edgenum; + VectorCopy(start, lr.start); + VectorCopy(end, lr.end); + lr.traveltype = TRAVEL_WALK; + lr.traveltime = 1; + foundreach = qtrue; + } //end if + } //end for + } //end for + } //end for + } //end for + if (foundreach) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = lr.areanum; + lreach->facenum = lr.facenum; + lreach->edgenum = lr.edgenum; + VectorCopy(lr.start, lreach->start); + VectorCopy(lr.end, lreach->end); + lreach->traveltype = lr.traveltype; + lreach->traveltime = lr.traveltime; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //if going into a crouch area + if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) + { + lreach->traveltime += aassettings.rs_startcrouch; + } //end if + /* + //NOTE: if there's nearby solid or a gap area after this area + if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) + { + lreach->traveltime += 100; + } //end if + */ + //avoid rather small areas + //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; + // + reach_equalfloor++; + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_Reachability_EqualFloorHeight +//=========================================================================== +// searches step, barrier, waterjump and walk off ledge reachabilities +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num) +{ + int i, j, k, l, edge1num, edge2num, areas[10], numareas; + int ground_bestarea2groundedgenum, ground_foundreach; + int water_bestarea2groundedgenum, water_foundreach; + int side1, area1swim, faceside1, groundface1num; + float dist, dist1, dist2, diff, invgravitydot, ortdot; + float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y; + float length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist; + vec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2; + vec3_t normal, ort, edgevec, start, end, dir; + vec3_t ground_beststart, ground_bestend, ground_bestnormal; + vec3_t water_beststart, water_bestend, water_bestnormal; + vec3_t invgravity = {0, 0, 1}; + vec3_t testpoint; + aas_plane_t *plane; + aas_area_t *area1, *area2; + aas_face_t *groundface1, *groundface2, *ground_bestface1, *water_bestface1; + aas_edge_t *edge1, *edge2; + aas_lreachability_t *lreach; + aas_trace_t trace; + + //must be able to walk or swim in the first area + if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; + // + if (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //if the first area contains a liquid + area1swim = AAS_AreaSwim(area1num); + //if the areas are not near anough in the x-y direction + for (i = 0; i < 2; i++) + { + if (area1->mins[i] > area2->maxs[i] + 10) return qfalse; + if (area1->maxs[i] < area2->mins[i] - 10) return qfalse; + } //end for + // + ground_foundreach = qfalse; + ground_bestdist = 99999; + ground_bestlength = 0; + ground_bestarea2groundedgenum = 0; + // + water_foundreach = qfalse; + water_bestdist = 99999; + water_bestlength = 0; + water_bestarea2groundedgenum = 0; + // + for (i = 0; i < area1->numfaces; i++) + { + groundface1num = aasworld.faceindex[area1->firstface + i]; + faceside1 = groundface1num < 0; + groundface1 = &aasworld.faces[abs(groundface1num)]; + //if this isn't a ground face + if (!(groundface1->faceflags & FACE_GROUND)) + { + //if we can swim in the first area + if (area1swim) + { + //face plane must be more or less horizontal + plane = &aasworld.planes[groundface1->planenum ^ (!faceside1)]; + if (DotProduct(plane->normal, invgravity) < 0.7) continue; + } //end if + else + { + //if we can't swim in the area it must be a ground face + continue; + } //end else + } //end if + // + for (k = 0; k < groundface1->numedges; k++) + { + edge1num = aasworld.edgeindex[groundface1->firstedge + k]; + side1 = (edge1num < 0); + //NOTE: for water faces we must take the side area 1 is + // on into account because the face is shared and doesn't + // have to be oriented correctly + if (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1); + edge1num = abs(edge1num); + edge1 = &aasworld.edges[edge1num]; + //vertexes of the edge + VectorCopy(aasworld.vertexes[edge1->v[!side1]], v1); + VectorCopy(aasworld.vertexes[edge1->v[side1]], v2); + //get a vertical plane through the edge + //NOTE: normal is pointing into area 2 because the + //face edges are stored counter clockwise + VectorSubtract(v2, v1, edgevec); + CrossProduct(edgevec, invgravity, normal); + VectorNormalize(normal); + dist = DotProduct(normal, v1); + //check the faces from the second area + for (j = 0; j < area2->numfaces; j++) + { + groundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])]; + //must be a ground face + if (!(groundface2->faceflags & FACE_GROUND)) continue; + //check the edges of this ground face + for (l = 0; l < groundface2->numedges; l++) + { + edge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]); + edge2 = &aasworld.edges[edge2num]; + //vertexes of the edge + VectorCopy(aasworld.vertexes[edge2->v[0]], v3); + VectorCopy(aasworld.vertexes[edge2->v[1]], v4); + //check the distance between the two points and the vertical plane + //through the edge of area1 + diff = DotProduct(normal, v3) - dist; + if (diff < -0.1 || diff > 0.1) continue; + diff = DotProduct(normal, v4) - dist; + if (diff < -0.1 || diff > 0.1) continue; + // + //project the two ground edges into the step side plane + //and calculate the shortest distance between the two + //edges if they overlap in the direction orthogonal to + //the gravity direction + CrossProduct(invgravity, normal, ort); + invgravitydot = DotProduct(invgravity, invgravity); + ortdot = DotProduct(ort, ort); + //projection into the step plane + //NOTE: since gravity is vertical this is just the z coordinate + y1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot; + y2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot; + y3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot; + y4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot; + // + x1 = DotProduct(v1, ort) / ortdot; + x2 = DotProduct(v2, ort) / ortdot; + x3 = DotProduct(v3, ort) / ortdot; + x4 = DotProduct(v4, ort) / ortdot; + // + if (x1 > x2) + { + tmp = x1; x1 = x2; x2 = tmp; + tmp = y1; y1 = y2; y2 = tmp; + VectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2); + } //end if + if (x3 > x4) + { + tmp = x3; x3 = x4; x4 = tmp; + tmp = y3; y3 = y4; y4 = tmp; + VectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4); + } //end if + //if the two projected edge lines have no overlap + if (x2 <= x3 || x4 <= x1) + { +// Log_Write("lines no overlap: from area %d to %d\r\n", area1num, area2num); + continue; + } //end if + //if the two lines fully overlap + if ((x1 - 0.5 < x3 && x4 < x2 + 0.5) && + (x3 - 0.5 < x1 && x2 < x4 + 0.5)) + { + dist1 = y3 - y1; + dist2 = y4 - y2; + VectorCopy(v1, p1area1); + VectorCopy(v2, p2area1); + VectorCopy(v3, p1area2); + VectorCopy(v4, p2area2); + } //end if + else + { + //if the points are equal + if (x1 > x3 - 0.1 && x1 < x3 + 0.1) + { + dist1 = y3 - y1; + VectorCopy(v1, p1area1); + VectorCopy(v3, p1area2); + } //end if + else if (x1 < x3) + { + y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1); + dist1 = y3 - y; + VectorCopy(v3, p1area1); + p1area1[2] = y; + VectorCopy(v3, p1area2); + } //end if + else + { + y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3); + dist1 = y - y1; + VectorCopy(v1, p1area1); + VectorCopy(v1, p1area2); + p1area2[2] = y; + } //end if + //if the points are equal + if (x2 > x4 - 0.1 && x2 < x4 + 0.1) + { + dist2 = y4 - y2; + VectorCopy(v2, p2area1); + VectorCopy(v4, p2area2); + } //end if + else if (x2 < x4) + { + y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3); + dist2 = y - y2; + VectorCopy(v2, p2area1); + VectorCopy(v2, p2area2); + p2area2[2] = y; + } //end if + else + { + y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1); + dist2 = y4 - y; + VectorCopy(v4, p2area1); + p2area1[2] = y; + VectorCopy(v4, p2area2); + } //end else + } //end else + //if both distances are pretty much equal + //then we take the middle of the points + if (dist1 > dist2 - 1 && dist1 < dist2 + 1) + { + dist = dist1; + VectorAdd(p1area1, p2area1, start); + VectorScale(start, 0.5, start); + VectorAdd(p1area2, p2area2, end); + VectorScale(end, 0.5, end); + } //end if + else if (dist1 < dist2) + { + dist = dist1; + VectorCopy(p1area1, start); + VectorCopy(p1area2, end); + } //end else if + else + { + dist = dist2; + VectorCopy(p2area1, start); + VectorCopy(p2area2, end); + } //end else + //get the length of the overlapping part of the edges of the two areas + VectorSubtract(p2area2, p1area2, dir); + length = VectorLength(dir); + // + if (groundface1->faceflags & FACE_GROUND) + { + //if the vertical distance is smaller + if (dist < ground_bestdist || + //or the vertical distance is pretty much the same + //but the overlapping part of the edges is longer + (dist < ground_bestdist + 1 && length > ground_bestlength)) + { + ground_bestdist = dist; + ground_bestlength = length; + ground_foundreach = qtrue; + ground_bestarea2groundedgenum = edge1num; + ground_bestface1 = groundface1; + //best point towards area1 + VectorCopy(start, ground_beststart); + //normal is pointing into area2 + VectorCopy(normal, ground_bestnormal); + //best point towards area2 + VectorCopy(end, ground_bestend); + } //end if + } //end if + else + { + //if the vertical distance is smaller + if (dist < water_bestdist || + //or the vertical distance is pretty much the same + //but the overlapping part of the edges is longer + (dist < water_bestdist + 1 && length > water_bestlength)) + { + water_bestdist = dist; + water_bestlength = length; + water_foundreach = qtrue; + water_bestarea2groundedgenum = edge1num; + water_bestface1 = groundface1; + //best point towards area1 + VectorCopy(start, water_beststart); + //normal is pointing into area2 + VectorCopy(normal, water_bestnormal); + //best point towards area2 + VectorCopy(end, water_bestend); + } //end if + } //end else + } //end for + } //end for + } //end for + } //end for + // + // NOTE: swim reachabilities are already filtered out + // + // Steps + // + // --------- + // | step height -> TRAVEL_WALK + //--------| + // + // --------- + //~~~~~~~~| step height and low water -> TRAVEL_WALK + //--------| + // + //~~~~~~~~~~~~~~~~~~ + // --------- + // | step height and low water up to the step -> TRAVEL_WALK + //--------| + // + //check for a step reachability + if (ground_foundreach) + { + //if area2 is higher but lower than the maximum step height + //NOTE: ground_bestdist >= 0 also catches equal floor reachabilities + if (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep) + { + //create walk reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); + VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_WALK; + lreach->traveltime = 0;//1; + //if going into a crouch area + if (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num)) + { + lreach->traveltime += aassettings.rs_startcrouch; + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //NOTE: if there's nearby solid or a gap area after this area + /* + if (!AAS_NearbySolidOrGap(lreach->start, lreach->end)) + { + lreach->traveltime += 100; + } //end if + */ + //avoid rather small areas + //if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100; + // + reach_step++; + return qtrue; + } //end if + } //end if + // + // Water Jumps + // + // --------- + // | + //~~~~~~~~| + // | + // | higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP + //--------| + // + //~~~~~~~~~~~~~~~~~~ + // --------- + // | + // | + // | + // | higher than step height and low water up to the step -> TRAVEL_WATERJUMP + //--------| + // + //check for a waterjump reachability + if (water_foundreach) + { + //get a test point a little bit towards area1 + VectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint); + //go down the maximum waterjump height + testpoint[2] -= aassettings.phys_maxwaterjump; + //if there IS water the sv_maxwaterjump height below the bestend point + if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID) + { + //don't create rediculous water jump reachabilities from areas very far below + //the water surface + if (water_bestdist < aassettings.phys_maxwaterjump + 24) + { + //waterjumping from or towards a crouch only area is not possible in Quake2 + if ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) && + (aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) + { + //create water jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = water_bestarea2groundedgenum; + VectorCopy(water_beststart, lreach->start); + VectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_WATERJUMP; + lreach->traveltime = aassettings.rs_waterjump; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //we've got another waterjump reachability + reach_waterjump++; + return qtrue; + } //end if + } //end if + } //end if + } //end if + // + // Barrier Jumps + // + // --------- + // | + // | + // | + // | higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP + //--------| + // + // --------- + // | + // | + // | + //~~~~~~~~| higher than step height lower than barrier height + //--------| and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP + // + //check for a barrier jump reachability + if (ground_foundreach) + { + //if area2 is higher but lower than the maximum barrier jump height + if (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier) + { + //if no water in area1 or a very thin layer of water on the ground + if (!water_foundreach || (ground_bestdist - water_bestdist < 16)) + { + //cannot perform a barrier jump towards or from a crouch area in Quake2 + if (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num)) + { + //create barrier jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); + VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_BARRIERJUMP; + lreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime(); + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //we've got another barrierjump reachability + reach_barrier++; + return qtrue; + } //end if + } //end if + } //end if + } //end if + // + // Walk and Walk Off Ledge + // + //--------| + // | can walk or step back -> TRAVEL_WALK + // --------- + // + //--------| + // | + // | + // | + // | cannot walk/step back -> TRAVEL_WALKOFFLEDGE + // --------- + // + //--------| + // | + // |~~~~~~~~ + // | + // | cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE + // --------- FIXME: create TRAVEL_WALK reach?? + // + //check for a walk or walk off ledge reachability + if (ground_foundreach) + { + if (ground_bestdist < 0) + { + if (ground_bestdist > -aassettings.phys_maxstep) + { + //create walk reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start); + VectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end); + lreach->traveltype = TRAVEL_WALK; + lreach->traveltime = 1; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //we've got another walk reachability + reach_walk++; + return qtrue; + } //end if + // if no maximum fall height set or less than the max + if (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) { + //trace a bounding box vertically to check for solids + VectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend); + VectorCopy(ground_bestend, start); + start[2] = ground_beststart[2]; + VectorCopy(ground_bestend, end); + end[2] += 4; + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + //if no solids were found + if (!trace.startsolid && trace.fraction >= 1.0) + { + //the trace end point must be in the goal area + trace.endpos[2] += 1; + if (AAS_PointAreaNum(trace.endpos) == area2num) + { + //if not going through a cluster portal + numareas = AAS_TraceAreas(start, end, areas, NULL, sizeof(areas) / sizeof(int)); + for (i = 0; i < numareas; i++) + if (AAS_AreaClusterPortal(areas[i])) + break; + if (i >= numareas) + { + //create a walk off ledge reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = ground_bestarea2groundedgenum; + VectorCopy(ground_beststart, lreach->start); + VectorCopy(ground_bestend, lreach->end); + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity; + //if falling from too high and not falling into water + if (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num)) + { + if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5) + { + lreach->traveltime += aassettings.rs_falldamage5; + } //end if + if (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10) + { + lreach->traveltime += aassettings.rs_falldamage10; + } //end if + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_walkoffledge++; + //NOTE: don't create a weapon (rl, bfg) jump reachability here + //because it interferes with other reachabilities + //like the ladder reachability + return qtrue; + } //end if + } //end if + } //end if + } //end if + } //end else + } //end if + return qfalse; +} //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge +//=========================================================================== +// returns the distance between the two vectors +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float VectorDistance(vec3_t v1, vec3_t v2) +{ + vec3_t dir; + + VectorSubtract(v2, v1, dir); + return VectorLength(dir); +} //end of the function VectorDistance +//=========================================================================== +// returns true if the first vector is between the last two vectors +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2) +{ + vec3_t dir1, dir2; + + VectorSubtract(v, v1, dir1); + VectorSubtract(v, v2, dir2); + return (DotProduct(dir1, dir2) <= 0); +} //end of the function VectorBetweenVectors +//=========================================================================== +// returns the mid point between the two vectors +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle) +{ + VectorAdd(v1, v2, middle); + VectorScale(middle, 0.5, middle); +} //end of the function VectorMiddle +//=========================================================================== +// calculate a range of points closest to each other on both edges +// +// Parameter: beststart1 start of the range of points on edge v1-v2 +// beststart2 end of the range of points on edge v1-v2 +// bestend1 start of the range of points on edge v3-v4 +// bestend2 end of the range of points on edge v3-v4 +// bestdist best distance so far +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, + aas_plane_t *plane1, aas_plane_t *plane2, + vec3_t beststart, vec3_t bestend, float bestdist) +{ + vec3_t dir1, dir2, p1, p2, p3, p4; + float a1, a2, b1, b2, dist; + int founddist; + + //edge vectors + VectorSubtract(v2, v1, dir1); + VectorSubtract(v4, v3, dir2); + //get the horizontal directions + dir1[2] = 0; + dir2[2] = 0; + // + // p1 = point on an edge vector of area2 closest to v1 + // p2 = point on an edge vector of area2 closest to v2 + // p3 = point on an edge vector of area1 closest to v3 + // p4 = point on an edge vector of area1 closest to v4 + // + if (dir2[0]) + { + a2 = dir2[1] / dir2[0]; + b2 = v3[1] - a2 * v3[0]; + //point on the edge vector of area2 closest to v1 + p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p1[1] = a2 * p1[0] + b2; + //point on the edge vector of area2 closest to v2 + p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p2[1] = a2 * p2[0] + b2; + } //end if + else + { + //point on the edge vector of area2 closest to v1 + p1[0] = v3[0]; + p1[1] = v1[1]; + //point on the edge vector of area2 closest to v2 + p2[0] = v3[0]; + p2[1] = v2[1]; + } //end else + // + if (dir1[0]) + { + // + a1 = dir1[1] / dir1[0]; + b1 = v1[1] - a1 * v1[0]; + //point on the edge vector of area1 closest to v3 + p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p3[1] = a1 * p3[0] + b1; + //point on the edge vector of area1 closest to v4 + p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p4[1] = a1 * p4[0] + b1; + } //end if + else + { + //point on the edge vector of area1 closest to v3 + p3[0] = v1[0]; + p3[1] = v3[1]; + //point on the edge vector of area1 closest to v4 + p4[0] = v1[0]; + p4[1] = v4[1]; + } //end else + //start with zero z-coordinates + p1[2] = 0; + p2[2] = 0; + p3[2] = 0; + p4[2] = 0; + //calculate the z-coordinates from the ground planes + p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; + p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; + p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; + p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; + // + founddist = qfalse; + // + if (VectorBetweenVectors(p1, v3, v4)) + { + dist = VectorDistance(v1, p1); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, v1, beststart); + VectorMiddle(bestend, p1, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart); + VectorCopy(p1, bestend); + } //end if + founddist = qtrue; + } //end if + if (VectorBetweenVectors(p2, v3, v4)) + { + dist = VectorDistance(v2, p2); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, v2, beststart); + VectorMiddle(bestend, p2, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart); + VectorCopy(p2, bestend); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p3, v1, v2)) + { + dist = VectorDistance(v3, p3); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, p3, beststart); + VectorMiddle(bestend, v3, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p3, beststart); + VectorCopy(v3, bestend); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p4, v1, v2)) + { + dist = VectorDistance(v4, p4); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + VectorMiddle(beststart, p4, beststart); + VectorMiddle(bestend, v4, bestend); + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p4, beststart); + VectorCopy(v4, bestend); + } //end if + founddist = qtrue; + } //end else if + //if no shortest distance was found the shortest distance + //is between one of the vertexes of edge1 and one of edge2 + if (!founddist) + { + dist = VectorDistance(v1, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart); + VectorCopy(v3, bestend); + } //end if + dist = VectorDistance(v1, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart); + VectorCopy(v4, bestend); + } //end if + dist = VectorDistance(v2, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart); + VectorCopy(v3, bestend); + } //end if + dist = VectorDistance(v2, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart); + VectorCopy(v4, bestend); + } //end if + } //end if + return bestdist; +} //end of the function AAS_ClosestEdgePoints*/ + +float AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4, + aas_plane_t *plane1, aas_plane_t *plane2, + vec3_t beststart1, vec3_t bestend1, + vec3_t beststart2, vec3_t bestend2, float bestdist) +{ + vec3_t dir1, dir2, p1, p2, p3, p4; + float a1, a2, b1, b2, dist, dist1, dist2; + int founddist; + + //edge vectors + VectorSubtract(v2, v1, dir1); + VectorSubtract(v4, v3, dir2); + //get the horizontal directions + dir1[2] = 0; + dir2[2] = 0; + // + // p1 = point on an edge vector of area2 closest to v1 + // p2 = point on an edge vector of area2 closest to v2 + // p3 = point on an edge vector of area1 closest to v3 + // p4 = point on an edge vector of area1 closest to v4 + // + if (dir2[0]) + { + a2 = dir2[1] / dir2[0]; + b2 = v3[1] - a2 * v3[0]; + //point on the edge vector of area2 closest to v1 + p1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p1[1] = a2 * p1[0] + b2; + //point on the edge vector of area2 closest to v2 + p2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0]; + p2[1] = a2 * p2[0] + b2; + } //end if + else + { + //point on the edge vector of area2 closest to v1 + p1[0] = v3[0]; + p1[1] = v1[1]; + //point on the edge vector of area2 closest to v2 + p2[0] = v3[0]; + p2[1] = v2[1]; + } //end else + // + if (dir1[0]) + { + // + a1 = dir1[1] / dir1[0]; + b1 = v1[1] - a1 * v1[0]; + //point on the edge vector of area1 closest to v3 + p3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p3[1] = a1 * p3[0] + b1; + //point on the edge vector of area1 closest to v4 + p4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0]; + p4[1] = a1 * p4[0] + b1; + } //end if + else + { + //point on the edge vector of area1 closest to v3 + p3[0] = v1[0]; + p3[1] = v3[1]; + //point on the edge vector of area1 closest to v4 + p4[0] = v1[0]; + p4[1] = v4[1]; + } //end else + //start with zero z-coordinates + p1[2] = 0; + p2[2] = 0; + p3[2] = 0; + p4[2] = 0; + //calculate the z-coordinates from the ground planes + p1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2]; + p2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2]; + p3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2]; + p4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2]; + // + founddist = qfalse; + // + if (VectorBetweenVectors(p1, v3, v4)) + { + dist = VectorDistance(v1, p1); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, v1); + dist2 = VectorDistance(beststart2, v1); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1); + } //end else + dist1 = VectorDistance(bestend1, p1); + dist2 = VectorDistance(bestend2, p1); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart1); + VectorCopy(v1, beststart2); + VectorCopy(p1, bestend1); + VectorCopy(p1, bestend2); + } //end if + founddist = qtrue; + } //end if + if (VectorBetweenVectors(p2, v3, v4)) + { + dist = VectorDistance(v2, p2); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, v2); + dist2 = VectorDistance(beststart2, v2); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1); + } //end else + dist1 = VectorDistance(bestend1, p2); + dist2 = VectorDistance(bestend2, p2); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart1); + VectorCopy(v2, beststart2); + VectorCopy(p2, bestend1); + VectorCopy(p2, bestend2); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p3, v1, v2)) + { + dist = VectorDistance(v3, p3); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, p3); + dist2 = VectorDistance(beststart2, p3); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1); + } //end else + dist1 = VectorDistance(bestend1, v3); + dist2 = VectorDistance(bestend2, v3); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p3, beststart1); + VectorCopy(p3, beststart2); + VectorCopy(v3, bestend1); + VectorCopy(v3, bestend2); + } //end if + founddist = qtrue; + } //end else if + if (VectorBetweenVectors(p4, v1, v2)) + { + dist = VectorDistance(v4, p4); + if (dist > bestdist - 0.5 && dist < bestdist + 0.5) + { + dist1 = VectorDistance(beststart1, p4); + dist2 = VectorDistance(beststart2, p4); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2); + } //end if + else + { + if (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1); + } //end else + dist1 = VectorDistance(bestend1, v4); + dist2 = VectorDistance(bestend2, v4); + if (dist1 > dist2) + { + if (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2); + } //end if + else + { + if (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1); + } //end else + } //end if + else if (dist < bestdist) + { + bestdist = dist; + VectorCopy(p4, beststart1); + VectorCopy(p4, beststart2); + VectorCopy(v4, bestend1); + VectorCopy(v4, bestend2); + } //end if + founddist = qtrue; + } //end else if + //if no shortest distance was found the shortest distance + //is between one of the vertexes of edge1 and one of edge2 + if (!founddist) + { + dist = VectorDistance(v1, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart1); + VectorCopy(v1, beststart2); + VectorCopy(v3, bestend1); + VectorCopy(v3, bestend2); + } //end if + dist = VectorDistance(v1, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v1, beststart1); + VectorCopy(v1, beststart2); + VectorCopy(v4, bestend1); + VectorCopy(v4, bestend2); + } //end if + dist = VectorDistance(v2, v3); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart1); + VectorCopy(v2, beststart2); + VectorCopy(v3, bestend1); + VectorCopy(v3, bestend2); + } //end if + dist = VectorDistance(v2, v4); + if (dist < bestdist) + { + bestdist = dist; + VectorCopy(v2, beststart1); + VectorCopy(v2, beststart2); + VectorCopy(v4, bestend1); + VectorCopy(v4, bestend2); + } //end if + } //end if + return bestdist; +} //end of the function AAS_ClosestEdgePoints +//=========================================================================== +// creates possible jump reachabilities between the areas +// +// The two closest points on the ground of the areas are calculated +// One of the points will be on an edge of a ground face of area1 and +// one on an edge of a ground face of area2. +// If there is a range of closest points the point in the middle of this range +// is selected. +// Between these two points there must be one or more gaps. +// If the gaps exist a potential jump is predicted. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Jump(int area1num, int area2num) +{ + int i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype; + int stopevent, areas[10], numareas; + float phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed; + vec_t *v1, *v2, *v3, *v4; + vec3_t beststart, beststart2, bestend, bestend2; + vec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2; + aas_edge_t *edge1, *edge2; + aas_plane_t *plane1, *plane2, *plane; + aas_trace_t trace; + aas_clientmove_t move; + aas_lreachability_t *lreach; + + if (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse; + //cannot jump from or to a crouch area + if (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + // + phys_jumpvel = aassettings.phys_jumpvel; + //maximum distance a player can jump + maxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel); + //maximum height a player can jump with the given initial z velocity + maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); + + //if the areas are not near anough in the x-y direction + for (i = 0; i < 2; i++) + { + if (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse; + if (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse; + } //end for + //if area2 is way to high to jump up to + if (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse; + // + bestdist = 999999; + // + for (i = 0; i < area1->numfaces; i++) + { + face1num = aasworld.faceindex[area1->firstface + i]; + face1 = &aasworld.faces[abs(face1num)]; + //if not a ground face + if (!(face1->faceflags & FACE_GROUND)) continue; + // + for (j = 0; j < area2->numfaces; j++) + { + face2num = aasworld.faceindex[area2->firstface + j]; + face2 = &aasworld.faces[abs(face2num)]; + //if not a ground face + if (!(face2->faceflags & FACE_GROUND)) continue; + // + for (k = 0; k < face1->numedges; k++) + { + edge1num = abs(aasworld.edgeindex[face1->firstedge + k]); + edge1 = &aasworld.edges[edge1num]; + for (l = 0; l < face2->numedges; l++) + { + edge2num = abs(aasworld.edgeindex[face2->firstedge + l]); + edge2 = &aasworld.edges[edge2num]; + //calculate the minimum distance between the two edges + v1 = aasworld.vertexes[edge1->v[0]]; + v2 = aasworld.vertexes[edge1->v[1]]; + v3 = aasworld.vertexes[edge2->v[0]]; + v4 = aasworld.vertexes[edge2->v[1]]; + //get the ground planes + plane1 = &aasworld.planes[face1->planenum]; + plane2 = &aasworld.planes[face2->planenum]; + // + bestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2, + beststart, bestend, + beststart2, bestend2, bestdist); + } //end for + } //end for + } //end for + } //end for + VectorMiddle(beststart, beststart2, beststart); + VectorMiddle(bestend, bestend2, bestend); + if (bestdist > 4 && bestdist < maxjumpdistance) + { +// Log_Write("shortest distance between %d and %d is %f\r\n", area1num, area2num, bestdist); + // if very close and almost no height difference then the bot can walk + if (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8) + { + speed = 400; + traveltype = TRAVEL_WALKOFFLEDGE; + } //end if + else if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) + { + //FIXME: why multiply with 1.2??? + speed *= 1.2f; + traveltype = TRAVEL_WALKOFFLEDGE; + } //end else if + else + { + //get the horizontal speed for the jump, if it isn't possible to calculate this + //speed (the jump is not possible) then there's no jump reachability created + if (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed)) + return qfalse; + speed *= 1.05f; + traveltype = TRAVEL_JUMP; + // + //NOTE: test if the horizontal distance isn't too small + VectorSubtract(bestend, beststart, dir); + dir[2] = 0; + if (VectorLength(dir) < 10) + return qfalse; + } //end if + // + VectorSubtract(bestend, beststart, dir); + VectorNormalize(dir); + VectorMA(beststart, 1, dir, teststart); + // + VectorCopy(teststart, testend); + testend[2] -= 100; + trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); + // + if (trace.startsolid) + return qfalse; + if (trace.fraction < 1) + { + plane = &aasworld.planes[trace.planenum]; + // if the bot can stand on the surface + if (DotProduct(plane->normal, up) >= 0.7) + { + // if no lava or slime below + if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) + { + if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) + return qfalse; + } //end if + } //end if + } //end if + // + VectorMA(bestend, -1, dir, teststart); + // + VectorCopy(teststart, testend); + testend[2] -= 100; + trace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1); + // + if (trace.startsolid) + return qfalse; + if (trace.fraction < 1) + { + plane = &aasworld.planes[trace.planenum]; + // if the bot can stand on the surface + if (DotProduct(plane->normal, up) >= 0.7) + { + // if no lava or slime below + if (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME))) + { + if (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier) + return qfalse; + } //end if + } //end if + } //end if + // + // get command movement + VectorClear(cmdmove); + if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) + cmdmove[2] = aassettings.phys_jumpvel; + else + cmdmove[2] = 0; + // + VectorSubtract(bestend, beststart, dir); + dir[2] = 0; + VectorNormalize(dir); + CrossProduct(dir, up, sidewards); + // + stopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE; + if (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num)) + stopevent |= SE_TOUCHCLUSTERPORTAL; + // + for (i = 0; i < 3; i++) + { + // + if (i == 1) + VectorAdd(testend, sidewards, testend); + else if (i == 2) + VectorSubtract(bestend, sidewards, testend); + else + VectorCopy(bestend, testend); + VectorSubtract(testend, beststart, dir); + dir[2] = 0; + VectorNormalize(dir); + VectorScale(dir, speed, velocity); + // + AAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 3, 30, 0.1f, + stopevent, 0, qfalse); + // if prediction time wasn't enough to fully predict the movement + if (move.frames >= 30) + return qfalse; + // don't enter slime or lava and don't fall from too high + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) + return qfalse; + // never jump or fall through a cluster portal + if (move.stopevent & SE_TOUCHCLUSTERPORTAL) + return qfalse; + //the end position should be in area2, also test a little bit back + //because the predicted jump could have rushed through the area + VectorMA(move.endpos, -64, dir, teststart); + teststart[2] += 1; + numareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, sizeof(areas) / sizeof(int)); + for (j = 0; j < numareas; j++) + { + if (areas[j] == area2num) + break; + } //end for + if (j < numareas) + break; + } + if (i >= 3) + return qfalse; + // +#ifdef REACH_DEBUG + //create the reachability + Log_Write("jump reachability between %d and %d\r\n", area1num, area2num); +#endif //REACH_DEBUG + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(beststart, lreach->start); + VectorCopy(bestend, lreach->end); + lreach->traveltype = traveltype; + + VectorSubtract(bestend, beststart, dir); + height = dir[2]; + dir[2] = 0; + if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir)) + { + lreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity; + } + else + { + lreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity; + } //end if + // + if (!AAS_AreaJumpPad(area2num)) + { + if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5) + { + lreach->traveltime += aassettings.rs_falldamage5; + } //end if + else if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10) + { + lreach->traveltime += aassettings.rs_falldamage10; + } //end if + } //end if + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + if ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP) + reach_jump++; + else + reach_walkoffledge++; + } //end if + return qfalse; +} //end of the function AAS_Reachability_Jump +//=========================================================================== +// create a possible ladder reachability from area1 to area2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Ladder(int area1num, int area2num) +{ + int i, j, k, l, edge1num, edge2num, sharededgenum, lowestedgenum; + int face1num, face2num, ladderface1num, ladderface2num; + int ladderface1vertical, ladderface2vertical, firstv; + float face1area, face2area, bestface1area, bestface2area; + float phys_jumpvel, maxjumpheight; + vec3_t area1point, area2point, v1, v2, up = {0, 0, 1}; + vec3_t mid, lowestpoint, start, end, sharededgevec, dir; + aas_area_t *area1, *area2; + aas_face_t *face1, *face2, *ladderface1, *ladderface2; + aas_plane_t *plane1, *plane2; + aas_edge_t *sharededge, *edge1; + aas_lreachability_t *lreach; + aas_trace_t trace; + + if (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse; + // + phys_jumpvel = aassettings.phys_jumpvel; + //maximum height a player can jump with the given initial z velocity + maxjumpheight = AAS_MaxJumpHeight(phys_jumpvel); + + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + // + ladderface1 = NULL; + ladderface2 = NULL; + ladderface1num = 0; //make compiler happy + ladderface2num = 0; //make compiler happy + bestface1area = -9999; + bestface2area = -9999; + sharededgenum = 0; //make compiler happy + lowestedgenum = 0; //make compiler happy + // + for (i = 0; i < area1->numfaces; i++) + { + face1num = aasworld.faceindex[area1->firstface + i]; + face1 = &aasworld.faces[abs(face1num)]; + //if not a ladder face + if (!(face1->faceflags & FACE_LADDER)) continue; + // + for (j = 0; j < area2->numfaces; j++) + { + face2num = aasworld.faceindex[area2->firstface + j]; + face2 = &aasworld.faces[abs(face2num)]; + //if not a ladder face + if (!(face2->faceflags & FACE_LADDER)) continue; + //check if the faces share an edge + for (k = 0; k < face1->numedges; k++) + { + edge1num = aasworld.edgeindex[face1->firstedge + k]; + for (l = 0; l < face2->numedges; l++) + { + edge2num = aasworld.edgeindex[face2->firstedge + l]; + if (abs(edge1num) == abs(edge2num)) + { + //get the face with the largest area + face1area = AAS_FaceArea(face1); + face2area = AAS_FaceArea(face2); + if (face1area > bestface1area && face2area > bestface2area) + { + bestface1area = face1area; + bestface2area = face2area; + ladderface1 = face1; + ladderface2 = face2; + ladderface1num = face1num; + ladderface2num = face2num; + sharededgenum = edge1num; + } //end if + break; + } //end if + } //end for + if (l != face2->numedges) break; + } //end for + } //end for + } //end for + // + if (ladderface1 && ladderface2) + { + //get the middle of the shared edge + sharededge = &aasworld.edges[abs(sharededgenum)]; + firstv = sharededgenum < 0; + // + VectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1); + VectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2); + VectorAdd(v1, v2, area1point); + VectorScale(area1point, 0.5, area1point); + VectorCopy(area1point, area2point); + // + //if the face plane in area 1 is pretty much vertical + plane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)]; + plane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)]; + // + //get the points really into the areas + VectorSubtract(v2, v1, sharededgevec); + CrossProduct(plane1->normal, sharededgevec, dir); + VectorNormalize(dir); + //NOTE: 32 because that's larger than 16 (bot bbox x,y) + VectorMA(area1point, -32, dir, area1point); + VectorMA(area2point, 32, dir, area2point); + // + ladderface1vertical = abs(DotProduct(plane1->normal, up)) < 0.1; + ladderface2vertical = abs(DotProduct(plane2->normal, up)) < 0.1; + //there's only reachability between vertical ladder faces + if (!ladderface1vertical && !ladderface2vertical) return qfalse; + //if both vertical ladder faces + if (ladderface1vertical && ladderface2vertical + //and the ladder faces do not make a sharp corner + && DotProduct(plane1->normal, plane2->normal) > 0.7 + //and the shared edge is not too vertical + && abs(DotProduct(sharededgevec, up)) < 0.7) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = ladderface1num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area1point, lreach->start); + //VectorCopy(area2point, lreach->end); + VectorMA(area2point, -3, plane1->normal, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_ladder++; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface2num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area2point, lreach->start); + //VectorCopy(area1point, lreach->end); + VectorMA(area1point, -3, plane1->normal, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_ladder++; + // + return qtrue; + } //end if + //if the second ladder face is also a ground face + //create ladder end (just ladder) reachability and + //walk off a ladder (ledge) reachability + if (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND)) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = ladderface1num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area1point, lreach->start); + VectorCopy(area2point, lreach->end); + lreach->end[2] += 16; + VectorMA(lreach->end, -15, plane1->normal, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_ladder++; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface2num; + lreach->edgenum = abs(sharededgenum); + VectorCopy(area2point, lreach->start); + VectorCopy(area1point, lreach->end); + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_walkoffledge++; + // + return qtrue; + } //end if + // + if (ladderface1vertical) + { + //find lowest edge of the ladder face + lowestpoint[2] = 99999; + for (i = 0; i < ladderface1->numedges; i++) + { + edge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]); + edge1 = &aasworld.edges[edge1num]; + // + VectorCopy(aasworld.vertexes[edge1->v[0]], v1); + VectorCopy(aasworld.vertexes[edge1->v[1]], v2); + // + VectorAdd(v1, v2, mid); + VectorScale(mid, 0.5, mid); + // + if (mid[2] < lowestpoint[2]) + { + VectorCopy(mid, lowestpoint); + lowestedgenum = edge1num; + } //end if + } //end for + // + plane1 = &aasworld.planes[ladderface1->planenum]; + //trace down in the middle of this edge + VectorMA(lowestpoint, 5, plane1->normal, start); + VectorCopy(start, end); + start[2] += 5; + end[2] -= 100; + //trace without entity collision + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + // + // +#ifdef REACH_DEBUG + if (trace.startsolid) + { + Log_Write("trace from area %d started in solid\r\n", area1num); + } //end if +#endif //REACH_DEBUG + // + trace.endpos[2] += 1; + area2num = AAS_PointAreaNum(trace.endpos); + // + area2 = &aasworld.areas[area2num]; + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + // + if (face2->faceflags & FACE_LADDER) + { + plane2 = &aasworld.planes[face2->planenum]; + if (abs(DotProduct(plane2->normal, up)) < 0.1) break; + } //end if + } //end for + //if from another area without vertical ladder faces + if (i >= area2->numfaces && area2num != area1num && + //the reachabilities shouldn't exist already + !AAS_ReachabilityExists(area1num, area2num) && + !AAS_ReachabilityExists(area2num, area1num)) + { + //if the height is jumpable + if (start[2] - trace.endpos[2] < maxjumpheight) + { + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = ladderface1num; + lreach->edgenum = lowestedgenum; + VectorCopy(lowestpoint, lreach->start); + VectorCopy(trace.endpos, lreach->end); + lreach->traveltype = TRAVEL_LADDER; + lreach->traveltime = 10; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_ladder++; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface1num; + lreach->edgenum = lowestedgenum; + VectorCopy(trace.endpos, lreach->start); + //get the end point a little bit into the ladder + VectorMA(lowestpoint, -5, plane1->normal, lreach->end); + //get the end point a little higher + lreach->end[2] += 10; + lreach->traveltype = TRAVEL_JUMP; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_jump++; + // + return qtrue; +#ifdef REACH_DEBUG + Log_Write("jump up to ladder reach between %d and %d\r\n", area2num, area1num); +#endif //REACH_DEBUG + } //end if +#ifdef REACH_DEBUG + else Log_Write("jump too high between area %d and %d\r\n", area2num, area1num); +#endif //REACH_DEBUG + } //end if + /*//if slime or lava below the ladder + //try jump reachability from far towards the ladder + if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME + | AREACONTENTS_LAVA)) + { + for (i = 20; i <= 120; i += 20) + { + //trace down in the middle of this edge + VectorMA(lowestpoint, i, plane1->normal, start); + VectorCopy(start, end); + start[2] += 5; + end[2] -= 100; + //trace without entity collision + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + // + if (trace.startsolid) break; + trace.endpos[2] += 1; + area2num = AAS_PointAreaNum(trace.endpos); + if (area2num == area1num) continue; + // + if (start[2] - trace.endpos[2] > maxjumpheight) continue; + if (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME + | AREACONTENTS_LAVA)) continue; + // + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area1num; + lreach->facenum = ladderface1num; + lreach->edgenum = lowestedgenum; + VectorCopy(trace.endpos, lreach->start); + VectorCopy(lowestpoint, lreach->end); + lreach->end[2] += 5; + lreach->traveltype = TRAVEL_JUMP; + lreach->traveltime = 10; + lreach->next = areareachability[area2num]; + areareachability[area2num] = lreach; + // + reach_jump++; + // + Log_Write("jump far to ladder reach between %d and %d\r\n", area2num, area1num); + // + break; + } //end for + } //end if*/ + } //end if + } //end if + return qfalse; +} //end of the function AAS_Reachability_Ladder +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TravelFlagsForTeam(int ent) +{ + int notteam; + + if (!AAS_IntForBSPEpairKey(ent, "bot_notteam", ¬team)) + return 0; + if (notteam == 1) + return TRAVELFLAG_NOTTEAM1; + if (notteam == 2) + return TRAVELFLAG_NOTTEAM2; + return 0; +} //end of the function AAS_TravelFlagsForTeam +//=========================================================================== +// create possible teleporter reachabilities +// this is very game dependent.... :( +// +// classname = trigger_multiple or trigger_teleport +// target = "t1" +// +// classname = target_teleporter +// targetname = "t1" +// target = "t2" +// +// classname = misc_teleporter_dest +// targetname = "t2" +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_Teleport(void) +{ + int area1num, area2num; + char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY]; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + int ent, dest; + float angle; + vec3_t origin, destorigin, mins, maxs, end, angles; + vec3_t mid, velocity, cmdmove; + aas_lreachability_t *lreach; + aas_clientmove_t move; + aas_trace_t trace; + aas_link_t *areas, *link; + + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (!strcmp(classname, "trigger_multiple")) + { + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); +//#ifdef REACH_DEBUG + botimport.Print(PRT_MESSAGE, "trigger_multiple model = \"%s\"\n", model); +//#endif REACH_DEBUG + VectorClear(angles); + AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); + // + if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "trigger_multiple at %1.0f %1.0f %1.0f without target\n", + origin[0], origin[1], origin[2]); + continue; + } //end if + for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) + { + if (!AAS_ValueForBSPEpairKey(dest, "classname", classname, MAX_EPAIRKEY)) continue; + if (!strcmp(classname, "target_teleporter")) + { + if (!AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) continue; + if (!strcmp(targetname, target)) + { + break; + } //end if + } //end if + } //end for + if (!dest) + { + continue; + } //end if + if (!AAS_ValueForBSPEpairKey(dest, "target", target, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "target_teleporter without target\n"); + continue; + } //end if + } //end else + else if (!strcmp(classname, "trigger_teleport")) + { + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); +//#ifdef REACH_DEBUG + botimport.Print(PRT_MESSAGE, "trigger_teleport model = \"%s\"\n", model); +//#endif REACH_DEBUG + VectorClear(angles); + AAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin); + // + if (!AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "trigger_teleport at %1.0f %1.0f %1.0f without target\n", + origin[0], origin[1], origin[2]); + continue; + } //end if + } //end if + else + { + continue; + } //end else + // + for (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest)) + { + //classname should be misc_teleporter_dest + //but I've also seen target_position and actually any + //entity could be used... burp + if (AAS_ValueForBSPEpairKey(dest, "targetname", targetname, MAX_EPAIRKEY)) + { + if (!strcmp(targetname, target)) + { + break; + } //end if + } //end if + } //end for + if (!dest) + { + botimport.Print(PRT_ERROR, "teleporter without misc_teleporter_dest (%s)\n", target); + continue; + } //end if + if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); + continue; + } //end if + // + area2num = AAS_PointAreaNum(destorigin); + //if not teleported into a teleporter or into a jumppad + if (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num)) + { + VectorCopy(destorigin, end); + end[2] -= 64; + trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); + continue; + } //end if + area2num = AAS_PointAreaNum(trace.endpos); + // + /* + if (!AAS_AreaTeleporter(area2num) && + !AAS_AreaJumpPad(area2num) && + !AAS_AreaGrounded(area2num)) + { + VectorCopy(trace.endpos, destorigin); + } + else*/ + { + //predict where you'll end up + AAS_FloatForBSPEpairKey(dest, "angle", &angle); + if (angle) + { + VectorSet(angles, 0, angle, 0); + AngleVectors(angles, velocity, NULL, NULL); + VectorScale(velocity, 400, velocity); + } //end if + else + { + VectorClear(velocity); + } //end else + VectorClear(cmdmove); + AAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 0, 30, 0.1f, + SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue); + area2num = AAS_PointAreaNum(move.endpos); + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA)) + { + botimport.Print(PRT_WARNING, "teleported into slime or lava at dest %s\n", target); + } //end if + VectorCopy(move.endpos, destorigin); + } //end else + } //end if + // + //botimport.Print(PRT_MESSAGE, "teleporter brush origin at %f %f %f\n", origin[0], origin[1], origin[2]); + //botimport.Print(PRT_MESSAGE, "teleporter brush mins = %f %f %f\n", mins[0], mins[1], mins[2]); + //botimport.Print(PRT_MESSAGE, "teleporter brush maxs = %f %f %f\n", maxs[0], maxs[1], maxs[2]); + VectorAdd(origin, mins, mins); + VectorAdd(origin, maxs, maxs); + // + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + //link an invalid (-1) entity + areas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH); + if (!areas) botimport.Print(PRT_MESSAGE, "trigger_multiple not in any area\n"); + // + for (link = areas; link; link = link->next_area) + { + //if (!AAS_AreaGrounded(link->areanum)) continue; + if (!AAS_AreaTeleporter(link->areanum)) continue; + // + area1num = link->areanum; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) break; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(mid, lreach->start); + VectorCopy(destorigin, lreach->end); + lreach->traveltype = TRAVEL_TELEPORT; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_teleport; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_teleport++; + } //end for + //unlink the invalid entity + AAS_UnlinkFromAreas(areas); + } //end for +} //end of the function AAS_Reachability_Teleport +//=========================================================================== +// create possible elevator (func_plat) reachabilities +// this is very game dependent.... :( +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_Elevator(void) +{ + int area1num, area2num, modelnum, i, j, k, l, n, p; + float lip, height, speed; + char model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY]; + int ent; + vec3_t mins, maxs, origin, angles = {0, 0, 0}; + vec3_t pos1, pos2, mids, platbottom, plattop; + vec3_t bottomorg, toporg, start, end, dir; + vec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8]; + aas_lreachability_t *lreach; + aas_trace_t trace; + +#ifdef REACH_DEBUG + Log_Write("AAS_Reachability_Elevator\r\n"); +#endif //REACH_DEBUG + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (!strcmp(classname, "func_plat")) + { +#ifdef REACH_DEBUG + Log_Write("found func plat\r\n"); +#endif //REACH_DEBUG + if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "func_plat without model\n"); + continue; + } //end if + //get the model number, and skip the leading * + modelnum = atoi(model+1); + if (modelnum <= 0) + { + botimport.Print(PRT_ERROR, "func_plat with invalid model number\n"); + continue; + } //end if + //get the mins, maxs and origin of the model + //NOTE: the origin is usually (0,0,0) and the mins and maxs + // are the absolute mins and maxs + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); + // + AAS_VectorForBSPEpairKey(ent, "origin", origin); + //pos1 is the top position, pos2 is the bottom + VectorCopy(origin, pos1); + VectorCopy(origin, pos2); + //get the lip of the plat + AAS_FloatForBSPEpairKey(ent, "lip", &lip); + if (!lip) lip = 8; + //get the movement height of the plat + AAS_FloatForBSPEpairKey(ent, "height", &height); + if (!height) height = (maxs[2] - mins[2]) - lip; + //get the speed of the plat + AAS_FloatForBSPEpairKey(ent, "speed", &speed); + if (!speed) speed = 200; + //get bottom position below pos1 + pos2[2] -= height; + // + //get a point just above the plat in the bottom position + VectorAdd(mins, maxs, mids); + VectorMA(pos2, 0.5, mids, platbottom); + platbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2; + //get a point just above the plat in the top position + VectorAdd(mins, maxs, mids); + VectorMA(pos2, 0.5, mids, plattop); + plattop[2] = maxs[2] + 2; + // + /*if (!area1num) + { + Log_Write("no grounded area near plat bottom\r\n"); + continue; + } //end if*/ + //get the mins and maxs a little larger + for (i = 0; i < 3; i++) + { + mins[i] -= 1; + maxs[i] += 1; + } //end for + // + //botimport.Print(PRT_MESSAGE, "platbottom[2] = %1.1f plattop[2] = %1.1f\n", platbottom[2], plattop[2]); + // + VectorAdd(mins, maxs, mids); + VectorScale(mids, 0.5, mids); + // + xvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0]; + yvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1]; + // + xvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0]; + yvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1]; + //find adjacent areas around the bottom of the plat + for (i = 0; i < 9; i++) + { + if (i < 8) //check at the sides of the plat + { + bottomorg[0] = origin[0] + xvals[i]; + bottomorg[1] = origin[1] + yvals[i]; + bottomorg[2] = platbottom[2] + 16; + //get a grounded or swim area near the plat in the bottom position + area1num = AAS_PointAreaNum(bottomorg); + for (k = 0; k < 16; k++) + { + if (area1num) + { + if (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break; + } //end if + bottomorg[2] += 4; + area1num = AAS_PointAreaNum(bottomorg); + } //end if + //if in solid + if (k >= 16) + { + continue; + } //end if + } //end if + else //at the middle of the plat + { + VectorCopy(plattop, bottomorg); + bottomorg[2] += 24; + area1num = AAS_PointAreaNum(bottomorg); + if (!area1num) continue; + VectorCopy(platbottom, bottomorg); + bottomorg[2] += 24; + } //end else + //look at adjacent areas around the top of the plat + //make larger steps to outside the plat everytime + for (n = 0; n < 3; n++) + { + for (k = 0; k < 3; k++) + { + mins[k] -= 4; + maxs[k] += 4; + } //end for + xvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0]; + yvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1]; + // + xvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0]; + yvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1]; + // + for (j = 0; j < 8; j++) + { + toporg[0] = origin[0] + xvals_top[j]; + toporg[1] = origin[1] + yvals_top[j]; + toporg[2] = plattop[2] + 16; + //get a grounded or swim area near the plat in the top position + area2num = AAS_PointAreaNum(toporg); + for (l = 0; l < 16; l++) + { + if (area2num) + { + if (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num)) + { + VectorCopy(plattop, start); + start[2] += 32; + VectorCopy(toporg, end); + end[2] += 1; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (trace.fraction >= 1) break; + } //end if + } //end if + toporg[2] += 4; + area2num = AAS_PointAreaNum(toporg); + } //end if + //if in solid + if (l >= 16) continue; + //never create a reachability in the same area + if (area2num == area1num) continue; + //if the area isn't grounded + if (!AAS_AreaGrounded(area2num)) continue; + //if there already exists reachability between the areas + if (AAS_ReachabilityExists(area1num, area2num)) continue; + //if the reachability start is within the elevator bounding box + VectorSubtract(bottomorg, platbottom, dir); + VectorNormalize(dir); + dir[0] = bottomorg[0] + 24 * dir[0]; + dir[1] = bottomorg[1] + 24 * dir[1]; + dir[2] = bottomorg[2]; + // + for (p = 0; p < 3; p++) + if (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break; + if (p >= 3) continue; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) continue; + lreach->areanum = area2num; + //the facenum is the model number + lreach->facenum = modelnum; + //the edgenum is the height + lreach->edgenum = (int) height; + // + VectorCopy(dir, lreach->start); + VectorCopy(toporg, lreach->end); + lreach->traveltype = TRAVEL_ELEVATOR; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_startelevator + height * 100 / speed; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + //don't go any further to the outside + n = 9999; + // +#ifdef REACH_DEBUG + Log_Write("elevator reach from %d to %d\r\n", area1num, area2num); +#endif //REACH_DEBUG + // + reach_elevator++; + } //end for + } //end for + } //end for + } //end if + } //end for +} //end of the function AAS_Reachability_Elevator +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface) +{ + int i, j, k, l; + int facenum, edgenum, bestfacenum; + float *v1, *v2, *v3, *v4; + float bestdist, speed, hordist, dist; + vec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint; + aas_lreachability_t *lreach, *lreachabilities; + aas_area_t *area; + aas_face_t *face; + aas_edge_t *edge; + aas_plane_t *faceplane, *bestfaceplane; + + // + lreachabilities = NULL; + bestfacenum = 0; + bestfaceplane = NULL; + // + for (i = 1; i < aasworld.numareas; i++) + { + area = &aasworld.areas[i]; + // get the shortest distance between one of the func_bob start edges and + // one of the face edges of area1 + bestdist = 999999; + for (j = 0; j < area->numfaces; j++) + { + facenum = aasworld.faceindex[area->firstface + j]; + face = &aasworld.faces[abs(facenum)]; + //if not a ground face + if (!(face->faceflags & FACE_GROUND)) continue; + //get the ground planes + faceplane = &aasworld.planes[face->planenum]; + // + for (k = 0; k < face->numedges; k++) + { + edgenum = abs(aasworld.edgeindex[face->firstedge + k]); + edge = &aasworld.edges[edgenum]; + //calculate the minimum distance between the two edges + v1 = aasworld.vertexes[edge->v[0]]; + v2 = aasworld.vertexes[edge->v[1]]; + // + for (l = 0; l < numpoints; l++) + { + v3 = facepoints[l]; + v4 = facepoints[(l+1) % numpoints]; + dist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane, + beststart, bestend, + beststart2, bestend2, bestdist); + if (dist < bestdist) + { + bestfacenum = facenum; + bestfaceplane = faceplane; + bestdist = dist; + } //end if + } //end for + } //end for + } //end for + // + if (bestdist > 192) continue; + // + VectorMiddle(beststart, beststart2, beststart); + VectorMiddle(bestend, bestend2, bestend); + // + if (!towardsface) + { + VectorCopy(beststart, tmp); + VectorCopy(bestend, beststart); + VectorCopy(tmp, bestend); + } //end if + // + VectorSubtract(bestend, beststart, hordir); + hordir[2] = 0; + hordist = VectorLength(hordir); + // + if (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue; + //the end point should not be significantly higher than the start point + if (bestend[2] - 32 > beststart[2]) continue; + //don't fall down too far + if (bestend[2] < beststart[2] - 128) continue; + //the distance should not be too far + if (hordist > 32) + { + //check for walk off ledge + if (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue; + } //end if + // + beststart[2] += 1; + bestend[2] += 1; + // + if (towardsface) VectorCopy(bestend, testpoint); + else VectorCopy(beststart, testpoint); + testpoint[2] = 0; + testpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2]; + // + if (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f)) + { + //if the faces are not overlapping then only go down + if (bestend[2] - 16 > beststart[2]) continue; + } //end if + lreach = AAS_AllocReachability(); + if (!lreach) return lreachabilities; + lreach->areanum = i; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(beststart, lreach->start); + VectorCopy(bestend, lreach->end); + lreach->traveltype = 0; + lreach->traveltime = 0; + lreach->next = lreachabilities; + lreachabilities = lreach; +#ifndef BSPC + if (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1); + else AAS_PermanentLine(lreach->start, lreach->end, 2); +#endif + } //end for + return lreachabilities; +} //end of the function AAS_FindFaceReachabilities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_FuncBobbing(void) +{ + int ent, spawnflags, modelnum, axis; + int i, numareas, areas[10]; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + vec3_t origin, move_end, move_start, move_start_top, move_end_top; + vec3_t mins, maxs, angles = {0, 0, 0}; + vec3_t start_edgeverts[4], end_edgeverts[4], mid; + vec3_t org, start, end, dir, points[10]; + float height; + aas_plane_t start_plane, end_plane; + aas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach; + aas_lreachability_t *firststartreach, *firstendreach; + + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (strcmp(classname, "func_bobbing")) continue; + AAS_FloatForBSPEpairKey(ent, "height", &height); + if (!height) height = 32; + // + if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) + { + botimport.Print(PRT_ERROR, "func_bobbing without model\n"); + continue; + } //end if + //get the model number, and skip the leading * + modelnum = atoi(model+1); + if (modelnum <= 0) + { + botimport.Print(PRT_ERROR, "func_bobbing with invalid model number\n"); + continue; + } //end if + //if the entity has an origin set then use it + if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) + VectorSet(origin, 0, 0, 0); + // + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + // + VectorAdd(mins, origin, mins); + VectorAdd(maxs, origin, maxs); + // + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + VectorCopy(mid, origin); + // + VectorCopy(origin, move_end); + VectorCopy(origin, move_start); + // + AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + // set the axis of bobbing + if (spawnflags & 1) axis = 0; + else if (spawnflags & 2) axis = 1; + else axis = 2; + // + move_start[axis] -= height; + move_end[axis] += height; + // + Log_Write("funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\n", + modelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]); + // +#ifndef BSPC + /* + AAS_DrawPermanentCross(move_start, 4, 1); + AAS_DrawPermanentCross(move_end, 4, 2); + */ +#endif + // + for (i = 0; i < 4; i++) + { + VectorCopy(move_start, start_edgeverts[i]); + start_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z + start_edgeverts[i][2] += 24; //+ player origin to ground dist + } //end for + start_edgeverts[0][0] += maxs[0] - mid[0]; + start_edgeverts[0][1] += maxs[1] - mid[1]; + start_edgeverts[1][0] += maxs[0] - mid[0]; + start_edgeverts[1][1] += mins[1] - mid[1]; + start_edgeverts[2][0] += mins[0] - mid[0]; + start_edgeverts[2][1] += mins[1] - mid[1]; + start_edgeverts[3][0] += mins[0] - mid[0]; + start_edgeverts[3][1] += maxs[1] - mid[1]; + // + start_plane.dist = start_edgeverts[0][2]; + VectorSet(start_plane.normal, 0, 0, 1); + // + for (i = 0; i < 4; i++) + { + VectorCopy(move_end, end_edgeverts[i]); + end_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z + end_edgeverts[i][2] += 24; //+ player origin to ground dist + } //end for + end_edgeverts[0][0] += maxs[0] - mid[0]; + end_edgeverts[0][1] += maxs[1] - mid[1]; + end_edgeverts[1][0] += maxs[0] - mid[0]; + end_edgeverts[1][1] += mins[1] - mid[1]; + end_edgeverts[2][0] += mins[0] - mid[0]; + end_edgeverts[2][1] += mins[1] - mid[1]; + end_edgeverts[3][0] += mins[0] - mid[0]; + end_edgeverts[3][1] += maxs[1] - mid[1]; + // + end_plane.dist = end_edgeverts[0][2]; + VectorSet(end_plane.normal, 0, 0, 1); + // +#ifndef BSPC +#if 0 + for (i = 0; i < 4; i++) + { + AAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1); + AAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1); + } //end for +#endif +#endif + VectorCopy(move_start, move_start_top); + move_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z + VectorCopy(move_end, move_end_top); + move_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z + // + if (!AAS_PointAreaNum(move_start_top)) continue; + if (!AAS_PointAreaNum(move_end_top)) continue; + // + for (i = 0; i < 2; i++) + { + firststartreach = firstendreach = NULL; + // + if (i == 0) + { + firststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue); + firstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse); + } //end if + else + { + firststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue); + firstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse); + } //end else + // + //create reachabilities from start to end + for (startreach = firststartreach; startreach; startreach = nextstartreach) + { + nextstartreach = startreach->next; + // + //trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1); + //if (trace.fraction < 1) continue; + // + for (endreach = firstendreach; endreach; endreach = nextendreach) + { + nextendreach = endreach->next; + // + //trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1); + //if (trace.fraction < 1) continue; + // + Log_Write("funcbob reach from area %d to %d\n", startreach->areanum, endreach->areanum); + // + // + if (i == 0) VectorCopy(move_start_top, org); + else VectorCopy(move_end_top, org); + VectorSubtract(startreach->start, org, dir); + dir[2] = 0; + VectorNormalize(dir); + VectorCopy(startreach->start, start); + VectorMA(startreach->start, 1, dir, start); + start[2] += 1; + VectorMA(startreach->start, 16, dir, end); + end[2] += 1; + // + numareas = AAS_TraceAreas(start, end, areas, points, 10); + if (numareas <= 0) continue; + if (numareas > 1) VectorCopy(points[1], startreach->start); + else VectorCopy(end, startreach->start); + // + if (!AAS_PointAreaNum(startreach->start)) continue; + if (!AAS_PointAreaNum(endreach->end)) continue; + // + lreach = AAS_AllocReachability(); + lreach->areanum = endreach->areanum; + if (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff); + else lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff); + lreach->facenum = (spawnflags << 16) | modelnum; + VectorCopy(startreach->start, lreach->start); + VectorCopy(endreach->end, lreach->end); +#ifndef BSPC +// AAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW); +// AAS_PermanentLine(lreach->start, lreach->end, 1); +#endif + lreach->traveltype = TRAVEL_FUNCBOB; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_funcbob; + reach_funcbob++; + lreach->next = areareachability[startreach->areanum]; + areareachability[startreach->areanum] = lreach; + // + } //end for + } //end for + for (startreach = firststartreach; startreach; startreach = nextstartreach) + { + nextstartreach = startreach->next; + AAS_FreeReachability(startreach); + } //end for + for (endreach = firstendreach; endreach; endreach = nextendreach) + { + nextendreach = endreach->next; + AAS_FreeReachability(endreach); + } //end for + //only go up with func_bobbing entities that go up and down + if (!(spawnflags & 1) && !(spawnflags & 2)) break; + } //end for + } //end for +} //end of the function AAS_Reachability_FuncBobbing +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_JumpPad(void) +{ + int face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads; + //int modelnum, ent2; + //float dist, time, height, gravity, forward; + float speed, zvel, hordist; + aas_face_t *face2; + aas_area_t *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, dir, cmdmove; + vec3_t velocity, absmins, absmaxs; + //vec3_t origin, ent2origin, angles, teststart; + aas_clientmove_t move; + //aas_trace_t trace; + aas_link_t *areas, *link; + //char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + char classname[MAX_EPAIRKEY]; + +#ifdef BSPC + bot_visualizejumppads = 0; +#else + bot_visualizejumppads = LibVarValue("bot_visualizejumppads", "0"); +#endif + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (strcmp(classname, "trigger_push")) continue; + // + if (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue; + /* + // + AAS_FloatForBSPEpairKey(ent, "speed", &speed); + if (!speed) speed = 1000; +// AAS_VectorForBSPEpairKey(ent, "angles", angles); +// AAS_SetMovedir(angles, velocity); +// VectorScale(velocity, speed, velocity); + VectorClear(angles); + //get the mins, maxs and origin of the model + AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY); + if (model[0]) modelnum = atoi(model+1); + else modelnum = 0; + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin); + VectorAdd(origin, absmins, absmins); + VectorAdd(origin, absmaxs, absmaxs); + // +#ifdef REACH_DEBUG + botimport.Print(PRT_MESSAGE, "absmins = %f %f %f\n", absmins[0], absmins[1], absmins[2]); + botimport.Print(PRT_MESSAGE, "absmaxs = %f %f %f\n", absmaxs[0], absmaxs[1], absmaxs[2]); +#endif REACH_DEBUG + VectorAdd(absmins, absmaxs, origin); + VectorScale (origin, 0.5, origin); + + //get the start areas + VectorCopy(origin, teststart); + teststart[2] += 64; + trace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1); + if (trace.startsolid) + { + botimport.Print(PRT_MESSAGE, "trigger_push start solid\n"); + VectorCopy(origin, areastart); + } //end if + else + { + VectorCopy(trace.endpos, areastart); + } //end else + areastart[2] += 0.125; + // + //AAS_DrawPermanentCross(origin, 4, 4); + //get the target entity + AAS_ValueForBSPEpairKey(ent, "target", target, MAX_EPAIRKEY); + for (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2)) + { + if (!AAS_ValueForBSPEpairKey(ent2, "targetname", targetname, MAX_EPAIRKEY)) continue; + if (!strcmp(targetname, target)) break; + } //end for + if (!ent2) + { + botimport.Print(PRT_MESSAGE, "trigger_push without target entity %s\n", target); + continue; + } //end if + AAS_VectorForBSPEpairKey(ent2, "origin", ent2origin); + // + height = ent2origin[2] - origin[2]; + gravity = aassettings.sv_gravity; + time = sqrt( height / ( 0.5 * gravity ) ); + if (!time) + { + botimport.Print(PRT_MESSAGE, "trigger_push without time\n"); + continue; + } //end if + // set s.origin2 to the push velocity + VectorSubtract ( ent2origin, origin, velocity); + dist = VectorNormalize( velocity); + forward = dist / time; + //FIXME: why multiply by 1.1 + forward *= 1.1; + VectorScale(velocity, forward, velocity); + velocity[2] = time * gravity; + */ + //get the areas the jump pad brush is in + areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); + /* + for (link = areas; link; link = link->next_area) + { + if (link->areanum == 563) + { + ret = qfalse; + } + } + */ + for (link = areas; link; link = link->next_area) + { + if (AAS_AreaJumpPad(link->areanum)) break; + } //end for + if (!link) + { + botimport.Print(PRT_MESSAGE, "trigger_push not in any jump pad area\n"); + AAS_UnlinkFromAreas(areas); + continue; + } //end if + // + botimport.Print(PRT_MESSAGE, "found a trigger_push with velocity %f %f %f\n", velocity[0], velocity[1], velocity[2]); + //if there is a horizontal velocity check for a reachability without air control + if (velocity[0] || velocity[1]) + { + VectorSet(cmdmove, 0, 0, 0); + //VectorCopy(velocity, cmdmove); + //cmdmove[2] = 0; + Com_Memset(&move, 0, sizeof(aas_clientmove_t)); + area2num = 0; + for (i = 0; i < 20; i++) + { + AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 0, 30, 0.1f, + SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads); + area2num = move.endarea; + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + if (link->areanum == area2num) break; + } //end if + if (!link) break; + VectorCopy(move.endpos, areastart); + VectorCopy(move.velocity, velocity); + } //end for + if (area2num && i < 20) + { + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + if (AAS_ReachabilityExists(link->areanum, area2num)) continue; + //create a rocket or bfg jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) + { + AAS_UnlinkFromAreas(areas); + return; + } //end if + lreach->areanum = area2num; + //NOTE: the facenum is the Z velocity + lreach->facenum = velocity[2]; + //NOTE: the edgenum is the horizontal velocity + lreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]); + VectorCopy(areastart, lreach->start); + VectorCopy(move.endpos, lreach->end); + lreach->traveltype = TRAVEL_JUMPPAD; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_jumppad; + lreach->next = areareachability[link->areanum]; + areareachability[link->areanum] = lreach; + // + reach_jumppad++; + } //end for + } //end if + } //end if + // + if (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue; + //check for areas we can reach with air control + for (area2num = 1; area2num < aasworld.numareas; area2num++) + { + visualize = qfalse; + /* + if (area2num == 3568) + { + for (link = areas; link; link = link->next_area) + { + if (link->areanum == 3380) + { + visualize = qtrue; + botimport.Print(PRT_MESSAGE, "bah\n"); + } //end if + } //end for + } //end if*/ + //never try to go back to one of the original jumppad areas + //and don't create reachabilities if they already exist + for (link = areas; link; link = link->next_area) + { + if (AAS_ReachabilityExists(link->areanum, area2num)) break; + if (AAS_AreaJumpPad(link->areanum)) + { + if (link->areanum == area2num) break; + } //end if + } //end if + if (link) continue; + // + area2 = &aasworld.areas[area2num]; + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + //if it is not a ground face + if (!(face2->faceflags & FACE_GROUND)) continue; + //get the center of the face + AAS_FaceCenter(face2num, facecenter); + //only go higher up + if (facecenter[2] < areastart[2]) continue; + //get the jumppad jump z velocity + zvel = velocity[2]; + //get the horizontal speed for the jump, if it isn't possible to calculate this + //speed + ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); + if (ret && speed < 150) + { + //direction towards the face center + VectorSubtract(facecenter, areastart, dir); + dir[2] = 0; + hordist = VectorNormalize(dir); + //if (hordist < 1.6 * facecenter[2] - areastart[2]) + { + //get command movement + VectorScale(dir, speed, cmdmove); + // + AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize); + //if prediction time wasn't enough to fully predict the movement + //don't enter slime or lava and don't fall from too high + if (move.frames < 30 && + !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER))) + { + //never go back to the same jumppad + for (link = areas; link; link = link->next_area) + { + if (link->areanum == move.endarea) break; + } + if (!link) + { + for (link = areas; link; link = link->next_area) + { + if (!AAS_AreaJumpPad(link->areanum)) continue; + if (AAS_ReachabilityExists(link->areanum, area2num)) continue; + //create a jumppad reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) + { + AAS_UnlinkFromAreas(areas); + return; + } //end if + lreach->areanum = move.endarea; + //NOTE: the facenum is the Z velocity + lreach->facenum = velocity[2]; + //NOTE: the edgenum is the horizontal velocity + lreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]); + VectorCopy(areastart, lreach->start); + VectorCopy(facecenter, lreach->end); + lreach->traveltype = TRAVEL_JUMPPAD; + lreach->traveltype |= AAS_TravelFlagsForTeam(ent); + lreach->traveltime = aassettings.rs_aircontrolledjumppad; + lreach->next = areareachability[link->areanum]; + areareachability[link->areanum] = lreach; + // + reach_jumppad++; + } //end for + } + } //end if + } //end if + } //end for + } //end for + } //end for + AAS_UnlinkFromAreas(areas); + } //end for +} //end of the function AAS_Reachability_JumpPad +//=========================================================================== +// never point at ground faces +// always a higher and pretty far area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_Grapple(int area1num, int area2num) +{ + int face2num, i, j, areanum, numareas, areas[20]; + float mingrappleangle, z, hordist; + bsp_trace_t bsptrace; + aas_trace_t trace; + aas_face_t *face2; + aas_area_t *area1, *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1}; + vec_t *v; + + //only grapple when on the ground or swimming + if (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse; + //don't grapple from a crouch area + if (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse; + //NOTE: disabled area swim it doesn't work right + if (AAS_AreaSwim(area1num)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //don't grapple towards way lower areas + if (area2->maxs[2] < area1->mins[2]) return qfalse; + // + VectorCopy(aasworld.areas[area1num].center, start); + //if not a swim area + if (!AAS_AreaSwim(area1num)) + { + if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, + start[0], start[1], start[2]); + VectorCopy(start, end); + end[2] -= 1000; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) return qfalse; + VectorCopy(trace.endpos, areastart); + } //end if + else + { + if (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse; + } //end else + // + //start is now the start point + // + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + //if it is not a solid face + if (!(face2->faceflags & FACE_SOLID)) continue; + //direction towards the first vertex of the face + v = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]]; + VectorSubtract(v, areastart, dir); + //if the face plane is facing away + if (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue; + //get the center of the face + AAS_FaceCenter(face2num, facecenter); + //only go higher up with the grapple + if (facecenter[2] < areastart[2] + 64) continue; + //only use vertical faces or downward facing faces + if (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue; + //direction towards the face center + VectorSubtract(facecenter, areastart, dir); + // + z = dir[2]; + dir[2] = 0; + hordist = VectorLength(dir); + if (!hordist) continue; + //if too far + if (hordist > 2000) continue; + //check the minimal angle of the movement + mingrappleangle = 15; //15 degrees + if (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue; + // + VectorCopy(facecenter, start); + VectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end); + // + bsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID); + //the grapple won't stick to the sky and the grapple point should be near the AAS wall + if ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue; + //trace a full bounding box from the area center on the ground to + //the center of the face + VectorSubtract(facecenter, areastart, dir); + VectorNormalize(dir); + VectorMA(areastart, 4, dir, start); + VectorCopy(bsptrace.endpos, end); + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + VectorSubtract(trace.endpos, facecenter, dir); + if (VectorLength(dir) > 24) continue; + // + VectorCopy(trace.endpos, start); + VectorCopy(trace.endpos, end); + end[2] -= AAS_FallDamageDistance(); + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1); + if (trace.fraction >= 1) continue; + //area to end in + areanum = AAS_PointAreaNum(trace.endpos); + //if not in lava or slime + if (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA)) + { + continue; + } //end if + //do not go the the source area + if (areanum == area1num) continue; + //don't create reachabilities if they already exist + if (AAS_ReachabilityExists(area1num, areanum)) continue; + //only end in areas we can stand + if (!AAS_AreaGrounded(areanum)) continue; + //never go through cluster portals!! + numareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20); + if (numareas >= 20) continue; + for (j = 0; j < numareas; j++) + { + if (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break; + } //end for + if (j < numareas) continue; + //create a new reachability link + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = areanum; + lreach->facenum = face2num; + lreach->edgenum = 0; + VectorCopy(areastart, lreach->start); + //VectorCopy(facecenter, lreach->end); + VectorCopy(bsptrace.endpos, lreach->end); + lreach->traveltype = TRAVEL_GRAPPLEHOOK; + VectorSubtract(lreach->end, lreach->start, dir); + lreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25; + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_grapple++; + } //end for + // + return qfalse; +} //end of the function AAS_Reachability_Grapple +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetWeaponJumpAreaFlags(void) +{ + int ent, i; + vec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15}; + vec3_t origin; + int areanum, weaponjumpareas, spawnflags; + char classname[MAX_EPAIRKEY]; + + weaponjumpareas = 0; + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if ( + !strcmp(classname, "item_armor_body") || + !strcmp(classname, "item_armor_combat") || + !strcmp(classname, "item_health_mega") || + !strcmp(classname, "weapon_grenadelauncher") || + !strcmp(classname, "weapon_rocketlauncher") || + !strcmp(classname, "weapon_lightning") || + !strcmp(classname, "weapon_plasmagun") || + !strcmp(classname, "weapon_railgun") || + !strcmp(classname, "weapon_bfg") || + !strcmp(classname, "item_quad") || + !strcmp(classname, "item_regen") || + !strcmp(classname, "item_invulnerability")) + { + if (AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + spawnflags = 0; + AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + //if not a stationary item + if (!(spawnflags & 1)) + { + if (!AAS_DropToFloor(origin, mins, maxs)) + { + botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + } //end if + } //end if + //areanum = AAS_PointAreaNum(origin); + areanum = AAS_BestReachableArea(origin, mins, maxs, origin); + //the bot may rocket jump towards this area + aasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP; + // + //if (!AAS_AreaGrounded(areanum)) + // botimport.Print(PRT_MESSAGE, "area not grounded\n"); + // + weaponjumpareas++; + } //end if + } //end if + } //end for + for (i = 1; i < aasworld.numareas; i++) + { + if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) + { + aasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP; + weaponjumpareas++; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "%d weapon jump areas\n", weaponjumpareas); +} //end of the function AAS_SetWeaponJumpAreaFlags +//=========================================================================== +// create a possible weapon jump reachability from area1 to area2 +// +// check if there's a cool item in the second area +// check if area1 is lower than area2 +// check if the bot can rocketjump from area1 to area2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_Reachability_WeaponJump(int area1num, int area2num) +{ + int face2num, i, n, ret, visualize; + float speed, zvel, hordist; + aas_face_t *face2; + aas_area_t *area1, *area2; + aas_lreachability_t *lreach; + vec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart; + vec3_t velocity; + aas_clientmove_t move; + aas_trace_t trace; + + visualize = qfalse; +// if (area1num == 4436 && area2num == 4318) +// { +// visualize = qtrue; +// } + if (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse; + if (!AAS_AreaGrounded(area2num)) return qfalse; + //NOTE: only weapon jump towards areas with an interesting item in it?? + if (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse; + // + area1 = &aasworld.areas[area1num]; + area2 = &aasworld.areas[area2num]; + //don't weapon jump towards way lower areas + if (area2->maxs[2] < area1->mins[2]) return qfalse; + // + VectorCopy(aasworld.areas[area1num].center, start); + //if not a swim area + if (!AAS_PointAreaNum(start)) Log_Write("area %d center %f %f %f in solid?\r\n", area1num, + start[0], start[1], start[2]); + VectorCopy(start, end); + end[2] -= 1000; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (trace.startsolid) return qfalse; + VectorCopy(trace.endpos, areastart); + // + //areastart is now the start point + // + for (i = 0; i < area2->numfaces; i++) + { + face2num = aasworld.faceindex[area2->firstface + i]; + face2 = &aasworld.faces[abs(face2num)]; + //if it is not a solid face + if (!(face2->faceflags & FACE_GROUND)) continue; + //get the center of the face + AAS_FaceCenter(face2num, facecenter); + //only go higher up with weapon jumps + if (facecenter[2] < areastart[2] + 64) continue; + //NOTE: set to 2 to allow bfg jump reachabilities + for (n = 0; n < 1/*2*/; n++) + { + //get the rocket jump z velocity + if (n) zvel = AAS_BFGJumpZVelocity(areastart); + else zvel = AAS_RocketJumpZVelocity(areastart); + //get the horizontal speed for the jump, if it isn't possible to calculate this + //speed (the jump is not possible) then there's no jump reachability created + ret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed); + if (ret && speed < 300) + { + //direction towards the face center + VectorSubtract(facecenter, areastart, dir); + dir[2] = 0; + hordist = VectorNormalize(dir); + //if (hordist < 1.6 * (facecenter[2] - areastart[2])) + { + //get command movement + VectorScale(dir, speed, cmdmove); + VectorSet(velocity, 0, 0, zvel); + /* + //get command movement + VectorScale(dir, speed, velocity); + velocity[2] = zvel; + VectorSet(cmdmove, 0, 0, 0); + */ + // + AAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue, + velocity, cmdmove, 30, 30, 0.1f, + SE_ENTERWATER|SE_ENTERSLIME| + SE_ENTERLAVA|SE_HITGROUNDDAMAGE| + SE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize); + //if prediction time wasn't enough to fully predict the movement + //don't enter slime or lava and don't fall from too high + if (move.frames < 30 && + !(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + && (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD))) + { + //create a rocket or bfg jump reachability from area1 to area2 + lreach = AAS_AllocReachability(); + if (!lreach) return qfalse; + lreach->areanum = area2num; + lreach->facenum = 0; + lreach->edgenum = 0; + VectorCopy(areastart, lreach->start); + VectorCopy(facecenter, lreach->end); + if (n) + { + lreach->traveltype = TRAVEL_BFGJUMP; + lreach->traveltime = aassettings.rs_bfgjump; + } //end if + else + { + lreach->traveltype = TRAVEL_ROCKETJUMP; + lreach->traveltime = aassettings.rs_rocketjump; + } //end else + lreach->next = areareachability[area1num]; + areareachability[area1num] = lreach; + // + reach_rocketjump++; + return qtrue; + } //end if + } //end if + } //end if + } //end for + } //end for + // + return qfalse; +} //end of the function AAS_Reachability_WeaponJump +//=========================================================================== +// calculates additional walk off ledge reachabilities for the given area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Reachability_WalkOffLedge(int areanum) +{ + int i, j, k, l, m, n, p, areas[10], numareas; + int face1num, face2num, face3num, edge1num, edge2num, edge3num; + int otherareanum, gap, reachareanum, side; + aas_area_t *area, *area2; + aas_face_t *face1, *face2, *face3; + aas_edge_t *edge; + aas_plane_t *plane; + vec_t *v1, *v2; + vec3_t sharededgevec, mid, dir, testend; + aas_lreachability_t *lreach; + aas_trace_t trace; + + if (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return; + // + area = &aasworld.areas[areanum]; + // + for (i = 0; i < area->numfaces; i++) + { + face1num = aasworld.faceindex[area->firstface + i]; + face1 = &aasworld.faces[abs(face1num)]; + //face 1 must be a ground face + if (!(face1->faceflags & FACE_GROUND)) continue; + //go through all the edges of this ground face + for (k = 0; k < face1->numedges; k++) + { + edge1num = aasworld.edgeindex[face1->firstedge + k]; + //find another not ground face using this same edge + for (j = 0; j < area->numfaces; j++) + { + face2num = aasworld.faceindex[area->firstface + j]; + face2 = &aasworld.faces[abs(face2num)]; + //face 2 may not be a ground face + if (face2->faceflags & FACE_GROUND) continue; + //compare all the edges + for (l = 0; l < face2->numedges; l++) + { + edge2num = aasworld.edgeindex[face2->firstedge + l]; + if (abs(edge1num) == abs(edge2num)) + { + //get the area at the other side of the face + if (face2->frontarea == areanum) otherareanum = face2->backarea; + else otherareanum = face2->frontarea; + // + area2 = &aasworld.areas[otherareanum]; + //if the other area is grounded! + if (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED) + { + //check for a possible gap + gap = qfalse; + for (n = 0; n < area2->numfaces; n++) + { + face3num = aasworld.faceindex[area2->firstface + n]; + //may not be the shared face of the two areas + if (abs(face3num) == abs(face2num)) continue; + // + face3 = &aasworld.faces[abs(face3num)]; + //find an edge shared by all three faces + for (m = 0; m < face3->numedges; m++) + { + edge3num = aasworld.edgeindex[face3->firstedge + m]; + //but the edge should be shared by all three faces + if (abs(edge3num) == abs(edge1num)) + { + if (!(face3->faceflags & FACE_SOLID)) + { + gap = qtrue; + break; + } //end if + // + if (face3->faceflags & FACE_GROUND) + { + gap = qfalse; + break; + } //end if + //FIXME: there are more situations to be handled + gap = qtrue; + break; + } //end if + } //end for + if (m < face3->numedges) break; + } //end for + if (!gap) break; + } //end if + //check for a walk off ledge reachability + edge = &aasworld.edges[abs(edge1num)]; + side = edge1num < 0; + // + v1 = aasworld.vertexes[edge->v[side]]; + v2 = aasworld.vertexes[edge->v[!side]]; + // + plane = &aasworld.planes[face1->planenum]; + //get the points really into the areas + VectorSubtract(v2, v1, sharededgevec); + CrossProduct(plane->normal, sharededgevec, dir); + VectorNormalize(dir); + // + VectorAdd(v1, v2, mid); + VectorScale(mid, 0.5, mid); + VectorMA(mid, 8, dir, mid); + // + VectorCopy(mid, testend); + testend[2] -= 1000; + trace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1); + // + if (trace.startsolid) + { + //Log_Write("area %d: trace.startsolid\r\n", areanum); + break; + } //end if + reachareanum = AAS_PointAreaNum(trace.endpos); + if (reachareanum == areanum) + { + //Log_Write("area %d: same area\r\n", areanum); + break; + } //end if + if (AAS_ReachabilityExists(areanum, reachareanum)) + { + //Log_Write("area %d: reachability already exists\r\n", areanum); + break; + } //end if + if (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum)) + { + //Log_Write("area %d, reach area %d: not grounded and not swim\r\n", areanum, reachareanum); + break; + } //end if + // + if (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME + | AREACONTENTS_LAVA)) + { + //Log_Write("area %d, reach area %d: lava or slime\r\n", areanum, reachareanum); + break; + } //end if + //if not going through a cluster portal + numareas = AAS_TraceAreas(mid, testend, areas, NULL, sizeof(areas) / sizeof(int)); + for (p = 0; p < numareas; p++) + if (AAS_AreaClusterPortal(areas[p])) + break; + if (p < numareas) + break; + // if a maximum fall height is set and the bot would fall down further + if (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight) + break; + // + lreach = AAS_AllocReachability(); + if (!lreach) break; + lreach->areanum = reachareanum; + lreach->facenum = 0; + lreach->edgenum = edge1num; + VectorCopy(mid, lreach->start); + VectorCopy(trace.endpos, lreach->end); + lreach->traveltype = TRAVEL_WALKOFFLEDGE; + lreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity; + if (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum)) + { + if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5) + { + lreach->traveltime += aassettings.rs_falldamage5; + } //end if + else if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10) + { + lreach->traveltime += aassettings.rs_falldamage10; + } //end if + } //end if + lreach->next = areareachability[areanum]; + areareachability[areanum] = lreach; + //we've got another walk off ledge reachability + reach_walkoffledge++; + } //end if + } //end for + } //end for + } //end for + } //end for +} //end of the function AAS_Reachability_WalkOffLedge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_StoreReachability(void) +{ + int i; + aas_areasettings_t *areasettings; + aas_lreachability_t *lreach; + aas_reachability_t *reach; + + if (aasworld.reachability) FreeMemory(aasworld.reachability); + aasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t)); + aasworld.reachabilitysize = 1; + for (i = 0; i < aasworld.numareas; i++) + { + areasettings = &aasworld.areasettings[i]; + areasettings->firstreachablearea = aasworld.reachabilitysize; + areasettings->numreachableareas = 0; + for (lreach = areareachability[i]; lreach; lreach = lreach->next) + { + reach = &aasworld.reachability[areasettings->firstreachablearea + + areasettings->numreachableareas]; + reach->areanum = lreach->areanum; + reach->facenum = lreach->facenum; + reach->edgenum = lreach->edgenum; + VectorCopy(lreach->start, reach->start); + VectorCopy(lreach->end, reach->end); + reach->traveltype = lreach->traveltype; + reach->traveltime = lreach->traveltime; + // + areasettings->numreachableareas++; + } //end for + aasworld.reachabilitysize += areasettings->numreachableareas; + } //end for +} //end of the function AAS_StoreReachability +//=========================================================================== +// +// TRAVEL_WALK 100% equal floor height + steps +// TRAVEL_CROUCH 100% +// TRAVEL_BARRIERJUMP 100% +// TRAVEL_JUMP 80% +// TRAVEL_LADDER 100% + fall down from ladder + jump up to ladder +// TRAVEL_WALKOFFLEDGE 90% walk off very steep walls? +// TRAVEL_SWIM 100% +// TRAVEL_WATERJUMP 100% +// TRAVEL_TELEPORT 100% +// TRAVEL_ELEVATOR 100% +// TRAVEL_GRAPPLEHOOK 100% +// TRAVEL_DOUBLEJUMP 0% +// TRAVEL_RAMPJUMP 0% +// TRAVEL_STRAFEJUMP 0% +// TRAVEL_ROCKETJUMP 100% (currently limited towards areas with items) +// TRAVEL_BFGJUMP 0% (currently disabled) +// TRAVEL_JUMPPAD 100% +// TRAVEL_FUNCBOB 100% +// +// Parameter: - +// Returns: true if NOT finished +// Changes Globals: - +//=========================================================================== +int AAS_ContinueInitReachability(float time) +{ + int i, j, todo, start_time; + static float framereachability, reachability_delay; + static int lastpercentage; + + if (!aasworld.loaded) return qfalse; + //if reachability is calculated for all areas + if (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse; + //if starting with area 1 (area 0 is a dummy) + if (aasworld.numreachabilityareas == 1) + { + botimport.Print(PRT_MESSAGE, "calculating reachability...\n"); + lastpercentage = 0; + framereachability = 2000; + reachability_delay = 1000; + } //end if + //number of areas to calculate reachability for this cycle + todo = aasworld.numreachabilityareas + (int) framereachability; + start_time = Sys_MilliSeconds(); + //loop over the areas + for (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++) + { + aasworld.numreachabilityareas++; + //only create jumppad reachabilities from jumppad areas + if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) + { + continue; + } //end if + //loop over the areas + for (j = 1; j < aasworld.numareas; j++) + { + if (i == j) continue; + //never create reachabilities from teleporter or jumppad areas to regular areas + if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) + { + if (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))) + { + continue; + } //end if + } //end if + //if there already is a reachability link from area i to j + if (AAS_ReachabilityExists(i, j)) continue; + //check for a swim reachability + if (AAS_Reachability_Swim(i, j)) continue; + //check for a simple walk on equal floor height reachability + if (AAS_Reachability_EqualFloorHeight(i, j)) continue; + //check for step, barrier, waterjump and walk off ledge reachabilities + if (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue; + //check for ladder reachabilities + if (AAS_Reachability_Ladder(i, j)) continue; + //check for a jump reachability + if (AAS_Reachability_Jump(i, j)) continue; + } //end for + //never create these reachabilities from teleporter or jumppad areas + if (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)) + { + continue; + } //end if + //loop over the areas + for (j = 1; j < aasworld.numareas; j++) + { + if (i == j) continue; + // + if (AAS_ReachabilityExists(i, j)) continue; + //check for a grapple hook reachability + if (calcgrapplereach) AAS_Reachability_Grapple(i, j); + //check for a weapon jump reachability + AAS_Reachability_WeaponJump(i, j); + } //end for + //if the calculation took more time than the max reachability delay + if (Sys_MilliSeconds() - start_time > (int) reachability_delay) break; + // + if (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break; + } //end for + // + if (aasworld.numreachabilityareas == aasworld.numareas) + { + botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) 100.0); + botimport.Print(PRT_MESSAGE, "\nplease wait while storing reachability...\n"); + aasworld.numreachabilityareas++; + } //end if + //if this is the last step in the reachability calculations + else if (aasworld.numreachabilityareas == aasworld.numareas + 1) + { + //create additional walk off ledge reachabilities for every area + for (i = 1; i < aasworld.numareas; i++) + { + //only create jumppad reachabilities from jumppad areas + if (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD) + { + continue; + } //end if + AAS_Reachability_WalkOffLedge(i); + } //end for + //create jump pad reachabilities + AAS_Reachability_JumpPad(); + //create teleporter reachabilities + AAS_Reachability_Teleport(); + //create elevator (func_plat) reachabilities + AAS_Reachability_Elevator(); + //create func_bobbing reachabilities + AAS_Reachability_FuncBobbing(); + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "%6d reach swim\n", reach_swim); + botimport.Print(PRT_MESSAGE, "%6d reach equal floor\n", reach_equalfloor); + botimport.Print(PRT_MESSAGE, "%6d reach step\n", reach_step); + botimport.Print(PRT_MESSAGE, "%6d reach barrier\n", reach_barrier); + botimport.Print(PRT_MESSAGE, "%6d reach waterjump\n", reach_waterjump); + botimport.Print(PRT_MESSAGE, "%6d reach walkoffledge\n", reach_walkoffledge); + botimport.Print(PRT_MESSAGE, "%6d reach jump\n", reach_jump); + botimport.Print(PRT_MESSAGE, "%6d reach ladder\n", reach_ladder); + botimport.Print(PRT_MESSAGE, "%6d reach walk\n", reach_walk); + botimport.Print(PRT_MESSAGE, "%6d reach teleport\n", reach_teleport); + botimport.Print(PRT_MESSAGE, "%6d reach funcbob\n", reach_funcbob); + botimport.Print(PRT_MESSAGE, "%6d reach elevator\n", reach_elevator); + botimport.Print(PRT_MESSAGE, "%6d reach grapple\n", reach_grapple); + botimport.Print(PRT_MESSAGE, "%6d reach rocketjump\n", reach_rocketjump); + botimport.Print(PRT_MESSAGE, "%6d reach jumppad\n", reach_jumppad); +#endif + //*/ + //store all the reachabilities + AAS_StoreReachability(); + //free the reachability link heap + AAS_ShutDownReachabilityHeap(); + // + FreeMemory(areareachability); + // + aasworld.numreachabilityareas++; + // + botimport.Print(PRT_MESSAGE, "calculating clusters...\n"); + } //end if + else + { + lastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas; + botimport.Print(PRT_MESSAGE, "\r%6.1f%%", (float) lastpercentage / 10); + } //end else + //not yet finished + return qtrue; +} //end of the function AAS_ContinueInitReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitReachability(void) +{ + if (!aasworld.loaded) return; + + if (aasworld.reachabilitysize) + { +#ifndef BSPC + if (!((int)LibVarGetValue("forcereachability"))) + { + aasworld.numreachabilityareas = aasworld.numareas + 2; + return; + } //end if +#else + aasworld.numreachabilityareas = aasworld.numareas + 2; + return; +#endif //BSPC + } //end if +#ifndef BSPC + calcgrapplereach = LibVarGetValue("grapplereach"); +#endif + aasworld.savefile = qtrue; + //start with area 1 because area zero is a dummy + aasworld.numreachabilityareas = 1; + ////aasworld.numreachabilityareas = aasworld.numareas + 1; //only calculate entity reachabilities + //setup the heap with reachability links + AAS_SetupReachabilityHeap(); + //allocate area reachability link array + areareachability = (aas_lreachability_t **) GetClearedMemory( + aasworld.numareas * sizeof(aas_lreachability_t *)); + // + AAS_SetWeaponJumpAreaFlags(); +} //end of the function AAS_InitReachable diff --git a/code/botlib/be_aas_reach.h b/code/botlib/be_aas_reach.h index e4b5ef2..fa5dcd4 100755 --- a/code/botlib/be_aas_reach.h +++ b/code/botlib/be_aas_reach.h @@ -1,68 +1,68 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_reach.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_reach.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-//initialize calculating the reachabilities
-void AAS_InitReachability(void);
-//continue calculating the reachabilities
-int AAS_ContinueInitReachability(float time);
-//
-int AAS_BestReachableLinkArea(aas_link_t *areas);
-#endif //AASINTERN
-
-//returns true if the are has reachabilities to other areas
-int AAS_AreaReachability(int areanum);
-//returns the best reachable area and goal origin for a bounding box at the given origin
-int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin);
-//returns the best jumppad area from which the bbox at origin is reachable
-int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs);
-//returns the next reachability using the given model
-int AAS_NextModelReachability(int num, int modelnum);
-//returns the total area of the ground faces of the given area
-float AAS_AreaGroundFaceArea(int areanum);
-//returns true if the area is crouch only
-int AAS_AreaCrouch(int areanum);
-//returns true if a player can swim in this area
-int AAS_AreaSwim(int areanum);
-//returns true if the area is filled with a liquid
-int AAS_AreaLiquid(int areanum);
-//returns true if the area contains lava
-int AAS_AreaLava(int areanum);
-//returns true if the area contains slime
-int AAS_AreaSlime(int areanum);
-//returns true if the area has one or more ground faces
-int AAS_AreaGrounded(int areanum);
-//returns true if the area has one or more ladder faces
-int AAS_AreaLadder(int areanum);
-//returns true if the area is a jump pad
-int AAS_AreaJumpPad(int areanum);
-//returns true if the area is donotenter
-int AAS_AreaDoNotEnter(int areanum);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_reach.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_reach.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize calculating the reachabilities +void AAS_InitReachability(void); +//continue calculating the reachabilities +int AAS_ContinueInitReachability(float time); +// +int AAS_BestReachableLinkArea(aas_link_t *areas); +#endif //AASINTERN + +//returns true if the are has reachabilities to other areas +int AAS_AreaReachability(int areanum); +//returns the best reachable area and goal origin for a bounding box at the given origin +int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin); +//returns the best jumppad area from which the bbox at origin is reachable +int AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs); +//returns the next reachability using the given model +int AAS_NextModelReachability(int num, int modelnum); +//returns the total area of the ground faces of the given area +float AAS_AreaGroundFaceArea(int areanum); +//returns true if the area is crouch only +int AAS_AreaCrouch(int areanum); +//returns true if a player can swim in this area +int AAS_AreaSwim(int areanum); +//returns true if the area is filled with a liquid +int AAS_AreaLiquid(int areanum); +//returns true if the area contains lava +int AAS_AreaLava(int areanum); +//returns true if the area contains slime +int AAS_AreaSlime(int areanum); +//returns true if the area has one or more ground faces +int AAS_AreaGrounded(int areanum); +//returns true if the area has one or more ladder faces +int AAS_AreaLadder(int areanum); +//returns true if the area is a jump pad +int AAS_AreaJumpPad(int areanum); +//returns true if the area is donotenter +int AAS_AreaDoNotEnter(int areanum); diff --git a/code/botlib/be_aas_route.c b/code/botlib/be_aas_route.c index 441984d..642be81 100755 --- a/code/botlib/be_aas_route.c +++ b/code/botlib/be_aas_route.c @@ -1,2209 +1,2209 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_route.c
- *
- * desc: AAS
- *
- * $Archive: /MissionPack/code/botlib/be_aas_route.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_utils.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_crc.h"
-#include "l_libvar.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_aas_def.h"
-
-#define ROUTING_DEBUG
-
-//travel time in hundreths of a second = distance * 100 / speed
-#define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100
-#define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150
-#define DISTANCEFACTOR_WALK 0.33f //walk speed = 300
-
-//cache refresh time
-#define CACHE_REFRESHTIME 15.0f //15 seconds refresh time
-
-//maximum number of routing updates each frame
-#define MAX_FRAMEROUTINGUPDATES 10
-
-
-/*
-
- area routing cache:
- stores the distances within one cluster to a specific goal area
- this goal area is in this same cluster and could be a cluster portal
- for every cluster there's a list with routing cache for every area
- in that cluster (including the portals of that cluster)
- area cache stores aasworld.clusters[?].numreachabilityareas travel times
-
- portal routing cache:
- stores the distances of all portals to a specific goal area
- this goal area could be in any cluster and could also be a cluster portal
- for every area (aasworld.numareas) the portal cache stores
- aasworld.numportals travel times
-
-*/
-
-#ifdef ROUTING_DEBUG
-int numareacacheupdates;
-int numportalcacheupdates;
-#endif //ROUTING_DEBUG
-
-int routingcachesize;
-int max_routingcachesize;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef ROUTING_DEBUG
-void AAS_RoutingInfo(void)
-{
- botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates);
- botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates);
- botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize);
-} //end of the function AAS_RoutingInfo
-#endif //ROUTING_DEBUG
-//===========================================================================
-// returns the number of the area in the cluster
-// assumes the given area is in the given cluster or a portal of the cluster
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-__inline int AAS_ClusterAreaNum(int cluster, int areanum)
-{
- int side, areacluster;
-
- areacluster = aasworld.areasettings[areanum].cluster;
- if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum;
- else
- {
-/*#ifdef ROUTING_DEBUG
- if (aasworld.portals[-areacluster].frontcluster != cluster &&
- aasworld.portals[-areacluster].backcluster != cluster)
- {
- botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n"
- , -areacluster, cluster);
- } //end if
-#endif //ROUTING_DEBUG*/
- side = aasworld.portals[-areacluster].frontcluster != cluster;
- return aasworld.portals[-areacluster].clusterareanum[side];
- } //end else
-} //end of the function AAS_ClusterAreaNum
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitTravelFlagFromType(void)
-{
- int i;
-
- for (i = 0; i < MAX_TRAVELTYPES; i++)
- {
- aasworld.travelflagfortype[i] = TFL_INVALID;
- } //end for
- aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID;
- aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK;
- aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH;
- aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP;
- aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP;
- aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER;
- aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE;
- aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM;
- aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP;
- aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT;
- aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR;
- aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP;
- aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP;
- aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK;
- aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP;
- aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP;
- aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP;
- aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD;
- aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB;
-} //end of the function AAS_InitTravelFlagFromType
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-__inline int AAS_TravelFlagForType_inline(int traveltype)
-{
- int tfl;
-
- tfl = 0;
- if (tfl & TRAVELFLAG_NOTTEAM1)
- tfl |= TFL_NOTTEAM1;
- if (tfl & TRAVELFLAG_NOTTEAM2)
- tfl |= TFL_NOTTEAM2;
- traveltype &= TRAVELTYPE_MASK;
- if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES)
- return TFL_INVALID;
- tfl |= aasworld.travelflagfortype[traveltype];
- return tfl;
-} //end of the function AAS_TravelFlagForType_inline
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_TravelFlagForType(int traveltype)
-{
- return AAS_TravelFlagForType_inline(traveltype);
-} //end of the function AAS_TravelFlagForType_inline
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UnlinkCache(aas_routingcache_t *cache)
-{
- if (cache->time_next) cache->time_next->time_prev = cache->time_prev;
- else aasworld.newestcache = cache->time_prev;
- if (cache->time_prev) cache->time_prev->time_next = cache->time_next;
- else aasworld.oldestcache = cache->time_next;
- cache->time_next = NULL;
- cache->time_prev = NULL;
-} //end of the function AAS_UnlinkCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_LinkCache(aas_routingcache_t *cache)
-{
- if (aasworld.newestcache)
- {
- aasworld.newestcache->time_next = cache;
- cache->time_prev = aasworld.newestcache;
- } //end if
- else
- {
- aasworld.oldestcache = cache;
- cache->time_prev = NULL;
- } //end else
- cache->time_next = NULL;
- aasworld.newestcache = cache;
-} //end of the function AAS_LinkCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeRoutingCache(aas_routingcache_t *cache)
-{
- AAS_UnlinkCache(cache);
- routingcachesize -= cache->size;
- FreeMemory(cache);
-} //end of the function AAS_FreeRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemoveRoutingCacheInCluster( int clusternum )
-{
- int i;
- aas_routingcache_t *cache, *nextcache;
- aas_cluster_t *cluster;
-
- if (!aasworld.clusterareacache)
- return;
- cluster = &aasworld.clusters[clusternum];
- for (i = 0; i < cluster->numareas; i++)
- {
- for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache)
- {
- nextcache = cache->next;
- AAS_FreeRoutingCache(cache);
- } //end for
- aasworld.clusterareacache[clusternum][i] = NULL;
- } //end for
-} //end of the function AAS_RemoveRoutingCacheInCluster
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_RemoveRoutingCacheUsingArea( int areanum )
-{
- int i, clusternum;
- aas_routingcache_t *cache, *nextcache;
-
- clusternum = aasworld.areasettings[areanum].cluster;
- if (clusternum > 0)
- {
- //remove all the cache in the cluster the area is in
- AAS_RemoveRoutingCacheInCluster( clusternum );
- } //end if
- else
- {
- // if this is a portal remove all cache in both the front and back cluster
- AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster );
- AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster );
- } //end else
- // remove all portal cache
- for (i = 0; i < aasworld.numareas; i++)
- {
- //refresh portal cache
- for (cache = aasworld.portalcache[i]; cache; cache = nextcache)
- {
- nextcache = cache->next;
- AAS_FreeRoutingCache(cache);
- } //end for
- aasworld.portalcache[i] = NULL;
- } //end for
-} //end of the function AAS_RemoveRoutingCacheUsingArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_EnableRoutingArea(int areanum, int enable)
-{
- int flags;
-
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- if (bot_developer)
- {
- botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum);
- } //end if
- return 0;
- } //end if
- flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED;
- if (enable < 0)
- return !flags;
-
- if (enable)
- aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED;
- else
- aasworld.areasettings[areanum].areaflags |= AREA_DISABLED;
- // if the status of the area changed
- if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) )
- {
- //remove all routing cache involving this area
- AAS_RemoveRoutingCacheUsingArea( areanum );
- } //end if
- return !flags;
-} //end of the function AAS_EnableRoutingArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-__inline float AAS_RoutingTime(void)
-{
- return AAS_Time();
-} //end of the function AAS_RoutingTime
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_GetAreaContentsTravelFlags(int areanum)
-{
- int contents, tfl;
-
- contents = aasworld.areasettings[areanum].contents;
- tfl = 0;
- if (contents & AREACONTENTS_WATER)
- tfl |= TFL_WATER;
- else if (contents & AREACONTENTS_SLIME)
- tfl |= TFL_SLIME;
- else if (contents & AREACONTENTS_LAVA)
- tfl |= TFL_LAVA;
- else
- tfl |= TFL_AIR;
- if (contents & AREACONTENTS_DONOTENTER)
- tfl |= TFL_DONOTENTER;
- if (contents & AREACONTENTS_NOTTEAM1)
- tfl |= TFL_NOTTEAM1;
- if (contents & AREACONTENTS_NOTTEAM2)
- tfl |= TFL_NOTTEAM2;
- if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE)
- tfl |= TFL_BRIDGE;
- return tfl;
-} //end of the function AAS_GetAreaContentsTravelFlags
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-__inline int AAS_AreaContentsTravelFlags_inline(int areanum)
-{
- return aasworld.areacontentstravelflags[areanum];
-} //end of the function AAS_AreaContentsTravelFlags
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaContentsTravelFlags(int areanum)
-{
- return aasworld.areacontentstravelflags[areanum];
-} //end of the function AAS_AreaContentsTravelFlags
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitAreaContentsTravelFlags(void)
-{
- int i;
-
- if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);
- aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int));
- //
- for (i = 0; i < aasworld.numareas; i++) {
- aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i);
- }
-} //end of the function AAS_InitAreaContentsTravelFlags
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_CreateReversedReachability(void)
-{
- int i, n;
- aas_reversedlink_t *revlink;
- aas_reachability_t *reach;
- aas_areasettings_t *settings;
- char *ptr;
-#ifdef DEBUG
- int starttime;
-
- starttime = Sys_MilliSeconds();
-#endif
- //free reversed links that have already been created
- if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);
- //allocate memory for the reversed reachability links
- ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) +
- aasworld.reachabilitysize * sizeof(aas_reversedlink_t));
- //
- aasworld.reversedreachability = (aas_reversedreachability_t *) ptr;
- //pointer to the memory for the reversed links
- ptr += aasworld.numareas * sizeof(aas_reversedreachability_t);
- //check all reachabilities of all areas
- for (i = 1; i < aasworld.numareas; i++)
- {
- //settings of the area
- settings = &aasworld.areasettings[i];
- //
- if (settings->numreachableareas >= 128)
- botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i);
- //create reversed links for the reachabilities
- for (n = 0; n < settings->numreachableareas && n < 128; n++)
- {
- //reachability link
- reach = &aasworld.reachability[settings->firstreachablearea + n];
- //
- revlink = (aas_reversedlink_t *) ptr;
- ptr += sizeof(aas_reversedlink_t);
- //
- revlink->areanum = i;
- revlink->linknum = settings->firstreachablearea + n;
- revlink->next = aasworld.reversedreachability[reach->areanum].first;
- aasworld.reversedreachability[reach->areanum].first = revlink;
- aasworld.reversedreachability[reach->areanum].numlinks++;
- } //end for
- } //end for
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime);
-#endif
-} //end of the function AAS_CreateReversedReachability
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end)
-{
- int intdist;
- float dist;
- vec3_t dir;
-
- VectorSubtract(start, end, dir);
- dist = VectorLength(dir);
- //if crouch only area
- if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH;
- //if swim area
- else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM;
- //normal walk area
- else dist *= DISTANCEFACTOR_WALK;
- //
- intdist = (int) dist;
- //make sure the distance isn't zero
- if (intdist <= 0) intdist = 1;
- return intdist;
-} //end of the function AAS_AreaTravelTime
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_CalculateAreaTravelTimes(void)
-{
- int i, l, n, size;
- char *ptr;
- vec3_t end;
- aas_reversedreachability_t *revreach;
- aas_reversedlink_t *revlink;
- aas_reachability_t *reach;
- aas_areasettings_t *settings;
- int starttime;
-
- starttime = Sys_MilliSeconds();
- //if there are still area travel times, free the memory
- if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);
- //get the total size of all the area travel times
- size = aasworld.numareas * sizeof(unsigned short **);
- for (i = 0; i < aasworld.numareas; i++)
- {
- revreach = &aasworld.reversedreachability[i];
- //settings of the area
- settings = &aasworld.areasettings[i];
- //
- size += settings->numreachableareas * sizeof(unsigned short *);
- //
- size += settings->numreachableareas * revreach->numlinks * sizeof(unsigned short);
- } //end for
- //allocate memory for the area travel times
- ptr = (char *) GetClearedMemory(size);
- aasworld.areatraveltimes = (unsigned short ***) ptr;
- ptr += aasworld.numareas * sizeof(unsigned short **);
- //calcluate the travel times for all the areas
- for (i = 0; i < aasworld.numareas; i++)
- {
- //reversed reachabilities of this area
- revreach = &aasworld.reversedreachability[i];
- //settings of the area
- settings = &aasworld.areasettings[i];
- //
- aasworld.areatraveltimes[i] = (unsigned short **) ptr;
- ptr += settings->numreachableareas * sizeof(unsigned short *);
- //
- for (l = 0; l < settings->numreachableareas; l++)
- {
- aasworld.areatraveltimes[i][l] = (unsigned short *) ptr;
- ptr += revreach->numlinks * sizeof(unsigned short);
- //reachability link
- reach = &aasworld.reachability[settings->firstreachablearea + l];
- //
- for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)
- {
- VectorCopy(aasworld.reachability[revlink->linknum].end, end);
- //
- aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start);
- } //end for
- } //end for
- } //end for
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime);
-#endif
-} //end of the function AAS_CalculateAreaTravelTimes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PortalMaxTravelTime(int portalnum)
-{
- int l, n, t, maxt;
- aas_portal_t *portal;
- aas_reversedreachability_t *revreach;
- aas_reversedlink_t *revlink;
- aas_areasettings_t *settings;
-
- portal = &aasworld.portals[portalnum];
- //reversed reachabilities of this portal area
- revreach = &aasworld.reversedreachability[portal->areanum];
- //settings of the portal area
- settings = &aasworld.areasettings[portal->areanum];
- //
- maxt = 0;
- for (l = 0; l < settings->numreachableareas; l++)
- {
- for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)
- {
- t = aasworld.areatraveltimes[portal->areanum][l][n];
- if (t > maxt)
- {
- maxt = t;
- } //end if
- } //end for
- } //end for
- return maxt;
-} //end of the function AAS_PortalMaxTravelTime
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitPortalMaxTravelTimes(void)
-{
- int i;
-
- if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);
-
- aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int));
-
- for (i = 0; i < aasworld.numportals; i++)
- {
- aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i);
- //botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]);
- } //end for
-} //end of the function AAS_InitPortalMaxTravelTimes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-/*
-int AAS_FreeOldestCache(void)
-{
- int i, j, bestcluster, bestarea, freed;
- float besttime;
- aas_routingcache_t *cache, *bestcache;
-
- freed = qfalse;
- besttime = 999999999;
- bestcache = NULL;
- bestcluster = 0;
- bestarea = 0;
- //refresh cluster cache
- for (i = 0; i < aasworld.numclusters; i++)
- {
- for (j = 0; j < aasworld.clusters[i].numareas; j++)
- {
- for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
- {
- //never remove cache leading towards a portal
- if (aasworld.areasettings[cache->areanum].cluster < 0) continue;
- //if this cache is older than the cache we found so far
- if (cache->time < besttime)
- {
- bestcache = cache;
- bestcluster = i;
- bestarea = j;
- besttime = cache->time;
- } //end if
- } //end for
- } //end for
- } //end for
- if (bestcache)
- {
- cache = bestcache;
- if (cache->prev) cache->prev->next = cache->next;
- else aasworld.clusterareacache[bestcluster][bestarea] = cache->next;
- if (cache->next) cache->next->prev = cache->prev;
- AAS_FreeRoutingCache(cache);
- freed = qtrue;
- } //end if
- besttime = 999999999;
- bestcache = NULL;
- bestarea = 0;
- for (i = 0; i < aasworld.numareas; i++)
- {
- //refresh portal cache
- for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
- {
- if (cache->time < besttime)
- {
- bestcache = cache;
- bestarea = i;
- besttime = cache->time;
- } //end if
- } //end for
- } //end for
- if (bestcache)
- {
- cache = bestcache;
- if (cache->prev) cache->prev->next = cache->next;
- else aasworld.portalcache[bestarea] = cache->next;
- if (cache->next) cache->next->prev = cache->prev;
- AAS_FreeRoutingCache(cache);
- freed = qtrue;
- } //end if
- return freed;
-} //end of the function AAS_FreeOldestCache
-*/
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_FreeOldestCache(void)
-{
- int clusterareanum;
- aas_routingcache_t *cache;
-
- for (cache = aasworld.oldestcache; cache; cache = cache->time_next) {
- // never free area cache leading towards a portal
- if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) {
- continue;
- }
- break;
- }
- if (cache) {
- // unlink the cache
- if (cache->type == CACHETYPE_AREA) {
- //number of the area in the cluster
- clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);
- // unlink from cluster area cache
- if (cache->prev) cache->prev->next = cache->next;
- else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next;
- if (cache->next) cache->next->prev = cache->prev;
- }
- else {
- // unlink from portal cache
- if (cache->prev) cache->prev->next = cache->next;
- else aasworld.portalcache[cache->areanum] = cache->next;
- if (cache->next) cache->next->prev = cache->prev;
- }
- AAS_FreeRoutingCache(cache);
- return qtrue;
- }
- return qfalse;
-} //end of the function AAS_FreeOldestCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes)
-{
- aas_routingcache_t *cache;
- int size;
-
- //
- size = sizeof(aas_routingcache_t)
- + numtraveltimes * sizeof(unsigned short int)
- + numtraveltimes * sizeof(unsigned char);
- //
- routingcachesize += size;
- //
- cache = (aas_routingcache_t *) GetClearedMemory(size);
- cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t)
- + numtraveltimes * sizeof(unsigned short int);
- cache->size = size;
- return cache;
-} //end of the function AAS_AllocRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeAllClusterAreaCache(void)
-{
- int i, j;
- aas_routingcache_t *cache, *nextcache;
- aas_cluster_t *cluster;
-
- //free all cluster cache if existing
- if (!aasworld.clusterareacache) return;
- //free caches
- for (i = 0; i < aasworld.numclusters; i++)
- {
- cluster = &aasworld.clusters[i];
- for (j = 0; j < cluster->numareas; j++)
- {
- for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache)
- {
- nextcache = cache->next;
- AAS_FreeRoutingCache(cache);
- } //end for
- aasworld.clusterareacache[i][j] = NULL;
- } //end for
- } //end for
- //free the cluster cache array
- FreeMemory(aasworld.clusterareacache);
- aasworld.clusterareacache = NULL;
-} //end of the function AAS_FreeAllClusterAreaCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitClusterAreaCache(void)
-{
- int i, size;
- char *ptr;
-
- //
- for (size = 0, i = 0; i < aasworld.numclusters; i++)
- {
- size += aasworld.clusters[i].numareas;
- } //end for
- //two dimensional array with pointers for every cluster to routing cache
- //for every area in that cluster
- ptr = (char *) GetClearedMemory(
- aasworld.numclusters * sizeof(aas_routingcache_t **) +
- size * sizeof(aas_routingcache_t *));
- aasworld.clusterareacache = (aas_routingcache_t ***) ptr;
- ptr += aasworld.numclusters * sizeof(aas_routingcache_t **);
- for (i = 0; i < aasworld.numclusters; i++)
- {
- aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr;
- ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *);
- } //end for
-} //end of the function AAS_InitClusterAreaCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeAllPortalCache(void)
-{
- int i;
- aas_routingcache_t *cache, *nextcache;
-
- //free all portal cache if existing
- if (!aasworld.portalcache) return;
- //free portal caches
- for (i = 0; i < aasworld.numareas; i++)
- {
- for (cache = aasworld.portalcache[i]; cache; cache = nextcache)
- {
- nextcache = cache->next;
- AAS_FreeRoutingCache(cache);
- } //end for
- aasworld.portalcache[i] = NULL;
- } //end for
- FreeMemory(aasworld.portalcache);
- aasworld.portalcache = NULL;
-} //end of the function AAS_FreeAllPortalCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitPortalCache(void)
-{
- //
- aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory(
- aasworld.numareas * sizeof(aas_routingcache_t *));
-} //end of the function AAS_InitPortalCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitRoutingUpdate(void)
-{
- int i, maxreachabilityareas;
-
- //free routing update fields if already existing
- if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);
- //
- maxreachabilityareas = 0;
- for (i = 0; i < aasworld.numclusters; i++)
- {
- if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas)
- {
- maxreachabilityareas = aasworld.clusters[i].numreachabilityareas;
- } //end if
- } //end for
- //allocate memory for the routing update fields
- aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory(
- maxreachabilityareas * sizeof(aas_routingupdate_t));
- //
- if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);
- //allocate memory for the portal update fields
- aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory(
- (aasworld.numportals+1) * sizeof(aas_routingupdate_t));
-} //end of the function AAS_InitRoutingUpdate
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_CreateAllRoutingCache(void)
-{
- int i, j, t;
-
- aasworld.initialized = qtrue;
- botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n");
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (!AAS_AreaReachability(i)) continue;
- for (j = 1; j < aasworld.numareas; j++)
- {
- if (i == j) continue;
- if (!AAS_AreaReachability(j)) continue;
- t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT);
- //Log_Write("traveltime from %d to %d is %d", i, j, t);
- } //end for
- } //end for
- aasworld.initialized = qfalse;
-} //end of the function AAS_CreateAllRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-
-//the route cache header
-//this header is followed by numportalcache + numareacache aas_routingcache_t
-//structures that store routing cache
-typedef struct routecacheheader_s
-{
- int ident;
- int version;
- int numareas;
- int numclusters;
- int areacrc;
- int clustercrc;
- int numportalcache;
- int numareacache;
-} routecacheheader_t;
-
-#define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M')
-#define RCVERSION 2
-
-//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed);
-//int AAS_CompressVis(byte *vis, int numareas, byte *dest);
-
-void AAS_WriteRouteCache(void)
-{
- int i, j, numportalcache, numareacache, totalsize;
- aas_routingcache_t *cache;
- aas_cluster_t *cluster;
- fileHandle_t fp;
- char filename[MAX_QPATH];
- routecacheheader_t routecacheheader;
-
- numportalcache = 0;
- for (i = 0; i < aasworld.numareas; i++)
- {
- for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
- {
- numportalcache++;
- } //end for
- } //end for
- numareacache = 0;
- for (i = 0; i < aasworld.numclusters; i++)
- {
- cluster = &aasworld.clusters[i];
- for (j = 0; j < cluster->numareas; j++)
- {
- for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
- {
- numareacache++;
- } //end for
- } //end for
- } //end for
- // open the file for writing
- Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname);
- botimport.FS_FOpenFile( filename, &fp, FS_WRITE );
- if (!fp)
- {
- AAS_Error("Unable to open file: %s\n", filename);
- return;
- } //end if
- //create the header
- routecacheheader.ident = RCID;
- routecacheheader.version = RCVERSION;
- routecacheheader.numareas = aasworld.numareas;
- routecacheheader.numclusters = aasworld.numclusters;
- routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas );
- routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters );
- routecacheheader.numportalcache = numportalcache;
- routecacheheader.numareacache = numareacache;
- //write the header
- botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp);
- //
- totalsize = 0;
- //write all the cache
- for (i = 0; i < aasworld.numareas; i++)
- {
- for (cache = aasworld.portalcache[i]; cache; cache = cache->next)
- {
- botimport.FS_Write(cache, cache->size, fp);
- totalsize += cache->size;
- } //end for
- } //end for
- for (i = 0; i < aasworld.numclusters; i++)
- {
- cluster = &aasworld.clusters[i];
- for (j = 0; j < cluster->numareas; j++)
- {
- for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)
- {
- botimport.FS_Write(cache, cache->size, fp);
- totalsize += cache->size;
- } //end for
- } //end for
- } //end for
- // write the visareas
- /*
- for (i = 0; i < aasworld.numareas; i++)
- {
- if (!aasworld.areavisibility[i]) {
- size = 0;
- botimport.FS_Write(&size, sizeof(int), fp);
- continue;
- }
- AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis );
- size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis );
- botimport.FS_Write(&size, sizeof(int), fp);
- botimport.FS_Write(aasworld.decompressedvis, size, fp);
- }
- */
- //
- botimport.FS_FCloseFile(fp);
- botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename);
- botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize);
-} //end of the function AAS_WriteRouteCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_routingcache_t *AAS_ReadCache(fileHandle_t fp)
-{
- int size;
- aas_routingcache_t *cache;
-
- botimport.FS_Read(&size, sizeof(size), fp);
- cache = (aas_routingcache_t *) GetMemory(size);
- cache->size = size;
- botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp);
- cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) +
- (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2;
- return cache;
-} //end of the function AAS_ReadCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_ReadRouteCache(void)
-{
- int i, clusterareanum;//, size;
- fileHandle_t fp;
- char filename[MAX_QPATH];
- routecacheheader_t routecacheheader;
- aas_routingcache_t *cache;
-
- Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname);
- botimport.FS_FOpenFile( filename, &fp, FS_READ );
- if (!fp)
- {
- return qfalse;
- } //end if
- botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp );
- if (routecacheheader.ident != RCID)
- {
- AAS_Error("%s is not a route cache dump\n");
- return qfalse;
- } //end if
- if (routecacheheader.version != RCVERSION)
- {
- AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION);
- return qfalse;
- } //end if
- if (routecacheheader.numareas != aasworld.numareas)
- {
- //AAS_Error("route cache dump has wrong number of areas\n");
- return qfalse;
- } //end if
- if (routecacheheader.numclusters != aasworld.numclusters)
- {
- //AAS_Error("route cache dump has wrong number of clusters\n");
- return qfalse;
- } //end if
- if (routecacheheader.areacrc !=
- CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ))
- {
- //AAS_Error("route cache dump area CRC incorrect\n");
- return qfalse;
- } //end if
- if (routecacheheader.clustercrc !=
- CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ))
- {
- //AAS_Error("route cache dump cluster CRC incorrect\n");
- return qfalse;
- } //end if
- //read all the portal cache
- for (i = 0; i < routecacheheader.numportalcache; i++)
- {
- cache = AAS_ReadCache(fp);
- cache->next = aasworld.portalcache[cache->areanum];
- cache->prev = NULL;
- if (aasworld.portalcache[cache->areanum])
- aasworld.portalcache[cache->areanum]->prev = cache;
- aasworld.portalcache[cache->areanum] = cache;
- } //end for
- //read all the cluster area cache
- for (i = 0; i < routecacheheader.numareacache; i++)
- {
- cache = AAS_ReadCache(fp);
- clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);
- cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum];
- cache->prev = NULL;
- if (aasworld.clusterareacache[cache->cluster][clusterareanum])
- aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache;
- aasworld.clusterareacache[cache->cluster][clusterareanum] = cache;
- } //end for
- // read the visareas
- /*
- aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *));
- aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte));
- for (i = 0; i < aasworld.numareas; i++)
- {
- botimport.FS_Read(&size, sizeof(size), fp );
- if (size) {
- aasworld.areavisibility[i] = (byte *) GetMemory(size);
- botimport.FS_Read(aasworld.areavisibility[i], size, fp );
- }
- }
- */
- //
- botimport.FS_FCloseFile(fp);
- return qtrue;
-} //end of the function AAS_ReadRouteCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#define MAX_REACHABILITYPASSAREAS 32
-
-void AAS_InitReachabilityAreas(void)
-{
- int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS];
- int numreachareas;
- aas_reachability_t *reach;
- vec3_t start, end;
-
- if (aasworld.reachabilityareas)
- FreeMemory(aasworld.reachabilityareas);
- if (aasworld.reachabilityareaindex)
- FreeMemory(aasworld.reachabilityareaindex);
-
- aasworld.reachabilityareas = (aas_reachabilityareas_t *)
- GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t));
- aasworld.reachabilityareaindex = (int *)
- GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int));
- numreachareas = 0;
- for (i = 0; i < aasworld.reachabilitysize; i++)
- {
- reach = &aasworld.reachability[i];
- numareas = 0;
- switch(reach->traveltype & TRAVELTYPE_MASK)
- {
- //trace areas from start to end
- case TRAVEL_BARRIERJUMP:
- case TRAVEL_WATERJUMP:
- VectorCopy(reach->start, end);
- end[2] = reach->end[2];
- numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS);
- break;
- case TRAVEL_WALKOFFLEDGE:
- VectorCopy(reach->end, start);
- start[2] = reach->start[2];
- numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);
- break;
- case TRAVEL_GRAPPLEHOOK:
- numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);
- break;
-
- //trace arch
- case TRAVEL_JUMP: break;
- case TRAVEL_ROCKETJUMP: break;
- case TRAVEL_BFGJUMP: break;
- case TRAVEL_JUMPPAD: break;
-
- //trace from reach->start to entity center, along entity movement
- //and from entity center to reach->end
- case TRAVEL_ELEVATOR: break;
- case TRAVEL_FUNCBOB: break;
-
- //no areas in between
- case TRAVEL_WALK: break;
- case TRAVEL_CROUCH: break;
- case TRAVEL_LADDER: break;
- case TRAVEL_SWIM: break;
- case TRAVEL_TELEPORT: break;
- default: break;
- } //end switch
- aasworld.reachabilityareas[i].firstarea = numreachareas;
- aasworld.reachabilityareas[i].numareas = numareas;
- for (j = 0; j < numareas; j++)
- {
- aasworld.reachabilityareaindex[numreachareas++] = areas[j];
- } //end for
- } //end for
-} //end of the function AAS_InitReachabilityAreas
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitRouting(void)
-{
- AAS_InitTravelFlagFromType();
- //
- AAS_InitAreaContentsTravelFlags();
- //initialize the routing update fields
- AAS_InitRoutingUpdate();
- //create reversed reachability links used by the routing update algorithm
- AAS_CreateReversedReachability();
- //initialize the cluster cache
- AAS_InitClusterAreaCache();
- //initialize portal cache
- AAS_InitPortalCache();
- //initialize the area travel times
- AAS_CalculateAreaTravelTimes();
- //calculate the maximum travel times through portals
- AAS_InitPortalMaxTravelTimes();
- //get the areas reachabilities go through
- AAS_InitReachabilityAreas();
- //
-#ifdef ROUTING_DEBUG
- numareacacheupdates = 0;
- numportalcacheupdates = 0;
-#endif //ROUTING_DEBUG
- //
- routingcachesize = 0;
- max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096");
- // read any routing cache if available
- AAS_ReadRouteCache();
-} //end of the function AAS_InitRouting
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeRoutingCaches(void)
-{
- // free all the existing cluster area cache
- AAS_FreeAllClusterAreaCache();
- // free all the existing portal cache
- AAS_FreeAllPortalCache();
- // free cached travel times within areas
- if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);
- aasworld.areatraveltimes = NULL;
- // free cached maximum travel time through cluster portals
- if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);
- aasworld.portalmaxtraveltimes = NULL;
- // free reversed reachability links
- if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);
- aasworld.reversedreachability = NULL;
- // free routing algorithm memory
- if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);
- aasworld.areaupdate = NULL;
- if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);
- aasworld.portalupdate = NULL;
- // free lists with areas the reachabilities go through
- if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas);
- aasworld.reachabilityareas = NULL;
- // free the reachability area index
- if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex);
- aasworld.reachabilityareaindex = NULL;
- // free area contents travel flags look up table
- if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);
- aasworld.areacontentstravelflags = NULL;
-} //end of the function AAS_FreeRoutingCaches
-//===========================================================================
-// update the given routing cache
-//
-// Parameter: areacache : routing cache to update
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache)
-{
- int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum;
- int numreachabilityareas;
- unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed
- aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
- aas_reachability_t *reach;
- aas_reversedreachability_t *revreach;
- aas_reversedlink_t *revlink;
-
-#ifdef ROUTING_DEBUG
- numareacacheupdates++;
-#endif //ROUTING_DEBUG
- //number of reachability areas within this cluster
- numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas;
- //
- aasworld.frameroutingupdates++;
- //clear the routing update fields
-// Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t));
- //
- badtravelflags = ~areacache->travelflags;
- //
- clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum);
- if (clusterareanum >= numreachabilityareas) return;
- //
- Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes));
- //
- curupdate = &aasworld.areaupdate[clusterareanum];
- curupdate->areanum = areacache->areanum;
- //VectorCopy(areacache->origin, curupdate->start);
- curupdate->areatraveltimes = startareatraveltimes;
- curupdate->tmptraveltime = areacache->starttraveltime;
- //
- areacache->traveltimes[clusterareanum] = areacache->starttraveltime;
- //put the area to start with in the current read list
- curupdate->next = NULL;
- curupdate->prev = NULL;
- updateliststart = curupdate;
- updatelistend = curupdate;
- //while there are updates in the current list
- while (updateliststart)
- {
- curupdate = updateliststart;
- //
- if (curupdate->next) curupdate->next->prev = NULL;
- else updatelistend = NULL;
- updateliststart = curupdate->next;
- //
- curupdate->inlist = qfalse;
- //check all reversed reachability links
- revreach = &aasworld.reversedreachability[curupdate->areanum];
- //
- for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++)
- {
- linknum = revlink->linknum;
- reach = &aasworld.reachability[linknum];
- //if there is used an undesired travel type
- if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;
- //if not allowed to enter the next area
- if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue;
- //if the next area has a not allowed travel flag
- if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;
- //number of the area the reversed reachability leads to
- nextareanum = revlink->areanum;
- //get the cluster number of the area
- cluster = aasworld.areasettings[nextareanum].cluster;
- //don't leave the cluster
- if (cluster > 0 && cluster != areacache->cluster) continue;
- //get the number of the area in the cluster
- clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum);
- if (clusterareanum >= numreachabilityareas) continue;
- //time already travelled plus the traveltime through
- //the current area plus the travel time from the reachability
- t = curupdate->tmptraveltime +
- //AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) +
- curupdate->areatraveltimes[i] +
- reach->traveltime;
- //
- if (!areacache->traveltimes[clusterareanum] ||
- areacache->traveltimes[clusterareanum] > t)
- {
- areacache->traveltimes[clusterareanum] = t;
- areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea;
- nextupdate = &aasworld.areaupdate[clusterareanum];
- nextupdate->areanum = nextareanum;
- nextupdate->tmptraveltime = t;
- //VectorCopy(reach->start, nextupdate->start);
- nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum -
- aasworld.areasettings[nextareanum].firstreachablearea];
- if (!nextupdate->inlist)
- {
- // we add the update to the end of the list
- // we could also use a B+ tree to have a real sorted list
- // on travel time which makes for faster routing updates
- nextupdate->next = NULL;
- nextupdate->prev = updatelistend;
- if (updatelistend) updatelistend->next = nextupdate;
- else updateliststart = nextupdate;
- updatelistend = nextupdate;
- nextupdate->inlist = qtrue;
- } //end if
- } //end if
- } //end for
- } //end while
-} //end of the function AAS_UpdateAreaRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags)
-{
- int clusterareanum;
- aas_routingcache_t *cache, *clustercache;
-
- //number of the area in the cluster
- clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
- //pointer to the cache for the area in the cluster
- clustercache = aasworld.clusterareacache[clusternum][clusterareanum];
- //find the cache without undesired travel flags
- for (cache = clustercache; cache; cache = cache->next)
- {
- //if there aren't used any undesired travel types for the cache
- if (cache->travelflags == travelflags) break;
- } //end for
- //if there was no cache
- if (!cache)
- {
- cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas);
- cache->cluster = clusternum;
- cache->areanum = areanum;
- VectorCopy(aasworld.areas[areanum].center, cache->origin);
- cache->starttraveltime = 1;
- cache->travelflags = travelflags;
- cache->prev = NULL;
- cache->next = clustercache;
- if (clustercache) clustercache->prev = cache;
- aasworld.clusterareacache[clusternum][clusterareanum] = cache;
- AAS_UpdateAreaRoutingCache(cache);
- } //end if
- else
- {
- AAS_UnlinkCache(cache);
- } //end else
- //the cache has been accessed
- cache->time = AAS_RoutingTime();
- cache->type = CACHETYPE_AREA;
- AAS_LinkCache(cache);
- return cache;
-} //end of the function AAS_GetAreaRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache)
-{
- int i, portalnum, clusterareanum, clusternum;
- unsigned short int t;
- aas_portal_t *portal;
- aas_cluster_t *cluster;
- aas_routingcache_t *cache;
- aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
-
-#ifdef ROUTING_DEBUG
- numportalcacheupdates++;
-#endif //ROUTING_DEBUG
- //clear the routing update fields
-// Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t));
- //
- curupdate = &aasworld.portalupdate[aasworld.numportals];
- curupdate->cluster = portalcache->cluster;
- curupdate->areanum = portalcache->areanum;
- curupdate->tmptraveltime = portalcache->starttraveltime;
- //if the start area is a cluster portal, store the travel time for that portal
- clusternum = aasworld.areasettings[portalcache->areanum].cluster;
- if (clusternum < 0)
- {
- portalcache->traveltimes[-clusternum] = portalcache->starttraveltime;
- } //end if
- //put the area to start with in the current read list
- curupdate->next = NULL;
- curupdate->prev = NULL;
- updateliststart = curupdate;
- updatelistend = curupdate;
- //while there are updates in the current list
- while (updateliststart)
- {
- curupdate = updateliststart;
- //remove the current update from the list
- if (curupdate->next) curupdate->next->prev = NULL;
- else updatelistend = NULL;
- updateliststart = curupdate->next;
- //current update is removed from the list
- curupdate->inlist = qfalse;
- //
- cluster = &aasworld.clusters[curupdate->cluster];
- //
- cache = AAS_GetAreaRoutingCache(curupdate->cluster,
- curupdate->areanum, portalcache->travelflags);
- //take all portals of the cluster
- for (i = 0; i < cluster->numportals; i++)
- {
- portalnum = aasworld.portalindex[cluster->firstportal + i];
- portal = &aasworld.portals[portalnum];
- //if this is the portal of the current update continue
- if (portal->areanum == curupdate->areanum) continue;
- //
- clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum);
- if (clusterareanum >= cluster->numreachabilityareas) continue;
- //
- t = cache->traveltimes[clusterareanum];
- if (!t) continue;
- t += curupdate->tmptraveltime;
- //
- if (!portalcache->traveltimes[portalnum] ||
- portalcache->traveltimes[portalnum] > t)
- {
- portalcache->traveltimes[portalnum] = t;
- nextupdate = &aasworld.portalupdate[portalnum];
- if (portal->frontcluster == curupdate->cluster)
- {
- nextupdate->cluster = portal->backcluster;
- } //end if
- else
- {
- nextupdate->cluster = portal->frontcluster;
- } //end else
- nextupdate->areanum = portal->areanum;
- //add travel time through the actual portal area for the next update
- nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum];
- if (!nextupdate->inlist)
- {
- // we add the update to the end of the list
- // we could also use a B+ tree to have a real sorted list
- // on travel time which makes for faster routing updates
- nextupdate->next = NULL;
- nextupdate->prev = updatelistend;
- if (updatelistend) updatelistend->next = nextupdate;
- else updateliststart = nextupdate;
- updatelistend = nextupdate;
- nextupdate->inlist = qtrue;
- } //end if
- } //end if
- } //end for
- } //end while
-} //end of the function AAS_UpdatePortalRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags)
-{
- aas_routingcache_t *cache;
-
- //find the cached portal routing if existing
- for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next)
- {
- if (cache->travelflags == travelflags) break;
- } //end for
- //if the portal routing isn't cached
- if (!cache)
- {
- cache = AAS_AllocRoutingCache(aasworld.numportals);
- cache->cluster = clusternum;
- cache->areanum = areanum;
- VectorCopy(aasworld.areas[areanum].center, cache->origin);
- cache->starttraveltime = 1;
- cache->travelflags = travelflags;
- //add the cache to the cache list
- cache->prev = NULL;
- cache->next = aasworld.portalcache[areanum];
- if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache;
- aasworld.portalcache[areanum] = cache;
- //update the cache
- AAS_UpdatePortalRoutingCache(cache);
- } //end if
- else
- {
- AAS_UnlinkCache(cache);
- } //end else
- //the cache has been accessed
- cache->time = AAS_RoutingTime();
- cache->type = CACHETYPE_PORTAL;
- AAS_LinkCache(cache);
- return cache;
-} //end of the function AAS_GetPortalRoutingCache
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum)
-{
- int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum;
- unsigned short int t, besttime;
- aas_portal_t *portal;
- aas_cluster_t *cluster;
- aas_routingcache_t *areacache, *portalcache;
- aas_reachability_t *reach;
-
- if (!aasworld.initialized) return qfalse;
-
- if (areanum == goalareanum)
- {
- *traveltime = 1;
- *reachnum = 0;
- return qtrue;
- }
- //
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- if (bot_developer)
- {
- botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum);
- } //end if
- return qfalse;
- } //end if
- if (goalareanum <= 0 || goalareanum >= aasworld.numareas)
- {
- if (bot_developer)
- {
- botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum);
- } //end if
- return qfalse;
- } //end if
- // make sure the routing cache doesn't grow to large
- while(AvailableMemory() < 1 * 1024 * 1024) {
- if (!AAS_FreeOldestCache()) break;
- }
- //
- if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum))
- {
- travelflags |= TFL_DONOTENTER;
- } //end if
- //NOTE: the number of routing updates is limited per frame
- /*
- if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES)
- {
-#ifdef DEBUG
- //Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed");
-#endif
- return 0;
- } //end if
- */
- //
- clusternum = aasworld.areasettings[areanum].cluster;
- goalclusternum = aasworld.areasettings[goalareanum].cluster;
- //check if the area is a portal of the goal area cluster
- if (clusternum < 0 && goalclusternum > 0)
- {
- portal = &aasworld.portals[-clusternum];
- if (portal->frontcluster == goalclusternum ||
- portal->backcluster == goalclusternum)
- {
- clusternum = goalclusternum;
- } //end if
- } //end if
- //check if the goalarea is a portal of the area cluster
- else if (clusternum > 0 && goalclusternum < 0)
- {
- portal = &aasworld.portals[-goalclusternum];
- if (portal->frontcluster == clusternum ||
- portal->backcluster == clusternum)
- {
- goalclusternum = clusternum;
- } //end if
- } //end if
- //if both areas are in the same cluster
- //NOTE: there might be a shorter route via another cluster!!! but we don't care
- if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum)
- {
- //
- areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags);
- //the number of the area in the cluster
- clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
- //the cluster the area is in
- cluster = &aasworld.clusters[clusternum];
- //if the area is NOT a reachability area
- if (clusterareanum >= cluster->numreachabilityareas) return 0;
- //if it is possible to travel to the goal area through this cluster
- if (areacache->traveltimes[clusterareanum] != 0)
- {
- *reachnum = aasworld.areasettings[areanum].firstreachablearea +
- areacache->reachabilities[clusterareanum];
- if (!origin) {
- *traveltime = areacache->traveltimes[clusterareanum];
- return qtrue;
- }
- reach = &aasworld.reachability[*reachnum];
- *traveltime = areacache->traveltimes[clusterareanum] +
- AAS_AreaTravelTime(areanum, origin, reach->start);
- //
- return qtrue;
- } //end if
- } //end if
- //
- clusternum = aasworld.areasettings[areanum].cluster;
- goalclusternum = aasworld.areasettings[goalareanum].cluster;
- //if the goal area is a portal
- if (goalclusternum < 0)
- {
- //just assume the goal area is part of the front cluster
- portal = &aasworld.portals[-goalclusternum];
- goalclusternum = portal->frontcluster;
- } //end if
- //get the portal routing cache
- portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags);
- //if the area is a cluster portal, read directly from the portal cache
- if (clusternum < 0)
- {
- *traveltime = portalcache->traveltimes[-clusternum];
- *reachnum = aasworld.areasettings[areanum].firstreachablearea +
- portalcache->reachabilities[-clusternum];
- return qtrue;
- } //end if
- //
- besttime = 0;
- bestreachnum = -1;
- //the cluster the area is in
- cluster = &aasworld.clusters[clusternum];
- //find the portal of the area cluster leading towards the goal area
- for (i = 0; i < cluster->numportals; i++)
- {
- portalnum = aasworld.portalindex[cluster->firstportal + i];
- //if the goal area isn't reachable from the portal
- if (!portalcache->traveltimes[portalnum]) continue;
- //
- portal = &aasworld.portals[portalnum];
- //get the cache of the portal area
- areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags);
- //current area inside the current cluster
- clusterareanum = AAS_ClusterAreaNum(clusternum, areanum);
- //if the area is NOT a reachability area
- if (clusterareanum >= cluster->numreachabilityareas) continue;
- //if the portal is NOT reachable from this area
- if (!areacache->traveltimes[clusterareanum]) continue;
- //total travel time is the travel time the portal area is from
- //the goal area plus the travel time towards the portal area
- t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum];
- //FIXME: add the exact travel time through the actual portal area
- //NOTE: for now we just add the largest travel time through the portal area
- // because we can't directly calculate the exact travel time
- // to be more specific we don't know which reachability was used to travel
- // into the portal area
- t += aasworld.portalmaxtraveltimes[portalnum];
- //
- if (origin)
- {
- *reachnum = aasworld.areasettings[areanum].firstreachablearea +
- areacache->reachabilities[clusterareanum];
- reach = aasworld.reachability + *reachnum;
- t += AAS_AreaTravelTime(areanum, origin, reach->start);
- } //end if
- //if the time is better than the one already found
- if (!besttime || t < besttime)
- {
- bestreachnum = *reachnum;
- besttime = t;
- } //end if
- } //end for
- if (bestreachnum < 0) {
- return qfalse;
- }
- *reachnum = bestreachnum;
- *traveltime = besttime;
- return qtrue;
-} //end of the function AAS_AreaRouteToGoalArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)
-{
- int traveltime, reachnum;
-
- if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))
- {
- return traveltime;
- }
- return 0;
-} //end of the function AAS_AreaTravelTimeToGoalArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)
-{
- int traveltime, reachnum;
-
- if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))
- {
- return reachnum;
- }
- return 0;
-} //end of the function AAS_AreaReachabilityToGoalArea
-//===========================================================================
-// predict the route and stop on one of the stop events
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin,
- int goalareanum, int travelflags, int maxareas, int maxtime,
- int stopevent, int stopcontents, int stoptfl, int stopareanum)
-{
- int curareanum, reachnum, i, j, testareanum;
- vec3_t curorigin;
- aas_reachability_t *reach;
- aas_reachabilityareas_t *reachareas;
-
- //init output
- route->stopevent = RSE_NONE;
- route->endarea = goalareanum;
- route->endcontents = 0;
- route->endtravelflags = 0;
- VectorCopy(origin, route->endpos);
- route->time = 0;
-
- curareanum = areanum;
- VectorCopy(origin, curorigin);
-
- for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++)
- {
- reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags);
- if (!reachnum)
- {
- route->stopevent = RSE_NOROUTE;
- return qfalse;
- } //end if
- reach = &aasworld.reachability[reachnum];
- //
- if (stopevent & RSE_USETRAVELTYPE)
- {
- if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl)
- {
- route->stopevent = RSE_USETRAVELTYPE;
- route->endarea = curareanum;
- route->endcontents = aasworld.areasettings[curareanum].contents;
- route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);
- VectorCopy(reach->start, route->endpos);
- return qtrue;
- } //end if
- if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl)
- {
- route->stopevent = RSE_USETRAVELTYPE;
- route->endarea = reach->areanum;
- route->endcontents = aasworld.areasettings[reach->areanum].contents;
- route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum);
- VectorCopy(reach->end, route->endpos);
- route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
- route->time += reach->traveltime;
- return qtrue;
- } //end if
- } //end if
- reachareas = &aasworld.reachabilityareas[reachnum];
- for (j = 0; j < reachareas->numareas + 1; j++)
- {
- if (j >= reachareas->numareas)
- testareanum = reach->areanum;
- else
- testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j];
- if (stopevent & RSE_ENTERCONTENTS)
- {
- if (aasworld.areasettings[testareanum].contents & stopcontents)
- {
- route->stopevent = RSE_ENTERCONTENTS;
- route->endarea = testareanum;
- route->endcontents = aasworld.areasettings[testareanum].contents;
- VectorCopy(reach->end, route->endpos);
- route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
- route->time += reach->traveltime;
- return qtrue;
- } //end if
- } //end if
- if (stopevent & RSE_ENTERAREA)
- {
- if (testareanum == stopareanum)
- {
- route->stopevent = RSE_ENTERAREA;
- route->endarea = testareanum;
- route->endcontents = aasworld.areasettings[testareanum].contents;
- VectorCopy(reach->start, route->endpos);
- return qtrue;
- } //end if
- } //end if
- } //end for
-
- route->time += AAS_AreaTravelTime(areanum, origin, reach->start);
- route->time += reach->traveltime;
- route->endarea = reach->areanum;
- route->endcontents = aasworld.areasettings[reach->areanum].contents;
- route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);
- VectorCopy(reach->end, route->endpos);
- //
- curareanum = reach->areanum;
- VectorCopy(reach->end, curorigin);
- //
- if (maxtime && route->time > maxtime)
- break;
- } //end while
- if (curareanum != goalareanum)
- return qfalse;
- return qtrue;
-} //end of the function AAS_PredictRoute
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BridgeWalkable(int areanum)
-{
- return qfalse;
-} //end of the function AAS_BridgeWalkable
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach)
-{
- if (!aasworld.initialized)
- {
- Com_Memset(reach, 0, sizeof(aas_reachability_t));
- return;
- } //end if
- if (num < 0 || num >= aasworld.reachabilitysize)
- {
- Com_Memset(reach, 0, sizeof(aas_reachability_t));
- return;
- } //end if
- Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));;
-} //end of the function AAS_ReachabilityFromNum
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NextAreaReachability(int areanum, int reachnum)
-{
- aas_areasettings_t *settings;
-
- if (!aasworld.initialized) return 0;
-
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum);
- return 0;
- } //end if
-
- settings = &aasworld.areasettings[areanum];
- if (!reachnum)
- {
- return settings->firstreachablearea;
- } //end if
- if (reachnum < settings->firstreachablearea)
- {
- botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara");
- return 0;
- } //end if
- reachnum++;
- if (reachnum >= settings->firstreachablearea + settings->numreachableareas)
- {
- return 0;
- } //end if
- return reachnum;
-} //end of the function AAS_NextAreaReachability
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NextModelReachability(int num, int modelnum)
-{
- int i;
-
- if (num <= 0) num = 1;
- else if (num >= aasworld.reachabilitysize) return 0;
- else num++;
- //
- for (i = num; i < aasworld.reachabilitysize; i++)
- {
- if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR)
- {
- if (aasworld.reachability[i].facenum == modelnum) return i;
- } //end if
- else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
- {
- if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i;
- } //end if
- } //end for
- return 0;
-} //end of the function AAS_NextModelReachability
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin)
-{
- int i, n, t;
- vec3_t start, end;
- aas_trace_t trace;
-
- //if the area has no reachabilities
- if (!AAS_AreaReachability(areanum)) return qfalse;
- //
- n = aasworld.numareas * random();
- for (i = 0; i < aasworld.numareas; i++)
- {
- if (n <= 0) n = 1;
- if (n >= aasworld.numareas) n = 1;
- if (AAS_AreaReachability(n))
- {
- t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags);
- //if the goal is reachable
- if (t > 0)
- {
- if (AAS_AreaSwim(n))
- {
- *goalareanum = n;
- VectorCopy(aasworld.areas[n].center, goalorigin);
- //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum);
- return qtrue;
- } //end if
- VectorCopy(aasworld.areas[n].center, start);
- if (!AAS_PointAreaNum(start))
- Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]);
- VectorCopy(start, end);
- end[2] -= 300;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);
- if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n)
- {
- if (AAS_AreaGroundFaceArea(n) > 300)
- {
- *goalareanum = n;
- VectorCopy(trace.endpos, goalorigin);
- //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum);
- return qtrue;
- } //end if
- } //end if
- } //end if
- } //end if
- n++;
- } //end for
- return qfalse;
-} //end of the function AAS_RandomGoalArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaVisible(int srcarea, int destarea)
-{
- return qfalse;
-} //end of the function AAS_AreaVisible
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point)
-{
- vec3_t vec, p2;
-
- AAS_ProjectPointOntoVector(point, v1, v2, p2);
- VectorSubtract(point, p2, vec);
- return VectorLength(vec);
-} //end of the function DistancePointToLine
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags)
-{
- int i, j, nextareanum, badtravelflags, numreach, bestarea;
- unsigned short int t, besttraveltime;
- static unsigned short int *hidetraveltimes;
- aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;
- aas_reachability_t *reach;
- float dist1, dist2;
- vec3_t v1, v2, p;
- qboolean startVisible;
-
- //
- if (!hidetraveltimes)
- {
- hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int));
- } //end if
- else
- {
- Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int));
- } //end else
- besttraveltime = 0;
- bestarea = 0;
- //assume visible
- startVisible = qtrue;
- //
- badtravelflags = ~travelflags;
- //
- curupdate = &aasworld.areaupdate[areanum];
- curupdate->areanum = areanum;
- VectorCopy(origin, curupdate->start);
- curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0];
- curupdate->tmptraveltime = 0;
- //put the area to start with in the current read list
- curupdate->next = NULL;
- curupdate->prev = NULL;
- updateliststart = curupdate;
- updatelistend = curupdate;
- //while there are updates in the list
- while (updateliststart)
- {
- curupdate = updateliststart;
- //
- if (curupdate->next) curupdate->next->prev = NULL;
- else updatelistend = NULL;
- updateliststart = curupdate->next;
- //
- curupdate->inlist = qfalse;
- //check all reversed reachability links
- numreach = aasworld.areasettings[curupdate->areanum].numreachableareas;
- reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea];
- //
- for (i = 0; i < numreach; i++, reach++)
- {
- //if an undesired travel type is used
- if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;
- //
- if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;
- //number of the area the reachability leads to
- nextareanum = reach->areanum;
- // if this moves us into the enemies area, skip it
- if (nextareanum == enemyareanum) continue;
- //time already travelled plus the traveltime through
- //the current area plus the travel time from the reachability
- t = curupdate->tmptraveltime +
- AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) +
- reach->traveltime;
-
- //avoid going near the enemy
- AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p);
- for (j = 0; j < 3; j++)
- if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) ||
- (p[j] < curupdate->start[j] && p[j] < reach->end[j]))
- break;
- if (j < 3)
- {
- VectorSubtract(enemyorigin, reach->end, v2);
- } //end if
- else
- {
- VectorSubtract(enemyorigin, p, v2);
- } //end else
- dist2 = VectorLength(v2);
- //never go through the enemy
- if (dist2 < 40) continue;
- //
- VectorSubtract(enemyorigin, curupdate->start, v1);
- dist1 = VectorLength(v1);
- //
- if (dist2 < dist1)
- {
- t += (dist1 - dist2) * 10;
- }
- // if we weren't visible when starting, make sure we don't move into their view
- if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) {
- continue;
- }
- //
- if (besttraveltime && t >= besttraveltime) continue;
- //
- if (!hidetraveltimes[nextareanum] ||
- hidetraveltimes[nextareanum] > t)
- {
- //if the nextarea is not visible from the enemy area
- if (!AAS_AreaVisible(enemyareanum, nextareanum))
- {
- besttraveltime = t;
- bestarea = nextareanum;
- } //end if
- hidetraveltimes[nextareanum] = t;
- nextupdate = &aasworld.areaupdate[nextareanum];
- nextupdate->areanum = nextareanum;
- nextupdate->tmptraveltime = t;
- //remember where we entered this area
- VectorCopy(reach->end, nextupdate->start);
- //if this update is not in the list yet
- if (!nextupdate->inlist)
- {
- //add the new update to the end of the list
- nextupdate->next = NULL;
- nextupdate->prev = updatelistend;
- if (updatelistend) updatelistend->next = nextupdate;
- else updateliststart = nextupdate;
- updatelistend = nextupdate;
- nextupdate->inlist = qtrue;
- } //end if
- } //end if
- } //end for
- } //end while
- return bestarea;
-} //end of the function AAS_NearestHideArea
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_route.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_route.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_utils.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_crc.h" +#include "l_libvar.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +#define ROUTING_DEBUG + +//travel time in hundreths of a second = distance * 100 / speed +#define DISTANCEFACTOR_CROUCH 1.3f //crouch speed = 100 +#define DISTANCEFACTOR_SWIM 1 //should be 0.66, swim speed = 150 +#define DISTANCEFACTOR_WALK 0.33f //walk speed = 300 + +//cache refresh time +#define CACHE_REFRESHTIME 15.0f //15 seconds refresh time + +//maximum number of routing updates each frame +#define MAX_FRAMEROUTINGUPDATES 10 + + +/* + + area routing cache: + stores the distances within one cluster to a specific goal area + this goal area is in this same cluster and could be a cluster portal + for every cluster there's a list with routing cache for every area + in that cluster (including the portals of that cluster) + area cache stores aasworld.clusters[?].numreachabilityareas travel times + + portal routing cache: + stores the distances of all portals to a specific goal area + this goal area could be in any cluster and could also be a cluster portal + for every area (aasworld.numareas) the portal cache stores + aasworld.numportals travel times + +*/ + +#ifdef ROUTING_DEBUG +int numareacacheupdates; +int numportalcacheupdates; +#endif //ROUTING_DEBUG + +int routingcachesize; +int max_routingcachesize; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef ROUTING_DEBUG +void AAS_RoutingInfo(void) +{ + botimport.Print(PRT_MESSAGE, "%d area cache updates\n", numareacacheupdates); + botimport.Print(PRT_MESSAGE, "%d portal cache updates\n", numportalcacheupdates); + botimport.Print(PRT_MESSAGE, "%d bytes routing cache\n", routingcachesize); +} //end of the function AAS_RoutingInfo +#endif //ROUTING_DEBUG +//=========================================================================== +// returns the number of the area in the cluster +// assumes the given area is in the given cluster or a portal of the cluster +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +__inline int AAS_ClusterAreaNum(int cluster, int areanum) +{ + int side, areacluster; + + areacluster = aasworld.areasettings[areanum].cluster; + if (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum; + else + { +/*#ifdef ROUTING_DEBUG + if (aasworld.portals[-areacluster].frontcluster != cluster && + aasworld.portals[-areacluster].backcluster != cluster) + { + botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n" + , -areacluster, cluster); + } //end if +#endif //ROUTING_DEBUG*/ + side = aasworld.portals[-areacluster].frontcluster != cluster; + return aasworld.portals[-areacluster].clusterareanum[side]; + } //end else +} //end of the function AAS_ClusterAreaNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitTravelFlagFromType(void) +{ + int i; + + for (i = 0; i < MAX_TRAVELTYPES; i++) + { + aasworld.travelflagfortype[i] = TFL_INVALID; + } //end for + aasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID; + aasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK; + aasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH; + aasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP; + aasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP; + aasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER; + aasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE; + aasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM; + aasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP; + aasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT; + aasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR; + aasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP; + aasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP; + aasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK; + aasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP; + aasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP; + aasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP; + aasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD; + aasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB; +} //end of the function AAS_InitTravelFlagFromType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +__inline int AAS_TravelFlagForType_inline(int traveltype) +{ + int tfl; + + tfl = 0; + if (tfl & TRAVELFLAG_NOTTEAM1) + tfl |= TFL_NOTTEAM1; + if (tfl & TRAVELFLAG_NOTTEAM2) + tfl |= TFL_NOTTEAM2; + traveltype &= TRAVELTYPE_MASK; + if (traveltype < 0 || traveltype >= MAX_TRAVELTYPES) + return TFL_INVALID; + tfl |= aasworld.travelflagfortype[traveltype]; + return tfl; +} //end of the function AAS_TravelFlagForType_inline +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TravelFlagForType(int traveltype) +{ + return AAS_TravelFlagForType_inline(traveltype); +} //end of the function AAS_TravelFlagForType_inline +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkCache(aas_routingcache_t *cache) +{ + if (cache->time_next) cache->time_next->time_prev = cache->time_prev; + else aasworld.newestcache = cache->time_prev; + if (cache->time_prev) cache->time_prev->time_next = cache->time_next; + else aasworld.oldestcache = cache->time_next; + cache->time_next = NULL; + cache->time_prev = NULL; +} //end of the function AAS_UnlinkCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_LinkCache(aas_routingcache_t *cache) +{ + if (aasworld.newestcache) + { + aasworld.newestcache->time_next = cache; + cache->time_prev = aasworld.newestcache; + } //end if + else + { + aasworld.oldestcache = cache; + cache->time_prev = NULL; + } //end else + cache->time_next = NULL; + aasworld.newestcache = cache; +} //end of the function AAS_LinkCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeRoutingCache(aas_routingcache_t *cache) +{ + AAS_UnlinkCache(cache); + routingcachesize -= cache->size; + FreeMemory(cache); +} //end of the function AAS_FreeRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveRoutingCacheInCluster( int clusternum ) +{ + int i; + aas_routingcache_t *cache, *nextcache; + aas_cluster_t *cluster; + + if (!aasworld.clusterareacache) + return; + cluster = &aasworld.clusters[clusternum]; + for (i = 0; i < cluster->numareas; i++) + { + for (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.clusterareacache[clusternum][i] = NULL; + } //end for +} //end of the function AAS_RemoveRoutingCacheInCluster +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveRoutingCacheUsingArea( int areanum ) +{ + int i, clusternum; + aas_routingcache_t *cache, *nextcache; + + clusternum = aasworld.areasettings[areanum].cluster; + if (clusternum > 0) + { + //remove all the cache in the cluster the area is in + AAS_RemoveRoutingCacheInCluster( clusternum ); + } //end if + else + { + // if this is a portal remove all cache in both the front and back cluster + AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster ); + AAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster ); + } //end else + // remove all portal cache + for (i = 0; i < aasworld.numareas; i++) + { + //refresh portal cache + for (cache = aasworld.portalcache[i]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.portalcache[i] = NULL; + } //end for +} //end of the function AAS_RemoveRoutingCacheUsingArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_EnableRoutingArea(int areanum, int enable) +{ + int flags; + + if (areanum <= 0 || areanum >= aasworld.numareas) + { + if (bot_developer) + { + botimport.Print(PRT_ERROR, "AAS_EnableRoutingArea: areanum %d out of range\n", areanum); + } //end if + return 0; + } //end if + flags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED; + if (enable < 0) + return !flags; + + if (enable) + aasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED; + else + aasworld.areasettings[areanum].areaflags |= AREA_DISABLED; + // if the status of the area changed + if ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) ) + { + //remove all routing cache involving this area + AAS_RemoveRoutingCacheUsingArea( areanum ); + } //end if + return !flags; +} //end of the function AAS_EnableRoutingArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +__inline float AAS_RoutingTime(void) +{ + return AAS_Time(); +} //end of the function AAS_RoutingTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GetAreaContentsTravelFlags(int areanum) +{ + int contents, tfl; + + contents = aasworld.areasettings[areanum].contents; + tfl = 0; + if (contents & AREACONTENTS_WATER) + tfl |= TFL_WATER; + else if (contents & AREACONTENTS_SLIME) + tfl |= TFL_SLIME; + else if (contents & AREACONTENTS_LAVA) + tfl |= TFL_LAVA; + else + tfl |= TFL_AIR; + if (contents & AREACONTENTS_DONOTENTER) + tfl |= TFL_DONOTENTER; + if (contents & AREACONTENTS_NOTTEAM1) + tfl |= TFL_NOTTEAM1; + if (contents & AREACONTENTS_NOTTEAM2) + tfl |= TFL_NOTTEAM2; + if (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE) + tfl |= TFL_BRIDGE; + return tfl; +} //end of the function AAS_GetAreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +__inline int AAS_AreaContentsTravelFlags_inline(int areanum) +{ + return aasworld.areacontentstravelflags[areanum]; +} //end of the function AAS_AreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaContentsTravelFlags(int areanum) +{ + return aasworld.areacontentstravelflags[areanum]; +} //end of the function AAS_AreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAreaContentsTravelFlags(void) +{ + int i; + + if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); + aasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int)); + // + for (i = 0; i < aasworld.numareas; i++) { + aasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i); + } +} //end of the function AAS_InitAreaContentsTravelFlags +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateReversedReachability(void) +{ + int i, n; + aas_reversedlink_t *revlink; + aas_reachability_t *reach; + aas_areasettings_t *settings; + char *ptr; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif + //free reversed links that have already been created + if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); + //allocate memory for the reversed reachability links + ptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) + + aasworld.reachabilitysize * sizeof(aas_reversedlink_t)); + // + aasworld.reversedreachability = (aas_reversedreachability_t *) ptr; + //pointer to the memory for the reversed links + ptr += aasworld.numareas * sizeof(aas_reversedreachability_t); + //check all reachabilities of all areas + for (i = 1; i < aasworld.numareas; i++) + { + //settings of the area + settings = &aasworld.areasettings[i]; + // + if (settings->numreachableareas >= 128) + botimport.Print(PRT_WARNING, "area %d has more than 128 reachabilities\n", i); + //create reversed links for the reachabilities + for (n = 0; n < settings->numreachableareas && n < 128; n++) + { + //reachability link + reach = &aasworld.reachability[settings->firstreachablearea + n]; + // + revlink = (aas_reversedlink_t *) ptr; + ptr += sizeof(aas_reversedlink_t); + // + revlink->areanum = i; + revlink->linknum = settings->firstreachablearea + n; + revlink->next = aasworld.reversedreachability[reach->areanum].first; + aasworld.reversedreachability[reach->areanum].first = revlink; + aasworld.reversedreachability[reach->areanum].numlinks++; + } //end for + } //end for +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "reversed reachability %d msec\n", Sys_MilliSeconds() - starttime); +#endif +} //end of the function AAS_CreateReversedReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end) +{ + int intdist; + float dist; + vec3_t dir; + + VectorSubtract(start, end, dir); + dist = VectorLength(dir); + //if crouch only area + if (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH; + //if swim area + else if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM; + //normal walk area + else dist *= DISTANCEFACTOR_WALK; + // + intdist = (int) dist; + //make sure the distance isn't zero + if (intdist <= 0) intdist = 1; + return intdist; +} //end of the function AAS_AreaTravelTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CalculateAreaTravelTimes(void) +{ + int i, l, n, size; + char *ptr; + vec3_t end; + aas_reversedreachability_t *revreach; + aas_reversedlink_t *revlink; + aas_reachability_t *reach; + aas_areasettings_t *settings; + int starttime; + + starttime = Sys_MilliSeconds(); + //if there are still area travel times, free the memory + if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); + //get the total size of all the area travel times + size = aasworld.numareas * sizeof(unsigned short **); + for (i = 0; i < aasworld.numareas; i++) + { + revreach = &aasworld.reversedreachability[i]; + //settings of the area + settings = &aasworld.areasettings[i]; + // + size += settings->numreachableareas * sizeof(unsigned short *); + // + size += settings->numreachableareas * revreach->numlinks * sizeof(unsigned short); + } //end for + //allocate memory for the area travel times + ptr = (char *) GetClearedMemory(size); + aasworld.areatraveltimes = (unsigned short ***) ptr; + ptr += aasworld.numareas * sizeof(unsigned short **); + //calcluate the travel times for all the areas + for (i = 0; i < aasworld.numareas; i++) + { + //reversed reachabilities of this area + revreach = &aasworld.reversedreachability[i]; + //settings of the area + settings = &aasworld.areasettings[i]; + // + aasworld.areatraveltimes[i] = (unsigned short **) ptr; + ptr += settings->numreachableareas * sizeof(unsigned short *); + // + for (l = 0; l < settings->numreachableareas; l++) + { + aasworld.areatraveltimes[i][l] = (unsigned short *) ptr; + ptr += revreach->numlinks * sizeof(unsigned short); + //reachability link + reach = &aasworld.reachability[settings->firstreachablearea + l]; + // + for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) + { + VectorCopy(aasworld.reachability[revlink->linknum].end, end); + // + aasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start); + } //end for + } //end for + } //end for +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "area travel times %d msec\n", Sys_MilliSeconds() - starttime); +#endif +} //end of the function AAS_CalculateAreaTravelTimes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PortalMaxTravelTime(int portalnum) +{ + int l, n, t, maxt; + aas_portal_t *portal; + aas_reversedreachability_t *revreach; + aas_reversedlink_t *revlink; + aas_areasettings_t *settings; + + portal = &aasworld.portals[portalnum]; + //reversed reachabilities of this portal area + revreach = &aasworld.reversedreachability[portal->areanum]; + //settings of the portal area + settings = &aasworld.areasettings[portal->areanum]; + // + maxt = 0; + for (l = 0; l < settings->numreachableareas; l++) + { + for (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++) + { + t = aasworld.areatraveltimes[portal->areanum][l][n]; + if (t > maxt) + { + maxt = t; + } //end if + } //end for + } //end for + return maxt; +} //end of the function AAS_PortalMaxTravelTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitPortalMaxTravelTimes(void) +{ + int i; + + if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); + + aasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int)); + + for (i = 0; i < aasworld.numportals; i++) + { + aasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i); + //botimport.Print(PRT_MESSAGE, "portal %d max tt = %d\n", i, aasworld.portalmaxtraveltimes[i]); + } //end for +} //end of the function AAS_InitPortalMaxTravelTimes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +int AAS_FreeOldestCache(void) +{ + int i, j, bestcluster, bestarea, freed; + float besttime; + aas_routingcache_t *cache, *bestcache; + + freed = qfalse; + besttime = 999999999; + bestcache = NULL; + bestcluster = 0; + bestarea = 0; + //refresh cluster cache + for (i = 0; i < aasworld.numclusters; i++) + { + for (j = 0; j < aasworld.clusters[i].numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) + { + //never remove cache leading towards a portal + if (aasworld.areasettings[cache->areanum].cluster < 0) continue; + //if this cache is older than the cache we found so far + if (cache->time < besttime) + { + bestcache = cache; + bestcluster = i; + bestarea = j; + besttime = cache->time; + } //end if + } //end for + } //end for + } //end for + if (bestcache) + { + cache = bestcache; + if (cache->prev) cache->prev->next = cache->next; + else aasworld.clusterareacache[bestcluster][bestarea] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + AAS_FreeRoutingCache(cache); + freed = qtrue; + } //end if + besttime = 999999999; + bestcache = NULL; + bestarea = 0; + for (i = 0; i < aasworld.numareas; i++) + { + //refresh portal cache + for (cache = aasworld.portalcache[i]; cache; cache = cache->next) + { + if (cache->time < besttime) + { + bestcache = cache; + bestarea = i; + besttime = cache->time; + } //end if + } //end for + } //end for + if (bestcache) + { + cache = bestcache; + if (cache->prev) cache->prev->next = cache->next; + else aasworld.portalcache[bestarea] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + AAS_FreeRoutingCache(cache); + freed = qtrue; + } //end if + return freed; +} //end of the function AAS_FreeOldestCache +*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FreeOldestCache(void) +{ + int clusterareanum; + aas_routingcache_t *cache; + + for (cache = aasworld.oldestcache; cache; cache = cache->time_next) { + // never free area cache leading towards a portal + if (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) { + continue; + } + break; + } + if (cache) { + // unlink the cache + if (cache->type == CACHETYPE_AREA) { + //number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); + // unlink from cluster area cache + if (cache->prev) cache->prev->next = cache->next; + else aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + } + else { + // unlink from portal cache + if (cache->prev) cache->prev->next = cache->next; + else aasworld.portalcache[cache->areanum] = cache->next; + if (cache->next) cache->next->prev = cache->prev; + } + AAS_FreeRoutingCache(cache); + return qtrue; + } + return qfalse; +} //end of the function AAS_FreeOldestCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes) +{ + aas_routingcache_t *cache; + int size; + + // + size = sizeof(aas_routingcache_t) + + numtraveltimes * sizeof(unsigned short int) + + numtraveltimes * sizeof(unsigned char); + // + routingcachesize += size; + // + cache = (aas_routingcache_t *) GetClearedMemory(size); + cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) + + numtraveltimes * sizeof(unsigned short int); + cache->size = size; + return cache; +} //end of the function AAS_AllocRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAllClusterAreaCache(void) +{ + int i, j; + aas_routingcache_t *cache, *nextcache; + aas_cluster_t *cluster; + + //free all cluster cache if existing + if (!aasworld.clusterareacache) return; + //free caches + for (i = 0; i < aasworld.numclusters; i++) + { + cluster = &aasworld.clusters[i]; + for (j = 0; j < cluster->numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.clusterareacache[i][j] = NULL; + } //end for + } //end for + //free the cluster cache array + FreeMemory(aasworld.clusterareacache); + aasworld.clusterareacache = NULL; +} //end of the function AAS_FreeAllClusterAreaCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitClusterAreaCache(void) +{ + int i, size; + char *ptr; + + // + for (size = 0, i = 0; i < aasworld.numclusters; i++) + { + size += aasworld.clusters[i].numareas; + } //end for + //two dimensional array with pointers for every cluster to routing cache + //for every area in that cluster + ptr = (char *) GetClearedMemory( + aasworld.numclusters * sizeof(aas_routingcache_t **) + + size * sizeof(aas_routingcache_t *)); + aasworld.clusterareacache = (aas_routingcache_t ***) ptr; + ptr += aasworld.numclusters * sizeof(aas_routingcache_t **); + for (i = 0; i < aasworld.numclusters; i++) + { + aasworld.clusterareacache[i] = (aas_routingcache_t **) ptr; + ptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *); + } //end for +} //end of the function AAS_InitClusterAreaCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAllPortalCache(void) +{ + int i; + aas_routingcache_t *cache, *nextcache; + + //free all portal cache if existing + if (!aasworld.portalcache) return; + //free portal caches + for (i = 0; i < aasworld.numareas; i++) + { + for (cache = aasworld.portalcache[i]; cache; cache = nextcache) + { + nextcache = cache->next; + AAS_FreeRoutingCache(cache); + } //end for + aasworld.portalcache[i] = NULL; + } //end for + FreeMemory(aasworld.portalcache); + aasworld.portalcache = NULL; +} //end of the function AAS_FreeAllPortalCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitPortalCache(void) +{ + // + aasworld.portalcache = (aas_routingcache_t **) GetClearedMemory( + aasworld.numareas * sizeof(aas_routingcache_t *)); +} //end of the function AAS_InitPortalCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitRoutingUpdate(void) +{ + int i, maxreachabilityareas; + + //free routing update fields if already existing + if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); + // + maxreachabilityareas = 0; + for (i = 0; i < aasworld.numclusters; i++) + { + if (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas) + { + maxreachabilityareas = aasworld.clusters[i].numreachabilityareas; + } //end if + } //end for + //allocate memory for the routing update fields + aasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory( + maxreachabilityareas * sizeof(aas_routingupdate_t)); + // + if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); + //allocate memory for the portal update fields + aasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory( + (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); +} //end of the function AAS_InitRoutingUpdate +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateAllRoutingCache(void) +{ + int i, j, t; + + aasworld.initialized = qtrue; + botimport.Print(PRT_MESSAGE, "AAS_CreateAllRoutingCache\n"); + for (i = 1; i < aasworld.numareas; i++) + { + if (!AAS_AreaReachability(i)) continue; + for (j = 1; j < aasworld.numareas; j++) + { + if (i == j) continue; + if (!AAS_AreaReachability(j)) continue; + t = AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT); + //Log_Write("traveltime from %d to %d is %d", i, j, t); + } //end for + } //end for + aasworld.initialized = qfalse; +} //end of the function AAS_CreateAllRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +//the route cache header +//this header is followed by numportalcache + numareacache aas_routingcache_t +//structures that store routing cache +typedef struct routecacheheader_s +{ + int ident; + int version; + int numareas; + int numclusters; + int areacrc; + int clustercrc; + int numportalcache; + int numareacache; +} routecacheheader_t; + +#define RCID (('C'<<24)+('R'<<16)+('E'<<8)+'M') +#define RCVERSION 2 + +//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed); +//int AAS_CompressVis(byte *vis, int numareas, byte *dest); + +void AAS_WriteRouteCache(void) +{ + int i, j, numportalcache, numareacache, totalsize; + aas_routingcache_t *cache; + aas_cluster_t *cluster; + fileHandle_t fp; + char filename[MAX_QPATH]; + routecacheheader_t routecacheheader; + + numportalcache = 0; + for (i = 0; i < aasworld.numareas; i++) + { + for (cache = aasworld.portalcache[i]; cache; cache = cache->next) + { + numportalcache++; + } //end for + } //end for + numareacache = 0; + for (i = 0; i < aasworld.numclusters; i++) + { + cluster = &aasworld.clusters[i]; + for (j = 0; j < cluster->numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) + { + numareacache++; + } //end for + } //end for + } //end for + // open the file for writing + Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); + botimport.FS_FOpenFile( filename, &fp, FS_WRITE ); + if (!fp) + { + AAS_Error("Unable to open file: %s\n", filename); + return; + } //end if + //create the header + routecacheheader.ident = RCID; + routecacheheader.version = RCVERSION; + routecacheheader.numareas = aasworld.numareas; + routecacheheader.numclusters = aasworld.numclusters; + routecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ); + routecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ); + routecacheheader.numportalcache = numportalcache; + routecacheheader.numareacache = numareacache; + //write the header + botimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp); + // + totalsize = 0; + //write all the cache + for (i = 0; i < aasworld.numareas; i++) + { + for (cache = aasworld.portalcache[i]; cache; cache = cache->next) + { + botimport.FS_Write(cache, cache->size, fp); + totalsize += cache->size; + } //end for + } //end for + for (i = 0; i < aasworld.numclusters; i++) + { + cluster = &aasworld.clusters[i]; + for (j = 0; j < cluster->numareas; j++) + { + for (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next) + { + botimport.FS_Write(cache, cache->size, fp); + totalsize += cache->size; + } //end for + } //end for + } //end for + // write the visareas + /* + for (i = 0; i < aasworld.numareas; i++) + { + if (!aasworld.areavisibility[i]) { + size = 0; + botimport.FS_Write(&size, sizeof(int), fp); + continue; + } + AAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis ); + size = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis ); + botimport.FS_Write(&size, sizeof(int), fp); + botimport.FS_Write(aasworld.decompressedvis, size, fp); + } + */ + // + botimport.FS_FCloseFile(fp); + botimport.Print(PRT_MESSAGE, "\nroute cache written to %s\n", filename); + botimport.Print(PRT_MESSAGE, "written %d bytes of routing cache\n", totalsize); +} //end of the function AAS_WriteRouteCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_ReadCache(fileHandle_t fp) +{ + int size; + aas_routingcache_t *cache; + + botimport.FS_Read(&size, sizeof(size), fp); + cache = (aas_routingcache_t *) GetMemory(size); + cache->size = size; + botimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp); + cache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) + + (size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2; + return cache; +} //end of the function AAS_ReadCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ReadRouteCache(void) +{ + int i, clusterareanum;//, size; + fileHandle_t fp; + char filename[MAX_QPATH]; + routecacheheader_t routecacheheader; + aas_routingcache_t *cache; + + Com_sprintf(filename, MAX_QPATH, "maps/%s.rcd", aasworld.mapname); + botimport.FS_FOpenFile( filename, &fp, FS_READ ); + if (!fp) + { + return qfalse; + } //end if + botimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp ); + if (routecacheheader.ident != RCID) + { + AAS_Error("%s is not a route cache dump\n"); + return qfalse; + } //end if + if (routecacheheader.version != RCVERSION) + { + AAS_Error("route cache dump has wrong version %d, should be %d", routecacheheader.version, RCVERSION); + return qfalse; + } //end if + if (routecacheheader.numareas != aasworld.numareas) + { + //AAS_Error("route cache dump has wrong number of areas\n"); + return qfalse; + } //end if + if (routecacheheader.numclusters != aasworld.numclusters) + { + //AAS_Error("route cache dump has wrong number of clusters\n"); + return qfalse; + } //end if + if (routecacheheader.areacrc != + CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas )) + { + //AAS_Error("route cache dump area CRC incorrect\n"); + return qfalse; + } //end if + if (routecacheheader.clustercrc != + CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters )) + { + //AAS_Error("route cache dump cluster CRC incorrect\n"); + return qfalse; + } //end if + //read all the portal cache + for (i = 0; i < routecacheheader.numportalcache; i++) + { + cache = AAS_ReadCache(fp); + cache->next = aasworld.portalcache[cache->areanum]; + cache->prev = NULL; + if (aasworld.portalcache[cache->areanum]) + aasworld.portalcache[cache->areanum]->prev = cache; + aasworld.portalcache[cache->areanum] = cache; + } //end for + //read all the cluster area cache + for (i = 0; i < routecacheheader.numareacache; i++) + { + cache = AAS_ReadCache(fp); + clusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum); + cache->next = aasworld.clusterareacache[cache->cluster][clusterareanum]; + cache->prev = NULL; + if (aasworld.clusterareacache[cache->cluster][clusterareanum]) + aasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache; + aasworld.clusterareacache[cache->cluster][clusterareanum] = cache; + } //end for + // read the visareas + /* + aasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *)); + aasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte)); + for (i = 0; i < aasworld.numareas; i++) + { + botimport.FS_Read(&size, sizeof(size), fp ); + if (size) { + aasworld.areavisibility[i] = (byte *) GetMemory(size); + botimport.FS_Read(aasworld.areavisibility[i], size, fp ); + } + } + */ + // + botimport.FS_FCloseFile(fp); + return qtrue; +} //end of the function AAS_ReadRouteCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define MAX_REACHABILITYPASSAREAS 32 + +void AAS_InitReachabilityAreas(void) +{ + int i, j, numareas, areas[MAX_REACHABILITYPASSAREAS]; + int numreachareas; + aas_reachability_t *reach; + vec3_t start, end; + + if (aasworld.reachabilityareas) + FreeMemory(aasworld.reachabilityareas); + if (aasworld.reachabilityareaindex) + FreeMemory(aasworld.reachabilityareaindex); + + aasworld.reachabilityareas = (aas_reachabilityareas_t *) + GetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t)); + aasworld.reachabilityareaindex = (int *) + GetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int)); + numreachareas = 0; + for (i = 0; i < aasworld.reachabilitysize; i++) + { + reach = &aasworld.reachability[i]; + numareas = 0; + switch(reach->traveltype & TRAVELTYPE_MASK) + { + //trace areas from start to end + case TRAVEL_BARRIERJUMP: + case TRAVEL_WATERJUMP: + VectorCopy(reach->start, end); + end[2] = reach->end[2]; + numareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS); + break; + case TRAVEL_WALKOFFLEDGE: + VectorCopy(reach->end, start); + start[2] = reach->start[2]; + numareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); + break; + case TRAVEL_GRAPPLEHOOK: + numareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS); + break; + + //trace arch + case TRAVEL_JUMP: break; + case TRAVEL_ROCKETJUMP: break; + case TRAVEL_BFGJUMP: break; + case TRAVEL_JUMPPAD: break; + + //trace from reach->start to entity center, along entity movement + //and from entity center to reach->end + case TRAVEL_ELEVATOR: break; + case TRAVEL_FUNCBOB: break; + + //no areas in between + case TRAVEL_WALK: break; + case TRAVEL_CROUCH: break; + case TRAVEL_LADDER: break; + case TRAVEL_SWIM: break; + case TRAVEL_TELEPORT: break; + default: break; + } //end switch + aasworld.reachabilityareas[i].firstarea = numreachareas; + aasworld.reachabilityareas[i].numareas = numareas; + for (j = 0; j < numareas; j++) + { + aasworld.reachabilityareaindex[numreachareas++] = areas[j]; + } //end for + } //end for +} //end of the function AAS_InitReachabilityAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitRouting(void) +{ + AAS_InitTravelFlagFromType(); + // + AAS_InitAreaContentsTravelFlags(); + //initialize the routing update fields + AAS_InitRoutingUpdate(); + //create reversed reachability links used by the routing update algorithm + AAS_CreateReversedReachability(); + //initialize the cluster cache + AAS_InitClusterAreaCache(); + //initialize portal cache + AAS_InitPortalCache(); + //initialize the area travel times + AAS_CalculateAreaTravelTimes(); + //calculate the maximum travel times through portals + AAS_InitPortalMaxTravelTimes(); + //get the areas reachabilities go through + AAS_InitReachabilityAreas(); + // +#ifdef ROUTING_DEBUG + numareacacheupdates = 0; + numportalcacheupdates = 0; +#endif //ROUTING_DEBUG + // + routingcachesize = 0; + max_routingcachesize = 1024 * (int) LibVarValue("max_routingcache", "4096"); + // read any routing cache if available + AAS_ReadRouteCache(); +} //end of the function AAS_InitRouting +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeRoutingCaches(void) +{ + // free all the existing cluster area cache + AAS_FreeAllClusterAreaCache(); + // free all the existing portal cache + AAS_FreeAllPortalCache(); + // free cached travel times within areas + if (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes); + aasworld.areatraveltimes = NULL; + // free cached maximum travel time through cluster portals + if (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes); + aasworld.portalmaxtraveltimes = NULL; + // free reversed reachability links + if (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability); + aasworld.reversedreachability = NULL; + // free routing algorithm memory + if (aasworld.areaupdate) FreeMemory(aasworld.areaupdate); + aasworld.areaupdate = NULL; + if (aasworld.portalupdate) FreeMemory(aasworld.portalupdate); + aasworld.portalupdate = NULL; + // free lists with areas the reachabilities go through + if (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas); + aasworld.reachabilityareas = NULL; + // free the reachability area index + if (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex); + aasworld.reachabilityareaindex = NULL; + // free area contents travel flags look up table + if (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags); + aasworld.areacontentstravelflags = NULL; +} //end of the function AAS_FreeRoutingCaches +//=========================================================================== +// update the given routing cache +// +// Parameter: areacache : routing cache to update +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache) +{ + int i, nextareanum, cluster, badtravelflags, clusterareanum, linknum; + int numreachabilityareas; + unsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + aas_reversedreachability_t *revreach; + aas_reversedlink_t *revlink; + +#ifdef ROUTING_DEBUG + numareacacheupdates++; +#endif //ROUTING_DEBUG + //number of reachability areas within this cluster + numreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas; + // + aasworld.frameroutingupdates++; + //clear the routing update fields +// Com_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t)); + // + badtravelflags = ~areacache->travelflags; + // + clusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum); + if (clusterareanum >= numreachabilityareas) return; + // + Com_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes)); + // + curupdate = &aasworld.areaupdate[clusterareanum]; + curupdate->areanum = areacache->areanum; + //VectorCopy(areacache->origin, curupdate->start); + curupdate->areatraveltimes = startareatraveltimes; + curupdate->tmptraveltime = areacache->starttraveltime; + // + areacache->traveltimes[clusterareanum] = areacache->starttraveltime; + //put the area to start with in the current read list + curupdate->next = NULL; + curupdate->prev = NULL; + updateliststart = curupdate; + updatelistend = curupdate; + //while there are updates in the current list + while (updateliststart) + { + curupdate = updateliststart; + // + if (curupdate->next) curupdate->next->prev = NULL; + else updatelistend = NULL; + updateliststart = curupdate->next; + // + curupdate->inlist = qfalse; + //check all reversed reachability links + revreach = &aasworld.reversedreachability[curupdate->areanum]; + // + for (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++) + { + linknum = revlink->linknum; + reach = &aasworld.reachability[linknum]; + //if there is used an undesired travel type + if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; + //if not allowed to enter the next area + if (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue; + //if the next area has a not allowed travel flag + if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; + //number of the area the reversed reachability leads to + nextareanum = revlink->areanum; + //get the cluster number of the area + cluster = aasworld.areasettings[nextareanum].cluster; + //don't leave the cluster + if (cluster > 0 && cluster != areacache->cluster) continue; + //get the number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum); + if (clusterareanum >= numreachabilityareas) continue; + //time already travelled plus the traveltime through + //the current area plus the travel time from the reachability + t = curupdate->tmptraveltime + + //AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) + + curupdate->areatraveltimes[i] + + reach->traveltime; + // + if (!areacache->traveltimes[clusterareanum] || + areacache->traveltimes[clusterareanum] > t) + { + areacache->traveltimes[clusterareanum] = t; + areacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea; + nextupdate = &aasworld.areaupdate[clusterareanum]; + nextupdate->areanum = nextareanum; + nextupdate->tmptraveltime = t; + //VectorCopy(reach->start, nextupdate->start); + nextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum - + aasworld.areasettings[nextareanum].firstreachablearea]; + if (!nextupdate->inlist) + { + // we add the update to the end of the list + // we could also use a B+ tree to have a real sorted list + // on travel time which makes for faster routing updates + nextupdate->next = NULL; + nextupdate->prev = updatelistend; + if (updatelistend) updatelistend->next = nextupdate; + else updateliststart = nextupdate; + updatelistend = nextupdate; + nextupdate->inlist = qtrue; + } //end if + } //end if + } //end for + } //end while +} //end of the function AAS_UpdateAreaRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags) +{ + int clusterareanum; + aas_routingcache_t *cache, *clustercache; + + //number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //pointer to the cache for the area in the cluster + clustercache = aasworld.clusterareacache[clusternum][clusterareanum]; + //find the cache without undesired travel flags + for (cache = clustercache; cache; cache = cache->next) + { + //if there aren't used any undesired travel types for the cache + if (cache->travelflags == travelflags) break; + } //end for + //if there was no cache + if (!cache) + { + cache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas); + cache->cluster = clusternum; + cache->areanum = areanum; + VectorCopy(aasworld.areas[areanum].center, cache->origin); + cache->starttraveltime = 1; + cache->travelflags = travelflags; + cache->prev = NULL; + cache->next = clustercache; + if (clustercache) clustercache->prev = cache; + aasworld.clusterareacache[clusternum][clusterareanum] = cache; + AAS_UpdateAreaRoutingCache(cache); + } //end if + else + { + AAS_UnlinkCache(cache); + } //end else + //the cache has been accessed + cache->time = AAS_RoutingTime(); + cache->type = CACHETYPE_AREA; + AAS_LinkCache(cache); + return cache; +} //end of the function AAS_GetAreaRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache) +{ + int i, portalnum, clusterareanum, clusternum; + unsigned short int t; + aas_portal_t *portal; + aas_cluster_t *cluster; + aas_routingcache_t *cache; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + +#ifdef ROUTING_DEBUG + numportalcacheupdates++; +#endif //ROUTING_DEBUG + //clear the routing update fields +// Com_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t)); + // + curupdate = &aasworld.portalupdate[aasworld.numportals]; + curupdate->cluster = portalcache->cluster; + curupdate->areanum = portalcache->areanum; + curupdate->tmptraveltime = portalcache->starttraveltime; + //if the start area is a cluster portal, store the travel time for that portal + clusternum = aasworld.areasettings[portalcache->areanum].cluster; + if (clusternum < 0) + { + portalcache->traveltimes[-clusternum] = portalcache->starttraveltime; + } //end if + //put the area to start with in the current read list + curupdate->next = NULL; + curupdate->prev = NULL; + updateliststart = curupdate; + updatelistend = curupdate; + //while there are updates in the current list + while (updateliststart) + { + curupdate = updateliststart; + //remove the current update from the list + if (curupdate->next) curupdate->next->prev = NULL; + else updatelistend = NULL; + updateliststart = curupdate->next; + //current update is removed from the list + curupdate->inlist = qfalse; + // + cluster = &aasworld.clusters[curupdate->cluster]; + // + cache = AAS_GetAreaRoutingCache(curupdate->cluster, + curupdate->areanum, portalcache->travelflags); + //take all portals of the cluster + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + portal = &aasworld.portals[portalnum]; + //if this is the portal of the current update continue + if (portal->areanum == curupdate->areanum) continue; + // + clusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum); + if (clusterareanum >= cluster->numreachabilityareas) continue; + // + t = cache->traveltimes[clusterareanum]; + if (!t) continue; + t += curupdate->tmptraveltime; + // + if (!portalcache->traveltimes[portalnum] || + portalcache->traveltimes[portalnum] > t) + { + portalcache->traveltimes[portalnum] = t; + nextupdate = &aasworld.portalupdate[portalnum]; + if (portal->frontcluster == curupdate->cluster) + { + nextupdate->cluster = portal->backcluster; + } //end if + else + { + nextupdate->cluster = portal->frontcluster; + } //end else + nextupdate->areanum = portal->areanum; + //add travel time through the actual portal area for the next update + nextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum]; + if (!nextupdate->inlist) + { + // we add the update to the end of the list + // we could also use a B+ tree to have a real sorted list + // on travel time which makes for faster routing updates + nextupdate->next = NULL; + nextupdate->prev = updatelistend; + if (updatelistend) updatelistend->next = nextupdate; + else updateliststart = nextupdate; + updatelistend = nextupdate; + nextupdate->inlist = qtrue; + } //end if + } //end if + } //end for + } //end while +} //end of the function AAS_UpdatePortalRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags) +{ + aas_routingcache_t *cache; + + //find the cached portal routing if existing + for (cache = aasworld.portalcache[areanum]; cache; cache = cache->next) + { + if (cache->travelflags == travelflags) break; + } //end for + //if the portal routing isn't cached + if (!cache) + { + cache = AAS_AllocRoutingCache(aasworld.numportals); + cache->cluster = clusternum; + cache->areanum = areanum; + VectorCopy(aasworld.areas[areanum].center, cache->origin); + cache->starttraveltime = 1; + cache->travelflags = travelflags; + //add the cache to the cache list + cache->prev = NULL; + cache->next = aasworld.portalcache[areanum]; + if (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache; + aasworld.portalcache[areanum] = cache; + //update the cache + AAS_UpdatePortalRoutingCache(cache); + } //end if + else + { + AAS_UnlinkCache(cache); + } //end else + //the cache has been accessed + cache->time = AAS_RoutingTime(); + cache->type = CACHETYPE_PORTAL; + AAS_LinkCache(cache); + return cache; +} //end of the function AAS_GetPortalRoutingCache +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum) +{ + int clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum; + unsigned short int t, besttime; + aas_portal_t *portal; + aas_cluster_t *cluster; + aas_routingcache_t *areacache, *portalcache; + aas_reachability_t *reach; + + if (!aasworld.initialized) return qfalse; + + if (areanum == goalareanum) + { + *traveltime = 1; + *reachnum = 0; + return qtrue; + } + // + if (areanum <= 0 || areanum >= aasworld.numareas) + { + if (bot_developer) + { + botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: areanum %d out of range\n", areanum); + } //end if + return qfalse; + } //end if + if (goalareanum <= 0 || goalareanum >= aasworld.numareas) + { + if (bot_developer) + { + botimport.Print(PRT_ERROR, "AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\n", goalareanum); + } //end if + return qfalse; + } //end if + // make sure the routing cache doesn't grow to large + while(AvailableMemory() < 1 * 1024 * 1024) { + if (!AAS_FreeOldestCache()) break; + } + // + if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum)) + { + travelflags |= TFL_DONOTENTER; + } //end if + //NOTE: the number of routing updates is limited per frame + /* + if (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES) + { +#ifdef DEBUG + //Log_Write("WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed"); +#endif + return 0; + } //end if + */ + // + clusternum = aasworld.areasettings[areanum].cluster; + goalclusternum = aasworld.areasettings[goalareanum].cluster; + //check if the area is a portal of the goal area cluster + if (clusternum < 0 && goalclusternum > 0) + { + portal = &aasworld.portals[-clusternum]; + if (portal->frontcluster == goalclusternum || + portal->backcluster == goalclusternum) + { + clusternum = goalclusternum; + } //end if + } //end if + //check if the goalarea is a portal of the area cluster + else if (clusternum > 0 && goalclusternum < 0) + { + portal = &aasworld.portals[-goalclusternum]; + if (portal->frontcluster == clusternum || + portal->backcluster == clusternum) + { + goalclusternum = clusternum; + } //end if + } //end if + //if both areas are in the same cluster + //NOTE: there might be a shorter route via another cluster!!! but we don't care + if (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum) + { + // + areacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags); + //the number of the area in the cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //the cluster the area is in + cluster = &aasworld.clusters[clusternum]; + //if the area is NOT a reachability area + if (clusterareanum >= cluster->numreachabilityareas) return 0; + //if it is possible to travel to the goal area through this cluster + if (areacache->traveltimes[clusterareanum] != 0) + { + *reachnum = aasworld.areasettings[areanum].firstreachablearea + + areacache->reachabilities[clusterareanum]; + if (!origin) { + *traveltime = areacache->traveltimes[clusterareanum]; + return qtrue; + } + reach = &aasworld.reachability[*reachnum]; + *traveltime = areacache->traveltimes[clusterareanum] + + AAS_AreaTravelTime(areanum, origin, reach->start); + // + return qtrue; + } //end if + } //end if + // + clusternum = aasworld.areasettings[areanum].cluster; + goalclusternum = aasworld.areasettings[goalareanum].cluster; + //if the goal area is a portal + if (goalclusternum < 0) + { + //just assume the goal area is part of the front cluster + portal = &aasworld.portals[-goalclusternum]; + goalclusternum = portal->frontcluster; + } //end if + //get the portal routing cache + portalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags); + //if the area is a cluster portal, read directly from the portal cache + if (clusternum < 0) + { + *traveltime = portalcache->traveltimes[-clusternum]; + *reachnum = aasworld.areasettings[areanum].firstreachablearea + + portalcache->reachabilities[-clusternum]; + return qtrue; + } //end if + // + besttime = 0; + bestreachnum = -1; + //the cluster the area is in + cluster = &aasworld.clusters[clusternum]; + //find the portal of the area cluster leading towards the goal area + for (i = 0; i < cluster->numportals; i++) + { + portalnum = aasworld.portalindex[cluster->firstportal + i]; + //if the goal area isn't reachable from the portal + if (!portalcache->traveltimes[portalnum]) continue; + // + portal = &aasworld.portals[portalnum]; + //get the cache of the portal area + areacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags); + //current area inside the current cluster + clusterareanum = AAS_ClusterAreaNum(clusternum, areanum); + //if the area is NOT a reachability area + if (clusterareanum >= cluster->numreachabilityareas) continue; + //if the portal is NOT reachable from this area + if (!areacache->traveltimes[clusterareanum]) continue; + //total travel time is the travel time the portal area is from + //the goal area plus the travel time towards the portal area + t = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum]; + //FIXME: add the exact travel time through the actual portal area + //NOTE: for now we just add the largest travel time through the portal area + // because we can't directly calculate the exact travel time + // to be more specific we don't know which reachability was used to travel + // into the portal area + t += aasworld.portalmaxtraveltimes[portalnum]; + // + if (origin) + { + *reachnum = aasworld.areasettings[areanum].firstreachablearea + + areacache->reachabilities[clusterareanum]; + reach = aasworld.reachability + *reachnum; + t += AAS_AreaTravelTime(areanum, origin, reach->start); + } //end if + //if the time is better than the one already found + if (!besttime || t < besttime) + { + bestreachnum = *reachnum; + besttime = t; + } //end if + } //end for + if (bestreachnum < 0) { + return qfalse; + } + *reachnum = bestreachnum; + *traveltime = besttime; + return qtrue; +} //end of the function AAS_AreaRouteToGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) +{ + int traveltime, reachnum; + + if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) + { + return traveltime; + } + return 0; +} //end of the function AAS_AreaTravelTimeToGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags) +{ + int traveltime, reachnum; + + if (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum)) + { + return reachnum; + } + return 0; +} //end of the function AAS_AreaReachabilityToGoalArea +//=========================================================================== +// predict the route and stop on one of the stop events +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, + int goalareanum, int travelflags, int maxareas, int maxtime, + int stopevent, int stopcontents, int stoptfl, int stopareanum) +{ + int curareanum, reachnum, i, j, testareanum; + vec3_t curorigin; + aas_reachability_t *reach; + aas_reachabilityareas_t *reachareas; + + //init output + route->stopevent = RSE_NONE; + route->endarea = goalareanum; + route->endcontents = 0; + route->endtravelflags = 0; + VectorCopy(origin, route->endpos); + route->time = 0; + + curareanum = areanum; + VectorCopy(origin, curorigin); + + for (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++) + { + reachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags); + if (!reachnum) + { + route->stopevent = RSE_NOROUTE; + return qfalse; + } //end if + reach = &aasworld.reachability[reachnum]; + // + if (stopevent & RSE_USETRAVELTYPE) + { + if (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl) + { + route->stopevent = RSE_USETRAVELTYPE; + route->endarea = curareanum; + route->endcontents = aasworld.areasettings[curareanum].contents; + route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); + VectorCopy(reach->start, route->endpos); + return qtrue; + } //end if + if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl) + { + route->stopevent = RSE_USETRAVELTYPE; + route->endarea = reach->areanum; + route->endcontents = aasworld.areasettings[reach->areanum].contents; + route->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum); + VectorCopy(reach->end, route->endpos); + route->time += AAS_AreaTravelTime(areanum, origin, reach->start); + route->time += reach->traveltime; + return qtrue; + } //end if + } //end if + reachareas = &aasworld.reachabilityareas[reachnum]; + for (j = 0; j < reachareas->numareas + 1; j++) + { + if (j >= reachareas->numareas) + testareanum = reach->areanum; + else + testareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j]; + if (stopevent & RSE_ENTERCONTENTS) + { + if (aasworld.areasettings[testareanum].contents & stopcontents) + { + route->stopevent = RSE_ENTERCONTENTS; + route->endarea = testareanum; + route->endcontents = aasworld.areasettings[testareanum].contents; + VectorCopy(reach->end, route->endpos); + route->time += AAS_AreaTravelTime(areanum, origin, reach->start); + route->time += reach->traveltime; + return qtrue; + } //end if + } //end if + if (stopevent & RSE_ENTERAREA) + { + if (testareanum == stopareanum) + { + route->stopevent = RSE_ENTERAREA; + route->endarea = testareanum; + route->endcontents = aasworld.areasettings[testareanum].contents; + VectorCopy(reach->start, route->endpos); + return qtrue; + } //end if + } //end if + } //end for + + route->time += AAS_AreaTravelTime(areanum, origin, reach->start); + route->time += reach->traveltime; + route->endarea = reach->areanum; + route->endcontents = aasworld.areasettings[reach->areanum].contents; + route->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype); + VectorCopy(reach->end, route->endpos); + // + curareanum = reach->areanum; + VectorCopy(reach->end, curorigin); + // + if (maxtime && route->time > maxtime) + break; + } //end while + if (curareanum != goalareanum) + return qfalse; + return qtrue; +} //end of the function AAS_PredictRoute +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BridgeWalkable(int areanum) +{ + return qfalse; +} //end of the function AAS_BridgeWalkable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach) +{ + if (!aasworld.initialized) + { + Com_Memset(reach, 0, sizeof(aas_reachability_t)); + return; + } //end if + if (num < 0 || num >= aasworld.reachabilitysize) + { + Com_Memset(reach, 0, sizeof(aas_reachability_t)); + return; + } //end if + Com_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));; +} //end of the function AAS_ReachabilityFromNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextAreaReachability(int areanum, int reachnum) +{ + aas_areasettings_t *settings; + + if (!aasworld.initialized) return 0; + + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_NextAreaReachability: areanum %d out of range\n", areanum); + return 0; + } //end if + + settings = &aasworld.areasettings[areanum]; + if (!reachnum) + { + return settings->firstreachablearea; + } //end if + if (reachnum < settings->firstreachablearea) + { + botimport.Print(PRT_FATAL, "AAS_NextAreaReachability: reachnum < settings->firstreachableara"); + return 0; + } //end if + reachnum++; + if (reachnum >= settings->firstreachablearea + settings->numreachableareas) + { + return 0; + } //end if + return reachnum; +} //end of the function AAS_NextAreaReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NextModelReachability(int num, int modelnum) +{ + int i; + + if (num <= 0) num = 1; + else if (num >= aasworld.reachabilitysize) return 0; + else num++; + // + for (i = num; i < aasworld.reachabilitysize; i++) + { + if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) + { + if (aasworld.reachability[i].facenum == modelnum) return i; + } //end if + else if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) + { + if ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i; + } //end if + } //end for + return 0; +} //end of the function AAS_NextModelReachability +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin) +{ + int i, n, t; + vec3_t start, end; + aas_trace_t trace; + + //if the area has no reachabilities + if (!AAS_AreaReachability(areanum)) return qfalse; + // + n = aasworld.numareas * random(); + for (i = 0; i < aasworld.numareas; i++) + { + if (n <= 0) n = 1; + if (n >= aasworld.numareas) n = 1; + if (AAS_AreaReachability(n)) + { + t = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags); + //if the goal is reachable + if (t > 0) + { + if (AAS_AreaSwim(n)) + { + *goalareanum = n; + VectorCopy(aasworld.areas[n].center, goalorigin); + //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); + return qtrue; + } //end if + VectorCopy(aasworld.areas[n].center, start); + if (!AAS_PointAreaNum(start)) + Log_Write("area %d center %f %f %f in solid?", n, start[0], start[1], start[2]); + VectorCopy(start, end); + end[2] -= 300; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n) + { + if (AAS_AreaGroundFaceArea(n) > 300) + { + *goalareanum = n; + VectorCopy(trace.endpos, goalorigin); + //botimport.Print(PRT_MESSAGE, "found random goal area %d\n", *goalareanum); + return qtrue; + } //end if + } //end if + } //end if + } //end if + n++; + } //end for + return qfalse; +} //end of the function AAS_RandomGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaVisible(int srcarea, int destarea) +{ + return qfalse; +} //end of the function AAS_AreaVisible +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point) +{ + vec3_t vec, p2; + + AAS_ProjectPointOntoVector(point, v1, v2, p2); + VectorSubtract(point, p2, vec); + return VectorLength(vec); +} //end of the function DistancePointToLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags) +{ + int i, j, nextareanum, badtravelflags, numreach, bestarea; + unsigned short int t, besttraveltime; + static unsigned short int *hidetraveltimes; + aas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate; + aas_reachability_t *reach; + float dist1, dist2; + vec3_t v1, v2, p; + qboolean startVisible; + + // + if (!hidetraveltimes) + { + hidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int)); + } //end if + else + { + Com_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int)); + } //end else + besttraveltime = 0; + bestarea = 0; + //assume visible + startVisible = qtrue; + // + badtravelflags = ~travelflags; + // + curupdate = &aasworld.areaupdate[areanum]; + curupdate->areanum = areanum; + VectorCopy(origin, curupdate->start); + curupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0]; + curupdate->tmptraveltime = 0; + //put the area to start with in the current read list + curupdate->next = NULL; + curupdate->prev = NULL; + updateliststart = curupdate; + updatelistend = curupdate; + //while there are updates in the list + while (updateliststart) + { + curupdate = updateliststart; + // + if (curupdate->next) curupdate->next->prev = NULL; + else updatelistend = NULL; + updateliststart = curupdate->next; + // + curupdate->inlist = qfalse; + //check all reversed reachability links + numreach = aasworld.areasettings[curupdate->areanum].numreachableareas; + reach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea]; + // + for (i = 0; i < numreach; i++, reach++) + { + //if an undesired travel type is used + if (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue; + // + if (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue; + //number of the area the reachability leads to + nextareanum = reach->areanum; + // if this moves us into the enemies area, skip it + if (nextareanum == enemyareanum) continue; + //time already travelled plus the traveltime through + //the current area plus the travel time from the reachability + t = curupdate->tmptraveltime + + AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) + + reach->traveltime; + + //avoid going near the enemy + AAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p); + for (j = 0; j < 3; j++) + if ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) || + (p[j] < curupdate->start[j] && p[j] < reach->end[j])) + break; + if (j < 3) + { + VectorSubtract(enemyorigin, reach->end, v2); + } //end if + else + { + VectorSubtract(enemyorigin, p, v2); + } //end else + dist2 = VectorLength(v2); + //never go through the enemy + if (dist2 < 40) continue; + // + VectorSubtract(enemyorigin, curupdate->start, v1); + dist1 = VectorLength(v1); + // + if (dist2 < dist1) + { + t += (dist1 - dist2) * 10; + } + // if we weren't visible when starting, make sure we don't move into their view + if (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) { + continue; + } + // + if (besttraveltime && t >= besttraveltime) continue; + // + if (!hidetraveltimes[nextareanum] || + hidetraveltimes[nextareanum] > t) + { + //if the nextarea is not visible from the enemy area + if (!AAS_AreaVisible(enemyareanum, nextareanum)) + { + besttraveltime = t; + bestarea = nextareanum; + } //end if + hidetraveltimes[nextareanum] = t; + nextupdate = &aasworld.areaupdate[nextareanum]; + nextupdate->areanum = nextareanum; + nextupdate->tmptraveltime = t; + //remember where we entered this area + VectorCopy(reach->end, nextupdate->start); + //if this update is not in the list yet + if (!nextupdate->inlist) + { + //add the new update to the end of the list + nextupdate->next = NULL; + nextupdate->prev = updatelistend; + if (updatelistend) updatelistend->next = nextupdate; + else updateliststart = nextupdate; + updatelistend = nextupdate; + nextupdate->inlist = qtrue; + } //end if + } //end if + } //end for + } //end while + return bestarea; +} //end of the function AAS_NearestHideArea diff --git a/code/botlib/be_aas_route.h b/code/botlib/be_aas_route.h index 8b92ac1..547336c 100755 --- a/code/botlib/be_aas_route.h +++ b/code/botlib/be_aas_route.h @@ -1,67 +1,67 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_route.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_route.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-//initialize the AAS routing
-void AAS_InitRouting(void);
-//free the AAS routing caches
-void AAS_FreeRoutingCaches(void);
-//returns the travel time from start to end in the given area
-unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end);
-//
-void AAS_CreateAllRoutingCache(void);
-void AAS_WriteRouteCache(void);
-//
-void AAS_RoutingInfo(void);
-#endif //AASINTERN
-
-//returns the travel flag for the given travel type
-int AAS_TravelFlagForType(int traveltype);
-//return the travel flag(s) for traveling through this area
-int AAS_AreaContentsTravelFlags(int areanum);
-//returns the index of the next reachability for the given area
-int AAS_NextAreaReachability(int areanum, int reachnum);
-//returns the reachability with the given index
-void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach);
-//returns a random goal area and goal origin
-int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin);
-//enable or disable an area for routing
-int AAS_EnableRoutingArea(int areanum, int enable);
-//returns the travel time within the given area from start to end
-unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end);
-//returns the travel time from the area to the goal area using the given travel flags
-int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags);
-//predict a route up to a stop event
-int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin,
- int goalareanum, int travelflags, int maxareas, int maxtime,
- int stopevent, int stopcontents, int stoptfl, int stopareanum);
-
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_route.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_route.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize the AAS routing +void AAS_InitRouting(void); +//free the AAS routing caches +void AAS_FreeRoutingCaches(void); +//returns the travel time from start to end in the given area +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); +// +void AAS_CreateAllRoutingCache(void); +void AAS_WriteRouteCache(void); +// +void AAS_RoutingInfo(void); +#endif //AASINTERN + +//returns the travel flag for the given travel type +int AAS_TravelFlagForType(int traveltype); +//return the travel flag(s) for traveling through this area +int AAS_AreaContentsTravelFlags(int areanum); +//returns the index of the next reachability for the given area +int AAS_NextAreaReachability(int areanum, int reachnum); +//returns the reachability with the given index +void AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach); +//returns a random goal area and goal origin +int AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin); +//enable or disable an area for routing +int AAS_EnableRoutingArea(int areanum, int enable); +//returns the travel time within the given area from start to end +unsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end); +//returns the travel time from the area to the goal area using the given travel flags +int AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags); +//predict a route up to a stop event +int AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin, + int goalareanum, int travelflags, int maxareas, int maxtime, + int stopevent, int stopcontents, int stoptfl, int stopareanum); + + diff --git a/code/botlib/be_aas_routealt.c b/code/botlib/be_aas_routealt.c index fd7cb8d..956302d 100755 --- a/code/botlib/be_aas_routealt.c +++ b/code/botlib/be_aas_routealt.c @@ -1,240 +1,240 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_routealt.c
- *
- * desc: AAS
- *
- * $Archive: /MissionPack/code/botlib/be_aas_routealt.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_utils.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_aas_def.h"
-
-#define ENABLE_ALTROUTING
-//#define ALTROUTE_DEBUG
-
-typedef struct midrangearea_s
-{
- int valid;
- unsigned short starttime;
- unsigned short goaltime;
-} midrangearea_t;
-
-midrangearea_t *midrangeareas;
-int *clusterareas;
-int numclusterareas;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_AltRoutingFloodCluster_r(int areanum)
-{
- int i, otherareanum;
- aas_area_t *area;
- aas_face_t *face;
-
- //add the current area to the areas of the current cluster
- clusterareas[numclusterareas] = areanum;
- numclusterareas++;
- //remove the area from the mid range areas
- midrangeareas[areanum].valid = qfalse;
- //flood to other areas through the faces of this area
- area = &aasworld.areas[areanum];
- for (i = 0; i < area->numfaces; i++)
- {
- face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
- //get the area at the other side of the face
- if (face->frontarea == areanum) otherareanum = face->backarea;
- else otherareanum = face->frontarea;
- //if there is an area at the other side of this face
- if (!otherareanum) continue;
- //if the other area is not a midrange area
- if (!midrangeareas[otherareanum].valid) continue;
- //
- AAS_AltRoutingFloodCluster_r(otherareanum);
- } //end for
-} //end of the function AAS_AltRoutingFloodCluster_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,
- aas_altroutegoal_t *altroutegoals, int maxaltroutegoals,
- int type)
-{
-#ifndef ENABLE_ALTROUTING
- return 0;
-#else
- int i, j, bestareanum;
- int numaltroutegoals, nummidrangeareas;
- int starttime, goaltime, goaltraveltime;
- float dist, bestdist;
- vec3_t mid, dir;
-#ifdef ALTROUTE_DEBUG
- int startmillisecs;
-
- startmillisecs = Sys_MilliSeconds();
-#endif
-
- if (!startareanum || !goalareanum)
- return 0;
- //travel time towards the goal area
- goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags);
- //clear the midrange areas
- Com_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t));
- numaltroutegoals = 0;
- //
- nummidrangeareas = 0;
- //
- for (i = 1; i < aasworld.numareas; i++)
- {
- //
- if (!(type & ALTROUTEGOAL_ALL))
- {
- if (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)))
- {
- if (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL)))
- {
- continue;
- } //end if
- } //end if
- } //end if
- //if the area has no reachabilities
- if (!AAS_AreaReachability(i)) continue;
- //tavel time from the area to the start area
- starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags);
- if (!starttime) continue;
- //if the travel time from the start to the area is greater than the shortest goal travel time
- if (starttime > (float) 1.1 * goaltraveltime) continue;
- //travel time from the area to the goal area
- goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags);
- if (!goaltime) continue;
- //if the travel time from the area to the goal is greater than the shortest goal travel time
- if (goaltime > (float) 0.8 * goaltraveltime) continue;
- //this is a mid range area
- midrangeareas[i].valid = qtrue;
- midrangeareas[i].starttime = starttime;
- midrangeareas[i].goaltime = goaltime;
- Log_Write("%d midrange area %d", nummidrangeareas, i);
- nummidrangeareas++;
- } //end for
- //
- for (i = 1; i < aasworld.numareas; i++)
- {
- if (!midrangeareas[i].valid) continue;
- //get the areas in one cluster
- numclusterareas = 0;
- AAS_AltRoutingFloodCluster_r(i);
- //now we've got a cluster with areas through which an alternative route could go
- //get the 'center' of the cluster
- VectorClear(mid);
- for (j = 0; j < numclusterareas; j++)
- {
- VectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid);
- } //end for
- VectorScale(mid, 1.0 / numclusterareas, mid);
- //get the area closest to the center of the cluster
- bestdist = 999999;
- bestareanum = 0;
- for (j = 0; j < numclusterareas; j++)
- {
- VectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir);
- dist = VectorLength(dir);
- if (dist < bestdist)
- {
- bestdist = dist;
- bestareanum = clusterareas[j];
- } //end if
- } //end for
- //now we've got an area for an alternative route
- //FIXME: add alternative goal origin
- VectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin);
- altroutegoals[numaltroutegoals].areanum = bestareanum;
- altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime;
- altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime;
- altroutegoals[numaltroutegoals].extratraveltime =
- (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) -
- goaltraveltime;
- numaltroutegoals++;
- //
-#ifdef ALTROUTE_DEBUG
- AAS_ShowAreaPolygons(bestareanum, 1, qtrue);
-#endif
- //don't return more than the maximum alternative route goals
- if (numaltroutegoals >= maxaltroutegoals) break;
- } //end for
-#ifdef ALTROUTE_DEBUG
- botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs);
-#endif
- return numaltroutegoals;
-#endif
-} //end of the function AAS_AlternativeRouteGoals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitAlternativeRouting(void)
-{
-#ifdef ENABLE_ALTROUTING
- if (midrangeareas) FreeMemory(midrangeareas);
- midrangeareas = (midrangearea_t *) GetMemory(aasworld.numareas * sizeof(midrangearea_t));
- if (clusterareas) FreeMemory(clusterareas);
- clusterareas = (int *) GetMemory(aasworld.numareas * sizeof(int));
-#endif
-} //end of the function AAS_InitAlternativeRouting
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_ShutdownAlternativeRouting(void)
-{
-#ifdef ENABLE_ALTROUTING
- if (midrangeareas) FreeMemory(midrangeareas);
- midrangeareas = NULL;
- if (clusterareas) FreeMemory(clusterareas);
- clusterareas = NULL;
- numclusterareas = 0;
-#endif
-} //end of the function AAS_ShutdownAlternativeRouting
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_routealt.c + * + * desc: AAS + * + * $Archive: /MissionPack/code/botlib/be_aas_routealt.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_utils.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_aas_def.h" + +#define ENABLE_ALTROUTING +//#define ALTROUTE_DEBUG + +typedef struct midrangearea_s +{ + int valid; + unsigned short starttime; + unsigned short goaltime; +} midrangearea_t; + +midrangearea_t *midrangeareas; +int *clusterareas; +int numclusterareas; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AltRoutingFloodCluster_r(int areanum) +{ + int i, otherareanum; + aas_area_t *area; + aas_face_t *face; + + //add the current area to the areas of the current cluster + clusterareas[numclusterareas] = areanum; + numclusterareas++; + //remove the area from the mid range areas + midrangeareas[areanum].valid = qfalse; + //flood to other areas through the faces of this area + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])]; + //get the area at the other side of the face + if (face->frontarea == areanum) otherareanum = face->backarea; + else otherareanum = face->frontarea; + //if there is an area at the other side of this face + if (!otherareanum) continue; + //if the other area is not a midrange area + if (!midrangeareas[otherareanum].valid) continue; + // + AAS_AltRoutingFloodCluster_r(otherareanum); + } //end for +} //end of the function AAS_AltRoutingFloodCluster_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, + aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, + int type) +{ +#ifndef ENABLE_ALTROUTING + return 0; +#else + int i, j, bestareanum; + int numaltroutegoals, nummidrangeareas; + int starttime, goaltime, goaltraveltime; + float dist, bestdist; + vec3_t mid, dir; +#ifdef ALTROUTE_DEBUG + int startmillisecs; + + startmillisecs = Sys_MilliSeconds(); +#endif + + if (!startareanum || !goalareanum) + return 0; + //travel time towards the goal area + goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); + //clear the midrange areas + Com_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t)); + numaltroutegoals = 0; + // + nummidrangeareas = 0; + // + for (i = 1; i < aasworld.numareas; i++) + { + // + if (!(type & ALTROUTEGOAL_ALL)) + { + if (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL))) + { + if (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL))) + { + continue; + } //end if + } //end if + } //end if + //if the area has no reachabilities + if (!AAS_AreaReachability(i)) continue; + //tavel time from the area to the start area + starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags); + if (!starttime) continue; + //if the travel time from the start to the area is greater than the shortest goal travel time + if (starttime > (float) 1.1 * goaltraveltime) continue; + //travel time from the area to the goal area + goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags); + if (!goaltime) continue; + //if the travel time from the area to the goal is greater than the shortest goal travel time + if (goaltime > (float) 0.8 * goaltraveltime) continue; + //this is a mid range area + midrangeareas[i].valid = qtrue; + midrangeareas[i].starttime = starttime; + midrangeareas[i].goaltime = goaltime; + Log_Write("%d midrange area %d", nummidrangeareas, i); + nummidrangeareas++; + } //end for + // + for (i = 1; i < aasworld.numareas; i++) + { + if (!midrangeareas[i].valid) continue; + //get the areas in one cluster + numclusterareas = 0; + AAS_AltRoutingFloodCluster_r(i); + //now we've got a cluster with areas through which an alternative route could go + //get the 'center' of the cluster + VectorClear(mid); + for (j = 0; j < numclusterareas; j++) + { + VectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid); + } //end for + VectorScale(mid, 1.0 / numclusterareas, mid); + //get the area closest to the center of the cluster + bestdist = 999999; + bestareanum = 0; + for (j = 0; j < numclusterareas; j++) + { + VectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir); + dist = VectorLength(dir); + if (dist < bestdist) + { + bestdist = dist; + bestareanum = clusterareas[j]; + } //end if + } //end for + //now we've got an area for an alternative route + //FIXME: add alternative goal origin + VectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin); + altroutegoals[numaltroutegoals].areanum = bestareanum; + altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime; + altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime; + altroutegoals[numaltroutegoals].extratraveltime = + (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) - + goaltraveltime; + numaltroutegoals++; + // +#ifdef ALTROUTE_DEBUG + AAS_ShowAreaPolygons(bestareanum, 1, qtrue); +#endif + //don't return more than the maximum alternative route goals + if (numaltroutegoals >= maxaltroutegoals) break; + } //end for +#ifdef ALTROUTE_DEBUG + botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs); +#endif + return numaltroutegoals; +#endif +} //end of the function AAS_AlternativeRouteGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAlternativeRouting(void) +{ +#ifdef ENABLE_ALTROUTING + if (midrangeareas) FreeMemory(midrangeareas); + midrangeareas = (midrangearea_t *) GetMemory(aasworld.numareas * sizeof(midrangearea_t)); + if (clusterareas) FreeMemory(clusterareas); + clusterareas = (int *) GetMemory(aasworld.numareas * sizeof(int)); +#endif +} //end of the function AAS_InitAlternativeRouting +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShutdownAlternativeRouting(void) +{ +#ifdef ENABLE_ALTROUTING + if (midrangeareas) FreeMemory(midrangeareas); + midrangeareas = NULL; + if (clusterareas) FreeMemory(clusterareas); + clusterareas = NULL; + numclusterareas = 0; +#endif +} //end of the function AAS_ShutdownAlternativeRouting diff --git a/code/botlib/be_aas_routealt.h b/code/botlib/be_aas_routealt.h index da4ef67..a9e1657 100755 --- a/code/botlib/be_aas_routealt.h +++ b/code/botlib/be_aas_routealt.h @@ -1,40 +1,40 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_routealt.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_routealt.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-void AAS_InitAlternativeRouting(void);
-void AAS_ShutdownAlternativeRouting(void);
-#endif //AASINTERN
-
-
-int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,
- aas_altroutegoal_t *altroutegoals, int maxaltroutegoals,
- int type);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_routealt.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_routealt.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +void AAS_InitAlternativeRouting(void); +void AAS_ShutdownAlternativeRouting(void); +#endif //AASINTERN + + +int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, + aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, + int type); diff --git a/code/botlib/be_aas_sample.c b/code/botlib/be_aas_sample.c index c0adb47..68806eb 100755 --- a/code/botlib/be_aas_sample.c +++ b/code/botlib/be_aas_sample.c @@ -1,1394 +1,1394 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_sample.c
- *
- * desc: AAS environment sampling
- *
- * $Archive: /MissionPack/code/botlib/be_aas_sample.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#ifndef BSPC
-#include "l_libvar.h"
-#endif
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_interface.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-
-extern botlib_import_t botimport;
-
-//#define AAS_SAMPLE_DEBUG
-
-#define BBOX_NORMAL_EPSILON 0.001
-
-#define ON_EPSILON 0 //0.0005
-
-#define TRACEPLANE_EPSILON 0.125
-
-typedef struct aas_tracestack_s
-{
- vec3_t start; //start point of the piece of line to trace
- vec3_t end; //end point of the piece of line to trace
- int planenum; //last plane used as splitter
- int nodenum; //node found after splitting with planenum
-} aas_tracestack_t;
-
-int numaaslinks;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs)
-{
- int index;
- //bounding box size for each presence type
- vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}};
- vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}};
-
- if (presencetype == PRESENCE_NORMAL) index = 1;
- else if (presencetype == PRESENCE_CROUCH) index = 2;
- else
- {
- botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n");
- index = 2;
- } //end if
- VectorCopy(boxmins[index], mins);
- VectorCopy(boxmaxs[index], maxs);
-} //end of the function AAS_PresenceTypeBoundingBox
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitAASLinkHeap(void)
-{
- int i, max_aaslinks;
-
- max_aaslinks = aasworld.linkheapsize;
- //if there's no link heap present
- if (!aasworld.linkheap)
- {
-#ifdef BSPC
- max_aaslinks = 6144;
-#else
- max_aaslinks = (int) LibVarValue("max_aaslinks", "6144");
-#endif
- if (max_aaslinks < 0) max_aaslinks = 0;
- aasworld.linkheapsize = max_aaslinks;
- aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t));
- } //end if
- //link the links on the heap
- aasworld.linkheap[0].prev_ent = NULL;
- aasworld.linkheap[0].next_ent = &aasworld.linkheap[1];
- for (i = 1; i < max_aaslinks-1; i++)
- {
- aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1];
- aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1];
- } //end for
- aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2];
- aasworld.linkheap[max_aaslinks-1].next_ent = NULL;
- //pointer to the first free link
- aasworld.freelinks = &aasworld.linkheap[0];
- //
- numaaslinks = max_aaslinks;
-} //end of the function AAS_InitAASLinkHeap
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeAASLinkHeap(void)
-{
- if (aasworld.linkheap) FreeMemory(aasworld.linkheap);
- aasworld.linkheap = NULL;
- aasworld.linkheapsize = 0;
-} //end of the function AAS_FreeAASLinkHeap
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_link_t *AAS_AllocAASLink(void)
-{
- aas_link_t *link;
-
- link = aasworld.freelinks;
- if (!link)
- {
-#ifndef BSPC
- if (bot_developer)
-#endif
- {
- botimport.Print(PRT_FATAL, "empty aas link heap\n");
- } //end if
- return NULL;
- } //end if
- if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent;
- if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL;
- numaaslinks--;
- return link;
-} //end of the function AAS_AllocAASLink
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_DeAllocAASLink(aas_link_t *link)
-{
- if (aasworld.freelinks) aasworld.freelinks->prev_ent = link;
- link->prev_ent = NULL;
- link->next_ent = aasworld.freelinks;
- link->prev_area = NULL;
- link->next_area = NULL;
- aasworld.freelinks = link;
- numaaslinks++;
-} //end of the function AAS_DeAllocAASLink
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_InitAASLinkedEntities(void)
-{
- if (!aasworld.loaded) return;
- if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
- aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory(
- aasworld.numareas * sizeof(aas_link_t *));
-} //end of the function AAS_InitAASLinkedEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FreeAASLinkedEntities(void)
-{
- if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);
- aasworld.arealinkedentities = NULL;
-} //end of the function AAS_InitAASLinkedEntities
-//===========================================================================
-// returns the AAS area the point is in
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PointAreaNum(vec3_t point)
-{
- int nodenum;
- vec_t dist;
- aas_node_t *node;
- aas_plane_t *plane;
-
- if (!aasworld.loaded)
- {
- botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n");
- return 0;
- } //end if
-
- //start with node 1 because node zero is a dummy used for solid leafs
- nodenum = 1;
- while (nodenum > 0)
- {
-// botimport.Print(PRT_MESSAGE, "[%d]", nodenum);
-#ifdef AAS_SAMPLE_DEBUG
- if (nodenum >= aasworld.numnodes)
- {
- botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes);
- return 0;
- } //end if
-#endif //AAS_SAMPLE_DEBUG
- node = &aasworld.nodes[nodenum];
-#ifdef AAS_SAMPLE_DEBUG
- if (node->planenum < 0 || node->planenum >= aasworld.numplanes)
- {
- botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes);
- return 0;
- } //end if
-#endif //AAS_SAMPLE_DEBUG
- plane = &aasworld.planes[node->planenum];
- dist = DotProduct(point, plane->normal) - plane->dist;
- if (dist > 0) nodenum = node->children[0];
- else nodenum = node->children[1];
- } //end while
- if (!nodenum)
- {
-#ifdef AAS_SAMPLE_DEBUG
- botimport.Print(PRT_MESSAGE, "in solid\n");
-#endif //AAS_SAMPLE_DEBUG
- return 0;
- } //end if
- return -nodenum;
-} //end of the function AAS_PointAreaNum
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PointReachabilityAreaIndex( vec3_t origin )
-{
- int areanum, cluster, i, index;
-
- if (!aasworld.initialized)
- return 0;
-
- if ( !origin )
- {
- index = 0;
- for (i = 0; i < aasworld.numclusters; i++)
- {
- index += aasworld.clusters[i].numreachabilityareas;
- } //end for
- return index;
- } //end if
-
- areanum = AAS_PointAreaNum( origin );
- if ( !areanum || !AAS_AreaReachability(areanum) )
- return 0;
- cluster = aasworld.areasettings[areanum].cluster;
- areanum = aasworld.areasettings[areanum].clusterareanum;
- if (cluster < 0)
- {
- cluster = aasworld.portals[-cluster].frontcluster;
- areanum = aasworld.portals[-cluster].clusterareanum[0];
- } //end if
-
- index = 0;
- for (i = 0; i < cluster; i++)
- {
- index += aasworld.clusters[i].numreachabilityareas;
- } //end for
- index += areanum;
- return index;
-} //end of the function AAS_PointReachabilityAreaIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaCluster(int areanum)
-{
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n");
- return 0;
- } //end if
- return aasworld.areasettings[areanum].cluster;
-} //end of the function AAS_AreaCluster
-//===========================================================================
-// returns the presence types of the given area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaPresenceType(int areanum)
-{
- if (!aasworld.loaded) return 0;
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n");
- return 0;
- } //end if
- return aasworld.areasettings[areanum].presencetype;
-} //end of the function AAS_AreaPresenceType
-//===========================================================================
-// returns the presence type at the given point
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_PointPresenceType(vec3_t point)
-{
- int areanum;
-
- if (!aasworld.loaded) return 0;
-
- areanum = AAS_PointAreaNum(point);
- if (!areanum) return PRESENCE_NONE;
- return aasworld.areasettings[areanum].presencetype;
-} //end of the function AAS_PointPresenceType
-//===========================================================================
-// calculates the minimum distance between the origin of the box and the
-// given plane when both will collide on the given side of the plane
-//
-// normal = normal vector of plane to calculate distance from
-// mins = minimums of box relative to origin
-// maxs = maximums of box relative to origin
-// side = side of the plane we want to calculate the distance from
-// 0 normal vector side
-// 1 not normal vector side
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side)
-{
- vec3_t v1, v2;
- int i;
-
- //swap maxs and mins when on the other side of the plane
- if (side)
- {
- //get a point of the box that would be one of the first
- //to collide with the plane
- for (i = 0; i < 3; i++)
- {
- if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
- else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i];
- else v1[i] = 0;
- } //end for
- } //end if
- else
- {
- //get a point of the box that would be one of the first
- //to collide with the plane
- for (i = 0; i < 3; i++)
- {
- if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i];
- else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
- else v1[i] = 0;
- } //end for
- } //end else
- //
- VectorCopy(normal, v2);
- VectorInverse(v2);
-// VectorNegate(normal, v2);
- return DotProduct(v1, v2);
-} //end of the function AAS_BoxOriginDistanceFromPlane
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end,
- int presencetype, int passent, aas_trace_t *trace)
-{
- int collision;
- vec3_t boxmins, boxmaxs;
- aas_link_t *link;
- bsp_trace_t bsptrace;
-
- AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs);
-
- Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy
- //assume no collision
- bsptrace.fraction = 1;
- collision = qfalse;
- for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent)
- {
- //ignore the pass entity
- if (link->entnum == passent) continue;
- //
- if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end,
- CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace))
- {
- collision = qtrue;
- } //end if
- } //end for
- if (collision)
- {
- trace->startsolid = bsptrace.startsolid;
- trace->ent = bsptrace.ent;
- VectorCopy(bsptrace.endpos, trace->endpos);
- trace->area = 0;
- trace->planenum = 0;
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function AAS_AreaEntityCollision
-//===========================================================================
-// recursive subdivision of the line by the BSP tree.
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype,
- int passent)
-{
- int side, nodenum, tmpplanenum;
- float front, back, frac;
- vec3_t cur_start, cur_end, cur_mid, v1, v2;
- aas_tracestack_t tracestack[127];
- aas_tracestack_t *tstack_p;
- aas_node_t *aasnode;
- aas_plane_t *plane;
- aas_trace_t trace;
-
- //clear the trace structure
- Com_Memset(&trace, 0, sizeof(aas_trace_t));
-
- if (!aasworld.loaded) return trace;
-
- tstack_p = tracestack;
- //we start with the whole line on the stack
- VectorCopy(start, tstack_p->start);
- VectorCopy(end, tstack_p->end);
- tstack_p->planenum = 0;
- //start with node 1 because node zero is a dummy for a solid leaf
- tstack_p->nodenum = 1; //starting at the root of the tree
- tstack_p++;
-
- while (1)
- {
- //pop up the stack
- tstack_p--;
- //if the trace stack is empty (ended up with a piece of the
- //line to be traced in an area)
- if (tstack_p < tracestack)
- {
- tstack_p++;
- //nothing was hit
- trace.startsolid = qfalse;
- trace.fraction = 1.0;
- //endpos is the end of the line
- VectorCopy(end, trace.endpos);
- //nothing hit
- trace.ent = 0;
- trace.area = 0;
- trace.planenum = 0;
- return trace;
- } //end if
- //number of the current node to test the line against
- nodenum = tstack_p->nodenum;
- //if it is an area
- if (nodenum < 0)
- {
-#ifdef AAS_SAMPLE_DEBUG
- if (-nodenum > aasworld.numareasettings)
- {
- botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n");
- return trace;
- } //end if
-#endif //AAS_SAMPLE_DEBUG
- //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
- //if can't enter the area because it hasn't got the right presence type
- if (!(aasworld.areasettings[-nodenum].presencetype & presencetype))
- {
- //if the start point is still the initial start point
- //NOTE: no need for epsilons because the points will be
- //exactly the same when they're both the start point
- if (tstack_p->start[0] == start[0] &&
- tstack_p->start[1] == start[1] &&
- tstack_p->start[2] == start[2])
- {
- trace.startsolid = qtrue;
- trace.fraction = 0.0;
- VectorClear(v1);
- } //end if
- else
- {
- trace.startsolid = qfalse;
- VectorSubtract(end, start, v1);
- VectorSubtract(tstack_p->start, start, v2);
- trace.fraction = VectorLength(v2) / VectorNormalize(v1);
- VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
- } //end else
- VectorCopy(tstack_p->start, trace.endpos);
- trace.ent = 0;
- trace.area = -nodenum;
-// VectorSubtract(end, start, v1);
- trace.planenum = tstack_p->planenum;
- //always take the plane with normal facing towards the trace start
- plane = &aasworld.planes[trace.planenum];
- if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
- return trace;
- } //end if
- else
- {
- if (passent >= 0)
- {
- if (AAS_AreaEntityCollision(-nodenum, tstack_p->start,
- tstack_p->end, presencetype, passent,
- &trace))
- {
- if (!trace.startsolid)
- {
- VectorSubtract(end, start, v1);
- VectorSubtract(trace.endpos, start, v2);
- trace.fraction = VectorLength(v2) / VectorLength(v1);
- } //end if
- return trace;
- } //end if
- } //end if
- } //end else
- trace.lastarea = -nodenum;
- continue;
- } //end if
- //if it is a solid leaf
- if (!nodenum)
- {
- //if the start point is still the initial start point
- //NOTE: no need for epsilons because the points will be
- //exactly the same when they're both the start point
- if (tstack_p->start[0] == start[0] &&
- tstack_p->start[1] == start[1] &&
- tstack_p->start[2] == start[2])
- {
- trace.startsolid = qtrue;
- trace.fraction = 0.0;
- VectorClear(v1);
- } //end if
- else
- {
- trace.startsolid = qfalse;
- VectorSubtract(end, start, v1);
- VectorSubtract(tstack_p->start, start, v2);
- trace.fraction = VectorLength(v2) / VectorNormalize(v1);
- VectorMA(tstack_p->start, -0.125, v1, tstack_p->start);
- } //end else
- VectorCopy(tstack_p->start, trace.endpos);
- trace.ent = 0;
- trace.area = 0; //hit solid leaf
-// VectorSubtract(end, start, v1);
- trace.planenum = tstack_p->planenum;
- //always take the plane with normal facing towards the trace start
- plane = &aasworld.planes[trace.planenum];
- if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;
- return trace;
- } //end if
-#ifdef AAS_SAMPLE_DEBUG
- if (nodenum > aasworld.numnodes)
- {
- botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n");
- return trace;
- } //end if
-#endif //AAS_SAMPLE_DEBUG
- //the node to test against
- aasnode = &aasworld.nodes[nodenum];
- //start point of current line to test against node
- VectorCopy(tstack_p->start, cur_start);
- //end point of the current line to test against node
- VectorCopy(tstack_p->end, cur_end);
- //the current node plane
- plane = &aasworld.planes[aasnode->planenum];
-
- switch(plane->type)
- {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!!
- //check for axial planes
- case PLANE_X:
- {
- front = cur_start[0] - plane->dist;
- back = cur_end[0] - plane->dist;
- break;
- } //end case
- case PLANE_Y:
- {
- front = cur_start[1] - plane->dist;
- back = cur_end[1] - plane->dist;
- break;
- } //end case
- case PLANE_Z:
- {
- front = cur_start[2] - plane->dist;
- back = cur_end[2] - plane->dist;
- break;
- } //end case*/
- default: //gee it's not an axial plane
- {
- front = DotProduct(cur_start, plane->normal) - plane->dist;
- back = DotProduct(cur_end, plane->normal) - plane->dist;
- break;
- } //end default
- } //end switch
- // bk010221 - old location of FPE hack and divide by zero expression
- //if the whole to be traced line is totally at the front of this node
- //only go down the tree with the front child
- if ((front >= -ON_EPSILON && back >= -ON_EPSILON))
- {
- //keep the current start and end point on the stack
- //and go down the tree with the front child
- tstack_p->nodenum = aasnode->children[0];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
- return trace;
- } //end if
- } //end if
- //if the whole to be traced line is totally at the back of this node
- //only go down the tree with the back child
- else if ((front < ON_EPSILON && back < ON_EPSILON))
- {
- //keep the current start and end point on the stack
- //and go down the tree with the back child
- tstack_p->nodenum = aasnode->children[1];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
- return trace;
- } //end if
- } //end if
- //go down the tree both at the front and back of the node
- else
- {
- tmpplanenum = tstack_p->planenum;
- // bk010221 - new location of divide by zero (see above)
- if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE
- //calculate the hitpoint with the node (split point of the line)
- //put the crosspoint TRACEPLANE_EPSILON pixels on the near side
- if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back);
- else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221
- //
- if (frac < 0)
- frac = 0.001f; //0
- else if (frac > 1)
- frac = 0.999f; //1
- //frac = front / (front-back);
- //
- cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
- cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
- cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
-
-// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
- //side the front part of the line is on
- side = front < 0;
- //first put the end part of the line on the stack (back side)
- VectorCopy(cur_mid, tstack_p->start);
- //not necesary to store because still on stack
- //VectorCopy(cur_end, tstack_p->end);
- tstack_p->planenum = aasnode->planenum;
- tstack_p->nodenum = aasnode->children[!side];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
- return trace;
- } //end if
- //now put the part near the start of the line on the stack so we will
- //continue with thats part first. This way we'll find the first
- //hit of the bbox
- VectorCopy(cur_start, tstack_p->start);
- VectorCopy(cur_mid, tstack_p->end);
- tstack_p->planenum = tmpplanenum;
- tstack_p->nodenum = aasnode->children[side];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n");
- return trace;
- } //end if
- } //end else
- } //end while
-// return trace;
-} //end of the function AAS_TraceClientBBox
-//===========================================================================
-// recursive subdivision of the line by the BSP tree.
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas)
-{
- int side, nodenum, tmpplanenum;
- int numareas;
- float front, back, frac;
- vec3_t cur_start, cur_end, cur_mid;
- aas_tracestack_t tracestack[127];
- aas_tracestack_t *tstack_p;
- aas_node_t *aasnode;
- aas_plane_t *plane;
-
- numareas = 0;
- areas[0] = 0;
- if (!aasworld.loaded) return numareas;
-
- tstack_p = tracestack;
- //we start with the whole line on the stack
- VectorCopy(start, tstack_p->start);
- VectorCopy(end, tstack_p->end);
- tstack_p->planenum = 0;
- //start with node 1 because node zero is a dummy for a solid leaf
- tstack_p->nodenum = 1; //starting at the root of the tree
- tstack_p++;
-
- while (1)
- {
- //pop up the stack
- tstack_p--;
- //if the trace stack is empty (ended up with a piece of the
- //line to be traced in an area)
- if (tstack_p < tracestack)
- {
- return numareas;
- } //end if
- //number of the current node to test the line against
- nodenum = tstack_p->nodenum;
- //if it is an area
- if (nodenum < 0)
- {
-#ifdef AAS_SAMPLE_DEBUG
- if (-nodenum > aasworld.numareasettings)
- {
- botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum);
- return numareas;
- } //end if
-#endif //AAS_SAMPLE_DEBUG
- //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start));
- areas[numareas] = -nodenum;
- if (points) VectorCopy(tstack_p->start, points[numareas]);
- numareas++;
- if (numareas >= maxareas) return numareas;
- continue;
- } //end if
- //if it is a solid leaf
- if (!nodenum)
- {
- continue;
- } //end if
-#ifdef AAS_SAMPLE_DEBUG
- if (nodenum > aasworld.numnodes)
- {
- botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n");
- return numareas;
- } //end if
-#endif //AAS_SAMPLE_DEBUG
- //the node to test against
- aasnode = &aasworld.nodes[nodenum];
- //start point of current line to test against node
- VectorCopy(tstack_p->start, cur_start);
- //end point of the current line to test against node
- VectorCopy(tstack_p->end, cur_end);
- //the current node plane
- plane = &aasworld.planes[aasnode->planenum];
-
- switch(plane->type)
- {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!!
- //check for axial planes
- case PLANE_X:
- {
- front = cur_start[0] - plane->dist;
- back = cur_end[0] - plane->dist;
- break;
- } //end case
- case PLANE_Y:
- {
- front = cur_start[1] - plane->dist;
- back = cur_end[1] - plane->dist;
- break;
- } //end case
- case PLANE_Z:
- {
- front = cur_start[2] - plane->dist;
- back = cur_end[2] - plane->dist;
- break;
- } //end case*/
- default: //gee it's not an axial plane
- {
- front = DotProduct(cur_start, plane->normal) - plane->dist;
- back = DotProduct(cur_end, plane->normal) - plane->dist;
- break;
- } //end default
- } //end switch
-
- //if the whole to be traced line is totally at the front of this node
- //only go down the tree with the front child
- if (front > 0 && back > 0)
- {
- //keep the current start and end point on the stack
- //and go down the tree with the front child
- tstack_p->nodenum = aasnode->children[0];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
- return numareas;
- } //end if
- } //end if
- //if the whole to be traced line is totally at the back of this node
- //only go down the tree with the back child
- else if (front <= 0 && back <= 0)
- {
- //keep the current start and end point on the stack
- //and go down the tree with the back child
- tstack_p->nodenum = aasnode->children[1];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
- return numareas;
- } //end if
- } //end if
- //go down the tree both at the front and back of the node
- else
- {
- tmpplanenum = tstack_p->planenum;
- //calculate the hitpoint with the node (split point of the line)
- //put the crosspoint TRACEPLANE_EPSILON pixels on the near side
- if (front < 0) frac = (front)/(front-back);
- else frac = (front)/(front-back);
- if (frac < 0) frac = 0;
- else if (frac > 1) frac = 1;
- //frac = front / (front-back);
- //
- cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;
- cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;
- cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;
-
-// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);
- //side the front part of the line is on
- side = front < 0;
- //first put the end part of the line on the stack (back side)
- VectorCopy(cur_mid, tstack_p->start);
- //not necesary to store because still on stack
- //VectorCopy(cur_end, tstack_p->end);
- tstack_p->planenum = aasnode->planenum;
- tstack_p->nodenum = aasnode->children[!side];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
- return numareas;
- } //end if
- //now put the part near the start of the line on the stack so we will
- //continue with thats part first. This way we'll find the first
- //hit of the bbox
- VectorCopy(cur_start, tstack_p->start);
- VectorCopy(cur_mid, tstack_p->end);
- tstack_p->planenum = tmpplanenum;
- tstack_p->nodenum = aasnode->children[side];
- tstack_p++;
- if (tstack_p >= &tracestack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n");
- return numareas;
- } //end if
- } //end else
- } //end while
-// return numareas;
-} //end of the function AAS_TraceAreas
-//===========================================================================
-// a simple cross product
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-// void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res)
-#define AAS_OrthogonalToVectors(v1, v2, res) \
- (res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\
- (res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\
- (res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]);
-//===========================================================================
-// tests if the given point is within the face boundaries
-//
-// Parameter: face : face to test if the point is in it
-// pnormal : normal of the plane to use for the face
-// point : point to test if inside face boundaries
-// Returns: qtrue if the point is within the face boundaries
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon)
-{
- int i, firstvertex, edgenum;
- vec3_t v0;
- vec3_t edgevec, pointvec, sepnormal;
- aas_edge_t *edge;
-#ifdef AAS_SAMPLE_DEBUG
- int lastvertex = 0;
-#endif //AAS_SAMPLE_DEBUG
-
- if (!aasworld.loaded) return qfalse;
-
- for (i = 0; i < face->numedges; i++)
- {
- edgenum = aasworld.edgeindex[face->firstedge + i];
- edge = &aasworld.edges[abs(edgenum)];
- //get the first vertex of the edge
- firstvertex = edgenum < 0;
- VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0);
- //edge vector
- VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec);
- //
-#ifdef AAS_SAMPLE_DEBUG
- if (lastvertex && lastvertex != edge->v[firstvertex])
- {
- botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n");
- } //end if
- lastvertex = edge->v[!firstvertex];
-#endif //AAS_SAMPLE_DEBUG
- //vector from first edge point to point possible in face
- VectorSubtract(point, v0, pointvec);
- //get a vector pointing inside the face orthogonal to both the
- //edge vector and the normal vector of the plane the face is in
- //this vector defines a plane through the origin (first vertex of
- //edge) and through both the edge vector and the normal vector
- //of the plane
- AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal);
- //check on wich side of the above plane the point is
- //this is done by checking the sign of the dot product of the
- //vector orthogonal vector from above and the vector from the
- //origin (first vertex of edge) to the point
- //if the dotproduct is smaller than zero the point is outside the face
- if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
- } //end for
- return qtrue;
-} //end of the function AAS_InsideFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon)
-{
- int i, firstvertex, edgenum;
- vec_t *v1, *v2;
- vec3_t edgevec, pointvec, sepnormal;
- aas_edge_t *edge;
- aas_plane_t *plane;
- aas_face_t *face;
-
- if (!aasworld.loaded) return qfalse;
-
- face = &aasworld.faces[facenum];
- plane = &aasworld.planes[face->planenum];
- //
- for (i = 0; i < face->numedges; i++)
- {
- edgenum = aasworld.edgeindex[face->firstedge + i];
- edge = &aasworld.edges[abs(edgenum)];
- //get the first vertex of the edge
- firstvertex = edgenum < 0;
- v1 = aasworld.vertexes[edge->v[firstvertex]];
- v2 = aasworld.vertexes[edge->v[!firstvertex]];
- //edge vector
- VectorSubtract(v2, v1, edgevec);
- //vector from first edge point to point possible in face
- VectorSubtract(point, v1, pointvec);
- //
- CrossProduct(edgevec, plane->normal, sepnormal);
- //
- if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;
- } //end for
- return qtrue;
-} //end of the function AAS_PointInsideFace
-//===========================================================================
-// returns the ground face the given point is above in the given area
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point)
-{
- int i, facenum;
- vec3_t up = {0, 0, 1};
- vec3_t normal;
- aas_area_t *area;
- aas_face_t *face;
-
- if (!aasworld.loaded) return NULL;
-
- area = &aasworld.areas[areanum];
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = aasworld.faceindex[area->firstface + i];
- face = &aasworld.faces[abs(facenum)];
- //if this is a ground face
- if (face->faceflags & FACE_GROUND)
- {
- //get the up or down normal
- if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal);
- else VectorCopy(up, normal);
- //check if the point is in the face
- if (AAS_InsideFace(face, normal, point, 0.01f)) return face;
- } //end if
- } //end for
- return NULL;
-} //end of the function AAS_AreaGroundFace
-//===========================================================================
-// returns the face the trace end position is situated in
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_FacePlane(int facenum, vec3_t normal, float *dist)
-{
- aas_plane_t *plane;
-
- plane = &aasworld.planes[aasworld.faces[facenum].planenum];
- VectorCopy(plane->normal, normal);
- *dist = plane->dist;
-} //end of the function AAS_FacePlane
-//===========================================================================
-// returns the face the trace end position is situated in
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_face_t *AAS_TraceEndFace(aas_trace_t *trace)
-{
- int i, facenum;
- aas_area_t *area;
- aas_face_t *face, *firstface = NULL;
-
- if (!aasworld.loaded) return NULL;
-
- //if started in solid no face was hit
- if (trace->startsolid) return NULL;
- //trace->lastarea is the last area the trace was in
- area = &aasworld.areas[trace->lastarea];
- //check which face the trace.endpos was in
- for (i = 0; i < area->numfaces; i++)
- {
- facenum = aasworld.faceindex[area->firstface + i];
- face = &aasworld.faces[abs(facenum)];
- //if the face is in the same plane as the trace end point
- if ((face->planenum & ~1) == (trace->planenum & ~1))
- {
- //firstface is used for optimization, if theres only one
- //face in the plane then it has to be the good one
- //if there are more faces in the same plane then always
- //check the one with the fewest edges first
-/* if (firstface)
- {
- if (firstface->numedges < face->numedges)
- {
- if (AAS_InsideFace(firstface,
- aasworld.planes[face->planenum].normal, trace->endpos))
- {
- return firstface;
- } //end if
- firstface = face;
- } //end if
- else
- {
- if (AAS_InsideFace(face,
- aasworld.planes[face->planenum].normal, trace->endpos))
- {
- return face;
- } //end if
- } //end else
- } //end if
- else
- {
- firstface = face;
- } //end else*/
- if (AAS_InsideFace(face,
- aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face;
- } //end if
- } //end for
- return firstface;
-} //end of the function AAS_TraceEndFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
-{
- int i, sides;
- float dist1, dist2;
- vec3_t corners[2];
-
- for (i = 0; i < 3; i++)
- {
- if (p->normal[i] < 0)
- {
- corners[0][i] = absmins[i];
- corners[1][i] = absmaxs[i];
- } //end if
- else
- {
- corners[1][i] = absmins[i];
- corners[0][i] = absmaxs[i];
- } //end else
- } //end for
- dist1 = DotProduct(p->normal, corners[0]) - p->dist;
- dist2 = DotProduct(p->normal, corners[1]) - p->dist;
- sides = 0;
- if (dist1 >= 0) sides = 1;
- if (dist2 < 0) sides |= 2;
-
- return sides;
-} //end of the function AAS_BoxOnPlaneSide2
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-//int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)
-#define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\
- ( (p)->type < 3) ?\
- (\
- ( (p)->dist <= (absmins)[(p)->type]) ?\
- (\
- 1\
- )\
- :\
- (\
- ( (p)->dist >= (absmaxs)[(p)->type]) ?\
- (\
- 2\
- )\
- :\
- (\
- 3\
- )\
- )\
- )\
- :\
- (\
- AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\
- )\
-) //end of the function AAS_BoxOnPlaneSide
-//===========================================================================
-// remove the links to this entity from all areas
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_UnlinkFromAreas(aas_link_t *areas)
-{
- aas_link_t *link, *nextlink;
-
- for (link = areas; link; link = nextlink)
- {
- //next area the entity is linked in
- nextlink = link->next_area;
- //remove the entity from the linked list of this area
- if (link->prev_ent) link->prev_ent->next_ent = link->next_ent;
- else aasworld.arealinkedentities[link->areanum] = link->next_ent;
- if (link->next_ent) link->next_ent->prev_ent = link->prev_ent;
- //deallocate the link structure
- AAS_DeAllocAASLink(link);
- } //end for
-} //end of the function AAS_UnlinkFromAreas
-//===========================================================================
-// link the entity to the areas the bounding box is totally or partly
-// situated in. This is done with recursion down the tree using the
-// bounding box to test for plane sides
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-
-typedef struct
-{
- int nodenum; //node found after splitting
-} aas_linkstack_t;
-
-aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum)
-{
- int side, nodenum;
- aas_linkstack_t linkstack[128];
- aas_linkstack_t *lstack_p;
- aas_node_t *aasnode;
- aas_plane_t *plane;
- aas_link_t *link, *areas;
-
- if (!aasworld.loaded)
- {
- botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n");
- return NULL;
- } //end if
-
- areas = NULL;
- //
- lstack_p = linkstack;
- //we start with the whole line on the stack
- //start with node 1 because node zero is a dummy used for solid leafs
- lstack_p->nodenum = 1; //starting at the root of the tree
- lstack_p++;
-
- while (1)
- {
- //pop up the stack
- lstack_p--;
- //if the trace stack is empty (ended up with a piece of the
- //line to be traced in an area)
- if (lstack_p < linkstack) break;
- //number of the current node to test the line against
- nodenum = lstack_p->nodenum;
- //if it is an area
- if (nodenum < 0)
- {
- //NOTE: the entity might have already been linked into this area
- // because several node children can point to the same area
- for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent)
- {
- if (link->entnum == entnum) break;
- } //end for
- if (link) continue;
- //
- link = AAS_AllocAASLink();
- if (!link) return areas;
- link->entnum = entnum;
- link->areanum = -nodenum;
- //put the link into the double linked area list of the entity
- link->prev_area = NULL;
- link->next_area = areas;
- if (areas) areas->prev_area = link;
- areas = link;
- //put the link into the double linked entity list of the area
- link->prev_ent = NULL;
- link->next_ent = aasworld.arealinkedentities[-nodenum];
- if (aasworld.arealinkedentities[-nodenum])
- aasworld.arealinkedentities[-nodenum]->prev_ent = link;
- aasworld.arealinkedentities[-nodenum] = link;
- //
- continue;
- } //end if
- //if solid leaf
- if (!nodenum) continue;
- //the node to test against
- aasnode = &aasworld.nodes[nodenum];
- //the current node plane
- plane = &aasworld.planes[aasnode->planenum];
- //get the side(s) the box is situated relative to the plane
- side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane);
- //if on the front side of the node
- if (side & 1)
- {
- lstack_p->nodenum = aasnode->children[0];
- lstack_p++;
- } //end if
- if (lstack_p >= &linkstack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n");
- break;
- } //end if
- //if on the back side of the node
- if (side & 2)
- {
- lstack_p->nodenum = aasnode->children[1];
- lstack_p++;
- } //end if
- if (lstack_p >= &linkstack[127])
- {
- botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n");
- break;
- } //end if
- } //end while
- return areas;
-} //end of the function AAS_AASLinkEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype)
-{
- vec3_t mins, maxs;
- vec3_t newabsmins, newabsmaxs;
-
- AAS_PresenceTypeBoundingBox(presencetype, mins, maxs);
- VectorSubtract(absmins, maxs, newabsmins);
- VectorSubtract(absmaxs, mins, newabsmaxs);
- //relink the entity
- return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum);
-} //end of the function AAS_LinkEntityClientBBox
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas)
-{
- aas_link_t *linkedareas, *link;
- int num;
-
- linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1);
- num = 0;
- for (link = linkedareas; link; link = link->next_area)
- {
- areas[num] = link->areanum;
- num++;
- if (num >= maxareas)
- break;
- } //end for
- AAS_UnlinkFromAreas(linkedareas);
- return num;
-} //end of the function AAS_BBoxAreas
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AAS_AreaInfo( int areanum, aas_areainfo_t *info )
-{
- aas_areasettings_t *settings;
- if (!info)
- return 0;
- if (areanum <= 0 || areanum >= aasworld.numareas)
- {
- botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum);
- return 0;
- } //end if
- settings = &aasworld.areasettings[areanum];
- info->cluster = settings->cluster;
- info->contents = settings->contents;
- info->flags = settings->areaflags;
- info->presencetype = settings->presencetype;
- VectorCopy(aasworld.areas[areanum].mins, info->mins);
- VectorCopy(aasworld.areas[areanum].maxs, info->maxs);
- VectorCopy(aasworld.areas[areanum].center, info->center);
- return sizeof(aas_areainfo_t);
-} //end of the function AAS_AreaInfo
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-aas_plane_t *AAS_PlaneFromNum(int planenum)
-{
- if (!aasworld.loaded) return 0;
-
- return &aasworld.planes[planenum];
-} //end of the function AAS_PlaneFromNum
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_sample.c + * + * desc: AAS environment sampling + * + * $Archive: /MissionPack/code/botlib/be_aas_sample.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#ifndef BSPC +#include "l_libvar.h" +#endif +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_interface.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" + +extern botlib_import_t botimport; + +//#define AAS_SAMPLE_DEBUG + +#define BBOX_NORMAL_EPSILON 0.001 + +#define ON_EPSILON 0 //0.0005 + +#define TRACEPLANE_EPSILON 0.125 + +typedef struct aas_tracestack_s +{ + vec3_t start; //start point of the piece of line to trace + vec3_t end; //end point of the piece of line to trace + int planenum; //last plane used as splitter + int nodenum; //node found after splitting with planenum +} aas_tracestack_t; + +int numaaslinks; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs) +{ + int index; + //bounding box size for each presence type + vec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}}; + vec3_t boxmaxs[3] = {{0, 0, 0}, { 15, 15, 32}, { 15, 15, 8}}; + + if (presencetype == PRESENCE_NORMAL) index = 1; + else if (presencetype == PRESENCE_CROUCH) index = 2; + else + { + botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n"); + index = 2; + } //end if + VectorCopy(boxmins[index], mins); + VectorCopy(boxmaxs[index], maxs); +} //end of the function AAS_PresenceTypeBoundingBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAASLinkHeap(void) +{ + int i, max_aaslinks; + + max_aaslinks = aasworld.linkheapsize; + //if there's no link heap present + if (!aasworld.linkheap) + { +#ifdef BSPC + max_aaslinks = 6144; +#else + max_aaslinks = (int) LibVarValue("max_aaslinks", "6144"); +#endif + if (max_aaslinks < 0) max_aaslinks = 0; + aasworld.linkheapsize = max_aaslinks; + aasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t)); + } //end if + //link the links on the heap + aasworld.linkheap[0].prev_ent = NULL; + aasworld.linkheap[0].next_ent = &aasworld.linkheap[1]; + for (i = 1; i < max_aaslinks-1; i++) + { + aasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1]; + aasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1]; + } //end for + aasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2]; + aasworld.linkheap[max_aaslinks-1].next_ent = NULL; + //pointer to the first free link + aasworld.freelinks = &aasworld.linkheap[0]; + // + numaaslinks = max_aaslinks; +} //end of the function AAS_InitAASLinkHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAASLinkHeap(void) +{ + if (aasworld.linkheap) FreeMemory(aasworld.linkheap); + aasworld.linkheap = NULL; + aasworld.linkheapsize = 0; +} //end of the function AAS_FreeAASLinkHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_link_t *AAS_AllocAASLink(void) +{ + aas_link_t *link; + + link = aasworld.freelinks; + if (!link) + { +#ifndef BSPC + if (bot_developer) +#endif + { + botimport.Print(PRT_FATAL, "empty aas link heap\n"); + } //end if + return NULL; + } //end if + if (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent; + if (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL; + numaaslinks--; + return link; +} //end of the function AAS_AllocAASLink +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DeAllocAASLink(aas_link_t *link) +{ + if (aasworld.freelinks) aasworld.freelinks->prev_ent = link; + link->prev_ent = NULL; + link->next_ent = aasworld.freelinks; + link->prev_area = NULL; + link->next_area = NULL; + aasworld.freelinks = link; + numaaslinks++; +} //end of the function AAS_DeAllocAASLink +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitAASLinkedEntities(void) +{ + if (!aasworld.loaded) return; + if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); + aasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory( + aasworld.numareas * sizeof(aas_link_t *)); +} //end of the function AAS_InitAASLinkedEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeAASLinkedEntities(void) +{ + if (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities); + aasworld.arealinkedentities = NULL; +} //end of the function AAS_InitAASLinkedEntities +//=========================================================================== +// returns the AAS area the point is in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointAreaNum(vec3_t point) +{ + int nodenum; + vec_t dist; + aas_node_t *node; + aas_plane_t *plane; + + if (!aasworld.loaded) + { + botimport.Print(PRT_ERROR, "AAS_PointAreaNum: aas not loaded\n"); + return 0; + } //end if + + //start with node 1 because node zero is a dummy used for solid leafs + nodenum = 1; + while (nodenum > 0) + { +// botimport.Print(PRT_MESSAGE, "[%d]", nodenum); +#ifdef AAS_SAMPLE_DEBUG + if (nodenum >= aasworld.numnodes) + { + botimport.Print(PRT_ERROR, "nodenum = %d >= aasworld.numnodes = %d\n", nodenum, aasworld.numnodes); + return 0; + } //end if +#endif //AAS_SAMPLE_DEBUG + node = &aasworld.nodes[nodenum]; +#ifdef AAS_SAMPLE_DEBUG + if (node->planenum < 0 || node->planenum >= aasworld.numplanes) + { + botimport.Print(PRT_ERROR, "node->planenum = %d >= aasworld.numplanes = %d\n", node->planenum, aasworld.numplanes); + return 0; + } //end if +#endif //AAS_SAMPLE_DEBUG + plane = &aasworld.planes[node->planenum]; + dist = DotProduct(point, plane->normal) - plane->dist; + if (dist > 0) nodenum = node->children[0]; + else nodenum = node->children[1]; + } //end while + if (!nodenum) + { +#ifdef AAS_SAMPLE_DEBUG + botimport.Print(PRT_MESSAGE, "in solid\n"); +#endif //AAS_SAMPLE_DEBUG + return 0; + } //end if + return -nodenum; +} //end of the function AAS_PointAreaNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointReachabilityAreaIndex( vec3_t origin ) +{ + int areanum, cluster, i, index; + + if (!aasworld.initialized) + return 0; + + if ( !origin ) + { + index = 0; + for (i = 0; i < aasworld.numclusters; i++) + { + index += aasworld.clusters[i].numreachabilityareas; + } //end for + return index; + } //end if + + areanum = AAS_PointAreaNum( origin ); + if ( !areanum || !AAS_AreaReachability(areanum) ) + return 0; + cluster = aasworld.areasettings[areanum].cluster; + areanum = aasworld.areasettings[areanum].clusterareanum; + if (cluster < 0) + { + cluster = aasworld.portals[-cluster].frontcluster; + areanum = aasworld.portals[-cluster].clusterareanum[0]; + } //end if + + index = 0; + for (i = 0; i < cluster; i++) + { + index += aasworld.clusters[i].numreachabilityareas; + } //end for + index += areanum; + return index; +} //end of the function AAS_PointReachabilityAreaIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaCluster(int areanum) +{ + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaCluster: invalid area number\n"); + return 0; + } //end if + return aasworld.areasettings[areanum].cluster; +} //end of the function AAS_AreaCluster +//=========================================================================== +// returns the presence types of the given area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaPresenceType(int areanum) +{ + if (!aasworld.loaded) return 0; + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaPresenceType: invalid area number\n"); + return 0; + } //end if + return aasworld.areasettings[areanum].presencetype; +} //end of the function AAS_AreaPresenceType +//=========================================================================== +// returns the presence type at the given point +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PointPresenceType(vec3_t point) +{ + int areanum; + + if (!aasworld.loaded) return 0; + + areanum = AAS_PointAreaNum(point); + if (!areanum) return PRESENCE_NONE; + return aasworld.areasettings[areanum].presencetype; +} //end of the function AAS_PointPresenceType +//=========================================================================== +// calculates the minimum distance between the origin of the box and the +// given plane when both will collide on the given side of the plane +// +// normal = normal vector of plane to calculate distance from +// mins = minimums of box relative to origin +// maxs = maximums of box relative to origin +// side = side of the plane we want to calculate the distance from +// 0 normal vector side +// 1 not normal vector side +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side) +{ + vec3_t v1, v2; + int i; + + //swap maxs and mins when on the other side of the plane + if (side) + { + //get a point of the box that would be one of the first + //to collide with the plane + for (i = 0; i < 3; i++) + { + if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; + else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i]; + else v1[i] = 0; + } //end for + } //end if + else + { + //get a point of the box that would be one of the first + //to collide with the plane + for (i = 0; i < 3; i++) + { + if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i]; + else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i]; + else v1[i] = 0; + } //end for + } //end else + // + VectorCopy(normal, v2); + VectorInverse(v2); +// VectorNegate(normal, v2); + return DotProduct(v1, v2); +} //end of the function AAS_BoxOriginDistanceFromPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end, + int presencetype, int passent, aas_trace_t *trace) +{ + int collision; + vec3_t boxmins, boxmaxs; + aas_link_t *link; + bsp_trace_t bsptrace; + + AAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs); + + Com_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy + //assume no collision + bsptrace.fraction = 1; + collision = qfalse; + for (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent) + { + //ignore the pass entity + if (link->entnum == passent) continue; + // + if (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end, + CONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace)) + { + collision = qtrue; + } //end if + } //end for + if (collision) + { + trace->startsolid = bsptrace.startsolid; + trace->ent = bsptrace.ent; + VectorCopy(bsptrace.endpos, trace->endpos); + trace->area = 0; + trace->planenum = 0; + return qtrue; + } //end if + return qfalse; +} //end of the function AAS_AreaEntityCollision +//=========================================================================== +// recursive subdivision of the line by the BSP tree. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, + int passent) +{ + int side, nodenum, tmpplanenum; + float front, back, frac; + vec3_t cur_start, cur_end, cur_mid, v1, v2; + aas_tracestack_t tracestack[127]; + aas_tracestack_t *tstack_p; + aas_node_t *aasnode; + aas_plane_t *plane; + aas_trace_t trace; + + //clear the trace structure + Com_Memset(&trace, 0, sizeof(aas_trace_t)); + + if (!aasworld.loaded) return trace; + + tstack_p = tracestack; + //we start with the whole line on the stack + VectorCopy(start, tstack_p->start); + VectorCopy(end, tstack_p->end); + tstack_p->planenum = 0; + //start with node 1 because node zero is a dummy for a solid leaf + tstack_p->nodenum = 1; //starting at the root of the tree + tstack_p++; + + while (1) + { + //pop up the stack + tstack_p--; + //if the trace stack is empty (ended up with a piece of the + //line to be traced in an area) + if (tstack_p < tracestack) + { + tstack_p++; + //nothing was hit + trace.startsolid = qfalse; + trace.fraction = 1.0; + //endpos is the end of the line + VectorCopy(end, trace.endpos); + //nothing hit + trace.ent = 0; + trace.area = 0; + trace.planenum = 0; + return trace; + } //end if + //number of the current node to test the line against + nodenum = tstack_p->nodenum; + //if it is an area + if (nodenum < 0) + { +#ifdef AAS_SAMPLE_DEBUG + if (-nodenum > aasworld.numareasettings) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n"); + return trace; + } //end if +#endif //AAS_SAMPLE_DEBUG + //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); + //if can't enter the area because it hasn't got the right presence type + if (!(aasworld.areasettings[-nodenum].presencetype & presencetype)) + { + //if the start point is still the initial start point + //NOTE: no need for epsilons because the points will be + //exactly the same when they're both the start point + if (tstack_p->start[0] == start[0] && + tstack_p->start[1] == start[1] && + tstack_p->start[2] == start[2]) + { + trace.startsolid = qtrue; + trace.fraction = 0.0; + VectorClear(v1); + } //end if + else + { + trace.startsolid = qfalse; + VectorSubtract(end, start, v1); + VectorSubtract(tstack_p->start, start, v2); + trace.fraction = VectorLength(v2) / VectorNormalize(v1); + VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); + } //end else + VectorCopy(tstack_p->start, trace.endpos); + trace.ent = 0; + trace.area = -nodenum; +// VectorSubtract(end, start, v1); + trace.planenum = tstack_p->planenum; + //always take the plane with normal facing towards the trace start + plane = &aasworld.planes[trace.planenum]; + if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; + return trace; + } //end if + else + { + if (passent >= 0) + { + if (AAS_AreaEntityCollision(-nodenum, tstack_p->start, + tstack_p->end, presencetype, passent, + &trace)) + { + if (!trace.startsolid) + { + VectorSubtract(end, start, v1); + VectorSubtract(trace.endpos, start, v2); + trace.fraction = VectorLength(v2) / VectorLength(v1); + } //end if + return trace; + } //end if + } //end if + } //end else + trace.lastarea = -nodenum; + continue; + } //end if + //if it is a solid leaf + if (!nodenum) + { + //if the start point is still the initial start point + //NOTE: no need for epsilons because the points will be + //exactly the same when they're both the start point + if (tstack_p->start[0] == start[0] && + tstack_p->start[1] == start[1] && + tstack_p->start[2] == start[2]) + { + trace.startsolid = qtrue; + trace.fraction = 0.0; + VectorClear(v1); + } //end if + else + { + trace.startsolid = qfalse; + VectorSubtract(end, start, v1); + VectorSubtract(tstack_p->start, start, v2); + trace.fraction = VectorLength(v2) / VectorNormalize(v1); + VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); + } //end else + VectorCopy(tstack_p->start, trace.endpos); + trace.ent = 0; + trace.area = 0; //hit solid leaf +// VectorSubtract(end, start, v1); + trace.planenum = tstack_p->planenum; + //always take the plane with normal facing towards the trace start + plane = &aasworld.planes[trace.planenum]; + if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; + return trace; + } //end if +#ifdef AAS_SAMPLE_DEBUG + if (nodenum > aasworld.numnodes) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n"); + return trace; + } //end if +#endif //AAS_SAMPLE_DEBUG + //the node to test against + aasnode = &aasworld.nodes[nodenum]; + //start point of current line to test against node + VectorCopy(tstack_p->start, cur_start); + //end point of the current line to test against node + VectorCopy(tstack_p->end, cur_end); + //the current node plane + plane = &aasworld.planes[aasnode->planenum]; + + switch(plane->type) + {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!! + //check for axial planes + case PLANE_X: + { + front = cur_start[0] - plane->dist; + back = cur_end[0] - plane->dist; + break; + } //end case + case PLANE_Y: + { + front = cur_start[1] - plane->dist; + back = cur_end[1] - plane->dist; + break; + } //end case + case PLANE_Z: + { + front = cur_start[2] - plane->dist; + back = cur_end[2] - plane->dist; + break; + } //end case*/ + default: //gee it's not an axial plane + { + front = DotProduct(cur_start, plane->normal) - plane->dist; + back = DotProduct(cur_end, plane->normal) - plane->dist; + break; + } //end default + } //end switch + // bk010221 - old location of FPE hack and divide by zero expression + //if the whole to be traced line is totally at the front of this node + //only go down the tree with the front child + if ((front >= -ON_EPSILON && back >= -ON_EPSILON)) + { + //keep the current start and end point on the stack + //and go down the tree with the front child + tstack_p->nodenum = aasnode->children[0]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + } //end if + //if the whole to be traced line is totally at the back of this node + //only go down the tree with the back child + else if ((front < ON_EPSILON && back < ON_EPSILON)) + { + //keep the current start and end point on the stack + //and go down the tree with the back child + tstack_p->nodenum = aasnode->children[1]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + } //end if + //go down the tree both at the front and back of the node + else + { + tmpplanenum = tstack_p->planenum; + // bk010221 - new location of divide by zero (see above) + if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE + //calculate the hitpoint with the node (split point of the line) + //put the crosspoint TRACEPLANE_EPSILON pixels on the near side + if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back); + else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221 + // + if (frac < 0) + frac = 0.001f; //0 + else if (frac > 1) + frac = 0.999f; //1 + //frac = front / (front-back); + // + cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; + cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; + cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; + +// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); + //side the front part of the line is on + side = front < 0; + //first put the end part of the line on the stack (back side) + VectorCopy(cur_mid, tstack_p->start); + //not necesary to store because still on stack + //VectorCopy(cur_end, tstack_p->end); + tstack_p->planenum = aasnode->planenum; + tstack_p->nodenum = aasnode->children[!side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + //now put the part near the start of the line on the stack so we will + //continue with thats part first. This way we'll find the first + //hit of the bbox + VectorCopy(cur_start, tstack_p->start); + VectorCopy(cur_mid, tstack_p->end); + tstack_p->planenum = tmpplanenum; + tstack_p->nodenum = aasnode->children[side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); + return trace; + } //end if + } //end else + } //end while +// return trace; +} //end of the function AAS_TraceClientBBox +//=========================================================================== +// recursive subdivision of the line by the BSP tree. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas) +{ + int side, nodenum, tmpplanenum; + int numareas; + float front, back, frac; + vec3_t cur_start, cur_end, cur_mid; + aas_tracestack_t tracestack[127]; + aas_tracestack_t *tstack_p; + aas_node_t *aasnode; + aas_plane_t *plane; + + numareas = 0; + areas[0] = 0; + if (!aasworld.loaded) return numareas; + + tstack_p = tracestack; + //we start with the whole line on the stack + VectorCopy(start, tstack_p->start); + VectorCopy(end, tstack_p->end); + tstack_p->planenum = 0; + //start with node 1 because node zero is a dummy for a solid leaf + tstack_p->nodenum = 1; //starting at the root of the tree + tstack_p++; + + while (1) + { + //pop up the stack + tstack_p--; + //if the trace stack is empty (ended up with a piece of the + //line to be traced in an area) + if (tstack_p < tracestack) + { + return numareas; + } //end if + //number of the current node to test the line against + nodenum = tstack_p->nodenum; + //if it is an area + if (nodenum < 0) + { +#ifdef AAS_SAMPLE_DEBUG + if (-nodenum > aasworld.numareasettings) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum); + return numareas; + } //end if +#endif //AAS_SAMPLE_DEBUG + //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); + areas[numareas] = -nodenum; + if (points) VectorCopy(tstack_p->start, points[numareas]); + numareas++; + if (numareas >= maxareas) return numareas; + continue; + } //end if + //if it is a solid leaf + if (!nodenum) + { + continue; + } //end if +#ifdef AAS_SAMPLE_DEBUG + if (nodenum > aasworld.numnodes) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n"); + return numareas; + } //end if +#endif //AAS_SAMPLE_DEBUG + //the node to test against + aasnode = &aasworld.nodes[nodenum]; + //start point of current line to test against node + VectorCopy(tstack_p->start, cur_start); + //end point of the current line to test against node + VectorCopy(tstack_p->end, cur_end); + //the current node plane + plane = &aasworld.planes[aasnode->planenum]; + + switch(plane->type) + {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!! + //check for axial planes + case PLANE_X: + { + front = cur_start[0] - plane->dist; + back = cur_end[0] - plane->dist; + break; + } //end case + case PLANE_Y: + { + front = cur_start[1] - plane->dist; + back = cur_end[1] - plane->dist; + break; + } //end case + case PLANE_Z: + { + front = cur_start[2] - plane->dist; + back = cur_end[2] - plane->dist; + break; + } //end case*/ + default: //gee it's not an axial plane + { + front = DotProduct(cur_start, plane->normal) - plane->dist; + back = DotProduct(cur_end, plane->normal) - plane->dist; + break; + } //end default + } //end switch + + //if the whole to be traced line is totally at the front of this node + //only go down the tree with the front child + if (front > 0 && back > 0) + { + //keep the current start and end point on the stack + //and go down the tree with the front child + tstack_p->nodenum = aasnode->children[0]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + } //end if + //if the whole to be traced line is totally at the back of this node + //only go down the tree with the back child + else if (front <= 0 && back <= 0) + { + //keep the current start and end point on the stack + //and go down the tree with the back child + tstack_p->nodenum = aasnode->children[1]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + } //end if + //go down the tree both at the front and back of the node + else + { + tmpplanenum = tstack_p->planenum; + //calculate the hitpoint with the node (split point of the line) + //put the crosspoint TRACEPLANE_EPSILON pixels on the near side + if (front < 0) frac = (front)/(front-back); + else frac = (front)/(front-back); + if (frac < 0) frac = 0; + else if (frac > 1) frac = 1; + //frac = front / (front-back); + // + cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; + cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; + cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; + +// AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); + //side the front part of the line is on + side = front < 0; + //first put the end part of the line on the stack (back side) + VectorCopy(cur_mid, tstack_p->start); + //not necesary to store because still on stack + //VectorCopy(cur_end, tstack_p->end); + tstack_p->planenum = aasnode->planenum; + tstack_p->nodenum = aasnode->children[!side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + //now put the part near the start of the line on the stack so we will + //continue with thats part first. This way we'll find the first + //hit of the bbox + VectorCopy(cur_start, tstack_p->start); + VectorCopy(cur_mid, tstack_p->end); + tstack_p->planenum = tmpplanenum; + tstack_p->nodenum = aasnode->children[side]; + tstack_p++; + if (tstack_p >= &tracestack[127]) + { + botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); + return numareas; + } //end if + } //end else + } //end while +// return numareas; +} //end of the function AAS_TraceAreas +//=========================================================================== +// a simple cross product +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +// void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res) +#define AAS_OrthogonalToVectors(v1, v2, res) \ + (res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\ + (res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\ + (res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]); +//=========================================================================== +// tests if the given point is within the face boundaries +// +// Parameter: face : face to test if the point is in it +// pnormal : normal of the plane to use for the face +// point : point to test if inside face boundaries +// Returns: qtrue if the point is within the face boundaries +// Changes Globals: - +//=========================================================================== +qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon) +{ + int i, firstvertex, edgenum; + vec3_t v0; + vec3_t edgevec, pointvec, sepnormal; + aas_edge_t *edge; +#ifdef AAS_SAMPLE_DEBUG + int lastvertex = 0; +#endif //AAS_SAMPLE_DEBUG + + if (!aasworld.loaded) return qfalse; + + for (i = 0; i < face->numedges; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + //get the first vertex of the edge + firstvertex = edgenum < 0; + VectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0); + //edge vector + VectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec); + // +#ifdef AAS_SAMPLE_DEBUG + if (lastvertex && lastvertex != edge->v[firstvertex]) + { + botimport.Print(PRT_MESSAGE, "winding not counter clockwise\n"); + } //end if + lastvertex = edge->v[!firstvertex]; +#endif //AAS_SAMPLE_DEBUG + //vector from first edge point to point possible in face + VectorSubtract(point, v0, pointvec); + //get a vector pointing inside the face orthogonal to both the + //edge vector and the normal vector of the plane the face is in + //this vector defines a plane through the origin (first vertex of + //edge) and through both the edge vector and the normal vector + //of the plane + AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal); + //check on wich side of the above plane the point is + //this is done by checking the sign of the dot product of the + //vector orthogonal vector from above and the vector from the + //origin (first vertex of edge) to the point + //if the dotproduct is smaller than zero the point is outside the face + if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_InsideFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon) +{ + int i, firstvertex, edgenum; + vec_t *v1, *v2; + vec3_t edgevec, pointvec, sepnormal; + aas_edge_t *edge; + aas_plane_t *plane; + aas_face_t *face; + + if (!aasworld.loaded) return qfalse; + + face = &aasworld.faces[facenum]; + plane = &aasworld.planes[face->planenum]; + // + for (i = 0; i < face->numedges; i++) + { + edgenum = aasworld.edgeindex[face->firstedge + i]; + edge = &aasworld.edges[abs(edgenum)]; + //get the first vertex of the edge + firstvertex = edgenum < 0; + v1 = aasworld.vertexes[edge->v[firstvertex]]; + v2 = aasworld.vertexes[edge->v[!firstvertex]]; + //edge vector + VectorSubtract(v2, v1, edgevec); + //vector from first edge point to point possible in face + VectorSubtract(point, v1, pointvec); + // + CrossProduct(edgevec, plane->normal, sepnormal); + // + if (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse; + } //end for + return qtrue; +} //end of the function AAS_PointInsideFace +//=========================================================================== +// returns the ground face the given point is above in the given area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point) +{ + int i, facenum; + vec3_t up = {0, 0, 1}; + vec3_t normal; + aas_area_t *area; + aas_face_t *face; + + if (!aasworld.loaded) return NULL; + + area = &aasworld.areas[areanum]; + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + face = &aasworld.faces[abs(facenum)]; + //if this is a ground face + if (face->faceflags & FACE_GROUND) + { + //get the up or down normal + if (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal); + else VectorCopy(up, normal); + //check if the point is in the face + if (AAS_InsideFace(face, normal, point, 0.01f)) return face; + } //end if + } //end for + return NULL; +} //end of the function AAS_AreaGroundFace +//=========================================================================== +// returns the face the trace end position is situated in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FacePlane(int facenum, vec3_t normal, float *dist) +{ + aas_plane_t *plane; + + plane = &aasworld.planes[aasworld.faces[facenum].planenum]; + VectorCopy(plane->normal, normal); + *dist = plane->dist; +} //end of the function AAS_FacePlane +//=========================================================================== +// returns the face the trace end position is situated in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_face_t *AAS_TraceEndFace(aas_trace_t *trace) +{ + int i, facenum; + aas_area_t *area; + aas_face_t *face, *firstface = NULL; + + if (!aasworld.loaded) return NULL; + + //if started in solid no face was hit + if (trace->startsolid) return NULL; + //trace->lastarea is the last area the trace was in + area = &aasworld.areas[trace->lastarea]; + //check which face the trace.endpos was in + for (i = 0; i < area->numfaces; i++) + { + facenum = aasworld.faceindex[area->firstface + i]; + face = &aasworld.faces[abs(facenum)]; + //if the face is in the same plane as the trace end point + if ((face->planenum & ~1) == (trace->planenum & ~1)) + { + //firstface is used for optimization, if theres only one + //face in the plane then it has to be the good one + //if there are more faces in the same plane then always + //check the one with the fewest edges first +/* if (firstface) + { + if (firstface->numedges < face->numedges) + { + if (AAS_InsideFace(firstface, + aasworld.planes[face->planenum].normal, trace->endpos)) + { + return firstface; + } //end if + firstface = face; + } //end if + else + { + if (AAS_InsideFace(face, + aasworld.planes[face->planenum].normal, trace->endpos)) + { + return face; + } //end if + } //end else + } //end if + else + { + firstface = face; + } //end else*/ + if (AAS_InsideFace(face, + aasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face; + } //end if + } //end for + return firstface; +} //end of the function AAS_TraceEndFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) +{ + int i, sides; + float dist1, dist2; + vec3_t corners[2]; + + for (i = 0; i < 3; i++) + { + if (p->normal[i] < 0) + { + corners[0][i] = absmins[i]; + corners[1][i] = absmaxs[i]; + } //end if + else + { + corners[1][i] = absmins[i]; + corners[0][i] = absmaxs[i]; + } //end else + } //end for + dist1 = DotProduct(p->normal, corners[0]) - p->dist; + dist2 = DotProduct(p->normal, corners[1]) - p->dist; + sides = 0; + if (dist1 >= 0) sides = 1; + if (dist2 < 0) sides |= 2; + + return sides; +} //end of the function AAS_BoxOnPlaneSide2 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +//int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p) +#define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\ + ( (p)->type < 3) ?\ + (\ + ( (p)->dist <= (absmins)[(p)->type]) ?\ + (\ + 1\ + )\ + :\ + (\ + ( (p)->dist >= (absmaxs)[(p)->type]) ?\ + (\ + 2\ + )\ + :\ + (\ + 3\ + )\ + )\ + )\ + :\ + (\ + AAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\ + )\ +) //end of the function AAS_BoxOnPlaneSide +//=========================================================================== +// remove the links to this entity from all areas +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_UnlinkFromAreas(aas_link_t *areas) +{ + aas_link_t *link, *nextlink; + + for (link = areas; link; link = nextlink) + { + //next area the entity is linked in + nextlink = link->next_area; + //remove the entity from the linked list of this area + if (link->prev_ent) link->prev_ent->next_ent = link->next_ent; + else aasworld.arealinkedentities[link->areanum] = link->next_ent; + if (link->next_ent) link->next_ent->prev_ent = link->prev_ent; + //deallocate the link structure + AAS_DeAllocAASLink(link); + } //end for +} //end of the function AAS_UnlinkFromAreas +//=========================================================================== +// link the entity to the areas the bounding box is totally or partly +// situated in. This is done with recursion down the tree using the +// bounding box to test for plane sides +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +typedef struct +{ + int nodenum; //node found after splitting +} aas_linkstack_t; + +aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum) +{ + int side, nodenum; + aas_linkstack_t linkstack[128]; + aas_linkstack_t *lstack_p; + aas_node_t *aasnode; + aas_plane_t *plane; + aas_link_t *link, *areas; + + if (!aasworld.loaded) + { + botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n"); + return NULL; + } //end if + + areas = NULL; + // + lstack_p = linkstack; + //we start with the whole line on the stack + //start with node 1 because node zero is a dummy used for solid leafs + lstack_p->nodenum = 1; //starting at the root of the tree + lstack_p++; + + while (1) + { + //pop up the stack + lstack_p--; + //if the trace stack is empty (ended up with a piece of the + //line to be traced in an area) + if (lstack_p < linkstack) break; + //number of the current node to test the line against + nodenum = lstack_p->nodenum; + //if it is an area + if (nodenum < 0) + { + //NOTE: the entity might have already been linked into this area + // because several node children can point to the same area + for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent) + { + if (link->entnum == entnum) break; + } //end for + if (link) continue; + // + link = AAS_AllocAASLink(); + if (!link) return areas; + link->entnum = entnum; + link->areanum = -nodenum; + //put the link into the double linked area list of the entity + link->prev_area = NULL; + link->next_area = areas; + if (areas) areas->prev_area = link; + areas = link; + //put the link into the double linked entity list of the area + link->prev_ent = NULL; + link->next_ent = aasworld.arealinkedentities[-nodenum]; + if (aasworld.arealinkedentities[-nodenum]) + aasworld.arealinkedentities[-nodenum]->prev_ent = link; + aasworld.arealinkedentities[-nodenum] = link; + // + continue; + } //end if + //if solid leaf + if (!nodenum) continue; + //the node to test against + aasnode = &aasworld.nodes[nodenum]; + //the current node plane + plane = &aasworld.planes[aasnode->planenum]; + //get the side(s) the box is situated relative to the plane + side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane); + //if on the front side of the node + if (side & 1) + { + lstack_p->nodenum = aasnode->children[0]; + lstack_p++; + } //end if + if (lstack_p >= &linkstack[127]) + { + botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); + break; + } //end if + //if on the back side of the node + if (side & 2) + { + lstack_p->nodenum = aasnode->children[1]; + lstack_p++; + } //end if + if (lstack_p >= &linkstack[127]) + { + botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); + break; + } //end if + } //end while + return areas; +} //end of the function AAS_AASLinkEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype) +{ + vec3_t mins, maxs; + vec3_t newabsmins, newabsmaxs; + + AAS_PresenceTypeBoundingBox(presencetype, mins, maxs); + VectorSubtract(absmins, maxs, newabsmins); + VectorSubtract(absmaxs, mins, newabsmaxs); + //relink the entity + return AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum); +} //end of the function AAS_LinkEntityClientBBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas) +{ + aas_link_t *linkedareas, *link; + int num; + + linkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1); + num = 0; + for (link = linkedareas; link; link = link->next_area) + { + areas[num] = link->areanum; + num++; + if (num >= maxareas) + break; + } //end for + AAS_UnlinkFromAreas(linkedareas); + return num; +} //end of the function AAS_BBoxAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AreaInfo( int areanum, aas_areainfo_t *info ) +{ + aas_areasettings_t *settings; + if (!info) + return 0; + if (areanum <= 0 || areanum >= aasworld.numareas) + { + botimport.Print(PRT_ERROR, "AAS_AreaInfo: areanum %d out of range\n", areanum); + return 0; + } //end if + settings = &aasworld.areasettings[areanum]; + info->cluster = settings->cluster; + info->contents = settings->contents; + info->flags = settings->areaflags; + info->presencetype = settings->presencetype; + VectorCopy(aasworld.areas[areanum].mins, info->mins); + VectorCopy(aasworld.areas[areanum].maxs, info->maxs); + VectorCopy(aasworld.areas[areanum].center, info->center); + return sizeof(aas_areainfo_t); +} //end of the function AAS_AreaInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +aas_plane_t *AAS_PlaneFromNum(int planenum) +{ + if (!aasworld.loaded) return 0; + + return &aasworld.planes[planenum]; +} //end of the function AAS_PlaneFromNum diff --git a/code/botlib/be_aas_sample.h b/code/botlib/be_aas_sample.h index 6c2fc7e..5c121a0 100755 --- a/code/botlib/be_aas_sample.h +++ b/code/botlib/be_aas_sample.h @@ -1,69 +1,69 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_aas_sample.h
- *
- * desc: AAS
- *
- * $Archive: /source/code/botlib/be_aas_sample.h $
- *
- *****************************************************************************/
-
-#ifdef AASINTERN
-void AAS_InitAASLinkHeap(void);
-void AAS_InitAASLinkedEntities(void);
-void AAS_FreeAASLinkHeap(void);
-void AAS_FreeAASLinkedEntities(void);
-aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point);
-aas_face_t *AAS_TraceEndFace(aas_trace_t *trace);
-aas_plane_t *AAS_PlaneFromNum(int planenum);
-aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum);
-aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype);
-qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon);
-qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon);
-void AAS_UnlinkFromAreas(aas_link_t *areas);
-#endif //AASINTERN
-
-//returns the mins and maxs of the bounding box for the given presence type
-void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs);
-//returns the cluster the area is in (negative portal number if the area is a portal)
-int AAS_AreaCluster(int areanum);
-//returns the presence type(s) of the area
-int AAS_AreaPresenceType(int areanum);
-//returns the presence type(s) at the given point
-int AAS_PointPresenceType(vec3_t point);
-//returns the result of the trace of a client bbox
-aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent);
-//stores the areas the trace went through and returns the number of passed areas
-int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);
-//returns the areas the bounding box is in
-int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas);
-//return area information
-int AAS_AreaInfo( int areanum, aas_areainfo_t *info );
-//returns the area the point is in
-int AAS_PointAreaNum(vec3_t point);
-//
-int AAS_PointReachabilityAreaIndex( vec3_t point );
-//returns the plane the given face is in
-void AAS_FacePlane(int facenum, vec3_t normal, float *dist);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_aas_sample.h + * + * desc: AAS + * + * $Archive: /source/code/botlib/be_aas_sample.h $ + * + *****************************************************************************/ + +#ifdef AASINTERN +void AAS_InitAASLinkHeap(void); +void AAS_InitAASLinkedEntities(void); +void AAS_FreeAASLinkHeap(void); +void AAS_FreeAASLinkedEntities(void); +aas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point); +aas_face_t *AAS_TraceEndFace(aas_trace_t *trace); +aas_plane_t *AAS_PlaneFromNum(int planenum); +aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum); +aas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype); +qboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon); +qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon); +void AAS_UnlinkFromAreas(aas_link_t *areas); +#endif //AASINTERN + +//returns the mins and maxs of the bounding box for the given presence type +void AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs); +//returns the cluster the area is in (negative portal number if the area is a portal) +int AAS_AreaCluster(int areanum); +//returns the presence type(s) of the area +int AAS_AreaPresenceType(int areanum); +//returns the presence type(s) at the given point +int AAS_PointPresenceType(vec3_t point); +//returns the result of the trace of a client bbox +aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent); +//stores the areas the trace went through and returns the number of passed areas +int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); +//returns the areas the bounding box is in +int AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas); +//return area information +int AAS_AreaInfo( int areanum, aas_areainfo_t *info ); +//returns the area the point is in +int AAS_PointAreaNum(vec3_t point); +// +int AAS_PointReachabilityAreaIndex( vec3_t point ); +//returns the plane the given face is in +void AAS_FacePlane(int facenum, vec3_t normal, float *dist); + diff --git a/code/botlib/be_ai_char.c b/code/botlib/be_ai_char.c index 042adb4..ea34030 100755 --- a/code/botlib/be_ai_char.c +++ b/code/botlib/be_ai_char.c @@ -1,790 +1,790 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_char.c
- *
- * desc: bot characters
- *
- * $Archive: /MissionPack/code/botlib/be_ai_char.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_log.h"
-#include "l_memory.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_libvar.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "../game/be_ai_char.h"
-
-#define MAX_CHARACTERISTICS 80
-
-#define CT_INTEGER 1
-#define CT_FLOAT 2
-#define CT_STRING 3
-
-#define DEFAULT_CHARACTER "bots/default_c.c"
-
-//characteristic value
-union cvalue
-{
- int integer;
- float _float;
- char *string;
-};
-//a characteristic
-typedef struct bot_characteristic_s
-{
- char type; //characteristic type
- union cvalue value; //characteristic value
-} bot_characteristic_t;
-
-//a bot character
-typedef struct bot_character_s
-{
- char filename[MAX_QPATH];
- float skill;
- bot_characteristic_t c[1]; //variable sized
-} bot_character_t;
-
-bot_character_t *botcharacters[MAX_CLIENTS + 1];
-
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-bot_character_t *BotCharacterFromHandle(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
- return NULL;
- } //end if
- if (!botcharacters[handle])
- {
- botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
- return NULL;
- } //end if
- return botcharacters[handle];
-} //end of the function BotCharacterFromHandle
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpCharacter(bot_character_t *ch)
-{
- int i;
-
- Log_Write("%s", ch->filename);
- Log_Write("skill %d\n", ch->skill);
- Log_Write("{\n");
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- switch(ch->c[i].type)
- {
- case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break;
- case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break;
- case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break;
- } //end case
- } //end for
- Log_Write("}\n");
-} //end of the function BotDumpCharacter
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeCharacterStrings(bot_character_t *ch)
-{
- int i;
-
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- if (ch->c[i].type == CT_STRING)
- {
- FreeMemory(ch->c[i].value.string);
- } //end if
- } //end for
-} //end of the function BotFreeCharacterStrings
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeCharacter2(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
- return;
- } //end if
- if (!botcharacters[handle])
- {
- botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
- return;
- } //end if
- BotFreeCharacterStrings(botcharacters[handle]);
- FreeMemory(botcharacters[handle]);
- botcharacters[handle] = NULL;
-} //end of the function BotFreeCharacter2
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeCharacter(int handle)
-{
- if (!LibVarGetValue("bot_reloadcharacters")) return;
- BotFreeCharacter2(handle);
-} //end of the function BotFreeCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch)
-{
- int i;
-
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- if (ch->c[i].type) continue;
- //
- if (defaultch->c[i].type == CT_FLOAT)
- {
- ch->c[i].type = CT_FLOAT;
- ch->c[i].value._float = defaultch->c[i].value._float;
- } //end if
- else if (defaultch->c[i].type == CT_INTEGER)
- {
- ch->c[i].type = CT_INTEGER;
- ch->c[i].value.integer = defaultch->c[i].value.integer;
- } //end else if
- else if (defaultch->c[i].type == CT_STRING)
- {
- ch->c[i].type = CT_STRING;
- ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1);
- strcpy(ch->c[i].value.string, defaultch->c[i].value.string);
- } //end else if
- } //end for
-} //end of the function BotDefaultCharacteristics
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)
-{
- int indent, index, foundcharacter;
- bot_character_t *ch;
- source_t *source;
- token_t token;
-
- foundcharacter = qfalse;
- //a bot character is parsed in two phases
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(charfile);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile);
- return NULL;
- } //end if
- ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
- MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
- strcpy(ch->filename, charfile);
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "skill"))
- {
- if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (!PC_ExpectTokenString(source, "{"))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- //if it's the correct skill
- if (skill < 0 || token.intvalue == skill)
- {
- foundcharacter = qtrue;
- ch->skill = token.intvalue;
- while(PC_ExpectAnyToken(source, &token))
- {
- if (!strcmp(token.string, "}")) break;
- if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
- {
- SourceError(source, "expected integer index, found %s\n", token.string);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- index = token.intvalue;
- if (index < 0 || index > MAX_CHARACTERISTICS)
- {
- SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (ch->c[index].type)
- {
- SourceError(source, "characteristic %d already initialized\n", index);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (token.type == TT_NUMBER)
- {
- if (token.subtype & TT_FLOAT)
- {
- ch->c[index].value._float = token.floatvalue;
- ch->c[index].type = CT_FLOAT;
- } //end if
- else
- {
- ch->c[index].value.integer = token.intvalue;
- ch->c[index].type = CT_INTEGER;
- } //end else
- } //end if
- else if (token.type == TT_STRING)
- {
- StripDoubleQuotes(token.string);
- ch->c[index].value.string = GetMemory(strlen(token.string)+1);
- strcpy(ch->c[index].value.string, token.string);
- ch->c[index].type = CT_STRING;
- } //end else if
- else
- {
- SourceError(source, "expected integer, float or string, found %s\n", token.string);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end else
- } //end if
- break;
- } //end if
- else
- {
- indent = 1;
- while(indent)
- {
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- if (!strcmp(token.string, "{")) indent++;
- else if (!strcmp(token.string, "}")) indent--;
- } //end while
- } //end else
- } //end if
- else
- {
- SourceError(source, "unknown definition %s\n", token.string);
- FreeSource(source);
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end else
- } //end while
- FreeSource(source);
- //
- if (!foundcharacter)
- {
- BotFreeCharacterStrings(ch);
- FreeMemory(ch);
- return NULL;
- } //end if
- return ch;
-} //end of the function BotLoadCharacterFromFile
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotFindCachedCharacter(char *charfile, float skill)
-{
- int handle;
-
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if ( !botcharacters[handle] ) continue;
- if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&
- (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) )
- {
- return handle;
- } //end if
- } //end for
- return 0;
-} //end of the function BotFindCachedCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadCachedCharacter(char *charfile, float skill, int reload)
-{
- int handle, cachedhandle, intskill;
- bot_character_t *ch = NULL;
-#ifdef DEBUG
- int starttime;
-
- starttime = Sys_MilliSeconds();
-#endif //DEBUG
-
- //find a free spot for a character
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if (!botcharacters[handle]) break;
- } //end for
- if (handle > MAX_CLIENTS) return 0;
- //try to load a cached character with the given skill
- if (!reload)
- {
- cachedhandle = BotFindCachedCharacter(charfile, skill);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
- return cachedhandle;
- } //end if
- } //end else
- //
- intskill = (int) (skill + 0.5);
- //try to load the character with the given skill
- ch = BotLoadCharacterFromFile(charfile, intskill);
- if (ch)
- {
- botcharacters[handle] = ch;
- //
- botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile);
-#ifdef DEBUG
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile);
- } //end if
-#endif //DEBUG
- return handle;
- } //end if
- //
- botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile);
- //
- if (!reload)
- {
- //try to load a cached default character with the given skill
- cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile);
- return cachedhandle;
- } //end if
- } //end if
- //try to load the default character with the given skill
- ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill);
- if (ch)
- {
- botcharacters[handle] = ch;
- botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile);
- return handle;
- } //end if
- //
- if (!reload)
- {
- //try to load a cached character with any skill
- cachedhandle = BotFindCachedCharacter(charfile, -1);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
- return cachedhandle;
- } //end if
- } //end if
- //try to load a character with any skill
- ch = BotLoadCharacterFromFile(charfile, -1);
- if (ch)
- {
- botcharacters[handle] = ch;
- botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile);
- return handle;
- } //end if
- //
- if (!reload)
- {
- //try to load a cached character with any skill
- cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1);
- if (cachedhandle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
- return cachedhandle;
- } //end if
- } //end if
- //try to load a character with any skill
- ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1);
- if (ch)
- {
- botcharacters[handle] = ch;
- botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile);
- return handle;
- } //end if
- //
- botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile);
- //couldn't load any character
- return 0;
-} //end of the function BotLoadCachedCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadCharacterSkill(char *charfile, float skill)
-{
- int ch, defaultch;
-
- defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse);
- ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters"));
-
- if (defaultch && ch)
- {
- BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]);
- } //end if
-
- return ch;
-} //end of the function BotLoadCharacterSkill
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotInterpolateCharacters(int handle1, int handle2, float desiredskill)
-{
- bot_character_t *ch1, *ch2, *out;
- int i, handle;
- float scale;
-
- ch1 = BotCharacterFromHandle(handle1);
- ch2 = BotCharacterFromHandle(handle2);
- if (!ch1 || !ch2)
- return 0;
- //find a free spot for a character
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if (!botcharacters[handle]) break;
- } //end for
- if (handle > MAX_CLIENTS) return 0;
- out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
- MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
- out->skill = desiredskill;
- strcpy(out->filename, ch1->filename);
- botcharacters[handle] = out;
-
- scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill);
- for (i = 0; i < MAX_CHARACTERISTICS; i++)
- {
- //
- if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT)
- {
- out->c[i].type = CT_FLOAT;
- out->c[i].value._float = ch1->c[i].value._float +
- (ch2->c[i].value._float - ch1->c[i].value._float) * scale;
- } //end if
- else if (ch1->c[i].type == CT_INTEGER)
- {
- out->c[i].type = CT_INTEGER;
- out->c[i].value.integer = ch1->c[i].value.integer;
- } //end else if
- else if (ch1->c[i].type == CT_STRING)
- {
- out->c[i].type = CT_STRING;
- out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1);
- strcpy(out->c[i].value.string, ch1->c[i].value.string);
- } //end else if
- } //end for
- return handle;
-} //end of the function BotInterpolateCharacters
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadCharacter(char *charfile, float skill)
-{
- int firstskill, secondskill, handle;
-
- //make sure the skill is in the valid range
- if (skill < 1.0) skill = 1.0;
- else if (skill > 5.0) skill = 5.0;
- //skill 1, 4 and 5 should be available in the character files
- if (skill == 1.0 || skill == 4.0 || skill == 5.0)
- {
- return BotLoadCharacterSkill(charfile, skill);
- } //end if
- //check if there's a cached skill
- handle = BotFindCachedCharacter(charfile, skill);
- if (handle)
- {
- botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
- return handle;
- } //end if
- if (skill < 4.0)
- {
- //load skill 1 and 4
- firstskill = BotLoadCharacterSkill(charfile, 1);
- if (!firstskill) return 0;
- secondskill = BotLoadCharacterSkill(charfile, 4);
- if (!secondskill) return firstskill;
- } //end if
- else
- {
- //load skill 4 and 5
- firstskill = BotLoadCharacterSkill(charfile, 4);
- if (!firstskill) return 0;
- secondskill = BotLoadCharacterSkill(charfile, 5);
- if (!secondskill) return firstskill;
- } //end else
- //interpolate between the two skills
- handle = BotInterpolateCharacters(firstskill, secondskill, skill);
- if (!handle) return 0;
- //write the character to the log file
- BotDumpCharacter(botcharacters[handle]);
- //
- return handle;
-} //end of the function BotLoadCharacter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int CheckCharacteristicIndex(int character, int index)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return qfalse;
- if (index < 0 || index >= MAX_CHARACTERISTICS)
- {
- botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index);
- return qfalse;
- } //end if
- if (!ch->c[index].type)
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index);
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function CheckCharacteristicIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float Characteristic_Float(int character, int index)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- //check if the index is in range
- if (!CheckCharacteristicIndex(character, index)) return 0;
- //an integer will be converted to a float
- if (ch->c[index].type == CT_INTEGER)
- {
- return (float) ch->c[index].value.integer;
- } //end if
- //floats are just returned
- else if (ch->c[index].type == CT_FLOAT)
- {
- return ch->c[index].value._float;
- } //end else if
- //cannot convert a string pointer to a float
- else
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index);
- return 0;
- } //end else if
-// return 0;
-} //end of the function Characteristic_Float
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float Characteristic_BFloat(int character, int index, float min, float max)
-{
- float value;
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- if (min > max)
- {
- botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max);
- return 0;
- } //end if
- value = Characteristic_Float(character, index);
- if (value < min) return min;
- if (value > max) return max;
- return value;
-} //end of the function Characteristic_BFloat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Characteristic_Integer(int character, int index)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- //check if the index is in range
- if (!CheckCharacteristicIndex(character, index)) return 0;
- //an integer will just be returned
- if (ch->c[index].type == CT_INTEGER)
- {
- return ch->c[index].value.integer;
- } //end if
- //floats are casted to integers
- else if (ch->c[index].type == CT_FLOAT)
- {
- return (int) ch->c[index].value._float;
- } //end else if
- else
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index);
- return 0;
- } //end else if
-// return 0;
-} //end of the function Characteristic_Integer
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Characteristic_BInteger(int character, int index, int min, int max)
-{
- int value;
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return 0;
- if (min > max)
- {
- botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max);
- return 0;
- } //end if
- value = Characteristic_Integer(character, index);
- if (value < min) return min;
- if (value > max) return max;
- return value;
-} //end of the function Characteristic_BInteger
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Characteristic_String(int character, int index, char *buf, int size)
-{
- bot_character_t *ch;
-
- ch = BotCharacterFromHandle(character);
- if (!ch) return;
- //check if the index is in range
- if (!CheckCharacteristicIndex(character, index)) return;
- //an integer will be converted to a float
- if (ch->c[index].type == CT_STRING)
- {
- strncpy(buf, ch->c[index].value.string, size-1);
- buf[size-1] = '\0';
- return;
- } //end if
- else
- {
- botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index);
- return;
- } //end else if
- return;
-} //end of the function Characteristic_String
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownCharacters(void)
-{
- int handle;
-
- for (handle = 1; handle <= MAX_CLIENTS; handle++)
- {
- if (botcharacters[handle])
- {
- BotFreeCharacter2(handle);
- } //end if
- } //end for
-} //end of the function BotShutdownCharacters
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_char.c + * + * desc: bot characters + * + * $Archive: /MissionPack/code/botlib/be_ai_char.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/be_ai_char.h" + +#define MAX_CHARACTERISTICS 80 + +#define CT_INTEGER 1 +#define CT_FLOAT 2 +#define CT_STRING 3 + +#define DEFAULT_CHARACTER "bots/default_c.c" + +//characteristic value +union cvalue +{ + int integer; + float _float; + char *string; +}; +//a characteristic +typedef struct bot_characteristic_s +{ + char type; //characteristic type + union cvalue value; //characteristic value +} bot_characteristic_t; + +//a bot character +typedef struct bot_character_s +{ + char filename[MAX_QPATH]; + float skill; + bot_characteristic_t c[1]; //variable sized +} bot_character_t; + +bot_character_t *botcharacters[MAX_CLIENTS + 1]; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_character_t *BotCharacterFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); + return NULL; + } //end if + if (!botcharacters[handle]) + { + botimport.Print(PRT_FATAL, "invalid character %d\n", handle); + return NULL; + } //end if + return botcharacters[handle]; +} //end of the function BotCharacterFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpCharacter(bot_character_t *ch) +{ + int i; + + Log_Write("%s", ch->filename); + Log_Write("skill %d\n", ch->skill); + Log_Write("{\n"); + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + switch(ch->c[i].type) + { + case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break; + case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break; + case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break; + } //end case + } //end for + Log_Write("}\n"); +} //end of the function BotDumpCharacter +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacterStrings(bot_character_t *ch) +{ + int i; + + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + if (ch->c[i].type == CT_STRING) + { + FreeMemory(ch->c[i].value.string); + } //end if + } //end for +} //end of the function BotFreeCharacterStrings +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacter2(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle); + return; + } //end if + if (!botcharacters[handle]) + { + botimport.Print(PRT_FATAL, "invalid character %d\n", handle); + return; + } //end if + BotFreeCharacterStrings(botcharacters[handle]); + FreeMemory(botcharacters[handle]); + botcharacters[handle] = NULL; +} //end of the function BotFreeCharacter2 +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeCharacter(int handle) +{ + if (!LibVarGetValue("bot_reloadcharacters")) return; + BotFreeCharacter2(handle); +} //end of the function BotFreeCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch) +{ + int i; + + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + if (ch->c[i].type) continue; + // + if (defaultch->c[i].type == CT_FLOAT) + { + ch->c[i].type = CT_FLOAT; + ch->c[i].value._float = defaultch->c[i].value._float; + } //end if + else if (defaultch->c[i].type == CT_INTEGER) + { + ch->c[i].type = CT_INTEGER; + ch->c[i].value.integer = defaultch->c[i].value.integer; + } //end else if + else if (defaultch->c[i].type == CT_STRING) + { + ch->c[i].type = CT_STRING; + ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1); + strcpy(ch->c[i].value.string, defaultch->c[i].value.string); + } //end else if + } //end for +} //end of the function BotDefaultCharacteristics +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill) +{ + int indent, index, foundcharacter; + bot_character_t *ch; + source_t *source; + token_t token; + + foundcharacter = qfalse; + //a bot character is parsed in two phases + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(charfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile); + return NULL; + } //end if + ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); + strcpy(ch->filename, charfile); + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "skill")) + { + if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + //if it's the correct skill + if (skill < 0 || token.intvalue == skill) + { + foundcharacter = qtrue; + ch->skill = token.intvalue; + while(PC_ExpectAnyToken(source, &token)) + { + if (!strcmp(token.string, "}")) break; + if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) + { + SourceError(source, "expected integer index, found %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + index = token.intvalue; + if (index < 0 || index > MAX_CHARACTERISTICS) + { + SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (ch->c[index].type) + { + SourceError(source, "characteristic %d already initialized\n", index); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (token.type == TT_NUMBER) + { + if (token.subtype & TT_FLOAT) + { + ch->c[index].value._float = token.floatvalue; + ch->c[index].type = CT_FLOAT; + } //end if + else + { + ch->c[index].value.integer = token.intvalue; + ch->c[index].type = CT_INTEGER; + } //end else + } //end if + else if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + ch->c[index].value.string = GetMemory(strlen(token.string)+1); + strcpy(ch->c[index].value.string, token.string); + ch->c[index].type = CT_STRING; + } //end else if + else + { + SourceError(source, "expected integer, float or string, found %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end else + } //end if + break; + } //end if + else + { + indent = 1; + while(indent) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + if (!strcmp(token.string, "{")) indent++; + else if (!strcmp(token.string, "}")) indent--; + } //end while + } //end else + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeSource(source); + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end else + } //end while + FreeSource(source); + // + if (!foundcharacter) + { + BotFreeCharacterStrings(ch); + FreeMemory(ch); + return NULL; + } //end if + return ch; +} //end of the function BotLoadCharacterFromFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFindCachedCharacter(char *charfile, float skill) +{ + int handle; + + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if ( !botcharacters[handle] ) continue; + if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 && + (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) ) + { + return handle; + } //end if + } //end for + return 0; +} //end of the function BotFindCachedCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCachedCharacter(char *charfile, float skill, int reload) +{ + int handle, cachedhandle, intskill; + bot_character_t *ch = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + + //find a free spot for a character + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (!botcharacters[handle]) break; + } //end for + if (handle > MAX_CLIENTS) return 0; + //try to load a cached character with the given skill + if (!reload) + { + cachedhandle = BotFindCachedCharacter(charfile, skill); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); + return cachedhandle; + } //end if + } //end else + // + intskill = (int) (skill + 0.5); + //try to load the character with the given skill + ch = BotLoadCharacterFromFile(charfile, intskill); + if (ch) + { + botcharacters[handle] = ch; + // + botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile); +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile); + } //end if +#endif //DEBUG + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile); + // + if (!reload) + { + //try to load a cached default character with the given skill + cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load the default character with the given skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile); + return handle; + } //end if + // + if (!reload) + { + //try to load a cached character with any skill + cachedhandle = BotFindCachedCharacter(charfile, -1); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load a character with any skill + ch = BotLoadCharacterFromFile(charfile, -1); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile); + return handle; + } //end if + // + if (!reload) + { + //try to load a cached character with any skill + cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1); + if (cachedhandle) + { + botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile); + return cachedhandle; + } //end if + } //end if + //try to load a character with any skill + ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1); + if (ch) + { + botcharacters[handle] = ch; + botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile); + return handle; + } //end if + // + botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile); + //couldn't load any character + return 0; +} //end of the function BotLoadCachedCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCharacterSkill(char *charfile, float skill) +{ + int ch, defaultch; + + defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse); + ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters")); + + if (defaultch && ch) + { + BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]); + } //end if + + return ch; +} //end of the function BotLoadCharacterSkill +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotInterpolateCharacters(int handle1, int handle2, float desiredskill) +{ + bot_character_t *ch1, *ch2, *out; + int i, handle; + float scale; + + ch1 = BotCharacterFromHandle(handle1); + ch2 = BotCharacterFromHandle(handle2); + if (!ch1 || !ch2) + return 0; + //find a free spot for a character + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (!botcharacters[handle]) break; + } //end for + if (handle > MAX_CLIENTS) return 0; + out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) + + MAX_CHARACTERISTICS * sizeof(bot_characteristic_t)); + out->skill = desiredskill; + strcpy(out->filename, ch1->filename); + botcharacters[handle] = out; + + scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill); + for (i = 0; i < MAX_CHARACTERISTICS; i++) + { + // + if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT) + { + out->c[i].type = CT_FLOAT; + out->c[i].value._float = ch1->c[i].value._float + + (ch2->c[i].value._float - ch1->c[i].value._float) * scale; + } //end if + else if (ch1->c[i].type == CT_INTEGER) + { + out->c[i].type = CT_INTEGER; + out->c[i].value.integer = ch1->c[i].value.integer; + } //end else if + else if (ch1->c[i].type == CT_STRING) + { + out->c[i].type = CT_STRING; + out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1); + strcpy(out->c[i].value.string, ch1->c[i].value.string); + } //end else if + } //end for + return handle; +} //end of the function BotInterpolateCharacters +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadCharacter(char *charfile, float skill) +{ + int firstskill, secondskill, handle; + + //make sure the skill is in the valid range + if (skill < 1.0) skill = 1.0; + else if (skill > 5.0) skill = 5.0; + //skill 1, 4 and 5 should be available in the character files + if (skill == 1.0 || skill == 4.0 || skill == 5.0) + { + return BotLoadCharacterSkill(charfile, skill); + } //end if + //check if there's a cached skill + handle = BotFindCachedCharacter(charfile, skill); + if (handle) + { + botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile); + return handle; + } //end if + if (skill < 4.0) + { + //load skill 1 and 4 + firstskill = BotLoadCharacterSkill(charfile, 1); + if (!firstskill) return 0; + secondskill = BotLoadCharacterSkill(charfile, 4); + if (!secondskill) return firstskill; + } //end if + else + { + //load skill 4 and 5 + firstskill = BotLoadCharacterSkill(charfile, 4); + if (!firstskill) return 0; + secondskill = BotLoadCharacterSkill(charfile, 5); + if (!secondskill) return firstskill; + } //end else + //interpolate between the two skills + handle = BotInterpolateCharacters(firstskill, secondskill, skill); + if (!handle) return 0; + //write the character to the log file + BotDumpCharacter(botcharacters[handle]); + // + return handle; +} //end of the function BotLoadCharacter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int CheckCharacteristicIndex(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return qfalse; + if (index < 0 || index >= MAX_CHARACTERISTICS) + { + botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index); + return qfalse; + } //end if + if (!ch->c[index].type) + { + botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index); + return qfalse; + } //end if + return qtrue; +} //end of the function CheckCharacteristicIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Characteristic_Float(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return 0; + //an integer will be converted to a float + if (ch->c[index].type == CT_INTEGER) + { + return (float) ch->c[index].value.integer; + } //end if + //floats are just returned + else if (ch->c[index].type == CT_FLOAT) + { + return ch->c[index].value._float; + } //end else if + //cannot convert a string pointer to a float + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index); + return 0; + } //end else if +// return 0; +} //end of the function Characteristic_Float +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Characteristic_BFloat(int character, int index, float min, float max) +{ + float value; + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + if (min > max) + { + botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max); + return 0; + } //end if + value = Characteristic_Float(character, index); + if (value < min) return min; + if (value > max) return max; + return value; +} //end of the function Characteristic_BFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Characteristic_Integer(int character, int index) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return 0; + //an integer will just be returned + if (ch->c[index].type == CT_INTEGER) + { + return ch->c[index].value.integer; + } //end if + //floats are casted to integers + else if (ch->c[index].type == CT_FLOAT) + { + return (int) ch->c[index].value._float; + } //end else if + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index); + return 0; + } //end else if +// return 0; +} //end of the function Characteristic_Integer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Characteristic_BInteger(int character, int index, int min, int max) +{ + int value; + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return 0; + if (min > max) + { + botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max); + return 0; + } //end if + value = Characteristic_Integer(character, index); + if (value < min) return min; + if (value > max) return max; + return value; +} //end of the function Characteristic_BInteger +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Characteristic_String(int character, int index, char *buf, int size) +{ + bot_character_t *ch; + + ch = BotCharacterFromHandle(character); + if (!ch) return; + //check if the index is in range + if (!CheckCharacteristicIndex(character, index)) return; + //an integer will be converted to a float + if (ch->c[index].type == CT_STRING) + { + strncpy(buf, ch->c[index].value.string, size-1); + buf[size-1] = '\0'; + return; + } //end if + else + { + botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index); + return; + } //end else if + return; +} //end of the function Characteristic_String +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownCharacters(void) +{ + int handle; + + for (handle = 1; handle <= MAX_CLIENTS; handle++) + { + if (botcharacters[handle]) + { + BotFreeCharacter2(handle); + } //end if + } //end for +} //end of the function BotShutdownCharacters + diff --git a/code/botlib/be_ai_chat.c b/code/botlib/be_ai_chat.c index 9483e9b..7c719dd 100755 --- a/code/botlib/be_ai_chat.c +++ b/code/botlib/be_ai_chat.c @@ -1,3017 +1,3017 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_chat.c
- *
- * desc: bot chat AI
- *
- * $Archive: /MissionPack/code/botlib/be_ai_chat.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_libvar.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_utils.h"
-#include "l_log.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "../game/be_ea.h"
-#include "../game/be_ai_chat.h"
-
-
-//escape character
-#define ESCAPE_CHAR 0x01 //'_'
-//
-// "hi ", people, " ", 0, " entered the game"
-//becomes:
-// "hi _rpeople_ _v0_ entered the game"
-//
-
-//match piece types
-#define MT_VARIABLE 1 //variable match piece
-#define MT_STRING 2 //string match piece
-//reply chat key flags
-#define RCKFL_AND 1 //key must be present
-#define RCKFL_NOT 2 //key must be absent
-#define RCKFL_NAME 4 //name of bot must be present
-#define RCKFL_STRING 8 //key is a string
-#define RCKFL_VARIABLES 16 //key is a match template
-#define RCKFL_BOTNAMES 32 //key is a series of botnames
-#define RCKFL_GENDERFEMALE 64 //bot must be female
-#define RCKFL_GENDERMALE 128 //bot must be male
-#define RCKFL_GENDERLESS 256 //bot must be genderless
-//time to ignore a chat message after using it
-#define CHATMESSAGE_RECENTTIME 20
-
-//the actuall chat messages
-typedef struct bot_chatmessage_s
-{
- char *chatmessage; //chat message string
- float time; //last time used
- struct bot_chatmessage_s *next; //next chat message in a list
-} bot_chatmessage_t;
-//bot chat type with chat lines
-typedef struct bot_chattype_s
-{
- char name[MAX_CHATTYPE_NAME];
- int numchatmessages;
- bot_chatmessage_t *firstchatmessage;
- struct bot_chattype_s *next;
-} bot_chattype_t;
-//bot chat lines
-typedef struct bot_chat_s
-{
- bot_chattype_t *types;
-} bot_chat_t;
-
-//random string
-typedef struct bot_randomstring_s
-{
- char *string;
- struct bot_randomstring_s *next;
-} bot_randomstring_t;
-//list with random strings
-typedef struct bot_randomlist_s
-{
- char *string;
- int numstrings;
- bot_randomstring_t *firstrandomstring;
- struct bot_randomlist_s *next;
-} bot_randomlist_t;
-
-//synonym
-typedef struct bot_synonym_s
-{
- char *string;
- float weight;
- struct bot_synonym_s *next;
-} bot_synonym_t;
-//list with synonyms
-typedef struct bot_synonymlist_s
-{
- unsigned long int context;
- float totalweight;
- bot_synonym_t *firstsynonym;
- struct bot_synonymlist_s *next;
-} bot_synonymlist_t;
-
-//fixed match string
-typedef struct bot_matchstring_s
-{
- char *string;
- struct bot_matchstring_s *next;
-} bot_matchstring_t;
-
-//piece of a match template
-typedef struct bot_matchpiece_s
-{
- int type;
- bot_matchstring_t *firststring;
- int variable;
- struct bot_matchpiece_s *next;
-} bot_matchpiece_t;
-//match template
-typedef struct bot_matchtemplate_s
-{
- unsigned long int context;
- int type;
- int subtype;
- bot_matchpiece_t *first;
- struct bot_matchtemplate_s *next;
-} bot_matchtemplate_t;
-
-//reply chat key
-typedef struct bot_replychatkey_s
-{
- int flags;
- char *string;
- bot_matchpiece_t *match;
- struct bot_replychatkey_s *next;
-} bot_replychatkey_t;
-//reply chat
-typedef struct bot_replychat_s
-{
- bot_replychatkey_t *keys;
- float priority;
- int numchatmessages;
- bot_chatmessage_t *firstchatmessage;
- struct bot_replychat_s *next;
-} bot_replychat_t;
-
-//string list
-typedef struct bot_stringlist_s
-{
- char *string;
- struct bot_stringlist_s *next;
-} bot_stringlist_t;
-
-//chat state of a bot
-typedef struct bot_chatstate_s
-{
- int gender; //0=it, 1=female, 2=male
- int client; //client number
- char name[32]; //name of the bot
- char chatmessage[MAX_MESSAGE_SIZE];
- int handle;
- //the console messages visible to the bot
- bot_consolemessage_t *firstmessage; //first message is the first typed message
- bot_consolemessage_t *lastmessage; //last message is the last typed message, bottom of console
- //number of console messages stored in the state
- int numconsolemessages;
- //the bot chat lines
- bot_chat_t *chat;
-} bot_chatstate_t;
-
-typedef struct {
- bot_chat_t *chat;
- char filename[MAX_QPATH];
- char chatname[MAX_QPATH];
-} bot_ichatdata_t;
-
-bot_ichatdata_t *ichatdata[MAX_CLIENTS];
-
-bot_chatstate_t *botchatstates[MAX_CLIENTS+1];
-//console message heap
-bot_consolemessage_t *consolemessageheap = NULL;
-bot_consolemessage_t *freeconsolemessages = NULL;
-//list with match strings
-bot_matchtemplate_t *matchtemplates = NULL;
-//list with synonyms
-bot_synonymlist_t *synonyms = NULL;
-//list with random strings
-bot_randomlist_t *randomstrings = NULL;
-//reply chats
-bot_replychat_t *replychats = NULL;
-
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-bot_chatstate_t *BotChatStateFromHandle(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle);
- return NULL;
- } //end if
- if (!botchatstates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
- return NULL;
- } //end if
- return botchatstates[handle];
-} //end of the function BotChatStateFromHandle
-//===========================================================================
-// initialize the heap with unused console messages
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void InitConsoleMessageHeap(void)
-{
- int i, max_messages;
-
- if (consolemessageheap) FreeMemory(consolemessageheap);
- //
- max_messages = (int) LibVarValue("max_messages", "1024");
- consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages *
- sizeof(bot_consolemessage_t));
- consolemessageheap[0].prev = NULL;
- consolemessageheap[0].next = &consolemessageheap[1];
- for (i = 1; i < max_messages-1; i++)
- {
- consolemessageheap[i].prev = &consolemessageheap[i - 1];
- consolemessageheap[i].next = &consolemessageheap[i + 1];
- } //end for
- consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2];
- consolemessageheap[max_messages-1].next = NULL;
- //pointer to the free console messages
- freeconsolemessages = consolemessageheap;
-} //end of the function InitConsoleMessageHeap
-//===========================================================================
-// allocate one console message from the heap
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_consolemessage_t *AllocConsoleMessage(void)
-{
- bot_consolemessage_t *message;
- message = freeconsolemessages;
- if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next;
- if (freeconsolemessages) freeconsolemessages->prev = NULL;
- return message;
-} //end of the function AllocConsoleMessage
-//===========================================================================
-// deallocate one console message from the heap
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeConsoleMessage(bot_consolemessage_t *message)
-{
- if (freeconsolemessages) freeconsolemessages->prev = message;
- message->prev = NULL;
- message->next = freeconsolemessages;
- freeconsolemessages = message;
-} //end of the function FreeConsoleMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotRemoveConsoleMessage(int chatstate, int handle)
-{
- bot_consolemessage_t *m, *nextm;
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
-
- for (m = cs->firstmessage; m; m = nextm)
- {
- nextm = m->next;
- if (m->handle == handle)
- {
- if (m->next) m->next->prev = m->prev;
- else cs->lastmessage = m->prev;
- if (m->prev) m->prev->next = m->next;
- else cs->firstmessage = m->next;
-
- FreeConsoleMessage(m);
- cs->numconsolemessages--;
- break;
- } //end if
- } //end for
-} //end of the function BotRemoveConsoleMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotQueueConsoleMessage(int chatstate, int type, char *message)
-{
- bot_consolemessage_t *m;
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
-
- m = AllocConsoleMessage();
- if (!m)
- {
- botimport.Print(PRT_ERROR, "empty console message heap\n");
- return;
- } //end if
- cs->handle++;
- if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1;
- m->handle = cs->handle;
- m->time = AAS_Time();
- m->type = type;
- strncpy(m->message, message, MAX_MESSAGE_SIZE);
- m->next = NULL;
- if (cs->lastmessage)
- {
- cs->lastmessage->next = m;
- m->prev = cs->lastmessage;
- cs->lastmessage = m;
- } //end if
- else
- {
- cs->lastmessage = m;
- cs->firstmessage = m;
- m->prev = NULL;
- } //end if
- cs->numconsolemessages++;
-} //end of the function BotQueueConsoleMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return 0;
- if (cs->firstmessage)
- {
- Com_Memcpy(cm, cs->firstmessage, sizeof(bot_consolemessage_t));
- cm->next = cm->prev = NULL;
- return cm->handle;
- } //end if
- return 0;
-} //end of the function BotConsoleMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotNumConsoleMessages(int chatstate)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return 0;
- return cs->numconsolemessages;
-} //end of the function BotNumConsoleMessages
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int IsWhiteSpace(char c)
-{
- if ((c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9')
- || c == '(' || c == ')'
- || c == '?' || c == ':'
- || c == '\''|| c == '/'
- || c == ',' || c == '.'
- || c == '[' || c == ']'
- || c == '-' || c == '_'
- || c == '+' || c == '=') return qfalse;
- return qtrue;
-} //end of the function IsWhiteSpace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotRemoveTildes(char *message)
-{
- int i;
-
- //remove all tildes from the chat message
- for (i = 0; message[i]; i++)
- {
- if (message[i] == '~')
- {
- memmove(&message[i], &message[i+1], strlen(&message[i+1])+1);
- } //end if
- } //end for
-} //end of the function BotRemoveTildes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void UnifyWhiteSpaces(char *string)
-{
- char *ptr, *oldptr;
-
- for (ptr = oldptr = string; *ptr; oldptr = ptr)
- {
- while(*ptr && IsWhiteSpace(*ptr)) ptr++;
- if (ptr > oldptr)
- {
- //if not at the start and not at the end of the string
- //write only one space
- if (oldptr > string && *ptr) *oldptr++ = ' ';
- //remove all other white spaces
- if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1);
- } //end if
- while(*ptr && !IsWhiteSpace(*ptr)) ptr++;
- } //end while
-} //end of the function UnifyWhiteSpaces
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int StringContains(char *str1, char *str2, int casesensitive)
-{
- int len, i, j, index;
-
- if (str1 == NULL || str2 == NULL) return -1;
-
- len = strlen(str1) - strlen(str2);
- index = 0;
- for (i = 0; i <= len; i++, str1++, index++)
- {
- for (j = 0; str2[j]; j++)
- {
- if (casesensitive)
- {
- if (str1[j] != str2[j]) break;
- } //end if
- else
- {
- if (toupper(str1[j]) != toupper(str2[j])) break;
- } //end else
- } //end for
- if (!str2[j]) return index;
- } //end for
- return -1;
-} //end of the function StringContains
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *StringContainsWord(char *str1, char *str2, int casesensitive)
-{
- int len, i, j;
-
- len = strlen(str1) - strlen(str2);
- for (i = 0; i <= len; i++, str1++)
- {
- //if not at the start of the string
- if (i)
- {
- //skip to the start of the next word
- while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++;
- if (!*str1) break;
- str1++;
- } //end for
- //compare the word
- for (j = 0; str2[j]; j++)
- {
- if (casesensitive)
- {
- if (str1[j] != str2[j]) break;
- } //end if
- else
- {
- if (toupper(str1[j]) != toupper(str2[j])) break;
- } //end else
- } //end for
- //if there was a word match
- if (!str2[j])
- {
- //if the first string has an end of word
- if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1;
- } //end if
- } //end for
- return NULL;
-} //end of the function StringContainsWord
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void StringReplaceWords(char *string, char *synonym, char *replacement)
-{
- char *str, *str2;
-
- //find the synonym in the string
- str = StringContainsWord(string, synonym, qfalse);
- //if the synonym occured in the string
- while(str)
- {
- //if the synonym isn't part of the replacement which is already in the string
- //usefull for abreviations
- str2 = StringContainsWord(string, replacement, qfalse);
- while(str2)
- {
- if (str2 <= str && str < str2 + strlen(replacement)) break;
- str2 = StringContainsWord(str2+1, replacement, qfalse);
- } //end while
- if (!str2)
- {
- memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1);
- //append the synonum replacement
- Com_Memcpy(str, replacement, strlen(replacement));
- } //end if
- //find the next synonym in the string
- str = StringContainsWord(str+strlen(replacement), synonym, qfalse);
- } //end if
-} //end of the function StringReplaceWords
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpSynonymList(bot_synonymlist_t *synlist)
-{
- FILE *fp;
- bot_synonymlist_t *syn;
- bot_synonym_t *synonym;
-
- fp = Log_FilePointer();
- if (!fp) return;
- for (syn = synlist; syn; syn = syn->next)
- {
- fprintf(fp, "%ld : [", syn->context);
- for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
- {
- fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight);
- if (synonym->next) fprintf(fp, ", ");
- } //end for
- fprintf(fp, "]\n");
- } //end for
-} //end of the function BotDumpSynonymList
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_synonymlist_t *BotLoadSynonyms(char *filename)
-{
- int pass, size, contextlevel, numsynonyms;
- unsigned long int context, contextstack[32];
- char *ptr = NULL;
- source_t *source;
- token_t token;
- bot_synonymlist_t *synlist, *lastsyn, *syn;
- bot_synonym_t *synonym, *lastsynonym;
-
- size = 0;
- synlist = NULL; //make compiler happy
- syn = NULL; //make compiler happy
- synonym = NULL; //make compiler happy
- //the synonyms are parsed in two phases
- for (pass = 0; pass < 2; pass++)
- {
- //
- if (pass && size) ptr = (char *) GetClearedHunkMemory(size);
- //
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(filename);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
- return NULL;
- } //end if
- //
- context = 0;
- contextlevel = 0;
- synlist = NULL; //list synonyms
- lastsyn = NULL; //last synonym in the list
- //
- while(PC_ReadToken(source, &token))
- {
- if (token.type == TT_NUMBER)
- {
- context |= token.intvalue;
- contextstack[contextlevel] = token.intvalue;
- contextlevel++;
- if (contextlevel >= 32)
- {
- SourceError(source, "more than 32 context levels");
- FreeSource(source);
- return NULL;
- } //end if
- if (!PC_ExpectTokenString(source, "{"))
- {
- FreeSource(source);
- return NULL;
- } //end if
- } //end if
- else if (token.type == TT_PUNCTUATION)
- {
- if (!strcmp(token.string, "}"))
- {
- contextlevel--;
- if (contextlevel < 0)
- {
- SourceError(source, "too many }");
- FreeSource(source);
- return NULL;
- } //end if
- context &= ~contextstack[contextlevel];
- } //end if
- else if (!strcmp(token.string, "["))
- {
- size += sizeof(bot_synonymlist_t);
- if (pass)
- {
- syn = (bot_synonymlist_t *) ptr;
- ptr += sizeof(bot_synonymlist_t);
- syn->context = context;
- syn->firstsynonym = NULL;
- syn->next = NULL;
- if (lastsyn) lastsyn->next = syn;
- else synlist = syn;
- lastsyn = syn;
- } //end if
- numsynonyms = 0;
- lastsynonym = NULL;
- while(1)
- {
- if (!PC_ExpectTokenString(source, "(") ||
- !PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- FreeSource(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- if (strlen(token.string) <= 0)
- {
- SourceError(source, "empty string", token.string);
- FreeSource(source);
- return NULL;
- } //end if
- size += sizeof(bot_synonym_t) + strlen(token.string) + 1;
- if (pass)
- {
- synonym = (bot_synonym_t *) ptr;
- ptr += sizeof(bot_synonym_t);
- synonym->string = ptr;
- ptr += strlen(token.string) + 1;
- strcpy(synonym->string, token.string);
- //
- if (lastsynonym) lastsynonym->next = synonym;
- else syn->firstsynonym = synonym;
- lastsynonym = synonym;
- } //end if
- numsynonyms++;
- if (!PC_ExpectTokenString(source, ",") ||
- !PC_ExpectTokenType(source, TT_NUMBER, 0, &token) ||
- !PC_ExpectTokenString(source, ")"))
- {
- FreeSource(source);
- return NULL;
- } //end if
- if (pass)
- {
- synonym->weight = token.floatvalue;
- syn->totalweight += synonym->weight;
- } //end if
- if (PC_CheckTokenString(source, "]")) break;
- if (!PC_ExpectTokenString(source, ","))
- {
- FreeSource(source);
- return NULL;
- } //end if
- } //end while
- if (numsynonyms < 2)
- {
- SourceError(source, "synonym must have at least two entries\n");
- FreeSource(source);
- return NULL;
- } //end if
- } //end else
- else
- {
- SourceError(source, "unexpected %s", token.string);
- FreeSource(source);
- return NULL;
- } //end if
- } //end else if
- } //end while
- //
- FreeSource(source);
- //
- if (contextlevel > 0)
- {
- SourceError(source, "missing }");
- return NULL;
- } //end if
- } //end for
- botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
- //
- //BotDumpSynonymList(synlist);
- //
- return synlist;
-} //end of the function BotLoadSynonyms
-//===========================================================================
-// replace all the synonyms in the string
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotReplaceSynonyms(char *string, unsigned long int context)
-{
- bot_synonymlist_t *syn;
- bot_synonym_t *synonym;
-
- for (syn = synonyms; syn; syn = syn->next)
- {
- if (!(syn->context & context)) continue;
- for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)
- {
- StringReplaceWords(string, synonym->string, syn->firstsynonym->string);
- } //end for
- } //end for
-} //end of the function BotReplaceSynonyms
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotReplaceWeightedSynonyms(char *string, unsigned long int context)
-{
- bot_synonymlist_t *syn;
- bot_synonym_t *synonym, *replacement;
- float weight, curweight;
-
- for (syn = synonyms; syn; syn = syn->next)
- {
- if (!(syn->context & context)) continue;
- //choose a weighted random replacement synonym
- weight = random() * syn->totalweight;
- if (!weight) continue;
- curweight = 0;
- for (replacement = syn->firstsynonym; replacement; replacement = replacement->next)
- {
- curweight += replacement->weight;
- if (weight < curweight) break;
- } //end for
- if (!replacement) continue;
- //replace all synonyms with the replacement
- for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
- {
- if (synonym == replacement) continue;
- StringReplaceWords(string, synonym->string, replacement->string);
- } //end for
- } //end for
-} //end of the function BotReplaceWeightedSynonyms
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotReplaceReplySynonyms(char *string, unsigned long int context)
-{
- char *str1, *str2, *replacement;
- bot_synonymlist_t *syn;
- bot_synonym_t *synonym;
-
- for (str1 = string; *str1; )
- {
- //go to the start of the next word
- while(*str1 && *str1 <= ' ') str1++;
- if (!*str1) break;
- //
- for (syn = synonyms; syn; syn = syn->next)
- {
- if (!(syn->context & context)) continue;
- for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)
- {
- str2 = synonym->string;
- //if the synonym is not at the front of the string continue
- str2 = StringContainsWord(str1, synonym->string, qfalse);
- if (!str2 || str2 != str1) continue;
- //
- replacement = syn->firstsynonym->string;
- //if the replacement IS in front of the string continue
- str2 = StringContainsWord(str1, replacement, qfalse);
- if (str2 && str2 == str1) continue;
- //
- memmove(str1 + strlen(replacement), str1+strlen(synonym->string),
- strlen(str1+strlen(synonym->string)) + 1);
- //append the synonum replacement
- Com_Memcpy(str1, replacement, strlen(replacement));
- //
- break;
- } //end for
- //if a synonym has been replaced
- if (synonym) break;
- } //end for
- //skip over this word
- while(*str1 && *str1 > ' ') str1++;
- if (!*str1) break;
- } //end while
-} //end of the function BotReplaceReplySynonyms
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadChatMessage(source_t *source, char *chatmessagestring)
-{
- char *ptr;
- token_t token;
-
- ptr = chatmessagestring;
- *ptr = 0;
- //
- while(1)
- {
- if (!PC_ExpectAnyToken(source, &token)) return qfalse;
- //fixed string
- if (token.type == TT_STRING)
- {
- StripDoubleQuotes(token.string);
- if (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE)
- {
- SourceError(source, "chat message too long\n");
- return qfalse;
- } //end if
- strcat(ptr, token.string);
- } //end else if
- //variable string
- else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))
- {
- if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)
- {
- SourceError(source, "chat message too long\n");
- return qfalse;
- } //end if
- sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR);
- } //end if
- //random string
- else if (token.type == TT_NAME)
- {
- if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)
- {
- SourceError(source, "chat message too long\n");
- return qfalse;
- } //end if
- sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR);
- } //end else if
- else
- {
- SourceError(source, "unknown message component %s\n", token.string);
- return qfalse;
- } //end else
- if (PC_CheckTokenString(source, ";")) break;
- if (!PC_ExpectTokenString(source, ",")) return qfalse;
- } //end while
- //
- return qtrue;
-} //end of the function BotLoadChatMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpRandomStringList(bot_randomlist_t *randomlist)
-{
- FILE *fp;
- bot_randomlist_t *random;
- bot_randomstring_t *rs;
-
- fp = Log_FilePointer();
- if (!fp) return;
- for (random = randomlist; random; random = random->next)
- {
- fprintf(fp, "%s = {", random->string);
- for (rs = random->firstrandomstring; rs; rs = rs->next)
- {
- fprintf(fp, "\"%s\"", rs->string);
- if (rs->next) fprintf(fp, ", ");
- else fprintf(fp, "}\n");
- } //end for
- } //end for
-} //end of the function BotDumpRandomStringList
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_randomlist_t *BotLoadRandomStrings(char *filename)
-{
- int pass, size;
- char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE];
- source_t *source;
- token_t token;
- bot_randomlist_t *randomlist, *lastrandom, *random;
- bot_randomstring_t *randomstring;
-
-#ifdef DEBUG
- int starttime = Sys_MilliSeconds();
-#endif //DEBUG
-
- size = 0;
- randomlist = NULL;
- random = NULL;
- //the synonyms are parsed in two phases
- for (pass = 0; pass < 2; pass++)
- {
- //
- if (pass && size) ptr = (char *) GetClearedHunkMemory(size);
- //
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(filename);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
- return NULL;
- } //end if
- //
- randomlist = NULL; //list
- lastrandom = NULL; //last
- //
- while(PC_ReadToken(source, &token))
- {
- if (token.type != TT_NAME)
- {
- SourceError(source, "unknown random %s", token.string);
- FreeSource(source);
- return NULL;
- } //end if
- size += sizeof(bot_randomlist_t) + strlen(token.string) + 1;
- if (pass)
- {
- random = (bot_randomlist_t *) ptr;
- ptr += sizeof(bot_randomlist_t);
- random->string = ptr;
- ptr += strlen(token.string) + 1;
- strcpy(random->string, token.string);
- random->firstrandomstring = NULL;
- random->numstrings = 0;
- //
- if (lastrandom) lastrandom->next = random;
- else randomlist = random;
- lastrandom = random;
- } //end if
- if (!PC_ExpectTokenString(source, "=") ||
- !PC_ExpectTokenString(source, "{"))
- {
- FreeSource(source);
- return NULL;
- } //end if
- while(!PC_CheckTokenString(source, "}"))
- {
- if (!BotLoadChatMessage(source, chatmessagestring))
- {
- FreeSource(source);
- return NULL;
- } //end if
- size += sizeof(bot_randomstring_t) + strlen(chatmessagestring) + 1;
- if (pass)
- {
- randomstring = (bot_randomstring_t *) ptr;
- ptr += sizeof(bot_randomstring_t);
- randomstring->string = ptr;
- ptr += strlen(chatmessagestring) + 1;
- strcpy(randomstring->string, chatmessagestring);
- //
- random->numstrings++;
- randomstring->next = random->firstrandomstring;
- random->firstrandomstring = randomstring;
- } //end if
- } //end while
- } //end while
- //free the source after one pass
- FreeSource(source);
- } //end for
- botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
- //
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime);
- //BotDumpRandomStringList(randomlist);
-#endif //DEBUG
- //
- return randomlist;
-} //end of the function BotLoadRandomStrings
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *RandomString(char *name)
-{
- bot_randomlist_t *random;
- bot_randomstring_t *rs;
- int i;
-
- for (random = randomstrings; random; random = random->next)
- {
- if (!strcmp(random->string, name))
- {
- i = random() * random->numstrings;
- for (rs = random->firstrandomstring; rs; rs = rs->next)
- {
- if (--i < 0) break;
- } //end for
- if (rs)
- {
- return rs->string;
- } //end if
- } //end for
- } //end for
- return NULL;
-} //end of the function RandomString
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpMatchTemplates(bot_matchtemplate_t *matches)
-{
- FILE *fp;
- bot_matchtemplate_t *mt;
- bot_matchpiece_t *mp;
- bot_matchstring_t *ms;
-
- fp = Log_FilePointer();
- if (!fp) return;
- for (mt = matches; mt; mt = mt->next)
- {
- fprintf(fp, "{ " );
- for (mp = mt->first; mp; mp = mp->next)
- {
- if (mp->type == MT_STRING)
- {
- for (ms = mp->firststring; ms; ms = ms->next)
- {
- fprintf(fp, "\"%s\"", ms->string);
- if (ms->next) fprintf(fp, "|");
- } //end for
- } //end if
- else if (mp->type == MT_VARIABLE)
- {
- fprintf(fp, "%d", mp->variable);
- } //end else if
- if (mp->next) fprintf(fp, ", ");
- } //end for
- fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype);
- } //end for
-} //end of the function BotDumpMatchTemplates
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeMatchPieces(bot_matchpiece_t *matchpieces)
-{
- bot_matchpiece_t *mp, *nextmp;
- bot_matchstring_t *ms, *nextms;
-
- for (mp = matchpieces; mp; mp = nextmp)
- {
- nextmp = mp->next;
- if (mp->type == MT_STRING)
- {
- for (ms = mp->firststring; ms; ms = nextms)
- {
- nextms = ms->next;
- FreeMemory(ms);
- } //end for
- } //end if
- FreeMemory(mp);
- } //end for
-} //end of the function BotFreeMatchPieces
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken)
-{
- int lastwasvariable, emptystring;
- token_t token;
- bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece;
- bot_matchstring_t *matchstring, *lastmatchstring;
-
- firstpiece = NULL;
- lastpiece = NULL;
- //
- lastwasvariable = qfalse;
- //
- while(PC_ReadToken(source, &token))
- {
- if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))
- {
- if (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES)
- {
- SourceError(source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES);
- FreeSource(source);
- BotFreeMatchPieces(firstpiece);
- return NULL;
- } //end if
- if (lastwasvariable)
- {
- SourceError(source, "not allowed to have adjacent variables\n");
- FreeSource(source);
- BotFreeMatchPieces(firstpiece);
- return NULL;
- } //end if
- lastwasvariable = qtrue;
- //
- matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));
- matchpiece->type = MT_VARIABLE;
- matchpiece->variable = token.intvalue;
- matchpiece->next = NULL;
- if (lastpiece) lastpiece->next = matchpiece;
- else firstpiece = matchpiece;
- lastpiece = matchpiece;
- } //end if
- else if (token.type == TT_STRING)
- {
- //
- matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));
- matchpiece->firststring = NULL;
- matchpiece->type = MT_STRING;
- matchpiece->variable = 0;
- matchpiece->next = NULL;
- if (lastpiece) lastpiece->next = matchpiece;
- else firstpiece = matchpiece;
- lastpiece = matchpiece;
- //
- lastmatchstring = NULL;
- emptystring = qfalse;
- //
- do
- {
- if (matchpiece->firststring)
- {
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- FreeSource(source);
- BotFreeMatchPieces(firstpiece);
- return NULL;
- } //end if
- } //end if
- StripDoubleQuotes(token.string);
- matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1);
- matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t);
- strcpy(matchstring->string, token.string);
- if (!strlen(token.string)) emptystring = qtrue;
- matchstring->next = NULL;
- if (lastmatchstring) lastmatchstring->next = matchstring;
- else matchpiece->firststring = matchstring;
- lastmatchstring = matchstring;
- } while(PC_CheckTokenString(source, "|"));
- //if there was no empty string found
- if (!emptystring) lastwasvariable = qfalse;
- } //end if
- else
- {
- SourceError(source, "invalid token %s\n", token.string);
- FreeSource(source);
- BotFreeMatchPieces(firstpiece);
- return NULL;
- } //end else
- if (PC_CheckTokenString(source, endtoken)) break;
- if (!PC_ExpectTokenString(source, ","))
- {
- FreeSource(source);
- BotFreeMatchPieces(firstpiece);
- return NULL;
- } //end if
- } //end while
- return firstpiece;
-} //end of the function BotLoadMatchPieces
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeMatchTemplates(bot_matchtemplate_t *mt)
-{
- bot_matchtemplate_t *nextmt;
-
- for (; mt; mt = nextmt)
- {
- nextmt = mt->next;
- BotFreeMatchPieces(mt->first);
- FreeMemory(mt);
- } //end for
-} //end of the function BotFreeMatchTemplates
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile)
-{
- source_t *source;
- token_t token;
- bot_matchtemplate_t *matchtemplate, *matches, *lastmatch;
- unsigned long int context;
-
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(matchfile);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile);
- return NULL;
- } //end if
- //
- matches = NULL; //list with matches
- lastmatch = NULL; //last match in the list
-
- while(PC_ReadToken(source, &token))
- {
- if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
- {
- SourceError(source, "expected integer, found %s\n", token.string);
- BotFreeMatchTemplates(matches);
- FreeSource(source);
- return NULL;
- } //end if
- //the context
- context = token.intvalue;
- //
- if (!PC_ExpectTokenString(source, "{"))
- {
- BotFreeMatchTemplates(matches);
- FreeSource(source);
- return NULL;
- } //end if
- //
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "}")) break;
- //
- PC_UnreadLastToken(source);
- //
- matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t));
- matchtemplate->context = context;
- matchtemplate->next = NULL;
- //add the match template to the list
- if (lastmatch) lastmatch->next = matchtemplate;
- else matches = matchtemplate;
- lastmatch = matchtemplate;
- //load the match template
- matchtemplate->first = BotLoadMatchPieces(source, "=");
- if (!matchtemplate->first)
- {
- BotFreeMatchTemplates(matches);
- return NULL;
- } //end if
- //read the match type
- if (!PC_ExpectTokenString(source, "(") ||
- !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
- {
- BotFreeMatchTemplates(matches);
- FreeSource(source);
- return NULL;
- } //end if
- matchtemplate->type = token.intvalue;
- //read the match subtype
- if (!PC_ExpectTokenString(source, ",") ||
- !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
- {
- BotFreeMatchTemplates(matches);
- FreeSource(source);
- return NULL;
- } //end if
- matchtemplate->subtype = token.intvalue;
- //read trailing punctuations
- if (!PC_ExpectTokenString(source, ")") ||
- !PC_ExpectTokenString(source, ";"))
- {
- BotFreeMatchTemplates(matches);
- FreeSource(source);
- return NULL;
- } //end if
- } //end while
- } //end while
- //free the source
- FreeSource(source);
- botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile);
- //
- //BotDumpMatchTemplates(matches);
- //
- return matches;
-} //end of the function BotLoadMatchTemplates
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match)
-{
- int lastvariable, index;
- char *strptr, *newstrptr;
- bot_matchpiece_t *mp;
- bot_matchstring_t *ms;
-
- //no last variable
- lastvariable = -1;
- //pointer to the string to compare the match string with
- strptr = match->string;
- //Log_Write("match: %s", strptr);
- //compare the string with the current match string
- for (mp = pieces; mp; mp = mp->next)
- {
- //if it is a piece of string
- if (mp->type == MT_STRING)
- {
- newstrptr = NULL;
- for (ms = mp->firststring; ms; ms = ms->next)
- {
- if (!strlen(ms->string))
- {
- newstrptr = strptr;
- break;
- } //end if
- //Log_Write("MT_STRING: %s", mp->string);
- index = StringContains(strptr, ms->string, qfalse);
- if (index >= 0)
- {
- newstrptr = strptr + index;
- if (lastvariable >= 0)
- {
- match->variables[lastvariable].length =
- (newstrptr - match->string) - match->variables[lastvariable].offset;
- //newstrptr - match->variables[lastvariable].ptr;
- lastvariable = -1;
- break;
- } //end if
- else if (index == 0)
- {
- break;
- } //end else
- newstrptr = NULL;
- } //end if
- } //end for
- if (!newstrptr) return qfalse;
- strptr = newstrptr + strlen(ms->string);
- } //end if
- //if it is a variable piece of string
- else if (mp->type == MT_VARIABLE)
- {
- //Log_Write("MT_VARIABLE");
- match->variables[mp->variable].offset = strptr - match->string;
- lastvariable = mp->variable;
- } //end else if
- } //end for
- //if a match was found
- if (!mp && (lastvariable >= 0 || !strlen(strptr)))
- {
- //if the last piece was a variable string
- if (lastvariable >= 0)
- {
- assert( match->variables[lastvariable].offset >= 0 ); // bk001204
- match->variables[lastvariable].length =
- strlen(&match->string[ (int) match->variables[lastvariable].offset]);
- } //end if
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function StringsMatch
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotFindMatch(char *str, bot_match_t *match, unsigned long int context)
-{
- int i;
- bot_matchtemplate_t *ms;
-
- strncpy(match->string, str, MAX_MESSAGE_SIZE);
- //remove any trailing enters
- while(strlen(match->string) &&
- match->string[strlen(match->string)-1] == '\n')
- {
- match->string[strlen(match->string)-1] = '\0';
- } //end while
- //compare the string with all the match strings
- for (ms = matchtemplates; ms; ms = ms->next)
- {
- if (!(ms->context & context)) continue;
- //reset the match variable offsets
- for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1;
- //
- if (StringsMatch(ms->first, match))
- {
- match->type = ms->type;
- match->subtype = ms->subtype;
- return qtrue;
- } //end if
- } //end for
- return qfalse;
-} //end of the function BotFindMatch
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size)
-{
- if (variable < 0 || variable >= MAX_MATCHVARIABLES)
- {
- botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n");
- strcpy(buf, "");
- return;
- } //end if
-
- if (match->variables[variable].offset >= 0)
- {
- if (match->variables[variable].length < size)
- size = match->variables[variable].length+1;
- assert( match->variables[variable].offset >= 0 ); // bk001204
- strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1);
- buf[size-1] = '\0';
- } //end if
- else
- {
- strcpy(buf, "");
- } //end else
- return;
-} //end of the function BotMatchVariable
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string)
-{
- bot_stringlist_t *s;
-
- for (s = list; s; s = s->next)
- {
- if (!strcmp(s->string, string)) return s;
- } //end for
- return NULL;
-} //end of the function BotFindStringInList
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist)
-{
- int i;
- char *msgptr;
- char temp[MAX_MESSAGE_SIZE];
- bot_stringlist_t *s;
-
- msgptr = message;
- //
- while(*msgptr)
- {
- if (*msgptr == ESCAPE_CHAR)
- {
- msgptr++;
- switch(*msgptr)
- {
- case 'v': //variable
- {
- //step over the 'v'
- msgptr++;
- while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++;
- //step over the trailing escape char
- if (*msgptr) msgptr++;
- break;
- } //end case
- case 'r': //random
- {
- //step over the 'r'
- msgptr++;
- for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
- {
- temp[i] = *msgptr++;
- } //end while
- temp[i] = '\0';
- //step over the trailing escape char
- if (*msgptr) msgptr++;
- //find the random keyword
- if (!RandomString(temp))
- {
- if (!BotFindStringInList(stringlist, temp))
- {
- Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp);
- s = GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1);
- s->string = (char *) s + sizeof(bot_stringlist_t);
- strcpy(s->string, temp);
- s->next = stringlist;
- stringlist = s;
- } //end if
- } //end if
- break;
- } //end case
- default:
- {
- botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message);
- break;
- } //end default
- } //end switch
- } //end if
- else
- {
- msgptr++;
- } //end else
- } //end while
- return stringlist;
-} //end of the function BotCheckChatMessageIntegrety
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotCheckInitialChatIntegrety(bot_chat_t *chat)
-{
- bot_chattype_t *t;
- bot_chatmessage_t *cm;
- bot_stringlist_t *stringlist, *s, *nexts;
-
- stringlist = NULL;
- for (t = chat->types; t; t = t->next)
- {
- for (cm = t->firstchatmessage; cm; cm = cm->next)
- {
- stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);
- } //end for
- } //end for
- for (s = stringlist; s; s = nexts)
- {
- nexts = s->next;
- FreeMemory(s);
- } //end for
-} //end of the function BotCheckInitialChatIntegrety
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotCheckReplyChatIntegrety(bot_replychat_t *replychat)
-{
- bot_replychat_t *rp;
- bot_chatmessage_t *cm;
- bot_stringlist_t *stringlist, *s, *nexts;
-
- stringlist = NULL;
- for (rp = replychat; rp; rp = rp->next)
- {
- for (cm = rp->firstchatmessage; cm; cm = cm->next)
- {
- stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);
- } //end for
- } //end for
- for (s = stringlist; s; s = nexts)
- {
- nexts = s->next;
- FreeMemory(s);
- } //end for
-} //end of the function BotCheckReplyChatIntegrety
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpReplyChat(bot_replychat_t *replychat)
-{
- FILE *fp;
- bot_replychat_t *rp;
- bot_replychatkey_t *key;
- bot_chatmessage_t *cm;
- bot_matchpiece_t *mp;
-
- fp = Log_FilePointer();
- if (!fp) return;
- fprintf(fp, "BotDumpReplyChat:\n");
- for (rp = replychat; rp; rp = rp->next)
- {
- fprintf(fp, "[");
- for (key = rp->keys; key; key = key->next)
- {
- if (key->flags & RCKFL_AND) fprintf(fp, "&");
- else if (key->flags & RCKFL_NOT) fprintf(fp, "!");
- //
- if (key->flags & RCKFL_NAME) fprintf(fp, "name");
- else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female");
- else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male");
- else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it");
- else if (key->flags & RCKFL_VARIABLES)
- {
- fprintf(fp, "(");
- for (mp = key->match; mp; mp = mp->next)
- {
- if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string);
- else fprintf(fp, "%d", mp->variable);
- if (mp->next) fprintf(fp, ", ");
- } //end for
- fprintf(fp, ")");
- } //end if
- else if (key->flags & RCKFL_STRING)
- {
- fprintf(fp, "\"%s\"", key->string);
- } //end if
- if (key->next) fprintf(fp, ", ");
- else fprintf(fp, "] = %1.0f\n", rp->priority);
- } //end for
- fprintf(fp, "{\n");
- for (cm = rp->firstchatmessage; cm; cm = cm->next)
- {
- fprintf(fp, "\t\"%s\";\n", cm->chatmessage);
- } //end for
- fprintf(fp, "}\n");
- } //end for
-} //end of the function BotDumpReplyChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeReplyChat(bot_replychat_t *replychat)
-{
- bot_replychat_t *rp, *nextrp;
- bot_replychatkey_t *key, *nextkey;
- bot_chatmessage_t *cm, *nextcm;
-
- for (rp = replychat; rp; rp = nextrp)
- {
- nextrp = rp->next;
- for (key = rp->keys; key; key = nextkey)
- {
- nextkey = key->next;
- if (key->match) BotFreeMatchPieces(key->match);
- if (key->string) FreeMemory(key->string);
- FreeMemory(key);
- } //end for
- for (cm = rp->firstchatmessage; cm; cm = nextcm)
- {
- nextcm = cm->next;
- FreeMemory(cm);
- } //end for
- FreeMemory(rp);
- } //end for
-} //end of the function BotFreeReplyChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys)
-{
- int allprefixed, hasvariableskey, hasstringkey;
- bot_matchpiece_t *m;
- bot_matchstring_t *ms;
- bot_replychatkey_t *key, *key2;
-
- //
- allprefixed = qtrue;
- hasvariableskey = hasstringkey = qfalse;
- for (key = keys; key; key = key->next)
- {
- if (!(key->flags & (RCKFL_AND|RCKFL_NOT)))
- {
- allprefixed = qfalse;
- if (key->flags & RCKFL_VARIABLES)
- {
- for (m = key->match; m; m = m->next)
- {
- if (m->type == MT_VARIABLE) hasvariableskey = qtrue;
- } //end for
- } //end if
- else if (key->flags & RCKFL_STRING)
- {
- hasstringkey = qtrue;
- } //end else if
- } //end if
- else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING))
- {
- for (key2 = keys; key2; key2 = key2->next)
- {
- if (key2 == key) continue;
- if (key2->flags & RCKFL_NOT) continue;
- if (key2->flags & RCKFL_VARIABLES)
- {
- for (m = key2->match; m; m = m->next)
- {
- if (m->type == MT_STRING)
- {
- for (ms = m->firststring; ms; ms = ms->next)
- {
- if (StringContains(ms->string, key->string, qfalse) != -1)
- {
- break;
- } //end if
- } //end for
- if (ms) break;
- } //end if
- else if (m->type == MT_VARIABLE)
- {
- break;
- } //end if
- } //end for
- if (!m)
- {
- SourceWarning(source, "one of the match templates does not "
- "leave space for the key %s with the & prefix", key->string);
- } //end if
- } //end if
- } //end for
- } //end else
- if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING))
- {
- for (key2 = keys; key2; key2 = key2->next)
- {
- if (key2 == key) continue;
- if (key2->flags & RCKFL_NOT) continue;
- if (key2->flags & RCKFL_STRING)
- {
- if (StringContains(key2->string, key->string, qfalse) != -1)
- {
- SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string);
- } //end if
- } //end if
- else if (key2->flags & RCKFL_VARIABLES)
- {
- for (m = key2->match; m; m = m->next)
- {
- if (m->type == MT_STRING)
- {
- for (ms = m->firststring; ms; ms = ms->next)
- {
- if (StringContains(ms->string, key->string, qfalse) != -1)
- {
- SourceWarning(source, "the key %s with prefix ! is inside "
- "the match template string %s", key->string, ms->string);
- } //end if
- } //end for
- } //end if
- } //end for
- } //end else if
- } //end for
- } //end if
- } //end for
- if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix");
- if (hasvariableskey && hasstringkey)
- {
- SourceWarning(source, "variables from the match template(s) could be "
- "invalid when outputting one of the chat messages");
- } //end if
-} //end of the function BotCheckValidReplyChatKeySet
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_replychat_t *BotLoadReplyChat(char *filename)
-{
- char chatmessagestring[MAX_MESSAGE_SIZE];
- char namebuffer[MAX_MESSAGE_SIZE];
- source_t *source;
- token_t token;
- bot_chatmessage_t *chatmessage = NULL;
- bot_replychat_t *replychat, *replychatlist;
- bot_replychatkey_t *key;
-
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(filename);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
- return NULL;
- } //end if
- //
- replychatlist = NULL;
- //
- while(PC_ReadToken(source, &token))
- {
- if (strcmp(token.string, "["))
- {
- SourceError(source, "expected [, found %s", token.string);
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- //
- replychat = GetClearedHunkMemory(sizeof(bot_replychat_t));
- replychat->keys = NULL;
- replychat->next = replychatlist;
- replychatlist = replychat;
- //read the keys, there must be at least one key
- do
- {
- //allocate a key
- key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t));
- key->flags = 0;
- key->string = NULL;
- key->match = NULL;
- key->next = replychat->keys;
- replychat->keys = key;
- //check for MUST BE PRESENT and MUST BE ABSENT keys
- if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND;
- else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT;
- //special keys
- if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME;
- else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE;
- else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE;
- else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS;
- else if (PC_CheckTokenString(source, "(")) //match key
- {
- key->flags |= RCKFL_VARIABLES;
- key->match = BotLoadMatchPieces(source, ")");
- if (!key->match)
- {
- BotFreeReplyChat(replychatlist);
- return NULL;
- } //end if
- } //end else if
- else if (PC_CheckTokenString(source, "<")) //bot names
- {
- key->flags |= RCKFL_BOTNAMES;
- strcpy(namebuffer, "");
- do
- {
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- if (strlen(namebuffer)) strcat(namebuffer, "\\");
- strcat(namebuffer, token.string);
- } while(PC_CheckTokenString(source, ","));
- if (!PC_ExpectTokenString(source, ">"))
- {
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1);
- strcpy(key->string, namebuffer);
- } //end else if
- else //normal string key
- {
- key->flags |= RCKFL_STRING;
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1);
- strcpy(key->string, token.string);
- } //end else
- //
- PC_CheckTokenString(source, ",");
- } while(!PC_CheckTokenString(source, "]"));
- //
- BotCheckValidReplyChatKeySet(source, replychat->keys);
- //read the = sign and the priority
- if (!PC_ExpectTokenString(source, "=") ||
- !PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
- {
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- replychat->priority = token.floatvalue;
- //read the leading {
- if (!PC_ExpectTokenString(source, "{"))
- {
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- replychat->numchatmessages = 0;
- //while the trailing } is not found
- while(!PC_CheckTokenString(source, "}"))
- {
- if (!BotLoadChatMessage(source, chatmessagestring))
- {
- BotFreeReplyChat(replychatlist);
- FreeSource(source);
- return NULL;
- } //end if
- chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1);
- chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t);
- strcpy(chatmessage->chatmessage, chatmessagestring);
- chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
- chatmessage->next = replychat->firstchatmessage;
- //add the chat message to the reply chat
- replychat->firstchatmessage = chatmessage;
- replychat->numchatmessages++;
- } //end while
- } //end while
- FreeSource(source);
- botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
- //
- //BotDumpReplyChat(replychatlist);
- if (bot_developer)
- {
- BotCheckReplyChatIntegrety(replychatlist);
- } //end if
- //
- if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n");
- //
- return replychatlist;
-} //end of the function BotLoadReplyChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpInitialChat(bot_chat_t *chat)
-{
- bot_chattype_t *t;
- bot_chatmessage_t *m;
-
- Log_Write("{");
- for (t = chat->types; t; t = t->next)
- {
- Log_Write(" type \"%s\"", t->name);
- Log_Write(" {");
- Log_Write(" numchatmessages = %d", t->numchatmessages);
- for (m = t->firstchatmessage; m; m = m->next)
- {
- Log_Write(" \"%s\"", m->chatmessage);
- } //end for
- Log_Write(" }");
- } //end for
- Log_Write("}");
-} //end of the function BotDumpInitialChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname)
-{
- int pass, foundchat, indent, size;
- char *ptr = NULL;
- char chatmessagestring[MAX_MESSAGE_SIZE];
- source_t *source;
- token_t token;
- bot_chat_t *chat = NULL;
- bot_chattype_t *chattype = NULL;
- bot_chatmessage_t *chatmessage = NULL;
-#ifdef DEBUG
- int starttime;
-
- starttime = Sys_MilliSeconds();
-#endif //DEBUG
- //
- size = 0;
- foundchat = qfalse;
- //a bot chat is parsed in two phases
- for (pass = 0; pass < 2; pass++)
- {
- //allocate memory
- if (pass && size) ptr = (char *) GetClearedMemory(size);
- //load the source file
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(chatfile);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile);
- return NULL;
- } //end if
- //chat structure
- if (pass)
- {
- chat = (bot_chat_t *) ptr;
- ptr += sizeof(bot_chat_t);
- } //end if
- size = sizeof(bot_chat_t);
- //
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "chat"))
- {
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- FreeSource(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- //after the chat name we expect a opening brace
- if (!PC_ExpectTokenString(source, "{"))
- {
- FreeSource(source);
- return NULL;
- } //end if
- //if the chat name is found
- if (!Q_stricmp(token.string, chatname))
- {
- foundchat = qtrue;
- //read the chat types
- while(1)
- {
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeSource(source);
- return NULL;
- } //end if
- if (!strcmp(token.string, "}")) break;
- if (strcmp(token.string, "type"))
- {
- SourceError(source, "expected type found %s\n", token.string);
- FreeSource(source);
- return NULL;
- } //end if
- //expect the chat type name
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) ||
- !PC_ExpectTokenString(source, "{"))
- {
- FreeSource(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- if (pass)
- {
- chattype = (bot_chattype_t *) ptr;
- strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME);
- chattype->firstchatmessage = NULL;
- //add the chat type to the chat
- chattype->next = chat->types;
- chat->types = chattype;
- //
- ptr += sizeof(bot_chattype_t);
- } //end if
- size += sizeof(bot_chattype_t);
- //read the chat messages
- while(!PC_CheckTokenString(source, "}"))
- {
- if (!BotLoadChatMessage(source, chatmessagestring))
- {
- FreeSource(source);
- return NULL;
- } //end if
- if (pass)
- {
- chatmessage = (bot_chatmessage_t *) ptr;
- chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
- //put the chat message in the list
- chatmessage->next = chattype->firstchatmessage;
- chattype->firstchatmessage = chatmessage;
- //store the chat message
- ptr += sizeof(bot_chatmessage_t);
- chatmessage->chatmessage = ptr;
- strcpy(chatmessage->chatmessage, chatmessagestring);
- ptr += strlen(chatmessagestring) + 1;
- //the number of chat messages increased
- chattype->numchatmessages++;
- } //end if
- size += sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1;
- } //end if
- } //end while
- } //end if
- else //skip the bot chat
- {
- indent = 1;
- while(indent)
- {
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeSource(source);
- return NULL;
- } //end if
- if (!strcmp(token.string, "{")) indent++;
- else if (!strcmp(token.string, "}")) indent--;
- } //end while
- } //end else
- } //end if
- else
- {
- SourceError(source, "unknown definition %s\n", token.string);
- FreeSource(source);
- return NULL;
- } //end else
- } //end while
- //free the source
- FreeSource(source);
- //if the requested character is not found
- if (!foundchat)
- {
- botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile);
- return NULL;
- } //end if
- } //end for
- //
- botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile);
- //
- //BotDumpInitialChat(chat);
- if (bot_developer)
- {
- BotCheckInitialChatIntegrety(chat);
- } //end if
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime);
-#endif //DEBUG
- //character was read succesfully
- return chat;
-} //end of the function BotLoadInitialChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeChatFile(int chatstate)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
- if (cs->chat) FreeMemory(cs->chat);
- cs->chat = NULL;
-} //end of the function BotFreeChatFile
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadChatFile(int chatstate, char *chatfile, char *chatname)
-{
- bot_chatstate_t *cs;
- int n, avail = 0;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return BLERR_CANNOTLOADICHAT;
- BotFreeChatFile(chatstate);
-
- if (!LibVarGetValue("bot_reloadcharacters"))
- {
- avail = -1;
- for( n = 0; n < MAX_CLIENTS; n++ ) {
- if( !ichatdata[n] ) {
- if( avail == -1 ) {
- avail = n;
- }
- continue;
- }
- if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) {
- continue;
- }
- if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) {
- continue;
- }
- cs->chat = ichatdata[n]->chat;
- // botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile );
- return BLERR_NOERROR;
- }
-
- if( avail == -1 ) {
- botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile);
- return BLERR_CANNOTLOADICHAT;
- }
- }
-
- cs->chat = BotLoadInitialChat(chatfile, chatname);
- if (!cs->chat)
- {
- botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile);
- return BLERR_CANNOTLOADICHAT;
- } //end if
- if (!LibVarGetValue("bot_reloadcharacters"))
- {
- ichatdata[avail] = GetClearedMemory( sizeof(bot_ichatdata_t) );
- ichatdata[avail]->chat = cs->chat;
- Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) );
- Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) );
- } //end if
-
- return BLERR_NOERROR;
-} //end of the function BotLoadChatFile
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext,
- bot_match_t *match, unsigned long vcontext, int reply)
-{
- int num, len, i, expansion;
- char *outputbuf, *ptr, *msgptr;
- char temp[MAX_MESSAGE_SIZE];
-
- expansion = qfalse;
- msgptr = message;
- outputbuf = outmessage;
- len = 0;
- //
- while(*msgptr)
- {
- if (*msgptr == ESCAPE_CHAR)
- {
- msgptr++;
- switch(*msgptr)
- {
- case 'v': //variable
- {
- msgptr++;
- num = 0;
- while(*msgptr && *msgptr != ESCAPE_CHAR)
- {
- num = num * 10 + (*msgptr++) - '0';
- } //end while
- //step over the trailing escape char
- if (*msgptr) msgptr++;
- if (num > MAX_MATCHVARIABLES)
- {
- botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num);
- return qfalse;
- } //end if
- if (match->variables[num].offset >= 0)
- {
- assert( match->variables[num].offset >= 0 ); // bk001204
- ptr = &match->string[ (int) match->variables[num].offset];
- for (i = 0; i < match->variables[num].length; i++)
- {
- temp[i] = ptr[i];
- } //end for
- temp[i] = 0;
- //if it's a reply message
- if (reply)
- {
- //replace the reply synonyms in the variables
- BotReplaceReplySynonyms(temp, vcontext);
- } //end if
- else
- {
- //replace synonyms in the variable context
- BotReplaceSynonyms(temp, vcontext);
- } //end else
- //
- if (len + strlen(temp) >= MAX_MESSAGE_SIZE)
- {
- botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message);
- return qfalse;
- } //end if
- strcpy(&outputbuf[len], temp);
- len += strlen(temp);
- } //end if
- break;
- } //end case
- case 'r': //random
- {
- msgptr++;
- for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
- {
- temp[i] = *msgptr++;
- } //end while
- temp[i] = '\0';
- //step over the trailing escape char
- if (*msgptr) msgptr++;
- //find the random keyword
- ptr = RandomString(temp);
- if (!ptr)
- {
- botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp);
- return qfalse;
- } //end if
- if (len + strlen(ptr) >= MAX_MESSAGE_SIZE)
- {
- botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message);
- return qfalse;
- } //end if
- strcpy(&outputbuf[len], ptr);
- len += strlen(ptr);
- expansion = qtrue;
- break;
- } //end case
- default:
- {
- botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message);
- break;
- } //end default
- } //end switch
- } //end if
- else
- {
- outputbuf[len++] = *msgptr++;
- if (len >= MAX_MESSAGE_SIZE)
- {
- botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message);
- break;
- } //end if
- } //end else
- } //end while
- outputbuf[len] = '\0';
- //replace synonyms weighted in the message context
- BotReplaceWeightedSynonyms(outputbuf, mcontext);
- //return true if a random was expanded
- return expansion;
-} //end of the function BotExpandChatMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext,
- bot_match_t *match, unsigned long vcontext, int reply)
-{
- int i;
- char srcmessage[MAX_MESSAGE_SIZE];
-
- strcpy(srcmessage, message);
- for (i = 0; i < 10; i++)
- {
- if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply))
- {
- break;
- } //end if
- strcpy(srcmessage, chatstate->chatmessage);
- } //end for
- if (i >= 10)
- {
- botimport.Print(PRT_WARNING, "too many expansions in chat message\n");
- botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage);
- } //end if
-} //end of the function BotConstructChatMessage
-//===========================================================================
-// randomly chooses one of the chat message of the given type
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type)
-{
- int n, numchatmessages;
- float besttime;
- bot_chattype_t *t;
- bot_chatmessage_t *m, *bestchatmessage;
- bot_chat_t *chat;
-
- chat = cs->chat;
- for (t = chat->types; t; t = t->next)
- {
- if (!Q_stricmp(t->name, type))
- {
- numchatmessages = 0;
- for (m = t->firstchatmessage; m; m = m->next)
- {
- if (m->time > AAS_Time()) continue;
- numchatmessages++;
- } //end if
- //if all chat messages have been used recently
- if (numchatmessages <= 0)
- {
- besttime = 0;
- bestchatmessage = NULL;
- for (m = t->firstchatmessage; m; m = m->next)
- {
- if (!besttime || m->time < besttime)
- {
- bestchatmessage = m;
- besttime = m->time;
- } //end if
- } //end for
- if (bestchatmessage) return bestchatmessage->chatmessage;
- } //end if
- else //choose a chat message randomly
- {
- n = random() * numchatmessages;
- for (m = t->firstchatmessage; m; m = m->next)
- {
- if (m->time > AAS_Time()) continue;
- if (--n < 0)
- {
- m->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
- return m->chatmessage;
- } //end if
- } //end for
- } //end else
- return NULL;
- } //end if
- } //end for
- return NULL;
-} //end of the function BotChooseInitialChatMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotNumInitialChats(int chatstate, char *type)
-{
- bot_chatstate_t *cs;
- bot_chattype_t *t;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return 0;
-
- for (t = cs->chat->types; t; t = t->next)
- {
- if (!Q_stricmp(t->name, type))
- {
- if (LibVarGetValue("bot_testichat")) {
- botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages);
- botimport.Print(PRT_MESSAGE, "-------------------\n");
- }
- return t->numchatmessages;
- } //end if
- } //end for
- return 0;
-} //end of the function BotNumInitialChats
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)
-{
- char *message;
- int index;
- bot_match_t match;
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
- //if no chat file is loaded
- if (!cs->chat) return;
- //choose a chat message randomly of the given type
- message = BotChooseInitialChatMessage(cs, type);
- //if there's no message of the given type
- if (!message)
- {
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type);
-#endif //DEBUG
- return;
- } //end if
- //
- Com_Memset(&match, 0, sizeof(match));
- index = 0;
- if( var0 ) {
- strcat(match.string, var0);
- match.variables[0].offset = index;
- match.variables[0].length = strlen(var0);
- index += strlen(var0);
- }
- if( var1 ) {
- strcat(match.string, var1);
- match.variables[1].offset = index;
- match.variables[1].length = strlen(var1);
- index += strlen(var1);
- }
- if( var2 ) {
- strcat(match.string, var2);
- match.variables[2].offset = index;
- match.variables[2].length = strlen(var2);
- index += strlen(var2);
- }
- if( var3 ) {
- strcat(match.string, var3);
- match.variables[3].offset = index;
- match.variables[3].length = strlen(var3);
- index += strlen(var3);
- }
- if( var4 ) {
- strcat(match.string, var4);
- match.variables[4].offset = index;
- match.variables[4].length = strlen(var4);
- index += strlen(var4);
- }
- if( var5 ) {
- strcat(match.string, var5);
- match.variables[5].offset = index;
- match.variables[5].length = strlen(var5);
- index += strlen(var5);
- }
- if( var6 ) {
- strcat(match.string, var6);
- match.variables[6].offset = index;
- match.variables[6].length = strlen(var6);
- index += strlen(var6);
- }
- if( var7 ) {
- strcat(match.string, var7);
- match.variables[7].offset = index;
- match.variables[7].length = strlen(var7);
- index += strlen(var7);
- }
- //
- BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse);
-} //end of the function BotInitialChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotPrintReplyChatKeys(bot_replychat_t *replychat)
-{
- bot_replychatkey_t *key;
- bot_matchpiece_t *mp;
-
- botimport.Print(PRT_MESSAGE, "[");
- for (key = replychat->keys; key; key = key->next)
- {
- if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&");
- else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!");
- //
- if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name");
- else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female");
- else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male");
- else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it");
- else if (key->flags & RCKFL_VARIABLES)
- {
- botimport.Print(PRT_MESSAGE, "(");
- for (mp = key->match; mp; mp = mp->next)
- {
- if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string);
- else botimport.Print(PRT_MESSAGE, "%d", mp->variable);
- if (mp->next) botimport.Print(PRT_MESSAGE, ", ");
- } //end for
- botimport.Print(PRT_MESSAGE, ")");
- } //end if
- else if (key->flags & RCKFL_STRING)
- {
- botimport.Print(PRT_MESSAGE, "\"%s\"", key->string);
- } //end if
- if (key->next) botimport.Print(PRT_MESSAGE, ", ");
- else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority);
- } //end for
- botimport.Print(PRT_MESSAGE, "{\n");
-} //end of the function BotPrintReplyChatKeys
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)
-{
- bot_replychat_t *rchat, *bestrchat;
- bot_replychatkey_t *key;
- bot_chatmessage_t *m, *bestchatmessage;
- bot_match_t match, bestmatch;
- int bestpriority, num, found, res, numchatmessages, index;
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return qfalse;
- Com_Memset(&match, 0, sizeof(bot_match_t));
- strcpy(match.string, message);
- bestpriority = -1;
- bestchatmessage = NULL;
- bestrchat = NULL;
- //go through all the reply chats
- for (rchat = replychats; rchat; rchat = rchat->next)
- {
- found = qfalse;
- for (key = rchat->keys; key; key = key->next)
- {
- res = qfalse;
- //get the match result
- if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1);
- else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1);
- else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE);
- else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE);
- else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS);
- else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match);
- else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL);
- //if the key must be present
- if (key->flags & RCKFL_AND)
- {
- if (!res)
- {
- found = qfalse;
- break;
- } //end if
- } //end else if
- //if the key must be absent
- else if (key->flags & RCKFL_NOT)
- {
- if (res)
- {
- found = qfalse;
- break;
- } //end if
- } //end if
- else if (res)
- {
- found = qtrue;
- } //end else
- } //end for
- //
- if (found)
- {
- if (rchat->priority > bestpriority)
- {
- numchatmessages = 0;
- for (m = rchat->firstchatmessage; m; m = m->next)
- {
- if (m->time > AAS_Time()) continue;
- numchatmessages++;
- } //end if
- num = random() * numchatmessages;
- for (m = rchat->firstchatmessage; m; m = m->next)
- {
- if (--num < 0) break;
- if (m->time > AAS_Time()) continue;
- } //end for
- //if the reply chat has a message
- if (m)
- {
- Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t));
- bestchatmessage = m;
- bestrchat = rchat;
- bestpriority = rchat->priority;
- } //end if
- } //end if
- } //end if
- } //end for
- if (bestchatmessage)
- {
- index = strlen(bestmatch.string);
- if( var0 ) {
- strcat(bestmatch.string, var0);
- bestmatch.variables[0].offset = index;
- bestmatch.variables[0].length = strlen(var0);
- index += strlen(var0);
- }
- if( var1 ) {
- strcat(bestmatch.string, var1);
- bestmatch.variables[1].offset = index;
- bestmatch.variables[1].length = strlen(var1);
- index += strlen(var1);
- }
- if( var2 ) {
- strcat(bestmatch.string, var2);
- bestmatch.variables[2].offset = index;
- bestmatch.variables[2].length = strlen(var2);
- index += strlen(var2);
- }
- if( var3 ) {
- strcat(bestmatch.string, var3);
- bestmatch.variables[3].offset = index;
- bestmatch.variables[3].length = strlen(var3);
- index += strlen(var3);
- }
- if( var4 ) {
- strcat(bestmatch.string, var4);
- bestmatch.variables[4].offset = index;
- bestmatch.variables[4].length = strlen(var4);
- index += strlen(var4);
- }
- if( var5 ) {
- strcat(bestmatch.string, var5);
- bestmatch.variables[5].offset = index;
- bestmatch.variables[5].length = strlen(var5);
- index += strlen(var5);
- }
- if( var6 ) {
- strcat(bestmatch.string, var6);
- bestmatch.variables[6].offset = index;
- bestmatch.variables[6].length = strlen(var6);
- index += strlen(var6);
- }
- if( var7 ) {
- strcat(bestmatch.string, var7);
- bestmatch.variables[7].offset = index;
- bestmatch.variables[7].length = strlen(var7);
- index += strlen(var7);
- }
- if (LibVarGetValue("bot_testrchat"))
- {
- for (m = bestrchat->firstchatmessage; m; m = m->next)
- {
- BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
- BotRemoveTildes(cs->chatmessage);
- botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
- } //end if
- } //end if
- else
- {
- bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
- BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
- } //end else
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function BotReplyChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotChatLength(int chatstate)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return 0;
- return strlen(cs->chatmessage);
-} //end of the function BotChatLength
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotEnterChat(int chatstate, int clientto, int sendto)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
-
- if (strlen(cs->chatmessage))
- {
- BotRemoveTildes(cs->chatmessage);
- if (LibVarGetValue("bot_testichat")) {
- botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
- }
- else {
- switch(sendto) {
- case CHAT_TEAM:
- EA_Command(cs->client, va("say_team %s", cs->chatmessage));
- break;
- case CHAT_TELL:
- EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage));
- break;
- default: //CHAT_ALL
- EA_Command(cs->client, va("say %s", cs->chatmessage));
- break;
- }
- }
- //clear the chat message from the state
- strcpy(cs->chatmessage, "");
- } //end if
-} //end of the function BotEnterChat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotGetChatMessage(int chatstate, char *buf, int size)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
-
- BotRemoveTildes(cs->chatmessage);
- strncpy(buf, cs->chatmessage, size-1);
- buf[size-1] = '\0';
- //clear the chat message from the state
- strcpy(cs->chatmessage, "");
-} //end of the function BotGetChatMessage
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotSetChatGender(int chatstate, int gender)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
- switch(gender)
- {
- case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break;
- case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break;
- default: cs->gender = CHAT_GENDERLESS; break;
- } //end switch
-} //end of the function BotSetChatGender
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotSetChatName(int chatstate, char *name, int client)
-{
- bot_chatstate_t *cs;
-
- cs = BotChatStateFromHandle(chatstate);
- if (!cs) return;
- cs->client = client;
- Com_Memset(cs->name, 0, sizeof(cs->name));
- strncpy(cs->name, name, sizeof(cs->name));
- cs->name[sizeof(cs->name)-1] = '\0';
-} //end of the function BotSetChatName
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetChatAI(void)
-{
- bot_replychat_t *rchat;
- bot_chatmessage_t *m;
-
- for (rchat = replychats; rchat; rchat = rchat->next)
- {
- for (m = rchat->firstchatmessage; m; m = m->next)
- {
- m->time = 0;
- } //end for
- } //end for
-} //end of the function BotResetChatAI
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-int BotAllocChatState(void)
-{
- int i;
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (!botchatstates[i])
- {
- botchatstates[i] = GetClearedMemory(sizeof(bot_chatstate_t));
- return i;
- } //end if
- } //end for
- return 0;
-} //end of the function BotAllocChatState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeChatState(int handle)
-{
- bot_chatstate_t *cs;
- bot_consolemessage_t m;
- int h;
-
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle);
- return;
- } //end if
- if (!botchatstates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
- return;
- } //end if
- cs = botchatstates[handle];
- if (LibVarGetValue("bot_reloadcharacters"))
- {
- BotFreeChatFile(handle);
- } //end if
- //free all the console messages left in the chat state
- for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m))
- {
- //remove the console message
- BotRemoveConsoleMessage(handle, h);
- } //end for
- FreeMemory(botchatstates[handle]);
- botchatstates[handle] = NULL;
-} //end of the function BotFreeChatState
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotSetupChatAI(void)
-{
- char *file;
-
-#ifdef DEBUG
- int starttime = Sys_MilliSeconds();
-#endif //DEBUG
-
- file = LibVarString("synfile", "syn.c");
- synonyms = BotLoadSynonyms(file);
- file = LibVarString("rndfile", "rnd.c");
- randomstrings = BotLoadRandomStrings(file);
- file = LibVarString("matchfile", "match.c");
- matchtemplates = BotLoadMatchTemplates(file);
- //
- if (!LibVarValue("nochat", "0"))
- {
- file = LibVarString("rchatfile", "rchat.c");
- replychats = BotLoadReplyChat(file);
- } //end if
-
- InitConsoleMessageHeap();
-
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime);
-#endif //DEBUG
- return BLERR_NOERROR;
-} //end of the function BotSetupChatAI
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownChatAI(void)
-{
- int i;
-
- //free all remaining chat states
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if (botchatstates[i])
- {
- BotFreeChatState(i);
- } //end if
- } //end for
- //free all cached chats
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if (ichatdata[i])
- {
- FreeMemory(ichatdata[i]->chat);
- FreeMemory(ichatdata[i]);
- ichatdata[i] = NULL;
- } //end if
- } //end for
- if (consolemessageheap) FreeMemory(consolemessageheap);
- consolemessageheap = NULL;
- if (matchtemplates) BotFreeMatchTemplates(matchtemplates);
- matchtemplates = NULL;
- if (randomstrings) FreeMemory(randomstrings);
- randomstrings = NULL;
- if (synonyms) FreeMemory(synonyms);
- synonyms = NULL;
- if (replychats) BotFreeReplyChat(replychats);
- replychats = NULL;
-} //end of the function BotShutdownChatAI
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_chat.c + * + * desc: bot chat AI + * + * $Archive: /MissionPack/code/botlib/be_ai_chat.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_utils.h" +#include "l_log.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/be_ea.h" +#include "../game/be_ai_chat.h" + + +//escape character +#define ESCAPE_CHAR 0x01 //'_' +// +// "hi ", people, " ", 0, " entered the game" +//becomes: +// "hi _rpeople_ _v0_ entered the game" +// + +//match piece types +#define MT_VARIABLE 1 //variable match piece +#define MT_STRING 2 //string match piece +//reply chat key flags +#define RCKFL_AND 1 //key must be present +#define RCKFL_NOT 2 //key must be absent +#define RCKFL_NAME 4 //name of bot must be present +#define RCKFL_STRING 8 //key is a string +#define RCKFL_VARIABLES 16 //key is a match template +#define RCKFL_BOTNAMES 32 //key is a series of botnames +#define RCKFL_GENDERFEMALE 64 //bot must be female +#define RCKFL_GENDERMALE 128 //bot must be male +#define RCKFL_GENDERLESS 256 //bot must be genderless +//time to ignore a chat message after using it +#define CHATMESSAGE_RECENTTIME 20 + +//the actuall chat messages +typedef struct bot_chatmessage_s +{ + char *chatmessage; //chat message string + float time; //last time used + struct bot_chatmessage_s *next; //next chat message in a list +} bot_chatmessage_t; +//bot chat type with chat lines +typedef struct bot_chattype_s +{ + char name[MAX_CHATTYPE_NAME]; + int numchatmessages; + bot_chatmessage_t *firstchatmessage; + struct bot_chattype_s *next; +} bot_chattype_t; +//bot chat lines +typedef struct bot_chat_s +{ + bot_chattype_t *types; +} bot_chat_t; + +//random string +typedef struct bot_randomstring_s +{ + char *string; + struct bot_randomstring_s *next; +} bot_randomstring_t; +//list with random strings +typedef struct bot_randomlist_s +{ + char *string; + int numstrings; + bot_randomstring_t *firstrandomstring; + struct bot_randomlist_s *next; +} bot_randomlist_t; + +//synonym +typedef struct bot_synonym_s +{ + char *string; + float weight; + struct bot_synonym_s *next; +} bot_synonym_t; +//list with synonyms +typedef struct bot_synonymlist_s +{ + unsigned long int context; + float totalweight; + bot_synonym_t *firstsynonym; + struct bot_synonymlist_s *next; +} bot_synonymlist_t; + +//fixed match string +typedef struct bot_matchstring_s +{ + char *string; + struct bot_matchstring_s *next; +} bot_matchstring_t; + +//piece of a match template +typedef struct bot_matchpiece_s +{ + int type; + bot_matchstring_t *firststring; + int variable; + struct bot_matchpiece_s *next; +} bot_matchpiece_t; +//match template +typedef struct bot_matchtemplate_s +{ + unsigned long int context; + int type; + int subtype; + bot_matchpiece_t *first; + struct bot_matchtemplate_s *next; +} bot_matchtemplate_t; + +//reply chat key +typedef struct bot_replychatkey_s +{ + int flags; + char *string; + bot_matchpiece_t *match; + struct bot_replychatkey_s *next; +} bot_replychatkey_t; +//reply chat +typedef struct bot_replychat_s +{ + bot_replychatkey_t *keys; + float priority; + int numchatmessages; + bot_chatmessage_t *firstchatmessage; + struct bot_replychat_s *next; +} bot_replychat_t; + +//string list +typedef struct bot_stringlist_s +{ + char *string; + struct bot_stringlist_s *next; +} bot_stringlist_t; + +//chat state of a bot +typedef struct bot_chatstate_s +{ + int gender; //0=it, 1=female, 2=male + int client; //client number + char name[32]; //name of the bot + char chatmessage[MAX_MESSAGE_SIZE]; + int handle; + //the console messages visible to the bot + bot_consolemessage_t *firstmessage; //first message is the first typed message + bot_consolemessage_t *lastmessage; //last message is the last typed message, bottom of console + //number of console messages stored in the state + int numconsolemessages; + //the bot chat lines + bot_chat_t *chat; +} bot_chatstate_t; + +typedef struct { + bot_chat_t *chat; + char filename[MAX_QPATH]; + char chatname[MAX_QPATH]; +} bot_ichatdata_t; + +bot_ichatdata_t *ichatdata[MAX_CLIENTS]; + +bot_chatstate_t *botchatstates[MAX_CLIENTS+1]; +//console message heap +bot_consolemessage_t *consolemessageheap = NULL; +bot_consolemessage_t *freeconsolemessages = NULL; +//list with match strings +bot_matchtemplate_t *matchtemplates = NULL; +//list with synonyms +bot_synonymlist_t *synonyms = NULL; +//list with random strings +bot_randomlist_t *randomstrings = NULL; +//reply chats +bot_replychat_t *replychats = NULL; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_chatstate_t *BotChatStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botchatstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); + return NULL; + } //end if + return botchatstates[handle]; +} //end of the function BotChatStateFromHandle +//=========================================================================== +// initialize the heap with unused console messages +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void InitConsoleMessageHeap(void) +{ + int i, max_messages; + + if (consolemessageheap) FreeMemory(consolemessageheap); + // + max_messages = (int) LibVarValue("max_messages", "1024"); + consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages * + sizeof(bot_consolemessage_t)); + consolemessageheap[0].prev = NULL; + consolemessageheap[0].next = &consolemessageheap[1]; + for (i = 1; i < max_messages-1; i++) + { + consolemessageheap[i].prev = &consolemessageheap[i - 1]; + consolemessageheap[i].next = &consolemessageheap[i + 1]; + } //end for + consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2]; + consolemessageheap[max_messages-1].next = NULL; + //pointer to the free console messages + freeconsolemessages = consolemessageheap; +} //end of the function InitConsoleMessageHeap +//=========================================================================== +// allocate one console message from the heap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_consolemessage_t *AllocConsoleMessage(void) +{ + bot_consolemessage_t *message; + message = freeconsolemessages; + if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next; + if (freeconsolemessages) freeconsolemessages->prev = NULL; + return message; +} //end of the function AllocConsoleMessage +//=========================================================================== +// deallocate one console message from the heap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeConsoleMessage(bot_consolemessage_t *message) +{ + if (freeconsolemessages) freeconsolemessages->prev = message; + message->prev = NULL; + message->next = freeconsolemessages; + freeconsolemessages = message; +} //end of the function FreeConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotRemoveConsoleMessage(int chatstate, int handle) +{ + bot_consolemessage_t *m, *nextm; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + for (m = cs->firstmessage; m; m = nextm) + { + nextm = m->next; + if (m->handle == handle) + { + if (m->next) m->next->prev = m->prev; + else cs->lastmessage = m->prev; + if (m->prev) m->prev->next = m->next; + else cs->firstmessage = m->next; + + FreeConsoleMessage(m); + cs->numconsolemessages--; + break; + } //end if + } //end for +} //end of the function BotRemoveConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotQueueConsoleMessage(int chatstate, int type, char *message) +{ + bot_consolemessage_t *m; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + m = AllocConsoleMessage(); + if (!m) + { + botimport.Print(PRT_ERROR, "empty console message heap\n"); + return; + } //end if + cs->handle++; + if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1; + m->handle = cs->handle; + m->time = AAS_Time(); + m->type = type; + strncpy(m->message, message, MAX_MESSAGE_SIZE); + m->next = NULL; + if (cs->lastmessage) + { + cs->lastmessage->next = m; + m->prev = cs->lastmessage; + cs->lastmessage = m; + } //end if + else + { + cs->lastmessage = m; + cs->firstmessage = m; + m->prev = NULL; + } //end if + cs->numconsolemessages++; +} //end of the function BotQueueConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + if (cs->firstmessage) + { + Com_Memcpy(cm, cs->firstmessage, sizeof(bot_consolemessage_t)); + cm->next = cm->prev = NULL; + return cm->handle; + } //end if + return 0; +} //end of the function BotConsoleMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotNumConsoleMessages(int chatstate) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + return cs->numconsolemessages; +} //end of the function BotNumConsoleMessages +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int IsWhiteSpace(char c) +{ + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '(' || c == ')' + || c == '?' || c == ':' + || c == '\''|| c == '/' + || c == ',' || c == '.' + || c == '[' || c == ']' + || c == '-' || c == '_' + || c == '+' || c == '=') return qfalse; + return qtrue; +} //end of the function IsWhiteSpace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotRemoveTildes(char *message) +{ + int i; + + //remove all tildes from the chat message + for (i = 0; message[i]; i++) + { + if (message[i] == '~') + { + memmove(&message[i], &message[i+1], strlen(&message[i+1])+1); + } //end if + } //end for +} //end of the function BotRemoveTildes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void UnifyWhiteSpaces(char *string) +{ + char *ptr, *oldptr; + + for (ptr = oldptr = string; *ptr; oldptr = ptr) + { + while(*ptr && IsWhiteSpace(*ptr)) ptr++; + if (ptr > oldptr) + { + //if not at the start and not at the end of the string + //write only one space + if (oldptr > string && *ptr) *oldptr++ = ' '; + //remove all other white spaces + if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1); + } //end if + while(*ptr && !IsWhiteSpace(*ptr)) ptr++; + } //end while +} //end of the function UnifyWhiteSpaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int StringContains(char *str1, char *str2, int casesensitive) +{ + int len, i, j, index; + + if (str1 == NULL || str2 == NULL) return -1; + + len = strlen(str1) - strlen(str2); + index = 0; + for (i = 0; i <= len; i++, str1++, index++) + { + for (j = 0; str2[j]; j++) + { + if (casesensitive) + { + if (str1[j] != str2[j]) break; + } //end if + else + { + if (toupper(str1[j]) != toupper(str2[j])) break; + } //end else + } //end for + if (!str2[j]) return index; + } //end for + return -1; +} //end of the function StringContains +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *StringContainsWord(char *str1, char *str2, int casesensitive) +{ + int len, i, j; + + len = strlen(str1) - strlen(str2); + for (i = 0; i <= len; i++, str1++) + { + //if not at the start of the string + if (i) + { + //skip to the start of the next word + while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++; + if (!*str1) break; + str1++; + } //end for + //compare the word + for (j = 0; str2[j]; j++) + { + if (casesensitive) + { + if (str1[j] != str2[j]) break; + } //end if + else + { + if (toupper(str1[j]) != toupper(str2[j])) break; + } //end else + } //end for + //if there was a word match + if (!str2[j]) + { + //if the first string has an end of word + if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1; + } //end if + } //end for + return NULL; +} //end of the function StringContainsWord +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void StringReplaceWords(char *string, char *synonym, char *replacement) +{ + char *str, *str2; + + //find the synonym in the string + str = StringContainsWord(string, synonym, qfalse); + //if the synonym occured in the string + while(str) + { + //if the synonym isn't part of the replacement which is already in the string + //usefull for abreviations + str2 = StringContainsWord(string, replacement, qfalse); + while(str2) + { + if (str2 <= str && str < str2 + strlen(replacement)) break; + str2 = StringContainsWord(str2+1, replacement, qfalse); + } //end while + if (!str2) + { + memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1); + //append the synonum replacement + Com_Memcpy(str, replacement, strlen(replacement)); + } //end if + //find the next synonym in the string + str = StringContainsWord(str+strlen(replacement), synonym, qfalse); + } //end if +} //end of the function StringReplaceWords +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpSynonymList(bot_synonymlist_t *synlist) +{ + FILE *fp; + bot_synonymlist_t *syn; + bot_synonym_t *synonym; + + fp = Log_FilePointer(); + if (!fp) return; + for (syn = synlist; syn; syn = syn->next) + { + fprintf(fp, "%ld : [", syn->context); + for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) + { + fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight); + if (synonym->next) fprintf(fp, ", "); + } //end for + fprintf(fp, "]\n"); + } //end for +} //end of the function BotDumpSynonymList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_synonymlist_t *BotLoadSynonyms(char *filename) +{ + int pass, size, contextlevel, numsynonyms; + unsigned long int context, contextstack[32]; + char *ptr = NULL; + source_t *source; + token_t token; + bot_synonymlist_t *synlist, *lastsyn, *syn; + bot_synonym_t *synonym, *lastsynonym; + + size = 0; + synlist = NULL; //make compiler happy + syn = NULL; //make compiler happy + synonym = NULL; //make compiler happy + //the synonyms are parsed in two phases + for (pass = 0; pass < 2; pass++) + { + // + if (pass && size) ptr = (char *) GetClearedHunkMemory(size); + // + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + context = 0; + contextlevel = 0; + synlist = NULL; //list synonyms + lastsyn = NULL; //last synonym in the list + // + while(PC_ReadToken(source, &token)) + { + if (token.type == TT_NUMBER) + { + context |= token.intvalue; + contextstack[contextlevel] = token.intvalue; + contextlevel++; + if (contextlevel >= 32) + { + SourceError(source, "more than 32 context levels"); + FreeSource(source); + return NULL; + } //end if + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + } //end if + else if (token.type == TT_PUNCTUATION) + { + if (!strcmp(token.string, "}")) + { + contextlevel--; + if (contextlevel < 0) + { + SourceError(source, "too many }"); + FreeSource(source); + return NULL; + } //end if + context &= ~contextstack[contextlevel]; + } //end if + else if (!strcmp(token.string, "[")) + { + size += sizeof(bot_synonymlist_t); + if (pass) + { + syn = (bot_synonymlist_t *) ptr; + ptr += sizeof(bot_synonymlist_t); + syn->context = context; + syn->firstsynonym = NULL; + syn->next = NULL; + if (lastsyn) lastsyn->next = syn; + else synlist = syn; + lastsyn = syn; + } //end if + numsynonyms = 0; + lastsynonym = NULL; + while(1) + { + if (!PC_ExpectTokenString(source, "(") || + !PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + if (strlen(token.string) <= 0) + { + SourceError(source, "empty string", token.string); + FreeSource(source); + return NULL; + } //end if + size += sizeof(bot_synonym_t) + strlen(token.string) + 1; + if (pass) + { + synonym = (bot_synonym_t *) ptr; + ptr += sizeof(bot_synonym_t); + synonym->string = ptr; + ptr += strlen(token.string) + 1; + strcpy(synonym->string, token.string); + // + if (lastsynonym) lastsynonym->next = synonym; + else syn->firstsynonym = synonym; + lastsynonym = synonym; + } //end if + numsynonyms++; + if (!PC_ExpectTokenString(source, ",") || + !PC_ExpectTokenType(source, TT_NUMBER, 0, &token) || + !PC_ExpectTokenString(source, ")")) + { + FreeSource(source); + return NULL; + } //end if + if (pass) + { + synonym->weight = token.floatvalue; + syn->totalweight += synonym->weight; + } //end if + if (PC_CheckTokenString(source, "]")) break; + if (!PC_ExpectTokenString(source, ",")) + { + FreeSource(source); + return NULL; + } //end if + } //end while + if (numsynonyms < 2) + { + SourceError(source, "synonym must have at least two entries\n"); + FreeSource(source); + return NULL; + } //end if + } //end else + else + { + SourceError(source, "unexpected %s", token.string); + FreeSource(source); + return NULL; + } //end if + } //end else if + } //end while + // + FreeSource(source); + // + if (contextlevel > 0) + { + SourceError(source, "missing }"); + return NULL; + } //end if + } //end for + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + // + //BotDumpSynonymList(synlist); + // + return synlist; +} //end of the function BotLoadSynonyms +//=========================================================================== +// replace all the synonyms in the string +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotReplaceSynonyms(char *string, unsigned long int context) +{ + bot_synonymlist_t *syn; + bot_synonym_t *synonym; + + for (syn = synonyms; syn; syn = syn->next) + { + if (!(syn->context & context)) continue; + for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) + { + StringReplaceWords(string, synonym->string, syn->firstsynonym->string); + } //end for + } //end for +} //end of the function BotReplaceSynonyms +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotReplaceWeightedSynonyms(char *string, unsigned long int context) +{ + bot_synonymlist_t *syn; + bot_synonym_t *synonym, *replacement; + float weight, curweight; + + for (syn = synonyms; syn; syn = syn->next) + { + if (!(syn->context & context)) continue; + //choose a weighted random replacement synonym + weight = random() * syn->totalweight; + if (!weight) continue; + curweight = 0; + for (replacement = syn->firstsynonym; replacement; replacement = replacement->next) + { + curweight += replacement->weight; + if (weight < curweight) break; + } //end for + if (!replacement) continue; + //replace all synonyms with the replacement + for (synonym = syn->firstsynonym; synonym; synonym = synonym->next) + { + if (synonym == replacement) continue; + StringReplaceWords(string, synonym->string, replacement->string); + } //end for + } //end for +} //end of the function BotReplaceWeightedSynonyms +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotReplaceReplySynonyms(char *string, unsigned long int context) +{ + char *str1, *str2, *replacement; + bot_synonymlist_t *syn; + bot_synonym_t *synonym; + + for (str1 = string; *str1; ) + { + //go to the start of the next word + while(*str1 && *str1 <= ' ') str1++; + if (!*str1) break; + // + for (syn = synonyms; syn; syn = syn->next) + { + if (!(syn->context & context)) continue; + for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next) + { + str2 = synonym->string; + //if the synonym is not at the front of the string continue + str2 = StringContainsWord(str1, synonym->string, qfalse); + if (!str2 || str2 != str1) continue; + // + replacement = syn->firstsynonym->string; + //if the replacement IS in front of the string continue + str2 = StringContainsWord(str1, replacement, qfalse); + if (str2 && str2 == str1) continue; + // + memmove(str1 + strlen(replacement), str1+strlen(synonym->string), + strlen(str1+strlen(synonym->string)) + 1); + //append the synonum replacement + Com_Memcpy(str1, replacement, strlen(replacement)); + // + break; + } //end for + //if a synonym has been replaced + if (synonym) break; + } //end for + //skip over this word + while(*str1 && *str1 > ' ') str1++; + if (!*str1) break; + } //end while +} //end of the function BotReplaceReplySynonyms +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadChatMessage(source_t *source, char *chatmessagestring) +{ + char *ptr; + token_t token; + + ptr = chatmessagestring; + *ptr = 0; + // + while(1) + { + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + //fixed string + if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + if (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE) + { + SourceError(source, "chat message too long\n"); + return qfalse; + } //end if + strcat(ptr, token.string); + } //end else if + //variable string + else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) + { + if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE) + { + SourceError(source, "chat message too long\n"); + return qfalse; + } //end if + sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR); + } //end if + //random string + else if (token.type == TT_NAME) + { + if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE) + { + SourceError(source, "chat message too long\n"); + return qfalse; + } //end if + sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR); + } //end else if + else + { + SourceError(source, "unknown message component %s\n", token.string); + return qfalse; + } //end else + if (PC_CheckTokenString(source, ";")) break; + if (!PC_ExpectTokenString(source, ",")) return qfalse; + } //end while + // + return qtrue; +} //end of the function BotLoadChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpRandomStringList(bot_randomlist_t *randomlist) +{ + FILE *fp; + bot_randomlist_t *random; + bot_randomstring_t *rs; + + fp = Log_FilePointer(); + if (!fp) return; + for (random = randomlist; random; random = random->next) + { + fprintf(fp, "%s = {", random->string); + for (rs = random->firstrandomstring; rs; rs = rs->next) + { + fprintf(fp, "\"%s\"", rs->string); + if (rs->next) fprintf(fp, ", "); + else fprintf(fp, "}\n"); + } //end for + } //end for +} //end of the function BotDumpRandomStringList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_randomlist_t *BotLoadRandomStrings(char *filename) +{ + int pass, size; + char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE]; + source_t *source; + token_t token; + bot_randomlist_t *randomlist, *lastrandom, *random; + bot_randomstring_t *randomstring; + +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif //DEBUG + + size = 0; + randomlist = NULL; + random = NULL; + //the synonyms are parsed in two phases + for (pass = 0; pass < 2; pass++) + { + // + if (pass && size) ptr = (char *) GetClearedHunkMemory(size); + // + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + randomlist = NULL; //list + lastrandom = NULL; //last + // + while(PC_ReadToken(source, &token)) + { + if (token.type != TT_NAME) + { + SourceError(source, "unknown random %s", token.string); + FreeSource(source); + return NULL; + } //end if + size += sizeof(bot_randomlist_t) + strlen(token.string) + 1; + if (pass) + { + random = (bot_randomlist_t *) ptr; + ptr += sizeof(bot_randomlist_t); + random->string = ptr; + ptr += strlen(token.string) + 1; + strcpy(random->string, token.string); + random->firstrandomstring = NULL; + random->numstrings = 0; + // + if (lastrandom) lastrandom->next = random; + else randomlist = random; + lastrandom = random; + } //end if + if (!PC_ExpectTokenString(source, "=") || + !PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + while(!PC_CheckTokenString(source, "}")) + { + if (!BotLoadChatMessage(source, chatmessagestring)) + { + FreeSource(source); + return NULL; + } //end if + size += sizeof(bot_randomstring_t) + strlen(chatmessagestring) + 1; + if (pass) + { + randomstring = (bot_randomstring_t *) ptr; + ptr += sizeof(bot_randomstring_t); + randomstring->string = ptr; + ptr += strlen(chatmessagestring) + 1; + strcpy(randomstring->string, chatmessagestring); + // + random->numstrings++; + randomstring->next = random->firstrandomstring; + random->firstrandomstring = randomstring; + } //end if + } //end while + } //end while + //free the source after one pass + FreeSource(source); + } //end for + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime); + //BotDumpRandomStringList(randomlist); +#endif //DEBUG + // + return randomlist; +} //end of the function BotLoadRandomStrings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *RandomString(char *name) +{ + bot_randomlist_t *random; + bot_randomstring_t *rs; + int i; + + for (random = randomstrings; random; random = random->next) + { + if (!strcmp(random->string, name)) + { + i = random() * random->numstrings; + for (rs = random->firstrandomstring; rs; rs = rs->next) + { + if (--i < 0) break; + } //end for + if (rs) + { + return rs->string; + } //end if + } //end for + } //end for + return NULL; +} //end of the function RandomString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpMatchTemplates(bot_matchtemplate_t *matches) +{ + FILE *fp; + bot_matchtemplate_t *mt; + bot_matchpiece_t *mp; + bot_matchstring_t *ms; + + fp = Log_FilePointer(); + if (!fp) return; + for (mt = matches; mt; mt = mt->next) + { + fprintf(fp, "{ " ); + for (mp = mt->first; mp; mp = mp->next) + { + if (mp->type == MT_STRING) + { + for (ms = mp->firststring; ms; ms = ms->next) + { + fprintf(fp, "\"%s\"", ms->string); + if (ms->next) fprintf(fp, "|"); + } //end for + } //end if + else if (mp->type == MT_VARIABLE) + { + fprintf(fp, "%d", mp->variable); + } //end else if + if (mp->next) fprintf(fp, ", "); + } //end for + fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype); + } //end for +} //end of the function BotDumpMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeMatchPieces(bot_matchpiece_t *matchpieces) +{ + bot_matchpiece_t *mp, *nextmp; + bot_matchstring_t *ms, *nextms; + + for (mp = matchpieces; mp; mp = nextmp) + { + nextmp = mp->next; + if (mp->type == MT_STRING) + { + for (ms = mp->firststring; ms; ms = nextms) + { + nextms = ms->next; + FreeMemory(ms); + } //end for + } //end if + FreeMemory(mp); + } //end for +} //end of the function BotFreeMatchPieces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken) +{ + int lastwasvariable, emptystring; + token_t token; + bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece; + bot_matchstring_t *matchstring, *lastmatchstring; + + firstpiece = NULL; + lastpiece = NULL; + // + lastwasvariable = qfalse; + // + while(PC_ReadToken(source, &token)) + { + if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER)) + { + if (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES) + { + SourceError(source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES); + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + if (lastwasvariable) + { + SourceError(source, "not allowed to have adjacent variables\n"); + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + lastwasvariable = qtrue; + // + matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); + matchpiece->type = MT_VARIABLE; + matchpiece->variable = token.intvalue; + matchpiece->next = NULL; + if (lastpiece) lastpiece->next = matchpiece; + else firstpiece = matchpiece; + lastpiece = matchpiece; + } //end if + else if (token.type == TT_STRING) + { + // + matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t)); + matchpiece->firststring = NULL; + matchpiece->type = MT_STRING; + matchpiece->variable = 0; + matchpiece->next = NULL; + if (lastpiece) lastpiece->next = matchpiece; + else firstpiece = matchpiece; + lastpiece = matchpiece; + // + lastmatchstring = NULL; + emptystring = qfalse; + // + do + { + if (matchpiece->firststring) + { + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + } //end if + StripDoubleQuotes(token.string); + matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1); + matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t); + strcpy(matchstring->string, token.string); + if (!strlen(token.string)) emptystring = qtrue; + matchstring->next = NULL; + if (lastmatchstring) lastmatchstring->next = matchstring; + else matchpiece->firststring = matchstring; + lastmatchstring = matchstring; + } while(PC_CheckTokenString(source, "|")); + //if there was no empty string found + if (!emptystring) lastwasvariable = qfalse; + } //end if + else + { + SourceError(source, "invalid token %s\n", token.string); + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end else + if (PC_CheckTokenString(source, endtoken)) break; + if (!PC_ExpectTokenString(source, ",")) + { + FreeSource(source); + BotFreeMatchPieces(firstpiece); + return NULL; + } //end if + } //end while + return firstpiece; +} //end of the function BotLoadMatchPieces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeMatchTemplates(bot_matchtemplate_t *mt) +{ + bot_matchtemplate_t *nextmt; + + for (; mt; mt = nextmt) + { + nextmt = mt->next; + BotFreeMatchPieces(mt->first); + FreeMemory(mt); + } //end for +} //end of the function BotFreeMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile) +{ + source_t *source; + token_t token; + bot_matchtemplate_t *matchtemplate, *matches, *lastmatch; + unsigned long int context; + + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(matchfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile); + return NULL; + } //end if + // + matches = NULL; //list with matches + lastmatch = NULL; //last match in the list + + while(PC_ReadToken(source, &token)) + { + if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER)) + { + SourceError(source, "expected integer, found %s\n", token.string); + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + //the context + context = token.intvalue; + // + if (!PC_ExpectTokenString(source, "{")) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + // + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "}")) break; + // + PC_UnreadLastToken(source); + // + matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t)); + matchtemplate->context = context; + matchtemplate->next = NULL; + //add the match template to the list + if (lastmatch) lastmatch->next = matchtemplate; + else matches = matchtemplate; + lastmatch = matchtemplate; + //load the match template + matchtemplate->first = BotLoadMatchPieces(source, "="); + if (!matchtemplate->first) + { + BotFreeMatchTemplates(matches); + return NULL; + } //end if + //read the match type + if (!PC_ExpectTokenString(source, "(") || + !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + matchtemplate->type = token.intvalue; + //read the match subtype + if (!PC_ExpectTokenString(source, ",") || + !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + matchtemplate->subtype = token.intvalue; + //read trailing punctuations + if (!PC_ExpectTokenString(source, ")") || + !PC_ExpectTokenString(source, ";")) + { + BotFreeMatchTemplates(matches); + FreeSource(source); + return NULL; + } //end if + } //end while + } //end while + //free the source + FreeSource(source); + botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile); + // + //BotDumpMatchTemplates(matches); + // + return matches; +} //end of the function BotLoadMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match) +{ + int lastvariable, index; + char *strptr, *newstrptr; + bot_matchpiece_t *mp; + bot_matchstring_t *ms; + + //no last variable + lastvariable = -1; + //pointer to the string to compare the match string with + strptr = match->string; + //Log_Write("match: %s", strptr); + //compare the string with the current match string + for (mp = pieces; mp; mp = mp->next) + { + //if it is a piece of string + if (mp->type == MT_STRING) + { + newstrptr = NULL; + for (ms = mp->firststring; ms; ms = ms->next) + { + if (!strlen(ms->string)) + { + newstrptr = strptr; + break; + } //end if + //Log_Write("MT_STRING: %s", mp->string); + index = StringContains(strptr, ms->string, qfalse); + if (index >= 0) + { + newstrptr = strptr + index; + if (lastvariable >= 0) + { + match->variables[lastvariable].length = + (newstrptr - match->string) - match->variables[lastvariable].offset; + //newstrptr - match->variables[lastvariable].ptr; + lastvariable = -1; + break; + } //end if + else if (index == 0) + { + break; + } //end else + newstrptr = NULL; + } //end if + } //end for + if (!newstrptr) return qfalse; + strptr = newstrptr + strlen(ms->string); + } //end if + //if it is a variable piece of string + else if (mp->type == MT_VARIABLE) + { + //Log_Write("MT_VARIABLE"); + match->variables[mp->variable].offset = strptr - match->string; + lastvariable = mp->variable; + } //end else if + } //end for + //if a match was found + if (!mp && (lastvariable >= 0 || !strlen(strptr))) + { + //if the last piece was a variable string + if (lastvariable >= 0) + { + assert( match->variables[lastvariable].offset >= 0 ); // bk001204 + match->variables[lastvariable].length = + strlen(&match->string[ (int) match->variables[lastvariable].offset]); + } //end if + return qtrue; + } //end if + return qfalse; +} //end of the function StringsMatch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFindMatch(char *str, bot_match_t *match, unsigned long int context) +{ + int i; + bot_matchtemplate_t *ms; + + strncpy(match->string, str, MAX_MESSAGE_SIZE); + //remove any trailing enters + while(strlen(match->string) && + match->string[strlen(match->string)-1] == '\n') + { + match->string[strlen(match->string)-1] = '\0'; + } //end while + //compare the string with all the match strings + for (ms = matchtemplates; ms; ms = ms->next) + { + if (!(ms->context & context)) continue; + //reset the match variable offsets + for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1; + // + if (StringsMatch(ms->first, match)) + { + match->type = ms->type; + match->subtype = ms->subtype; + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function BotFindMatch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size) +{ + if (variable < 0 || variable >= MAX_MATCHVARIABLES) + { + botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n"); + strcpy(buf, ""); + return; + } //end if + + if (match->variables[variable].offset >= 0) + { + if (match->variables[variable].length < size) + size = match->variables[variable].length+1; + assert( match->variables[variable].offset >= 0 ); // bk001204 + strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1); + buf[size-1] = '\0'; + } //end if + else + { + strcpy(buf, ""); + } //end else + return; +} //end of the function BotMatchVariable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string) +{ + bot_stringlist_t *s; + + for (s = list; s; s = s->next) + { + if (!strcmp(s->string, string)) return s; + } //end for + return NULL; +} //end of the function BotFindStringInList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist) +{ + int i; + char *msgptr; + char temp[MAX_MESSAGE_SIZE]; + bot_stringlist_t *s; + + msgptr = message; + // + while(*msgptr) + { + if (*msgptr == ESCAPE_CHAR) + { + msgptr++; + switch(*msgptr) + { + case 'v': //variable + { + //step over the 'v' + msgptr++; + while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++; + //step over the trailing escape char + if (*msgptr) msgptr++; + break; + } //end case + case 'r': //random + { + //step over the 'r' + msgptr++; + for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) + { + temp[i] = *msgptr++; + } //end while + temp[i] = '\0'; + //step over the trailing escape char + if (*msgptr) msgptr++; + //find the random keyword + if (!RandomString(temp)) + { + if (!BotFindStringInList(stringlist, temp)) + { + Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp); + s = GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1); + s->string = (char *) s + sizeof(bot_stringlist_t); + strcpy(s->string, temp); + s->next = stringlist; + stringlist = s; + } //end if + } //end if + break; + } //end case + default: + { + botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message); + break; + } //end default + } //end switch + } //end if + else + { + msgptr++; + } //end else + } //end while + return stringlist; +} //end of the function BotCheckChatMessageIntegrety +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckInitialChatIntegrety(bot_chat_t *chat) +{ + bot_chattype_t *t; + bot_chatmessage_t *cm; + bot_stringlist_t *stringlist, *s, *nexts; + + stringlist = NULL; + for (t = chat->types; t; t = t->next) + { + for (cm = t->firstchatmessage; cm; cm = cm->next) + { + stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); + } //end for + } //end for + for (s = stringlist; s; s = nexts) + { + nexts = s->next; + FreeMemory(s); + } //end for +} //end of the function BotCheckInitialChatIntegrety +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckReplyChatIntegrety(bot_replychat_t *replychat) +{ + bot_replychat_t *rp; + bot_chatmessage_t *cm; + bot_stringlist_t *stringlist, *s, *nexts; + + stringlist = NULL; + for (rp = replychat; rp; rp = rp->next) + { + for (cm = rp->firstchatmessage; cm; cm = cm->next) + { + stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist); + } //end for + } //end for + for (s = stringlist; s; s = nexts) + { + nexts = s->next; + FreeMemory(s); + } //end for +} //end of the function BotCheckReplyChatIntegrety +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpReplyChat(bot_replychat_t *replychat) +{ + FILE *fp; + bot_replychat_t *rp; + bot_replychatkey_t *key; + bot_chatmessage_t *cm; + bot_matchpiece_t *mp; + + fp = Log_FilePointer(); + if (!fp) return; + fprintf(fp, "BotDumpReplyChat:\n"); + for (rp = replychat; rp; rp = rp->next) + { + fprintf(fp, "["); + for (key = rp->keys; key; key = key->next) + { + if (key->flags & RCKFL_AND) fprintf(fp, "&"); + else if (key->flags & RCKFL_NOT) fprintf(fp, "!"); + // + if (key->flags & RCKFL_NAME) fprintf(fp, "name"); + else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female"); + else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male"); + else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it"); + else if (key->flags & RCKFL_VARIABLES) + { + fprintf(fp, "("); + for (mp = key->match; mp; mp = mp->next) + { + if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string); + else fprintf(fp, "%d", mp->variable); + if (mp->next) fprintf(fp, ", "); + } //end for + fprintf(fp, ")"); + } //end if + else if (key->flags & RCKFL_STRING) + { + fprintf(fp, "\"%s\"", key->string); + } //end if + if (key->next) fprintf(fp, ", "); + else fprintf(fp, "] = %1.0f\n", rp->priority); + } //end for + fprintf(fp, "{\n"); + for (cm = rp->firstchatmessage; cm; cm = cm->next) + { + fprintf(fp, "\t\"%s\";\n", cm->chatmessage); + } //end for + fprintf(fp, "}\n"); + } //end for +} //end of the function BotDumpReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeReplyChat(bot_replychat_t *replychat) +{ + bot_replychat_t *rp, *nextrp; + bot_replychatkey_t *key, *nextkey; + bot_chatmessage_t *cm, *nextcm; + + for (rp = replychat; rp; rp = nextrp) + { + nextrp = rp->next; + for (key = rp->keys; key; key = nextkey) + { + nextkey = key->next; + if (key->match) BotFreeMatchPieces(key->match); + if (key->string) FreeMemory(key->string); + FreeMemory(key); + } //end for + for (cm = rp->firstchatmessage; cm; cm = nextcm) + { + nextcm = cm->next; + FreeMemory(cm); + } //end for + FreeMemory(rp); + } //end for +} //end of the function BotFreeReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys) +{ + int allprefixed, hasvariableskey, hasstringkey; + bot_matchpiece_t *m; + bot_matchstring_t *ms; + bot_replychatkey_t *key, *key2; + + // + allprefixed = qtrue; + hasvariableskey = hasstringkey = qfalse; + for (key = keys; key; key = key->next) + { + if (!(key->flags & (RCKFL_AND|RCKFL_NOT))) + { + allprefixed = qfalse; + if (key->flags & RCKFL_VARIABLES) + { + for (m = key->match; m; m = m->next) + { + if (m->type == MT_VARIABLE) hasvariableskey = qtrue; + } //end for + } //end if + else if (key->flags & RCKFL_STRING) + { + hasstringkey = qtrue; + } //end else if + } //end if + else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING)) + { + for (key2 = keys; key2; key2 = key2->next) + { + if (key2 == key) continue; + if (key2->flags & RCKFL_NOT) continue; + if (key2->flags & RCKFL_VARIABLES) + { + for (m = key2->match; m; m = m->next) + { + if (m->type == MT_STRING) + { + for (ms = m->firststring; ms; ms = ms->next) + { + if (StringContains(ms->string, key->string, qfalse) != -1) + { + break; + } //end if + } //end for + if (ms) break; + } //end if + else if (m->type == MT_VARIABLE) + { + break; + } //end if + } //end for + if (!m) + { + SourceWarning(source, "one of the match templates does not " + "leave space for the key %s with the & prefix", key->string); + } //end if + } //end if + } //end for + } //end else + if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING)) + { + for (key2 = keys; key2; key2 = key2->next) + { + if (key2 == key) continue; + if (key2->flags & RCKFL_NOT) continue; + if (key2->flags & RCKFL_STRING) + { + if (StringContains(key2->string, key->string, qfalse) != -1) + { + SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string); + } //end if + } //end if + else if (key2->flags & RCKFL_VARIABLES) + { + for (m = key2->match; m; m = m->next) + { + if (m->type == MT_STRING) + { + for (ms = m->firststring; ms; ms = ms->next) + { + if (StringContains(ms->string, key->string, qfalse) != -1) + { + SourceWarning(source, "the key %s with prefix ! is inside " + "the match template string %s", key->string, ms->string); + } //end if + } //end for + } //end if + } //end for + } //end else if + } //end for + } //end if + } //end for + if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix"); + if (hasvariableskey && hasstringkey) + { + SourceWarning(source, "variables from the match template(s) could be " + "invalid when outputting one of the chat messages"); + } //end if +} //end of the function BotCheckValidReplyChatKeySet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_replychat_t *BotLoadReplyChat(char *filename) +{ + char chatmessagestring[MAX_MESSAGE_SIZE]; + char namebuffer[MAX_MESSAGE_SIZE]; + source_t *source; + token_t token; + bot_chatmessage_t *chatmessage = NULL; + bot_replychat_t *replychat, *replychatlist; + bot_replychatkey_t *key; + + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + replychatlist = NULL; + // + while(PC_ReadToken(source, &token)) + { + if (strcmp(token.string, "[")) + { + SourceError(source, "expected [, found %s", token.string); + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + // + replychat = GetClearedHunkMemory(sizeof(bot_replychat_t)); + replychat->keys = NULL; + replychat->next = replychatlist; + replychatlist = replychat; + //read the keys, there must be at least one key + do + { + //allocate a key + key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t)); + key->flags = 0; + key->string = NULL; + key->match = NULL; + key->next = replychat->keys; + replychat->keys = key; + //check for MUST BE PRESENT and MUST BE ABSENT keys + if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND; + else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT; + //special keys + if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME; + else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE; + else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE; + else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS; + else if (PC_CheckTokenString(source, "(")) //match key + { + key->flags |= RCKFL_VARIABLES; + key->match = BotLoadMatchPieces(source, ")"); + if (!key->match) + { + BotFreeReplyChat(replychatlist); + return NULL; + } //end if + } //end else if + else if (PC_CheckTokenString(source, "<")) //bot names + { + key->flags |= RCKFL_BOTNAMES; + strcpy(namebuffer, ""); + do + { + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + if (strlen(namebuffer)) strcat(namebuffer, "\\"); + strcat(namebuffer, token.string); + } while(PC_CheckTokenString(source, ",")); + if (!PC_ExpectTokenString(source, ">")) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1); + strcpy(key->string, namebuffer); + } //end else if + else //normal string key + { + key->flags |= RCKFL_STRING; + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1); + strcpy(key->string, token.string); + } //end else + // + PC_CheckTokenString(source, ","); + } while(!PC_CheckTokenString(source, "]")); + // + BotCheckValidReplyChatKeySet(source, replychat->keys); + //read the = sign and the priority + if (!PC_ExpectTokenString(source, "=") || + !PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + replychat->priority = token.floatvalue; + //read the leading { + if (!PC_ExpectTokenString(source, "{")) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + replychat->numchatmessages = 0; + //while the trailing } is not found + while(!PC_CheckTokenString(source, "}")) + { + if (!BotLoadChatMessage(source, chatmessagestring)) + { + BotFreeReplyChat(replychatlist); + FreeSource(source); + return NULL; + } //end if + chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1); + chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t); + strcpy(chatmessage->chatmessage, chatmessagestring); + chatmessage->time = -2*CHATMESSAGE_RECENTTIME; + chatmessage->next = replychat->firstchatmessage; + //add the chat message to the reply chat + replychat->firstchatmessage = chatmessage; + replychat->numchatmessages++; + } //end while + } //end while + FreeSource(source); + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); + // + //BotDumpReplyChat(replychatlist); + if (bot_developer) + { + BotCheckReplyChatIntegrety(replychatlist); + } //end if + // + if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n"); + // + return replychatlist; +} //end of the function BotLoadReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpInitialChat(bot_chat_t *chat) +{ + bot_chattype_t *t; + bot_chatmessage_t *m; + + Log_Write("{"); + for (t = chat->types; t; t = t->next) + { + Log_Write(" type \"%s\"", t->name); + Log_Write(" {"); + Log_Write(" numchatmessages = %d", t->numchatmessages); + for (m = t->firstchatmessage; m; m = m->next) + { + Log_Write(" \"%s\"", m->chatmessage); + } //end for + Log_Write(" }"); + } //end for + Log_Write("}"); +} //end of the function BotDumpInitialChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname) +{ + int pass, foundchat, indent, size; + char *ptr = NULL; + char chatmessagestring[MAX_MESSAGE_SIZE]; + source_t *source; + token_t token; + bot_chat_t *chat = NULL; + bot_chattype_t *chattype = NULL; + bot_chatmessage_t *chatmessage = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + // + size = 0; + foundchat = qfalse; + //a bot chat is parsed in two phases + for (pass = 0; pass < 2; pass++) + { + //allocate memory + if (pass && size) ptr = (char *) GetClearedMemory(size); + //load the source file + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(chatfile); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile); + return NULL; + } //end if + //chat structure + if (pass) + { + chat = (bot_chat_t *) ptr; + ptr += sizeof(bot_chat_t); + } //end if + size = sizeof(bot_chat_t); + // + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "chat")) + { + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + //after the chat name we expect a opening brace + if (!PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + //if the chat name is found + if (!Q_stricmp(token.string, chatname)) + { + foundchat = qtrue; + //read the chat types + while(1) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + return NULL; + } //end if + if (!strcmp(token.string, "}")) break; + if (strcmp(token.string, "type")) + { + SourceError(source, "expected type found %s\n", token.string); + FreeSource(source); + return NULL; + } //end if + //expect the chat type name + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) || + !PC_ExpectTokenString(source, "{")) + { + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + if (pass) + { + chattype = (bot_chattype_t *) ptr; + strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME); + chattype->firstchatmessage = NULL; + //add the chat type to the chat + chattype->next = chat->types; + chat->types = chattype; + // + ptr += sizeof(bot_chattype_t); + } //end if + size += sizeof(bot_chattype_t); + //read the chat messages + while(!PC_CheckTokenString(source, "}")) + { + if (!BotLoadChatMessage(source, chatmessagestring)) + { + FreeSource(source); + return NULL; + } //end if + if (pass) + { + chatmessage = (bot_chatmessage_t *) ptr; + chatmessage->time = -2*CHATMESSAGE_RECENTTIME; + //put the chat message in the list + chatmessage->next = chattype->firstchatmessage; + chattype->firstchatmessage = chatmessage; + //store the chat message + ptr += sizeof(bot_chatmessage_t); + chatmessage->chatmessage = ptr; + strcpy(chatmessage->chatmessage, chatmessagestring); + ptr += strlen(chatmessagestring) + 1; + //the number of chat messages increased + chattype->numchatmessages++; + } //end if + size += sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1; + } //end if + } //end while + } //end if + else //skip the bot chat + { + indent = 1; + while(indent) + { + if (!PC_ExpectAnyToken(source, &token)) + { + FreeSource(source); + return NULL; + } //end if + if (!strcmp(token.string, "{")) indent++; + else if (!strcmp(token.string, "}")) indent--; + } //end while + } //end else + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeSource(source); + return NULL; + } //end else + } //end while + //free the source + FreeSource(source); + //if the requested character is not found + if (!foundchat) + { + botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile); + return NULL; + } //end if + } //end for + // + botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile); + // + //BotDumpInitialChat(chat); + if (bot_developer) + { + BotCheckInitialChatIntegrety(chat); + } //end if +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime); +#endif //DEBUG + //character was read succesfully + return chat; +} //end of the function BotLoadInitialChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeChatFile(int chatstate) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + if (cs->chat) FreeMemory(cs->chat); + cs->chat = NULL; +} //end of the function BotFreeChatFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadChatFile(int chatstate, char *chatfile, char *chatname) +{ + bot_chatstate_t *cs; + int n, avail = 0; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return BLERR_CANNOTLOADICHAT; + BotFreeChatFile(chatstate); + + if (!LibVarGetValue("bot_reloadcharacters")) + { + avail = -1; + for( n = 0; n < MAX_CLIENTS; n++ ) { + if( !ichatdata[n] ) { + if( avail == -1 ) { + avail = n; + } + continue; + } + if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) { + continue; + } + if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) { + continue; + } + cs->chat = ichatdata[n]->chat; + // botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile ); + return BLERR_NOERROR; + } + + if( avail == -1 ) { + botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile); + return BLERR_CANNOTLOADICHAT; + } + } + + cs->chat = BotLoadInitialChat(chatfile, chatname); + if (!cs->chat) + { + botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile); + return BLERR_CANNOTLOADICHAT; + } //end if + if (!LibVarGetValue("bot_reloadcharacters")) + { + ichatdata[avail] = GetClearedMemory( sizeof(bot_ichatdata_t) ); + ichatdata[avail]->chat = cs->chat; + Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) ); + Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) ); + } //end if + + return BLERR_NOERROR; +} //end of the function BotLoadChatFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext, + bot_match_t *match, unsigned long vcontext, int reply) +{ + int num, len, i, expansion; + char *outputbuf, *ptr, *msgptr; + char temp[MAX_MESSAGE_SIZE]; + + expansion = qfalse; + msgptr = message; + outputbuf = outmessage; + len = 0; + // + while(*msgptr) + { + if (*msgptr == ESCAPE_CHAR) + { + msgptr++; + switch(*msgptr) + { + case 'v': //variable + { + msgptr++; + num = 0; + while(*msgptr && *msgptr != ESCAPE_CHAR) + { + num = num * 10 + (*msgptr++) - '0'; + } //end while + //step over the trailing escape char + if (*msgptr) msgptr++; + if (num > MAX_MATCHVARIABLES) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num); + return qfalse; + } //end if + if (match->variables[num].offset >= 0) + { + assert( match->variables[num].offset >= 0 ); // bk001204 + ptr = &match->string[ (int) match->variables[num].offset]; + for (i = 0; i < match->variables[num].length; i++) + { + temp[i] = ptr[i]; + } //end for + temp[i] = 0; + //if it's a reply message + if (reply) + { + //replace the reply synonyms in the variables + BotReplaceReplySynonyms(temp, vcontext); + } //end if + else + { + //replace synonyms in the variable context + BotReplaceSynonyms(temp, vcontext); + } //end else + // + if (len + strlen(temp) >= MAX_MESSAGE_SIZE) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message); + return qfalse; + } //end if + strcpy(&outputbuf[len], temp); + len += strlen(temp); + } //end if + break; + } //end case + case 'r': //random + { + msgptr++; + for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++) + { + temp[i] = *msgptr++; + } //end while + temp[i] = '\0'; + //step over the trailing escape char + if (*msgptr) msgptr++; + //find the random keyword + ptr = RandomString(temp); + if (!ptr) + { + botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp); + return qfalse; + } //end if + if (len + strlen(ptr) >= MAX_MESSAGE_SIZE) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); + return qfalse; + } //end if + strcpy(&outputbuf[len], ptr); + len += strlen(ptr); + expansion = qtrue; + break; + } //end case + default: + { + botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message); + break; + } //end default + } //end switch + } //end if + else + { + outputbuf[len++] = *msgptr++; + if (len >= MAX_MESSAGE_SIZE) + { + botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message); + break; + } //end if + } //end else + } //end while + outputbuf[len] = '\0'; + //replace synonyms weighted in the message context + BotReplaceWeightedSynonyms(outputbuf, mcontext); + //return true if a random was expanded + return expansion; +} //end of the function BotExpandChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext, + bot_match_t *match, unsigned long vcontext, int reply) +{ + int i; + char srcmessage[MAX_MESSAGE_SIZE]; + + strcpy(srcmessage, message); + for (i = 0; i < 10; i++) + { + if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply)) + { + break; + } //end if + strcpy(srcmessage, chatstate->chatmessage); + } //end for + if (i >= 10) + { + botimport.Print(PRT_WARNING, "too many expansions in chat message\n"); + botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage); + } //end if +} //end of the function BotConstructChatMessage +//=========================================================================== +// randomly chooses one of the chat message of the given type +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type) +{ + int n, numchatmessages; + float besttime; + bot_chattype_t *t; + bot_chatmessage_t *m, *bestchatmessage; + bot_chat_t *chat; + + chat = cs->chat; + for (t = chat->types; t; t = t->next) + { + if (!Q_stricmp(t->name, type)) + { + numchatmessages = 0; + for (m = t->firstchatmessage; m; m = m->next) + { + if (m->time > AAS_Time()) continue; + numchatmessages++; + } //end if + //if all chat messages have been used recently + if (numchatmessages <= 0) + { + besttime = 0; + bestchatmessage = NULL; + for (m = t->firstchatmessage; m; m = m->next) + { + if (!besttime || m->time < besttime) + { + bestchatmessage = m; + besttime = m->time; + } //end if + } //end for + if (bestchatmessage) return bestchatmessage->chatmessage; + } //end if + else //choose a chat message randomly + { + n = random() * numchatmessages; + for (m = t->firstchatmessage; m; m = m->next) + { + if (m->time > AAS_Time()) continue; + if (--n < 0) + { + m->time = AAS_Time() + CHATMESSAGE_RECENTTIME; + return m->chatmessage; + } //end if + } //end for + } //end else + return NULL; + } //end if + } //end for + return NULL; +} //end of the function BotChooseInitialChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotNumInitialChats(int chatstate, char *type) +{ + bot_chatstate_t *cs; + bot_chattype_t *t; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + + for (t = cs->chat->types; t; t = t->next) + { + if (!Q_stricmp(t->name, type)) + { + if (LibVarGetValue("bot_testichat")) { + botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages); + botimport.Print(PRT_MESSAGE, "-------------------\n"); + } + return t->numchatmessages; + } //end if + } //end for + return 0; +} //end of the function BotNumInitialChats +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) +{ + char *message; + int index; + bot_match_t match; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + //if no chat file is loaded + if (!cs->chat) return; + //choose a chat message randomly of the given type + message = BotChooseInitialChatMessage(cs, type); + //if there's no message of the given type + if (!message) + { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type); +#endif //DEBUG + return; + } //end if + // + Com_Memset(&match, 0, sizeof(match)); + index = 0; + if( var0 ) { + strcat(match.string, var0); + match.variables[0].offset = index; + match.variables[0].length = strlen(var0); + index += strlen(var0); + } + if( var1 ) { + strcat(match.string, var1); + match.variables[1].offset = index; + match.variables[1].length = strlen(var1); + index += strlen(var1); + } + if( var2 ) { + strcat(match.string, var2); + match.variables[2].offset = index; + match.variables[2].length = strlen(var2); + index += strlen(var2); + } + if( var3 ) { + strcat(match.string, var3); + match.variables[3].offset = index; + match.variables[3].length = strlen(var3); + index += strlen(var3); + } + if( var4 ) { + strcat(match.string, var4); + match.variables[4].offset = index; + match.variables[4].length = strlen(var4); + index += strlen(var4); + } + if( var5 ) { + strcat(match.string, var5); + match.variables[5].offset = index; + match.variables[5].length = strlen(var5); + index += strlen(var5); + } + if( var6 ) { + strcat(match.string, var6); + match.variables[6].offset = index; + match.variables[6].length = strlen(var6); + index += strlen(var6); + } + if( var7 ) { + strcat(match.string, var7); + match.variables[7].offset = index; + match.variables[7].length = strlen(var7); + index += strlen(var7); + } + // + BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse); +} //end of the function BotInitialChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotPrintReplyChatKeys(bot_replychat_t *replychat) +{ + bot_replychatkey_t *key; + bot_matchpiece_t *mp; + + botimport.Print(PRT_MESSAGE, "["); + for (key = replychat->keys; key; key = key->next) + { + if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&"); + else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!"); + // + if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name"); + else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female"); + else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male"); + else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it"); + else if (key->flags & RCKFL_VARIABLES) + { + botimport.Print(PRT_MESSAGE, "("); + for (mp = key->match; mp; mp = mp->next) + { + if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string); + else botimport.Print(PRT_MESSAGE, "%d", mp->variable); + if (mp->next) botimport.Print(PRT_MESSAGE, ", "); + } //end for + botimport.Print(PRT_MESSAGE, ")"); + } //end if + else if (key->flags & RCKFL_STRING) + { + botimport.Print(PRT_MESSAGE, "\"%s\"", key->string); + } //end if + if (key->next) botimport.Print(PRT_MESSAGE, ", "); + else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority); + } //end for + botimport.Print(PRT_MESSAGE, "{\n"); +} //end of the function BotPrintReplyChatKeys +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7) +{ + bot_replychat_t *rchat, *bestrchat; + bot_replychatkey_t *key; + bot_chatmessage_t *m, *bestchatmessage; + bot_match_t match, bestmatch; + int bestpriority, num, found, res, numchatmessages, index; + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return qfalse; + Com_Memset(&match, 0, sizeof(bot_match_t)); + strcpy(match.string, message); + bestpriority = -1; + bestchatmessage = NULL; + bestrchat = NULL; + //go through all the reply chats + for (rchat = replychats; rchat; rchat = rchat->next) + { + found = qfalse; + for (key = rchat->keys; key; key = key->next) + { + res = qfalse; + //get the match result + if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1); + else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1); + else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE); + else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE); + else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS); + else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match); + else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL); + //if the key must be present + if (key->flags & RCKFL_AND) + { + if (!res) + { + found = qfalse; + break; + } //end if + } //end else if + //if the key must be absent + else if (key->flags & RCKFL_NOT) + { + if (res) + { + found = qfalse; + break; + } //end if + } //end if + else if (res) + { + found = qtrue; + } //end else + } //end for + // + if (found) + { + if (rchat->priority > bestpriority) + { + numchatmessages = 0; + for (m = rchat->firstchatmessage; m; m = m->next) + { + if (m->time > AAS_Time()) continue; + numchatmessages++; + } //end if + num = random() * numchatmessages; + for (m = rchat->firstchatmessage; m; m = m->next) + { + if (--num < 0) break; + if (m->time > AAS_Time()) continue; + } //end for + //if the reply chat has a message + if (m) + { + Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t)); + bestchatmessage = m; + bestrchat = rchat; + bestpriority = rchat->priority; + } //end if + } //end if + } //end if + } //end for + if (bestchatmessage) + { + index = strlen(bestmatch.string); + if( var0 ) { + strcat(bestmatch.string, var0); + bestmatch.variables[0].offset = index; + bestmatch.variables[0].length = strlen(var0); + index += strlen(var0); + } + if( var1 ) { + strcat(bestmatch.string, var1); + bestmatch.variables[1].offset = index; + bestmatch.variables[1].length = strlen(var1); + index += strlen(var1); + } + if( var2 ) { + strcat(bestmatch.string, var2); + bestmatch.variables[2].offset = index; + bestmatch.variables[2].length = strlen(var2); + index += strlen(var2); + } + if( var3 ) { + strcat(bestmatch.string, var3); + bestmatch.variables[3].offset = index; + bestmatch.variables[3].length = strlen(var3); + index += strlen(var3); + } + if( var4 ) { + strcat(bestmatch.string, var4); + bestmatch.variables[4].offset = index; + bestmatch.variables[4].length = strlen(var4); + index += strlen(var4); + } + if( var5 ) { + strcat(bestmatch.string, var5); + bestmatch.variables[5].offset = index; + bestmatch.variables[5].length = strlen(var5); + index += strlen(var5); + } + if( var6 ) { + strcat(bestmatch.string, var6); + bestmatch.variables[6].offset = index; + bestmatch.variables[6].length = strlen(var6); + index += strlen(var6); + } + if( var7 ) { + strcat(bestmatch.string, var7); + bestmatch.variables[7].offset = index; + bestmatch.variables[7].length = strlen(var7); + index += strlen(var7); + } + if (LibVarGetValue("bot_testrchat")) + { + for (m = bestrchat->firstchatmessage; m; m = m->next) + { + BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue); + BotRemoveTildes(cs->chatmessage); + botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); + } //end if + } //end if + else + { + bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME; + BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue); + } //end else + return qtrue; + } //end if + return qfalse; +} //end of the function BotReplyChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChatLength(int chatstate) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return 0; + return strlen(cs->chatmessage); +} //end of the function BotChatLength +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotEnterChat(int chatstate, int clientto, int sendto) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + if (strlen(cs->chatmessage)) + { + BotRemoveTildes(cs->chatmessage); + if (LibVarGetValue("bot_testichat")) { + botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage); + } + else { + switch(sendto) { + case CHAT_TEAM: + EA_Command(cs->client, va("say_team %s", cs->chatmessage)); + break; + case CHAT_TELL: + EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage)); + break; + default: //CHAT_ALL + EA_Command(cs->client, va("say %s", cs->chatmessage)); + break; + } + } + //clear the chat message from the state + strcpy(cs->chatmessage, ""); + } //end if +} //end of the function BotEnterChat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotGetChatMessage(int chatstate, char *buf, int size) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + + BotRemoveTildes(cs->chatmessage); + strncpy(buf, cs->chatmessage, size-1); + buf[size-1] = '\0'; + //clear the chat message from the state + strcpy(cs->chatmessage, ""); +} //end of the function BotGetChatMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSetChatGender(int chatstate, int gender) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + switch(gender) + { + case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break; + case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break; + default: cs->gender = CHAT_GENDERLESS; break; + } //end switch +} //end of the function BotSetChatGender +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSetChatName(int chatstate, char *name, int client) +{ + bot_chatstate_t *cs; + + cs = BotChatStateFromHandle(chatstate); + if (!cs) return; + cs->client = client; + Com_Memset(cs->name, 0, sizeof(cs->name)); + strncpy(cs->name, name, sizeof(cs->name)); + cs->name[sizeof(cs->name)-1] = '\0'; +} //end of the function BotSetChatName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetChatAI(void) +{ + bot_replychat_t *rchat; + bot_chatmessage_t *m; + + for (rchat = replychats; rchat; rchat = rchat->next) + { + for (m = rchat->firstchatmessage; m; m = m->next) + { + m->time = 0; + } //end for + } //end for +} //end of the function BotResetChatAI +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotAllocChatState(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botchatstates[i]) + { + botchatstates[i] = GetClearedMemory(sizeof(bot_chatstate_t)); + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocChatState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeChatState(int handle) +{ + bot_chatstate_t *cs; + bot_consolemessage_t m; + int h; + + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle); + return; + } //end if + if (!botchatstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle); + return; + } //end if + cs = botchatstates[handle]; + if (LibVarGetValue("bot_reloadcharacters")) + { + BotFreeChatFile(handle); + } //end if + //free all the console messages left in the chat state + for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m)) + { + //remove the console message + BotRemoveConsoleMessage(handle, h); + } //end for + FreeMemory(botchatstates[handle]); + botchatstates[handle] = NULL; +} //end of the function BotFreeChatState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupChatAI(void) +{ + char *file; + +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif //DEBUG + + file = LibVarString("synfile", "syn.c"); + synonyms = BotLoadSynonyms(file); + file = LibVarString("rndfile", "rnd.c"); + randomstrings = BotLoadRandomStrings(file); + file = LibVarString("matchfile", "match.c"); + matchtemplates = BotLoadMatchTemplates(file); + // + if (!LibVarValue("nochat", "0")) + { + file = LibVarString("rchatfile", "rchat.c"); + replychats = BotLoadReplyChat(file); + } //end if + + InitConsoleMessageHeap(); + +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime); +#endif //DEBUG + return BLERR_NOERROR; +} //end of the function BotSetupChatAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownChatAI(void) +{ + int i; + + //free all remaining chat states + for(i = 0; i < MAX_CLIENTS; i++) + { + if (botchatstates[i]) + { + BotFreeChatState(i); + } //end if + } //end for + //free all cached chats + for(i = 0; i < MAX_CLIENTS; i++) + { + if (ichatdata[i]) + { + FreeMemory(ichatdata[i]->chat); + FreeMemory(ichatdata[i]); + ichatdata[i] = NULL; + } //end if + } //end for + if (consolemessageheap) FreeMemory(consolemessageheap); + consolemessageheap = NULL; + if (matchtemplates) BotFreeMatchTemplates(matchtemplates); + matchtemplates = NULL; + if (randomstrings) FreeMemory(randomstrings); + randomstrings = NULL; + if (synonyms) FreeMemory(synonyms); + synonyms = NULL; + if (replychats) BotFreeReplyChat(replychats); + replychats = NULL; +} //end of the function BotShutdownChatAI diff --git a/code/botlib/be_ai_gen.c b/code/botlib/be_ai_gen.c index 510d6b0..b897738 100755 --- a/code/botlib/be_ai_gen.c +++ b/code/botlib/be_ai_gen.c @@ -1,134 +1,134 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_gen.c
- *
- * desc: genetic selection
- *
- * $Archive: /MissionPack/code/botlib/be_ai_gen.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "../game/be_ai_gen.h"
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int GeneticSelection(int numranks, float *rankings)
-{
- float sum, select;
- int i, index;
-
- sum = 0;
- for (i = 0; i < numranks; i++)
- {
- if (rankings[i] < 0) continue;
- sum += rankings[i];
- } //end for
- if (sum > 0)
- {
- //select a bot where the ones with the higest rankings have
- //the highest chance of being selected
- select = random() * sum;
- for (i = 0; i < numranks; i++)
- {
- if (rankings[i] < 0) continue;
- sum -= rankings[i];
- if (sum <= 0) return i;
- } //end for
- } //end if
- //select a bot randomly
- index = random() * numranks;
- for (i = 0; i < numranks; i++)
- {
- if (rankings[index] >= 0) return index;
- index = (index + 1) % numranks;
- } //end for
- return 0;
-} //end of the function GeneticSelection
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child)
-{
- float rankings[256], max;
- int i;
-
- if (numranks > 256)
- {
- botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too many bots\n");
- *parent1 = *parent2 = *child = 0;
- return qfalse;
- } //end if
- for (max = 0, i = 0; i < numranks; i++)
- {
- if (ranks[i] < 0) continue;
- max++;
- } //end for
- if (max < 3)
- {
- botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too few valid bots\n");
- *parent1 = *parent2 = *child = 0;
- return qfalse;
- } //end if
- Com_Memcpy(rankings, ranks, sizeof(float) * numranks);
- //select first parent
- *parent1 = GeneticSelection(numranks, rankings);
- rankings[*parent1] = -1;
- //select second parent
- *parent2 = GeneticSelection(numranks, rankings);
- rankings[*parent2] = -1;
- //reverse the rankings
- max = 0;
- for (i = 0; i < numranks; i++)
- {
- if (rankings[i] < 0) continue;
- if (rankings[i] > max) max = rankings[i];
- } //end for
- for (i = 0; i < numranks; i++)
- {
- if (rankings[i] < 0) continue;
- rankings[i] = max - rankings[i];
- } //end for
- //select child
- *child = GeneticSelection(numranks, rankings);
- return qtrue;
-} //end of the function GeneticParentsAndChildSelection
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_gen.c + * + * desc: genetic selection + * + * $Archive: /MissionPack/code/botlib/be_ai_gen.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/be_ai_gen.h" + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GeneticSelection(int numranks, float *rankings) +{ + float sum, select; + int i, index; + + sum = 0; + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + sum += rankings[i]; + } //end for + if (sum > 0) + { + //select a bot where the ones with the higest rankings have + //the highest chance of being selected + select = random() * sum; + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + sum -= rankings[i]; + if (sum <= 0) return i; + } //end for + } //end if + //select a bot randomly + index = random() * numranks; + for (i = 0; i < numranks; i++) + { + if (rankings[index] >= 0) return index; + index = (index + 1) % numranks; + } //end for + return 0; +} //end of the function GeneticSelection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child) +{ + float rankings[256], max; + int i; + + if (numranks > 256) + { + botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too many bots\n"); + *parent1 = *parent2 = *child = 0; + return qfalse; + } //end if + for (max = 0, i = 0; i < numranks; i++) + { + if (ranks[i] < 0) continue; + max++; + } //end for + if (max < 3) + { + botimport.Print(PRT_WARNING, "GeneticParentsAndChildSelection: too few valid bots\n"); + *parent1 = *parent2 = *child = 0; + return qfalse; + } //end if + Com_Memcpy(rankings, ranks, sizeof(float) * numranks); + //select first parent + *parent1 = GeneticSelection(numranks, rankings); + rankings[*parent1] = -1; + //select second parent + *parent2 = GeneticSelection(numranks, rankings); + rankings[*parent2] = -1; + //reverse the rankings + max = 0; + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + if (rankings[i] > max) max = rankings[i]; + } //end for + for (i = 0; i < numranks; i++) + { + if (rankings[i] < 0) continue; + rankings[i] = max - rankings[i]; + } //end for + //select child + *child = GeneticSelection(numranks, rankings); + return qtrue; +} //end of the function GeneticParentsAndChildSelection diff --git a/code/botlib/be_ai_goal.c b/code/botlib/be_ai_goal.c index 2735c57..f71bc43 100755 --- a/code/botlib/be_ai_goal.c +++ b/code/botlib/be_ai_goal.c @@ -1,1821 +1,1821 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_goal.c
- *
- * desc: goal AI
- *
- * $Archive: /MissionPack/code/botlib/be_ai_goal.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_utils.h"
-#include "l_libvar.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_ai_weight.h"
-#include "../game/be_ai_goal.h"
-#include "../game/be_ai_move.h"
-
-//#define DEBUG_AI_GOAL
-#ifdef RANDOMIZE
-#define UNDECIDEDFUZZY
-#endif //RANDOMIZE
-#define DROPPEDWEIGHT
-//minimum avoid goal time
-#define AVOID_MINIMUM_TIME 10
-//default avoid goal time
-#define AVOID_DEFAULT_TIME 30
-//avoid dropped goal time
-#define AVOID_DROPPED_TIME 10
-//
-#define TRAVELTIME_SCALE 0.01
-//item flags
-#define IFL_NOTFREE 1 //not in free for all
-#define IFL_NOTTEAM 2 //not in team play
-#define IFL_NOTSINGLE 4 //not in single player
-#define IFL_NOTBOT 8 //bot should never go for this
-#define IFL_ROAM 16 //bot roam goal
-
-//location in the map "target_location"
-typedef struct maplocation_s
-{
- vec3_t origin;
- int areanum;
- char name[MAX_EPAIRKEY];
- struct maplocation_s *next;
-} maplocation_t;
-
-//camp spots "info_camp"
-typedef struct campspot_s
-{
- vec3_t origin;
- int areanum;
- char name[MAX_EPAIRKEY];
- float range;
- float weight;
- float wait;
- float random;
- struct campspot_s *next;
-} campspot_t;
-
-//FIXME: these are game specific
-typedef enum {
- GT_FFA, // free for all
- GT_TOURNAMENT, // one on one tournament
- GT_SINGLE_PLAYER, // single player tournament
-
- //-- team games go after this --
-
- GT_TEAM, // team deathmatch
- GT_CTF, // capture the flag
-#ifdef MISSIONPACK
- GT_1FCTF,
- GT_OBELISK,
- GT_HARVESTER,
-#endif
- GT_MAX_GAME_TYPE
-} gametype_t;
-
-typedef struct levelitem_s
-{
- int number; //number of the level item
- int iteminfo; //index into the item info
- int flags; //item flags
- float weight; //fixed roam weight
- vec3_t origin; //origin of the item
- int goalareanum; //area the item is in
- vec3_t goalorigin; //goal origin within the area
- int entitynum; //entity number
- float timeout; //item is removed after this time
- struct levelitem_s *prev, *next;
-} levelitem_t;
-
-typedef struct iteminfo_s
-{
- char classname[32]; //classname of the item
- char name[MAX_STRINGFIELD]; //name of the item
- char model[MAX_STRINGFIELD]; //model of the item
- int modelindex; //model index
- int type; //item type
- int index; //index in the inventory
- float respawntime; //respawn time
- vec3_t mins; //mins of the item
- vec3_t maxs; //maxs of the item
- int number; //number of the item info
-} iteminfo_t;
-
-#define ITEMINFO_OFS(x) (int)&(((iteminfo_t *)0)->x)
-
-fielddef_t iteminfo_fields[] =
-{
-{"name", ITEMINFO_OFS(name), FT_STRING},
-{"model", ITEMINFO_OFS(model), FT_STRING},
-{"modelindex", ITEMINFO_OFS(modelindex), FT_INT},
-{"type", ITEMINFO_OFS(type), FT_INT},
-{"index", ITEMINFO_OFS(index), FT_INT},
-{"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT},
-{"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3},
-{"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3},
-{0, 0, 0}
-};
-
-structdef_t iteminfo_struct =
-{
- sizeof(iteminfo_t), iteminfo_fields
-};
-
-typedef struct itemconfig_s
-{
- int numiteminfo;
- iteminfo_t *iteminfo;
-} itemconfig_t;
-
-//goal state
-typedef struct bot_goalstate_s
-{
- struct weightconfig_s *itemweightconfig; //weight config
- int *itemweightindex; //index from item to weight
- //
- int client; //client using this goal state
- int lastreachabilityarea; //last area with reachabilities the bot was in
- //
- bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack
- int goalstacktop; //the top of the goal stack
- //
- int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid
- float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals
-} bot_goalstate_t;
-
-bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // bk001206 - FIXME: init?
-//item configuration
-itemconfig_t *itemconfig = NULL; // bk001206 - init
-//level items
-levelitem_t *levelitemheap = NULL; // bk001206 - init
-levelitem_t *freelevelitems = NULL; // bk001206 - init
-levelitem_t *levelitems = NULL; // bk001206 - init
-int numlevelitems = 0;
-//map locations
-maplocation_t *maplocations = NULL; // bk001206 - init
-//camp spots
-campspot_t *campspots = NULL; // bk001206 - init
-//the game type
-int g_gametype = 0; // bk001206 - init
-//additional dropped item weight
-libvar_t *droppedweight = NULL; // bk001206 - init
-
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-bot_goalstate_t *BotGoalStateFromHandle(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle);
- return NULL;
- } //end if
- if (!botgoalstates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle);
- return NULL;
- } //end if
- return botgoalstates[handle];
-} //end of the function BotGoalStateFromHandle
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child)
-{
- bot_goalstate_t *p1, *p2, *c;
-
- p1 = BotGoalStateFromHandle(parent1);
- p2 = BotGoalStateFromHandle(parent2);
- c = BotGoalStateFromHandle(child);
-
- InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig,
- c->itemweightconfig);
-} //end of the function BotInterbreedingGoalFuzzyLogic
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotSaveGoalFuzzyLogic(int goalstate, char *filename)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
-
- //WriteWeightConfig(filename, gs->itemweightconfig);
-} //end of the function BotSaveGoalFuzzyLogic
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotMutateGoalFuzzyLogic(int goalstate, float range)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
-
- EvolveWeightConfig(gs->itemweightconfig);
-} //end of the function BotMutateGoalFuzzyLogic
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-itemconfig_t *LoadItemConfig(char *filename)
-{
- int max_iteminfo;
- token_t token;
- char path[MAX_PATH];
- source_t *source;
- itemconfig_t *ic;
- iteminfo_t *ii;
-
- max_iteminfo = (int) LibVarValue("max_iteminfo", "256");
- if (max_iteminfo < 0)
- {
- botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo);
- max_iteminfo = 256;
- LibVarSet( "max_iteminfo", "256" );
- }
-
- strncpy( path, filename, MAX_PATH );
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile( path );
- if( !source ) {
- botimport.Print( PRT_ERROR, "counldn't load %s\n", path );
- return NULL;
- } //end if
- //initialize item config
- ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) +
- max_iteminfo * sizeof(iteminfo_t));
- ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t));
- ic->numiteminfo = 0;
- //parse the item config file
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "iteminfo"))
- {
- if (ic->numiteminfo >= max_iteminfo)
- {
- SourceError(source, "more than %d item info defined\n", max_iteminfo);
- FreeMemory(ic);
- FreeSource(source);
- return NULL;
- } //end if
- ii = &ic->iteminfo[ic->numiteminfo];
- Com_Memset(ii, 0, sizeof(iteminfo_t));
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- FreeMemory(ic);
- FreeMemory(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- strncpy(ii->classname, token.string, sizeof(ii->classname)-1);
- if (!ReadStructure(source, &iteminfo_struct, (char *) ii))
- {
- FreeMemory(ic);
- FreeSource(source);
- return NULL;
- } //end if
- ii->number = ic->numiteminfo;
- ic->numiteminfo++;
- } //end if
- else
- {
- SourceError(source, "unknown definition %s\n", token.string);
- FreeMemory(ic);
- FreeSource(source);
- return NULL;
- } //end else
- } //end while
- FreeSource(source);
- //
- if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n");
- botimport.Print(PRT_MESSAGE, "loaded %s\n", path);
- return ic;
-} //end of the function LoadItemConfig
-//===========================================================================
-// index to find the weight function of an iteminfo
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic)
-{
- int *index, i;
-
- //initialize item weight index
- index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo);
-
- for (i = 0; i < ic->numiteminfo; i++)
- {
- index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname);
- if (index[i] < 0)
- {
- Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname);
- } //end if
- } //end for
- return index;
-} //end of the function ItemWeightIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void InitLevelItemHeap(void)
-{
- int i, max_levelitems;
-
- if (levelitemheap) FreeMemory(levelitemheap);
-
- max_levelitems = (int) LibVarValue("max_levelitems", "256");
- levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t));
-
- for (i = 0; i < max_levelitems-1; i++)
- {
- levelitemheap[i].next = &levelitemheap[i + 1];
- } //end for
- levelitemheap[max_levelitems-1].next = NULL;
- //
- freelevelitems = levelitemheap;
-} //end of the function InitLevelItemHeap
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-levelitem_t *AllocLevelItem(void)
-{
- levelitem_t *li;
-
- li = freelevelitems;
- if (!li)
- {
- botimport.Print(PRT_FATAL, "out of level items\n");
- return NULL;
- } //end if
- //
- freelevelitems = freelevelitems->next;
- Com_Memset(li, 0, sizeof(levelitem_t));
- return li;
-} //end of the function AllocLevelItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeLevelItem(levelitem_t *li)
-{
- li->next = freelevelitems;
- freelevelitems = li;
-} //end of the function FreeLevelItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AddLevelItemToList(levelitem_t *li)
-{
- if (levelitems) levelitems->prev = li;
- li->prev = NULL;
- li->next = levelitems;
- levelitems = li;
-} //end of the function AddLevelItemToList
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void RemoveLevelItemFromList(levelitem_t *li)
-{
- if (li->prev) li->prev->next = li->next;
- else levelitems = li->next;
- if (li->next) li->next->prev = li->prev;
-} //end of the function RemoveLevelItemFromList
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeInfoEntities(void)
-{
- maplocation_t *ml, *nextml;
- campspot_t *cs, *nextcs;
-
- for (ml = maplocations; ml; ml = nextml)
- {
- nextml = ml->next;
- FreeMemory(ml);
- } //end for
- maplocations = NULL;
- for (cs = campspots; cs; cs = nextcs)
- {
- nextcs = cs->next;
- FreeMemory(cs);
- } //end for
- campspots = NULL;
-} //end of the function BotFreeInfoEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotInitInfoEntities(void)
-{
- char classname[MAX_EPAIRKEY];
- maplocation_t *ml;
- campspot_t *cs;
- int ent, numlocations, numcampspots;
-
- BotFreeInfoEntities();
- //
- numlocations = 0;
- numcampspots = 0;
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
-
- //map locations
- if (!strcmp(classname, "target_location"))
- {
- ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t));
- AAS_VectorForBSPEpairKey(ent, "origin", ml->origin);
- AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name));
- ml->areanum = AAS_PointAreaNum(ml->origin);
- ml->next = maplocations;
- maplocations = ml;
- numlocations++;
- } //end if
- //camp spots
- else if (!strcmp(classname, "info_camp"))
- {
- cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t));
- AAS_VectorForBSPEpairKey(ent, "origin", cs->origin);
- //cs->origin[2] += 16;
- AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name));
- AAS_FloatForBSPEpairKey(ent, "range", &cs->range);
- AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight);
- AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait);
- AAS_FloatForBSPEpairKey(ent, "random", &cs->random);
- cs->areanum = AAS_PointAreaNum(cs->origin);
- if (!cs->areanum)
- {
- botimport.Print(PRT_MESSAGE, "camp spot at %1.1f %1.1f %1.1f in solid\n", cs->origin[0], cs->origin[1], cs->origin[2]);
- FreeMemory(cs);
- continue;
- } //end if
- cs->next = campspots;
- campspots = cs;
- //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW);
- numcampspots++;
- } //end else if
- } //end for
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations);
- botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots);
- } //end if
-} //end of the function BotInitInfoEntities
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotInitLevelItems(void)
-{
- int i, spawnflags, value;
- char classname[MAX_EPAIRKEY];
- vec3_t origin, end;
- int ent, goalareanum;
- itemconfig_t *ic;
- levelitem_t *li;
- bsp_trace_t trace;
-
- //initialize the map locations and camp spots
- BotInitInfoEntities();
-
- //initialize the level item heap
- InitLevelItemHeap();
- levelitems = NULL;
- numlevelitems = 0;
- //
- ic = itemconfig;
- if (!ic) return;
-
- //if there's no AAS file loaded
- if (!AAS_Loaded()) return;
-
- //update the modelindexes of the item info
- for (i = 0; i < ic->numiteminfo; i++)
- {
- //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model);
- if (!ic->iteminfo[i].modelindex)
- {
- Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname);
- } //end if
- } //end for
-
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- //
- spawnflags = 0;
- AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
- //
- for (i = 0; i < ic->numiteminfo; i++)
- {
- if (!strcmp(classname, ic->iteminfo[i].classname)) break;
- } //end for
- if (i >= ic->numiteminfo)
- {
- Log_Write("entity %s unknown item\r\n", classname);
- continue;
- } //end if
- //get the origin of the item
- if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
- {
- botimport.Print(PRT_ERROR, "item %s without origin\n", classname);
- continue;
- } //end else
- //
- goalareanum = 0;
- //if it is a floating item
- if (spawnflags & 1)
- {
- //if the item is not floating in water
- if (!(AAS_PointContents(origin) & CONTENTS_WATER))
- {
- VectorCopy(origin, end);
- end[2] -= 32;
- trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
- //if the item not near the ground
- if (trace.fraction >= 1)
- {
- //if the item is not reachable from a jumppad
- goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs);
- Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
- //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
- if (!goalareanum) continue;
- } //end if
- } //end if
- } //end if
-
- li = AllocLevelItem();
- if (!li) return;
- //
- li->number = ++numlevelitems;
- li->timeout = 0;
- li->entitynum = 0;
- //
- li->flags = 0;
- AAS_IntForBSPEpairKey(ent, "notfree", &value);
- if (value) li->flags |= IFL_NOTFREE;
- AAS_IntForBSPEpairKey(ent, "notteam", &value);
- if (value) li->flags |= IFL_NOTTEAM;
- AAS_IntForBSPEpairKey(ent, "notsingle", &value);
- if (value) li->flags |= IFL_NOTSINGLE;
- AAS_IntForBSPEpairKey(ent, "notbot", &value);
- if (value) li->flags |= IFL_NOTBOT;
- if (!strcmp(classname, "item_botroam"))
- {
- li->flags |= IFL_ROAM;
- AAS_FloatForBSPEpairKey(ent, "weight", &li->weight);
- } //end if
- //if not a stationary item
- if (!(spawnflags & 1))
- {
- if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs))
- {
- botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
- classname, origin[0], origin[1], origin[2]);
- } //end if
- } //end if
- //item info of the level item
- li->iteminfo = i;
- //origin of the item
- VectorCopy(origin, li->origin);
- //
- if (goalareanum)
- {
- li->goalareanum = goalareanum;
- VectorCopy(origin, li->goalorigin);
- } //end if
- else
- {
- //get the item goal area and goal origin
- li->goalareanum = AAS_BestReachableArea(origin,
- ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
- li->goalorigin);
- if (!li->goalareanum)
- {
- botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n",
- classname, origin[0], origin[1], origin[2]);
- } //end if
- } //end else
- //
- AddLevelItemToList(li);
- } //end for
- botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems);
-} //end of the function BotInitLevelItems
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotGoalName(int number, char *name, int size)
-{
- levelitem_t *li;
-
- if (!itemconfig) return;
- //
- for (li = levelitems; li; li = li->next)
- {
- if (li->number == number)
- {
- strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1);
- name[size-1] = '\0';
- return;
- } //end for
- } //end for
- strcpy(name, "");
- return;
-} //end of the function BotGoalName
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetAvoidGoals(int goalstate)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int));
- Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float));
-} //end of the function BotResetAvoidGoals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpAvoidGoals(int goalstate)
-{
- int i;
- bot_goalstate_t *gs;
- char name[32];
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- for (i = 0; i < MAX_AVOIDGOALS; i++)
- {
- if (gs->avoidgoaltimes[i] >= AAS_Time())
- {
- BotGoalName(gs->avoidgoals[i], name, 32);
- Log_Write("avoid goal %s, number %d for %f seconds", name,
- gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time());
- } //end if
- } //end for
-} //end of the function BotDumpAvoidGoals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime)
-{
- int i;
-
- for (i = 0; i < MAX_AVOIDGOALS; i++)
- {
- //if the avoid goal is already stored
- if (gs->avoidgoals[i] == number)
- {
- gs->avoidgoals[i] = number;
- gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
- return;
- } //end if
- } //end for
-
- for (i = 0; i < MAX_AVOIDGOALS; i++)
- {
- //if this avoid goal has expired
- if (gs->avoidgoaltimes[i] < AAS_Time())
- {
- gs->avoidgoals[i] = number;
- gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
- return;
- } //end if
- } //end for
-} //end of the function BotAddToAvoidGoals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotRemoveFromAvoidGoals(int goalstate, int number)
-{
- int i;
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- //don't use the goals the bot wants to avoid
- for (i = 0; i < MAX_AVOIDGOALS; i++)
- {
- if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())
- {
- gs->avoidgoaltimes[i] = 0;
- return;
- } //end if
- } //end for
-} //end of the function BotRemoveFromAvoidGoals
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float BotAvoidGoalTime(int goalstate, int number)
-{
- int i;
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return 0;
- //don't use the goals the bot wants to avoid
- for (i = 0; i < MAX_AVOIDGOALS; i++)
- {
- if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())
- {
- return gs->avoidgoaltimes[i] - AAS_Time();
- } //end if
- } //end for
- return 0;
-} //end of the function BotAvoidGoalTime
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime)
-{
- bot_goalstate_t *gs;
- levelitem_t *li;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs)
- return;
- if (avoidtime < 0)
- {
- if (!itemconfig)
- return;
- //
- for (li = levelitems; li; li = li->next)
- {
- if (li->number == number)
- {
- avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime;
- if (!avoidtime)
- avoidtime = AVOID_DEFAULT_TIME;
- if (avoidtime < AVOID_MINIMUM_TIME)
- avoidtime = AVOID_MINIMUM_TIME;
- BotAddToAvoidGoals(gs, number, avoidtime);
- return;
- } //end for
- } //end for
- return;
- } //end if
- else
- {
- BotAddToAvoidGoals(gs, number, avoidtime);
- } //end else
-} //end of the function BotSetAvoidGoalTime
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal)
-{
- levelitem_t *li;
-
- if (!itemconfig) return -1;
- li = levelitems;
- if (index >= 0)
- {
- for (; li; li = li->next)
- {
- if (li->number == index)
- {
- li = li->next;
- break;
- } //end if
- } //end for
- } //end for
- for (; li; li = li->next)
- {
- //
- if (g_gametype == GT_SINGLE_PLAYER) {
- if (li->flags & IFL_NOTSINGLE) continue;
- }
- else if (g_gametype >= GT_TEAM) {
- if (li->flags & IFL_NOTTEAM) continue;
- }
- else {
- if (li->flags & IFL_NOTFREE) continue;
- }
- if (li->flags & IFL_NOTBOT) continue;
- //
- if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name))
- {
- goal->areanum = li->goalareanum;
- VectorCopy(li->goalorigin, goal->origin);
- goal->entitynum = li->entitynum;
- VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins);
- VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs);
- goal->number = li->number;
- goal->flags = GFL_ITEM;
- if (li->timeout) goal->flags |= GFL_DROPPED;
- //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name);
- return li->number;
- } //end if
- } //end for
- return -1;
-} //end of the function BotGetLevelItemGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotGetMapLocationGoal(char *name, bot_goal_t *goal)
-{
- maplocation_t *ml;
- vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
-
- for (ml = maplocations; ml; ml = ml->next)
- {
- if (!Q_stricmp(ml->name, name))
- {
- goal->areanum = ml->areanum;
- VectorCopy(ml->origin, goal->origin);
- goal->entitynum = 0;
- VectorCopy(mins, goal->mins);
- VectorCopy(maxs, goal->maxs);
- return qtrue;
- } //end if
- } //end for
- return qfalse;
-} //end of the function BotGetMapLocationGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotGetNextCampSpotGoal(int num, bot_goal_t *goal)
-{
- int i;
- campspot_t *cs;
- vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
-
- if (num < 0) num = 0;
- i = num;
- for (cs = campspots; cs; cs = cs->next)
- {
- if (--i < 0)
- {
- goal->areanum = cs->areanum;
- VectorCopy(cs->origin, goal->origin);
- goal->entitynum = 0;
- VectorCopy(mins, goal->mins);
- VectorCopy(maxs, goal->maxs);
- return num+1;
- } //end if
- } //end for
- return 0;
-} //end of the function BotGetNextCampSpotGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFindEntityForLevelItem(levelitem_t *li)
-{
- int ent, modelindex;
- itemconfig_t *ic;
- aas_entityinfo_t entinfo;
- vec3_t dir;
-
- ic = itemconfig;
- if (!itemconfig) return;
- for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))
- {
- //get the model index of the entity
- modelindex = AAS_EntityModelindex(ent);
- //
- if (!modelindex) continue;
- //get info about the entity
- AAS_EntityInfo(ent, &entinfo);
- //if the entity is still moving
- if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
- entinfo.origin[1] != entinfo.lastvisorigin[1] ||
- entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
- //
- if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
- {
- //check if the entity is very close
- VectorSubtract(li->origin, entinfo.origin, dir);
- if (VectorLength(dir) < 30)
- {
- //found an entity for this level item
- li->entitynum = ent;
- } //end if
- } //end if
- } //end for
-} //end of the function BotFindEntityForLevelItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-
-//NOTE: enum entityType_t in bg_public.h
-#define ET_ITEM 2
-
-void BotUpdateEntityItems(void)
-{
- int ent, i, modelindex;
- vec3_t dir;
- levelitem_t *li, *nextli;
- aas_entityinfo_t entinfo;
- itemconfig_t *ic;
-
- //timeout current entity items if necessary
- for (li = levelitems; li; li = nextli)
- {
- nextli = li->next;
- //if it is a item that will time out
- if (li->timeout)
- {
- //timeout the item
- if (li->timeout < AAS_Time())
- {
- RemoveLevelItemFromList(li);
- FreeLevelItem(li);
- } //end if
- } //end if
- } //end for
- //find new entity items
- ic = itemconfig;
- if (!itemconfig) return;
- //
- for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))
- {
- if (AAS_EntityType(ent) != ET_ITEM) continue;
- //get the model index of the entity
- modelindex = AAS_EntityModelindex(ent);
- //
- if (!modelindex) continue;
- //get info about the entity
- AAS_EntityInfo(ent, &entinfo);
- //FIXME: don't do this
- //skip all floating items for now
- //if (entinfo.groundent != ENTITYNUM_WORLD) continue;
- //if the entity is still moving
- if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
- entinfo.origin[1] != entinfo.lastvisorigin[1] ||
- entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
- //check if the entity is already stored as a level item
- for (li = levelitems; li; li = li->next)
- {
- //if the level item is linked to an entity
- if (li->entitynum && li->entitynum == ent)
- {
- //the entity is re-used if the models are different
- if (ic->iteminfo[li->iteminfo].modelindex != modelindex)
- {
- //remove this level item
- RemoveLevelItemFromList(li);
- FreeLevelItem(li);
- li = NULL;
- break;
- } //end if
- else
- {
- if (entinfo.origin[0] != li->origin[0] ||
- entinfo.origin[1] != li->origin[1] ||
- entinfo.origin[2] != li->origin[2])
- {
- VectorCopy(entinfo.origin, li->origin);
- //also update the goal area number
- li->goalareanum = AAS_BestReachableArea(li->origin,
- ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
- li->goalorigin);
- } //end if
- break;
- } //end else
- } //end if
- } //end for
- if (li) continue;
- //try to link the entity to a level item
- for (li = levelitems; li; li = li->next)
- {
- //if this level item is already linked
- if (li->entitynum) continue;
- //
- if (g_gametype == GT_SINGLE_PLAYER) {
- if (li->flags & IFL_NOTSINGLE) continue;
- }
- else if (g_gametype >= GT_TEAM) {
- if (li->flags & IFL_NOTTEAM) continue;
- }
- else {
- if (li->flags & IFL_NOTFREE) continue;
- }
- //if the model of the level item and the entity are the same
- if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
- {
- //check if the entity is very close
- VectorSubtract(li->origin, entinfo.origin, dir);
- if (VectorLength(dir) < 30)
- {
- //found an entity for this level item
- li->entitynum = ent;
- //if the origin is different
- if (entinfo.origin[0] != li->origin[0] ||
- entinfo.origin[1] != li->origin[1] ||
- entinfo.origin[2] != li->origin[2])
- {
- //update the level item origin
- VectorCopy(entinfo.origin, li->origin);
- //also update the goal area number
- li->goalareanum = AAS_BestReachableArea(li->origin,
- ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
- li->goalorigin);
- } //end if
-#ifdef DEBUG
- Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname);
-#endif //DEBUG
- break;
- } //end if
- } //end else
- } //end for
- if (li) continue;
- //check if the model is from a known item
- for (i = 0; i < ic->numiteminfo; i++)
- {
- if (ic->iteminfo[i].modelindex == modelindex)
- {
- break;
- } //end if
- } //end for
- //if the model is not from a known item
- if (i >= ic->numiteminfo) continue;
- //allocate a new level item
- li = AllocLevelItem();
- //
- if (!li) continue;
- //entity number of the level item
- li->entitynum = ent;
- //number for the level item
- li->number = numlevelitems + ent;
- //set the item info index for the level item
- li->iteminfo = i;
- //origin of the item
- VectorCopy(entinfo.origin, li->origin);
- //get the item goal area and goal origin
- li->goalareanum = AAS_BestReachableArea(li->origin,
- ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
- li->goalorigin);
- //never go for items dropped into jumppads
- if (AAS_AreaJumpPad(li->goalareanum))
- {
- FreeLevelItem(li);
- continue;
- } //end if
- //time this item out after 30 seconds
- //dropped items disappear after 30 seconds
- li->timeout = AAS_Time() + 30;
- //add the level item to the list
- AddLevelItemToList(li);
- //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname);
- } //end for
- /*
- for (li = levelitems; li; li = li->next)
- {
- if (!li->entitynum)
- {
- BotFindEntityForLevelItem(li);
- } //end if
- } //end for*/
-} //end of the function BotUpdateEntityItems
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotDumpGoalStack(int goalstate)
-{
- int i;
- bot_goalstate_t *gs;
- char name[32];
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- for (i = 1; i <= gs->goalstacktop; i++)
- {
- BotGoalName(gs->goalstack[i].number, name, 32);
- Log_Write("%d: %s", i, name);
- } //end for
-} //end of the function BotDumpGoalStack
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotPushGoal(int goalstate, bot_goal_t *goal)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- if (gs->goalstacktop >= MAX_GOALSTACK-1)
- {
- botimport.Print(PRT_ERROR, "goal heap overflow\n");
- BotDumpGoalStack(goalstate);
- return;
- } //end if
- gs->goalstacktop++;
- Com_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t));
-} //end of the function BotPushGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotPopGoal(int goalstate)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- if (gs->goalstacktop > 0) gs->goalstacktop--;
-} //end of the function BotPopGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotEmptyGoalStack(int goalstate)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- gs->goalstacktop = 0;
-} //end of the function BotEmptyGoalStack
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotGetTopGoal(int goalstate, bot_goal_t *goal)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return qfalse;
- if (!gs->goalstacktop) return qfalse;
- Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t));
- return qtrue;
-} //end of the function BotGetTopGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotGetSecondGoal(int goalstate, bot_goal_t *goal)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return qfalse;
- if (gs->goalstacktop <= 1) return qfalse;
- Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t));
- return qtrue;
-} //end of the function BotGetSecondGoal
-//===========================================================================
-// pops a new long term goal on the goal stack in the goalstate
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags)
-{
- int areanum, t, weightnum;
- float weight, bestweight, avoidtime;
- iteminfo_t *iteminfo;
- itemconfig_t *ic;
- levelitem_t *li, *bestitem;
- bot_goal_t goal;
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs)
- return qfalse;
- if (!gs->itemweightconfig)
- return qfalse;
- //get the area the bot is in
- areanum = BotReachabilityArea(origin, gs->client);
- //if the bot is in solid or if the area the bot is in has no reachability links
- if (!areanum || !AAS_AreaReachability(areanum))
- {
- //use the last valid area the bot was in
- areanum = gs->lastreachabilityarea;
- } //end if
- //remember the last area with reachabilities the bot was in
- gs->lastreachabilityarea = areanum;
- //if still in solid
- if (!areanum)
- return qfalse;
- //the item configuration
- ic = itemconfig;
- if (!itemconfig)
- return qfalse;
- //best weight and item so far
- bestweight = 0;
- bestitem = NULL;
- Com_Memset(&goal, 0, sizeof(bot_goal_t));
- //go through the items in the level
- for (li = levelitems; li; li = li->next)
- {
- if (g_gametype == GT_SINGLE_PLAYER) {
- if (li->flags & IFL_NOTSINGLE)
- continue;
- }
- else if (g_gametype >= GT_TEAM) {
- if (li->flags & IFL_NOTTEAM)
- continue;
- }
- else {
- if (li->flags & IFL_NOTFREE)
- continue;
- }
- if (li->flags & IFL_NOTBOT)
- continue;
- //if the item is not in a possible goal area
- if (!li->goalareanum)
- continue;
- //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)
- if (!li->entitynum && !(li->flags & IFL_ROAM))
- continue;
- //get the fuzzy weight function for this item
- iteminfo = &ic->iteminfo[li->iteminfo];
- weightnum = gs->itemweightindex[iteminfo->number];
- if (weightnum < 0)
- continue;
-
-#ifdef UNDECIDEDFUZZY
- weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum);
-#else
- weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum);
-#endif //UNDECIDEDFUZZY
-#ifdef DROPPEDWEIGHT
- //HACK: to make dropped items more attractive
- if (li->timeout)
- weight += droppedweight->value;
-#endif //DROPPEDWEIGHT
- //use weight scale for item_botroam
- if (li->flags & IFL_ROAM) weight *= li->weight;
- //
- if (weight > 0)
- {
- //get the travel time towards the goal area
- t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);
- //if the goal is reachable
- if (t > 0)
- {
- //if this item won't respawn before we get there
- avoidtime = BotAvoidGoalTime(goalstate, li->number);
- if (avoidtime - t * 0.009 > 0)
- continue;
- //
- weight /= (float) t * TRAVELTIME_SCALE;
- //
- if (weight > bestweight)
- {
- bestweight = weight;
- bestitem = li;
- } //end if
- } //end if
- } //end if
- } //end for
- //if no goal item found
- if (!bestitem)
- {
- /*
- //if not in lava or slime
- if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum))
- {
- if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin))
- {
- VectorSet(goal.mins, -15, -15, -15);
- VectorSet(goal.maxs, 15, 15, 15);
- goal.entitynum = 0;
- goal.number = 0;
- goal.flags = GFL_ROAM;
- goal.iteminfo = 0;
- //push the goal on the stack
- BotPushGoal(goalstate, &goal);
- //
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "chosen roam goal area %d\n", goal.areanum);
-#endif //DEBUG
- return qtrue;
- } //end if
- } //end if
- */
- return qfalse;
- } //end if
- //create a bot goal for this item
- iteminfo = &ic->iteminfo[bestitem->iteminfo];
- VectorCopy(bestitem->goalorigin, goal.origin);
- VectorCopy(iteminfo->mins, goal.mins);
- VectorCopy(iteminfo->maxs, goal.maxs);
- goal.areanum = bestitem->goalareanum;
- goal.entitynum = bestitem->entitynum;
- goal.number = bestitem->number;
- goal.flags = GFL_ITEM;
- if (bestitem->timeout)
- goal.flags |= GFL_DROPPED;
- if (bestitem->flags & IFL_ROAM)
- goal.flags |= GFL_ROAM;
- goal.iteminfo = bestitem->iteminfo;
- //if it's a dropped item
- if (bestitem->timeout)
- {
- avoidtime = AVOID_DROPPED_TIME;
- } //end if
- else
- {
- avoidtime = iteminfo->respawntime;
- if (!avoidtime)
- avoidtime = AVOID_DEFAULT_TIME;
- if (avoidtime < AVOID_MINIMUM_TIME)
- avoidtime = AVOID_MINIMUM_TIME;
- } //end else
- //add the chosen goal to the goals to avoid for a while
- BotAddToAvoidGoals(gs, bestitem->number, avoidtime);
- //push the goal on the stack
- BotPushGoal(goalstate, &goal);
- //
- return qtrue;
-} //end of the function BotChooseLTGItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags,
- bot_goal_t *ltg, float maxtime)
-{
- int areanum, t, weightnum, ltg_time;
- float weight, bestweight, avoidtime;
- iteminfo_t *iteminfo;
- itemconfig_t *ic;
- levelitem_t *li, *bestitem;
- bot_goal_t goal;
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs)
- return qfalse;
- if (!gs->itemweightconfig)
- return qfalse;
- //get the area the bot is in
- areanum = BotReachabilityArea(origin, gs->client);
- //if the bot is in solid or if the area the bot is in has no reachability links
- if (!areanum || !AAS_AreaReachability(areanum))
- {
- //use the last valid area the bot was in
- areanum = gs->lastreachabilityarea;
- } //end if
- //remember the last area with reachabilities the bot was in
- gs->lastreachabilityarea = areanum;
- //if still in solid
- if (!areanum)
- return qfalse;
- //
- if (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags);
- else ltg_time = 99999;
- //the item configuration
- ic = itemconfig;
- if (!itemconfig)
- return qfalse;
- //best weight and item so far
- bestweight = 0;
- bestitem = NULL;
- Com_Memset(&goal, 0, sizeof(bot_goal_t));
- //go through the items in the level
- for (li = levelitems; li; li = li->next)
- {
- if (g_gametype == GT_SINGLE_PLAYER) {
- if (li->flags & IFL_NOTSINGLE)
- continue;
- }
- else if (g_gametype >= GT_TEAM) {
- if (li->flags & IFL_NOTTEAM)
- continue;
- }
- else {
- if (li->flags & IFL_NOTFREE)
- continue;
- }
- if (li->flags & IFL_NOTBOT)
- continue;
- //if the item is in a possible goal area
- if (!li->goalareanum)
- continue;
- //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)
- if (!li->entitynum && !(li->flags & IFL_ROAM))
- continue;
- //get the fuzzy weight function for this item
- iteminfo = &ic->iteminfo[li->iteminfo];
- weightnum = gs->itemweightindex[iteminfo->number];
- if (weightnum < 0)
- continue;
- //
-#ifdef UNDECIDEDFUZZY
- weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum);
-#else
- weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum);
-#endif //UNDECIDEDFUZZY
-#ifdef DROPPEDWEIGHT
- //HACK: to make dropped items more attractive
- if (li->timeout)
- weight += droppedweight->value;
-#endif //DROPPEDWEIGHT
- //use weight scale for item_botroam
- if (li->flags & IFL_ROAM) weight *= li->weight;
- //
- if (weight > 0)
- {
- //get the travel time towards the goal area
- t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);
- //if the goal is reachable
- if (t > 0 && t < maxtime)
- {
- //if this item won't respawn before we get there
- avoidtime = BotAvoidGoalTime(goalstate, li->number);
- if (avoidtime - t * 0.009 > 0)
- continue;
- //
- weight /= (float) t * TRAVELTIME_SCALE;
- //
- if (weight > bestweight)
- {
- t = 0;
- if (ltg && !li->timeout)
- {
- //get the travel time from the goal to the long term goal
- t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags);
- } //end if
- //if the travel back is possible and doesn't take too long
- if (t <= ltg_time)
- {
- bestweight = weight;
- bestitem = li;
- } //end if
- } //end if
- } //end if
- } //end if
- } //end for
- //if no goal item found
- if (!bestitem)
- return qfalse;
- //create a bot goal for this item
- iteminfo = &ic->iteminfo[bestitem->iteminfo];
- VectorCopy(bestitem->goalorigin, goal.origin);
- VectorCopy(iteminfo->mins, goal.mins);
- VectorCopy(iteminfo->maxs, goal.maxs);
- goal.areanum = bestitem->goalareanum;
- goal.entitynum = bestitem->entitynum;
- goal.number = bestitem->number;
- goal.flags = GFL_ITEM;
- if (bestitem->timeout)
- goal.flags |= GFL_DROPPED;
- if (bestitem->flags & IFL_ROAM)
- goal.flags |= GFL_ROAM;
- goal.iteminfo = bestitem->iteminfo;
- //if it's a dropped item
- if (bestitem->timeout)
- {
- avoidtime = AVOID_DROPPED_TIME;
- } //end if
- else
- {
- avoidtime = iteminfo->respawntime;
- if (!avoidtime)
- avoidtime = AVOID_DEFAULT_TIME;
- if (avoidtime < AVOID_MINIMUM_TIME)
- avoidtime = AVOID_MINIMUM_TIME;
- } //end else
- //add the chosen goal to the goals to avoid for a while
- BotAddToAvoidGoals(gs, bestitem->number, avoidtime);
- //push the goal on the stack
- BotPushGoal(goalstate, &goal);
- //
- return qtrue;
-} //end of the function BotChooseNBGItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotTouchingGoal(vec3_t origin, bot_goal_t *goal)
-{
- int i;
- vec3_t boxmins, boxmaxs;
- vec3_t absmins, absmaxs;
- vec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10};
- vec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0};
-
- AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs);
- VectorSubtract(goal->mins, boxmaxs, absmins);
- VectorSubtract(goal->maxs, boxmins, absmaxs);
- VectorAdd(absmins, goal->origin, absmins);
- VectorAdd(absmaxs, goal->origin, absmaxs);
- //make the box a little smaller for safety
- VectorSubtract(absmaxs, safety_maxs, absmaxs);
- VectorSubtract(absmins, safety_mins, absmins);
-
- for (i = 0; i < 3; i++)
- {
- if (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse;
- } //end for
- return qtrue;
-} //end of the function BotTouchingGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal)
-{
- aas_entityinfo_t entinfo;
- bsp_trace_t trace;
- vec3_t middle;
-
- if (!(goal->flags & GFL_ITEM)) return qfalse;
- //
- VectorAdd(goal->mins, goal->mins, middle);
- VectorScale(middle, 0.5, middle);
- VectorAdd(goal->origin, middle, middle);
- //
- trace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID);
- //if the goal middle point is visible
- if (trace.fraction >= 1)
- {
- //the goal entity number doesn't have to be valid
- //just assume it's valid
- if (goal->entitynum <= 0)
- return qfalse;
- //
- //if the entity data isn't valid
- AAS_EntityInfo(goal->entitynum, &entinfo);
- //NOTE: for some wacko reason entities are sometimes
- // not updated
- //if (!entinfo.valid) return qtrue;
- if (entinfo.ltime < AAS_Time() - 0.5)
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function BotItemGoalInVisButNotVisible
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetGoalState(int goalstate)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- Com_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t));
- gs->goalstacktop = 0;
- BotResetAvoidGoals(goalstate);
-} //end of the function BotResetGoalState
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadItemWeights(int goalstate, char *filename)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return BLERR_CANNOTLOADITEMWEIGHTS;
- //load the weight configuration
- gs->itemweightconfig = ReadWeightConfig(filename);
- if (!gs->itemweightconfig)
- {
- botimport.Print(PRT_FATAL, "couldn't load weights\n");
- return BLERR_CANNOTLOADITEMWEIGHTS;
- } //end if
- //if there's no item configuration
- if (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS;
- //create the item weight index
- gs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig);
- //everything went ok
- return BLERR_NOERROR;
-} //end of the function BotLoadItemWeights
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeItemWeights(int goalstate)
-{
- bot_goalstate_t *gs;
-
- gs = BotGoalStateFromHandle(goalstate);
- if (!gs) return;
- if (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig);
- if (gs->itemweightindex) FreeMemory(gs->itemweightindex);
-} //end of the function BotFreeItemWeights
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotAllocGoalState(int client)
-{
- int i;
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (!botgoalstates[i])
- {
- botgoalstates[i] = GetClearedMemory(sizeof(bot_goalstate_t));
- botgoalstates[i]->client = client;
- return i;
- } //end if
- } //end for
- return 0;
-} //end of the function BotAllocGoalState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeGoalState(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle);
- return;
- } //end if
- if (!botgoalstates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid goal state handle %d\n", handle);
- return;
- } //end if
- BotFreeItemWeights(handle);
- FreeMemory(botgoalstates[handle]);
- botgoalstates[handle] = NULL;
-} //end of the function BotFreeGoalState
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotSetupGoalAI(void)
-{
- char *filename;
-
- //check if teamplay is on
- g_gametype = LibVarValue("g_gametype", "0");
- //item configuration file
- filename = LibVarString("itemconfig", "items.c");
- //load the item configuration
- itemconfig = LoadItemConfig(filename);
- if (!itemconfig)
- {
- botimport.Print(PRT_FATAL, "couldn't load item config\n");
- return BLERR_CANNOTLOADITEMCONFIG;
- } //end if
- //
- droppedweight = LibVar("droppedweight", "1000");
- //everything went ok
- return BLERR_NOERROR;
-} //end of the function BotSetupGoalAI
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownGoalAI(void)
-{
- int i;
-
- if (itemconfig) FreeMemory(itemconfig);
- itemconfig = NULL;
- if (levelitemheap) FreeMemory(levelitemheap);
- levelitemheap = NULL;
- freelevelitems = NULL;
- levelitems = NULL;
- numlevelitems = 0;
-
- BotFreeInfoEntities();
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (botgoalstates[i])
- {
- BotFreeGoalState(i);
- } //end if
- } //end for
-} //end of the function BotShutdownGoalAI
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_goal.c + * + * desc: goal AI + * + * $Archive: /MissionPack/code/botlib/be_ai_goal.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_utils.h" +#include "l_libvar.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" + +//#define DEBUG_AI_GOAL +#ifdef RANDOMIZE +#define UNDECIDEDFUZZY +#endif //RANDOMIZE +#define DROPPEDWEIGHT +//minimum avoid goal time +#define AVOID_MINIMUM_TIME 10 +//default avoid goal time +#define AVOID_DEFAULT_TIME 30 +//avoid dropped goal time +#define AVOID_DROPPED_TIME 10 +// +#define TRAVELTIME_SCALE 0.01 +//item flags +#define IFL_NOTFREE 1 //not in free for all +#define IFL_NOTTEAM 2 //not in team play +#define IFL_NOTSINGLE 4 //not in single player +#define IFL_NOTBOT 8 //bot should never go for this +#define IFL_ROAM 16 //bot roam goal + +//location in the map "target_location" +typedef struct maplocation_s +{ + vec3_t origin; + int areanum; + char name[MAX_EPAIRKEY]; + struct maplocation_s *next; +} maplocation_t; + +//camp spots "info_camp" +typedef struct campspot_s +{ + vec3_t origin; + int areanum; + char name[MAX_EPAIRKEY]; + float range; + float weight; + float wait; + float random; + struct campspot_s *next; +} campspot_t; + +//FIXME: these are game specific +typedef enum { + GT_FFA, // free for all + GT_TOURNAMENT, // one on one tournament + GT_SINGLE_PLAYER, // single player tournament + + //-- team games go after this -- + + GT_TEAM, // team deathmatch + GT_CTF, // capture the flag +#ifdef MISSIONPACK + GT_1FCTF, + GT_OBELISK, + GT_HARVESTER, +#endif + GT_MAX_GAME_TYPE +} gametype_t; + +typedef struct levelitem_s +{ + int number; //number of the level item + int iteminfo; //index into the item info + int flags; //item flags + float weight; //fixed roam weight + vec3_t origin; //origin of the item + int goalareanum; //area the item is in + vec3_t goalorigin; //goal origin within the area + int entitynum; //entity number + float timeout; //item is removed after this time + struct levelitem_s *prev, *next; +} levelitem_t; + +typedef struct iteminfo_s +{ + char classname[32]; //classname of the item + char name[MAX_STRINGFIELD]; //name of the item + char model[MAX_STRINGFIELD]; //model of the item + int modelindex; //model index + int type; //item type + int index; //index in the inventory + float respawntime; //respawn time + vec3_t mins; //mins of the item + vec3_t maxs; //maxs of the item + int number; //number of the item info +} iteminfo_t; + +#define ITEMINFO_OFS(x) (int)&(((iteminfo_t *)0)->x) + +fielddef_t iteminfo_fields[] = +{ +{"name", ITEMINFO_OFS(name), FT_STRING}, +{"model", ITEMINFO_OFS(model), FT_STRING}, +{"modelindex", ITEMINFO_OFS(modelindex), FT_INT}, +{"type", ITEMINFO_OFS(type), FT_INT}, +{"index", ITEMINFO_OFS(index), FT_INT}, +{"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT}, +{"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3}, +{"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3}, +{0, 0, 0} +}; + +structdef_t iteminfo_struct = +{ + sizeof(iteminfo_t), iteminfo_fields +}; + +typedef struct itemconfig_s +{ + int numiteminfo; + iteminfo_t *iteminfo; +} itemconfig_t; + +//goal state +typedef struct bot_goalstate_s +{ + struct weightconfig_s *itemweightconfig; //weight config + int *itemweightindex; //index from item to weight + // + int client; //client using this goal state + int lastreachabilityarea; //last area with reachabilities the bot was in + // + bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack + int goalstacktop; //the top of the goal stack + // + int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid + float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals +} bot_goalstate_t; + +bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // bk001206 - FIXME: init? +//item configuration +itemconfig_t *itemconfig = NULL; // bk001206 - init +//level items +levelitem_t *levelitemheap = NULL; // bk001206 - init +levelitem_t *freelevelitems = NULL; // bk001206 - init +levelitem_t *levelitems = NULL; // bk001206 - init +int numlevelitems = 0; +//map locations +maplocation_t *maplocations = NULL; // bk001206 - init +//camp spots +campspot_t *campspots = NULL; // bk001206 - init +//the game type +int g_gametype = 0; // bk001206 - init +//additional dropped item weight +libvar_t *droppedweight = NULL; // bk001206 - init + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_goalstate_t *BotGoalStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botgoalstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle); + return NULL; + } //end if + return botgoalstates[handle]; +} //end of the function BotGoalStateFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child) +{ + bot_goalstate_t *p1, *p2, *c; + + p1 = BotGoalStateFromHandle(parent1); + p2 = BotGoalStateFromHandle(parent2); + c = BotGoalStateFromHandle(child); + + InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig, + c->itemweightconfig); +} //end of the function BotInterbreedingGoalFuzzyLogic +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSaveGoalFuzzyLogic(int goalstate, char *filename) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + + //WriteWeightConfig(filename, gs->itemweightconfig); +} //end of the function BotSaveGoalFuzzyLogic +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotMutateGoalFuzzyLogic(int goalstate, float range) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + + EvolveWeightConfig(gs->itemweightconfig); +} //end of the function BotMutateGoalFuzzyLogic +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +itemconfig_t *LoadItemConfig(char *filename) +{ + int max_iteminfo; + token_t token; + char path[MAX_PATH]; + source_t *source; + itemconfig_t *ic; + iteminfo_t *ii; + + max_iteminfo = (int) LibVarValue("max_iteminfo", "256"); + if (max_iteminfo < 0) + { + botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo); + max_iteminfo = 256; + LibVarSet( "max_iteminfo", "256" ); + } + + strncpy( path, filename, MAX_PATH ); + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile( path ); + if( !source ) { + botimport.Print( PRT_ERROR, "counldn't load %s\n", path ); + return NULL; + } //end if + //initialize item config + ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) + + max_iteminfo * sizeof(iteminfo_t)); + ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t)); + ic->numiteminfo = 0; + //parse the item config file + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "iteminfo")) + { + if (ic->numiteminfo >= max_iteminfo) + { + SourceError(source, "more than %d item info defined\n", max_iteminfo); + FreeMemory(ic); + FreeSource(source); + return NULL; + } //end if + ii = &ic->iteminfo[ic->numiteminfo]; + Com_Memset(ii, 0, sizeof(iteminfo_t)); + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeMemory(ic); + FreeMemory(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + strncpy(ii->classname, token.string, sizeof(ii->classname)-1); + if (!ReadStructure(source, &iteminfo_struct, (char *) ii)) + { + FreeMemory(ic); + FreeSource(source); + return NULL; + } //end if + ii->number = ic->numiteminfo; + ic->numiteminfo++; + } //end if + else + { + SourceError(source, "unknown definition %s\n", token.string); + FreeMemory(ic); + FreeSource(source); + return NULL; + } //end else + } //end while + FreeSource(source); + // + if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n"); + botimport.Print(PRT_MESSAGE, "loaded %s\n", path); + return ic; +} //end of the function LoadItemConfig +//=========================================================================== +// index to find the weight function of an iteminfo +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic) +{ + int *index, i; + + //initialize item weight index + index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo); + + for (i = 0; i < ic->numiteminfo; i++) + { + index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname); + if (index[i] < 0) + { + Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname); + } //end if + } //end for + return index; +} //end of the function ItemWeightIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void InitLevelItemHeap(void) +{ + int i, max_levelitems; + + if (levelitemheap) FreeMemory(levelitemheap); + + max_levelitems = (int) LibVarValue("max_levelitems", "256"); + levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t)); + + for (i = 0; i < max_levelitems-1; i++) + { + levelitemheap[i].next = &levelitemheap[i + 1]; + } //end for + levelitemheap[max_levelitems-1].next = NULL; + // + freelevelitems = levelitemheap; +} //end of the function InitLevelItemHeap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +levelitem_t *AllocLevelItem(void) +{ + levelitem_t *li; + + li = freelevelitems; + if (!li) + { + botimport.Print(PRT_FATAL, "out of level items\n"); + return NULL; + } //end if + // + freelevelitems = freelevelitems->next; + Com_Memset(li, 0, sizeof(levelitem_t)); + return li; +} //end of the function AllocLevelItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeLevelItem(levelitem_t *li) +{ + li->next = freelevelitems; + freelevelitems = li; +} //end of the function FreeLevelItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddLevelItemToList(levelitem_t *li) +{ + if (levelitems) levelitems->prev = li; + li->prev = NULL; + li->next = levelitems; + levelitems = li; +} //end of the function AddLevelItemToList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveLevelItemFromList(levelitem_t *li) +{ + if (li->prev) li->prev->next = li->next; + else levelitems = li->next; + if (li->next) li->next->prev = li->prev; +} //end of the function RemoveLevelItemFromList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeInfoEntities(void) +{ + maplocation_t *ml, *nextml; + campspot_t *cs, *nextcs; + + for (ml = maplocations; ml; ml = nextml) + { + nextml = ml->next; + FreeMemory(ml); + } //end for + maplocations = NULL; + for (cs = campspots; cs; cs = nextcs) + { + nextcs = cs->next; + FreeMemory(cs); + } //end for + campspots = NULL; +} //end of the function BotFreeInfoEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInitInfoEntities(void) +{ + char classname[MAX_EPAIRKEY]; + maplocation_t *ml; + campspot_t *cs; + int ent, numlocations, numcampspots; + + BotFreeInfoEntities(); + // + numlocations = 0; + numcampspots = 0; + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + + //map locations + if (!strcmp(classname, "target_location")) + { + ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t)); + AAS_VectorForBSPEpairKey(ent, "origin", ml->origin); + AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name)); + ml->areanum = AAS_PointAreaNum(ml->origin); + ml->next = maplocations; + maplocations = ml; + numlocations++; + } //end if + //camp spots + else if (!strcmp(classname, "info_camp")) + { + cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t)); + AAS_VectorForBSPEpairKey(ent, "origin", cs->origin); + //cs->origin[2] += 16; + AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name)); + AAS_FloatForBSPEpairKey(ent, "range", &cs->range); + AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight); + AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait); + AAS_FloatForBSPEpairKey(ent, "random", &cs->random); + cs->areanum = AAS_PointAreaNum(cs->origin); + if (!cs->areanum) + { + botimport.Print(PRT_MESSAGE, "camp spot at %1.1f %1.1f %1.1f in solid\n", cs->origin[0], cs->origin[1], cs->origin[2]); + FreeMemory(cs); + continue; + } //end if + cs->next = campspots; + campspots = cs; + //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW); + numcampspots++; + } //end else if + } //end for + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations); + botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots); + } //end if +} //end of the function BotInitInfoEntities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotInitLevelItems(void) +{ + int i, spawnflags, value; + char classname[MAX_EPAIRKEY]; + vec3_t origin, end; + int ent, goalareanum; + itemconfig_t *ic; + levelitem_t *li; + bsp_trace_t trace; + + //initialize the map locations and camp spots + BotInitInfoEntities(); + + //initialize the level item heap + InitLevelItemHeap(); + levelitems = NULL; + numlevelitems = 0; + // + ic = itemconfig; + if (!ic) return; + + //if there's no AAS file loaded + if (!AAS_Loaded()) return; + + //update the modelindexes of the item info + for (i = 0; i < ic->numiteminfo; i++) + { + //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model); + if (!ic->iteminfo[i].modelindex) + { + Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname); + } //end if + } //end for + + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + // + spawnflags = 0; + AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags); + // + for (i = 0; i < ic->numiteminfo; i++) + { + if (!strcmp(classname, ic->iteminfo[i].classname)) break; + } //end for + if (i >= ic->numiteminfo) + { + Log_Write("entity %s unknown item\r\n", classname); + continue; + } //end if + //get the origin of the item + if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) + { + botimport.Print(PRT_ERROR, "item %s without origin\n", classname); + continue; + } //end else + // + goalareanum = 0; + //if it is a floating item + if (spawnflags & 1) + { + //if the item is not floating in water + if (!(AAS_PointContents(origin) & CONTENTS_WATER)) + { + VectorCopy(origin, end); + end[2] -= 32; + trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + //if the item not near the ground + if (trace.fraction >= 1) + { + //if the item is not reachable from a jumppad + goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs); + Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); + //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum); + if (!goalareanum) continue; + } //end if + } //end if + } //end if + + li = AllocLevelItem(); + if (!li) return; + // + li->number = ++numlevelitems; + li->timeout = 0; + li->entitynum = 0; + // + li->flags = 0; + AAS_IntForBSPEpairKey(ent, "notfree", &value); + if (value) li->flags |= IFL_NOTFREE; + AAS_IntForBSPEpairKey(ent, "notteam", &value); + if (value) li->flags |= IFL_NOTTEAM; + AAS_IntForBSPEpairKey(ent, "notsingle", &value); + if (value) li->flags |= IFL_NOTSINGLE; + AAS_IntForBSPEpairKey(ent, "notbot", &value); + if (value) li->flags |= IFL_NOTBOT; + if (!strcmp(classname, "item_botroam")) + { + li->flags |= IFL_ROAM; + AAS_FloatForBSPEpairKey(ent, "weight", &li->weight); + } //end if + //if not a stationary item + if (!(spawnflags & 1)) + { + if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs)) + { + botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + } //end if + } //end if + //item info of the level item + li->iteminfo = i; + //origin of the item + VectorCopy(origin, li->origin); + // + if (goalareanum) + { + li->goalareanum = goalareanum; + VectorCopy(origin, li->goalorigin); + } //end if + else + { + //get the item goal area and goal origin + li->goalareanum = AAS_BestReachableArea(origin, + ic->iteminfo[i].mins, ic->iteminfo[i].maxs, + li->goalorigin); + if (!li->goalareanum) + { + botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n", + classname, origin[0], origin[1], origin[2]); + } //end if + } //end else + // + AddLevelItemToList(li); + } //end for + botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems); +} //end of the function BotInitLevelItems +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotGoalName(int number, char *name, int size) +{ + levelitem_t *li; + + if (!itemconfig) return; + // + for (li = levelitems; li; li = li->next) + { + if (li->number == number) + { + strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1); + name[size-1] = '\0'; + return; + } //end for + } //end for + strcpy(name, ""); + return; +} //end of the function BotGoalName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetAvoidGoals(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int)); + Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float)); +} //end of the function BotResetAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpAvoidGoals(int goalstate) +{ + int i; + bot_goalstate_t *gs; + char name[32]; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + if (gs->avoidgoaltimes[i] >= AAS_Time()) + { + BotGoalName(gs->avoidgoals[i], name, 32); + Log_Write("avoid goal %s, number %d for %f seconds", name, + gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time()); + } //end if + } //end for +} //end of the function BotDumpAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime) +{ + int i; + + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + //if the avoid goal is already stored + if (gs->avoidgoals[i] == number) + { + gs->avoidgoals[i] = number; + gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; + return; + } //end if + } //end for + + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + //if this avoid goal has expired + if (gs->avoidgoaltimes[i] < AAS_Time()) + { + gs->avoidgoals[i] = number; + gs->avoidgoaltimes[i] = AAS_Time() + avoidtime; + return; + } //end if + } //end for +} //end of the function BotAddToAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotRemoveFromAvoidGoals(int goalstate, int number) +{ + int i; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + //don't use the goals the bot wants to avoid + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) + { + gs->avoidgoaltimes[i] = 0; + return; + } //end if + } //end for +} //end of the function BotRemoveFromAvoidGoals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float BotAvoidGoalTime(int goalstate, int number) +{ + int i; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return 0; + //don't use the goals the bot wants to avoid + for (i = 0; i < MAX_AVOIDGOALS; i++) + { + if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time()) + { + return gs->avoidgoaltimes[i] - AAS_Time(); + } //end if + } //end for + return 0; +} //end of the function BotAvoidGoalTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime) +{ + bot_goalstate_t *gs; + levelitem_t *li; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) + return; + if (avoidtime < 0) + { + if (!itemconfig) + return; + // + for (li = levelitems; li; li = li->next) + { + if (li->number == number) + { + avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime; + if (!avoidtime) + avoidtime = AVOID_DEFAULT_TIME; + if (avoidtime < AVOID_MINIMUM_TIME) + avoidtime = AVOID_MINIMUM_TIME; + BotAddToAvoidGoals(gs, number, avoidtime); + return; + } //end for + } //end for + return; + } //end if + else + { + BotAddToAvoidGoals(gs, number, avoidtime); + } //end else +} //end of the function BotSetAvoidGoalTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal) +{ + levelitem_t *li; + + if (!itemconfig) return -1; + li = levelitems; + if (index >= 0) + { + for (; li; li = li->next) + { + if (li->number == index) + { + li = li->next; + break; + } //end if + } //end for + } //end for + for (; li; li = li->next) + { + // + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) continue; + } + else { + if (li->flags & IFL_NOTFREE) continue; + } + if (li->flags & IFL_NOTBOT) continue; + // + if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name)) + { + goal->areanum = li->goalareanum; + VectorCopy(li->goalorigin, goal->origin); + goal->entitynum = li->entitynum; + VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins); + VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs); + goal->number = li->number; + goal->flags = GFL_ITEM; + if (li->timeout) goal->flags |= GFL_DROPPED; + //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name); + return li->number; + } //end if + } //end for + return -1; +} //end of the function BotGetLevelItemGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetMapLocationGoal(char *name, bot_goal_t *goal) +{ + maplocation_t *ml; + vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; + + for (ml = maplocations; ml; ml = ml->next) + { + if (!Q_stricmp(ml->name, name)) + { + goal->areanum = ml->areanum; + VectorCopy(ml->origin, goal->origin); + goal->entitynum = 0; + VectorCopy(mins, goal->mins); + VectorCopy(maxs, goal->maxs); + return qtrue; + } //end if + } //end for + return qfalse; +} //end of the function BotGetMapLocationGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetNextCampSpotGoal(int num, bot_goal_t *goal) +{ + int i; + campspot_t *cs; + vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8}; + + if (num < 0) num = 0; + i = num; + for (cs = campspots; cs; cs = cs->next) + { + if (--i < 0) + { + goal->areanum = cs->areanum; + VectorCopy(cs->origin, goal->origin); + goal->entitynum = 0; + VectorCopy(mins, goal->mins); + VectorCopy(maxs, goal->maxs); + return num+1; + } //end if + } //end for + return 0; +} //end of the function BotGetNextCampSpotGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFindEntityForLevelItem(levelitem_t *li) +{ + int ent, modelindex; + itemconfig_t *ic; + aas_entityinfo_t entinfo; + vec3_t dir; + + ic = itemconfig; + if (!itemconfig) return; + for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) + { + //get the model index of the entity + modelindex = AAS_EntityModelindex(ent); + // + if (!modelindex) continue; + //get info about the entity + AAS_EntityInfo(ent, &entinfo); + //if the entity is still moving + if (entinfo.origin[0] != entinfo.lastvisorigin[0] || + entinfo.origin[1] != entinfo.lastvisorigin[1] || + entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; + // + if (ic->iteminfo[li->iteminfo].modelindex == modelindex) + { + //check if the entity is very close + VectorSubtract(li->origin, entinfo.origin, dir); + if (VectorLength(dir) < 30) + { + //found an entity for this level item + li->entitynum = ent; + } //end if + } //end if + } //end for +} //end of the function BotFindEntityForLevelItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +//NOTE: enum entityType_t in bg_public.h +#define ET_ITEM 2 + +void BotUpdateEntityItems(void) +{ + int ent, i, modelindex; + vec3_t dir; + levelitem_t *li, *nextli; + aas_entityinfo_t entinfo; + itemconfig_t *ic; + + //timeout current entity items if necessary + for (li = levelitems; li; li = nextli) + { + nextli = li->next; + //if it is a item that will time out + if (li->timeout) + { + //timeout the item + if (li->timeout < AAS_Time()) + { + RemoveLevelItemFromList(li); + FreeLevelItem(li); + } //end if + } //end if + } //end for + //find new entity items + ic = itemconfig; + if (!itemconfig) return; + // + for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent)) + { + if (AAS_EntityType(ent) != ET_ITEM) continue; + //get the model index of the entity + modelindex = AAS_EntityModelindex(ent); + // + if (!modelindex) continue; + //get info about the entity + AAS_EntityInfo(ent, &entinfo); + //FIXME: don't do this + //skip all floating items for now + //if (entinfo.groundent != ENTITYNUM_WORLD) continue; + //if the entity is still moving + if (entinfo.origin[0] != entinfo.lastvisorigin[0] || + entinfo.origin[1] != entinfo.lastvisorigin[1] || + entinfo.origin[2] != entinfo.lastvisorigin[2]) continue; + //check if the entity is already stored as a level item + for (li = levelitems; li; li = li->next) + { + //if the level item is linked to an entity + if (li->entitynum && li->entitynum == ent) + { + //the entity is re-used if the models are different + if (ic->iteminfo[li->iteminfo].modelindex != modelindex) + { + //remove this level item + RemoveLevelItemFromList(li); + FreeLevelItem(li); + li = NULL; + break; + } //end if + else + { + if (entinfo.origin[0] != li->origin[0] || + entinfo.origin[1] != li->origin[1] || + entinfo.origin[2] != li->origin[2]) + { + VectorCopy(entinfo.origin, li->origin); + //also update the goal area number + li->goalareanum = AAS_BestReachableArea(li->origin, + ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, + li->goalorigin); + } //end if + break; + } //end else + } //end if + } //end for + if (li) continue; + //try to link the entity to a level item + for (li = levelitems; li; li = li->next) + { + //if this level item is already linked + if (li->entitynum) continue; + // + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) continue; + } + else { + if (li->flags & IFL_NOTFREE) continue; + } + //if the model of the level item and the entity are the same + if (ic->iteminfo[li->iteminfo].modelindex == modelindex) + { + //check if the entity is very close + VectorSubtract(li->origin, entinfo.origin, dir); + if (VectorLength(dir) < 30) + { + //found an entity for this level item + li->entitynum = ent; + //if the origin is different + if (entinfo.origin[0] != li->origin[0] || + entinfo.origin[1] != li->origin[1] || + entinfo.origin[2] != li->origin[2]) + { + //update the level item origin + VectorCopy(entinfo.origin, li->origin); + //also update the goal area number + li->goalareanum = AAS_BestReachableArea(li->origin, + ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs, + li->goalorigin); + } //end if +#ifdef DEBUG + Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname); +#endif //DEBUG + break; + } //end if + } //end else + } //end for + if (li) continue; + //check if the model is from a known item + for (i = 0; i < ic->numiteminfo; i++) + { + if (ic->iteminfo[i].modelindex == modelindex) + { + break; + } //end if + } //end for + //if the model is not from a known item + if (i >= ic->numiteminfo) continue; + //allocate a new level item + li = AllocLevelItem(); + // + if (!li) continue; + //entity number of the level item + li->entitynum = ent; + //number for the level item + li->number = numlevelitems + ent; + //set the item info index for the level item + li->iteminfo = i; + //origin of the item + VectorCopy(entinfo.origin, li->origin); + //get the item goal area and goal origin + li->goalareanum = AAS_BestReachableArea(li->origin, + ic->iteminfo[i].mins, ic->iteminfo[i].maxs, + li->goalorigin); + //never go for items dropped into jumppads + if (AAS_AreaJumpPad(li->goalareanum)) + { + FreeLevelItem(li); + continue; + } //end if + //time this item out after 30 seconds + //dropped items disappear after 30 seconds + li->timeout = AAS_Time() + 30; + //add the level item to the list + AddLevelItemToList(li); + //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname); + } //end for + /* + for (li = levelitems; li; li = li->next) + { + if (!li->entitynum) + { + BotFindEntityForLevelItem(li); + } //end if + } //end for*/ +} //end of the function BotUpdateEntityItems +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotDumpGoalStack(int goalstate) +{ + int i; + bot_goalstate_t *gs; + char name[32]; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + for (i = 1; i <= gs->goalstacktop; i++) + { + BotGoalName(gs->goalstack[i].number, name, 32); + Log_Write("%d: %s", i, name); + } //end for +} //end of the function BotDumpGoalStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotPushGoal(int goalstate, bot_goal_t *goal) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + if (gs->goalstacktop >= MAX_GOALSTACK-1) + { + botimport.Print(PRT_ERROR, "goal heap overflow\n"); + BotDumpGoalStack(goalstate); + return; + } //end if + gs->goalstacktop++; + Com_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t)); +} //end of the function BotPushGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotPopGoal(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + if (gs->goalstacktop > 0) gs->goalstacktop--; +} //end of the function BotPopGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotEmptyGoalStack(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + gs->goalstacktop = 0; +} //end of the function BotEmptyGoalStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetTopGoal(int goalstate, bot_goal_t *goal) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return qfalse; + if (!gs->goalstacktop) return qfalse; + Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t)); + return qtrue; +} //end of the function BotGetTopGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetSecondGoal(int goalstate, bot_goal_t *goal) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return qfalse; + if (gs->goalstacktop <= 1) return qfalse; + Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t)); + return qtrue; +} //end of the function BotGetSecondGoal +//=========================================================================== +// pops a new long term goal on the goal stack in the goalstate +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags) +{ + int areanum, t, weightnum; + float weight, bestweight, avoidtime; + iteminfo_t *iteminfo; + itemconfig_t *ic; + levelitem_t *li, *bestitem; + bot_goal_t goal; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) + return qfalse; + if (!gs->itemweightconfig) + return qfalse; + //get the area the bot is in + areanum = BotReachabilityArea(origin, gs->client); + //if the bot is in solid or if the area the bot is in has no reachability links + if (!areanum || !AAS_AreaReachability(areanum)) + { + //use the last valid area the bot was in + areanum = gs->lastreachabilityarea; + } //end if + //remember the last area with reachabilities the bot was in + gs->lastreachabilityarea = areanum; + //if still in solid + if (!areanum) + return qfalse; + //the item configuration + ic = itemconfig; + if (!itemconfig) + return qfalse; + //best weight and item so far + bestweight = 0; + bestitem = NULL; + Com_Memset(&goal, 0, sizeof(bot_goal_t)); + //go through the items in the level + for (li = levelitems; li; li = li->next) + { + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) + continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) + continue; + } + else { + if (li->flags & IFL_NOTFREE) + continue; + } + if (li->flags & IFL_NOTBOT) + continue; + //if the item is not in a possible goal area + if (!li->goalareanum) + continue; + //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) + if (!li->entitynum && !(li->flags & IFL_ROAM)) + continue; + //get the fuzzy weight function for this item + iteminfo = &ic->iteminfo[li->iteminfo]; + weightnum = gs->itemweightindex[iteminfo->number]; + if (weightnum < 0) + continue; + +#ifdef UNDECIDEDFUZZY + weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); +#else + weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); +#endif //UNDECIDEDFUZZY +#ifdef DROPPEDWEIGHT + //HACK: to make dropped items more attractive + if (li->timeout) + weight += droppedweight->value; +#endif //DROPPEDWEIGHT + //use weight scale for item_botroam + if (li->flags & IFL_ROAM) weight *= li->weight; + // + if (weight > 0) + { + //get the travel time towards the goal area + t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); + //if the goal is reachable + if (t > 0) + { + //if this item won't respawn before we get there + avoidtime = BotAvoidGoalTime(goalstate, li->number); + if (avoidtime - t * 0.009 > 0) + continue; + // + weight /= (float) t * TRAVELTIME_SCALE; + // + if (weight > bestweight) + { + bestweight = weight; + bestitem = li; + } //end if + } //end if + } //end if + } //end for + //if no goal item found + if (!bestitem) + { + /* + //if not in lava or slime + if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum)) + { + if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin)) + { + VectorSet(goal.mins, -15, -15, -15); + VectorSet(goal.maxs, 15, 15, 15); + goal.entitynum = 0; + goal.number = 0; + goal.flags = GFL_ROAM; + goal.iteminfo = 0; + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "chosen roam goal area %d\n", goal.areanum); +#endif //DEBUG + return qtrue; + } //end if + } //end if + */ + return qfalse; + } //end if + //create a bot goal for this item + iteminfo = &ic->iteminfo[bestitem->iteminfo]; + VectorCopy(bestitem->goalorigin, goal.origin); + VectorCopy(iteminfo->mins, goal.mins); + VectorCopy(iteminfo->maxs, goal.maxs); + goal.areanum = bestitem->goalareanum; + goal.entitynum = bestitem->entitynum; + goal.number = bestitem->number; + goal.flags = GFL_ITEM; + if (bestitem->timeout) + goal.flags |= GFL_DROPPED; + if (bestitem->flags & IFL_ROAM) + goal.flags |= GFL_ROAM; + goal.iteminfo = bestitem->iteminfo; + //if it's a dropped item + if (bestitem->timeout) + { + avoidtime = AVOID_DROPPED_TIME; + } //end if + else + { + avoidtime = iteminfo->respawntime; + if (!avoidtime) + avoidtime = AVOID_DEFAULT_TIME; + if (avoidtime < AVOID_MINIMUM_TIME) + avoidtime = AVOID_MINIMUM_TIME; + } //end else + //add the chosen goal to the goals to avoid for a while + BotAddToAvoidGoals(gs, bestitem->number, avoidtime); + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // + return qtrue; +} //end of the function BotChooseLTGItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags, + bot_goal_t *ltg, float maxtime) +{ + int areanum, t, weightnum, ltg_time; + float weight, bestweight, avoidtime; + iteminfo_t *iteminfo; + itemconfig_t *ic; + levelitem_t *li, *bestitem; + bot_goal_t goal; + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) + return qfalse; + if (!gs->itemweightconfig) + return qfalse; + //get the area the bot is in + areanum = BotReachabilityArea(origin, gs->client); + //if the bot is in solid or if the area the bot is in has no reachability links + if (!areanum || !AAS_AreaReachability(areanum)) + { + //use the last valid area the bot was in + areanum = gs->lastreachabilityarea; + } //end if + //remember the last area with reachabilities the bot was in + gs->lastreachabilityarea = areanum; + //if still in solid + if (!areanum) + return qfalse; + // + if (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags); + else ltg_time = 99999; + //the item configuration + ic = itemconfig; + if (!itemconfig) + return qfalse; + //best weight and item so far + bestweight = 0; + bestitem = NULL; + Com_Memset(&goal, 0, sizeof(bot_goal_t)); + //go through the items in the level + for (li = levelitems; li; li = li->next) + { + if (g_gametype == GT_SINGLE_PLAYER) { + if (li->flags & IFL_NOTSINGLE) + continue; + } + else if (g_gametype >= GT_TEAM) { + if (li->flags & IFL_NOTTEAM) + continue; + } + else { + if (li->flags & IFL_NOTFREE) + continue; + } + if (li->flags & IFL_NOTBOT) + continue; + //if the item is in a possible goal area + if (!li->goalareanum) + continue; + //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) + if (!li->entitynum && !(li->flags & IFL_ROAM)) + continue; + //get the fuzzy weight function for this item + iteminfo = &ic->iteminfo[li->iteminfo]; + weightnum = gs->itemweightindex[iteminfo->number]; + if (weightnum < 0) + continue; + // +#ifdef UNDECIDEDFUZZY + weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum); +#else + weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum); +#endif //UNDECIDEDFUZZY +#ifdef DROPPEDWEIGHT + //HACK: to make dropped items more attractive + if (li->timeout) + weight += droppedweight->value; +#endif //DROPPEDWEIGHT + //use weight scale for item_botroam + if (li->flags & IFL_ROAM) weight *= li->weight; + // + if (weight > 0) + { + //get the travel time towards the goal area + t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags); + //if the goal is reachable + if (t > 0 && t < maxtime) + { + //if this item won't respawn before we get there + avoidtime = BotAvoidGoalTime(goalstate, li->number); + if (avoidtime - t * 0.009 > 0) + continue; + // + weight /= (float) t * TRAVELTIME_SCALE; + // + if (weight > bestweight) + { + t = 0; + if (ltg && !li->timeout) + { + //get the travel time from the goal to the long term goal + t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags); + } //end if + //if the travel back is possible and doesn't take too long + if (t <= ltg_time) + { + bestweight = weight; + bestitem = li; + } //end if + } //end if + } //end if + } //end if + } //end for + //if no goal item found + if (!bestitem) + return qfalse; + //create a bot goal for this item + iteminfo = &ic->iteminfo[bestitem->iteminfo]; + VectorCopy(bestitem->goalorigin, goal.origin); + VectorCopy(iteminfo->mins, goal.mins); + VectorCopy(iteminfo->maxs, goal.maxs); + goal.areanum = bestitem->goalareanum; + goal.entitynum = bestitem->entitynum; + goal.number = bestitem->number; + goal.flags = GFL_ITEM; + if (bestitem->timeout) + goal.flags |= GFL_DROPPED; + if (bestitem->flags & IFL_ROAM) + goal.flags |= GFL_ROAM; + goal.iteminfo = bestitem->iteminfo; + //if it's a dropped item + if (bestitem->timeout) + { + avoidtime = AVOID_DROPPED_TIME; + } //end if + else + { + avoidtime = iteminfo->respawntime; + if (!avoidtime) + avoidtime = AVOID_DEFAULT_TIME; + if (avoidtime < AVOID_MINIMUM_TIME) + avoidtime = AVOID_MINIMUM_TIME; + } //end else + //add the chosen goal to the goals to avoid for a while + BotAddToAvoidGoals(gs, bestitem->number, avoidtime); + //push the goal on the stack + BotPushGoal(goalstate, &goal); + // + return qtrue; +} //end of the function BotChooseNBGItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotTouchingGoal(vec3_t origin, bot_goal_t *goal) +{ + int i; + vec3_t boxmins, boxmaxs; + vec3_t absmins, absmaxs; + vec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10}; + vec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0}; + + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs); + VectorSubtract(goal->mins, boxmaxs, absmins); + VectorSubtract(goal->maxs, boxmins, absmaxs); + VectorAdd(absmins, goal->origin, absmins); + VectorAdd(absmaxs, goal->origin, absmaxs); + //make the box a little smaller for safety + VectorSubtract(absmaxs, safety_maxs, absmaxs); + VectorSubtract(absmins, safety_mins, absmins); + + for (i = 0; i < 3; i++) + { + if (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse; + } //end for + return qtrue; +} //end of the function BotTouchingGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal) +{ + aas_entityinfo_t entinfo; + bsp_trace_t trace; + vec3_t middle; + + if (!(goal->flags & GFL_ITEM)) return qfalse; + // + VectorAdd(goal->mins, goal->mins, middle); + VectorScale(middle, 0.5, middle); + VectorAdd(goal->origin, middle, middle); + // + trace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID); + //if the goal middle point is visible + if (trace.fraction >= 1) + { + //the goal entity number doesn't have to be valid + //just assume it's valid + if (goal->entitynum <= 0) + return qfalse; + // + //if the entity data isn't valid + AAS_EntityInfo(goal->entitynum, &entinfo); + //NOTE: for some wacko reason entities are sometimes + // not updated + //if (!entinfo.valid) return qtrue; + if (entinfo.ltime < AAS_Time() - 0.5) + return qtrue; + } //end if + return qfalse; +} //end of the function BotItemGoalInVisButNotVisible +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetGoalState(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + Com_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t)); + gs->goalstacktop = 0; + BotResetAvoidGoals(goalstate); +} //end of the function BotResetGoalState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadItemWeights(int goalstate, char *filename) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return BLERR_CANNOTLOADITEMWEIGHTS; + //load the weight configuration + gs->itemweightconfig = ReadWeightConfig(filename); + if (!gs->itemweightconfig) + { + botimport.Print(PRT_FATAL, "couldn't load weights\n"); + return BLERR_CANNOTLOADITEMWEIGHTS; + } //end if + //if there's no item configuration + if (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS; + //create the item weight index + gs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig); + //everything went ok + return BLERR_NOERROR; +} //end of the function BotLoadItemWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeItemWeights(int goalstate) +{ + bot_goalstate_t *gs; + + gs = BotGoalStateFromHandle(goalstate); + if (!gs) return; + if (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig); + if (gs->itemweightindex) FreeMemory(gs->itemweightindex); +} //end of the function BotFreeItemWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAllocGoalState(int client) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botgoalstates[i]) + { + botgoalstates[i] = GetClearedMemory(sizeof(bot_goalstate_t)); + botgoalstates[i]->client = client; + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocGoalState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeGoalState(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle); + return; + } //end if + if (!botgoalstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid goal state handle %d\n", handle); + return; + } //end if + BotFreeItemWeights(handle); + FreeMemory(botgoalstates[handle]); + botgoalstates[handle] = NULL; +} //end of the function BotFreeGoalState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupGoalAI(void) +{ + char *filename; + + //check if teamplay is on + g_gametype = LibVarValue("g_gametype", "0"); + //item configuration file + filename = LibVarString("itemconfig", "items.c"); + //load the item configuration + itemconfig = LoadItemConfig(filename); + if (!itemconfig) + { + botimport.Print(PRT_FATAL, "couldn't load item config\n"); + return BLERR_CANNOTLOADITEMCONFIG; + } //end if + // + droppedweight = LibVar("droppedweight", "1000"); + //everything went ok + return BLERR_NOERROR; +} //end of the function BotSetupGoalAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownGoalAI(void) +{ + int i; + + if (itemconfig) FreeMemory(itemconfig); + itemconfig = NULL; + if (levelitemheap) FreeMemory(levelitemheap); + levelitemheap = NULL; + freelevelitems = NULL; + levelitems = NULL; + numlevelitems = 0; + + BotFreeInfoEntities(); + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (botgoalstates[i]) + { + BotFreeGoalState(i); + } //end if + } //end for +} //end of the function BotShutdownGoalAI diff --git a/code/botlib/be_ai_move.c b/code/botlib/be_ai_move.c index 210d4d9..1f3b8ee 100755 --- a/code/botlib/be_ai_move.c +++ b/code/botlib/be_ai_move.c @@ -1,3610 +1,3610 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_move.c
- *
- * desc: bot movement AI
- *
- * $Archive: /MissionPack/code/botlib/be_ai_move.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_libvar.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-
-#include "../game/be_ea.h"
-#include "../game/be_ai_goal.h"
-#include "../game/be_ai_move.h"
-
-
-//#define DEBUG_AI_MOVE
-//#define DEBUG_ELEVATOR
-//#define DEBUG_GRAPPLE
-
-// bk001204 - redundant bot_avoidspot_t, see ../game/be_ai_move.h
-
-//movement state
-//NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and
-// MFL_GRAPPLEPULL must be set outside the movement code
-typedef struct bot_movestate_s
-{
- //input vars (all set outside the movement code)
- vec3_t origin; //origin of the bot
- vec3_t velocity; //velocity of the bot
- vec3_t viewoffset; //view offset
- int entitynum; //entity number of the bot
- int client; //client number of the bot
- float thinktime; //time the bot thinks
- int presencetype; //presencetype of the bot
- vec3_t viewangles; //view angles of the bot
- //state vars
- int areanum; //area the bot is in
- int lastareanum; //last area the bot was in
- int lastgoalareanum; //last goal area number
- int lastreachnum; //last reachability number
- vec3_t lastorigin; //origin previous cycle
- int reachareanum; //area number of the reachabilty
- int moveflags; //movement flags
- int jumpreach; //set when jumped
- float grapplevisible_time; //last time the grapple was visible
- float lastgrappledist; //last distance to the grapple end
- float reachability_time; //time to use current reachability
- int avoidreach[MAX_AVOIDREACH]; //reachabilities to avoid
- float avoidreachtimes[MAX_AVOIDREACH]; //times to avoid the reachabilities
- int avoidreachtries[MAX_AVOIDREACH]; //number of tries before avoiding
- //
- bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid
- int numavoidspots;
-} bot_movestate_t;
-
-//used to avoid reachability links for some time after being used
-#define AVOIDREACH
-#define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use
-#define AVOIDREACH_TRIES 4
-//prediction times
-#define PREDICTIONTIME_JUMP 3 //in seconds
-#define PREDICTIONTIME_MOVE 2 //in seconds
-//weapon indexes for weapon jumping
-#define WEAPONINDEX_ROCKET_LAUNCHER 5
-#define WEAPONINDEX_BFG 9
-
-#define MODELTYPE_FUNC_PLAT 1
-#define MODELTYPE_FUNC_BOB 2
-#define MODELTYPE_FUNC_DOOR 3
-#define MODELTYPE_FUNC_STATIC 4
-
-libvar_t *sv_maxstep;
-libvar_t *sv_maxbarrier;
-libvar_t *sv_gravity;
-libvar_t *weapindex_rocketlauncher;
-libvar_t *weapindex_bfg10k;
-libvar_t *weapindex_grapple;
-libvar_t *entitytypemissile;
-libvar_t *offhandgrapple;
-libvar_t *cmd_grappleoff;
-libvar_t *cmd_grappleon;
-//type of model, func_plat or func_bobbing
-int modeltypes[MAX_MODELS];
-
-bot_movestate_t *botmovestates[MAX_CLIENTS+1];
-
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-int BotAllocMoveState(void)
-{
- int i;
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (!botmovestates[i])
- {
- botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t));
- return i;
- } //end if
- } //end for
- return 0;
-} //end of the function BotAllocMoveState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeMoveState(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
- return;
- } //end if
- if (!botmovestates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
- return;
- } //end if
- FreeMemory(botmovestates[handle]);
- botmovestates[handle] = NULL;
-} //end of the function BotFreeMoveState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-bot_movestate_t *BotMoveStateFromHandle(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
- return NULL;
- } //end if
- if (!botmovestates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
- return NULL;
- } //end if
- return botmovestates[handle];
-} //end of the function BotMoveStateFromHandle
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotInitMoveState(int handle, bot_initmove_t *initmove)
-{
- bot_movestate_t *ms;
-
- ms = BotMoveStateFromHandle(handle);
- if (!ms) return;
- VectorCopy(initmove->origin, ms->origin);
- VectorCopy(initmove->velocity, ms->velocity);
- VectorCopy(initmove->viewoffset, ms->viewoffset);
- ms->entitynum = initmove->entitynum;
- ms->client = initmove->client;
- ms->thinktime = initmove->thinktime;
- ms->presencetype = initmove->presencetype;
- VectorCopy(initmove->viewangles, ms->viewangles);
- //
- ms->moveflags &= ~MFL_ONGROUND;
- if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND;
- ms->moveflags &= ~MFL_TELEPORTED;
- if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED;
- ms->moveflags &= ~MFL_WATERJUMP;
- if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP;
- ms->moveflags &= ~MFL_WALK;
- if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK;
- ms->moveflags &= ~MFL_GRAPPLEPULL;
- if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL;
-} //end of the function BotInitMoveState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-float AngleDiff(float ang1, float ang2)
-{
- float diff;
-
- diff = ang1 - ang2;
- if (ang1 > ang2)
- {
- if (diff > 180.0) diff -= 360.0;
- } //end if
- else
- {
- if (diff < -180.0) diff += 360.0;
- } //end else
- return diff;
-} //end of the function AngleDiff
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotFuzzyPointReachabilityArea(vec3_t origin)
-{
- int firstareanum, j, x, y, z;
- int areas[10], numareas, areanum, bestareanum;
- float dist, bestdist;
- vec3_t points[10], v, end;
-
- firstareanum = 0;
- areanum = AAS_PointAreaNum(origin);
- if (areanum)
- {
- firstareanum = areanum;
- if (AAS_AreaReachability(areanum)) return areanum;
- } //end if
- VectorCopy(origin, end);
- end[2] += 4;
- numareas = AAS_TraceAreas(origin, end, areas, points, 10);
- for (j = 0; j < numareas; j++)
- {
- if (AAS_AreaReachability(areas[j])) return areas[j];
- } //end for
- bestdist = 999999;
- bestareanum = 0;
- for (z = 1; z >= -1; z -= 1)
- {
- for (x = 1; x >= -1; x -= 1)
- {
- for (y = 1; y >= -1; y -= 1)
- {
- VectorCopy(origin, end);
- end[0] += x * 8;
- end[1] += y * 8;
- end[2] += z * 12;
- numareas = AAS_TraceAreas(origin, end, areas, points, 10);
- for (j = 0; j < numareas; j++)
- {
- if (AAS_AreaReachability(areas[j]))
- {
- VectorSubtract(points[j], origin, v);
- dist = VectorLength(v);
- if (dist < bestdist)
- {
- bestareanum = areas[j];
- bestdist = dist;
- } //end if
- } //end if
- if (!firstareanum) firstareanum = areas[j];
- } //end for
- } //end for
- } //end for
- if (bestareanum) return bestareanum;
- } //end for
- return firstareanum;
-} //end of the function BotFuzzyPointReachabilityArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotReachabilityArea(vec3_t origin, int client)
-{
- int modelnum, modeltype, reachnum, areanum;
- aas_reachability_t reach;
- vec3_t org, end, mins, maxs, up = {0, 0, 1};
- bsp_trace_t bsptrace;
- aas_trace_t trace;
-
- //check if the bot is standing on something
- AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
- VectorMA(origin, -3, up, end);
- bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
- if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE)
- {
- //if standing on the world the bot should be in a valid area
- if (bsptrace.ent == ENTITYNUM_WORLD)
- {
- return BotFuzzyPointReachabilityArea(origin);
- } //end if
-
- modelnum = AAS_EntityModelindex(bsptrace.ent);
- modeltype = modeltypes[modelnum];
-
- //if standing on a func_plat or func_bobbing then the bot is assumed to be
- //in the area the reachability points to
- if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB)
- {
- reachnum = AAS_NextModelReachability(0, modelnum);
- if (reachnum)
- {
- AAS_ReachabilityFromNum(reachnum, &reach);
- return reach.areanum;
- } //end if
- } //end else if
-
- //if the bot is swimming the bot should be in a valid area
- if (AAS_Swimming(origin))
- {
- return BotFuzzyPointReachabilityArea(origin);
- } //end if
- //
- areanum = BotFuzzyPointReachabilityArea(origin);
- //if the bot is in an area with reachabilities
- if (areanum && AAS_AreaReachability(areanum)) return areanum;
- //trace down till the ground is hit because the bot is standing on some other entity
- VectorCopy(origin, org);
- VectorCopy(org, end);
- end[2] -= 800;
- trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1);
- if (!trace.startsolid)
- {
- VectorCopy(trace.endpos, org);
- } //end if
- //
- return BotFuzzyPointReachabilityArea(org);
- } //end if
- //
- return BotFuzzyPointReachabilityArea(origin);
-} //end of the function BotReachabilityArea
-//===========================================================================
-// returns the reachability area the bot is in
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-/*
-int BotReachabilityArea(vec3_t origin, int testground)
-{
- int firstareanum, i, j, x, y, z;
- int areas[10], numareas, areanum, bestareanum;
- float dist, bestdist;
- vec3_t org, end, points[10], v;
- aas_trace_t trace;
-
- firstareanum = 0;
- for (i = 0; i < 2; i++)
- {
- VectorCopy(origin, org);
- //if test at the ground (used when bot is standing on an entity)
- if (i > 0)
- {
- VectorCopy(origin, end);
- end[2] -= 800;
- trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);
- if (!trace.startsolid)
- {
- VectorCopy(trace.endpos, org);
- } //end if
- } //end if
-
- firstareanum = 0;
- areanum = AAS_PointAreaNum(org);
- if (areanum)
- {
- firstareanum = areanum;
- if (AAS_AreaReachability(areanum)) return areanum;
- } //end if
- bestdist = 999999;
- bestareanum = 0;
- for (z = 1; z >= -1; z -= 1)
- {
- for (x = 1; x >= -1; x -= 1)
- {
- for (y = 1; y >= -1; y -= 1)
- {
- VectorCopy(org, end);
- end[0] += x * 8;
- end[1] += y * 8;
- end[2] += z * 12;
- numareas = AAS_TraceAreas(org, end, areas, points, 10);
- for (j = 0; j < numareas; j++)
- {
- if (AAS_AreaReachability(areas[j]))
- {
- VectorSubtract(points[j], org, v);
- dist = VectorLength(v);
- if (dist < bestdist)
- {
- bestareanum = areas[j];
- bestdist = dist;
- } //end if
- } //end if
- } //end for
- } //end for
- } //end for
- if (bestareanum) return bestareanum;
- } //end for
- if (!testground) break;
- } //end for
-//#ifdef DEBUG
- //botimport.Print(PRT_MESSAGE, "no reachability area\n");
-//#endif //DEBUG
- return firstareanum;
-} //end of the function BotReachabilityArea*/
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach)
-{
- int i, modelnum;
- vec3_t mins, maxs, modelorigin, org, end;
- vec3_t angles = {0, 0, 0};
- vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8};
- bsp_trace_t trace;
-
- modelnum = reach->facenum & 0x0000FFFF;
- //get some bsp model info
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
- //
- if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin))
- {
- botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
- return qfalse;
- } //end if
- //
- for (i = 0; i < 2; i++)
- {
- if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse;
- if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse;
- } //end for
- //
- VectorCopy(origin, org);
- org[2] += 24;
- VectorCopy(origin, end);
- end[2] -= 48;
- //
- trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
- if (!trace.startsolid && !trace.allsolid)
- {
- //NOTE: the reachability face number is the model number of the elevator
- if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum)
- {
- return qtrue;
- } //end if
- } //end if
- return qfalse;
-} //end of the function BotOnMover
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int MoverDown(aas_reachability_t *reach)
-{
- int modelnum;
- vec3_t mins, maxs, origin;
- vec3_t angles = {0, 0, 0};
-
- modelnum = reach->facenum & 0x0000FFFF;
- //get some bsp model info
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
- //
- if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
- {
- botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
- return qfalse;
- } //end if
- //if the top of the plat is below the reachability start point
- if (origin[2] + maxs[2] < reach->start[2]) return qtrue;
- return qfalse;
-} //end of the function MoverDown
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotSetBrushModelTypes(void)
-{
- int ent, modelnum;
- char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
-
- Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int));
- //
- for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
- {
- if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
- if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue;
- if (model[0]) modelnum = atoi(model+1);
- else modelnum = 0;
-
- if (modelnum < 0 || modelnum > MAX_MODELS)
- {
- botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname);
- continue;
- } //end if
-
- if (!Q_stricmp(classname, "func_bobbing"))
- modeltypes[modelnum] = MODELTYPE_FUNC_BOB;
- else if (!Q_stricmp(classname, "func_plat"))
- modeltypes[modelnum] = MODELTYPE_FUNC_PLAT;
- else if (!Q_stricmp(classname, "func_door"))
- modeltypes[modelnum] = MODELTYPE_FUNC_DOOR;
- else if (!Q_stricmp(classname, "func_static"))
- modeltypes[modelnum] = MODELTYPE_FUNC_STATIC;
- } //end for
-} //end of the function BotSetBrushModelTypes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotOnTopOfEntity(bot_movestate_t *ms)
-{
- vec3_t mins, maxs, end, up = {0, 0, 1};
- bsp_trace_t trace;
-
- AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
- VectorMA(ms->origin, -3, up, end);
- trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
- if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
- {
- return trace.ent;
- } //end if
- return -1;
-} //end of the function BotOnTopOfEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags)
-{
- //if the reachability uses an unwanted travel type
- if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse;
- //don't go into areas with bad travel types
- if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse;
- return qtrue;
-} //end of the function BotValidTravel
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime)
-{
- int i;
-
- for (i = 0; i < MAX_AVOIDREACH; i++)
- {
- if (ms->avoidreach[i] == number)
- {
- if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++;
- else ms->avoidreachtries[i] = 1;
- ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
- return;
- } //end if
- } //end for
- //add the reachability to the reachabilities to avoid for a while
- for (i = 0; i < MAX_AVOIDREACH; i++)
- {
- if (ms->avoidreachtimes[i] < AAS_Time())
- {
- ms->avoidreach[i] = number;
- ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
- ms->avoidreachtries[i] = 1;
- return;
- } //end if
- } //end for
-} //end of the function BotAddToAvoidReach
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2)
-{
- vec3_t proj, dir;
- int j;
-
- AAS_ProjectPointOntoVector(p, lp1, lp2, proj);
- for (j = 0; j < 3; j++)
- if ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||
- (proj[j] < lp1[j] && proj[j] < lp2[j]))
- break;
- if (j < 3) {
- if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
- VectorSubtract(p, lp1, dir);
- else
- VectorSubtract(p, lp2, dir);
- return VectorLengthSquared(dir);
- }
- VectorSubtract(p, proj, dir);
- return VectorLengthSquared(dir);
-} //end of the function DistanceFromLineSquared
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float VectorDistanceSquared(vec3_t p1, vec3_t p2)
-{
- vec3_t dir;
- VectorSubtract(p2, p1, dir);
- return VectorLengthSquared(dir);
-} //end of the function VectorDistanceSquared
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots)
-{
- int checkbetween, i, type;
- float squareddist, squaredradius;
-
- switch(reach->traveltype & TRAVELTYPE_MASK)
- {
- case TRAVEL_WALK: checkbetween = qtrue; break;
- case TRAVEL_CROUCH: checkbetween = qtrue; break;
- case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break;
- case TRAVEL_LADDER: checkbetween = qtrue; break;
- case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break;
- case TRAVEL_JUMP: checkbetween = qfalse; break;
- case TRAVEL_SWIM: checkbetween = qtrue; break;
- case TRAVEL_WATERJUMP: checkbetween = qtrue; break;
- case TRAVEL_TELEPORT: checkbetween = qfalse; break;
- case TRAVEL_ELEVATOR: checkbetween = qfalse; break;
- case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break;
- case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break;
- case TRAVEL_BFGJUMP: checkbetween = qfalse; break;
- case TRAVEL_JUMPPAD: checkbetween = qfalse; break;
- case TRAVEL_FUNCBOB: checkbetween = qfalse; break;
- default: checkbetween = qtrue; break;
- } //end switch
-
- type = AVOID_CLEAR;
- for (i = 0; i < numavoidspots; i++)
- {
- squaredradius = Square(avoidspots[i].radius);
- squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start);
- // if moving towards the avoid spot
- if (squareddist < squaredradius &&
- VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist)
- {
- type = avoidspots[i].type;
- } //end if
- else if (checkbetween) {
- squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end);
- // if moving towards the avoid spot
- if (squareddist < squaredradius &&
- VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
- {
- type = avoidspots[i].type;
- } //end if
- } //end if
- else
- {
- VectorDistanceSquared(avoidspots[i].origin, reach->end);
- // if the reachability leads closer to the avoid spot
- if (squareddist < squaredradius &&
- VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
- {
- type = avoidspots[i].type;
- } //end if
- } //end else
- if (type == AVOID_ALWAYS)
- return type;
- } //end for
- return type;
-} //end of the function BotAvoidSpots
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type)
-{
- bot_movestate_t *ms;
-
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return;
- if (type == AVOID_CLEAR)
- {
- ms->numavoidspots = 0;
- return;
- } //end if
-
- if (ms->numavoidspots >= MAX_AVOIDSPOTS)
- return;
- VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin);
- ms->avoidspots[ms->numavoidspots].radius = radius;
- ms->avoidspots[ms->numavoidspots].type = type;
- ms->numavoidspots++;
-} //end of the function BotAddAvoidSpot
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotGetReachabilityToGoal(vec3_t origin, int areanum,
- int lastgoalareanum, int lastareanum,
- int *avoidreach, float *avoidreachtimes, int *avoidreachtries,
- bot_goal_t *goal, int travelflags, int movetravelflags,
- struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags)
-{
- int i, t, besttime, bestreachnum, reachnum;
- aas_reachability_t reach;
-
- //if not in a valid area
- if (!areanum) return 0;
- //
- if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum))
- {
- travelflags |= TFL_DONOTENTER;
- movetravelflags |= TFL_DONOTENTER;
- } //end if
- //use the routing to find the next area to go to
- besttime = 0;
- bestreachnum = 0;
- //
- for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum;
- reachnum = AAS_NextAreaReachability(areanum, reachnum))
- {
-#ifdef AVOIDREACH
- //check if it isn't an reachability to avoid
- for (i = 0; i < MAX_AVOIDREACH; i++)
- {
- if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break;
- } //end for
- if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES)
- {
-#ifdef DEBUG
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]);
- } //end if
-#endif //DEBUG
- continue;
- } //end if
-#endif //AVOIDREACH
- //get the reachability from the number
- AAS_ReachabilityFromNum(reachnum, &reach);
- //NOTE: do not go back to the previous area if the goal didn't change
- //NOTE: is this actually avoidance of local routing minima between two areas???
- if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue;
- //if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue;
- //if the travel isn't valid
- if (!BotValidTravel(origin, &reach, movetravelflags)) continue;
- //get the travel time
- t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags);
- //if the goal area isn't reachable from the reachable area
- if (!t) continue;
- //if the bot should not use this reachability to avoid bad spots
- if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) {
- if (flags) {
- *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT;
- }
- continue;
- }
- //add the travel time towards the area
- t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start);
- //if the travel time is better than the ones already found
- if (!besttime || t < besttime)
- {
- besttime = t;
- bestreachnum = reachnum;
- } //end if
- } //end for
- //
- return bestreachnum;
-} //end of the function BotGetReachabilityToGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target)
-{
- vec3_t dir;
- float curdist;
-
- VectorSubtract(end, start, dir);
- curdist = VectorNormalize(dir);
- if (*dist + curdist < maxdist)
- {
- VectorCopy(end, target);
- *dist += curdist;
- return qfalse;
- } //end if
- else
- {
- VectorMA(start, maxdist - *dist, dir, target);
- *dist = maxdist;
- return qtrue;
- } //end else
-} //end of the function BotAddToTarget
-
-int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target)
-{
- aas_reachability_t reach;
- int reachnum, lastareanum;
- bot_movestate_t *ms;
- vec3_t end;
- float dist;
-
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return qfalse;
- reachnum = 0;
- //if the bot has no goal or no last reachability
- if (!ms->lastreachnum || !goal) return qfalse;
-
- reachnum = ms->lastreachnum;
- VectorCopy(ms->origin, end);
- lastareanum = ms->lastareanum;
- dist = 0;
- while(reachnum && dist < lookahead)
- {
- AAS_ReachabilityFromNum(reachnum, &reach);
- if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue;
- //never look beyond teleporters
- if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue;
- //never look beyond the weapon jump point
- if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue;
- if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue;
- //don't add jump pad distances
- if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD &&
- (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR &&
- (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB)
- {
- if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue;
- } //end if
- reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum,
- ms->lastgoalareanum, lastareanum,
- ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
- goal, travelflags, travelflags, NULL, 0, NULL);
- VectorCopy(reach.end, end);
- lastareanum = reach.areanum;
- if (lastareanum == goal->areanum)
- {
- BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target);
- return qtrue;
- } //end if
- } //end while
- //
- return qfalse;
-} //end of the function BotMovementViewTarget
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotVisible(int ent, vec3_t eye, vec3_t target)
-{
- bsp_trace_t trace;
-
- trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
- if (trace.fraction >= 1) return qtrue;
- return qfalse;
-} //end of the function BotVisible
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target)
-{
- aas_reachability_t reach;
- int reachnum, lastgoalareanum, lastareanum, i;
- int avoidreach[MAX_AVOIDREACH];
- float avoidreachtimes[MAX_AVOIDREACH];
- int avoidreachtries[MAX_AVOIDREACH];
- vec3_t end;
-
- //if the bot has no goal or no last reachability
- if (!goal) return qfalse;
- //if the areanum is not valid
- if (!areanum) return qfalse;
- //if the goal areanum is not valid
- if (!goal->areanum) return qfalse;
-
- Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
- lastgoalareanum = goal->areanum;
- lastareanum = areanum;
- VectorCopy(origin, end);
- //only do 20 hops
- for (i = 0; i < 20 && (areanum != goal->areanum); i++)
- {
- //
- reachnum = BotGetReachabilityToGoal(end, areanum,
- lastgoalareanum, lastareanum,
- avoidreach, avoidreachtimes, avoidreachtries,
- goal, travelflags, travelflags, NULL, 0, NULL);
- if (!reachnum) return qfalse;
- AAS_ReachabilityFromNum(reachnum, &reach);
- //
- if (BotVisible(goal->entitynum, goal->origin, reach.start))
- {
- VectorCopy(reach.start, target);
- return qtrue;
- } //end if
- //
- if (BotVisible(goal->entitynum, goal->origin, reach.end))
- {
- VectorCopy(reach.end, target);
- return qtrue;
- } //end if
- //
- if (reach.areanum == goal->areanum)
- {
- VectorCopy(reach.end, target);
- return qtrue;
- } //end if
- //
- lastareanum = areanum;
- areanum = reach.areanum;
- VectorCopy(reach.end, end);
- //
- } //end while
- //
- return qfalse;
-} //end of the function BotPredictVisiblePosition
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter)
-{
- int modelnum;
- vec3_t mins, maxs, origin, mids;
- vec3_t angles = {0, 0, 0};
-
- modelnum = reach->facenum & 0x0000FFFF;
- //get some bsp model info
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
- //
- if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
- {
- botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
- } //end if
- //get a point just above the plat in the bottom position
- VectorAdd(mins, maxs, mids);
- VectorMA(origin, 0.5, mids, bottomcenter);
- bottomcenter[2] = reach->start[2];
-} //end of the function MoverBottomCenter
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum)
-{
- float dist, startz;
- vec3_t start, end;
- aas_trace_t trace;
-
- //do gap checking
- startz = origin[2];
- //this enables walking down stairs more fluidly
- {
- VectorCopy(origin, start);
- VectorCopy(origin, end);
- end[2] -= 60;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
- if (trace.fraction >= 1) return 1;
- startz = trace.endpos[2] + 1;
- }
- //
- for (dist = 8; dist <= 100; dist += 8)
- {
- VectorMA(origin, dist, hordir, start);
- start[2] = startz + 24;
- VectorCopy(start, end);
- end[2] -= 48 + sv_maxbarrier->value;
- trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
- //if solid is found the bot can't walk any further and fall into a gap
- if (!trace.startsolid)
- {
- //if it is a gap
- if (trace.endpos[2] < startz - sv_maxstep->value - 8)
- {
- VectorCopy(trace.endpos, end);
- end[2] -= 20;
- if (AAS_PointContents(end) & CONTENTS_WATER) break;
- //if a gap is found slow down
- //botimport.Print(PRT_MESSAGE, "gap at %f\n", dist);
- return dist;
- } //end if
- startz = trace.endpos[2];
- } //end if
- } //end for
- return 0;
-} //end of the function BotGapDistance
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed)
-{
- vec3_t start, hordir, end;
- aas_trace_t trace;
-
- VectorCopy(ms->origin, end);
- end[2] += sv_maxbarrier->value;
- //trace right up
- trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum);
- //this shouldn't happen... but we check anyway
- if (trace.startsolid) return qfalse;
- //if very low ceiling it isn't possible to jump up to a barrier
- if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
- //
- hordir[0] = dir[0];
- hordir[1] = dir[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end);
- VectorCopy(trace.endpos, start);
- end[2] = trace.endpos[2];
- //trace from previous trace end pos horizontally in the move direction
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
- //again this shouldn't happen
- if (trace.startsolid) return qfalse;
- //
- VectorCopy(trace.endpos, start);
- VectorCopy(trace.endpos, end);
- end[2] = ms->origin[2];
- //trace down from the previous trace end pos
- trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
- //if solid
- if (trace.startsolid) return qfalse;
- //if no obstacle at all
- if (trace.fraction >= 1.0) return qfalse;
- //if less than the maximum step height
- if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
- //
- EA_Jump(ms->client);
- EA_Move(ms->client, hordir, speed);
- ms->moveflags |= MFL_BARRIERJUMP;
- //there is a barrier
- return qtrue;
-} //end of the function BotCheckBarrierJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
-{
- vec3_t normdir;
-
- VectorCopy(dir, normdir);
- VectorNormalize(normdir);
- EA_Move(ms->client, normdir, speed);
- return qtrue;
-} //end of the function BotSwimInDirection
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
-{
- vec3_t hordir, cmdmove, velocity, tmpdir, origin;
- int presencetype, maxframes, cmdframes, stopevent;
- aas_clientmove_t move;
- float dist;
-
- if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
- //if the bot is on the ground
- if (ms->moveflags & MFL_ONGROUND)
- {
- //if there is a barrier the bot can jump on
- if (BotCheckBarrierJump(ms, dir, speed)) return qtrue;
- //remove barrier jump flag
- ms->moveflags &= ~MFL_BARRIERJUMP;
- //get the presence type for the movement
- if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH;
- else presencetype = PRESENCE_NORMAL;
- //horizontal direction
- hordir[0] = dir[0];
- hordir[1] = dir[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //if the bot is not supposed to jump
- if (!(type & MOVE_JUMP))
- {
- //if there is a gap, try to jump over it
- if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP;
- } //end if
- //get command movement
- VectorScale(hordir, speed, cmdmove);
- VectorCopy(ms->velocity, velocity);
- //
- if (type & MOVE_JUMP)
- {
- //botimport.Print(PRT_MESSAGE, "trying jump\n");
- cmdmove[2] = 400;
- maxframes = PREDICTIONTIME_JUMP / 0.1;
- cmdframes = 1;
- stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE|
- SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
- } //end if
- else
- {
- maxframes = 2;
- cmdframes = 2;
- stopevent = SE_HITGROUNDDAMAGE|
- SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
- } //end else
- //AAS_ClearShownDebugLines();
- //
- VectorCopy(ms->origin, origin);
- origin[2] += 0.5;
- AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue,
- velocity, cmdmove, cmdframes, maxframes, 0.1f,
- stopevent, 0, qfalse);//qtrue);
- //if prediction time wasn't enough to fully predict the movement
- if (move.frames >= maxframes && (type & MOVE_JUMP))
- {
- //botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client);
- return qfalse;
- } //end if
- //don't enter slime or lava and don't fall from too high
- if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
- {
- //botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client);
- //if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n");
- //if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n");
- //if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n");
- return qfalse;
- } //end if
- //if ground was hit
- if (move.stopevent & SE_HITGROUND)
- {
- //check for nearby gap
- VectorNormalize2(move.velocity, tmpdir);
- dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum);
- if (dist > 0) return qfalse;
- //
- dist = BotGapDistance(move.endpos, hordir, ms->entitynum);
- if (dist > 0) return qfalse;
- } //end if
- //get horizontal movement
- tmpdir[0] = move.endpos[0] - ms->origin[0];
- tmpdir[1] = move.endpos[1] - ms->origin[1];
- tmpdir[2] = 0;
- //
- //AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE);
- //the bot is blocked by something
- if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse;
- //perform the movement
- if (type & MOVE_JUMP) EA_Jump(ms->client);
- if (type & MOVE_CROUCH) EA_Crouch(ms->client);
- EA_Move(ms->client, hordir, speed);
- //movement was succesfull
- return qtrue;
- } //end if
- else
- {
- if (ms->moveflags & MFL_BARRIERJUMP)
- {
- //if near the top or going down
- if (ms->velocity[2] < 50)
- {
- EA_Move(ms->client, dir, speed);
- } //end if
- } //end if
- //FIXME: do air control to avoid hazards
- return qtrue;
- } //end else
-} //end of the function BotWalkInDirection
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type)
-{
- bot_movestate_t *ms;
-
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return qfalse;
- //if swimming
- if (AAS_Swimming(ms->origin))
- {
- return BotSwimInDirection(ms, dir, speed, type);
- } //end if
- else
- {
- return BotWalkInDirection(ms, dir, speed, type);
- } //end else
-} //end of the function BotMoveInDirection
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out)
-{
- float x1, dx1, dy1, x2, dx2, dy2, d;
-
- dx1 = p2[0] - p1[0];
- dy1 = p2[1] - p1[1];
- dx2 = p4[0] - p3[0];
- dy2 = p4[1] - p3[1];
-
- d = dy1 * dx2 - dx1 * dy2;
- if (d != 0)
- {
- x1 = p1[1] * dx1 - p1[0] * dy1;
- x2 = p3[1] * dx2 - p3[0] * dy2;
- out[0] = (int) ((dx1 * x2 - dx2 * x1) / d);
- out[1] = (int) ((dy1 * x2 - dy2 * x1) / d);
- return qtrue;
- } //end if
- else
- {
- return qfalse;
- } //end else
-} //end of the function Intersection
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result)
-{
- vec3_t mins, maxs, end, up = {0, 0, 1};
- bsp_trace_t trace;
-
- //test for entities obstructing the bot's path
- AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
- //
- if (fabs(DotProduct(dir, up)) < 0.7)
- {
- mins[2] += sv_maxstep->value; //if the bot can step on
- maxs[2] -= 10; //a little lower to avoid low ceiling
- } //end if
- VectorMA(ms->origin, 3, dir, end);
- trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY);
- //if not started in solid and not hitting the world entity
- if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
- {
- result->blocked = qtrue;
- result->blockentity = trace.ent;
-#ifdef DEBUG
- //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client);
-#endif //DEBUG
- } //end if
- //if not in an area with reachability
- else if (checkbottom && !AAS_AreaReachability(ms->areanum))
- {
- //check if the bot is standing on something
- AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
- VectorMA(ms->origin, -3, up, end);
- trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
- if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
- {
- result->blocked = qtrue;
- result->blockentity = trace.ent;
- result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
-#ifdef DEBUG
- //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client);
-#endif //DEBUG
- } //end if
- } //end else
-} //end of the function BotCheckBlocked
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotClearMoveResult(bot_moveresult_t *moveresult)
-{
- moveresult->failure = qfalse;
- moveresult->type = 0;
- moveresult->blocked = qfalse;
- moveresult->blockentity = 0;
- moveresult->traveltype = 0;
- moveresult->flags = 0;
-} //end of the function BotClearMoveResult
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- float dist, speed;
- vec3_t hordir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //first walk straight to the reachability start
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
- //
- if (dist < 10)
- {
- //walk straight to the reachability end
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- } //end if
- //if going towards a crouch area
- if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL))
- {
- //if pretty close to the reachable area
- if (dist < 20) EA_Crouch(ms->client);
- } //end if
- //
- dist = BotGapDistance(ms->origin, hordir, ms->entitynum);
- //
- if (ms->moveflags & MFL_WALK)
- {
- if (dist > 0) speed = 200 - (180 - 1 * dist);
- else speed = 200;
- EA_Walk(ms->client);
- } //end if
- else
- {
- if (dist > 0) speed = 400 - (360 - 2 * dist);
- else speed = 400;
- } //end else
- //elemantary action move in direction
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_Walk
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir;
- float dist, speed;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //if not on the ground and changed areas... don't walk back!!
- //(doesn't seem to help)
- /*
- ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
- if (ms->areanum == reach->areanum)
- {
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n");
-#endif //DEBUG
- return result;
- } //end if*/
- //go straight to the reachability end
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- if (dist > 100) dist = 100;
- speed = 400 - (400 - 3 * dist);
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotFinishTravel_Walk
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- float speed;
- vec3_t hordir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
- speed = 400;
- //walk straight to reachability end
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
- //elemantary actions
- EA_Crouch(ms->client);
- EA_Move(ms->client, hordir, speed);
- //
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_Crouch
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- float dist, speed;
- vec3_t hordir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //walk straight to reachability start
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
- //if pretty close to the barrier
- if (dist < 9)
- {
- EA_Jump(ms->client);
- } //end if
- else
- {
- if (dist > 60) dist = 60;
- speed = 360 - (360 - 6 * dist);
- EA_Move(ms->client, hordir, speed);
- } //end else
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_BarrierJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- float dist;
- vec3_t hordir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //if near the top or going down
- if (ms->velocity[2] < 250)
- {
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
- //
- EA_Move(ms->client, hordir, 400);
- VectorCopy(hordir, result.movedir);
- } //end if
- //
- return result;
-} //end of the function BotFinishTravel_BarrierJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t dir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //swim straight to reachability end
- VectorSubtract(reach->start, ms->origin, dir);
- VectorNormalize(dir);
- //
- BotCheckBlocked(ms, dir, qtrue, &result);
- //elemantary actions
- EA_Move(ms->client, dir, 400);
- //
- VectorCopy(dir, result.movedir);
- Vector2Angles(dir, result.ideal_viewangles);
- result.flags |= MOVERESULT_SWIMVIEW;
- //
- return result;
-} //end of the function BotTravel_Swim
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t dir, hordir;
- float dist;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //swim straight to reachability end
- VectorSubtract(reach->end, ms->origin, dir);
- VectorCopy(dir, hordir);
- hordir[2] = 0;
- dir[2] += 15 + crandom() * 40;
- //botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]);
- VectorNormalize(dir);
- dist = VectorNormalize(hordir);
- //elemantary actions
- //EA_Move(ms->client, dir, 400);
- EA_MoveForward(ms->client);
- //move up if close to the actual out of water jump spot
- if (dist < 40) EA_MoveUp(ms->client);
- //set the ideal view angles
- Vector2Angles(dir, result.ideal_viewangles);
- result.flags |= MOVERESULT_MOVEMENTVIEW;
- //
- VectorCopy(dir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_WaterJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t dir, pnt;
- float dist;
- bot_moveresult_t result;
-
- //botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n");
- BotClearMoveResult(&result);
- //if waterjumping there's nothing to do
- if (ms->moveflags & MFL_WATERJUMP) return result;
- //if not touching any water anymore don't do anything
- //otherwise the bot sometimes keeps jumping?
- VectorCopy(ms->origin, pnt);
- pnt[2] -= 32; //extra for q2dm4 near red armor/mega health
- if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result;
- //swim straight to reachability end
- VectorSubtract(reach->end, ms->origin, dir);
- dir[0] += crandom() * 10;
- dir[1] += crandom() * 10;
- dir[2] += 70 + crandom() * 10;
- dist = VectorNormalize(dir);
- //elemantary actions
- EA_Move(ms->client, dir, 400);
- //set the ideal view angles
- Vector2Angles(dir, result.ideal_viewangles);
- result.flags |= MOVERESULT_MOVEMENTVIEW;
- //
- VectorCopy(dir, result.movedir);
- //
- return result;
-} //end of the function BotFinishTravel_WaterJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir, dir;
- float dist, speed, reachhordist;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //check if the bot is blocked by anything
- VectorSubtract(reach->start, ms->origin, dir);
- VectorNormalize(dir);
- BotCheckBlocked(ms, dir, qtrue, &result);
- //if the reachability start and end are practially above each other
- VectorSubtract(reach->end, reach->start, dir);
- dir[2] = 0;
- reachhordist = VectorLength(dir);
- //walk straight to the reachability start
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //if pretty close to the start focus on the reachability end
- if (dist < 48)
- {
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //
- if (reachhordist < 20)
- {
- speed = 100;
- } //end if
- else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed))
- {
- speed = 400;
- } //end if
- } //end if
- else
- {
- if (reachhordist < 20)
- {
- if (dist > 64) dist = 64;
- speed = 400 - (256 - 4 * dist);
- } //end if
- else
- {
- speed = 400;
- } //end else
- } //end else
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
- //elemantary action
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_WalkOffLedge
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed)
-{
- vec3_t org, vel;
- float dist;
- int i;
-
- VectorCopy(origin, org);
- VectorScale(velocity, 0.1, vel);
- for (i = 0; i < 50; i++)
- {
- vel[2] -= sv_gravity->value * 0.01;
- //if going down and next position would be below the goal
- if (vel[2] < 0 && org[2] + vel[2] < goal[2])
- {
- VectorScale(vel, (goal[2] - org[2]) / vel[2], vel);
- VectorAdd(org, vel, org);
- VectorSubtract(goal, org, dir);
- dist = VectorNormalize(dir);
- if (dist > 32) dist = 32;
- *speed = 400 - (400 - 13 * dist);
- return qtrue;
- } //end if
- else
- {
- VectorAdd(org, vel, org);
- } //end else
- } //end for
- VectorSet(dir, 0, 0, 0);
- *speed = 400;
- return qfalse;
-} //end of the function BotAirControl
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t dir, hordir, end, v;
- float dist, speed;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
- VectorSubtract(reach->end, ms->origin, dir);
- BotCheckBlocked(ms, dir, qtrue, &result);
- //
- VectorSubtract(reach->end, ms->origin, v);
- v[2] = 0;
- dist = VectorNormalize(v);
- if (dist > 16) VectorMA(reach->end, 16, v, end);
- else VectorCopy(reach->end, end);
- //
- if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed))
- {
- //go straight to the reachability end
- VectorCopy(dir, hordir);
- hordir[2] = 0;
- //
- dist = VectorNormalize(hordir);
- speed = 400;
- } //end if
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotFinishTravel_WalkOffLedge
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-/*
-bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir;
- float dist, gapdist, speed, horspeed, sv_jumpvel;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
- sv_jumpvel = botlibglobals.sv_jumpvel->value;
- //walk straight to the reachability start
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- speed = 350;
- //
- gapdist = BotGapDistance(ms, hordir, ms->entitynum);
- //if pretty close to the start focus on the reachability end
- if (dist < 50 || (gapdist && gapdist < 50))
- {
- //NOTE: using max speed (400) works best
- //if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed))
- //{
- // speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;
- //} //end if
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- VectorNormalize(hordir);
- //elemantary action jump
- EA_Jump(ms->client);
- //
- ms->jumpreach = ms->lastreachnum;
- speed = 600;
- } //end if
- else
- {
- if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed))
- {
- speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;
- } //end if
- } //end else
- //elemantary action
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_Jump*/
-/*
-bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir, dir1, dir2, mins, maxs, start, end;
- float dist1, dist2, speed;
- bot_moveresult_t result;
- bsp_trace_t trace;
-
- BotClearMoveResult(&result);
- //
- hordir[0] = reach->start[0] - reach->end[0];
- hordir[1] = reach->start[1] - reach->end[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //
- VectorCopy(reach->start, start);
- start[2] += 1;
- //minus back the bouding box size plus 16
- VectorMA(reach->start, 80, hordir, end);
- //
- AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs);
- //check for solids
- trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID);
- if (trace.startsolid) VectorCopy(start, trace.endpos);
- //check for a gap
- for (dist1 = 0; dist1 < 80; dist1 += 10)
- {
- VectorMA(start, dist1+10, hordir, end);
- end[2] += 1;
- if (AAS_PointAreaNum(end) != ms->reachareanum) break;
- } //end for
- if (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos);
-// dist1 = BotGapDistance(start, hordir, ms->entitynum);
-// if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos);
- //
- VectorSubtract(ms->origin, reach->start, dir1);
- dir1[2] = 0;
- dist1 = VectorNormalize(dir1);
- VectorSubtract(ms->origin, trace.endpos, dir2);
- dir2[2] = 0;
- dist2 = VectorNormalize(dir2);
- //if just before the reachability start
- if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
- {
- //botimport.Print(PRT_MESSAGE, "between jump start and run to point\n");
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //elemantary action jump
- if (dist1 < 24) EA_Jump(ms->client);
- else if (dist1 < 32) EA_DelayedJump(ms->client);
- EA_Move(ms->client, hordir, 600);
- //
- ms->jumpreach = ms->lastreachnum;
- } //end if
- else
- {
- //botimport.Print(PRT_MESSAGE, "going towards run to point\n");
- hordir[0] = trace.endpos[0] - ms->origin[0];
- hordir[1] = trace.endpos[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //
- if (dist2 > 80) dist2 = 80;
- speed = 400 - (400 - 5 * dist2);
- EA_Move(ms->client, hordir, speed);
- } //end else
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_Jump*/
-//*
-bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir, dir1, dir2, start, end, runstart;
-// vec3_t runstart, dir1, dir2, hordir;
- float dist1, dist2, speed;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
- AAS_JumpReachRunStart(reach, runstart);
- //*
- hordir[0] = runstart[0] - reach->start[0];
- hordir[1] = runstart[1] - reach->start[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //
- VectorCopy(reach->start, start);
- start[2] += 1;
- VectorMA(reach->start, 80, hordir, runstart);
- //check for a gap
- for (dist1 = 0; dist1 < 80; dist1 += 10)
- {
- VectorMA(start, dist1+10, hordir, end);
- end[2] += 1;
- if (AAS_PointAreaNum(end) != ms->reachareanum) break;
- } //end for
- if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart);
- //
- VectorSubtract(ms->origin, reach->start, dir1);
- dir1[2] = 0;
- dist1 = VectorNormalize(dir1);
- VectorSubtract(ms->origin, runstart, dir2);
- dir2[2] = 0;
- dist2 = VectorNormalize(dir2);
- //if just before the reachability start
- if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
- {
-// botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //elemantary action jump
- if (dist1 < 24) EA_Jump(ms->client);
- else if (dist1 < 32) EA_DelayedJump(ms->client);
- EA_Move(ms->client, hordir, 600);
- //
- ms->jumpreach = ms->lastreachnum;
- } //end if
- else
- {
-// botimport.Print(PRT_MESSAGE, "going towards run start point\n");
- hordir[0] = runstart[0] - ms->origin[0];
- hordir[1] = runstart[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //
- if (dist2 > 80) dist2 = 80;
- speed = 400 - (400 - 5 * dist2);
- EA_Move(ms->client, hordir, speed);
- } //end else
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_Jump*/
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir, hordir2;
- float speed, dist;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //if not jumped yet
- if (!ms->jumpreach) return result;
- //go straight to the reachability end
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- hordir2[0] = reach->end[0] - reach->start[0];
- hordir2[1] = reach->end[1] - reach->start[1];
- hordir2[2] = 0;
- VectorNormalize(hordir2);
- //
- if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result;
- //always use max speed when traveling through the air
- speed = 800;
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotFinishTravel_Jump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- //float dist, speed;
- vec3_t dir, viewdir;//, hordir;
- vec3_t origin = {0, 0, 0};
-// vec3_t up = {0, 0, 1};
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
-// if ((ms->moveflags & MFL_AGAINSTLADDER))
- //NOTE: not a good idea for ladders starting in water
- // || !(ms->moveflags & MFL_ONGROUND))
- {
- //botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n");
- VectorSubtract(reach->end, ms->origin, dir);
- VectorNormalize(dir);
- //set the ideal view angles, facing the ladder up or down
- viewdir[0] = dir[0];
- viewdir[1] = dir[1];
- viewdir[2] = 3 * dir[2];
- Vector2Angles(viewdir, result.ideal_viewangles);
- //elemantary action
- EA_Move(ms->client, origin, 0);
- EA_MoveForward(ms->client);
- //set movement view flag so the AI can see the view is focussed
- result.flags |= MOVERESULT_MOVEMENTVIEW;
- } //end if
-/* else
- {
- //botimport.Print(PRT_MESSAGE, "moving towards ladder\n");
- VectorSubtract(reach->end, ms->origin, dir);
- //make sure the horizontal movement is large anough
- VectorCopy(dir, hordir);
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- dir[0] = hordir[0];
- dir[1] = hordir[1];
- if (dir[2] > 0) dir[2] = 1;
- else dir[2] = -1;
- if (dist > 50) dist = 50;
- speed = 400 - (200 - 4 * dist);
- EA_Move(ms->client, dir, speed);
- } //end else*/
- //save the movement direction
- VectorCopy(dir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_Ladder
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir;
- float dist;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //if the bot is being teleported
- if (ms->moveflags & MFL_TELEPORTED) return result;
-
- //walk straight to center of the teleporter
- VectorSubtract(reach->start, ms->origin, hordir);
- if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
-
- if (dist < 30) EA_Move(ms->client, hordir, 200);
- else EA_Move(ms->client, hordir, 400);
-
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
-
- VectorCopy(hordir, result.movedir);
- return result;
-} //end of the function BotTravel_Teleport
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t dir, dir1, dir2, hordir, bottomcenter;
- float dist, dist1, dist2, speed;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //if standing on the plat
- if (BotOnMover(ms->origin, ms->entitynum, reach))
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "bot on elevator\n");
-#endif //DEBUG_ELEVATOR
- //if vertically not too far from the end point
- if (abs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value)
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "bot moving to end\n");
-#endif //DEBUG_ELEVATOR
- //move to the end point
- VectorSubtract(reach->end, ms->origin, hordir);
- hordir[2] = 0;
- VectorNormalize(hordir);
- if (!BotCheckBarrierJump(ms, hordir, 100))
- {
- EA_Move(ms->client, hordir, 400);
- } //end if
- VectorCopy(hordir, result.movedir);
- } //end else
- //if not really close to the center of the elevator
- else
- {
- MoverBottomCenter(reach, bottomcenter);
- VectorSubtract(bottomcenter, ms->origin, hordir);
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- if (dist > 10)
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "bot moving to center\n");
-#endif //DEBUG_ELEVATOR
- //move to the center of the plat
- if (dist > 100) dist = 100;
- speed = 400 - (400 - 4 * dist);
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- } //end if
- } //end else
- } //end if
- else
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "bot not on elevator\n");
-#endif //DEBUG_ELEVATOR
- //if very near the reachability end
- VectorSubtract(reach->end, ms->origin, dir);
- dist = VectorLength(dir);
- if (dist < 64)
- {
- if (dist > 60) dist = 60;
- speed = 360 - (360 - 6 * dist);
- //
- if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
- {
- if (speed > 5) EA_Move(ms->client, dir, speed);
- } //end if
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- //stop using this reachability
- ms->reachability_time = 0;
- return result;
- } //end if
- //get direction and distance to reachability start
- VectorSubtract(reach->start, ms->origin, dir1);
- if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
- dist1 = VectorNormalize(dir1);
- //if the elevator isn't down
- if (!MoverDown(reach))
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "elevator not down\n");
-#endif //DEBUG_ELEVATOR
- dist = dist1;
- VectorCopy(dir1, dir);
- //
- BotCheckBlocked(ms, dir, qfalse, &result);
- //
- if (dist > 60) dist = 60;
- speed = 360 - (360 - 6 * dist);
- //
- if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
- {
- if (speed > 5) EA_Move(ms->client, dir, speed);
- } //end if
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- //this isn't a failure... just wait till the elevator comes down
- result.type = RESULTTYPE_ELEVATORUP;
- result.flags |= MOVERESULT_WAITING;
- return result;
- } //end if
- //get direction and distance to elevator bottom center
- MoverBottomCenter(reach, bottomcenter);
- VectorSubtract(bottomcenter, ms->origin, dir2);
- if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
- dist2 = VectorNormalize(dir2);
- //if very close to the reachability start or
- //closer to the elevator center or
- //between reachability start and elevator center
- if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "bot moving to center\n");
-#endif //DEBUG_ELEVATOR
- dist = dist2;
- VectorCopy(dir2, dir);
- } //end if
- else //closer to the reachability start
- {
-#ifdef DEBUG_ELEVATOR
- botimport.Print(PRT_MESSAGE, "bot moving to start\n");
-#endif //DEBUG_ELEVATOR
- dist = dist1;
- VectorCopy(dir1, dir);
- } //end else
- //
- BotCheckBlocked(ms, dir, qfalse, &result);
- //
- if (dist > 60) dist = 60;
- speed = 400 - (400 - 6 * dist);
- //
- if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
- {
- EA_Move(ms->client, dir, speed);
- } //end if
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- } //end else
- return result;
-} //end of the function BotTravel_Elevator
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t bottomcenter, bottomdir, topdir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
- MoverBottomCenter(reach, bottomcenter);
- VectorSubtract(bottomcenter, ms->origin, bottomdir);
- //
- VectorSubtract(reach->end, ms->origin, topdir);
- //
- if (fabs(bottomdir[2]) < fabs(topdir[2]))
- {
- VectorNormalize(bottomdir);
- EA_Move(ms->client, bottomdir, 300);
- } //end if
- else
- {
- VectorNormalize(topdir);
- EA_Move(ms->client, topdir, 300);
- } //end else
- return result;
-} //end of the function BotFinishTravel_Elevator
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin)
-{
- int spawnflags, modelnum;
- vec3_t mins, maxs, mid, angles = {0, 0, 0};
- int num0, num1;
-
- modelnum = reach->facenum & 0x0000FFFF;
- if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
- {
- botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum);
- VectorSet(start, 0, 0, 0);
- VectorSet(end, 0, 0, 0);
- return;
- } //end if
- AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
- VectorAdd(mins, maxs, mid);
- VectorScale(mid, 0.5, mid);
- VectorCopy(mid, start);
- VectorCopy(mid, end);
- spawnflags = reach->facenum >> 16;
- num0 = reach->edgenum >> 16;
- if (num0 > 0x00007FFF) num0 |= 0xFFFF0000;
- num1 = reach->edgenum & 0x0000FFFF;
- if (num1 > 0x00007FFF) num1 |= 0xFFFF0000;
- if (spawnflags & 1)
- {
- start[0] = num0;
- end[0] = num1;
- //
- origin[0] += mid[0];
- origin[1] = mid[1];
- origin[2] = mid[2];
- } //end if
- else if (spawnflags & 2)
- {
- start[1] = num0;
- end[1] = num1;
- //
- origin[0] = mid[0];
- origin[1] += mid[1];
- origin[2] = mid[2];
- } //end else if
- else
- {
- start[2] = num0;
- end[2] = num1;
- //
- origin[0] = mid[0];
- origin[1] = mid[1];
- origin[2] += mid[2];
- } //end else
-} //end of the function BotFuncBobStartEnd
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin;
- float dist, dist1, dist2, speed;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //
- BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
- //if standing ontop of the func_bobbing
- if (BotOnMover(ms->origin, ms->entitynum, reach))
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n");
-#endif
- //if near end point of reachability
- VectorSubtract(bob_origin, bob_end, dir);
- if (VectorLength(dir) < 24)
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n");
-#endif
- //move to the end point
- VectorSubtract(reach->end, ms->origin, hordir);
- hordir[2] = 0;
- VectorNormalize(hordir);
- if (!BotCheckBarrierJump(ms, hordir, 100))
- {
- EA_Move(ms->client, hordir, 400);
- } //end if
- VectorCopy(hordir, result.movedir);
- } //end else
- //if not really close to the center of the elevator
- else
- {
- MoverBottomCenter(reach, bottomcenter);
- VectorSubtract(bottomcenter, ms->origin, hordir);
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- if (dist > 10)
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
-#endif
- //move to the center of the plat
- if (dist > 100) dist = 100;
- speed = 400 - (400 - 4 * dist);
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- } //end if
- } //end else
- } //end if
- else
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n");
-#endif
- //if very near the reachability end
- VectorSubtract(reach->end, ms->origin, dir);
- dist = VectorLength(dir);
- if (dist < 64)
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot moving to end\n");
-#endif
- if (dist > 60) dist = 60;
- speed = 360 - (360 - 6 * dist);
- //if swimming or no barrier jump
- if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
- {
- if (speed > 5) EA_Move(ms->client, dir, speed);
- } //end if
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- //stop using this reachability
- ms->reachability_time = 0;
- return result;
- } //end if
- //get direction and distance to reachability start
- VectorSubtract(reach->start, ms->origin, dir1);
- if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
- dist1 = VectorNormalize(dir1);
- //if func_bobbing is Not it's start position
- VectorSubtract(bob_origin, bob_start, dir);
- if (VectorLength(dir) > 16)
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n");
-#endif
- dist = dist1;
- VectorCopy(dir1, dir);
- //
- BotCheckBlocked(ms, dir, qfalse, &result);
- //
- if (dist > 60) dist = 60;
- speed = 360 - (360 - 6 * dist);
- //
- if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
- {
- if (speed > 5) EA_Move(ms->client, dir, speed);
- } //end if
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- //this isn't a failure... just wait till the func_bobbing arrives
- result.type = RESULTTYPE_WAITFORFUNCBOBBING;
- result.flags |= MOVERESULT_WAITING;
- return result;
- } //end if
- //get direction and distance to func_bob bottom center
- MoverBottomCenter(reach, bottomcenter);
- VectorSubtract(bottomcenter, ms->origin, dir2);
- if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
- dist2 = VectorNormalize(dir2);
- //if very close to the reachability start or
- //closer to the elevator center or
- //between reachability start and func_bobbing center
- if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
-#endif
- dist = dist2;
- VectorCopy(dir2, dir);
- } //end if
- else //closer to the reachability start
- {
-#ifdef DEBUG_FUNCBOB
- botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n");
-#endif
- dist = dist1;
- VectorCopy(dir1, dir);
- } //end else
- //
- BotCheckBlocked(ms, dir, qfalse, &result);
- //
- if (dist > 60) dist = 60;
- speed = 400 - (400 - 6 * dist);
- //
- if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
- {
- EA_Move(ms->client, dir, speed);
- } //end if
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- } //end else
- return result;
-} //end of the function BotTravel_FuncBobbing
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter;
- bot_moveresult_t result;
- float dist, speed;
-
- BotClearMoveResult(&result);
- //
- BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
- //
- VectorSubtract(bob_origin, bob_end, dir);
- dist = VectorLength(dir);
- //if the func_bobbing is near the end
- if (dist < 16)
- {
- VectorSubtract(reach->end, ms->origin, hordir);
- if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- if (dist > 60) dist = 60;
- speed = 360 - (360 - 6 * dist);
- //
- if (speed > 5) EA_Move(ms->client, dir, speed);
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
- } //end if
- else
- {
- MoverBottomCenter(reach, bottomcenter);
- VectorSubtract(bottomcenter, ms->origin, hordir);
- if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- if (dist > 5)
- {
- //move to the center of the plat
- if (dist > 100) dist = 100;
- speed = 400 - (400 - 4 * dist);
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- } //end if
- } //end else
- return result;
-} //end of the function BotFinishTravel_FuncBobbing
-//===========================================================================
-// 0 no valid grapple hook visible
-// 1 the grapple hook is still flying
-// 2 the grapple hooked into a wall
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- int i;
- aas_entityinfo_t entinfo;
-
- //if the grapple hook is pulling
- if (ms->moveflags & MFL_GRAPPLEPULL)
- return 2;
- //check for a visible grapple missile entity
- //or visible grapple entity
- for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i))
- {
- if (AAS_EntityType(i) == (int) entitytypemissile->value)
- {
- AAS_EntityInfo(i, &entinfo);
- if (entinfo.weapon == (int) weapindex_grapple->value)
- {
- return 1;
- } //end if
- } //end if
- } //end for
- //no valid grapple at all
- return 0;
-} //end of the function GrappleState
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetGrapple(bot_movestate_t *ms)
-{
- aas_reachability_t reach;
-
- AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
- //if not using the grapple hook reachability anymore
- if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK)
- {
- if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time)
- {
- if (offhandgrapple->value)
- EA_Command(ms->client, cmd_grappleoff->string);
- ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
- ms->grapplevisible_time = 0;
-#ifdef DEBUG_GRAPPLE
- botimport.Print(PRT_MESSAGE, "reset grapple\n");
-#endif //DEBUG_GRAPPLE
- } //end if
- } //end if
-} //end of the function BotResetGrapple
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- bot_moveresult_t result;
- float dist, speed;
- vec3_t dir, viewdir, org;
- int state, areanum;
- bsp_trace_t trace;
-
-#ifdef DEBUG_GRAPPLE
- static int debugline;
- if (!debugline) debugline = botimport.DebugLineCreate();
- botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE);
-#endif //DEBUG_GRAPPLE
-
- BotClearMoveResult(&result);
- //
- if (ms->moveflags & MFL_GRAPPLERESET)
- {
- if (offhandgrapple->value)
- EA_Command(ms->client, cmd_grappleoff->string);
- ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
- return result;
- } //end if
- //
- if (!(int) offhandgrapple->value)
- {
- result.weapon = weapindex_grapple->value;
- result.flags |= MOVERESULT_MOVEMENTWEAPON;
- } //end if
- //
- if (ms->moveflags & MFL_ACTIVEGRAPPLE)
- {
-#ifdef DEBUG_GRAPPLE
- botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n");
-#endif //DEBUG_GRAPPLE
- //
- state = GrappleState(ms, reach);
- //
- VectorSubtract(reach->end, ms->origin, dir);
- dir[2] = 0;
- dist = VectorLength(dir);
- //if very close to the grapple end or the grappled is hooked and
- //the bot doesn't get any closer
- if (state && dist < 48)
- {
- if (ms->lastgrappledist - dist < 1)
- {
-#ifdef DEBUG_GRAPPLE
- botimport.Print(PRT_ERROR, "grapple normal end\n");
-#endif //DEBUG_GRAPPLE
- if (offhandgrapple->value)
- EA_Command(ms->client, cmd_grappleoff->string);
- ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
- ms->moveflags |= MFL_GRAPPLERESET;
- ms->reachability_time = 0; //end the reachability
- return result;
- } //end if
- } //end if
- //if no valid grapple at all, or the grapple hooked and the bot
- //isn't moving anymore
- else if (!state || (state == 2 && dist > ms->lastgrappledist - 2))
- {
- if (ms->grapplevisible_time < AAS_Time() - 0.4)
- {
-#ifdef DEBUG_GRAPPLE
- botimport.Print(PRT_ERROR, "grapple not visible\n");
-#endif //DEBUG_GRAPPLE
- if (offhandgrapple->value)
- EA_Command(ms->client, cmd_grappleoff->string);
- ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
- ms->moveflags |= MFL_GRAPPLERESET;
- ms->reachability_time = 0; //end the reachability
- return result;
- } //end if
- } //end if
- else
- {
- ms->grapplevisible_time = AAS_Time();
- } //end else
- //
- if (!(int) offhandgrapple->value)
- {
- EA_Attack(ms->client);
- } //end if
- //remember the current grapple distance
- ms->lastgrappledist = dist;
- } //end if
- else
- {
-#ifdef DEBUG_GRAPPLE
- botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n");
-#endif //DEBUG_GRAPPLE
- //
- ms->grapplevisible_time = AAS_Time();
- //
- VectorSubtract(reach->start, ms->origin, dir);
- if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0;
- VectorAdd(ms->origin, ms->viewoffset, org);
- VectorSubtract(reach->end, org, viewdir);
- //
- dist = VectorNormalize(dir);
- Vector2Angles(viewdir, result.ideal_viewangles);
- result.flags |= MOVERESULT_MOVEMENTVIEW;
- //
- if (dist < 5 &&
- fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 &&
- fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2)
- {
-#ifdef DEBUG_GRAPPLE
- botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n");
-#endif //DEBUG_GRAPPLE
- //check if the grapple missile path is clear
- VectorAdd(ms->origin, ms->viewoffset, org);
- trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID);
- VectorSubtract(reach->end, trace.endpos, dir);
- if (VectorLength(dir) > 16)
- {
- result.failure = qtrue;
- return result;
- } //end if
- //activate the grapple
- if (offhandgrapple->value)
- {
- EA_Command(ms->client, cmd_grappleon->string);
- } //end if
- else
- {
- EA_Attack(ms->client);
- } //end else
- ms->moveflags |= MFL_ACTIVEGRAPPLE;
- ms->lastgrappledist = 999999;
- } //end if
- else
- {
- if (dist < 70) speed = 300 - (300 - 4 * dist);
- else speed = 400;
- //
- BotCheckBlocked(ms, dir, qtrue, &result);
- //elemantary action move in direction
- EA_Move(ms->client, dir, speed);
- VectorCopy(dir, result.movedir);
- } //end else
- //if in another area before actually grappling
- areanum = AAS_PointAreaNum(ms->origin);
- if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0;
- } //end else
- return result;
-} //end of the function BotTravel_Grapple
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir;
- float dist, speed;
- bot_moveresult_t result;
-
- //botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n");
- BotClearMoveResult(&result);
- //
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- //
- dist = VectorNormalize(hordir);
- //look in the movement direction
- Vector2Angles(hordir, result.ideal_viewangles);
- //look straight down
- result.ideal_viewangles[PITCH] = 90;
- //
- if (dist < 5 &&
- fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
- fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
- {
- //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //elemantary action jump
- EA_Jump(ms->client);
- EA_Attack(ms->client);
- EA_Move(ms->client, hordir, 800);
- //
- ms->jumpreach = ms->lastreachnum;
- } //end if
- else
- {
- if (dist > 80) dist = 80;
- speed = 400 - (400 - 5 * dist);
- EA_Move(ms->client, hordir, speed);
- } //end else
- //look in the movement direction
- Vector2Angles(hordir, result.ideal_viewangles);
- //look straight down
- result.ideal_viewangles[PITCH] = 90;
- //set the view angles directly
- EA_View(ms->client, result.ideal_viewangles);
- //view is important for the movment
- result.flags |= MOVERESULT_MOVEMENTVIEWSET;
- //select the rocket launcher
- EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value);
- //weapon is used for movement
- result.weapon = (int) weapindex_rocketlauncher->value;
- result.flags |= MOVERESULT_MOVEMENTWEAPON;
- //
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_RocketJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir;
- float dist, speed;
- bot_moveresult_t result;
-
- //botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n");
- BotClearMoveResult(&result);
- //
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- //
- dist = VectorNormalize(hordir);
- //
- if (dist < 5 &&
- fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
- fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
- {
- //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //elemantary action jump
- EA_Jump(ms->client);
- EA_Attack(ms->client);
- EA_Move(ms->client, hordir, 800);
- //
- ms->jumpreach = ms->lastreachnum;
- } //end if
- else
- {
- if (dist > 80) dist = 80;
- speed = 400 - (400 - 5 * dist);
- EA_Move(ms->client, hordir, speed);
- } //end else
- //look in the movement direction
- Vector2Angles(hordir, result.ideal_viewangles);
- //look straight down
- result.ideal_viewangles[PITCH] = 90;
- //set the view angles directly
- EA_View(ms->client, result.ideal_viewangles);
- //view is important for the movment
- result.flags |= MOVERESULT_MOVEMENTVIEWSET;
- //select the rocket launcher
- EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value);
- //weapon is used for movement
- result.weapon = (int) weapindex_bfg10k->value;
- result.flags |= MOVERESULT_MOVEMENTWEAPON;
- //
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_BFGJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- vec3_t hordir;
- float speed;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //if not jumped yet
- if (!ms->jumpreach) return result;
- /*
- //go straight to the reachability end
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- //always use max speed when traveling through the air
- EA_Move(ms->client, hordir, 800);
- VectorCopy(hordir, result.movedir);
- */
- //
- if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
- {
- //go straight to the reachability end
- VectorSubtract(reach->end, ms->origin, hordir);
- hordir[2] = 0;
- VectorNormalize(hordir);
- speed = 400;
- } //end if
- //
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotFinishTravel_WeaponJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- float dist, speed;
- vec3_t hordir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- //first walk straight to the reachability start
- hordir[0] = reach->start[0] - ms->origin[0];
- hordir[1] = reach->start[1] - ms->origin[1];
- hordir[2] = 0;
- dist = VectorNormalize(hordir);
- //
- BotCheckBlocked(ms, hordir, qtrue, &result);
- speed = 400;
- //elemantary action move in direction
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotTravel_JumpPad
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
-{
- float speed;
- vec3_t hordir;
- bot_moveresult_t result;
-
- BotClearMoveResult(&result);
- if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
- {
- hordir[0] = reach->end[0] - ms->origin[0];
- hordir[1] = reach->end[1] - ms->origin[1];
- hordir[2] = 0;
- VectorNormalize(hordir);
- speed = 400;
- } //end if
- BotCheckBlocked(ms, hordir, qtrue, &result);
- //elemantary action move in direction
- EA_Move(ms->client, hordir, speed);
- VectorCopy(hordir, result.movedir);
- //
- return result;
-} //end of the function BotFinishTravel_JumpPad
-//===========================================================================
-// time before the reachability times out
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotReachabilityTime(aas_reachability_t *reach)
-{
- switch(reach->traveltype & TRAVELTYPE_MASK)
- {
- case TRAVEL_WALK: return 5;
- case TRAVEL_CROUCH: return 5;
- case TRAVEL_BARRIERJUMP: return 5;
- case TRAVEL_LADDER: return 6;
- case TRAVEL_WALKOFFLEDGE: return 5;
- case TRAVEL_JUMP: return 5;
- case TRAVEL_SWIM: return 5;
- case TRAVEL_WATERJUMP: return 5;
- case TRAVEL_TELEPORT: return 5;
- case TRAVEL_ELEVATOR: return 10;
- case TRAVEL_GRAPPLEHOOK: return 8;
- case TRAVEL_ROCKETJUMP: return 6;
- case TRAVEL_BFGJUMP: return 6;
- case TRAVEL_JUMPPAD: return 10;
- case TRAVEL_FUNCBOB: return 10;
- default:
- {
- botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype);
- return 8;
- } //end case
- } //end switch
-} //end of the function BotReachabilityTime
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal)
-{
- bot_moveresult_t result;
- vec3_t dir;
- float dist, speed;
-
-#ifdef DEBUG
- //botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1));
- //AAS_ClearShownDebugLines();
- //AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED);
-#endif //DEBUG
- BotClearMoveResult(&result);
- //walk straight to the goal origin
- dir[0] = goal->origin[0] - ms->origin[0];
- dir[1] = goal->origin[1] - ms->origin[1];
- if (ms->moveflags & MFL_SWIMMING)
- {
- dir[2] = goal->origin[2] - ms->origin[2];
- result.traveltype = TRAVEL_SWIM;
- } //end if
- else
- {
- dir[2] = 0;
- result.traveltype = TRAVEL_WALK;
- } //endif
- //
- dist = VectorNormalize(dir);
- if (dist > 100) dist = 100;
- speed = 400 - (400 - 4 * dist);
- if (speed < 10) speed = 0;
- //
- BotCheckBlocked(ms, dir, qtrue, &result);
- //elemantary action move in direction
- EA_Move(ms->client, dir, speed);
- VectorCopy(dir, result.movedir);
- //
- if (ms->moveflags & MFL_SWIMMING)
- {
- Vector2Angles(dir, result.ideal_viewangles);
- result.flags |= MOVERESULT_SWIMVIEW;
- } //end if
- //if (!debugline) debugline = botimport.DebugLineCreate();
- //botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE);
- //
- ms->lastreachnum = 0;
- ms->lastareanum = 0;
- ms->lastgoalareanum = goal->areanum;
- VectorCopy(ms->origin, ms->lastorigin);
- //
- return result;
-} //end of the function BotMoveInGoalArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags)
-{
- int reachnum, lastreachnum, foundjumppad, ent, resultflags;
- aas_reachability_t reach, lastreach;
- bot_movestate_t *ms;
- //vec3_t mins, maxs, up = {0, 0, 1};
- //bsp_trace_t trace;
- //static int debugline;
-
-
- BotClearMoveResult(result);
- //
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return;
- //reset the grapple before testing if the bot has a valid goal
- //because the bot could loose all it's goals when stuck to a wall
- BotResetGrapple(ms);
- //
- if (!goal)
- {
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client);
-#endif //DEBUG
- result->failure = qtrue;
- return;
- } //end if
- //botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach);
- //remove some of the move flags
- ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER);
- //set some of the move flags
- //NOTE: the MFL_ONGROUND flag is also set in the higher AI
- if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
- //
- if (ms->moveflags & MFL_ONGROUND)
- {
- int modeltype, modelnum;
-
- ent = BotOnTopOfEntity(ms);
-
- if (ent != -1)
- {
- modelnum = AAS_EntityModelindex(ent);
- if (modelnum >= 0 && modelnum < MAX_MODELS)
- {
- modeltype = modeltypes[modelnum];
-
- if (modeltype == MODELTYPE_FUNC_PLAT)
- {
- AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
- //if the bot is Not using the elevator
- if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR ||
- //NOTE: the face number is the plat model number
- (reach.facenum & 0x0000FFFF) != modelnum)
- {
- reachnum = AAS_NextModelReachability(0, modelnum);
- if (reachnum)
- {
- //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client);
- AAS_ReachabilityFromNum(reachnum, &reach);
- ms->lastreachnum = reachnum;
- ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
- } //end if
- else
- {
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client);
- } //end if
- result->blocked = qtrue;
- result->blockentity = ent;
- result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
- return;
- } //end else
- } //end if
- result->flags |= MOVERESULT_ONTOPOF_ELEVATOR;
- } //end if
- else if (modeltype == MODELTYPE_FUNC_BOB)
- {
- AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
- //if the bot is Not using the func bobbing
- if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB ||
- //NOTE: the face number is the func_bobbing model number
- (reach.facenum & 0x0000FFFF) != modelnum)
- {
- reachnum = AAS_NextModelReachability(0, modelnum);
- if (reachnum)
- {
- //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client);
- AAS_ReachabilityFromNum(reachnum, &reach);
- ms->lastreachnum = reachnum;
- ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
- } //end if
- else
- {
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client);
- } //end if
- result->blocked = qtrue;
- result->blockentity = ent;
- result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
- return;
- } //end else
- } //end if
- result->flags |= MOVERESULT_ONTOPOF_FUNCBOB;
- } //end if
- else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR)
- {
- // check if ontop of a door bridge ?
- ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
- // if not in a reachability area
- if (!AAS_AreaReachability(ms->areanum))
- {
- result->blocked = qtrue;
- result->blockentity = ent;
- result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
- return;
- } //end if
- } //end else if
- else
- {
- result->blocked = qtrue;
- result->blockentity = ent;
- result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
- return;
- } //end else
- } //end if
- } //end if
- } //end if
- //if swimming
- if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING;
- //if against a ladder
- if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER;
- //if the bot is on the ground, swimming or against a ladder
- if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER))
- {
- //botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1));
- //
- AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach);
- //reachability area the bot is in
- //ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR));
- ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
- //
- if ( !ms->areanum )
- {
- result->failure = qtrue;
- result->blocked = qtrue;
- result->blockentity = 0;
- result->type = RESULTTYPE_INSOLIDAREA;
- return;
- } //end if
- //if the bot is in the goal area
- if (ms->areanum == goal->areanum)
- {
- *result = BotMoveInGoalArea(ms, goal);
- return;
- } //end if
- //assume we can use the reachability from the last frame
- reachnum = ms->lastreachnum;
- //if there is a last reachability
- if (reachnum)
- {
- AAS_ReachabilityFromNum(reachnum, &reach);
- //check if the reachability is still valid
- if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags))
- {
- reachnum = 0;
- } //end if
- //special grapple hook case
- else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK)
- {
- if (ms->reachability_time < AAS_Time() ||
- (ms->moveflags & MFL_GRAPPLERESET))
- {
- reachnum = 0;
- } //end if
- } //end if
- //special elevator case
- else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR ||
- (reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
- {
- if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) ||
- (result->flags & MOVERESULT_ONTOPOF_FUNCBOB))
- {
- ms->reachability_time = AAS_Time() + 5;
- } //end if
- //if the bot was going for an elevator and reached the reachability area
- if (ms->areanum == reach.areanum ||
- ms->reachability_time < AAS_Time())
- {
- reachnum = 0;
- } //end if
- } //end if
- else
- {
-#ifdef DEBUG
- if (bot_developer)
- {
- if (ms->reachability_time < AAS_Time())
- {
- botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client);
- AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
- botimport.Print(PRT_MESSAGE, "\n");
- } //end if
- /*
- if (ms->lastareanum != ms->areanum)
- {
- botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum);
- } //end if*/
- } //end if
-#endif //DEBUG
- //if the goal area changed or the reachability timed out
- //or the area changed
- if (ms->lastgoalareanum != goal->areanum ||
- ms->reachability_time < AAS_Time() ||
- ms->lastareanum != ms->areanum)
- {
- reachnum = 0;
- //botimport.Print(PRT_MESSAGE, "area change or timeout\n");
- } //end else if
- } //end else
- } //end if
- resultflags = 0;
- //if the bot needs a new reachability
- if (!reachnum)
- {
- //if the area has no reachability links
- if (!AAS_AreaReachability(ms->areanum))
- {
-#ifdef DEBUG
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum);
- } //end if
-#endif //DEBUG
- } //end if
- //get a new reachability leading towards the goal
- reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum,
- ms->lastgoalareanum, ms->lastareanum,
- ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
- goal, travelflags, travelflags,
- ms->avoidspots, ms->numavoidspots, &resultflags);
- //the area number the reachability starts in
- ms->reachareanum = ms->areanum;
- //reset some state variables
- ms->jumpreach = 0; //for TRAVEL_JUMP
- ms->moveflags &= ~MFL_GRAPPLERESET; //for TRAVEL_GRAPPLEHOOK
- //if there is a reachability to the goal
- if (reachnum)
- {
- AAS_ReachabilityFromNum(reachnum, &reach);
- //set a timeout for this reachability
- ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
- //
-#ifdef AVOIDREACH
- //add the reachability to the reachabilities to avoid for a while
- BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME);
-#endif //AVOIDREACH
- } //end if
-#ifdef DEBUG
-
- else if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "goal not reachable\n");
- Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy
- } //end else
- if (bot_developer)
- {
- //if still going for the same goal
- if (ms->lastgoalareanum == goal->areanum)
- {
- if (ms->lastareanum == reach.areanum)
- {
- botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n");
- } //end if
- } //end if
- } //end if
-#endif //DEBUG
- } //end else
- //
- ms->lastreachnum = reachnum;
- ms->lastgoalareanum = goal->areanum;
- ms->lastareanum = ms->areanum;
- //if the bot has a reachability
- if (reachnum)
- {
- //get the reachability from the number
- AAS_ReachabilityFromNum(reachnum, &reach);
- result->traveltype = reach.traveltype;
- //
-#ifdef DEBUG_AI_MOVE
- AAS_ClearShownDebugLines();
- AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
- AAS_ShowReachability(&reach);
-#endif //DEBUG_AI_MOVE
- //
-#ifdef DEBUG
- //botimport.Print(PRT_MESSAGE, "client %d: ", ms->client);
- //AAS_PrintTravelType(reach.traveltype);
- //botimport.Print(PRT_MESSAGE, "\n");
-#endif //DEBUG
- switch(reach.traveltype & TRAVELTYPE_MASK)
- {
- case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;
- case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break;
- case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break;
- case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
- case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break;
- case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break;
- case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
- case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break;
- case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break;
- case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break;
- case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
- case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break;
- case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break;
- case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break;
- case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break;
- default:
- {
- botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
- break;
- } //end case
- } //end switch
- result->traveltype = reach.traveltype;
- result->flags |= resultflags;
- } //end if
- else
- {
- result->failure = qtrue;
- result->flags |= resultflags;
- Com_Memset(&reach, 0, sizeof(aas_reachability_t));
- } //end else
-#ifdef DEBUG
- if (bot_developer)
- {
- if (result->failure)
- {
- botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client);
- AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
- botimport.Print(PRT_MESSAGE, "\n");
- } //end if
- } //end if
-#endif //DEBUG
- } //end if
- else
- {
- int i, numareas, areas[16];
- vec3_t end;
-
- //special handling of jump pads when the bot uses a jump pad without knowing it
- foundjumppad = qfalse;
- VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end);
- numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16);
- for (i = numareas-1; i >= 0; i--)
- {
- if (AAS_AreaJumpPad(areas[i]))
- {
- //botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]);
- foundjumppad = qtrue;
- lastreachnum = BotGetReachabilityToGoal(end, areas[i],
- ms->lastgoalareanum, ms->lastareanum,
- ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
- goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL);
- if (lastreachnum)
- {
- ms->lastreachnum = lastreachnum;
- ms->lastareanum = areas[i];
- //botimport.Print(PRT_MESSAGE, "found jumppad reachability\n");
- break;
- } //end if
- else
- {
- for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum;
- lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum))
- {
- //get the reachability from the number
- AAS_ReachabilityFromNum(lastreachnum, &reach);
- if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)
- {
- ms->lastreachnum = lastreachnum;
- ms->lastareanum = areas[i];
- //botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n");
- break;
- } //end if
- } //end for
- if (lastreachnum) break;
- } //end else
- } //end if
- } //end for
- if (bot_developer)
- {
- //if a jumppad is found with the trace but no reachability is found
- if (foundjumppad && !ms->lastreachnum)
- {
- botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client);
- } //end if
- } //end if
- //
- if (ms->lastreachnum)
- {
- //botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1));
- AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
- result->traveltype = reach.traveltype;
-#ifdef DEBUG
- //botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client);
- //AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
- //botimport.Print(PRT_MESSAGE, "\n");
-#endif //DEBUG
- //
- switch(reach.traveltype & TRAVELTYPE_MASK)
- {
- case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break;
- case TRAVEL_CROUCH: /*do nothing*/ break;
- case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break;
- case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
- case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break;
- case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break;
- case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
- case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break;
- case TRAVEL_TELEPORT: /*do nothing*/ break;
- case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break;
- case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
- case TRAVEL_ROCKETJUMP:
- case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break;
- case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break;
- case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break;
- default:
- {
- botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
- break;
- } //end case
- } //end switch
- result->traveltype = reach.traveltype;
-#ifdef DEBUG
- if (bot_developer)
- {
- if (result->failure)
- {
- botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client);
- AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
- botimport.Print(PRT_MESSAGE, "\n");
- } //end if
- } //end if
-#endif //DEBUG
- } //end if
- } //end else
- //FIXME: is it right to do this here?
- if (result->blocked) ms->reachability_time -= 10 * ms->thinktime;
- //copy the last origin
- VectorCopy(ms->origin, ms->lastorigin);
- //return the movement result
- return;
-} //end of the function BotMoveToGoal
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetAvoidReach(int movestate)
-{
- bot_movestate_t *ms;
-
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return;
- Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
- Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float));
- Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int));
-} //end of the function BotResetAvoidReach
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetLastAvoidReach(int movestate)
-{
- int i, latest;
- float latesttime;
- bot_movestate_t *ms;
-
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return;
- latesttime = 0;
- latest = 0;
- for (i = 0; i < MAX_AVOIDREACH; i++)
- {
- if (ms->avoidreachtimes[i] > latesttime)
- {
- latesttime = ms->avoidreachtimes[i];
- latest = i;
- } //end if
- } //end for
- if (latesttime)
- {
- ms->avoidreachtimes[latest] = 0;
- if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--;
- } //end if
-} //end of the function BotResetLastAvoidReach
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetMoveState(int movestate)
-{
- bot_movestate_t *ms;
-
- ms = BotMoveStateFromHandle(movestate);
- if (!ms) return;
- Com_Memset(ms, 0, sizeof(bot_movestate_t));
-} //end of the function BotResetMoveState
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotSetupMoveAI(void)
-{
- BotSetBrushModelTypes();
- sv_maxstep = LibVar("sv_step", "18");
- sv_maxbarrier = LibVar("sv_maxbarrier", "32");
- sv_gravity = LibVar("sv_gravity", "800");
- weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5");
- weapindex_bfg10k = LibVar("weapindex_bfg10k", "9");
- weapindex_grapple = LibVar("weapindex_grapple", "10");
- entitytypemissile = LibVar("entitytypemissile", "3");
- offhandgrapple = LibVar("offhandgrapple", "0");
- cmd_grappleon = LibVar("cmd_grappleon", "grappleon");
- cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff");
- return BLERR_NOERROR;
-} //end of the function BotSetupMoveAI
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownMoveAI(void)
-{
- int i;
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (botmovestates[i])
- {
- FreeMemory(botmovestates[i]);
- botmovestates[i] = NULL;
- } //end if
- } //end for
-} //end of the function BotShutdownMoveAI
-
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_move.c + * + * desc: bot movement AI + * + * $Archive: /MissionPack/code/botlib/be_ai_move.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" + +#include "../game/be_ea.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" + + +//#define DEBUG_AI_MOVE +//#define DEBUG_ELEVATOR +//#define DEBUG_GRAPPLE + +// bk001204 - redundant bot_avoidspot_t, see ../game/be_ai_move.h + +//movement state +//NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and +// MFL_GRAPPLEPULL must be set outside the movement code +typedef struct bot_movestate_s +{ + //input vars (all set outside the movement code) + vec3_t origin; //origin of the bot + vec3_t velocity; //velocity of the bot + vec3_t viewoffset; //view offset + int entitynum; //entity number of the bot + int client; //client number of the bot + float thinktime; //time the bot thinks + int presencetype; //presencetype of the bot + vec3_t viewangles; //view angles of the bot + //state vars + int areanum; //area the bot is in + int lastareanum; //last area the bot was in + int lastgoalareanum; //last goal area number + int lastreachnum; //last reachability number + vec3_t lastorigin; //origin previous cycle + int reachareanum; //area number of the reachabilty + int moveflags; //movement flags + int jumpreach; //set when jumped + float grapplevisible_time; //last time the grapple was visible + float lastgrappledist; //last distance to the grapple end + float reachability_time; //time to use current reachability + int avoidreach[MAX_AVOIDREACH]; //reachabilities to avoid + float avoidreachtimes[MAX_AVOIDREACH]; //times to avoid the reachabilities + int avoidreachtries[MAX_AVOIDREACH]; //number of tries before avoiding + // + bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS]; //spots to avoid + int numavoidspots; +} bot_movestate_t; + +//used to avoid reachability links for some time after being used +#define AVOIDREACH +#define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use +#define AVOIDREACH_TRIES 4 +//prediction times +#define PREDICTIONTIME_JUMP 3 //in seconds +#define PREDICTIONTIME_MOVE 2 //in seconds +//weapon indexes for weapon jumping +#define WEAPONINDEX_ROCKET_LAUNCHER 5 +#define WEAPONINDEX_BFG 9 + +#define MODELTYPE_FUNC_PLAT 1 +#define MODELTYPE_FUNC_BOB 2 +#define MODELTYPE_FUNC_DOOR 3 +#define MODELTYPE_FUNC_STATIC 4 + +libvar_t *sv_maxstep; +libvar_t *sv_maxbarrier; +libvar_t *sv_gravity; +libvar_t *weapindex_rocketlauncher; +libvar_t *weapindex_bfg10k; +libvar_t *weapindex_grapple; +libvar_t *entitytypemissile; +libvar_t *offhandgrapple; +libvar_t *cmd_grappleoff; +libvar_t *cmd_grappleon; +//type of model, func_plat or func_bobbing +int modeltypes[MAX_MODELS]; + +bot_movestate_t *botmovestates[MAX_CLIENTS+1]; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotAllocMoveState(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botmovestates[i]) + { + botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t)); + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocMoveState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeMoveState(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return; + } //end if + if (!botmovestates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return; + } //end if + FreeMemory(botmovestates[handle]); + botmovestates[handle] = NULL; +} //end of the function BotFreeMoveState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_movestate_t *BotMoveStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botmovestates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return NULL; + } //end if + return botmovestates[handle]; +} //end of the function BotMoveStateFromHandle +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotInitMoveState(int handle, bot_initmove_t *initmove) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(handle); + if (!ms) return; + VectorCopy(initmove->origin, ms->origin); + VectorCopy(initmove->velocity, ms->velocity); + VectorCopy(initmove->viewoffset, ms->viewoffset); + ms->entitynum = initmove->entitynum; + ms->client = initmove->client; + ms->thinktime = initmove->thinktime; + ms->presencetype = initmove->presencetype; + VectorCopy(initmove->viewangles, ms->viewangles); + // + ms->moveflags &= ~MFL_ONGROUND; + if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND; + ms->moveflags &= ~MFL_TELEPORTED; + if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED; + ms->moveflags &= ~MFL_WATERJUMP; + if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP; + ms->moveflags &= ~MFL_WALK; + if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK; + ms->moveflags &= ~MFL_GRAPPLEPULL; + if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL; +} //end of the function BotInitMoveState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +float AngleDiff(float ang1, float ang2) +{ + float diff; + + diff = ang1 - ang2; + if (ang1 > ang2) + { + if (diff > 180.0) diff -= 360.0; + } //end if + else + { + if (diff < -180.0) diff += 360.0; + } //end else + return diff; +} //end of the function AngleDiff +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotFuzzyPointReachabilityArea(vec3_t origin) +{ + int firstareanum, j, x, y, z; + int areas[10], numareas, areanum, bestareanum; + float dist, bestdist; + vec3_t points[10], v, end; + + firstareanum = 0; + areanum = AAS_PointAreaNum(origin); + if (areanum) + { + firstareanum = areanum; + if (AAS_AreaReachability(areanum)) return areanum; + } //end if + VectorCopy(origin, end); + end[2] += 4; + numareas = AAS_TraceAreas(origin, end, areas, points, 10); + for (j = 0; j < numareas; j++) + { + if (AAS_AreaReachability(areas[j])) return areas[j]; + } //end for + bestdist = 999999; + bestareanum = 0; + for (z = 1; z >= -1; z -= 1) + { + for (x = 1; x >= -1; x -= 1) + { + for (y = 1; y >= -1; y -= 1) + { + VectorCopy(origin, end); + end[0] += x * 8; + end[1] += y * 8; + end[2] += z * 12; + numareas = AAS_TraceAreas(origin, end, areas, points, 10); + for (j = 0; j < numareas; j++) + { + if (AAS_AreaReachability(areas[j])) + { + VectorSubtract(points[j], origin, v); + dist = VectorLength(v); + if (dist < bestdist) + { + bestareanum = areas[j]; + bestdist = dist; + } //end if + } //end if + if (!firstareanum) firstareanum = areas[j]; + } //end for + } //end for + } //end for + if (bestareanum) return bestareanum; + } //end for + return firstareanum; +} //end of the function BotFuzzyPointReachabilityArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotReachabilityArea(vec3_t origin, int client) +{ + int modelnum, modeltype, reachnum, areanum; + aas_reachability_t reach; + vec3_t org, end, mins, maxs, up = {0, 0, 1}; + bsp_trace_t bsptrace; + aas_trace_t trace; + + //check if the bot is standing on something + AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); + VectorMA(origin, -3, up, end); + bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE) + { + //if standing on the world the bot should be in a valid area + if (bsptrace.ent == ENTITYNUM_WORLD) + { + return BotFuzzyPointReachabilityArea(origin); + } //end if + + modelnum = AAS_EntityModelindex(bsptrace.ent); + modeltype = modeltypes[modelnum]; + + //if standing on a func_plat or func_bobbing then the bot is assumed to be + //in the area the reachability points to + if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB) + { + reachnum = AAS_NextModelReachability(0, modelnum); + if (reachnum) + { + AAS_ReachabilityFromNum(reachnum, &reach); + return reach.areanum; + } //end if + } //end else if + + //if the bot is swimming the bot should be in a valid area + if (AAS_Swimming(origin)) + { + return BotFuzzyPointReachabilityArea(origin); + } //end if + // + areanum = BotFuzzyPointReachabilityArea(origin); + //if the bot is in an area with reachabilities + if (areanum && AAS_AreaReachability(areanum)) return areanum; + //trace down till the ground is hit because the bot is standing on some other entity + VectorCopy(origin, org); + VectorCopy(org, end); + end[2] -= 800; + trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + VectorCopy(trace.endpos, org); + } //end if + // + return BotFuzzyPointReachabilityArea(org); + } //end if + // + return BotFuzzyPointReachabilityArea(origin); +} //end of the function BotReachabilityArea +//=========================================================================== +// returns the reachability area the bot is in +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +int BotReachabilityArea(vec3_t origin, int testground) +{ + int firstareanum, i, j, x, y, z; + int areas[10], numareas, areanum, bestareanum; + float dist, bestdist; + vec3_t org, end, points[10], v; + aas_trace_t trace; + + firstareanum = 0; + for (i = 0; i < 2; i++) + { + VectorCopy(origin, org); + //if test at the ground (used when bot is standing on an entity) + if (i > 0) + { + VectorCopy(origin, end); + end[2] -= 800; + trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + VectorCopy(trace.endpos, org); + } //end if + } //end if + + firstareanum = 0; + areanum = AAS_PointAreaNum(org); + if (areanum) + { + firstareanum = areanum; + if (AAS_AreaReachability(areanum)) return areanum; + } //end if + bestdist = 999999; + bestareanum = 0; + for (z = 1; z >= -1; z -= 1) + { + for (x = 1; x >= -1; x -= 1) + { + for (y = 1; y >= -1; y -= 1) + { + VectorCopy(org, end); + end[0] += x * 8; + end[1] += y * 8; + end[2] += z * 12; + numareas = AAS_TraceAreas(org, end, areas, points, 10); + for (j = 0; j < numareas; j++) + { + if (AAS_AreaReachability(areas[j])) + { + VectorSubtract(points[j], org, v); + dist = VectorLength(v); + if (dist < bestdist) + { + bestareanum = areas[j]; + bestdist = dist; + } //end if + } //end if + } //end for + } //end for + } //end for + if (bestareanum) return bestareanum; + } //end for + if (!testground) break; + } //end for +//#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "no reachability area\n"); +//#endif //DEBUG + return firstareanum; +} //end of the function BotReachabilityArea*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach) +{ + int i, modelnum; + vec3_t mins, maxs, modelorigin, org, end; + vec3_t angles = {0, 0, 0}; + vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8}; + bsp_trace_t trace; + + modelnum = reach->facenum & 0x0000FFFF; + //get some bsp model info + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + // + if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin)) + { + botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); + return qfalse; + } //end if + // + for (i = 0; i < 2; i++) + { + if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse; + if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse; + } //end for + // + VectorCopy(origin, org); + org[2] += 24; + VectorCopy(origin, end); + end[2] -= 48; + // + trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!trace.startsolid && !trace.allsolid) + { + //NOTE: the reachability face number is the model number of the elevator + if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum) + { + return qtrue; + } //end if + } //end if + return qfalse; +} //end of the function BotOnMover +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int MoverDown(aas_reachability_t *reach) +{ + int modelnum; + vec3_t mins, maxs, origin; + vec3_t angles = {0, 0, 0}; + + modelnum = reach->facenum & 0x0000FFFF; + //get some bsp model info + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); + // + if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) + { + botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); + return qfalse; + } //end if + //if the top of the plat is below the reachability start point + if (origin[2] + maxs[2] < reach->start[2]) return qtrue; + return qfalse; +} //end of the function MoverDown +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotSetBrushModelTypes(void) +{ + int ent, modelnum; + char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY]; + + Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int)); + // + for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent)) + { + if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue; + if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue; + if (model[0]) modelnum = atoi(model+1); + else modelnum = 0; + + if (modelnum < 0 || modelnum > MAX_MODELS) + { + botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname); + continue; + } //end if + + if (!Q_stricmp(classname, "func_bobbing")) + modeltypes[modelnum] = MODELTYPE_FUNC_BOB; + else if (!Q_stricmp(classname, "func_plat")) + modeltypes[modelnum] = MODELTYPE_FUNC_PLAT; + else if (!Q_stricmp(classname, "func_door")) + modeltypes[modelnum] = MODELTYPE_FUNC_DOOR; + else if (!Q_stricmp(classname, "func_static")) + modeltypes[modelnum] = MODELTYPE_FUNC_STATIC; + } //end for +} //end of the function BotSetBrushModelTypes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotOnTopOfEntity(bot_movestate_t *ms) +{ + vec3_t mins, maxs, end, up = {0, 0, 1}; + bsp_trace_t trace; + + AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); + VectorMA(ms->origin, -3, up, end); + trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) + { + return trace.ent; + } //end if + return -1; +} //end of the function BotOnTopOfEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags) +{ + //if the reachability uses an unwanted travel type + if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse; + //don't go into areas with bad travel types + if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse; + return qtrue; +} //end of the function BotValidTravel +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime) +{ + int i; + + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (ms->avoidreach[i] == number) + { + if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++; + else ms->avoidreachtries[i] = 1; + ms->avoidreachtimes[i] = AAS_Time() + avoidtime; + return; + } //end if + } //end for + //add the reachability to the reachabilities to avoid for a while + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (ms->avoidreachtimes[i] < AAS_Time()) + { + ms->avoidreach[i] = number; + ms->avoidreachtimes[i] = AAS_Time() + avoidtime; + ms->avoidreachtries[i] = 1; + return; + } //end if + } //end for +} //end of the function BotAddToAvoidReach +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2) +{ + vec3_t proj, dir; + int j; + + AAS_ProjectPointOntoVector(p, lp1, lp2, proj); + for (j = 0; j < 3; j++) + if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || + (proj[j] < lp1[j] && proj[j] < lp2[j])) + break; + if (j < 3) { + if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j])) + VectorSubtract(p, lp1, dir); + else + VectorSubtract(p, lp2, dir); + return VectorLengthSquared(dir); + } + VectorSubtract(p, proj, dir); + return VectorLengthSquared(dir); +} //end of the function DistanceFromLineSquared +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float VectorDistanceSquared(vec3_t p1, vec3_t p2) +{ + vec3_t dir; + VectorSubtract(p2, p1, dir); + return VectorLengthSquared(dir); +} //end of the function VectorDistanceSquared +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots) +{ + int checkbetween, i, type; + float squareddist, squaredradius; + + switch(reach->traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: checkbetween = qtrue; break; + case TRAVEL_CROUCH: checkbetween = qtrue; break; + case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break; + case TRAVEL_LADDER: checkbetween = qtrue; break; + case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break; + case TRAVEL_JUMP: checkbetween = qfalse; break; + case TRAVEL_SWIM: checkbetween = qtrue; break; + case TRAVEL_WATERJUMP: checkbetween = qtrue; break; + case TRAVEL_TELEPORT: checkbetween = qfalse; break; + case TRAVEL_ELEVATOR: checkbetween = qfalse; break; + case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break; + case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break; + case TRAVEL_BFGJUMP: checkbetween = qfalse; break; + case TRAVEL_JUMPPAD: checkbetween = qfalse; break; + case TRAVEL_FUNCBOB: checkbetween = qfalse; break; + default: checkbetween = qtrue; break; + } //end switch + + type = AVOID_CLEAR; + for (i = 0; i < numavoidspots; i++) + { + squaredradius = Square(avoidspots[i].radius); + squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start); + // if moving towards the avoid spot + if (squareddist < squaredradius && + VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist) + { + type = avoidspots[i].type; + } //end if + else if (checkbetween) { + squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end); + // if moving towards the avoid spot + if (squareddist < squaredradius && + VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) + { + type = avoidspots[i].type; + } //end if + } //end if + else + { + VectorDistanceSquared(avoidspots[i].origin, reach->end); + // if the reachability leads closer to the avoid spot + if (squareddist < squaredradius && + VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist) + { + type = avoidspots[i].type; + } //end if + } //end else + if (type == AVOID_ALWAYS) + return type; + } //end for + return type; +} //end of the function BotAvoidSpots +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + if (type == AVOID_CLEAR) + { + ms->numavoidspots = 0; + return; + } //end if + + if (ms->numavoidspots >= MAX_AVOIDSPOTS) + return; + VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin); + ms->avoidspots[ms->numavoidspots].radius = radius; + ms->avoidspots[ms->numavoidspots].type = type; + ms->numavoidspots++; +} //end of the function BotAddAvoidSpot +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotGetReachabilityToGoal(vec3_t origin, int areanum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t *goal, int travelflags, int movetravelflags, + struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags) +{ + int i, t, besttime, bestreachnum, reachnum; + aas_reachability_t reach; + + //if not in a valid area + if (!areanum) return 0; + // + if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum)) + { + travelflags |= TFL_DONOTENTER; + movetravelflags |= TFL_DONOTENTER; + } //end if + //use the routing to find the next area to go to + besttime = 0; + bestreachnum = 0; + // + for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum; + reachnum = AAS_NextAreaReachability(areanum, reachnum)) + { +#ifdef AVOIDREACH + //check if it isn't an reachability to avoid + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break; + } //end for + if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES) + { +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]); + } //end if +#endif //DEBUG + continue; + } //end if +#endif //AVOIDREACH + //get the reachability from the number + AAS_ReachabilityFromNum(reachnum, &reach); + //NOTE: do not go back to the previous area if the goal didn't change + //NOTE: is this actually avoidance of local routing minima between two areas??? + if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue; + //if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue; + //if the travel isn't valid + if (!BotValidTravel(origin, &reach, movetravelflags)) continue; + //get the travel time + t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags); + //if the goal area isn't reachable from the reachable area + if (!t) continue; + //if the bot should not use this reachability to avoid bad spots + if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) { + if (flags) { + *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT; + } + continue; + } + //add the travel time towards the area + t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start); + //if the travel time is better than the ones already found + if (!besttime || t < besttime) + { + besttime = t; + bestreachnum = reachnum; + } //end if + } //end for + // + return bestreachnum; +} //end of the function BotGetReachabilityToGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target) +{ + vec3_t dir; + float curdist; + + VectorSubtract(end, start, dir); + curdist = VectorNormalize(dir); + if (*dist + curdist < maxdist) + { + VectorCopy(end, target); + *dist += curdist; + return qfalse; + } //end if + else + { + VectorMA(start, maxdist - *dist, dir, target); + *dist = maxdist; + return qtrue; + } //end else +} //end of the function BotAddToTarget + +int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target) +{ + aas_reachability_t reach; + int reachnum, lastareanum; + bot_movestate_t *ms; + vec3_t end; + float dist; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return qfalse; + reachnum = 0; + //if the bot has no goal or no last reachability + if (!ms->lastreachnum || !goal) return qfalse; + + reachnum = ms->lastreachnum; + VectorCopy(ms->origin, end); + lastareanum = ms->lastareanum; + dist = 0; + while(reachnum && dist < lookahead) + { + AAS_ReachabilityFromNum(reachnum, &reach); + if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue; + //never look beyond teleporters + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue; + //never look beyond the weapon jump point + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue; + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue; + //don't add jump pad distances + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD && + (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR && + (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB) + { + if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue; + } //end if + reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum, + ms->lastgoalareanum, lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, travelflags, NULL, 0, NULL); + VectorCopy(reach.end, end); + lastareanum = reach.areanum; + if (lastareanum == goal->areanum) + { + BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target); + return qtrue; + } //end if + } //end while + // + return qfalse; +} //end of the function BotMovementViewTarget +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotVisible(int ent, vec3_t eye, vec3_t target) +{ + bsp_trace_t trace; + + trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (trace.fraction >= 1) return qtrue; + return qfalse; +} //end of the function BotVisible +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target) +{ + aas_reachability_t reach; + int reachnum, lastgoalareanum, lastareanum, i; + int avoidreach[MAX_AVOIDREACH]; + float avoidreachtimes[MAX_AVOIDREACH]; + int avoidreachtries[MAX_AVOIDREACH]; + vec3_t end; + + //if the bot has no goal or no last reachability + if (!goal) return qfalse; + //if the areanum is not valid + if (!areanum) return qfalse; + //if the goal areanum is not valid + if (!goal->areanum) return qfalse; + + Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); + lastgoalareanum = goal->areanum; + lastareanum = areanum; + VectorCopy(origin, end); + //only do 20 hops + for (i = 0; i < 20 && (areanum != goal->areanum); i++) + { + // + reachnum = BotGetReachabilityToGoal(end, areanum, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + goal, travelflags, travelflags, NULL, 0, NULL); + if (!reachnum) return qfalse; + AAS_ReachabilityFromNum(reachnum, &reach); + // + if (BotVisible(goal->entitynum, goal->origin, reach.start)) + { + VectorCopy(reach.start, target); + return qtrue; + } //end if + // + if (BotVisible(goal->entitynum, goal->origin, reach.end)) + { + VectorCopy(reach.end, target); + return qtrue; + } //end if + // + if (reach.areanum == goal->areanum) + { + VectorCopy(reach.end, target); + return qtrue; + } //end if + // + lastareanum = areanum; + areanum = reach.areanum; + VectorCopy(reach.end, end); + // + } //end while + // + return qfalse; +} //end of the function BotPredictVisiblePosition +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter) +{ + int modelnum; + vec3_t mins, maxs, origin, mids; + vec3_t angles = {0, 0, 0}; + + modelnum = reach->facenum & 0x0000FFFF; + //get some bsp model info + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); + // + if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) + { + botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum); + } //end if + //get a point just above the plat in the bottom position + VectorAdd(mins, maxs, mids); + VectorMA(origin, 0.5, mids, bottomcenter); + bottomcenter[2] = reach->start[2]; +} //end of the function MoverBottomCenter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum) +{ + float dist, startz; + vec3_t start, end; + aas_trace_t trace; + + //do gap checking + startz = origin[2]; + //this enables walking down stairs more fluidly + { + VectorCopy(origin, start); + VectorCopy(origin, end); + end[2] -= 60; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); + if (trace.fraction >= 1) return 1; + startz = trace.endpos[2] + 1; + } + // + for (dist = 8; dist <= 100; dist += 8) + { + VectorMA(origin, dist, hordir, start); + start[2] = startz + 24; + VectorCopy(start, end); + end[2] -= 48 + sv_maxbarrier->value; + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum); + //if solid is found the bot can't walk any further and fall into a gap + if (!trace.startsolid) + { + //if it is a gap + if (trace.endpos[2] < startz - sv_maxstep->value - 8) + { + VectorCopy(trace.endpos, end); + end[2] -= 20; + if (AAS_PointContents(end) & CONTENTS_WATER) break; + //if a gap is found slow down + //botimport.Print(PRT_MESSAGE, "gap at %f\n", dist); + return dist; + } //end if + startz = trace.endpos[2]; + } //end if + } //end for + return 0; +} //end of the function BotGapDistance +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed) +{ + vec3_t start, hordir, end; + aas_trace_t trace; + + VectorCopy(ms->origin, end); + end[2] += sv_maxbarrier->value; + //trace right up + trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum); + //this shouldn't happen... but we check anyway + if (trace.startsolid) return qfalse; + //if very low ceiling it isn't possible to jump up to a barrier + if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; + // + hordir[0] = dir[0]; + hordir[1] = dir[1]; + hordir[2] = 0; + VectorNormalize(hordir); + VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end); + VectorCopy(trace.endpos, start); + end[2] = trace.endpos[2]; + //trace from previous trace end pos horizontally in the move direction + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); + //again this shouldn't happen + if (trace.startsolid) return qfalse; + // + VectorCopy(trace.endpos, start); + VectorCopy(trace.endpos, end); + end[2] = ms->origin[2]; + //trace down from the previous trace end pos + trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum); + //if solid + if (trace.startsolid) return qfalse; + //if no obstacle at all + if (trace.fraction >= 1.0) return qfalse; + //if less than the maximum step height + if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse; + // + EA_Jump(ms->client); + EA_Move(ms->client, hordir, speed); + ms->moveflags |= MFL_BARRIERJUMP; + //there is a barrier + return qtrue; +} //end of the function BotCheckBarrierJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) +{ + vec3_t normdir; + + VectorCopy(dir, normdir); + VectorNormalize(normdir); + EA_Move(ms->client, normdir, speed); + return qtrue; +} //end of the function BotSwimInDirection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type) +{ + vec3_t hordir, cmdmove, velocity, tmpdir, origin; + int presencetype, maxframes, cmdframes, stopevent; + aas_clientmove_t move; + float dist; + + if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; + //if the bot is on the ground + if (ms->moveflags & MFL_ONGROUND) + { + //if there is a barrier the bot can jump on + if (BotCheckBarrierJump(ms, dir, speed)) return qtrue; + //remove barrier jump flag + ms->moveflags &= ~MFL_BARRIERJUMP; + //get the presence type for the movement + if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH; + else presencetype = PRESENCE_NORMAL; + //horizontal direction + hordir[0] = dir[0]; + hordir[1] = dir[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //if the bot is not supposed to jump + if (!(type & MOVE_JUMP)) + { + //if there is a gap, try to jump over it + if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP; + } //end if + //get command movement + VectorScale(hordir, speed, cmdmove); + VectorCopy(ms->velocity, velocity); + // + if (type & MOVE_JUMP) + { + //botimport.Print(PRT_MESSAGE, "trying jump\n"); + cmdmove[2] = 400; + maxframes = PREDICTIONTIME_JUMP / 0.1; + cmdframes = 1; + stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE| + SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; + } //end if + else + { + maxframes = 2; + cmdframes = 2; + stopevent = SE_HITGROUNDDAMAGE| + SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA; + } //end else + //AAS_ClearShownDebugLines(); + // + VectorCopy(ms->origin, origin); + origin[2] += 0.5; + AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue, + velocity, cmdmove, cmdframes, maxframes, 0.1f, + stopevent, 0, qfalse);//qtrue); + //if prediction time wasn't enough to fully predict the movement + if (move.frames >= maxframes && (type & MOVE_JUMP)) + { + //botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client); + return qfalse; + } //end if + //don't enter slime or lava and don't fall from too high + if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE)) + { + //botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client); + //if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n"); + //if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n"); + //if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n"); + return qfalse; + } //end if + //if ground was hit + if (move.stopevent & SE_HITGROUND) + { + //check for nearby gap + VectorNormalize2(move.velocity, tmpdir); + dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum); + if (dist > 0) return qfalse; + // + dist = BotGapDistance(move.endpos, hordir, ms->entitynum); + if (dist > 0) return qfalse; + } //end if + //get horizontal movement + tmpdir[0] = move.endpos[0] - ms->origin[0]; + tmpdir[1] = move.endpos[1] - ms->origin[1]; + tmpdir[2] = 0; + // + //AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE); + //the bot is blocked by something + if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse; + //perform the movement + if (type & MOVE_JUMP) EA_Jump(ms->client); + if (type & MOVE_CROUCH) EA_Crouch(ms->client); + EA_Move(ms->client, hordir, speed); + //movement was succesfull + return qtrue; + } //end if + else + { + if (ms->moveflags & MFL_BARRIERJUMP) + { + //if near the top or going down + if (ms->velocity[2] < 50) + { + EA_Move(ms->client, dir, speed); + } //end if + } //end if + //FIXME: do air control to avoid hazards + return qtrue; + } //end else +} //end of the function BotWalkInDirection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return qfalse; + //if swimming + if (AAS_Swimming(ms->origin)) + { + return BotSwimInDirection(ms, dir, speed, type); + } //end if + else + { + return BotWalkInDirection(ms, dir, speed, type); + } //end else +} //end of the function BotMoveInDirection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out) +{ + float x1, dx1, dy1, x2, dx2, dy2, d; + + dx1 = p2[0] - p1[0]; + dy1 = p2[1] - p1[1]; + dx2 = p4[0] - p3[0]; + dy2 = p4[1] - p3[1]; + + d = dy1 * dx2 - dx1 * dy2; + if (d != 0) + { + x1 = p1[1] * dx1 - p1[0] * dy1; + x2 = p3[1] * dx2 - p3[0] * dy2; + out[0] = (int) ((dx1 * x2 - dx2 * x1) / d); + out[1] = (int) ((dy1 * x2 - dy2 * x1) / d); + return qtrue; + } //end if + else + { + return qfalse; + } //end else +} //end of the function Intersection +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result) +{ + vec3_t mins, maxs, end, up = {0, 0, 1}; + bsp_trace_t trace; + + //test for entities obstructing the bot's path + AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); + // + if (fabs(DotProduct(dir, up)) < 0.7) + { + mins[2] += sv_maxstep->value; //if the bot can step on + maxs[2] -= 10; //a little lower to avoid low ceiling + } //end if + VectorMA(ms->origin, 3, dir, end); + trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY); + //if not started in solid and not hitting the world entity + if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) + { + result->blocked = qtrue; + result->blockentity = trace.ent; +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); +#endif //DEBUG + } //end if + //if not in an area with reachability + else if (checkbottom && !AAS_AreaReachability(ms->areanum)) + { + //check if the bot is standing on something + AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs); + VectorMA(ms->origin, -3, up, end); + trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP); + if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) ) + { + result->blocked = qtrue; + result->blockentity = trace.ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client); +#endif //DEBUG + } //end if + } //end else +} //end of the function BotCheckBlocked +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotClearMoveResult(bot_moveresult_t *moveresult) +{ + moveresult->failure = qfalse; + moveresult->type = 0; + moveresult->blocked = qfalse; + moveresult->blockentity = 0; + moveresult->traveltype = 0; + moveresult->flags = 0; +} //end of the function BotClearMoveResult +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist, speed; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //first walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + // + if (dist < 10) + { + //walk straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + } //end if + //if going towards a crouch area + if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL)) + { + //if pretty close to the reachable area + if (dist < 20) EA_Crouch(ms->client); + } //end if + // + dist = BotGapDistance(ms->origin, hordir, ms->entitynum); + // + if (ms->moveflags & MFL_WALK) + { + if (dist > 0) speed = 200 - (180 - 1 * dist); + else speed = 200; + EA_Walk(ms->client); + } //end if + else + { + if (dist > 0) speed = 400 - (360 - 2 * dist); + else speed = 400; + } //end else + //elemantary action move in direction + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Walk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, speed; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if not on the ground and changed areas... don't walk back!! + //(doesn't seem to help) + /* + ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + if (ms->areanum == reach->areanum) + { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n"); +#endif //DEBUG + return result; + } //end if*/ + //go straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 100) dist = 100; + speed = 400 - (400 - 3 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_Walk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float speed; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + speed = 400; + //walk straight to reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + //elemantary actions + EA_Crouch(ms->client); + EA_Move(ms->client, hordir, speed); + // + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Crouch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist, speed; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //walk straight to reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + //if pretty close to the barrier + if (dist < 9) + { + EA_Jump(ms->client); + } //end if + else + { + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + EA_Move(ms->client, hordir, speed); + } //end else + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_BarrierJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if near the top or going down + if (ms->velocity[2] < 250) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + // + EA_Move(ms->client, hordir, 400); + VectorCopy(hordir, result.movedir); + } //end if + // + return result; +} //end of the function BotFinishTravel_BarrierJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //swim straight to reachability end + VectorSubtract(reach->start, ms->origin, dir); + VectorNormalize(dir); + // + BotCheckBlocked(ms, dir, qtrue, &result); + //elemantary actions + EA_Move(ms->client, dir, 400); + // + VectorCopy(dir, result.movedir); + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_SWIMVIEW; + // + return result; +} //end of the function BotTravel_Swim +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, hordir; + float dist; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //swim straight to reachability end + VectorSubtract(reach->end, ms->origin, dir); + VectorCopy(dir, hordir); + hordir[2] = 0; + dir[2] += 15 + crandom() * 40; + //botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]); + VectorNormalize(dir); + dist = VectorNormalize(hordir); + //elemantary actions + //EA_Move(ms->client, dir, 400); + EA_MoveForward(ms->client); + //move up if close to the actual out of water jump spot + if (dist < 40) EA_MoveUp(ms->client); + //set the ideal view angles + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // + VectorCopy(dir, result.movedir); + // + return result; +} //end of the function BotTravel_WaterJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, pnt; + float dist; + bot_moveresult_t result; + + //botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n"); + BotClearMoveResult(&result); + //if waterjumping there's nothing to do + if (ms->moveflags & MFL_WATERJUMP) return result; + //if not touching any water anymore don't do anything + //otherwise the bot sometimes keeps jumping? + VectorCopy(ms->origin, pnt); + pnt[2] -= 32; //extra for q2dm4 near red armor/mega health + if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result; + //swim straight to reachability end + VectorSubtract(reach->end, ms->origin, dir); + dir[0] += crandom() * 10; + dir[1] += crandom() * 10; + dir[2] += 70 + crandom() * 10; + dist = VectorNormalize(dir); + //elemantary actions + EA_Move(ms->client, dir, 400); + //set the ideal view angles + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // + VectorCopy(dir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_WaterJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, dir; + float dist, speed, reachhordist; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //check if the bot is blocked by anything + VectorSubtract(reach->start, ms->origin, dir); + VectorNormalize(dir); + BotCheckBlocked(ms, dir, qtrue, &result); + //if the reachability start and end are practially above each other + VectorSubtract(reach->end, reach->start, dir); + dir[2] = 0; + reachhordist = VectorLength(dir); + //walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + //if pretty close to the start focus on the reachability end + if (dist < 48) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + if (reachhordist < 20) + { + speed = 100; + } //end if + else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed)) + { + speed = 400; + } //end if + } //end if + else + { + if (reachhordist < 20) + { + if (dist > 64) dist = 64; + speed = 400 - (256 - 4 * dist); + } //end if + else + { + speed = 400; + } //end else + } //end else + // + BotCheckBlocked(ms, hordir, qtrue, &result); + //elemantary action + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_WalkOffLedge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed) +{ + vec3_t org, vel; + float dist; + int i; + + VectorCopy(origin, org); + VectorScale(velocity, 0.1, vel); + for (i = 0; i < 50; i++) + { + vel[2] -= sv_gravity->value * 0.01; + //if going down and next position would be below the goal + if (vel[2] < 0 && org[2] + vel[2] < goal[2]) + { + VectorScale(vel, (goal[2] - org[2]) / vel[2], vel); + VectorAdd(org, vel, org); + VectorSubtract(goal, org, dir); + dist = VectorNormalize(dir); + if (dist > 32) dist = 32; + *speed = 400 - (400 - 13 * dist); + return qtrue; + } //end if + else + { + VectorAdd(org, vel, org); + } //end else + } //end for + VectorSet(dir, 0, 0, 0); + *speed = 400; + return qfalse; +} //end of the function BotAirControl +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, hordir, end, v; + float dist, speed; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + VectorSubtract(reach->end, ms->origin, dir); + BotCheckBlocked(ms, dir, qtrue, &result); + // + VectorSubtract(reach->end, ms->origin, v); + v[2] = 0; + dist = VectorNormalize(v); + if (dist > 16) VectorMA(reach->end, 16, v, end); + else VectorCopy(reach->end, end); + // + if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed)) + { + //go straight to the reachability end + VectorCopy(dir, hordir); + hordir[2] = 0; + // + dist = VectorNormalize(hordir); + speed = 400; + } //end if + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_WalkOffLedge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, gapdist, speed, horspeed, sv_jumpvel; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + sv_jumpvel = botlibglobals.sv_jumpvel->value; + //walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + speed = 350; + // + gapdist = BotGapDistance(ms, hordir, ms->entitynum); + //if pretty close to the start focus on the reachability end + if (dist < 50 || (gapdist && gapdist < 50)) + { + //NOTE: using max speed (400) works best + //if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed)) + //{ + // speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; + //} //end if + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + VectorNormalize(hordir); + //elemantary action jump + EA_Jump(ms->client); + // + ms->jumpreach = ms->lastreachnum; + speed = 600; + } //end if + else + { + if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed)) + { + speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value; + } //end if + } //end else + //elemantary action + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Jump*/ +/* +bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, dir1, dir2, mins, maxs, start, end; + float dist1, dist2, speed; + bot_moveresult_t result; + bsp_trace_t trace; + + BotClearMoveResult(&result); + // + hordir[0] = reach->start[0] - reach->end[0]; + hordir[1] = reach->start[1] - reach->end[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + VectorCopy(reach->start, start); + start[2] += 1; + //minus back the bouding box size plus 16 + VectorMA(reach->start, 80, hordir, end); + // + AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs); + //check for solids + trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID); + if (trace.startsolid) VectorCopy(start, trace.endpos); + //check for a gap + for (dist1 = 0; dist1 < 80; dist1 += 10) + { + VectorMA(start, dist1+10, hordir, end); + end[2] += 1; + if (AAS_PointAreaNum(end) != ms->reachareanum) break; + } //end for + if (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos); +// dist1 = BotGapDistance(start, hordir, ms->entitynum); +// if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos); + // + VectorSubtract(ms->origin, reach->start, dir1); + dir1[2] = 0; + dist1 = VectorNormalize(dir1); + VectorSubtract(ms->origin, trace.endpos, dir2); + dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if just before the reachability start + if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) + { + //botimport.Print(PRT_MESSAGE, "between jump start and run to point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + if (dist1 < 24) EA_Jump(ms->client); + else if (dist1 < 32) EA_DelayedJump(ms->client); + EA_Move(ms->client, hordir, 600); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { + //botimport.Print(PRT_MESSAGE, "going towards run to point\n"); + hordir[0] = trace.endpos[0] - ms->origin[0]; + hordir[1] = trace.endpos[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + if (dist2 > 80) dist2 = 80; + speed = 400 - (400 - 5 * dist2); + EA_Move(ms->client, hordir, speed); + } //end else + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Jump*/ +//* +bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, dir1, dir2, start, end, runstart; +// vec3_t runstart, dir1, dir2, hordir; + float dist1, dist2, speed; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + AAS_JumpReachRunStart(reach, runstart); + //* + hordir[0] = runstart[0] - reach->start[0]; + hordir[1] = runstart[1] - reach->start[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + VectorCopy(reach->start, start); + start[2] += 1; + VectorMA(reach->start, 80, hordir, runstart); + //check for a gap + for (dist1 = 0; dist1 < 80; dist1 += 10) + { + VectorMA(start, dist1+10, hordir, end); + end[2] += 1; + if (AAS_PointAreaNum(end) != ms->reachareanum) break; + } //end for + if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart); + // + VectorSubtract(ms->origin, reach->start, dir1); + dir1[2] = 0; + dist1 = VectorNormalize(dir1); + VectorSubtract(ms->origin, runstart, dir2); + dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if just before the reachability start + if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5) + { +// botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + if (dist1 < 24) EA_Jump(ms->client); + else if (dist1 < 32) EA_DelayedJump(ms->client); + EA_Move(ms->client, hordir, 600); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { +// botimport.Print(PRT_MESSAGE, "going towards run start point\n"); + hordir[0] = runstart[0] - ms->origin[0]; + hordir[1] = runstart[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + // + if (dist2 > 80) dist2 = 80; + speed = 400 - (400 - 5 * dist2); + EA_Move(ms->client, hordir, speed); + } //end else + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_Jump*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir, hordir2; + float speed, dist; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if not jumped yet + if (!ms->jumpreach) return result; + //go straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + hordir2[0] = reach->end[0] - reach->start[0]; + hordir2[1] = reach->end[1] - reach->start[1]; + hordir2[2] = 0; + VectorNormalize(hordir2); + // + if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result; + //always use max speed when traveling through the air + speed = 800; + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_Jump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach) +{ + //float dist, speed; + vec3_t dir, viewdir;//, hordir; + vec3_t origin = {0, 0, 0}; +// vec3_t up = {0, 0, 1}; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // +// if ((ms->moveflags & MFL_AGAINSTLADDER)) + //NOTE: not a good idea for ladders starting in water + // || !(ms->moveflags & MFL_ONGROUND)) + { + //botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n"); + VectorSubtract(reach->end, ms->origin, dir); + VectorNormalize(dir); + //set the ideal view angles, facing the ladder up or down + viewdir[0] = dir[0]; + viewdir[1] = dir[1]; + viewdir[2] = 3 * dir[2]; + Vector2Angles(viewdir, result.ideal_viewangles); + //elemantary action + EA_Move(ms->client, origin, 0); + EA_MoveForward(ms->client); + //set movement view flag so the AI can see the view is focussed + result.flags |= MOVERESULT_MOVEMENTVIEW; + } //end if +/* else + { + //botimport.Print(PRT_MESSAGE, "moving towards ladder\n"); + VectorSubtract(reach->end, ms->origin, dir); + //make sure the horizontal movement is large anough + VectorCopy(dir, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + dir[0] = hordir[0]; + dir[1] = hordir[1]; + if (dir[2] > 0) dir[2] = 1; + else dir[2] = -1; + if (dist > 50) dist = 50; + speed = 400 - (200 - 4 * dist); + EA_Move(ms->client, dir, speed); + } //end else*/ + //save the movement direction + VectorCopy(dir, result.movedir); + // + return result; +} //end of the function BotTravel_Ladder +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if the bot is being teleported + if (ms->moveflags & MFL_TELEPORTED) return result; + + //walk straight to center of the teleporter + VectorSubtract(reach->start, ms->origin, hordir); + if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + + if (dist < 30) EA_Move(ms->client, hordir, 200); + else EA_Move(ms->client, hordir, 400); + + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + + VectorCopy(hordir, result.movedir); + return result; +} //end of the function BotTravel_Teleport +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, dir1, dir2, hordir, bottomcenter; + float dist, dist1, dist2, speed; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if standing on the plat + if (BotOnMover(ms->origin, ms->entitynum, reach)) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot on elevator\n"); +#endif //DEBUG_ELEVATOR + //if vertically not too far from the end point + if (abs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to end\n"); +#endif //DEBUG_ELEVATOR + //move to the end point + VectorSubtract(reach->end, ms->origin, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + if (!BotCheckBarrierJump(ms, hordir, 100)) + { + EA_Move(ms->client, hordir, 400); + } //end if + VectorCopy(hordir, result.movedir); + } //end else + //if not really close to the center of the elevator + else + { + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 10) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to center\n"); +#endif //DEBUG_ELEVATOR + //move to the center of the plat + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } //end if + } //end else + } //end if + else + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot not on elevator\n"); +#endif //DEBUG_ELEVATOR + //if very near the reachability end + VectorSubtract(reach->end, ms->origin, dir); + dist = VectorLength(dir); + if (dist < 64) + { + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //stop using this reachability + ms->reachability_time = 0; + return result; + } //end if + //get direction and distance to reachability start + VectorSubtract(reach->start, ms->origin, dir1); + if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; + dist1 = VectorNormalize(dir1); + //if the elevator isn't down + if (!MoverDown(reach)) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "elevator not down\n"); +#endif //DEBUG_ELEVATOR + dist = dist1; + VectorCopy(dir1, dir); + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //this isn't a failure... just wait till the elevator comes down + result.type = RESULTTYPE_ELEVATORUP; + result.flags |= MOVERESULT_WAITING; + return result; + } //end if + //get direction and distance to elevator bottom center + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, dir2); + if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if very close to the reachability start or + //closer to the elevator center or + //between reachability start and elevator center + if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to center\n"); +#endif //DEBUG_ELEVATOR + dist = dist2; + VectorCopy(dir2, dir); + } //end if + else //closer to the reachability start + { +#ifdef DEBUG_ELEVATOR + botimport.Print(PRT_MESSAGE, "bot moving to start\n"); +#endif //DEBUG_ELEVATOR + dist = dist1; + VectorCopy(dir1, dir); + } //end else + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 400 - (400 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + } //end else + return result; +} //end of the function BotTravel_Elevator +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t bottomcenter, bottomdir, topdir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, bottomdir); + // + VectorSubtract(reach->end, ms->origin, topdir); + // + if (fabs(bottomdir[2]) < fabs(topdir[2])) + { + VectorNormalize(bottomdir); + EA_Move(ms->client, bottomdir, 300); + } //end if + else + { + VectorNormalize(topdir); + EA_Move(ms->client, topdir, 300); + } //end else + return result; +} //end of the function BotFinishTravel_Elevator +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin) +{ + int spawnflags, modelnum; + vec3_t mins, maxs, mid, angles = {0, 0, 0}; + int num0, num1; + + modelnum = reach->facenum & 0x0000FFFF; + if (!AAS_OriginOfMoverWithModelNum(modelnum, origin)) + { + botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum); + VectorSet(start, 0, 0, 0); + VectorSet(end, 0, 0, 0); + return; + } //end if + AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL); + VectorAdd(mins, maxs, mid); + VectorScale(mid, 0.5, mid); + VectorCopy(mid, start); + VectorCopy(mid, end); + spawnflags = reach->facenum >> 16; + num0 = reach->edgenum >> 16; + if (num0 > 0x00007FFF) num0 |= 0xFFFF0000; + num1 = reach->edgenum & 0x0000FFFF; + if (num1 > 0x00007FFF) num1 |= 0xFFFF0000; + if (spawnflags & 1) + { + start[0] = num0; + end[0] = num1; + // + origin[0] += mid[0]; + origin[1] = mid[1]; + origin[2] = mid[2]; + } //end if + else if (spawnflags & 2) + { + start[1] = num0; + end[1] = num1; + // + origin[0] = mid[0]; + origin[1] += mid[1]; + origin[2] = mid[2]; + } //end else if + else + { + start[2] = num0; + end[2] = num1; + // + origin[0] = mid[0]; + origin[1] = mid[1]; + origin[2] += mid[2]; + } //end else +} //end of the function BotFuncBobStartEnd +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin; + float dist, dist1, dist2, speed; + bot_moveresult_t result; + + BotClearMoveResult(&result); + // + BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); + //if standing ontop of the func_bobbing + if (BotOnMover(ms->origin, ms->entitynum, reach)) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n"); +#endif + //if near end point of reachability + VectorSubtract(bob_origin, bob_end, dir); + if (VectorLength(dir) < 24) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n"); +#endif + //move to the end point + VectorSubtract(reach->end, ms->origin, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + if (!BotCheckBarrierJump(ms, hordir, 100)) + { + EA_Move(ms->client, hordir, 400); + } //end if + VectorCopy(hordir, result.movedir); + } //end else + //if not really close to the center of the elevator + else + { + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, hordir); + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 10) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); +#endif + //move to the center of the plat + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } //end if + } //end else + } //end if + else + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n"); +#endif + //if very near the reachability end + VectorSubtract(reach->end, ms->origin, dir); + dist = VectorLength(dir); + if (dist < 64) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to end\n"); +#endif + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + //if swimming or no barrier jump + if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //stop using this reachability + ms->reachability_time = 0; + return result; + } //end if + //get direction and distance to reachability start + VectorSubtract(reach->start, ms->origin, dir1); + if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0; + dist1 = VectorNormalize(dir1); + //if func_bobbing is Not it's start position + VectorSubtract(bob_origin, bob_start, dir); + if (VectorLength(dir) > 16) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n"); +#endif + dist = dist1; + VectorCopy(dir1, dir); + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + if (speed > 5) EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + //this isn't a failure... just wait till the func_bobbing arrives + result.type = RESULTTYPE_WAITFORFUNCBOBBING; + result.flags |= MOVERESULT_WAITING; + return result; + } //end if + //get direction and distance to func_bob bottom center + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, dir2); + if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0; + dist2 = VectorNormalize(dir2); + //if very close to the reachability start or + //closer to the elevator center or + //between reachability start and func_bobbing center + if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0) + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n"); +#endif + dist = dist2; + VectorCopy(dir2, dir); + } //end if + else //closer to the reachability start + { +#ifdef DEBUG_FUNCBOB + botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n"); +#endif + dist = dist1; + VectorCopy(dir1, dir); + } //end else + // + BotCheckBlocked(ms, dir, qfalse, &result); + // + if (dist > 60) dist = 60; + speed = 400 - (400 - 6 * dist); + // + if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50)) + { + EA_Move(ms->client, dir, speed); + } //end if + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + } //end else + return result; +} //end of the function BotTravel_FuncBobbing +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter; + bot_moveresult_t result; + float dist, speed; + + BotClearMoveResult(&result); + // + BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin); + // + VectorSubtract(bob_origin, bob_end, dir); + dist = VectorLength(dir); + //if the func_bobbing is near the end + if (dist < 16) + { + VectorSubtract(reach->end, ms->origin, hordir); + if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 60) dist = 60; + speed = 360 - (360 - 6 * dist); + // + if (speed > 5) EA_Move(ms->client, dir, speed); + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW; + } //end if + else + { + MoverBottomCenter(reach, bottomcenter); + VectorSubtract(bottomcenter, ms->origin, hordir); + if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0; + dist = VectorNormalize(hordir); + // + if (dist > 5) + { + //move to the center of the plat + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + } //end if + } //end else + return result; +} //end of the function BotFinishTravel_FuncBobbing +//=========================================================================== +// 0 no valid grapple hook visible +// 1 the grapple hook is still flying +// 2 the grapple hooked into a wall +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach) +{ + int i; + aas_entityinfo_t entinfo; + + //if the grapple hook is pulling + if (ms->moveflags & MFL_GRAPPLEPULL) + return 2; + //check for a visible grapple missile entity + //or visible grapple entity + for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i)) + { + if (AAS_EntityType(i) == (int) entitytypemissile->value) + { + AAS_EntityInfo(i, &entinfo); + if (entinfo.weapon == (int) weapindex_grapple->value) + { + return 1; + } //end if + } //end if + } //end for + //no valid grapple at all + return 0; +} //end of the function GrappleState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetGrapple(bot_movestate_t *ms) +{ + aas_reachability_t reach; + + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + //if not using the grapple hook reachability anymore + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK) + { + if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time) + { + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->grapplevisible_time = 0; +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "reset grapple\n"); +#endif //DEBUG_GRAPPLE + } //end if + } //end if +} //end of the function BotResetGrapple +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach) +{ + bot_moveresult_t result; + float dist, speed; + vec3_t dir, viewdir, org; + int state, areanum; + bsp_trace_t trace; + +#ifdef DEBUG_GRAPPLE + static int debugline; + if (!debugline) debugline = botimport.DebugLineCreate(); + botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE); +#endif //DEBUG_GRAPPLE + + BotClearMoveResult(&result); + // + if (ms->moveflags & MFL_GRAPPLERESET) + { + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + return result; + } //end if + // + if (!(int) offhandgrapple->value) + { + result.weapon = weapindex_grapple->value; + result.flags |= MOVERESULT_MOVEMENTWEAPON; + } //end if + // + if (ms->moveflags & MFL_ACTIVEGRAPPLE) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n"); +#endif //DEBUG_GRAPPLE + // + state = GrappleState(ms, reach); + // + VectorSubtract(reach->end, ms->origin, dir); + dir[2] = 0; + dist = VectorLength(dir); + //if very close to the grapple end or the grappled is hooked and + //the bot doesn't get any closer + if (state && dist < 48) + { + if (ms->lastgrappledist - dist < 1) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_ERROR, "grapple normal end\n"); +#endif //DEBUG_GRAPPLE + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->moveflags |= MFL_GRAPPLERESET; + ms->reachability_time = 0; //end the reachability + return result; + } //end if + } //end if + //if no valid grapple at all, or the grapple hooked and the bot + //isn't moving anymore + else if (!state || (state == 2 && dist > ms->lastgrappledist - 2)) + { + if (ms->grapplevisible_time < AAS_Time() - 0.4) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_ERROR, "grapple not visible\n"); +#endif //DEBUG_GRAPPLE + if (offhandgrapple->value) + EA_Command(ms->client, cmd_grappleoff->string); + ms->moveflags &= ~MFL_ACTIVEGRAPPLE; + ms->moveflags |= MFL_GRAPPLERESET; + ms->reachability_time = 0; //end the reachability + return result; + } //end if + } //end if + else + { + ms->grapplevisible_time = AAS_Time(); + } //end else + // + if (!(int) offhandgrapple->value) + { + EA_Attack(ms->client); + } //end if + //remember the current grapple distance + ms->lastgrappledist = dist; + } //end if + else + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n"); +#endif //DEBUG_GRAPPLE + // + ms->grapplevisible_time = AAS_Time(); + // + VectorSubtract(reach->start, ms->origin, dir); + if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0; + VectorAdd(ms->origin, ms->viewoffset, org); + VectorSubtract(reach->end, org, viewdir); + // + dist = VectorNormalize(dir); + Vector2Angles(viewdir, result.ideal_viewangles); + result.flags |= MOVERESULT_MOVEMENTVIEW; + // + if (dist < 5 && + fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 && + fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2) + { +#ifdef DEBUG_GRAPPLE + botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n"); +#endif //DEBUG_GRAPPLE + //check if the grapple missile path is clear + VectorAdd(ms->origin, ms->viewoffset, org); + trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID); + VectorSubtract(reach->end, trace.endpos, dir); + if (VectorLength(dir) > 16) + { + result.failure = qtrue; + return result; + } //end if + //activate the grapple + if (offhandgrapple->value) + { + EA_Command(ms->client, cmd_grappleon->string); + } //end if + else + { + EA_Attack(ms->client); + } //end else + ms->moveflags |= MFL_ACTIVEGRAPPLE; + ms->lastgrappledist = 999999; + } //end if + else + { + if (dist < 70) speed = 300 - (300 - 4 * dist); + else speed = 400; + // + BotCheckBlocked(ms, dir, qtrue, &result); + //elemantary action move in direction + EA_Move(ms->client, dir, speed); + VectorCopy(dir, result.movedir); + } //end else + //if in another area before actually grappling + areanum = AAS_PointAreaNum(ms->origin); + if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0; + } //end else + return result; +} //end of the function BotTravel_Grapple +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, speed; + bot_moveresult_t result; + + //botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n"); + BotClearMoveResult(&result); + // + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + // + dist = VectorNormalize(hordir); + //look in the movement direction + Vector2Angles(hordir, result.ideal_viewangles); + //look straight down + result.ideal_viewangles[PITCH] = 90; + // + if (dist < 5 && + fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && + fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) + { + //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + EA_Jump(ms->client); + EA_Attack(ms->client); + EA_Move(ms->client, hordir, 800); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { + if (dist > 80) dist = 80; + speed = 400 - (400 - 5 * dist); + EA_Move(ms->client, hordir, speed); + } //end else + //look in the movement direction + Vector2Angles(hordir, result.ideal_viewangles); + //look straight down + result.ideal_viewangles[PITCH] = 90; + //set the view angles directly + EA_View(ms->client, result.ideal_viewangles); + //view is important for the movment + result.flags |= MOVERESULT_MOVEMENTVIEWSET; + //select the rocket launcher + EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value); + //weapon is used for movement + result.weapon = (int) weapindex_rocketlauncher->value; + result.flags |= MOVERESULT_MOVEMENTWEAPON; + // + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_RocketJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float dist, speed; + bot_moveresult_t result; + + //botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n"); + BotClearMoveResult(&result); + // + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + // + dist = VectorNormalize(hordir); + // + if (dist < 5 && + fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 && + fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5) + { + //botimport.Print(PRT_MESSAGE, "between jump start and run start point\n"); + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //elemantary action jump + EA_Jump(ms->client); + EA_Attack(ms->client); + EA_Move(ms->client, hordir, 800); + // + ms->jumpreach = ms->lastreachnum; + } //end if + else + { + if (dist > 80) dist = 80; + speed = 400 - (400 - 5 * dist); + EA_Move(ms->client, hordir, speed); + } //end else + //look in the movement direction + Vector2Angles(hordir, result.ideal_viewangles); + //look straight down + result.ideal_viewangles[PITCH] = 90; + //set the view angles directly + EA_View(ms->client, result.ideal_viewangles); + //view is important for the movment + result.flags |= MOVERESULT_MOVEMENTVIEWSET; + //select the rocket launcher + EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value); + //weapon is used for movement + result.weapon = (int) weapindex_bfg10k->value; + result.flags |= MOVERESULT_MOVEMENTWEAPON; + // + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_BFGJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach) +{ + vec3_t hordir; + float speed; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //if not jumped yet + if (!ms->jumpreach) return result; + /* + //go straight to the reachability end + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + //always use max speed when traveling through the air + EA_Move(ms->client, hordir, 800); + VectorCopy(hordir, result.movedir); + */ + // + if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) + { + //go straight to the reachability end + VectorSubtract(reach->end, ms->origin, hordir); + hordir[2] = 0; + VectorNormalize(hordir); + speed = 400; + } //end if + // + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_WeaponJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float dist, speed; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + //first walk straight to the reachability start + hordir[0] = reach->start[0] - ms->origin[0]; + hordir[1] = reach->start[1] - ms->origin[1]; + hordir[2] = 0; + dist = VectorNormalize(hordir); + // + BotCheckBlocked(ms, hordir, qtrue, &result); + speed = 400; + //elemantary action move in direction + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotTravel_JumpPad +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach) +{ + float speed; + vec3_t hordir; + bot_moveresult_t result; + + BotClearMoveResult(&result); + if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed)) + { + hordir[0] = reach->end[0] - ms->origin[0]; + hordir[1] = reach->end[1] - ms->origin[1]; + hordir[2] = 0; + VectorNormalize(hordir); + speed = 400; + } //end if + BotCheckBlocked(ms, hordir, qtrue, &result); + //elemantary action move in direction + EA_Move(ms->client, hordir, speed); + VectorCopy(hordir, result.movedir); + // + return result; +} //end of the function BotFinishTravel_JumpPad +//=========================================================================== +// time before the reachability times out +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotReachabilityTime(aas_reachability_t *reach) +{ + switch(reach->traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: return 5; + case TRAVEL_CROUCH: return 5; + case TRAVEL_BARRIERJUMP: return 5; + case TRAVEL_LADDER: return 6; + case TRAVEL_WALKOFFLEDGE: return 5; + case TRAVEL_JUMP: return 5; + case TRAVEL_SWIM: return 5; + case TRAVEL_WATERJUMP: return 5; + case TRAVEL_TELEPORT: return 5; + case TRAVEL_ELEVATOR: return 10; + case TRAVEL_GRAPPLEHOOK: return 8; + case TRAVEL_ROCKETJUMP: return 6; + case TRAVEL_BFGJUMP: return 6; + case TRAVEL_JUMPPAD: return 10; + case TRAVEL_FUNCBOB: return 10; + default: + { + botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype); + return 8; + } //end case + } //end switch +} //end of the function BotReachabilityTime +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal) +{ + bot_moveresult_t result; + vec3_t dir; + float dist, speed; + +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1)); + //AAS_ClearShownDebugLines(); + //AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED); +#endif //DEBUG + BotClearMoveResult(&result); + //walk straight to the goal origin + dir[0] = goal->origin[0] - ms->origin[0]; + dir[1] = goal->origin[1] - ms->origin[1]; + if (ms->moveflags & MFL_SWIMMING) + { + dir[2] = goal->origin[2] - ms->origin[2]; + result.traveltype = TRAVEL_SWIM; + } //end if + else + { + dir[2] = 0; + result.traveltype = TRAVEL_WALK; + } //endif + // + dist = VectorNormalize(dir); + if (dist > 100) dist = 100; + speed = 400 - (400 - 4 * dist); + if (speed < 10) speed = 0; + // + BotCheckBlocked(ms, dir, qtrue, &result); + //elemantary action move in direction + EA_Move(ms->client, dir, speed); + VectorCopy(dir, result.movedir); + // + if (ms->moveflags & MFL_SWIMMING) + { + Vector2Angles(dir, result.ideal_viewangles); + result.flags |= MOVERESULT_SWIMVIEW; + } //end if + //if (!debugline) debugline = botimport.DebugLineCreate(); + //botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE); + // + ms->lastreachnum = 0; + ms->lastareanum = 0; + ms->lastgoalareanum = goal->areanum; + VectorCopy(ms->origin, ms->lastorigin); + // + return result; +} //end of the function BotMoveInGoalArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags) +{ + int reachnum, lastreachnum, foundjumppad, ent, resultflags; + aas_reachability_t reach, lastreach; + bot_movestate_t *ms; + //vec3_t mins, maxs, up = {0, 0, 1}; + //bsp_trace_t trace; + //static int debugline; + + + BotClearMoveResult(result); + // + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + //reset the grapple before testing if the bot has a valid goal + //because the bot could loose all it's goals when stuck to a wall + BotResetGrapple(ms); + // + if (!goal) + { +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client); +#endif //DEBUG + result->failure = qtrue; + return; + } //end if + //botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach); + //remove some of the move flags + ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER); + //set some of the move flags + //NOTE: the MFL_ONGROUND flag is also set in the higher AI + if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND; + // + if (ms->moveflags & MFL_ONGROUND) + { + int modeltype, modelnum; + + ent = BotOnTopOfEntity(ms); + + if (ent != -1) + { + modelnum = AAS_EntityModelindex(ent); + if (modelnum >= 0 && modelnum < MAX_MODELS) + { + modeltype = modeltypes[modelnum]; + + if (modeltype == MODELTYPE_FUNC_PLAT) + { + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + //if the bot is Not using the elevator + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR || + //NOTE: the face number is the plat model number + (reach.facenum & 0x0000FFFF) != modelnum) + { + reachnum = AAS_NextModelReachability(0, modelnum); + if (reachnum) + { + //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client); + AAS_ReachabilityFromNum(reachnum, &reach); + ms->lastreachnum = reachnum; + ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); + } //end if + else + { + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client); + } //end if + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end else + } //end if + result->flags |= MOVERESULT_ONTOPOF_ELEVATOR; + } //end if + else if (modeltype == MODELTYPE_FUNC_BOB) + { + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + //if the bot is Not using the func bobbing + if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB || + //NOTE: the face number is the func_bobbing model number + (reach.facenum & 0x0000FFFF) != modelnum) + { + reachnum = AAS_NextModelReachability(0, modelnum); + if (reachnum) + { + //botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client); + AAS_ReachabilityFromNum(reachnum, &reach); + ms->lastreachnum = reachnum; + ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); + } //end if + else + { + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client); + } //end if + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end else + } //end if + result->flags |= MOVERESULT_ONTOPOF_FUNCBOB; + } //end if + else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR) + { + // check if ontop of a door bridge ? + ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + // if not in a reachability area + if (!AAS_AreaReachability(ms->areanum)) + { + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end if + } //end else if + else + { + result->blocked = qtrue; + result->blockentity = ent; + result->flags |= MOVERESULT_ONTOPOFOBSTACLE; + return; + } //end else + } //end if + } //end if + } //end if + //if swimming + if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING; + //if against a ladder + if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER; + //if the bot is on the ground, swimming or against a ladder + if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER)) + { + //botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); + // + AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach); + //reachability area the bot is in + //ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR)); + ms->areanum = BotFuzzyPointReachabilityArea(ms->origin); + // + if ( !ms->areanum ) + { + result->failure = qtrue; + result->blocked = qtrue; + result->blockentity = 0; + result->type = RESULTTYPE_INSOLIDAREA; + return; + } //end if + //if the bot is in the goal area + if (ms->areanum == goal->areanum) + { + *result = BotMoveInGoalArea(ms, goal); + return; + } //end if + //assume we can use the reachability from the last frame + reachnum = ms->lastreachnum; + //if there is a last reachability + if (reachnum) + { + AAS_ReachabilityFromNum(reachnum, &reach); + //check if the reachability is still valid + if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags)) + { + reachnum = 0; + } //end if + //special grapple hook case + else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK) + { + if (ms->reachability_time < AAS_Time() || + (ms->moveflags & MFL_GRAPPLERESET)) + { + reachnum = 0; + } //end if + } //end if + //special elevator case + else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR || + (reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) + { + if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) || + (result->flags & MOVERESULT_ONTOPOF_FUNCBOB)) + { + ms->reachability_time = AAS_Time() + 5; + } //end if + //if the bot was going for an elevator and reached the reachability area + if (ms->areanum == reach.areanum || + ms->reachability_time < AAS_Time()) + { + reachnum = 0; + } //end if + } //end if + else + { +#ifdef DEBUG + if (bot_developer) + { + if (ms->reachability_time < AAS_Time()) + { + botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + /* + if (ms->lastareanum != ms->areanum) + { + botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum); + } //end if*/ + } //end if +#endif //DEBUG + //if the goal area changed or the reachability timed out + //or the area changed + if (ms->lastgoalareanum != goal->areanum || + ms->reachability_time < AAS_Time() || + ms->lastareanum != ms->areanum) + { + reachnum = 0; + //botimport.Print(PRT_MESSAGE, "area change or timeout\n"); + } //end else if + } //end else + } //end if + resultflags = 0; + //if the bot needs a new reachability + if (!reachnum) + { + //if the area has no reachability links + if (!AAS_AreaReachability(ms->areanum)) + { +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum); + } //end if +#endif //DEBUG + } //end if + //get a new reachability leading towards the goal + reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum, + ms->lastgoalareanum, ms->lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, travelflags, + ms->avoidspots, ms->numavoidspots, &resultflags); + //the area number the reachability starts in + ms->reachareanum = ms->areanum; + //reset some state variables + ms->jumpreach = 0; //for TRAVEL_JUMP + ms->moveflags &= ~MFL_GRAPPLERESET; //for TRAVEL_GRAPPLEHOOK + //if there is a reachability to the goal + if (reachnum) + { + AAS_ReachabilityFromNum(reachnum, &reach); + //set a timeout for this reachability + ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach); + // +#ifdef AVOIDREACH + //add the reachability to the reachabilities to avoid for a while + BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME); +#endif //AVOIDREACH + } //end if +#ifdef DEBUG + + else if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "goal not reachable\n"); + Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy + } //end else + if (bot_developer) + { + //if still going for the same goal + if (ms->lastgoalareanum == goal->areanum) + { + if (ms->lastareanum == reach.areanum) + { + botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n"); + } //end if + } //end if + } //end if +#endif //DEBUG + } //end else + // + ms->lastreachnum = reachnum; + ms->lastgoalareanum = goal->areanum; + ms->lastareanum = ms->areanum; + //if the bot has a reachability + if (reachnum) + { + //get the reachability from the number + AAS_ReachabilityFromNum(reachnum, &reach); + result->traveltype = reach.traveltype; + // +#ifdef DEBUG_AI_MOVE + AAS_ClearShownDebugLines(); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + AAS_ShowReachability(&reach); +#endif //DEBUG_AI_MOVE + // +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "client %d: ", ms->client); + //AAS_PrintTravelType(reach.traveltype); + //botimport.Print(PRT_MESSAGE, "\n"); +#endif //DEBUG + switch(reach.traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break; + case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break; + case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break; + case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; + case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break; + case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break; + case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; + case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break; + case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break; + case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break; + case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; + case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break; + case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break; + case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break; + case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break; + default: + { + botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); + break; + } //end case + } //end switch + result->traveltype = reach.traveltype; + result->flags |= resultflags; + } //end if + else + { + result->failure = qtrue; + result->flags |= resultflags; + Com_Memset(&reach, 0, sizeof(aas_reachability_t)); + } //end else +#ifdef DEBUG + if (bot_developer) + { + if (result->failure) + { + botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + } //end if +#endif //DEBUG + } //end if + else + { + int i, numareas, areas[16]; + vec3_t end; + + //special handling of jump pads when the bot uses a jump pad without knowing it + foundjumppad = qfalse; + VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end); + numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16); + for (i = numareas-1; i >= 0; i--) + { + if (AAS_AreaJumpPad(areas[i])) + { + //botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]); + foundjumppad = qtrue; + lastreachnum = BotGetReachabilityToGoal(end, areas[i], + ms->lastgoalareanum, ms->lastareanum, + ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries, + goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL); + if (lastreachnum) + { + ms->lastreachnum = lastreachnum; + ms->lastareanum = areas[i]; + //botimport.Print(PRT_MESSAGE, "found jumppad reachability\n"); + break; + } //end if + else + { + for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum; + lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum)) + { + //get the reachability from the number + AAS_ReachabilityFromNum(lastreachnum, &reach); + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) + { + ms->lastreachnum = lastreachnum; + ms->lastareanum = areas[i]; + //botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n"); + break; + } //end if + } //end for + if (lastreachnum) break; + } //end else + } //end if + } //end for + if (bot_developer) + { + //if a jumppad is found with the trace but no reachability is found + if (foundjumppad && !ms->lastreachnum) + { + botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client); + } //end if + } //end if + // + if (ms->lastreachnum) + { + //botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1)); + AAS_ReachabilityFromNum(ms->lastreachnum, &reach); + result->traveltype = reach.traveltype; +#ifdef DEBUG + //botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client); + //AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + //botimport.Print(PRT_MESSAGE, "\n"); +#endif //DEBUG + // + switch(reach.traveltype & TRAVELTYPE_MASK) + { + case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break; + case TRAVEL_CROUCH: /*do nothing*/ break; + case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break; + case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break; + case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break; + case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break; + case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break; + case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break; + case TRAVEL_TELEPORT: /*do nothing*/ break; + case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break; + case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break; + case TRAVEL_ROCKETJUMP: + case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break; + case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break; + case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break; + default: + { + botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK)); + break; + } //end case + } //end switch + result->traveltype = reach.traveltype; +#ifdef DEBUG + if (bot_developer) + { + if (result->failure) + { + botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client); + AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK); + botimport.Print(PRT_MESSAGE, "\n"); + } //end if + } //end if +#endif //DEBUG + } //end if + } //end else + //FIXME: is it right to do this here? + if (result->blocked) ms->reachability_time -= 10 * ms->thinktime; + //copy the last origin + VectorCopy(ms->origin, ms->lastorigin); + //return the movement result + return; +} //end of the function BotMoveToGoal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetAvoidReach(int movestate) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int)); + Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float)); + Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int)); +} //end of the function BotResetAvoidReach +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetLastAvoidReach(int movestate) +{ + int i, latest; + float latesttime; + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + latesttime = 0; + latest = 0; + for (i = 0; i < MAX_AVOIDREACH; i++) + { + if (ms->avoidreachtimes[i] > latesttime) + { + latesttime = ms->avoidreachtimes[i]; + latest = i; + } //end if + } //end for + if (latesttime) + { + ms->avoidreachtimes[latest] = 0; + if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--; + } //end if +} //end of the function BotResetLastAvoidReach +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetMoveState(int movestate) +{ + bot_movestate_t *ms; + + ms = BotMoveStateFromHandle(movestate); + if (!ms) return; + Com_Memset(ms, 0, sizeof(bot_movestate_t)); +} //end of the function BotResetMoveState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupMoveAI(void) +{ + BotSetBrushModelTypes(); + sv_maxstep = LibVar("sv_step", "18"); + sv_maxbarrier = LibVar("sv_maxbarrier", "32"); + sv_gravity = LibVar("sv_gravity", "800"); + weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5"); + weapindex_bfg10k = LibVar("weapindex_bfg10k", "9"); + weapindex_grapple = LibVar("weapindex_grapple", "10"); + entitytypemissile = LibVar("entitytypemissile", "3"); + offhandgrapple = LibVar("offhandgrapple", "0"); + cmd_grappleon = LibVar("cmd_grappleon", "grappleon"); + cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff"); + return BLERR_NOERROR; +} //end of the function BotSetupMoveAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownMoveAI(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (botmovestates[i]) + { + FreeMemory(botmovestates[i]); + botmovestates[i] = NULL; + } //end if + } //end for +} //end of the function BotShutdownMoveAI + + diff --git a/code/botlib/be_ai_weap.c b/code/botlib/be_ai_weap.c index 435292d..22d989e 100755 --- a/code/botlib/be_ai_weap.c +++ b/code/botlib/be_ai_weap.c @@ -1,543 +1,543 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_weap.c
- *
- * desc: weapon AI
- *
- * $Archive: /MissionPack/code/botlib/be_ai_weap.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_libvar.h"
-#include "l_log.h"
-#include "l_memory.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_ai_weight.h" //fuzzy weights
-#include "../game/be_ai_weap.h"
-
-//#define DEBUG_AI_WEAP
-
-//structure field offsets
-#define WEAPON_OFS(x) (int)&(((weaponinfo_t *)0)->x)
-#define PROJECTILE_OFS(x) (int)&(((projectileinfo_t *)0)->x)
-
-//weapon definition // bk001212 - static
-static fielddef_t weaponinfo_fields[] =
-{
-{"number", WEAPON_OFS(number), FT_INT}, //weapon number
-{"name", WEAPON_OFS(name), FT_STRING}, //name of the weapon
-{"level", WEAPON_OFS(level), FT_INT},
-{"model", WEAPON_OFS(model), FT_STRING}, //model of the weapon
-{"weaponindex", WEAPON_OFS(weaponindex), FT_INT}, //index of weapon in inventory
-{"flags", WEAPON_OFS(flags), FT_INT}, //special flags
-{"projectile", WEAPON_OFS(projectile), FT_STRING}, //projectile used by the weapon
-{"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT}, //number of projectiles
-{"hspread", WEAPON_OFS(hspread), FT_FLOAT}, //horizontal spread of projectiles (degrees from middle)
-{"vspread", WEAPON_OFS(vspread), FT_FLOAT}, //vertical spread of projectiles (degrees from middle)
-{"speed", WEAPON_OFS(speed), FT_FLOAT}, //speed of the projectile (0 = instant hit)
-{"acceleration", WEAPON_OFS(acceleration), FT_FLOAT}, //"acceleration" * time (in seconds) + "speed" = projectile speed
-{"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3}, //amount of recoil the player gets from the weapon
-{"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3}, //projectile start offset relative to eye and view angles
-{"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles
-{"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets
-{"ammoamount", WEAPON_OFS(ammoamount), FT_INT}, //ammo amount used per shot
-{"ammoindex", WEAPON_OFS(ammoindex), FT_INT}, //index of ammo in inventory
-{"activate", WEAPON_OFS(activate), FT_FLOAT}, //time it takes to select the weapon
-{"reload", WEAPON_OFS(reload), FT_FLOAT}, //time it takes to reload the weapon
-{"spinup", WEAPON_OFS(spinup), FT_FLOAT}, //time it takes before first shot
-{"spindown", WEAPON_OFS(spindown), FT_FLOAT}, //time it takes before weapon stops firing
-{NULL, 0, 0, 0}
-};
-
-//projectile definition
-static fielddef_t projectileinfo_fields[] =
-{
-{"name", PROJECTILE_OFS(name), FT_STRING}, //name of the projectile
-{"model", WEAPON_OFS(model), FT_STRING}, //model of the projectile
-{"flags", PROJECTILE_OFS(flags), FT_INT}, //special flags
-{"gravity", PROJECTILE_OFS(gravity), FT_FLOAT}, //amount of gravity applied to the projectile [0,1]
-{"damage", PROJECTILE_OFS(damage), FT_INT}, //damage of the projectile
-{"radius", PROJECTILE_OFS(radius), FT_FLOAT}, //radius of damage
-{"visdamage", PROJECTILE_OFS(visdamage), FT_INT}, //damage of the projectile to visible entities
-{"damagetype", PROJECTILE_OFS(damagetype), FT_INT}, //type of damage (combination of the DAMAGETYPE_? flags)
-{"healthinc", PROJECTILE_OFS(healthinc), FT_INT}, //health increase the owner gets
-{"push", PROJECTILE_OFS(push), FT_FLOAT}, //amount a player is pushed away from the projectile impact
-{"detonation", PROJECTILE_OFS(detonation), FT_FLOAT}, //time before projectile explodes after fire pressed
-{"bounce", PROJECTILE_OFS(bounce), FT_FLOAT}, //amount the projectile bounces
-{"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT}, //amount the bounce decreases per bounce
-{"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT}, //minimum bounce value before bouncing stops
-//recurive projectile definition??
-{NULL, 0, 0, 0}
-};
-
-static structdef_t weaponinfo_struct =
-{
- sizeof(weaponinfo_t), weaponinfo_fields
-};
-static structdef_t projectileinfo_struct =
-{
- sizeof(projectileinfo_t), projectileinfo_fields
-};
-
-//weapon configuration: set of weapons with projectiles
-typedef struct weaponconfig_s
-{
- int numweapons;
- int numprojectiles;
- projectileinfo_t *projectileinfo;
- weaponinfo_t *weaponinfo;
-} weaponconfig_t;
-
-//the bot weapon state
-typedef struct bot_weaponstate_s
-{
- struct weightconfig_s *weaponweightconfig; //weapon weight configuration
- int *weaponweightindex; //weapon weight index
-} bot_weaponstate_t;
-
-static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1];
-static weaponconfig_t *weaponconfig;
-
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-int BotValidWeaponNumber(int weaponnum)
-{
- if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons)
- {
- botimport.Print(PRT_ERROR, "weapon number out of range\n");
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function BotValidWeaponNumber
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-bot_weaponstate_t *BotWeaponStateFromHandle(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
- return NULL;
- } //end if
- if (!botweaponstates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
- return NULL;
- } //end if
- return botweaponstates[handle];
-} //end of the function BotWeaponStateFromHandle
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef DEBUG_AI_WEAP
-void DumpWeaponConfig(weaponconfig_t *wc)
-{
- FILE *fp;
- int i;
-
- fp = Log_FileStruct();
- if (!fp) return;
- for (i = 0; i < wc->numprojectiles; i++)
- {
- WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]);
- Log_Flush();
- } //end for
- for (i = 0; i < wc->numweapons; i++)
- {
- WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]);
- Log_Flush();
- } //end for
-} //end of the function DumpWeaponConfig
-#endif //DEBUG_AI_WEAP
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-weaponconfig_t *LoadWeaponConfig(char *filename)
-{
- int max_weaponinfo, max_projectileinfo;
- token_t token;
- char path[MAX_PATH];
- int i, j;
- source_t *source;
- weaponconfig_t *wc;
- weaponinfo_t weaponinfo;
-
- max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32");
- if (max_weaponinfo < 0)
- {
- botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo);
- max_weaponinfo = 32;
- LibVarSet("max_weaponinfo", "32");
- } //end if
- max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32");
- if (max_projectileinfo < 0)
- {
- botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo);
- max_projectileinfo = 32;
- LibVarSet("max_projectileinfo", "32");
- } //end if
- strncpy(path, filename, MAX_PATH);
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(path);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", path);
- return NULL;
- } //end if
- //initialize weapon config
- wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) +
- max_weaponinfo * sizeof(weaponinfo_t) +
- max_projectileinfo * sizeof(projectileinfo_t));
- wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t));
- wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo +
- max_weaponinfo * sizeof(weaponinfo_t));
- wc->numweapons = max_weaponinfo;
- wc->numprojectiles = 0;
- //parse the source file
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "weaponinfo"))
- {
- Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t));
- if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo))
- {
- FreeMemory(wc);
- FreeSource(source);
- return NULL;
- } //end if
- if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo)
- {
- botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path);
- FreeMemory(wc);
- FreeSource(source);
- return NULL;
- } //end if
- Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t));
- wc->weaponinfo[weaponinfo.number].valid = qtrue;
- } //end if
- else if (!strcmp(token.string, "projectileinfo"))
- {
- if (wc->numprojectiles >= max_projectileinfo)
- {
- botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path);
- FreeMemory(wc);
- FreeSource(source);
- return NULL;
- } //end if
- Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t));
- if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles]))
- {
- FreeMemory(wc);
- FreeSource(source);
- return NULL;
- } //end if
- wc->numprojectiles++;
- } //end if
- else
- {
- botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path);
- FreeMemory(wc);
- FreeSource(source);
- return NULL;
- } //end else
- } //end while
- FreeSource(source);
- //fix up weapons
- for (i = 0; i < wc->numweapons; i++)
- {
- if (!wc->weaponinfo[i].valid) continue;
- if (!wc->weaponinfo[i].name[0])
- {
- botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path);
- FreeMemory(wc);
- return NULL;
- } //end if
- if (!wc->weaponinfo[i].projectile[0])
- {
- botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path);
- FreeMemory(wc);
- return NULL;
- } //end if
- //find the projectile info and copy it to the weapon info
- for (j = 0; j < wc->numprojectiles; j++)
- {
- if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile))
- {
- Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t));
- break;
- } //end if
- } //end for
- if (j == wc->numprojectiles)
- {
- botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path);
- FreeMemory(wc);
- return NULL;
- } //end if
- } //end for
- if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n");
- botimport.Print(PRT_MESSAGE, "loaded %s\n", path);
- return wc;
-} //end of the function LoadWeaponConfig
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc)
-{
- int *index, i;
-
- //initialize item weight index
- index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons);
-
- for (i = 0; i < wc->numweapons; i++)
- {
- index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name);
- } //end for
- return index;
-} //end of the function WeaponWeightIndex
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotFreeWeaponWeights(int weaponstate)
-{
- bot_weaponstate_t *ws;
-
- ws = BotWeaponStateFromHandle(weaponstate);
- if (!ws) return;
- if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig);
- if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex);
-} //end of the function BotFreeWeaponWeights
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotLoadWeaponWeights(int weaponstate, char *filename)
-{
- bot_weaponstate_t *ws;
-
- ws = BotWeaponStateFromHandle(weaponstate);
- if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS;
- BotFreeWeaponWeights(weaponstate);
- //
- ws->weaponweightconfig = ReadWeightConfig(filename);
- if (!ws->weaponweightconfig)
- {
- botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename);
- return BLERR_CANNOTLOADWEAPONWEIGHTS;
- } //end if
- if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG;
- ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig);
- return BLERR_NOERROR;
-} //end of the function BotLoadWeaponWeights
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo)
-{
- bot_weaponstate_t *ws;
-
- if (!BotValidWeaponNumber(weapon)) return;
- ws = BotWeaponStateFromHandle(weaponstate);
- if (!ws) return;
- if (!weaponconfig) return;
- Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t));
-} //end of the function BotGetWeaponInfo
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotChooseBestFightWeapon(int weaponstate, int *inventory)
-{
- int i, index, bestweapon;
- float weight, bestweight;
- weaponconfig_t *wc;
- bot_weaponstate_t *ws;
-
- ws = BotWeaponStateFromHandle(weaponstate);
- if (!ws) return 0;
- wc = weaponconfig;
- if (!weaponconfig) return 0;
-
- //if the bot has no weapon weight configuration
- if (!ws->weaponweightconfig) return 0;
-
- bestweight = 0;
- bestweapon = 0;
- for (i = 0; i < wc->numweapons; i++)
- {
- if (!wc->weaponinfo[i].valid) continue;
- index = ws->weaponweightindex[i];
- if (index < 0) continue;
- weight = FuzzyWeight(inventory, ws->weaponweightconfig, index);
- if (weight > bestweight)
- {
- bestweight = weight;
- bestweapon = i;
- } //end if
- } //end for
- return bestweapon;
-} //end of the function BotChooseBestFightWeapon
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotResetWeaponState(int weaponstate)
-{
- struct weightconfig_s *weaponweightconfig;
- int *weaponweightindex;
- bot_weaponstate_t *ws;
-
- ws = BotWeaponStateFromHandle(weaponstate);
- if (!ws) return;
- weaponweightconfig = ws->weaponweightconfig;
- weaponweightindex = ws->weaponweightindex;
-
- //Com_Memset(ws, 0, sizeof(bot_weaponstate_t));
- ws->weaponweightconfig = weaponweightconfig;
- ws->weaponweightindex = weaponweightindex;
-} //end of the function BotResetWeaponState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-int BotAllocWeaponState(void)
-{
- int i;
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (!botweaponstates[i])
- {
- botweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t));
- return i;
- } //end if
- } //end for
- return 0;
-} //end of the function BotAllocWeaponState
-//========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//========================================================================
-void BotFreeWeaponState(int handle)
-{
- if (handle <= 0 || handle > MAX_CLIENTS)
- {
- botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
- return;
- } //end if
- if (!botweaponstates[handle])
- {
- botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
- return;
- } //end if
- BotFreeWeaponWeights(handle);
- FreeMemory(botweaponstates[handle]);
- botweaponstates[handle] = NULL;
-} //end of the function BotFreeWeaponState
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int BotSetupWeaponAI(void)
-{
- char *file;
-
- file = LibVarString("weaponconfig", "weapons.c");
- weaponconfig = LoadWeaponConfig(file);
- if (!weaponconfig)
- {
- botimport.Print(PRT_FATAL, "couldn't load the weapon config\n");
- return BLERR_CANNOTLOADWEAPONCONFIG;
- } //end if
-
-#ifdef DEBUG_AI_WEAP
- DumpWeaponConfig(weaponconfig);
-#endif //DEBUG_AI_WEAP
- //
- return BLERR_NOERROR;
-} //end of the function BotSetupWeaponAI
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownWeaponAI(void)
-{
- int i;
-
- if (weaponconfig) FreeMemory(weaponconfig);
- weaponconfig = NULL;
-
- for (i = 1; i <= MAX_CLIENTS; i++)
- {
- if (botweaponstates[i])
- {
- BotFreeWeaponState(i);
- } //end if
- } //end for
-} //end of the function BotShutdownWeaponAI
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weap.c + * + * desc: weapon AI + * + * $Archive: /MissionPack/code/botlib/be_ai_weap.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_libvar.h" +#include "l_log.h" +#include "l_memory.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" //fuzzy weights +#include "../game/be_ai_weap.h" + +//#define DEBUG_AI_WEAP + +//structure field offsets +#define WEAPON_OFS(x) (int)&(((weaponinfo_t *)0)->x) +#define PROJECTILE_OFS(x) (int)&(((projectileinfo_t *)0)->x) + +//weapon definition // bk001212 - static +static fielddef_t weaponinfo_fields[] = +{ +{"number", WEAPON_OFS(number), FT_INT}, //weapon number +{"name", WEAPON_OFS(name), FT_STRING}, //name of the weapon +{"level", WEAPON_OFS(level), FT_INT}, +{"model", WEAPON_OFS(model), FT_STRING}, //model of the weapon +{"weaponindex", WEAPON_OFS(weaponindex), FT_INT}, //index of weapon in inventory +{"flags", WEAPON_OFS(flags), FT_INT}, //special flags +{"projectile", WEAPON_OFS(projectile), FT_STRING}, //projectile used by the weapon +{"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT}, //number of projectiles +{"hspread", WEAPON_OFS(hspread), FT_FLOAT}, //horizontal spread of projectiles (degrees from middle) +{"vspread", WEAPON_OFS(vspread), FT_FLOAT}, //vertical spread of projectiles (degrees from middle) +{"speed", WEAPON_OFS(speed), FT_FLOAT}, //speed of the projectile (0 = instant hit) +{"acceleration", WEAPON_OFS(acceleration), FT_FLOAT}, //"acceleration" * time (in seconds) + "speed" = projectile speed +{"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3}, //amount of recoil the player gets from the weapon +{"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3}, //projectile start offset relative to eye and view angles +{"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles +{"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets +{"ammoamount", WEAPON_OFS(ammoamount), FT_INT}, //ammo amount used per shot +{"ammoindex", WEAPON_OFS(ammoindex), FT_INT}, //index of ammo in inventory +{"activate", WEAPON_OFS(activate), FT_FLOAT}, //time it takes to select the weapon +{"reload", WEAPON_OFS(reload), FT_FLOAT}, //time it takes to reload the weapon +{"spinup", WEAPON_OFS(spinup), FT_FLOAT}, //time it takes before first shot +{"spindown", WEAPON_OFS(spindown), FT_FLOAT}, //time it takes before weapon stops firing +{NULL, 0, 0, 0} +}; + +//projectile definition +static fielddef_t projectileinfo_fields[] = +{ +{"name", PROJECTILE_OFS(name), FT_STRING}, //name of the projectile +{"model", WEAPON_OFS(model), FT_STRING}, //model of the projectile +{"flags", PROJECTILE_OFS(flags), FT_INT}, //special flags +{"gravity", PROJECTILE_OFS(gravity), FT_FLOAT}, //amount of gravity applied to the projectile [0,1] +{"damage", PROJECTILE_OFS(damage), FT_INT}, //damage of the projectile +{"radius", PROJECTILE_OFS(radius), FT_FLOAT}, //radius of damage +{"visdamage", PROJECTILE_OFS(visdamage), FT_INT}, //damage of the projectile to visible entities +{"damagetype", PROJECTILE_OFS(damagetype), FT_INT}, //type of damage (combination of the DAMAGETYPE_? flags) +{"healthinc", PROJECTILE_OFS(healthinc), FT_INT}, //health increase the owner gets +{"push", PROJECTILE_OFS(push), FT_FLOAT}, //amount a player is pushed away from the projectile impact +{"detonation", PROJECTILE_OFS(detonation), FT_FLOAT}, //time before projectile explodes after fire pressed +{"bounce", PROJECTILE_OFS(bounce), FT_FLOAT}, //amount the projectile bounces +{"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT}, //amount the bounce decreases per bounce +{"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT}, //minimum bounce value before bouncing stops +//recurive projectile definition?? +{NULL, 0, 0, 0} +}; + +static structdef_t weaponinfo_struct = +{ + sizeof(weaponinfo_t), weaponinfo_fields +}; +static structdef_t projectileinfo_struct = +{ + sizeof(projectileinfo_t), projectileinfo_fields +}; + +//weapon configuration: set of weapons with projectiles +typedef struct weaponconfig_s +{ + int numweapons; + int numprojectiles; + projectileinfo_t *projectileinfo; + weaponinfo_t *weaponinfo; +} weaponconfig_t; + +//the bot weapon state +typedef struct bot_weaponstate_s +{ + struct weightconfig_s *weaponweightconfig; //weapon weight configuration + int *weaponweightindex; //weapon weight index +} bot_weaponstate_t; + +static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1]; +static weaponconfig_t *weaponconfig; + +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotValidWeaponNumber(int weaponnum) +{ + if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons) + { + botimport.Print(PRT_ERROR, "weapon number out of range\n"); + return qfalse; + } //end if + return qtrue; +} //end of the function BotValidWeaponNumber +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +bot_weaponstate_t *BotWeaponStateFromHandle(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return NULL; + } //end if + if (!botweaponstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return NULL; + } //end if + return botweaponstates[handle]; +} //end of the function BotWeaponStateFromHandle +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef DEBUG_AI_WEAP +void DumpWeaponConfig(weaponconfig_t *wc) +{ + FILE *fp; + int i; + + fp = Log_FileStruct(); + if (!fp) return; + for (i = 0; i < wc->numprojectiles; i++) + { + WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]); + Log_Flush(); + } //end for + for (i = 0; i < wc->numweapons; i++) + { + WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]); + Log_Flush(); + } //end for +} //end of the function DumpWeaponConfig +#endif //DEBUG_AI_WEAP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +weaponconfig_t *LoadWeaponConfig(char *filename) +{ + int max_weaponinfo, max_projectileinfo; + token_t token; + char path[MAX_PATH]; + int i, j; + source_t *source; + weaponconfig_t *wc; + weaponinfo_t weaponinfo; + + max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32"); + if (max_weaponinfo < 0) + { + botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo); + max_weaponinfo = 32; + LibVarSet("max_weaponinfo", "32"); + } //end if + max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32"); + if (max_projectileinfo < 0) + { + botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo); + max_projectileinfo = 32; + LibVarSet("max_projectileinfo", "32"); + } //end if + strncpy(path, filename, MAX_PATH); + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(path); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", path); + return NULL; + } //end if + //initialize weapon config + wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) + + max_weaponinfo * sizeof(weaponinfo_t) + + max_projectileinfo * sizeof(projectileinfo_t)); + wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t)); + wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo + + max_weaponinfo * sizeof(weaponinfo_t)); + wc->numweapons = max_weaponinfo; + wc->numprojectiles = 0; + //parse the source file + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "weaponinfo")) + { + Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t)); + if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo)) + { + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo) + { + botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path); + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t)); + wc->weaponinfo[weaponinfo.number].valid = qtrue; + } //end if + else if (!strcmp(token.string, "projectileinfo")) + { + if (wc->numprojectiles >= max_projectileinfo) + { + botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path); + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t)); + if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles])) + { + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end if + wc->numprojectiles++; + } //end if + else + { + botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path); + FreeMemory(wc); + FreeSource(source); + return NULL; + } //end else + } //end while + FreeSource(source); + //fix up weapons + for (i = 0; i < wc->numweapons; i++) + { + if (!wc->weaponinfo[i].valid) continue; + if (!wc->weaponinfo[i].name[0]) + { + botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path); + FreeMemory(wc); + return NULL; + } //end if + if (!wc->weaponinfo[i].projectile[0]) + { + botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path); + FreeMemory(wc); + return NULL; + } //end if + //find the projectile info and copy it to the weapon info + for (j = 0; j < wc->numprojectiles; j++) + { + if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile)) + { + Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t)); + break; + } //end if + } //end for + if (j == wc->numprojectiles) + { + botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path); + FreeMemory(wc); + return NULL; + } //end if + } //end for + if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n"); + botimport.Print(PRT_MESSAGE, "loaded %s\n", path); + return wc; +} //end of the function LoadWeaponConfig +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc) +{ + int *index, i; + + //initialize item weight index + index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons); + + for (i = 0; i < wc->numweapons; i++) + { + index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name); + } //end for + return index; +} //end of the function WeaponWeightIndex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeWeaponWeights(int weaponstate) +{ + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return; + if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig); + if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex); +} //end of the function BotFreeWeaponWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotLoadWeaponWeights(int weaponstate, char *filename) +{ + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS; + BotFreeWeaponWeights(weaponstate); + // + ws->weaponweightconfig = ReadWeightConfig(filename); + if (!ws->weaponweightconfig) + { + botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename); + return BLERR_CANNOTLOADWEAPONWEIGHTS; + } //end if + if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG; + ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig); + return BLERR_NOERROR; +} //end of the function BotLoadWeaponWeights +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo) +{ + bot_weaponstate_t *ws; + + if (!BotValidWeaponNumber(weapon)) return; + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return; + if (!weaponconfig) return; + Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t)); +} //end of the function BotGetWeaponInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotChooseBestFightWeapon(int weaponstate, int *inventory) +{ + int i, index, bestweapon; + float weight, bestweight; + weaponconfig_t *wc; + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return 0; + wc = weaponconfig; + if (!weaponconfig) return 0; + + //if the bot has no weapon weight configuration + if (!ws->weaponweightconfig) return 0; + + bestweight = 0; + bestweapon = 0; + for (i = 0; i < wc->numweapons; i++) + { + if (!wc->weaponinfo[i].valid) continue; + index = ws->weaponweightindex[i]; + if (index < 0) continue; + weight = FuzzyWeight(inventory, ws->weaponweightconfig, index); + if (weight > bestweight) + { + bestweight = weight; + bestweapon = i; + } //end if + } //end for + return bestweapon; +} //end of the function BotChooseBestFightWeapon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotResetWeaponState(int weaponstate) +{ + struct weightconfig_s *weaponweightconfig; + int *weaponweightindex; + bot_weaponstate_t *ws; + + ws = BotWeaponStateFromHandle(weaponstate); + if (!ws) return; + weaponweightconfig = ws->weaponweightconfig; + weaponweightindex = ws->weaponweightindex; + + //Com_Memset(ws, 0, sizeof(bot_weaponstate_t)); + ws->weaponweightconfig = weaponweightconfig; + ws->weaponweightindex = weaponweightindex; +} //end of the function BotResetWeaponState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +int BotAllocWeaponState(void) +{ + int i; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (!botweaponstates[i]) + { + botweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t)); + return i; + } //end if + } //end for + return 0; +} //end of the function BotAllocWeaponState +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void BotFreeWeaponState(int handle) +{ + if (handle <= 0 || handle > MAX_CLIENTS) + { + botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle); + return; + } //end if + if (!botweaponstates[handle]) + { + botimport.Print(PRT_FATAL, "invalid move state %d\n", handle); + return; + } //end if + BotFreeWeaponWeights(handle); + FreeMemory(botweaponstates[handle]); + botweaponstates[handle] = NULL; +} //end of the function BotFreeWeaponState +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotSetupWeaponAI(void) +{ + char *file; + + file = LibVarString("weaponconfig", "weapons.c"); + weaponconfig = LoadWeaponConfig(file); + if (!weaponconfig) + { + botimport.Print(PRT_FATAL, "couldn't load the weapon config\n"); + return BLERR_CANNOTLOADWEAPONCONFIG; + } //end if + +#ifdef DEBUG_AI_WEAP + DumpWeaponConfig(weaponconfig); +#endif //DEBUG_AI_WEAP + // + return BLERR_NOERROR; +} //end of the function BotSetupWeaponAI +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownWeaponAI(void) +{ + int i; + + if (weaponconfig) FreeMemory(weaponconfig); + weaponconfig = NULL; + + for (i = 1; i <= MAX_CLIENTS; i++) + { + if (botweaponstates[i]) + { + BotFreeWeaponState(i); + } //end if + } //end for +} //end of the function BotShutdownWeaponAI + diff --git a/code/botlib/be_ai_weight.c b/code/botlib/be_ai_weight.c index c501593..9212519 100755 --- a/code/botlib/be_ai_weight.c +++ b/code/botlib/be_ai_weight.c @@ -1,912 +1,912 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_weight.c
- *
- * desc: fuzzy logic
- *
- * $Archive: /MissionPack/code/botlib/be_ai_weight.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_utils.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_libvar.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_interface.h"
-#include "be_ai_weight.h"
-
-#define MAX_INVENTORYVALUE 999999
-#define EVALUATERECURSIVELY
-
-#define MAX_WEIGHT_FILES 128
-weightconfig_t *weightFileList[MAX_WEIGHT_FILES];
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int ReadValue(source_t *source, float *value)
-{
- token_t token;
-
- if (!PC_ExpectAnyToken(source, &token)) return qfalse;
- if (!strcmp(token.string, "-"))
- {
- SourceWarning(source, "negative value set to zero\n");
- if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse;
- } //end if
- if (token.type != TT_NUMBER)
- {
- SourceError(source, "invalid return value %s\n", token.string);
- return qfalse;
- } //end if
- *value = token.floatvalue;
- return qtrue;
-} //end of the function ReadValue
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs)
-{
- if (PC_CheckTokenString(source, "balance"))
- {
- fs->type = WT_BALANCE;
- if (!PC_ExpectTokenString(source, "(")) return qfalse;
- if (!ReadValue(source, &fs->weight)) return qfalse;
- if (!PC_ExpectTokenString(source, ",")) return qfalse;
- if (!ReadValue(source, &fs->minweight)) return qfalse;
- if (!PC_ExpectTokenString(source, ",")) return qfalse;
- if (!ReadValue(source, &fs->maxweight)) return qfalse;
- if (!PC_ExpectTokenString(source, ")")) return qfalse;
- } //end if
- else
- {
- fs->type = 0;
- if (!ReadValue(source, &fs->weight)) return qfalse;
- fs->minweight = fs->weight;
- fs->maxweight = fs->weight;
- } //end if
- if (!PC_ExpectTokenString(source, ";")) return qfalse;
- return qtrue;
-} //end of the function ReadFuzzyWeight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeFuzzySeperators_r(fuzzyseperator_t *fs)
-{
- if (!fs) return;
- if (fs->child) FreeFuzzySeperators_r(fs->child);
- if (fs->next) FreeFuzzySeperators_r(fs->next);
- FreeMemory(fs);
-} //end of the function FreeFuzzySeperators
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeWeightConfig2(weightconfig_t *config)
-{
- int i;
-
- for (i = 0; i < config->numweights; i++)
- {
- FreeFuzzySeperators_r(config->weights[i].firstseperator);
- if (config->weights[i].name) FreeMemory(config->weights[i].name);
- } //end for
- FreeMemory(config);
-} //end of the function FreeWeightConfig2
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeWeightConfig(weightconfig_t *config)
-{
- if (!LibVarGetValue("bot_reloadcharacters")) return;
- FreeWeightConfig2(config);
-} //end of the function FreeWeightConfig
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source)
-{
- int newindent, index, def, founddefault;
- token_t token;
- fuzzyseperator_t *fs, *lastfs, *firstfs;
-
- founddefault = qfalse;
- firstfs = NULL;
- lastfs = NULL;
- if (!PC_ExpectTokenString(source, "(")) return NULL;
- if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL;
- index = token.intvalue;
- if (!PC_ExpectTokenString(source, ")")) return NULL;
- if (!PC_ExpectTokenString(source, "{")) return NULL;
- if (!PC_ExpectAnyToken(source, &token)) return NULL;
- do
- {
- def = !strcmp(token.string, "default");
- if (def || !strcmp(token.string, "case"))
- {
- fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
- fs->index = index;
- if (lastfs) lastfs->next = fs;
- else firstfs = fs;
- lastfs = fs;
- if (def)
- {
- if (founddefault)
- {
- SourceError(source, "switch already has a default\n");
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- fs->value = MAX_INVENTORYVALUE;
- founddefault = qtrue;
- } //end if
- else
- {
- if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- fs->value = token.intvalue;
- } //end else
- if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token))
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- newindent = qfalse;
- if (!strcmp(token.string, "{"))
- {
- newindent = qtrue;
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- } //end if
- if (!strcmp(token.string, "return"))
- {
- if (!ReadFuzzyWeight(source, fs))
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- } //end if
- else if (!strcmp(token.string, "switch"))
- {
- fs->child = ReadFuzzySeperators_r(source);
- if (!fs->child)
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- } //end else if
- else
- {
- SourceError(source, "invalid name %s\n", token.string);
- return NULL;
- } //end else
- if (newindent)
- {
- if (!PC_ExpectTokenString(source, "}"))
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- } //end if
- } //end if
- else
- {
- FreeFuzzySeperators_r(firstfs);
- SourceError(source, "invalid name %s\n", token.string);
- return NULL;
- } //end else
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeFuzzySeperators_r(firstfs);
- return NULL;
- } //end if
- } while(strcmp(token.string, "}"));
- //
- if (!founddefault)
- {
- SourceWarning(source, "switch without default\n");
- fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
- fs->index = index;
- fs->value = MAX_INVENTORYVALUE;
- fs->weight = 0;
- fs->next = NULL;
- fs->child = NULL;
- if (lastfs) lastfs->next = fs;
- else firstfs = fs;
- lastfs = fs;
- } //end if
- //
- return firstfs;
-} //end of the function ReadFuzzySeperators_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-weightconfig_t *ReadWeightConfig(char *filename)
-{
- int newindent, avail = 0, n;
- token_t token;
- source_t *source;
- fuzzyseperator_t *fs;
- weightconfig_t *config = NULL;
-#ifdef DEBUG
- int starttime;
-
- starttime = Sys_MilliSeconds();
-#endif //DEBUG
-
- if (!LibVarGetValue("bot_reloadcharacters"))
- {
- avail = -1;
- for( n = 0; n < MAX_WEIGHT_FILES; n++ )
- {
- config = weightFileList[n];
- if( !config )
- {
- if( avail == -1 )
- {
- avail = n;
- } //end if
- continue;
- } //end if
- if( strcmp( filename, config->filename ) == 0 )
- {
- //botimport.Print( PRT_MESSAGE, "retained %s\n", filename );
- return config;
- } //end if
- } //end for
-
- if( avail == -1 )
- {
- botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename );
- return NULL;
- } //end if
- } //end if
-
- PC_SetBaseFolder(BOTFILESBASEFOLDER);
- source = LoadSourceFile(filename);
- if (!source)
- {
- botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
- return NULL;
- } //end if
- //
- config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t));
- config->numweights = 0;
- Q_strncpyz( config->filename, filename, sizeof(config->filename) );
- //parse the item config file
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, "weight"))
- {
- if (config->numweights >= MAX_WEIGHTS)
- {
- SourceWarning(source, "too many fuzzy weights\n");
- break;
- } //end if
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
- {
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end if
- StripDoubleQuotes(token.string);
- config->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1);
- strcpy(config->weights[config->numweights].name, token.string);
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end if
- newindent = qfalse;
- if (!strcmp(token.string, "{"))
- {
- newindent = qtrue;
- if (!PC_ExpectAnyToken(source, &token))
- {
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end if
- } //end if
- if (!strcmp(token.string, "switch"))
- {
- fs = ReadFuzzySeperators_r(source);
- if (!fs)
- {
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end if
- config->weights[config->numweights].firstseperator = fs;
- } //end if
- else if (!strcmp(token.string, "return"))
- {
- fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));
- fs->index = 0;
- fs->value = MAX_INVENTORYVALUE;
- fs->next = NULL;
- fs->child = NULL;
- if (!ReadFuzzyWeight(source, fs))
- {
- FreeMemory(fs);
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end if
- config->weights[config->numweights].firstseperator = fs;
- } //end else if
- else
- {
- SourceError(source, "invalid name %s\n", token.string);
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end else
- if (newindent)
- {
- if (!PC_ExpectTokenString(source, "}"))
- {
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end if
- } //end if
- config->numweights++;
- } //end if
- else
- {
- SourceError(source, "invalid name %s\n", token.string);
- FreeWeightConfig(config);
- FreeSource(source);
- return NULL;
- } //end else
- } //end while
- //free the source at the end of a pass
- FreeSource(source);
- //if the file was located in a pak file
- botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
-#ifdef DEBUG
- if (bot_developer)
- {
- botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime);
- } //end if
-#endif //DEBUG
- //
- if (!LibVarGetValue("bot_reloadcharacters"))
- {
- weightFileList[avail] = config;
- } //end if
- //
- return config;
-} //end of the function ReadWeightConfig
-#if 0
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs)
-{
- if (fs->type == WT_BALANCE)
- {
- if (fprintf(fp, " return balance(") < 0) return qfalse;
- if (!WriteFloat(fp, fs->weight)) return qfalse;
- if (fprintf(fp, ",") < 0) return qfalse;
- if (!WriteFloat(fp, fs->minweight)) return qfalse;
- if (fprintf(fp, ",") < 0) return qfalse;
- if (!WriteFloat(fp, fs->maxweight)) return qfalse;
- if (fprintf(fp, ");\n") < 0) return qfalse;
- } //end if
- else
- {
- if (fprintf(fp, " return ") < 0) return qfalse;
- if (!WriteFloat(fp, fs->weight)) return qfalse;
- if (fprintf(fp, ";\n") < 0) return qfalse;
- } //end else
- return qtrue;
-} //end of the function WriteFuzzyWeight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent)
-{
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse;
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "{\n") < 0) return qfalse;
- indent++;
- do
- {
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fs->next)
- {
- if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse;
- } //end if
- else
- {
- if (fprintf(fp, "default:") < 0) return qfalse;
- } //end else
- if (fs->child)
- {
- if (fprintf(fp, "\n") < 0) return qfalse;
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "{\n") < 0) return qfalse;
- if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse;
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fs->next)
- {
- if (fprintf(fp, "} //end case\n") < 0) return qfalse;
- } //end if
- else
- {
- if (fprintf(fp, "} //end default\n") < 0) return qfalse;
- } //end else
- } //end if
- else
- {
- if (!WriteFuzzyWeight(fp, fs)) return qfalse;
- } //end else
- fs = fs->next;
- } while(fs);
- indent--;
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "} //end switch\n") < 0) return qfalse;
- return qtrue;
-} //end of the function WriteItemFuzzyWeights_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean WriteWeightConfig(char *filename, weightconfig_t *config)
-{
- int i;
- FILE *fp;
- weight_t *ifw;
-
- fp = fopen(filename, "wb");
- if (!fp) return qfalse;
-
- for (i = 0; i < config->numweights; i++)
- {
- ifw = &config->weights[i];
- if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse;
- if (fprintf(fp, "{\n") < 0) return qfalse;
- if (ifw->firstseperator->index > 0)
- {
- if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse;
- } //end if
- else
- {
- if (!WriteIndent(fp, 1)) return qfalse;
- if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse;
- } //end else
- if (fprintf(fp, "} //end weight\n") < 0) return qfalse;
- } //end for
- fclose(fp);
- return qtrue;
-} //end of the function WriteWeightConfig
-#endif
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int FindFuzzyWeight(weightconfig_t *wc, char *name)
-{
- int i;
-
- for (i = 0; i < wc->numweights; i++)
- {
- if (!strcmp(wc->weights[i].name, name))
- {
- return i;
- } //end if
- } //end if
- return -1;
-} //end of the function FindFuzzyWeight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs)
-{
- float scale, w1, w2;
-
- if (inventory[fs->index] < fs->value)
- {
- if (fs->child) return FuzzyWeight_r(inventory, fs->child);
- else return fs->weight;
- } //end if
- else if (fs->next)
- {
- if (inventory[fs->index] < fs->next->value)
- {
- //first weight
- if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child);
- else w1 = fs->weight;
- //second weight
- if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
- else w2 = fs->next->weight;
- //the scale factor
- scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
- //scale between the two weights
- return scale * w1 + (1 - scale) * w2;
- } //end if
- return FuzzyWeight_r(inventory, fs->next);
- } //end else if
- return fs->weight;
-} //end of the function FuzzyWeight_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs)
-{
- float scale, w1, w2;
-
- if (inventory[fs->index] < fs->value)
- {
- if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child);
- else return fs->minweight + random() * (fs->maxweight - fs->minweight);
- } //end if
- else if (fs->next)
- {
- if (inventory[fs->index] < fs->next->value)
- {
- //first weight
- if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child);
- else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight);
- //second weight
- if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);
- else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight);
- //the scale factor
- scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);
- //scale between the two weights
- return scale * w1 + (1 - scale) * w2;
- } //end if
- return FuzzyWeightUndecided_r(inventory, fs->next);
- } //end else if
- return fs->weight;
-} //end of the function FuzzyWeightUndecided_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum)
-{
-#ifdef EVALUATERECURSIVELY
- return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator);
-#else
- fuzzyseperator_t *s;
-
- s = wc->weights[weightnum].firstseperator;
- if (!s) return 0;
- while(1)
- {
- if (inventory[s->index] < s->value)
- {
- if (s->child) s = s->child;
- else return s->weight;
- } //end if
- else
- {
- if (s->next) s = s->next;
- else return s->weight;
- } //end else
- } //end if
- return 0;
-#endif
-} //end of the function FuzzyWeight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum)
-{
-#ifdef EVALUATERECURSIVELY
- return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator);
-#else
- fuzzyseperator_t *s;
-
- s = wc->weights[weightnum].firstseperator;
- if (!s) return 0;
- while(1)
- {
- if (inventory[s->index] < s->value)
- {
- if (s->child) s = s->child;
- else return s->minweight + random() * (s->maxweight - s->minweight);
- } //end if
- else
- {
- if (s->next) s = s->next;
- else return s->minweight + random() * (s->maxweight - s->minweight);
- } //end else
- } //end if
- return 0;
-#endif
-} //end of the function FuzzyWeightUndecided
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EvolveFuzzySeperator_r(fuzzyseperator_t *fs)
-{
- if (fs->child)
- {
- EvolveFuzzySeperator_r(fs->child);
- } //end if
- else if (fs->type == WT_BALANCE)
- {
- //every once in a while an evolution leap occurs, mutation
- if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight);
- else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5;
- //modify bounds if necesary because of mutation
- if (fs->weight < fs->minweight) fs->minweight = fs->weight;
- else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight;
- } //end else if
- if (fs->next) EvolveFuzzySeperator_r(fs->next);
-} //end of the function EvolveFuzzySeperator_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EvolveWeightConfig(weightconfig_t *config)
-{
- int i;
-
- for (i = 0; i < config->numweights; i++)
- {
- EvolveFuzzySeperator_r(config->weights[i].firstseperator);
- } //end for
-} //end of the function EvolveWeightConfig
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale)
-{
- if (fs->child)
- {
- ScaleFuzzySeperator_r(fs->child, scale);
- } //end if
- else if (fs->type == WT_BALANCE)
- {
- //
- fs->weight = (fs->maxweight + fs->minweight) * scale;
- //get the weight between bounds
- if (fs->weight < fs->minweight) fs->weight = fs->minweight;
- else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight;
- } //end else if
- if (fs->next) ScaleFuzzySeperator_r(fs->next, scale);
-} //end of the function ScaleFuzzySeperator_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void ScaleWeight(weightconfig_t *config, char *name, float scale)
-{
- int i;
-
- if (scale < 0) scale = 0;
- else if (scale > 1) scale = 1;
- for (i = 0; i < config->numweights; i++)
- {
- if (!strcmp(name, config->weights[i].name))
- {
- ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale);
- break;
- } //end if
- } //end for
-} //end of the function ScaleWeight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale)
-{
- if (fs->child)
- {
- ScaleFuzzySeperatorBalanceRange_r(fs->child, scale);
- } //end if
- else if (fs->type == WT_BALANCE)
- {
- float mid = (fs->minweight + fs->maxweight) * 0.5;
- //get the weight between bounds
- fs->maxweight = mid + (fs->maxweight - mid) * scale;
- fs->minweight = mid + (fs->minweight - mid) * scale;
- if (fs->maxweight < fs->minweight)
- {
- fs->maxweight = fs->minweight;
- } //end if
- } //end else if
- if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale);
-} //end of the function ScaleFuzzySeperatorBalanceRange_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale)
-{
- int i;
-
- if (scale < 0) scale = 0;
- else if (scale > 100) scale = 100;
- for (i = 0; i < config->numweights; i++)
- {
- ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale);
- } //end for
-} //end of the function ScaleFuzzyBalanceRange
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,
- fuzzyseperator_t *fsout)
-{
- if (fs1->child)
- {
- if (!fs2->child || !fsout->child)
- {
- botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n");
- return qfalse;
- } //end if
- if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child))
- {
- return qfalse;
- } //end if
- } //end if
- else if (fs1->type == WT_BALANCE)
- {
- if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE)
- {
- botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n");
- return qfalse;
- } //end if
- fsout->weight = (fs1->weight + fs2->weight) / 2;
- if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight;
- if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight;
- } //end else if
- if (fs1->next)
- {
- if (!fs2->next || !fsout->next)
- {
- botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n");
- return qfalse;
- } //end if
- if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next))
- {
- return qfalse;
- } //end if
- } //end if
- return qtrue;
-} //end of the function InterbreedFuzzySeperator_r
-//===========================================================================
-// config1 and config2 are interbreeded and stored in configout
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2,
- weightconfig_t *configout)
-{
- int i;
-
- if (config1->numweights != config2->numweights ||
- config1->numweights != configout->numweights)
- {
- botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n");
- return;
- } //end if
- for (i = 0; i < config1->numweights; i++)
- {
- InterbreedFuzzySeperator_r(config1->weights[i].firstseperator,
- config2->weights[i].firstseperator,
- configout->weights[i].firstseperator);
- } //end for
-} //end of the function InterbreedWeightConfigs
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void BotShutdownWeights(void)
-{
- int i;
-
- for( i = 0; i < MAX_WEIGHT_FILES; i++ )
- {
- if (weightFileList[i])
- {
- FreeWeightConfig2(weightFileList[i]);
- weightFileList[i] = NULL;
- } //end if
- } //end for
-} //end of the function BotShutdownWeights
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weight.c + * + * desc: fuzzy logic + * + * $Archive: /MissionPack/code/botlib/be_ai_weight.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_utils.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_libvar.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" + +#define MAX_INVENTORYVALUE 999999 +#define EVALUATERECURSIVELY + +#define MAX_WEIGHT_FILES 128 +weightconfig_t *weightFileList[MAX_WEIGHT_FILES]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadValue(source_t *source, float *value) +{ + token_t token; + + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + if (!strcmp(token.string, "-")) + { + SourceWarning(source, "negative value set to zero\n"); + if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token)) return qfalse; + } //end if + if (token.type != TT_NUMBER) + { + SourceError(source, "invalid return value %s\n", token.string); + return qfalse; + } //end if + *value = token.floatvalue; + return qtrue; +} //end of the function ReadValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs) +{ + if (PC_CheckTokenString(source, "balance")) + { + fs->type = WT_BALANCE; + if (!PC_ExpectTokenString(source, "(")) return qfalse; + if (!ReadValue(source, &fs->weight)) return qfalse; + if (!PC_ExpectTokenString(source, ",")) return qfalse; + if (!ReadValue(source, &fs->minweight)) return qfalse; + if (!PC_ExpectTokenString(source, ",")) return qfalse; + if (!ReadValue(source, &fs->maxweight)) return qfalse; + if (!PC_ExpectTokenString(source, ")")) return qfalse; + } //end if + else + { + fs->type = 0; + if (!ReadValue(source, &fs->weight)) return qfalse; + fs->minweight = fs->weight; + fs->maxweight = fs->weight; + } //end if + if (!PC_ExpectTokenString(source, ";")) return qfalse; + return qtrue; +} //end of the function ReadFuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeFuzzySeperators_r(fuzzyseperator_t *fs) +{ + if (!fs) return; + if (fs->child) FreeFuzzySeperators_r(fs->child); + if (fs->next) FreeFuzzySeperators_r(fs->next); + FreeMemory(fs); +} //end of the function FreeFuzzySeperators +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeWeightConfig2(weightconfig_t *config) +{ + int i; + + for (i = 0; i < config->numweights; i++) + { + FreeFuzzySeperators_r(config->weights[i].firstseperator); + if (config->weights[i].name) FreeMemory(config->weights[i].name); + } //end for + FreeMemory(config); +} //end of the function FreeWeightConfig2 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeWeightConfig(weightconfig_t *config) +{ + if (!LibVarGetValue("bot_reloadcharacters")) return; + FreeWeightConfig2(config); +} //end of the function FreeWeightConfig +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +fuzzyseperator_t *ReadFuzzySeperators_r(source_t *source) +{ + int newindent, index, def, founddefault; + token_t token; + fuzzyseperator_t *fs, *lastfs, *firstfs; + + founddefault = qfalse; + firstfs = NULL; + lastfs = NULL; + if (!PC_ExpectTokenString(source, "(")) return NULL; + if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL; + index = token.intvalue; + if (!PC_ExpectTokenString(source, ")")) return NULL; + if (!PC_ExpectTokenString(source, "{")) return NULL; + if (!PC_ExpectAnyToken(source, &token)) return NULL; + do + { + def = !strcmp(token.string, "default"); + if (def || !strcmp(token.string, "case")) + { + fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); + fs->index = index; + if (lastfs) lastfs->next = fs; + else firstfs = fs; + lastfs = fs; + if (def) + { + if (founddefault) + { + SourceError(source, "switch already has a default\n"); + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + fs->value = MAX_INVENTORYVALUE; + founddefault = qtrue; + } //end if + else + { + if (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + fs->value = token.intvalue; + } //end else + if (!PC_ExpectTokenString(source, ":") || !PC_ExpectAnyToken(source, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + newindent = qfalse; + if (!strcmp(token.string, "{")) + { + newindent = qtrue; + if (!PC_ExpectAnyToken(source, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end if + if (!strcmp(token.string, "return")) + { + if (!ReadFuzzyWeight(source, fs)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end if + else if (!strcmp(token.string, "switch")) + { + fs->child = ReadFuzzySeperators_r(source); + if (!fs->child) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end else if + else + { + SourceError(source, "invalid name %s\n", token.string); + return NULL; + } //end else + if (newindent) + { + if (!PC_ExpectTokenString(source, "}")) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } //end if + } //end if + else + { + FreeFuzzySeperators_r(firstfs); + SourceError(source, "invalid name %s\n", token.string); + return NULL; + } //end else + if (!PC_ExpectAnyToken(source, &token)) + { + FreeFuzzySeperators_r(firstfs); + return NULL; + } //end if + } while(strcmp(token.string, "}")); + // + if (!founddefault) + { + SourceWarning(source, "switch without default\n"); + fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); + fs->index = index; + fs->value = MAX_INVENTORYVALUE; + fs->weight = 0; + fs->next = NULL; + fs->child = NULL; + if (lastfs) lastfs->next = fs; + else firstfs = fs; + lastfs = fs; + } //end if + // + return firstfs; +} //end of the function ReadFuzzySeperators_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +weightconfig_t *ReadWeightConfig(char *filename) +{ + int newindent, avail = 0, n; + token_t token; + source_t *source; + fuzzyseperator_t *fs; + weightconfig_t *config = NULL; +#ifdef DEBUG + int starttime; + + starttime = Sys_MilliSeconds(); +#endif //DEBUG + + if (!LibVarGetValue("bot_reloadcharacters")) + { + avail = -1; + for( n = 0; n < MAX_WEIGHT_FILES; n++ ) + { + config = weightFileList[n]; + if( !config ) + { + if( avail == -1 ) + { + avail = n; + } //end if + continue; + } //end if + if( strcmp( filename, config->filename ) == 0 ) + { + //botimport.Print( PRT_MESSAGE, "retained %s\n", filename ); + return config; + } //end if + } //end for + + if( avail == -1 ) + { + botimport.Print( PRT_ERROR, "weightFileList was full trying to load %s\n", filename ); + return NULL; + } //end if + } //end if + + PC_SetBaseFolder(BOTFILESBASEFOLDER); + source = LoadSourceFile(filename); + if (!source) + { + botimport.Print(PRT_ERROR, "counldn't load %s\n", filename); + return NULL; + } //end if + // + config = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t)); + config->numweights = 0; + Q_strncpyz( config->filename, filename, sizeof(config->filename) ); + //parse the item config file + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, "weight")) + { + if (config->numweights >= MAX_WEIGHTS) + { + SourceWarning(source, "too many fuzzy weights\n"); + break; + } //end if + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + StripDoubleQuotes(token.string); + config->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1); + strcpy(config->weights[config->numweights].name, token.string); + if (!PC_ExpectAnyToken(source, &token)) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + newindent = qfalse; + if (!strcmp(token.string, "{")) + { + newindent = qtrue; + if (!PC_ExpectAnyToken(source, &token)) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + } //end if + if (!strcmp(token.string, "switch")) + { + fs = ReadFuzzySeperators_r(source); + if (!fs) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + config->weights[config->numweights].firstseperator = fs; + } //end if + else if (!strcmp(token.string, "return")) + { + fs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t)); + fs->index = 0; + fs->value = MAX_INVENTORYVALUE; + fs->next = NULL; + fs->child = NULL; + if (!ReadFuzzyWeight(source, fs)) + { + FreeMemory(fs); + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + config->weights[config->numweights].firstseperator = fs; + } //end else if + else + { + SourceError(source, "invalid name %s\n", token.string); + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end else + if (newindent) + { + if (!PC_ExpectTokenString(source, "}")) + { + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end if + } //end if + config->numweights++; + } //end if + else + { + SourceError(source, "invalid name %s\n", token.string); + FreeWeightConfig(config); + FreeSource(source); + return NULL; + } //end else + } //end while + //free the source at the end of a pass + FreeSource(source); + //if the file was located in a pak file + botimport.Print(PRT_MESSAGE, "loaded %s\n", filename); +#ifdef DEBUG + if (bot_developer) + { + botimport.Print(PRT_MESSAGE, "weights loaded in %d msec\n", Sys_MilliSeconds() - starttime); + } //end if +#endif //DEBUG + // + if (!LibVarGetValue("bot_reloadcharacters")) + { + weightFileList[avail] = config; + } //end if + // + return config; +} //end of the function ReadWeightConfig +#if 0 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs) +{ + if (fs->type == WT_BALANCE) + { + if (fprintf(fp, " return balance(") < 0) return qfalse; + if (!WriteFloat(fp, fs->weight)) return qfalse; + if (fprintf(fp, ",") < 0) return qfalse; + if (!WriteFloat(fp, fs->minweight)) return qfalse; + if (fprintf(fp, ",") < 0) return qfalse; + if (!WriteFloat(fp, fs->maxweight)) return qfalse; + if (fprintf(fp, ");\n") < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, " return ") < 0) return qfalse; + if (!WriteFloat(fp, fs->weight)) return qfalse; + if (fprintf(fp, ";\n") < 0) return qfalse; + } //end else + return qtrue; +} //end of the function WriteFuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent) +{ + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "switch(%d)\n", fs->index) < 0) return qfalse; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "{\n") < 0) return qfalse; + indent++; + do + { + if (!WriteIndent(fp, indent)) return qfalse; + if (fs->next) + { + if (fprintf(fp, "case %d:", fs->value) < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, "default:") < 0) return qfalse; + } //end else + if (fs->child) + { + if (fprintf(fp, "\n") < 0) return qfalse; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "{\n") < 0) return qfalse; + if (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse; + if (!WriteIndent(fp, indent)) return qfalse; + if (fs->next) + { + if (fprintf(fp, "} //end case\n") < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, "} //end default\n") < 0) return qfalse; + } //end else + } //end if + else + { + if (!WriteFuzzyWeight(fp, fs)) return qfalse; + } //end else + fs = fs->next; + } while(fs); + indent--; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "} //end switch\n") < 0) return qfalse; + return qtrue; +} //end of the function WriteItemFuzzyWeights_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteWeightConfig(char *filename, weightconfig_t *config) +{ + int i; + FILE *fp; + weight_t *ifw; + + fp = fopen(filename, "wb"); + if (!fp) return qfalse; + + for (i = 0; i < config->numweights; i++) + { + ifw = &config->weights[i]; + if (fprintf(fp, "\nweight \"%s\"\n", ifw->name) < 0) return qfalse; + if (fprintf(fp, "{\n") < 0) return qfalse; + if (ifw->firstseperator->index > 0) + { + if (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse; + } //end if + else + { + if (!WriteIndent(fp, 1)) return qfalse; + if (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse; + } //end else + if (fprintf(fp, "} //end weight\n") < 0) return qfalse; + } //end for + fclose(fp); + return qtrue; +} //end of the function WriteWeightConfig +#endif +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int FindFuzzyWeight(weightconfig_t *wc, char *name) +{ + int i; + + for (i = 0; i < wc->numweights; i++) + { + if (!strcmp(wc->weights[i].name, name)) + { + return i; + } //end if + } //end if + return -1; +} //end of the function FindFuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs) +{ + float scale, w1, w2; + + if (inventory[fs->index] < fs->value) + { + if (fs->child) return FuzzyWeight_r(inventory, fs->child); + else return fs->weight; + } //end if + else if (fs->next) + { + if (inventory[fs->index] < fs->next->value) + { + //first weight + if (fs->child) w1 = FuzzyWeight_r(inventory, fs->child); + else w1 = fs->weight; + //second weight + if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); + else w2 = fs->next->weight; + //the scale factor + scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); + //scale between the two weights + return scale * w1 + (1 - scale) * w2; + } //end if + return FuzzyWeight_r(inventory, fs->next); + } //end else if + return fs->weight; +} //end of the function FuzzyWeight_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs) +{ + float scale, w1, w2; + + if (inventory[fs->index] < fs->value) + { + if (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child); + else return fs->minweight + random() * (fs->maxweight - fs->minweight); + } //end if + else if (fs->next) + { + if (inventory[fs->index] < fs->next->value) + { + //first weight + if (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child); + else w1 = fs->minweight + random() * (fs->maxweight - fs->minweight); + //second weight + if (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child); + else w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight); + //the scale factor + scale = (inventory[fs->index] - fs->value) / (fs->next->value - fs->value); + //scale between the two weights + return scale * w1 + (1 - scale) * w2; + } //end if + return FuzzyWeightUndecided_r(inventory, fs->next); + } //end else if + return fs->weight; +} //end of the function FuzzyWeightUndecided_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum) +{ +#ifdef EVALUATERECURSIVELY + return FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator); +#else + fuzzyseperator_t *s; + + s = wc->weights[weightnum].firstseperator; + if (!s) return 0; + while(1) + { + if (inventory[s->index] < s->value) + { + if (s->child) s = s->child; + else return s->weight; + } //end if + else + { + if (s->next) s = s->next; + else return s->weight; + } //end else + } //end if + return 0; +#endif +} //end of the function FuzzyWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum) +{ +#ifdef EVALUATERECURSIVELY + return FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator); +#else + fuzzyseperator_t *s; + + s = wc->weights[weightnum].firstseperator; + if (!s) return 0; + while(1) + { + if (inventory[s->index] < s->value) + { + if (s->child) s = s->child; + else return s->minweight + random() * (s->maxweight - s->minweight); + } //end if + else + { + if (s->next) s = s->next; + else return s->minweight + random() * (s->maxweight - s->minweight); + } //end else + } //end if + return 0; +#endif +} //end of the function FuzzyWeightUndecided +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EvolveFuzzySeperator_r(fuzzyseperator_t *fs) +{ + if (fs->child) + { + EvolveFuzzySeperator_r(fs->child); + } //end if + else if (fs->type == WT_BALANCE) + { + //every once in a while an evolution leap occurs, mutation + if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight); + else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5; + //modify bounds if necesary because of mutation + if (fs->weight < fs->minweight) fs->minweight = fs->weight; + else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight; + } //end else if + if (fs->next) EvolveFuzzySeperator_r(fs->next); +} //end of the function EvolveFuzzySeperator_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EvolveWeightConfig(weightconfig_t *config) +{ + int i; + + for (i = 0; i < config->numweights; i++) + { + EvolveFuzzySeperator_r(config->weights[i].firstseperator); + } //end for +} //end of the function EvolveWeightConfig +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale) +{ + if (fs->child) + { + ScaleFuzzySeperator_r(fs->child, scale); + } //end if + else if (fs->type == WT_BALANCE) + { + // + fs->weight = (fs->maxweight + fs->minweight) * scale; + //get the weight between bounds + if (fs->weight < fs->minweight) fs->weight = fs->minweight; + else if (fs->weight > fs->maxweight) fs->weight = fs->maxweight; + } //end else if + if (fs->next) ScaleFuzzySeperator_r(fs->next, scale); +} //end of the function ScaleFuzzySeperator_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleWeight(weightconfig_t *config, char *name, float scale) +{ + int i; + + if (scale < 0) scale = 0; + else if (scale > 1) scale = 1; + for (i = 0; i < config->numweights; i++) + { + if (!strcmp(name, config->weights[i].name)) + { + ScaleFuzzySeperator_r(config->weights[i].firstseperator, scale); + break; + } //end if + } //end for +} //end of the function ScaleWeight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale) +{ + if (fs->child) + { + ScaleFuzzySeperatorBalanceRange_r(fs->child, scale); + } //end if + else if (fs->type == WT_BALANCE) + { + float mid = (fs->minweight + fs->maxweight) * 0.5; + //get the weight between bounds + fs->maxweight = mid + (fs->maxweight - mid) * scale; + fs->minweight = mid + (fs->minweight - mid) * scale; + if (fs->maxweight < fs->minweight) + { + fs->maxweight = fs->minweight; + } //end if + } //end else if + if (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale); +} //end of the function ScaleFuzzySeperatorBalanceRange_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ScaleFuzzyBalanceRange(weightconfig_t *config, float scale) +{ + int i; + + if (scale < 0) scale = 0; + else if (scale > 100) scale = 100; + for (i = 0; i < config->numweights; i++) + { + ScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale); + } //end for +} //end of the function ScaleFuzzyBalanceRange +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2, + fuzzyseperator_t *fsout) +{ + if (fs1->child) + { + if (!fs2->child || !fsout->child) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal child\n"); + return qfalse; + } //end if + if (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child)) + { + return qfalse; + } //end if + } //end if + else if (fs1->type == WT_BALANCE) + { + if (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal balance\n"); + return qfalse; + } //end if + fsout->weight = (fs1->weight + fs2->weight) / 2; + if (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight; + if (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight; + } //end else if + if (fs1->next) + { + if (!fs2->next || !fsout->next) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal next\n"); + return qfalse; + } //end if + if (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next)) + { + return qfalse; + } //end if + } //end if + return qtrue; +} //end of the function InterbreedFuzzySeperator_r +//=========================================================================== +// config1 and config2 are interbreeded and stored in configout +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, + weightconfig_t *configout) +{ + int i; + + if (config1->numweights != config2->numweights || + config1->numweights != configout->numweights) + { + botimport.Print(PRT_ERROR, "cannot interbreed weight configs, unequal numweights\n"); + return; + } //end if + for (i = 0; i < config1->numweights; i++) + { + InterbreedFuzzySeperator_r(config1->weights[i].firstseperator, + config2->weights[i].firstseperator, + configout->weights[i].firstseperator); + } //end for +} //end of the function InterbreedWeightConfigs +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotShutdownWeights(void) +{ + int i; + + for( i = 0; i < MAX_WEIGHT_FILES; i++ ) + { + if (weightFileList[i]) + { + FreeWeightConfig2(weightFileList[i]); + weightFileList[i] = NULL; + } //end if + } //end for +} //end of the function BotShutdownWeights diff --git a/code/botlib/be_ai_weight.h b/code/botlib/be_ai_weight.h index 4fc2f6b..ebbf92f 100755 --- a/code/botlib/be_ai_weight.h +++ b/code/botlib/be_ai_weight.h @@ -1,83 +1,83 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ai_weight.h
- *
- * desc: fuzzy weights
- *
- * $Archive: /source/code/botlib/be_ai_weight.h $
- *
- *****************************************************************************/
-
-#define WT_BALANCE 1
-#define MAX_WEIGHTS 128
-
-//fuzzy seperator
-typedef struct fuzzyseperator_s
-{
- int index;
- int value;
- int type;
- float weight;
- float minweight;
- float maxweight;
- struct fuzzyseperator_s *child;
- struct fuzzyseperator_s *next;
-} fuzzyseperator_t;
-
-//fuzzy weight
-typedef struct weight_s
-{
- char *name;
- struct fuzzyseperator_s *firstseperator;
-} weight_t;
-
-//weight configuration
-typedef struct weightconfig_s
-{
- int numweights;
- weight_t weights[MAX_WEIGHTS];
- char filename[MAX_QPATH];
-} weightconfig_t;
-
-//reads a weight configuration
-weightconfig_t *ReadWeightConfig(char *filename);
-//free a weight configuration
-void FreeWeightConfig(weightconfig_t *config);
-//writes a weight configuration, returns true if successfull
-qboolean WriteWeightConfig(char *filename, weightconfig_t *config);
-//find the fuzzy weight with the given name
-int FindFuzzyWeight(weightconfig_t *wc, char *name);
-//returns the fuzzy weight for the given inventory and weight
-float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum);
-float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum);
-//scales the weight with the given name
-void ScaleWeight(weightconfig_t *config, char *name, float scale);
-//scale the balance range
-void ScaleBalanceRange(weightconfig_t *config, float scale);
-//evolves the weight configuration
-void EvolveWeightConfig(weightconfig_t *config);
-//interbreed the weight configurations and stores the interbreeded one in configout
-void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout);
-//frees cached weight configurations
-void BotShutdownWeights(void);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ai_weight.h + * + * desc: fuzzy weights + * + * $Archive: /source/code/botlib/be_ai_weight.h $ + * + *****************************************************************************/ + +#define WT_BALANCE 1 +#define MAX_WEIGHTS 128 + +//fuzzy seperator +typedef struct fuzzyseperator_s +{ + int index; + int value; + int type; + float weight; + float minweight; + float maxweight; + struct fuzzyseperator_s *child; + struct fuzzyseperator_s *next; +} fuzzyseperator_t; + +//fuzzy weight +typedef struct weight_s +{ + char *name; + struct fuzzyseperator_s *firstseperator; +} weight_t; + +//weight configuration +typedef struct weightconfig_s +{ + int numweights; + weight_t weights[MAX_WEIGHTS]; + char filename[MAX_QPATH]; +} weightconfig_t; + +//reads a weight configuration +weightconfig_t *ReadWeightConfig(char *filename); +//free a weight configuration +void FreeWeightConfig(weightconfig_t *config); +//writes a weight configuration, returns true if successfull +qboolean WriteWeightConfig(char *filename, weightconfig_t *config); +//find the fuzzy weight with the given name +int FindFuzzyWeight(weightconfig_t *wc, char *name); +//returns the fuzzy weight for the given inventory and weight +float FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum); +float FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum); +//scales the weight with the given name +void ScaleWeight(weightconfig_t *config, char *name, float scale); +//scale the balance range +void ScaleBalanceRange(weightconfig_t *config, float scale); +//evolves the weight configuration +void EvolveWeightConfig(weightconfig_t *config); +//interbreed the weight configurations and stores the interbreeded one in configout +void InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout); +//frees cached weight configurations +void BotShutdownWeights(void); diff --git a/code/botlib/be_ea.c b/code/botlib/be_ea.c index 2c57099..5c0dd6c 100755 --- a/code/botlib/be_ea.c +++ b/code/botlib/be_ea.c @@ -1,508 +1,508 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_ea.c
- *
- * desc: elementary actions
- *
- * $Archive: /MissionPack/code/botlib/be_ea.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "../game/botlib.h"
-#include "be_interface.h"
-
-#define MAX_USERMOVE 400
-#define MAX_COMMANDARGUMENTS 10
-#define ACTION_JUMPEDLASTFRAME 128
-
-bot_input_t *botinputs;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Say(int client, char *str)
-{
- botimport.BotClientCommand(client, va("say %s", str) );
-} //end of the function EA_Say
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_SayTeam(int client, char *str)
-{
- botimport.BotClientCommand(client, va("say_team %s", str));
-} //end of the function EA_SayTeam
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Tell(int client, int clientto, char *str)
-{
- botimport.BotClientCommand(client, va("tell %d, %s", clientto, str));
-} //end of the function EA_SayTeam
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_UseItem(int client, char *it)
-{
- botimport.BotClientCommand(client, va("use %s", it));
-} //end of the function EA_UseItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_DropItem(int client, char *it)
-{
- botimport.BotClientCommand(client, va("drop %s", it));
-} //end of the function EA_DropItem
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_UseInv(int client, char *inv)
-{
- botimport.BotClientCommand(client, va("invuse %s", inv));
-} //end of the function EA_UseInv
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_DropInv(int client, char *inv)
-{
- botimport.BotClientCommand(client, va("invdrop %s", inv));
-} //end of the function EA_DropInv
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Gesture(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_GESTURE;
-} //end of the function EA_Gesture
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Command(int client, char *command)
-{
- botimport.BotClientCommand(client, command);
-} //end of the function EA_Command
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_SelectWeapon(int client, int weapon)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->weapon = weapon;
-} //end of the function EA_SelectWeapon
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Attack(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_ATTACK;
-} //end of the function EA_Attack
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Talk(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_TALK;
-} //end of the function EA_Talk
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Use(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_USE;
-} //end of the function EA_Use
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Respawn(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_RESPAWN;
-} //end of the function EA_Respawn
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Jump(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- if (bi->actionflags & ACTION_JUMPEDLASTFRAME)
- {
- bi->actionflags &= ~ACTION_JUMP;
- } //end if
- else
- {
- bi->actionflags |= ACTION_JUMP;
- } //end if
-} //end of the function EA_Jump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_DelayedJump(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- if (bi->actionflags & ACTION_JUMPEDLASTFRAME)
- {
- bi->actionflags &= ~ACTION_DELAYEDJUMP;
- } //end if
- else
- {
- bi->actionflags |= ACTION_DELAYEDJUMP;
- } //end if
-} //end of the function EA_DelayedJump
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Crouch(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_CROUCH;
-} //end of the function EA_Crouch
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Walk(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_WALK;
-} //end of the function EA_Walk
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Action(int client, int action)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= action;
-} //end of function EA_Action
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_MoveUp(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_MOVEUP;
-} //end of the function EA_MoveUp
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_MoveDown(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_MOVEDOWN;
-} //end of the function EA_MoveDown
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_MoveForward(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_MOVEFORWARD;
-} //end of the function EA_MoveForward
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_MoveBack(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_MOVEBACK;
-} //end of the function EA_MoveBack
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_MoveLeft(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_MOVELEFT;
-} //end of the function EA_MoveLeft
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_MoveRight(int client)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- bi->actionflags |= ACTION_MOVERIGHT;
-} //end of the function EA_MoveRight
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Move(int client, vec3_t dir, float speed)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- VectorCopy(dir, bi->dir);
- //cap speed
- if (speed > MAX_USERMOVE) speed = MAX_USERMOVE;
- else if (speed < -MAX_USERMOVE) speed = -MAX_USERMOVE;
- bi->speed = speed;
-} //end of the function EA_Move
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_View(int client, vec3_t viewangles)
-{
- bot_input_t *bi;
-
- bi = &botinputs[client];
-
- VectorCopy(viewangles, bi->viewangles);
-} //end of the function EA_View
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_EndRegular(int client, float thinktime)
-{
-/*
- bot_input_t *bi;
- int jumped = qfalse;
-
- bi = &botinputs[client];
-
- bi->actionflags &= ~ACTION_JUMPEDLASTFRAME;
-
- bi->thinktime = thinktime;
- botimport.BotInput(client, bi);
-
- bi->thinktime = 0;
- VectorClear(bi->dir);
- bi->speed = 0;
- jumped = bi->actionflags & ACTION_JUMP;
- bi->actionflags = 0;
- if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME;
-*/
-} //end of the function EA_EndRegular
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_GetInput(int client, float thinktime, bot_input_t *input)
-{
- bot_input_t *bi;
-// int jumped = qfalse;
-
- bi = &botinputs[client];
-
-// bi->actionflags &= ~ACTION_JUMPEDLASTFRAME;
-
- bi->thinktime = thinktime;
- Com_Memcpy(input, bi, sizeof(bot_input_t));
-
- /*
- bi->thinktime = 0;
- VectorClear(bi->dir);
- bi->speed = 0;
- jumped = bi->actionflags & ACTION_JUMP;
- bi->actionflags = 0;
- if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME;
- */
-} //end of the function EA_GetInput
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_ResetInput(int client)
-{
- bot_input_t *bi;
- int jumped = qfalse;
-
- bi = &botinputs[client];
- bi->actionflags &= ~ACTION_JUMPEDLASTFRAME;
-
- bi->thinktime = 0;
- VectorClear(bi->dir);
- bi->speed = 0;
- jumped = bi->actionflags & ACTION_JUMP;
- bi->actionflags = 0;
- if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME;
-} //end of the function EA_ResetInput
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int EA_Setup(void)
-{
- //initialize the bot inputs
- botinputs = (bot_input_t *) GetClearedHunkMemory(
- botlibglobals.maxclients * sizeof(bot_input_t));
- return BLERR_NOERROR;
-} //end of the function EA_Setup
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void EA_Shutdown(void)
-{
- FreeMemory(botinputs);
- botinputs = NULL;
-} //end of the function EA_Shutdown
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_ea.c + * + * desc: elementary actions + * + * $Archive: /MissionPack/code/botlib/be_ea.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "../game/botlib.h" +#include "be_interface.h" + +#define MAX_USERMOVE 400 +#define MAX_COMMANDARGUMENTS 10 +#define ACTION_JUMPEDLASTFRAME 128 + +bot_input_t *botinputs; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Say(int client, char *str) +{ + botimport.BotClientCommand(client, va("say %s", str) ); +} //end of the function EA_Say +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_SayTeam(int client, char *str) +{ + botimport.BotClientCommand(client, va("say_team %s", str)); +} //end of the function EA_SayTeam +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Tell(int client, int clientto, char *str) +{ + botimport.BotClientCommand(client, va("tell %d, %s", clientto, str)); +} //end of the function EA_SayTeam +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_UseItem(int client, char *it) +{ + botimport.BotClientCommand(client, va("use %s", it)); +} //end of the function EA_UseItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_DropItem(int client, char *it) +{ + botimport.BotClientCommand(client, va("drop %s", it)); +} //end of the function EA_DropItem +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_UseInv(int client, char *inv) +{ + botimport.BotClientCommand(client, va("invuse %s", inv)); +} //end of the function EA_UseInv +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_DropInv(int client, char *inv) +{ + botimport.BotClientCommand(client, va("invdrop %s", inv)); +} //end of the function EA_DropInv +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Gesture(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_GESTURE; +} //end of the function EA_Gesture +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Command(int client, char *command) +{ + botimport.BotClientCommand(client, command); +} //end of the function EA_Command +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_SelectWeapon(int client, int weapon) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->weapon = weapon; +} //end of the function EA_SelectWeapon +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Attack(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_ATTACK; +} //end of the function EA_Attack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Talk(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_TALK; +} //end of the function EA_Talk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Use(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_USE; +} //end of the function EA_Use +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Respawn(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_RESPAWN; +} //end of the function EA_Respawn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Jump(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + if (bi->actionflags & ACTION_JUMPEDLASTFRAME) + { + bi->actionflags &= ~ACTION_JUMP; + } //end if + else + { + bi->actionflags |= ACTION_JUMP; + } //end if +} //end of the function EA_Jump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_DelayedJump(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + if (bi->actionflags & ACTION_JUMPEDLASTFRAME) + { + bi->actionflags &= ~ACTION_DELAYEDJUMP; + } //end if + else + { + bi->actionflags |= ACTION_DELAYEDJUMP; + } //end if +} //end of the function EA_DelayedJump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Crouch(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_CROUCH; +} //end of the function EA_Crouch +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Walk(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_WALK; +} //end of the function EA_Walk +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Action(int client, int action) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= action; +} //end of function EA_Action +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveUp(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEUP; +} //end of the function EA_MoveUp +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveDown(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEDOWN; +} //end of the function EA_MoveDown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveForward(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEFORWARD; +} //end of the function EA_MoveForward +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveBack(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVEBACK; +} //end of the function EA_MoveBack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveLeft(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVELEFT; +} //end of the function EA_MoveLeft +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_MoveRight(int client) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + bi->actionflags |= ACTION_MOVERIGHT; +} //end of the function EA_MoveRight +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Move(int client, vec3_t dir, float speed) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + VectorCopy(dir, bi->dir); + //cap speed + if (speed > MAX_USERMOVE) speed = MAX_USERMOVE; + else if (speed < -MAX_USERMOVE) speed = -MAX_USERMOVE; + bi->speed = speed; +} //end of the function EA_Move +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_View(int client, vec3_t viewangles) +{ + bot_input_t *bi; + + bi = &botinputs[client]; + + VectorCopy(viewangles, bi->viewangles); +} //end of the function EA_View +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_EndRegular(int client, float thinktime) +{ +/* + bot_input_t *bi; + int jumped = qfalse; + + bi = &botinputs[client]; + + bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; + + bi->thinktime = thinktime; + botimport.BotInput(client, bi); + + bi->thinktime = 0; + VectorClear(bi->dir); + bi->speed = 0; + jumped = bi->actionflags & ACTION_JUMP; + bi->actionflags = 0; + if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; +*/ +} //end of the function EA_EndRegular +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_GetInput(int client, float thinktime, bot_input_t *input) +{ + bot_input_t *bi; +// int jumped = qfalse; + + bi = &botinputs[client]; + +// bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; + + bi->thinktime = thinktime; + Com_Memcpy(input, bi, sizeof(bot_input_t)); + + /* + bi->thinktime = 0; + VectorClear(bi->dir); + bi->speed = 0; + jumped = bi->actionflags & ACTION_JUMP; + bi->actionflags = 0; + if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; + */ +} //end of the function EA_GetInput +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_ResetInput(int client) +{ + bot_input_t *bi; + int jumped = qfalse; + + bi = &botinputs[client]; + bi->actionflags &= ~ACTION_JUMPEDLASTFRAME; + + bi->thinktime = 0; + VectorClear(bi->dir); + bi->speed = 0; + jumped = bi->actionflags & ACTION_JUMP; + bi->actionflags = 0; + if (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME; +} //end of the function EA_ResetInput +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int EA_Setup(void) +{ + //initialize the bot inputs + botinputs = (bot_input_t *) GetClearedHunkMemory( + botlibglobals.maxclients * sizeof(bot_input_t)); + return BLERR_NOERROR; +} //end of the function EA_Setup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void EA_Shutdown(void) +{ + FreeMemory(botinputs); + botinputs = NULL; +} //end of the function EA_Shutdown diff --git a/code/botlib/be_interface.c b/code/botlib/be_interface.c index b5a022b..b4f2d3c 100755 --- a/code/botlib/be_interface.c +++ b/code/botlib/be_interface.c @@ -1,881 +1,881 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_interface.c // bk010221 - FIXME - DEAD code elimination
- *
- * desc: bot library interface
- *
- * $Archive: /MissionPack/code/botlib/be_interface.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_libvar.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "aasfile.h"
-#include "../game/botlib.h"
-#include "../game/be_aas.h"
-#include "be_aas_funcs.h"
-#include "be_aas_def.h"
-#include "be_interface.h"
-
-#include "../game/be_ea.h"
-#include "be_ai_weight.h"
-#include "../game/be_ai_goal.h"
-#include "../game/be_ai_move.h"
-#include "../game/be_ai_weap.h"
-#include "../game/be_ai_chat.h"
-#include "../game/be_ai_char.h"
-#include "../game/be_ai_gen.h"
-
-//library globals in a structure
-botlib_globals_t botlibglobals;
-
-botlib_export_t be_botlib_export;
-botlib_import_t botimport;
-//
-int bot_developer;
-//qtrue if the library is setup
-int botlibsetup = qfalse;
-
-//===========================================================================
-//
-// several functions used by the exported functions
-//
-//===========================================================================
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Sys_MilliSeconds(void)
-{
- return clock() * 1000 / CLOCKS_PER_SEC;
-} //end of the function Sys_MilliSeconds
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean ValidClientNumber(int num, char *str)
-{
- if (num < 0 || num > botlibglobals.maxclients)
- {
- //weird: the disabled stuff results in a crash
- botimport.Print(PRT_ERROR, "%s: invalid client number %d, [0, %d]\n",
- str, num, botlibglobals.maxclients);
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function BotValidateClientNumber
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean ValidEntityNumber(int num, char *str)
-{
- if (num < 0 || num > botlibglobals.maxentities)
- {
- botimport.Print(PRT_ERROR, "%s: invalid entity number %d, [0, %d]\n",
- str, num, botlibglobals.maxentities);
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function BotValidateClientNumber
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean BotLibSetup(char *str)
-{
- if (!botlibglobals.botlibsetup)
- {
- botimport.Print(PRT_ERROR, "%s: bot library used before being setup\n", str);
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function BotLibSetup
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibSetup(void)
-{
- int errnum;
-
- bot_developer = LibVarGetValue("bot_developer");
- memset( &botlibglobals, 0, sizeof(botlibglobals) ); // bk001207 - init
- //initialize byte swapping (litte endian etc.)
-// Swap_Init();
- Log_Open("botlib.log");
- //
- botimport.Print(PRT_MESSAGE, "------- BotLib Initialization -------\n");
- //
- botlibglobals.maxclients = (int) LibVarValue("maxclients", "128");
- botlibglobals.maxentities = (int) LibVarValue("maxentities", "1024");
-
- errnum = AAS_Setup(); //be_aas_main.c
- if (errnum != BLERR_NOERROR) return errnum;
- errnum = EA_Setup(); //be_ea.c
- if (errnum != BLERR_NOERROR) return errnum;
- errnum = BotSetupWeaponAI(); //be_ai_weap.c
- if (errnum != BLERR_NOERROR)return errnum;
- errnum = BotSetupGoalAI(); //be_ai_goal.c
- if (errnum != BLERR_NOERROR) return errnum;
- errnum = BotSetupChatAI(); //be_ai_chat.c
- if (errnum != BLERR_NOERROR) return errnum;
- errnum = BotSetupMoveAI(); //be_ai_move.c
- if (errnum != BLERR_NOERROR) return errnum;
-
- botlibsetup = qtrue;
- botlibglobals.botlibsetup = qtrue;
-
- return BLERR_NOERROR;
-} //end of the function Export_BotLibSetup
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibShutdown(void)
-{
- if (!BotLibSetup("BotLibShutdown")) return BLERR_LIBRARYNOTSETUP;
-#ifndef DEMO
- //DumpFileCRCs();
-#endif //DEMO
- //
- BotShutdownChatAI(); //be_ai_chat.c
- BotShutdownMoveAI(); //be_ai_move.c
- BotShutdownGoalAI(); //be_ai_goal.c
- BotShutdownWeaponAI(); //be_ai_weap.c
- BotShutdownWeights(); //be_ai_weight.c
- BotShutdownCharacters(); //be_ai_char.c
- //shud down aas
- AAS_Shutdown();
- //shut down bot elemantary actions
- EA_Shutdown();
- //free all libvars
- LibVarDeAllocAll();
- //remove all global defines from the pre compiler
- PC_RemoveAllGlobalDefines();
-
- //dump all allocated memory
-// DumpMemory();
-#ifdef DEBUG
- PrintMemoryLabels();
-#endif
- //shut down library log file
- Log_Shutdown();
- //
- botlibsetup = qfalse;
- botlibglobals.botlibsetup = qfalse;
- // print any files still open
- PC_CheckOpenSourceHandles();
- //
- return BLERR_NOERROR;
-} //end of the function Export_BotLibShutdown
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibVarSet(char *var_name, char *value)
-{
- LibVarSet(var_name, value);
- return BLERR_NOERROR;
-} //end of the function Export_BotLibVarSet
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibVarGet(char *var_name, char *value, int size)
-{
- char *varvalue;
-
- varvalue = LibVarGetString(var_name);
- strncpy(value, varvalue, size-1);
- value[size-1] = '\0';
- return BLERR_NOERROR;
-} //end of the function Export_BotLibVarGet
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibStartFrame(float time)
-{
- if (!BotLibSetup("BotStartFrame")) return BLERR_LIBRARYNOTSETUP;
- return AAS_StartFrame(time);
-} //end of the function Export_BotLibStartFrame
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibLoadMap(const char *mapname)
-{
-#ifdef DEBUG
- int starttime = Sys_MilliSeconds();
-#endif
- int errnum;
-
- if (!BotLibSetup("BotLoadMap")) return BLERR_LIBRARYNOTSETUP;
- //
- botimport.Print(PRT_MESSAGE, "------------ Map Loading ------------\n");
- //startup AAS for the current map, model and sound index
- errnum = AAS_LoadMap(mapname);
- if (errnum != BLERR_NOERROR) return errnum;
- //initialize the items in the level
- BotInitLevelItems(); //be_ai_goal.h
- BotSetBrushModelTypes(); //be_ai_move.h
- //
- botimport.Print(PRT_MESSAGE, "-------------------------------------\n");
-#ifdef DEBUG
- botimport.Print(PRT_MESSAGE, "map loaded in %d msec\n", Sys_MilliSeconds() - starttime);
-#endif
- //
- return BLERR_NOERROR;
-} //end of the function Export_BotLibLoadMap
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Export_BotLibUpdateEntity(int ent, bot_entitystate_t *state)
-{
- if (!BotLibSetup("BotUpdateEntity")) return BLERR_LIBRARYNOTSETUP;
- if (!ValidEntityNumber(ent, "BotUpdateEntity")) return BLERR_INVALIDENTITYNUMBER;
-
- return AAS_UpdateEntity(ent, state);
-} //end of the function Export_BotLibUpdateEntity
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir);
-void ElevatorBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter);
-int BotGetReachabilityToGoal(vec3_t origin, int areanum,
- int lastgoalareanum, int lastareanum,
- int *avoidreach, float *avoidreachtimes, int *avoidreachtries,
- bot_goal_t *goal, int travelflags, int movetravelflags,
- struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags);
-
-int AAS_PointLight(vec3_t origin, int *red, int *green, int *blue);
-
-int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);
-
-int AAS_Reachability_WeaponJump(int area1num, int area2num);
-
-int BotFuzzyPointReachabilityArea(vec3_t origin);
-
-float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum);
-
-void AAS_FloodAreas(vec3_t origin);
-
-int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3)
-{
-
-// return AAS_PointLight(parm2, NULL, NULL, NULL);
-
-#ifdef DEBUG
- static int area = -1;
- static int line[2];
- int newarea, i, highlightarea, flood;
-// int reachnum;
- vec3_t eye, forward, right, end, origin;
-// vec3_t bottomcenter;
-// aas_trace_t trace;
-// aas_face_t *face;
-// aas_entity_t *ent;
-// bsp_trace_t bsptrace;
-// aas_reachability_t reach;
-// bot_goal_t goal;
-
- // clock_t start_time, end_time;
- vec3_t mins = {-16, -16, -24};
- vec3_t maxs = {16, 16, 32};
-
-// int areas[10], numareas;
-
-
- //return 0;
-
- if (!aasworld.loaded) return 0;
-
- /*
- if (parm0 & 1)
- {
- AAS_ClearShownPolygons();
- AAS_FloodAreas(parm2);
- } //end if
- return 0;
- */
- for (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate();
-
-// AAS_ClearShownDebugLines();
-
- //if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, "against ladder\n");
- //BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea);
- //botimport.Print(PRT_MESSAGE, "%f %f %f\n", parm2[0], parm2[1], parm2[2]);
- //*
- highlightarea = LibVarGetValue("bot_highlightarea");
- if (highlightarea > 0)
- {
- newarea = highlightarea;
- } //end if
- else
- {
- VectorCopy(parm2, origin);
- origin[2] += 0.5;
- //newarea = AAS_PointAreaNum(origin);
- newarea = BotFuzzyPointReachabilityArea(origin);
- } //end else
-
- botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum,
- AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT));
- //newarea = BotReachabilityArea(origin, qtrue);
- if (newarea != area)
- {
- botimport.Print(PRT_MESSAGE, "origin = %f, %f, %f\n", origin[0], origin[1], origin[2]);
- area = newarea;
- botimport.Print(PRT_MESSAGE, "new area %d, cluster %d, presence type %d\n",
- area, AAS_AreaCluster(area), AAS_PointPresenceType(origin));
- botimport.Print(PRT_MESSAGE, "area contents: ");
- if (aasworld.areasettings[area].contents & AREACONTENTS_WATER)
- {
- botimport.Print(PRT_MESSAGE, "water &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_LAVA)
- {
- botimport.Print(PRT_MESSAGE, "lava &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_SLIME)
- {
- botimport.Print(PRT_MESSAGE, "slime &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD)
- {
- botimport.Print(PRT_MESSAGE, "jump pad &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL)
- {
- botimport.Print(PRT_MESSAGE, "cluster portal &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL)
- {
- botimport.Print(PRT_MESSAGE, "view portal &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER)
- {
- botimport.Print(PRT_MESSAGE, "do not enter &");
- } //end if
- if (aasworld.areasettings[area].contents & AREACONTENTS_MOVER)
- {
- botimport.Print(PRT_MESSAGE, "mover &");
- } //end if
- if (!aasworld.areasettings[area].contents)
- {
- botimport.Print(PRT_MESSAGE, "empty");
- } //end if
- botimport.Print(PRT_MESSAGE, "\n");
- botimport.Print(PRT_MESSAGE, "travel time to goal (%d) = %d\n", botlibglobals.goalareanum,
- AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP));
- /*
- VectorCopy(origin, end);
- end[2] += 5;
- numareas = AAS_TraceAreas(origin, end, areas, NULL, 10);
- AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);
- botimport.Print(PRT_MESSAGE, "num areas = %d, area = %d\n", numareas, areas[0]);
- */
- /*
- botlibglobals.goalareanum = newarea;
- VectorCopy(parm2, botlibglobals.goalorigin);
- botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n",
- origin[0], origin[1], origin[2], newarea);
- */
- } //end if
- //*
- flood = LibVarGetValue("bot_flood");
- if (parm0 & 1)
- {
- if (flood)
- {
- AAS_ClearShownPolygons();
- AAS_ClearShownDebugLines();
- AAS_FloodAreas(parm2);
- }
- else
- {
- botlibglobals.goalareanum = newarea;
- VectorCopy(parm2, botlibglobals.goalorigin);
- botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n",
- origin[0], origin[1], origin[2], newarea);
- }
- } //end if*/
- if (flood)
- return 0;
-// if (parm0 & BUTTON_USE)
-// {
-// botlibglobals.runai = !botlibglobals.runai;
-// if (botlibglobals.runai) botimport.Print(PRT_MESSAGE, "started AI\n");
-// else botimport.Print(PRT_MESSAGE, "stopped AI\n");
- //* /
- /*
- goal.areanum = botlibglobals.goalareanum;
- reachnum = BotGetReachabilityToGoal(parm2, newarea, 1,
- ms.avoidreach, ms.avoidreachtimes,
- &goal, TFL_DEFAULT);
- if (!reachnum)
- {
- botimport.Print(PRT_MESSAGE, "goal not reachable\n");
- } //end if
- else
- {
- AAS_ReachabilityFromNum(reachnum, &reach);
- AAS_ClearShownDebugLines();
- AAS_ShowArea(area, qtrue);
- AAS_ShowArea(reach.areanum, qtrue);
- AAS_DrawCross(reach.start, 6, LINECOLOR_BLUE);
- AAS_DrawCross(reach.end, 6, LINECOLOR_RED);
- //
- if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR)
- {
- ElevatorBottomCenter(&reach, bottomcenter);
- AAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN);
- } //end if
- } //end else*/
-// botimport.Print(PRT_MESSAGE, "travel time to goal = %d\n",
-// AAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT));
-// botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n");
-// AAS_Reachability_WeaponJump(703, 716);
-// } //end if*/
-
-/* face = AAS_AreaGroundFace(newarea, parm2);
- if (face)
- {
- AAS_ShowFace(face - aasworld.faces);
- } //end if*/
- /*
- AAS_ClearShownDebugLines();
- AAS_ShowArea(newarea, parm0 & BUTTON_USE);
- AAS_ShowReachableAreas(area);
- */
- AAS_ClearShownPolygons();
- AAS_ClearShownDebugLines();
- AAS_ShowAreaPolygons(newarea, 1, parm0 & 4);
- if (parm0 & 2) AAS_ShowReachableAreas(area);
- else
- {
- static int lastgoalareanum, lastareanum;
- static int avoidreach[MAX_AVOIDREACH];
- static float avoidreachtimes[MAX_AVOIDREACH];
- static int avoidreachtries[MAX_AVOIDREACH];
- int reachnum, resultFlags;
- bot_goal_t goal;
- aas_reachability_t reach;
-
- /*
- goal.areanum = botlibglobals.goalareanum;
- VectorCopy(botlibglobals.goalorigin, goal.origin);
- reachnum = BotGetReachabilityToGoal(origin, newarea,
- lastgoalareanum, lastareanum,
- avoidreach, avoidreachtimes, avoidreachtries,
- &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP,
- NULL, 0, &resultFlags);
- AAS_ReachabilityFromNum(reachnum, &reach);
- AAS_ShowReachability(&reach);
- */
- int curarea;
- vec3_t curorigin;
-
- goal.areanum = botlibglobals.goalareanum;
- VectorCopy(botlibglobals.goalorigin, goal.origin);
- VectorCopy(origin, curorigin);
- curarea = newarea;
- for ( i = 0; i < 100; i++ ) {
- if ( curarea == goal.areanum ) {
- break;
- }
- reachnum = BotGetReachabilityToGoal(curorigin, curarea,
- lastgoalareanum, lastareanum,
- avoidreach, avoidreachtimes, avoidreachtries,
- &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP,
- NULL, 0, &resultFlags);
- AAS_ReachabilityFromNum(reachnum, &reach);
- AAS_ShowReachability(&reach);
- VectorCopy(reach.end, origin);
- lastareanum = curarea;
- curarea = reach.areanum;
- }
- } //end else
- VectorClear(forward);
- //BotGapDistance(origin, forward, 0);
- /*
- if (parm0 & BUTTON_USE)
- {
- botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n");
- AAS_Reachability_WeaponJump(703, 716);
- } //end if*/
-
- AngleVectors(parm3, forward, right, NULL);
- //get the eye 16 units to the right of the origin
- VectorMA(parm2, 8, right, eye);
- //get the eye 24 units up
- eye[2] += 24;
- //get the end point for the line to be traced
- VectorMA(eye, 800, forward, end);
-
-// AAS_TestMovementPrediction(1, parm2, forward);
-/*
- //trace the line to find the hit point
- trace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1);
- if (!line[0]) line[0] = botimport.DebugLineCreate();
- botimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE);
- //
- AAS_ClearShownDebugLines();
- if (trace.ent)
- {
- ent = &aasworld.entities[trace.ent];
- AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs);
- } //end if
-*/
-
-/*
- start_time = clock();
- for (i = 0; i < 2000; i++)
- {
- AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);
-// AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1);
- } //end for
- end_time = clock();
- botimport.Print(PRT_MESSAGE, "me %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC);
- start_time = clock();
- for (i = 0; i < 2000; i++)
- {
- AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);
- } //end for
- end_time = clock();
- botimport.Print(PRT_MESSAGE, "id %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC);
-*/
-
- // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead..
-#if 0
- AAS_ClearShownDebugLines();
- //bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID);
- bsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);
- if (!line[0]) line[0] = botimport.DebugLineCreate();
- botimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW);
- if (bsptrace.fraction < 1.0)
- {
- face = AAS_TraceEndFace(&trace);
- if (face)
- {
- AAS_ShowFace(face - aasworld.faces);
- } //end if
-
- AAS_DrawPlaneCross(bsptrace.endpos,
- bsptrace.plane.normal,
- bsptrace.plane.dist + bsptrace.exp_dist,
- bsptrace.plane.type, LINECOLOR_GREEN);
- if (trace.ent)
- {
- ent = &aasworld.entities[trace.ent];
- AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs);
- } //end if
- } //end if
- //bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID);
- bsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);
- botimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE);
- if (bsptrace.fraction < 1.0)
- {
- AAS_DrawPlaneCross(bsptrace.endpos,
- bsptrace.plane.normal,
- bsptrace.plane.dist,// + bsptrace.exp_dist,
- bsptrace.plane.type, LINECOLOR_RED);
- if (bsptrace.ent)
- {
- ent = &aasworld.entities[bsptrace.ent];
- AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs);
- } //end if
- } //end if
-#endif
-#endif
- return 0;
-} //end of the function BotExportTest
-
-
-/*
-============
-Init_AAS_Export
-============
-*/
-static void Init_AAS_Export( aas_export_t *aas ) {
- //--------------------------------------------
- // be_aas_entity.c
- //--------------------------------------------
- aas->AAS_EntityInfo = AAS_EntityInfo;
- //--------------------------------------------
- // be_aas_main.c
- //--------------------------------------------
- aas->AAS_Initialized = AAS_Initialized;
- aas->AAS_PresenceTypeBoundingBox = AAS_PresenceTypeBoundingBox;
- aas->AAS_Time = AAS_Time;
- //--------------------------------------------
- // be_aas_sample.c
- //--------------------------------------------
- aas->AAS_PointAreaNum = AAS_PointAreaNum;
- aas->AAS_PointReachabilityAreaIndex = AAS_PointReachabilityAreaIndex;
- aas->AAS_TraceAreas = AAS_TraceAreas;
- aas->AAS_BBoxAreas = AAS_BBoxAreas;
- aas->AAS_AreaInfo = AAS_AreaInfo;
- //--------------------------------------------
- // be_aas_bspq3.c
- //--------------------------------------------
- aas->AAS_PointContents = AAS_PointContents;
- aas->AAS_NextBSPEntity = AAS_NextBSPEntity;
- aas->AAS_ValueForBSPEpairKey = AAS_ValueForBSPEpairKey;
- aas->AAS_VectorForBSPEpairKey = AAS_VectorForBSPEpairKey;
- aas->AAS_FloatForBSPEpairKey = AAS_FloatForBSPEpairKey;
- aas->AAS_IntForBSPEpairKey = AAS_IntForBSPEpairKey;
- //--------------------------------------------
- // be_aas_reach.c
- //--------------------------------------------
- aas->AAS_AreaReachability = AAS_AreaReachability;
- //--------------------------------------------
- // be_aas_route.c
- //--------------------------------------------
- aas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea;
- aas->AAS_EnableRoutingArea = AAS_EnableRoutingArea;
- aas->AAS_PredictRoute = AAS_PredictRoute;
- //--------------------------------------------
- // be_aas_altroute.c
- //--------------------------------------------
- aas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals;
- //--------------------------------------------
- // be_aas_move.c
- //--------------------------------------------
- aas->AAS_Swimming = AAS_Swimming;
- aas->AAS_PredictClientMovement = AAS_PredictClientMovement;
-}
-
-
-/*
-============
-Init_EA_Export
-============
-*/
-static void Init_EA_Export( ea_export_t *ea ) {
- //ClientCommand elementary actions
- ea->EA_Command = EA_Command;
- ea->EA_Say = EA_Say;
- ea->EA_SayTeam = EA_SayTeam;
-
- ea->EA_Action = EA_Action;
- ea->EA_Gesture = EA_Gesture;
- ea->EA_Talk = EA_Talk;
- ea->EA_Attack = EA_Attack;
- ea->EA_Use = EA_Use;
- ea->EA_Respawn = EA_Respawn;
- ea->EA_Crouch = EA_Crouch;
- ea->EA_MoveUp = EA_MoveUp;
- ea->EA_MoveDown = EA_MoveDown;
- ea->EA_MoveForward = EA_MoveForward;
- ea->EA_MoveBack = EA_MoveBack;
- ea->EA_MoveLeft = EA_MoveLeft;
- ea->EA_MoveRight = EA_MoveRight;
-
- ea->EA_SelectWeapon = EA_SelectWeapon;
- ea->EA_Jump = EA_Jump;
- ea->EA_DelayedJump = EA_DelayedJump;
- ea->EA_Move = EA_Move;
- ea->EA_View = EA_View;
- ea->EA_GetInput = EA_GetInput;
- ea->EA_EndRegular = EA_EndRegular;
- ea->EA_ResetInput = EA_ResetInput;
-}
-
-
-/*
-============
-Init_AI_Export
-============
-*/
-static void Init_AI_Export( ai_export_t *ai ) {
- //-----------------------------------
- // be_ai_char.h
- //-----------------------------------
- ai->BotLoadCharacter = BotLoadCharacter;
- ai->BotFreeCharacter = BotFreeCharacter;
- ai->Characteristic_Float = Characteristic_Float;
- ai->Characteristic_BFloat = Characteristic_BFloat;
- ai->Characteristic_Integer = Characteristic_Integer;
- ai->Characteristic_BInteger = Characteristic_BInteger;
- ai->Characteristic_String = Characteristic_String;
- //-----------------------------------
- // be_ai_chat.h
- //-----------------------------------
- ai->BotAllocChatState = BotAllocChatState;
- ai->BotFreeChatState = BotFreeChatState;
- ai->BotQueueConsoleMessage = BotQueueConsoleMessage;
- ai->BotRemoveConsoleMessage = BotRemoveConsoleMessage;
- ai->BotNextConsoleMessage = BotNextConsoleMessage;
- ai->BotNumConsoleMessages = BotNumConsoleMessages;
- ai->BotInitialChat = BotInitialChat;
- ai->BotNumInitialChats = BotNumInitialChats;
- ai->BotReplyChat = BotReplyChat;
- ai->BotChatLength = BotChatLength;
- ai->BotEnterChat = BotEnterChat;
- ai->BotGetChatMessage = BotGetChatMessage;
- ai->StringContains = StringContains;
- ai->BotFindMatch = BotFindMatch;
- ai->BotMatchVariable = BotMatchVariable;
- ai->UnifyWhiteSpaces = UnifyWhiteSpaces;
- ai->BotReplaceSynonyms = BotReplaceSynonyms;
- ai->BotLoadChatFile = BotLoadChatFile;
- ai->BotSetChatGender = BotSetChatGender;
- ai->BotSetChatName = BotSetChatName;
- //-----------------------------------
- // be_ai_goal.h
- //-----------------------------------
- ai->BotResetGoalState = BotResetGoalState;
- ai->BotResetAvoidGoals = BotResetAvoidGoals;
- ai->BotRemoveFromAvoidGoals = BotRemoveFromAvoidGoals;
- ai->BotPushGoal = BotPushGoal;
- ai->BotPopGoal = BotPopGoal;
- ai->BotEmptyGoalStack = BotEmptyGoalStack;
- ai->BotDumpAvoidGoals = BotDumpAvoidGoals;
- ai->BotDumpGoalStack = BotDumpGoalStack;
- ai->BotGoalName = BotGoalName;
- ai->BotGetTopGoal = BotGetTopGoal;
- ai->BotGetSecondGoal = BotGetSecondGoal;
- ai->BotChooseLTGItem = BotChooseLTGItem;
- ai->BotChooseNBGItem = BotChooseNBGItem;
- ai->BotTouchingGoal = BotTouchingGoal;
- ai->BotItemGoalInVisButNotVisible = BotItemGoalInVisButNotVisible;
- ai->BotGetLevelItemGoal = BotGetLevelItemGoal;
- ai->BotGetNextCampSpotGoal = BotGetNextCampSpotGoal;
- ai->BotGetMapLocationGoal = BotGetMapLocationGoal;
- ai->BotAvoidGoalTime = BotAvoidGoalTime;
- ai->BotSetAvoidGoalTime = BotSetAvoidGoalTime;
- ai->BotInitLevelItems = BotInitLevelItems;
- ai->BotUpdateEntityItems = BotUpdateEntityItems;
- ai->BotLoadItemWeights = BotLoadItemWeights;
- ai->BotFreeItemWeights = BotFreeItemWeights;
- ai->BotInterbreedGoalFuzzyLogic = BotInterbreedGoalFuzzyLogic;
- ai->BotSaveGoalFuzzyLogic = BotSaveGoalFuzzyLogic;
- ai->BotMutateGoalFuzzyLogic = BotMutateGoalFuzzyLogic;
- ai->BotAllocGoalState = BotAllocGoalState;
- ai->BotFreeGoalState = BotFreeGoalState;
- //-----------------------------------
- // be_ai_move.h
- //-----------------------------------
- ai->BotResetMoveState = BotResetMoveState;
- ai->BotMoveToGoal = BotMoveToGoal;
- ai->BotMoveInDirection = BotMoveInDirection;
- ai->BotResetAvoidReach = BotResetAvoidReach;
- ai->BotResetLastAvoidReach = BotResetLastAvoidReach;
- ai->BotReachabilityArea = BotReachabilityArea;
- ai->BotMovementViewTarget = BotMovementViewTarget;
- ai->BotPredictVisiblePosition = BotPredictVisiblePosition;
- ai->BotAllocMoveState = BotAllocMoveState;
- ai->BotFreeMoveState = BotFreeMoveState;
- ai->BotInitMoveState = BotInitMoveState;
- ai->BotAddAvoidSpot = BotAddAvoidSpot;
- //-----------------------------------
- // be_ai_weap.h
- //-----------------------------------
- ai->BotChooseBestFightWeapon = BotChooseBestFightWeapon;
- ai->BotGetWeaponInfo = BotGetWeaponInfo;
- ai->BotLoadWeaponWeights = BotLoadWeaponWeights;
- ai->BotAllocWeaponState = BotAllocWeaponState;
- ai->BotFreeWeaponState = BotFreeWeaponState;
- ai->BotResetWeaponState = BotResetWeaponState;
- //-----------------------------------
- // be_ai_gen.h
- //-----------------------------------
- ai->GeneticParentsAndChildSelection = GeneticParentsAndChildSelection;
-}
-
-
-/*
-============
-GetBotLibAPI
-============
-*/
-botlib_export_t *GetBotLibAPI(int apiVersion, botlib_import_t *import) {
- assert(import); // bk001129 - this wasn't set for baseq3/
- botimport = *import;
- assert(botimport.Print); // bk001129 - pars pro toto
-
- Com_Memset( &be_botlib_export, 0, sizeof( be_botlib_export ) );
-
- if ( apiVersion != BOTLIB_API_VERSION ) {
- botimport.Print( PRT_ERROR, "Mismatched BOTLIB_API_VERSION: expected %i, got %i\n", BOTLIB_API_VERSION, apiVersion );
- return NULL;
- }
-
- Init_AAS_Export(&be_botlib_export.aas);
- Init_EA_Export(&be_botlib_export.ea);
- Init_AI_Export(&be_botlib_export.ai);
-
- be_botlib_export.BotLibSetup = Export_BotLibSetup;
- be_botlib_export.BotLibShutdown = Export_BotLibShutdown;
- be_botlib_export.BotLibVarSet = Export_BotLibVarSet;
- be_botlib_export.BotLibVarGet = Export_BotLibVarGet;
-
- be_botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine;
- be_botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle;
- be_botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle;
- be_botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle;
- be_botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine;
-
- be_botlib_export.BotLibStartFrame = Export_BotLibStartFrame;
- be_botlib_export.BotLibLoadMap = Export_BotLibLoadMap;
- be_botlib_export.BotLibUpdateEntity = Export_BotLibUpdateEntity;
- be_botlib_export.Test = BotExportTest;
-
- return &be_botlib_export;
-}
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_interface.c // bk010221 - FIXME - DEAD code elimination + * + * desc: bot library interface + * + * $Archive: /MissionPack/code/botlib/be_interface.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_libvar.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" +#include "be_interface.h" + +#include "../game/be_ea.h" +#include "be_ai_weight.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_gen.h" + +//library globals in a structure +botlib_globals_t botlibglobals; + +botlib_export_t be_botlib_export; +botlib_import_t botimport; +// +int bot_developer; +//qtrue if the library is setup +int botlibsetup = qfalse; + +//=========================================================================== +// +// several functions used by the exported functions +// +//=========================================================================== + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Sys_MilliSeconds(void) +{ + return clock() * 1000 / CLOCKS_PER_SEC; +} //end of the function Sys_MilliSeconds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ValidClientNumber(int num, char *str) +{ + if (num < 0 || num > botlibglobals.maxclients) + { + //weird: the disabled stuff results in a crash + botimport.Print(PRT_ERROR, "%s: invalid client number %d, [0, %d]\n", + str, num, botlibglobals.maxclients); + return qfalse; + } //end if + return qtrue; +} //end of the function BotValidateClientNumber +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ValidEntityNumber(int num, char *str) +{ + if (num < 0 || num > botlibglobals.maxentities) + { + botimport.Print(PRT_ERROR, "%s: invalid entity number %d, [0, %d]\n", + str, num, botlibglobals.maxentities); + return qfalse; + } //end if + return qtrue; +} //end of the function BotValidateClientNumber +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean BotLibSetup(char *str) +{ + if (!botlibglobals.botlibsetup) + { + botimport.Print(PRT_ERROR, "%s: bot library used before being setup\n", str); + return qfalse; + } //end if + return qtrue; +} //end of the function BotLibSetup + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibSetup(void) +{ + int errnum; + + bot_developer = LibVarGetValue("bot_developer"); + memset( &botlibglobals, 0, sizeof(botlibglobals) ); // bk001207 - init + //initialize byte swapping (litte endian etc.) +// Swap_Init(); + Log_Open("botlib.log"); + // + botimport.Print(PRT_MESSAGE, "------- BotLib Initialization -------\n"); + // + botlibglobals.maxclients = (int) LibVarValue("maxclients", "128"); + botlibglobals.maxentities = (int) LibVarValue("maxentities", "1024"); + + errnum = AAS_Setup(); //be_aas_main.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = EA_Setup(); //be_ea.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = BotSetupWeaponAI(); //be_ai_weap.c + if (errnum != BLERR_NOERROR)return errnum; + errnum = BotSetupGoalAI(); //be_ai_goal.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = BotSetupChatAI(); //be_ai_chat.c + if (errnum != BLERR_NOERROR) return errnum; + errnum = BotSetupMoveAI(); //be_ai_move.c + if (errnum != BLERR_NOERROR) return errnum; + + botlibsetup = qtrue; + botlibglobals.botlibsetup = qtrue; + + return BLERR_NOERROR; +} //end of the function Export_BotLibSetup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibShutdown(void) +{ + if (!BotLibSetup("BotLibShutdown")) return BLERR_LIBRARYNOTSETUP; +#ifndef DEMO + //DumpFileCRCs(); +#endif //DEMO + // + BotShutdownChatAI(); //be_ai_chat.c + BotShutdownMoveAI(); //be_ai_move.c + BotShutdownGoalAI(); //be_ai_goal.c + BotShutdownWeaponAI(); //be_ai_weap.c + BotShutdownWeights(); //be_ai_weight.c + BotShutdownCharacters(); //be_ai_char.c + //shud down aas + AAS_Shutdown(); + //shut down bot elemantary actions + EA_Shutdown(); + //free all libvars + LibVarDeAllocAll(); + //remove all global defines from the pre compiler + PC_RemoveAllGlobalDefines(); + + //dump all allocated memory +// DumpMemory(); +#ifdef DEBUG + PrintMemoryLabels(); +#endif + //shut down library log file + Log_Shutdown(); + // + botlibsetup = qfalse; + botlibglobals.botlibsetup = qfalse; + // print any files still open + PC_CheckOpenSourceHandles(); + // + return BLERR_NOERROR; +} //end of the function Export_BotLibShutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibVarSet(char *var_name, char *value) +{ + LibVarSet(var_name, value); + return BLERR_NOERROR; +} //end of the function Export_BotLibVarSet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibVarGet(char *var_name, char *value, int size) +{ + char *varvalue; + + varvalue = LibVarGetString(var_name); + strncpy(value, varvalue, size-1); + value[size-1] = '\0'; + return BLERR_NOERROR; +} //end of the function Export_BotLibVarGet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibStartFrame(float time) +{ + if (!BotLibSetup("BotStartFrame")) return BLERR_LIBRARYNOTSETUP; + return AAS_StartFrame(time); +} //end of the function Export_BotLibStartFrame +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibLoadMap(const char *mapname) +{ +#ifdef DEBUG + int starttime = Sys_MilliSeconds(); +#endif + int errnum; + + if (!BotLibSetup("BotLoadMap")) return BLERR_LIBRARYNOTSETUP; + // + botimport.Print(PRT_MESSAGE, "------------ Map Loading ------------\n"); + //startup AAS for the current map, model and sound index + errnum = AAS_LoadMap(mapname); + if (errnum != BLERR_NOERROR) return errnum; + //initialize the items in the level + BotInitLevelItems(); //be_ai_goal.h + BotSetBrushModelTypes(); //be_ai_move.h + // + botimport.Print(PRT_MESSAGE, "-------------------------------------\n"); +#ifdef DEBUG + botimport.Print(PRT_MESSAGE, "map loaded in %d msec\n", Sys_MilliSeconds() - starttime); +#endif + // + return BLERR_NOERROR; +} //end of the function Export_BotLibLoadMap +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Export_BotLibUpdateEntity(int ent, bot_entitystate_t *state) +{ + if (!BotLibSetup("BotUpdateEntity")) return BLERR_LIBRARYNOTSETUP; + if (!ValidEntityNumber(ent, "BotUpdateEntity")) return BLERR_INVALIDENTITYNUMBER; + + return AAS_UpdateEntity(ent, state); +} //end of the function Export_BotLibUpdateEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir); +void ElevatorBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter); +int BotGetReachabilityToGoal(vec3_t origin, int areanum, + int lastgoalareanum, int lastareanum, + int *avoidreach, float *avoidreachtimes, int *avoidreachtries, + bot_goal_t *goal, int travelflags, int movetravelflags, + struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags); + +int AAS_PointLight(vec3_t origin, int *red, int *green, int *blue); + +int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas); + +int AAS_Reachability_WeaponJump(int area1num, int area2num); + +int BotFuzzyPointReachabilityArea(vec3_t origin); + +float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum); + +void AAS_FloodAreas(vec3_t origin); + +int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) +{ + +// return AAS_PointLight(parm2, NULL, NULL, NULL); + +#ifdef DEBUG + static int area = -1; + static int line[2]; + int newarea, i, highlightarea, flood; +// int reachnum; + vec3_t eye, forward, right, end, origin; +// vec3_t bottomcenter; +// aas_trace_t trace; +// aas_face_t *face; +// aas_entity_t *ent; +// bsp_trace_t bsptrace; +// aas_reachability_t reach; +// bot_goal_t goal; + + // clock_t start_time, end_time; + vec3_t mins = {-16, -16, -24}; + vec3_t maxs = {16, 16, 32}; + +// int areas[10], numareas; + + + //return 0; + + if (!aasworld.loaded) return 0; + + /* + if (parm0 & 1) + { + AAS_ClearShownPolygons(); + AAS_FloodAreas(parm2); + } //end if + return 0; + */ + for (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate(); + +// AAS_ClearShownDebugLines(); + + //if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, "against ladder\n"); + //BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea); + //botimport.Print(PRT_MESSAGE, "%f %f %f\n", parm2[0], parm2[1], parm2[2]); + //* + highlightarea = LibVarGetValue("bot_highlightarea"); + if (highlightarea > 0) + { + newarea = highlightarea; + } //end if + else + { + VectorCopy(parm2, origin); + origin[2] += 0.5; + //newarea = AAS_PointAreaNum(origin); + newarea = BotFuzzyPointReachabilityArea(origin); + } //end else + + botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum, + AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT)); + //newarea = BotReachabilityArea(origin, qtrue); + if (newarea != area) + { + botimport.Print(PRT_MESSAGE, "origin = %f, %f, %f\n", origin[0], origin[1], origin[2]); + area = newarea; + botimport.Print(PRT_MESSAGE, "new area %d, cluster %d, presence type %d\n", + area, AAS_AreaCluster(area), AAS_PointPresenceType(origin)); + botimport.Print(PRT_MESSAGE, "area contents: "); + if (aasworld.areasettings[area].contents & AREACONTENTS_WATER) + { + botimport.Print(PRT_MESSAGE, "water &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_LAVA) + { + botimport.Print(PRT_MESSAGE, "lava &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_SLIME) + { + botimport.Print(PRT_MESSAGE, "slime &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD) + { + botimport.Print(PRT_MESSAGE, "jump pad &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL) + { + botimport.Print(PRT_MESSAGE, "cluster portal &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL) + { + botimport.Print(PRT_MESSAGE, "view portal &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER) + { + botimport.Print(PRT_MESSAGE, "do not enter &"); + } //end if + if (aasworld.areasettings[area].contents & AREACONTENTS_MOVER) + { + botimport.Print(PRT_MESSAGE, "mover &"); + } //end if + if (!aasworld.areasettings[area].contents) + { + botimport.Print(PRT_MESSAGE, "empty"); + } //end if + botimport.Print(PRT_MESSAGE, "\n"); + botimport.Print(PRT_MESSAGE, "travel time to goal (%d) = %d\n", botlibglobals.goalareanum, + AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP)); + /* + VectorCopy(origin, end); + end[2] += 5; + numareas = AAS_TraceAreas(origin, end, areas, NULL, 10); + AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); + botimport.Print(PRT_MESSAGE, "num areas = %d, area = %d\n", numareas, areas[0]); + */ + /* + botlibglobals.goalareanum = newarea; + VectorCopy(parm2, botlibglobals.goalorigin); + botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", + origin[0], origin[1], origin[2], newarea); + */ + } //end if + //* + flood = LibVarGetValue("bot_flood"); + if (parm0 & 1) + { + if (flood) + { + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + AAS_FloodAreas(parm2); + } + else + { + botlibglobals.goalareanum = newarea; + VectorCopy(parm2, botlibglobals.goalorigin); + botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", + origin[0], origin[1], origin[2], newarea); + } + } //end if*/ + if (flood) + return 0; +// if (parm0 & BUTTON_USE) +// { +// botlibglobals.runai = !botlibglobals.runai; +// if (botlibglobals.runai) botimport.Print(PRT_MESSAGE, "started AI\n"); +// else botimport.Print(PRT_MESSAGE, "stopped AI\n"); + //* / + /* + goal.areanum = botlibglobals.goalareanum; + reachnum = BotGetReachabilityToGoal(parm2, newarea, 1, + ms.avoidreach, ms.avoidreachtimes, + &goal, TFL_DEFAULT); + if (!reachnum) + { + botimport.Print(PRT_MESSAGE, "goal not reachable\n"); + } //end if + else + { + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ClearShownDebugLines(); + AAS_ShowArea(area, qtrue); + AAS_ShowArea(reach.areanum, qtrue); + AAS_DrawCross(reach.start, 6, LINECOLOR_BLUE); + AAS_DrawCross(reach.end, 6, LINECOLOR_RED); + // + if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) + { + ElevatorBottomCenter(&reach, bottomcenter); + AAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN); + } //end if + } //end else*/ +// botimport.Print(PRT_MESSAGE, "travel time to goal = %d\n", +// AAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT)); +// botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); +// AAS_Reachability_WeaponJump(703, 716); +// } //end if*/ + +/* face = AAS_AreaGroundFace(newarea, parm2); + if (face) + { + AAS_ShowFace(face - aasworld.faces); + } //end if*/ + /* + AAS_ClearShownDebugLines(); + AAS_ShowArea(newarea, parm0 & BUTTON_USE); + AAS_ShowReachableAreas(area); + */ + AAS_ClearShownPolygons(); + AAS_ClearShownDebugLines(); + AAS_ShowAreaPolygons(newarea, 1, parm0 & 4); + if (parm0 & 2) AAS_ShowReachableAreas(area); + else + { + static int lastgoalareanum, lastareanum; + static int avoidreach[MAX_AVOIDREACH]; + static float avoidreachtimes[MAX_AVOIDREACH]; + static int avoidreachtries[MAX_AVOIDREACH]; + int reachnum, resultFlags; + bot_goal_t goal; + aas_reachability_t reach; + + /* + goal.areanum = botlibglobals.goalareanum; + VectorCopy(botlibglobals.goalorigin, goal.origin); + reachnum = BotGetReachabilityToGoal(origin, newarea, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, + NULL, 0, &resultFlags); + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ShowReachability(&reach); + */ + int curarea; + vec3_t curorigin; + + goal.areanum = botlibglobals.goalareanum; + VectorCopy(botlibglobals.goalorigin, goal.origin); + VectorCopy(origin, curorigin); + curarea = newarea; + for ( i = 0; i < 100; i++ ) { + if ( curarea == goal.areanum ) { + break; + } + reachnum = BotGetReachabilityToGoal(curorigin, curarea, + lastgoalareanum, lastareanum, + avoidreach, avoidreachtimes, avoidreachtries, + &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, + NULL, 0, &resultFlags); + AAS_ReachabilityFromNum(reachnum, &reach); + AAS_ShowReachability(&reach); + VectorCopy(reach.end, origin); + lastareanum = curarea; + curarea = reach.areanum; + } + } //end else + VectorClear(forward); + //BotGapDistance(origin, forward, 0); + /* + if (parm0 & BUTTON_USE) + { + botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); + AAS_Reachability_WeaponJump(703, 716); + } //end if*/ + + AngleVectors(parm3, forward, right, NULL); + //get the eye 16 units to the right of the origin + VectorMA(parm2, 8, right, eye); + //get the eye 24 units up + eye[2] += 24; + //get the end point for the line to be traced + VectorMA(eye, 800, forward, end); + +// AAS_TestMovementPrediction(1, parm2, forward); +/* + //trace the line to find the hit point + trace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); + if (!line[0]) line[0] = botimport.DebugLineCreate(); + botimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE); + // + AAS_ClearShownDebugLines(); + if (trace.ent) + { + ent = &aasworld.entities[trace.ent]; + AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); + } //end if +*/ + +/* + start_time = clock(); + for (i = 0; i < 2000; i++) + { + AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); +// AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); + } //end for + end_time = clock(); + botimport.Print(PRT_MESSAGE, "me %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); + start_time = clock(); + for (i = 0; i < 2000; i++) + { + AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); + } //end for + end_time = clock(); + botimport.Print(PRT_MESSAGE, "id %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); +*/ + + // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead.. +#if 0 + AAS_ClearShownDebugLines(); + //bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); + bsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); + if (!line[0]) line[0] = botimport.DebugLineCreate(); + botimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW); + if (bsptrace.fraction < 1.0) + { + face = AAS_TraceEndFace(&trace); + if (face) + { + AAS_ShowFace(face - aasworld.faces); + } //end if + + AAS_DrawPlaneCross(bsptrace.endpos, + bsptrace.plane.normal, + bsptrace.plane.dist + bsptrace.exp_dist, + bsptrace.plane.type, LINECOLOR_GREEN); + if (trace.ent) + { + ent = &aasworld.entities[trace.ent]; + AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); + } //end if + } //end if + //bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); + bsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); + botimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE); + if (bsptrace.fraction < 1.0) + { + AAS_DrawPlaneCross(bsptrace.endpos, + bsptrace.plane.normal, + bsptrace.plane.dist,// + bsptrace.exp_dist, + bsptrace.plane.type, LINECOLOR_RED); + if (bsptrace.ent) + { + ent = &aasworld.entities[bsptrace.ent]; + AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); + } //end if + } //end if +#endif +#endif + return 0; +} //end of the function BotExportTest + + +/* +============ +Init_AAS_Export +============ +*/ +static void Init_AAS_Export( aas_export_t *aas ) { + //-------------------------------------------- + // be_aas_entity.c + //-------------------------------------------- + aas->AAS_EntityInfo = AAS_EntityInfo; + //-------------------------------------------- + // be_aas_main.c + //-------------------------------------------- + aas->AAS_Initialized = AAS_Initialized; + aas->AAS_PresenceTypeBoundingBox = AAS_PresenceTypeBoundingBox; + aas->AAS_Time = AAS_Time; + //-------------------------------------------- + // be_aas_sample.c + //-------------------------------------------- + aas->AAS_PointAreaNum = AAS_PointAreaNum; + aas->AAS_PointReachabilityAreaIndex = AAS_PointReachabilityAreaIndex; + aas->AAS_TraceAreas = AAS_TraceAreas; + aas->AAS_BBoxAreas = AAS_BBoxAreas; + aas->AAS_AreaInfo = AAS_AreaInfo; + //-------------------------------------------- + // be_aas_bspq3.c + //-------------------------------------------- + aas->AAS_PointContents = AAS_PointContents; + aas->AAS_NextBSPEntity = AAS_NextBSPEntity; + aas->AAS_ValueForBSPEpairKey = AAS_ValueForBSPEpairKey; + aas->AAS_VectorForBSPEpairKey = AAS_VectorForBSPEpairKey; + aas->AAS_FloatForBSPEpairKey = AAS_FloatForBSPEpairKey; + aas->AAS_IntForBSPEpairKey = AAS_IntForBSPEpairKey; + //-------------------------------------------- + // be_aas_reach.c + //-------------------------------------------- + aas->AAS_AreaReachability = AAS_AreaReachability; + //-------------------------------------------- + // be_aas_route.c + //-------------------------------------------- + aas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea; + aas->AAS_EnableRoutingArea = AAS_EnableRoutingArea; + aas->AAS_PredictRoute = AAS_PredictRoute; + //-------------------------------------------- + // be_aas_altroute.c + //-------------------------------------------- + aas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals; + //-------------------------------------------- + // be_aas_move.c + //-------------------------------------------- + aas->AAS_Swimming = AAS_Swimming; + aas->AAS_PredictClientMovement = AAS_PredictClientMovement; +} + + +/* +============ +Init_EA_Export +============ +*/ +static void Init_EA_Export( ea_export_t *ea ) { + //ClientCommand elementary actions + ea->EA_Command = EA_Command; + ea->EA_Say = EA_Say; + ea->EA_SayTeam = EA_SayTeam; + + ea->EA_Action = EA_Action; + ea->EA_Gesture = EA_Gesture; + ea->EA_Talk = EA_Talk; + ea->EA_Attack = EA_Attack; + ea->EA_Use = EA_Use; + ea->EA_Respawn = EA_Respawn; + ea->EA_Crouch = EA_Crouch; + ea->EA_MoveUp = EA_MoveUp; + ea->EA_MoveDown = EA_MoveDown; + ea->EA_MoveForward = EA_MoveForward; + ea->EA_MoveBack = EA_MoveBack; + ea->EA_MoveLeft = EA_MoveLeft; + ea->EA_MoveRight = EA_MoveRight; + + ea->EA_SelectWeapon = EA_SelectWeapon; + ea->EA_Jump = EA_Jump; + ea->EA_DelayedJump = EA_DelayedJump; + ea->EA_Move = EA_Move; + ea->EA_View = EA_View; + ea->EA_GetInput = EA_GetInput; + ea->EA_EndRegular = EA_EndRegular; + ea->EA_ResetInput = EA_ResetInput; +} + + +/* +============ +Init_AI_Export +============ +*/ +static void Init_AI_Export( ai_export_t *ai ) { + //----------------------------------- + // be_ai_char.h + //----------------------------------- + ai->BotLoadCharacter = BotLoadCharacter; + ai->BotFreeCharacter = BotFreeCharacter; + ai->Characteristic_Float = Characteristic_Float; + ai->Characteristic_BFloat = Characteristic_BFloat; + ai->Characteristic_Integer = Characteristic_Integer; + ai->Characteristic_BInteger = Characteristic_BInteger; + ai->Characteristic_String = Characteristic_String; + //----------------------------------- + // be_ai_chat.h + //----------------------------------- + ai->BotAllocChatState = BotAllocChatState; + ai->BotFreeChatState = BotFreeChatState; + ai->BotQueueConsoleMessage = BotQueueConsoleMessage; + ai->BotRemoveConsoleMessage = BotRemoveConsoleMessage; + ai->BotNextConsoleMessage = BotNextConsoleMessage; + ai->BotNumConsoleMessages = BotNumConsoleMessages; + ai->BotInitialChat = BotInitialChat; + ai->BotNumInitialChats = BotNumInitialChats; + ai->BotReplyChat = BotReplyChat; + ai->BotChatLength = BotChatLength; + ai->BotEnterChat = BotEnterChat; + ai->BotGetChatMessage = BotGetChatMessage; + ai->StringContains = StringContains; + ai->BotFindMatch = BotFindMatch; + ai->BotMatchVariable = BotMatchVariable; + ai->UnifyWhiteSpaces = UnifyWhiteSpaces; + ai->BotReplaceSynonyms = BotReplaceSynonyms; + ai->BotLoadChatFile = BotLoadChatFile; + ai->BotSetChatGender = BotSetChatGender; + ai->BotSetChatName = BotSetChatName; + //----------------------------------- + // be_ai_goal.h + //----------------------------------- + ai->BotResetGoalState = BotResetGoalState; + ai->BotResetAvoidGoals = BotResetAvoidGoals; + ai->BotRemoveFromAvoidGoals = BotRemoveFromAvoidGoals; + ai->BotPushGoal = BotPushGoal; + ai->BotPopGoal = BotPopGoal; + ai->BotEmptyGoalStack = BotEmptyGoalStack; + ai->BotDumpAvoidGoals = BotDumpAvoidGoals; + ai->BotDumpGoalStack = BotDumpGoalStack; + ai->BotGoalName = BotGoalName; + ai->BotGetTopGoal = BotGetTopGoal; + ai->BotGetSecondGoal = BotGetSecondGoal; + ai->BotChooseLTGItem = BotChooseLTGItem; + ai->BotChooseNBGItem = BotChooseNBGItem; + ai->BotTouchingGoal = BotTouchingGoal; + ai->BotItemGoalInVisButNotVisible = BotItemGoalInVisButNotVisible; + ai->BotGetLevelItemGoal = BotGetLevelItemGoal; + ai->BotGetNextCampSpotGoal = BotGetNextCampSpotGoal; + ai->BotGetMapLocationGoal = BotGetMapLocationGoal; + ai->BotAvoidGoalTime = BotAvoidGoalTime; + ai->BotSetAvoidGoalTime = BotSetAvoidGoalTime; + ai->BotInitLevelItems = BotInitLevelItems; + ai->BotUpdateEntityItems = BotUpdateEntityItems; + ai->BotLoadItemWeights = BotLoadItemWeights; + ai->BotFreeItemWeights = BotFreeItemWeights; + ai->BotInterbreedGoalFuzzyLogic = BotInterbreedGoalFuzzyLogic; + ai->BotSaveGoalFuzzyLogic = BotSaveGoalFuzzyLogic; + ai->BotMutateGoalFuzzyLogic = BotMutateGoalFuzzyLogic; + ai->BotAllocGoalState = BotAllocGoalState; + ai->BotFreeGoalState = BotFreeGoalState; + //----------------------------------- + // be_ai_move.h + //----------------------------------- + ai->BotResetMoveState = BotResetMoveState; + ai->BotMoveToGoal = BotMoveToGoal; + ai->BotMoveInDirection = BotMoveInDirection; + ai->BotResetAvoidReach = BotResetAvoidReach; + ai->BotResetLastAvoidReach = BotResetLastAvoidReach; + ai->BotReachabilityArea = BotReachabilityArea; + ai->BotMovementViewTarget = BotMovementViewTarget; + ai->BotPredictVisiblePosition = BotPredictVisiblePosition; + ai->BotAllocMoveState = BotAllocMoveState; + ai->BotFreeMoveState = BotFreeMoveState; + ai->BotInitMoveState = BotInitMoveState; + ai->BotAddAvoidSpot = BotAddAvoidSpot; + //----------------------------------- + // be_ai_weap.h + //----------------------------------- + ai->BotChooseBestFightWeapon = BotChooseBestFightWeapon; + ai->BotGetWeaponInfo = BotGetWeaponInfo; + ai->BotLoadWeaponWeights = BotLoadWeaponWeights; + ai->BotAllocWeaponState = BotAllocWeaponState; + ai->BotFreeWeaponState = BotFreeWeaponState; + ai->BotResetWeaponState = BotResetWeaponState; + //----------------------------------- + // be_ai_gen.h + //----------------------------------- + ai->GeneticParentsAndChildSelection = GeneticParentsAndChildSelection; +} + + +/* +============ +GetBotLibAPI +============ +*/ +botlib_export_t *GetBotLibAPI(int apiVersion, botlib_import_t *import) { + assert(import); // bk001129 - this wasn't set for baseq3/ + botimport = *import; + assert(botimport.Print); // bk001129 - pars pro toto + + Com_Memset( &be_botlib_export, 0, sizeof( be_botlib_export ) ); + + if ( apiVersion != BOTLIB_API_VERSION ) { + botimport.Print( PRT_ERROR, "Mismatched BOTLIB_API_VERSION: expected %i, got %i\n", BOTLIB_API_VERSION, apiVersion ); + return NULL; + } + + Init_AAS_Export(&be_botlib_export.aas); + Init_EA_Export(&be_botlib_export.ea); + Init_AI_Export(&be_botlib_export.ai); + + be_botlib_export.BotLibSetup = Export_BotLibSetup; + be_botlib_export.BotLibShutdown = Export_BotLibShutdown; + be_botlib_export.BotLibVarSet = Export_BotLibVarSet; + be_botlib_export.BotLibVarGet = Export_BotLibVarGet; + + be_botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine; + be_botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle; + be_botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle; + be_botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle; + be_botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine; + + be_botlib_export.BotLibStartFrame = Export_BotLibStartFrame; + be_botlib_export.BotLibLoadMap = Export_BotLibLoadMap; + be_botlib_export.BotLibUpdateEntity = Export_BotLibUpdateEntity; + be_botlib_export.Test = BotExportTest; + + return &be_botlib_export; +} diff --git a/code/botlib/be_interface.h b/code/botlib/be_interface.h index a9f6347..bdeb512 100755 --- a/code/botlib/be_interface.h +++ b/code/botlib/be_interface.h @@ -1,57 +1,57 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: be_interface.h
- *
- * desc: botlib interface
- *
- * $Archive: /source/code/botlib/be_interface.h $
- *
- *****************************************************************************/
-
-//#define DEBUG //debug code
-#define RANDOMIZE //randomize bot behaviour
-
-//FIXME: get rid of this global structure
-typedef struct botlib_globals_s
-{
- int botlibsetup; //true when the bot library has been setup
- int maxentities; //maximum number of entities
- int maxclients; //maximum number of clients
- float time; //the global time
-#ifdef DEBUG
- qboolean debug; //true if debug is on
- int goalareanum;
- vec3_t goalorigin;
- int runai;
-#endif
-} botlib_globals_t;
-
-
-extern botlib_globals_t botlibglobals;
-extern botlib_import_t botimport;
-extern int bot_developer; //true if developer is on
-
-//
-int Sys_MilliSeconds(void);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: be_interface.h + * + * desc: botlib interface + * + * $Archive: /source/code/botlib/be_interface.h $ + * + *****************************************************************************/ + +//#define DEBUG //debug code +#define RANDOMIZE //randomize bot behaviour + +//FIXME: get rid of this global structure +typedef struct botlib_globals_s +{ + int botlibsetup; //true when the bot library has been setup + int maxentities; //maximum number of entities + int maxclients; //maximum number of clients + float time; //the global time +#ifdef DEBUG + qboolean debug; //true if debug is on + int goalareanum; + vec3_t goalorigin; + int runai; +#endif +} botlib_globals_t; + + +extern botlib_globals_t botlibglobals; +extern botlib_import_t botimport; +extern int bot_developer; //true if developer is on + +// +int Sys_MilliSeconds(void); + diff --git a/code/botlib/botlib.vcproj b/code/botlib/botlib.vcproj index e544a14..498c788 100755 --- a/code/botlib/botlib.vcproj +++ b/code/botlib/botlib.vcproj @@ -1,1559 +1,1559 @@ -<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="7.10"
- Name="botlib"
- SccProjectName=""$/MissionPack/code/botlib", HBAAAAAA"
- SccLocalPath=".">
- <Platforms>
- <Platform
- Name="Win32"/>
- </Platforms>
- <Configurations>
- <Configuration
- Name="Debug TA|Win32"
- OutputDirectory=".\Debug_TA"
- IntermediateDirectory=".\Debug_TA"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- OptimizeForProcessor="1"
- PreprocessorDefinitions="WIN32;_DEBUG;_LIB;BOTLIB;DEBUG"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="2"
- PrecompiledHeaderFile=".\Debug_TA/botlib.pch"
- AssemblerListingLocation=".\Debug_TA/"
- ObjectFile=".\Debug_TA/"
- ProgramDataBaseFileName=".\Debug_TA/"
- BrowseInformation="1"
- WarningLevel="3"
- SuppressStartupBanner="TRUE"
- DebugInformationFormat="4"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile=".\Debug_TA\botlib.lib"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="_DEBUG"
- Culture="1033"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory=".\Release"
- IntermediateDirectory=".\Release"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- InlineFunctionExpansion="1"
- OptimizeForProcessor="2"
- PreprocessorDefinitions="WIN32;NDEBUG;_LIB;BOTLIB"
- StringPooling="TRUE"
- RuntimeLibrary="4"
- EnableFunctionLevelLinking="TRUE"
- UsePrecompiledHeader="2"
- PrecompiledHeaderFile=".\Release/botlib.pch"
- AssemblerListingLocation=".\Release/"
- ObjectFile=".\Release/"
- ProgramDataBaseFileName=".\Release/"
- WarningLevel="4"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile=".\Release\botlib.lib"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="NDEBUG"
- Culture="1033"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- <Configuration
- Name="vector|Win32"
- OutputDirectory=".\botlib___Win32_vector0"
- IntermediateDirectory=".\botlib___Win32_vector0"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/vec /vec:stats /vec:pii /vec:o10 /vec:yes "
- Optimization="2"
- InlineFunctionExpansion="1"
- OptimizeForProcessor="2"
- PreprocessorDefinitions="WIN32;NDEBUG;_LIB;BOTLIB"
- StringPooling="TRUE"
- RuntimeLibrary="4"
- EnableFunctionLevelLinking="TRUE"
- UsePrecompiledHeader="2"
- PrecompiledHeaderFile=".\botlib___Win32_vector0/botlib.pch"
- AssemblerListingLocation=".\botlib___Win32_vector0/"
- ObjectFile=".\botlib___Win32_vector0/"
- ProgramDataBaseFileName=".\botlib___Win32_vector0/"
- WarningLevel="4"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile=".\botlib___Win32_vector0\botlib.lib"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="NDEBUG"
- Culture="1033"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory=".\Debug"
- IntermediateDirectory=".\Debug"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- OptimizeForProcessor="1"
- PreprocessorDefinitions="WIN32;_DEBUG;_LIB;BOTLIB;DEBUG"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="2"
- PrecompiledHeaderFile=".\Debug/botlib.pch"
- AssemblerListingLocation=".\Debug/"
- ObjectFile=".\Debug/"
- ProgramDataBaseFileName=".\Debug/"
- BrowseInformation="1"
- WarningLevel="3"
- SuppressStartupBanner="TRUE"
- DebugInformationFormat="4"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile=".\Debug\botlib.lib"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="_DEBUG"
- Culture="1033"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- <Configuration
- Name="Release TA|Win32"
- OutputDirectory=".\Release_TA"
- IntermediateDirectory=".\Release_TA"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- InlineFunctionExpansion="1"
- OptimizeForProcessor="2"
- PreprocessorDefinitions="WIN32;NDEBUG;_LIB;BOTLIB"
- StringPooling="TRUE"
- RuntimeLibrary="4"
- EnableFunctionLevelLinking="TRUE"
- UsePrecompiledHeader="2"
- PrecompiledHeaderFile=".\Release_TA/botlib.pch"
- AssemblerListingLocation=".\Release_TA/"
- ObjectFile=".\Release_TA/"
- ProgramDataBaseFileName=".\Release_TA/"
- WarningLevel="4"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile=".\Release_TA\botlib.lib"
- SuppressStartupBanner="TRUE"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- PreprocessorDefinitions="NDEBUG"
- Culture="1033"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
- <File
- RelativePath="be_aas_bspq3.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_cluster.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_debug.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_entity.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_file.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_main.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_move.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_optimize.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_reach.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_route.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_routealt.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_aas_sample.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_char.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_chat.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_gen.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_goal.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_move.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_weap.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ai_weight.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_ea.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="be_interface.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_crc.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_libvar.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_log.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_memory.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_precomp.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_script.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="l_struct.c">
- <FileConfiguration
- Name="Debug TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="vector|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- PreprocessorDefinitions=""
- BasicRuntimeChecks="3"
- BrowseInformation="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release TA|Win32">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="2"
- PreprocessorDefinitions=""/>
- </FileConfiguration>
- </File>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hm;inl">
- <File
- RelativePath="aasfile.h">
- </File>
- <File
- RelativePath="be_aas_bsp.h">
- </File>
- <File
- RelativePath="be_aas_cluster.h">
- </File>
- <File
- RelativePath="be_aas_debug.h">
- </File>
- <File
- RelativePath="be_aas_def.h">
- </File>
- <File
- RelativePath="be_aas_entity.h">
- </File>
- <File
- RelativePath="be_aas_file.h">
- </File>
- <File
- RelativePath="be_aas_funcs.h">
- </File>
- <File
- RelativePath="be_aas_main.h">
- </File>
- <File
- RelativePath="be_aas_move.h">
- </File>
- <File
- RelativePath="be_aas_optimize.h">
- </File>
- <File
- RelativePath="be_aas_reach.h">
- </File>
- <File
- RelativePath="be_aas_route.h">
- </File>
- <File
- RelativePath="be_aas_routealt.h">
- </File>
- <File
- RelativePath="be_aas_sample.h">
- </File>
- <File
- RelativePath="be_ai_weight.h">
- </File>
- <File
- RelativePath="be_interface.h">
- </File>
- <File
- RelativePath="..\game\bg_public.h">
- </File>
- <File
- RelativePath="..\qcommon\cm_public.h">
- </File>
- <File
- RelativePath="..\game\g_public.h">
- </File>
- <File
- RelativePath="l_crc.h">
- </File>
- <File
- RelativePath="l_libvar.h">
- </File>
- <File
- RelativePath="l_log.h">
- </File>
- <File
- RelativePath="l_memory.h">
- </File>
- <File
- RelativePath="l_precomp.h">
- </File>
- <File
- RelativePath="l_script.h">
- </File>
- <File
- RelativePath="l_struct.h">
- </File>
- <File
- RelativePath="l_utils.h">
- </File>
- <File
- RelativePath="..\game\q_shared.h">
- </File>
- <File
- RelativePath="..\qcommon\qcommon.h">
- </File>
- <File
- RelativePath="..\qcommon\qfiles.h">
- </File>
- <File
- RelativePath="..\server\server.h">
- </File>
- <File
- RelativePath="..\game\surfaceflags.h">
- </File>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="botlib" + SccProjectName=""$/MissionPack/code/botlib", HBAAAAAA" + SccLocalPath="."> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug TA|Win32" + OutputDirectory=".\Debug_TA" + IntermediateDirectory=".\Debug_TA" + ConfigurationType="4" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + OptimizeForProcessor="1" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB;BOTLIB;DEBUG" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Debug_TA/botlib.pch" + AssemblerListingLocation=".\Debug_TA/" + ObjectFile=".\Debug_TA/" + ProgramDataBaseFileName=".\Debug_TA/" + BrowseInformation="1" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile=".\Debug_TA\botlib.lib" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="_DEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory=".\Release" + IntermediateDirectory=".\Release" + ConfigurationType="4" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + InlineFunctionExpansion="1" + OptimizeForProcessor="2" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB;BOTLIB" + StringPooling="TRUE" + RuntimeLibrary="4" + EnableFunctionLevelLinking="TRUE" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Release/botlib.pch" + AssemblerListingLocation=".\Release/" + ObjectFile=".\Release/" + ProgramDataBaseFileName=".\Release/" + WarningLevel="4" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile=".\Release\botlib.lib" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="NDEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="vector|Win32" + OutputDirectory=".\botlib___Win32_vector0" + IntermediateDirectory=".\botlib___Win32_vector0" + ConfigurationType="4" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/vec /vec:stats /vec:pii /vec:o10 /vec:yes " + Optimization="2" + InlineFunctionExpansion="1" + OptimizeForProcessor="2" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB;BOTLIB" + StringPooling="TRUE" + RuntimeLibrary="4" + EnableFunctionLevelLinking="TRUE" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\botlib___Win32_vector0/botlib.pch" + AssemblerListingLocation=".\botlib___Win32_vector0/" + ObjectFile=".\botlib___Win32_vector0/" + ProgramDataBaseFileName=".\botlib___Win32_vector0/" + WarningLevel="4" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile=".\botlib___Win32_vector0\botlib.lib" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="NDEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Debug|Win32" + OutputDirectory=".\Debug" + IntermediateDirectory=".\Debug" + ConfigurationType="4" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + OptimizeForProcessor="1" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB;BOTLIB;DEBUG" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Debug/botlib.pch" + AssemblerListingLocation=".\Debug/" + ObjectFile=".\Debug/" + ProgramDataBaseFileName=".\Debug/" + BrowseInformation="1" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile=".\Debug\botlib.lib" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="_DEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release TA|Win32" + OutputDirectory=".\Release_TA" + IntermediateDirectory=".\Release_TA" + ConfigurationType="4" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + InlineFunctionExpansion="1" + OptimizeForProcessor="2" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB;BOTLIB" + StringPooling="TRUE" + RuntimeLibrary="4" + EnableFunctionLevelLinking="TRUE" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Release_TA/botlib.pch" + AssemblerListingLocation=".\Release_TA/" + ObjectFile=".\Release_TA/" + ProgramDataBaseFileName=".\Release_TA/" + WarningLevel="4" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile=".\Release_TA\botlib.lib" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="NDEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"> + <File + RelativePath="be_aas_bspq3.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_cluster.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_debug.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_entity.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_file.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_main.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_move.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_optimize.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_reach.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_route.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_routealt.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_aas_sample.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_char.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_chat.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_gen.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_goal.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_move.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_weap.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ai_weight.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_ea.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="be_interface.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_crc.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_libvar.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_log.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_memory.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_precomp.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_script.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File + RelativePath="l_struct.c"> + <FileConfiguration + Name="Debug TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="vector|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release TA|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl"> + <File + RelativePath="aasfile.h"> + </File> + <File + RelativePath="be_aas_bsp.h"> + </File> + <File + RelativePath="be_aas_cluster.h"> + </File> + <File + RelativePath="be_aas_debug.h"> + </File> + <File + RelativePath="be_aas_def.h"> + </File> + <File + RelativePath="be_aas_entity.h"> + </File> + <File + RelativePath="be_aas_file.h"> + </File> + <File + RelativePath="be_aas_funcs.h"> + </File> + <File + RelativePath="be_aas_main.h"> + </File> + <File + RelativePath="be_aas_move.h"> + </File> + <File + RelativePath="be_aas_optimize.h"> + </File> + <File + RelativePath="be_aas_reach.h"> + </File> + <File + RelativePath="be_aas_route.h"> + </File> + <File + RelativePath="be_aas_routealt.h"> + </File> + <File + RelativePath="be_aas_sample.h"> + </File> + <File + RelativePath="be_ai_weight.h"> + </File> + <File + RelativePath="be_interface.h"> + </File> + <File + RelativePath="..\game\bg_public.h"> + </File> + <File + RelativePath="..\qcommon\cm_public.h"> + </File> + <File + RelativePath="..\game\g_public.h"> + </File> + <File + RelativePath="l_crc.h"> + </File> + <File + RelativePath="l_libvar.h"> + </File> + <File + RelativePath="l_log.h"> + </File> + <File + RelativePath="l_memory.h"> + </File> + <File + RelativePath="l_precomp.h"> + </File> + <File + RelativePath="l_script.h"> + </File> + <File + RelativePath="l_struct.h"> + </File> + <File + RelativePath="l_utils.h"> + </File> + <File + RelativePath="..\game\q_shared.h"> + </File> + <File + RelativePath="..\qcommon\qcommon.h"> + </File> + <File + RelativePath="..\qcommon\qfiles.h"> + </File> + <File + RelativePath="..\server\server.h"> + </File> + <File + RelativePath="..\game\surfaceflags.h"> + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/code/botlib/l_crc.c b/code/botlib/l_crc.c index 9a945f5..8b1067b 100755 --- a/code/botlib/l_crc.c +++ b/code/botlib/l_crc.c @@ -1,151 +1,151 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_crc.c
- *
- * desc: CRC calculation
- *
- * $Archive: /MissionPack/CODE/botlib/l_crc.c $
- *
- *****************************************************************************/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "../game/q_shared.h"
-#include "../game/botlib.h"
-#include "be_interface.h" //for botimport.Print
-
-
-// FIXME: byte swap?
-
-// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
-// and the initial and final xor values shown below... in other words, the
-// CCITT standard CRC used by XMODEM
-
-#define CRC_INIT_VALUE 0xffff
-#define CRC_XOR_VALUE 0x0000
-
-unsigned short crctable[257] =
-{
- 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
- 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
- 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
- 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
- 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
- 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
- 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
- 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
- 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
- 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
- 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
- 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
- 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
- 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
- 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
- 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
- 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
- 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
- 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
- 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
- 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
- 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
- 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
- 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
- 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
- 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
- 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
- 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
- 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
- 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
- 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
- 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
-};
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void CRC_Init(unsigned short *crcvalue)
-{
- *crcvalue = CRC_INIT_VALUE;
-} //end of the function CRC_Init
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void CRC_ProcessByte(unsigned short *crcvalue, byte data)
-{
- *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
-} //end of the function CRC_ProcessByte
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-unsigned short CRC_Value(unsigned short crcvalue)
-{
- return crcvalue ^ CRC_XOR_VALUE;
-} //end of the function CRC_Value
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-unsigned short CRC_ProcessString(unsigned char *data, int length)
-{
- unsigned short crcvalue;
- int i, ind;
-
- CRC_Init(&crcvalue);
-
- for (i = 0; i < length; i++)
- {
- ind = (crcvalue >> 8) ^ data[i];
- if (ind < 0 || ind > 256) ind = 0;
- crcvalue = (crcvalue << 8) ^ crctable[ind];
- } //end for
- return CRC_Value(crcvalue);
-} //end of the function CRC_ProcessString
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void CRC_ContinueProcessString(unsigned short *crc, char *data, int length)
-{
- int i;
-
- for (i = 0; i < length; i++)
- {
- *crc = (*crc << 8) ^ crctable[(*crc >> 8) ^ data[i]];
- } //end for
-} //end of the function CRC_ProcessString
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_crc.c + * + * desc: CRC calculation + * + * $Archive: /MissionPack/CODE/botlib/l_crc.c $ + * + *****************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../game/q_shared.h" +#include "../game/botlib.h" +#include "be_interface.h" //for botimport.Print + + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +unsigned short crctable[257] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} //end of the function CRC_Init +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CRC_ProcessByte(unsigned short *crcvalue, byte data) +{ + *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; +} //end of the function CRC_ProcessByte +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} //end of the function CRC_Value +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned short CRC_ProcessString(unsigned char *data, int length) +{ + unsigned short crcvalue; + int i, ind; + + CRC_Init(&crcvalue); + + for (i = 0; i < length; i++) + { + ind = (crcvalue >> 8) ^ data[i]; + if (ind < 0 || ind > 256) ind = 0; + crcvalue = (crcvalue << 8) ^ crctable[ind]; + } //end for + return CRC_Value(crcvalue); +} //end of the function CRC_ProcessString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CRC_ContinueProcessString(unsigned short *crc, char *data, int length) +{ + int i; + + for (i = 0; i < length; i++) + { + *crc = (*crc << 8) ^ crctable[(*crc >> 8) ^ data[i]]; + } //end for +} //end of the function CRC_ProcessString diff --git a/code/botlib/l_crc.h b/code/botlib/l_crc.h index 3f37cd4..67baf56 100755 --- a/code/botlib/l_crc.h +++ b/code/botlib/l_crc.h @@ -1,29 +1,29 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-typedef unsigned short crc_t;
-
-void CRC_Init(unsigned short *crcvalue);
-void CRC_ProcessByte(unsigned short *crcvalue, byte data);
-unsigned short CRC_Value(unsigned short crcvalue);
-unsigned short CRC_ProcessString(unsigned char *data, int length);
-void CRC_ContinueProcessString(unsigned short *crc, char *data, int length);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +typedef unsigned short crc_t; + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); +unsigned short CRC_ProcessString(unsigned char *data, int length); +void CRC_ContinueProcessString(unsigned short *crc, char *data, int length); diff --git a/code/botlib/l_libvar.c b/code/botlib/l_libvar.c index d7ac798..a8629c8 100755 --- a/code/botlib/l_libvar.c +++ b/code/botlib/l_libvar.c @@ -1,294 +1,294 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_libvar.c
- *
- * desc: bot library variables
- *
- * $Archive: /MissionPack/code/botlib/l_libvar.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "l_memory.h"
-#include "l_libvar.h"
-
-//list with library variables
-libvar_t *libvarlist;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float LibVarStringValue(char *string)
-{
- int dotfound = 0;
- float value = 0;
-
- while(*string)
- {
- if (*string < '0' || *string > '9')
- {
- if (dotfound || *string != '.')
- {
- return 0;
- } //end if
- else
- {
- dotfound = 10;
- string++;
- } //end if
- } //end if
- if (dotfound)
- {
- value = value + (float) (*string - '0') / (float) dotfound;
- dotfound *= 10;
- } //end if
- else
- {
- value = value * 10.0 + (float) (*string - '0');
- } //end else
- string++;
- } //end while
- return value;
-} //end of the function LibVarStringValue
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-libvar_t *LibVarAlloc(char *var_name)
-{
- libvar_t *v;
-
- v = (libvar_t *) GetMemory(sizeof(libvar_t) + strlen(var_name) + 1);
- Com_Memset(v, 0, sizeof(libvar_t));
- v->name = (char *) v + sizeof(libvar_t);
- strcpy(v->name, var_name);
- //add the variable in the list
- v->next = libvarlist;
- libvarlist = v;
- return v;
-} //end of the function LibVarAlloc
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void LibVarDeAlloc(libvar_t *v)
-{
- if (v->string) FreeMemory(v->string);
- FreeMemory(v);
-} //end of the function LibVarDeAlloc
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void LibVarDeAllocAll(void)
-{
- libvar_t *v;
-
- for (v = libvarlist; v; v = libvarlist)
- {
- libvarlist = libvarlist->next;
- LibVarDeAlloc(v);
- } //end for
- libvarlist = NULL;
-} //end of the function LibVarDeAllocAll
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-libvar_t *LibVarGet(char *var_name)
-{
- libvar_t *v;
-
- for (v = libvarlist; v; v = v->next)
- {
- if (!Q_stricmp(v->name, var_name))
- {
- return v;
- } //end if
- } //end for
- return NULL;
-} //end of the function LibVarGet
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *LibVarGetString(char *var_name)
-{
- libvar_t *v;
-
- v = LibVarGet(var_name);
- if (v)
- {
- return v->string;
- } //end if
- else
- {
- return "";
- } //end else
-} //end of the function LibVarGetString
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float LibVarGetValue(char *var_name)
-{
- libvar_t *v;
-
- v = LibVarGet(var_name);
- if (v)
- {
- return v->value;
- } //end if
- else
- {
- return 0;
- } //end else
-} //end of the function LibVarGetValue
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-libvar_t *LibVar(char *var_name, char *value)
-{
- libvar_t *v;
- v = LibVarGet(var_name);
- if (v) return v;
- //create new variable
- v = LibVarAlloc(var_name);
- //variable string
- v->string = (char *) GetMemory(strlen(value) + 1);
- strcpy(v->string, value);
- //the value
- v->value = LibVarStringValue(v->string);
- //variable is modified
- v->modified = qtrue;
- //
- return v;
-} //end of the function LibVar
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *LibVarString(char *var_name, char *value)
-{
- libvar_t *v;
-
- v = LibVar(var_name, value);
- return v->string;
-} //end of the function LibVarString
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float LibVarValue(char *var_name, char *value)
-{
- libvar_t *v;
-
- v = LibVar(var_name, value);
- return v->value;
-} //end of the function LibVarValue
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void LibVarSet(char *var_name, char *value)
-{
- libvar_t *v;
-
- v = LibVarGet(var_name);
- if (v)
- {
- FreeMemory(v->string);
- } //end if
- else
- {
- v = LibVarAlloc(var_name);
- } //end else
- //variable string
- v->string = (char *) GetMemory(strlen(value) + 1);
- strcpy(v->string, value);
- //the value
- v->value = LibVarStringValue(v->string);
- //variable is modified
- v->modified = qtrue;
-} //end of the function LibVarSet
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean LibVarChanged(char *var_name)
-{
- libvar_t *v;
-
- v = LibVarGet(var_name);
- if (v)
- {
- return v->modified;
- } //end if
- else
- {
- return qfalse;
- } //end else
-} //end of the function LibVarChanged
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void LibVarSetNotModified(char *var_name)
-{
- libvar_t *v;
-
- v = LibVarGet(var_name);
- if (v)
- {
- v->modified = qfalse;
- } //end if
-} //end of the function LibVarSetNotModified
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_libvar.c + * + * desc: bot library variables + * + * $Archive: /MissionPack/code/botlib/l_libvar.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_libvar.h" + +//list with library variables +libvar_t *libvarlist; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float LibVarStringValue(char *string) +{ + int dotfound = 0; + float value = 0; + + while(*string) + { + if (*string < '0' || *string > '9') + { + if (dotfound || *string != '.') + { + return 0; + } //end if + else + { + dotfound = 10; + string++; + } //end if + } //end if + if (dotfound) + { + value = value + (float) (*string - '0') / (float) dotfound; + dotfound *= 10; + } //end if + else + { + value = value * 10.0 + (float) (*string - '0'); + } //end else + string++; + } //end while + return value; +} //end of the function LibVarStringValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +libvar_t *LibVarAlloc(char *var_name) +{ + libvar_t *v; + + v = (libvar_t *) GetMemory(sizeof(libvar_t) + strlen(var_name) + 1); + Com_Memset(v, 0, sizeof(libvar_t)); + v->name = (char *) v + sizeof(libvar_t); + strcpy(v->name, var_name); + //add the variable in the list + v->next = libvarlist; + libvarlist = v; + return v; +} //end of the function LibVarAlloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarDeAlloc(libvar_t *v) +{ + if (v->string) FreeMemory(v->string); + FreeMemory(v); +} //end of the function LibVarDeAlloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarDeAllocAll(void) +{ + libvar_t *v; + + for (v = libvarlist; v; v = libvarlist) + { + libvarlist = libvarlist->next; + LibVarDeAlloc(v); + } //end for + libvarlist = NULL; +} //end of the function LibVarDeAllocAll +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +libvar_t *LibVarGet(char *var_name) +{ + libvar_t *v; + + for (v = libvarlist; v; v = v->next) + { + if (!Q_stricmp(v->name, var_name)) + { + return v; + } //end if + } //end for + return NULL; +} //end of the function LibVarGet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *LibVarGetString(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + return v->string; + } //end if + else + { + return ""; + } //end else +} //end of the function LibVarGetString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float LibVarGetValue(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + return v->value; + } //end if + else + { + return 0; + } //end else +} //end of the function LibVarGetValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +libvar_t *LibVar(char *var_name, char *value) +{ + libvar_t *v; + v = LibVarGet(var_name); + if (v) return v; + //create new variable + v = LibVarAlloc(var_name); + //variable string + v->string = (char *) GetMemory(strlen(value) + 1); + strcpy(v->string, value); + //the value + v->value = LibVarStringValue(v->string); + //variable is modified + v->modified = qtrue; + // + return v; +} //end of the function LibVar +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *LibVarString(char *var_name, char *value) +{ + libvar_t *v; + + v = LibVar(var_name, value); + return v->string; +} //end of the function LibVarString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float LibVarValue(char *var_name, char *value) +{ + libvar_t *v; + + v = LibVar(var_name, value); + return v->value; +} //end of the function LibVarValue +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarSet(char *var_name, char *value) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + FreeMemory(v->string); + } //end if + else + { + v = LibVarAlloc(var_name); + } //end else + //variable string + v->string = (char *) GetMemory(strlen(value) + 1); + strcpy(v->string, value); + //the value + v->value = LibVarStringValue(v->string); + //variable is modified + v->modified = qtrue; +} //end of the function LibVarSet +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean LibVarChanged(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + return v->modified; + } //end if + else + { + return qfalse; + } //end else +} //end of the function LibVarChanged +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LibVarSetNotModified(char *var_name) +{ + libvar_t *v; + + v = LibVarGet(var_name); + if (v) + { + v->modified = qfalse; + } //end if +} //end of the function LibVarSetNotModified diff --git a/code/botlib/l_libvar.h b/code/botlib/l_libvar.h index 79fc65e..fef4303 100755 --- a/code/botlib/l_libvar.h +++ b/code/botlib/l_libvar.h @@ -1,63 +1,63 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_libvar.h
- *
- * desc: botlib vars
- *
- * $Archive: /source/code/botlib/l_libvar.h $
- *
- *****************************************************************************/
-
-//library variable
-typedef struct libvar_s
-{
- char *name;
- char *string;
- int flags;
- qboolean modified; // set each time the cvar is changed
- float value;
- struct libvar_s *next;
-} libvar_t;
-
-//removes all library variables
-void LibVarDeAllocAll(void);
-//gets the library variable with the given name
-libvar_t *LibVarGet(char *var_name);
-//gets the string of the library variable with the given name
-char *LibVarGetString(char *var_name);
-//gets the value of the library variable with the given name
-float LibVarGetValue(char *var_name);
-//creates the library variable if not existing already and returns it
-libvar_t *LibVar(char *var_name, char *value);
-//creates the library variable if not existing already and returns the value
-float LibVarValue(char *var_name, char *value);
-//creates the library variable if not existing already and returns the value string
-char *LibVarString(char *var_name, char *value);
-//sets the library variable
-void LibVarSet(char *var_name, char *value);
-//returns true if the library variable has been modified
-qboolean LibVarChanged(char *var_name);
-//sets the library variable to unmodified
-void LibVarSetNotModified(char *var_name);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_libvar.h + * + * desc: botlib vars + * + * $Archive: /source/code/botlib/l_libvar.h $ + * + *****************************************************************************/ + +//library variable +typedef struct libvar_s +{ + char *name; + char *string; + int flags; + qboolean modified; // set each time the cvar is changed + float value; + struct libvar_s *next; +} libvar_t; + +//removes all library variables +void LibVarDeAllocAll(void); +//gets the library variable with the given name +libvar_t *LibVarGet(char *var_name); +//gets the string of the library variable with the given name +char *LibVarGetString(char *var_name); +//gets the value of the library variable with the given name +float LibVarGetValue(char *var_name); +//creates the library variable if not existing already and returns it +libvar_t *LibVar(char *var_name, char *value); +//creates the library variable if not existing already and returns the value +float LibVarValue(char *var_name, char *value); +//creates the library variable if not existing already and returns the value string +char *LibVarString(char *var_name, char *value); +//sets the library variable +void LibVarSet(char *var_name, char *value); +//returns true if the library variable has been modified +qboolean LibVarChanged(char *var_name); +//sets the library variable to unmodified +void LibVarSetNotModified(char *var_name); + diff --git a/code/botlib/l_log.c b/code/botlib/l_log.c index d532c8c..7b1a346 100755 --- a/code/botlib/l_log.c +++ b/code/botlib/l_log.c @@ -1,169 +1,169 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_log.c
- *
- * desc: log file
- *
- * $Archive: /MissionPack/CODE/botlib/l_log.c $
- *
- *****************************************************************************/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "../game/q_shared.h"
-#include "../game/botlib.h"
-#include "be_interface.h" //for botimport.Print
-#include "l_libvar.h"
-
-#define MAX_LOGFILENAMESIZE 1024
-
-typedef struct logfile_s
-{
- char filename[MAX_LOGFILENAMESIZE];
- FILE *fp;
- int numwrites;
-} logfile_t;
-
-static logfile_t logfile;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Log_Open(char *filename)
-{
- if (!LibVarValue("log", "0")) return;
- if (!filename || !strlen(filename))
- {
- botimport.Print(PRT_MESSAGE, "openlog <filename>\n");
- return;
- } //end if
- if (logfile.fp)
- {
- botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename);
- return;
- } //end if
- logfile.fp = fopen(filename, "wb");
- if (!logfile.fp)
- {
- botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename);
- return;
- } //end if
- strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE);
- botimport.Print(PRT_MESSAGE, "Opened log %s\n", logfile.filename);
-} //end of the function Log_Create
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Log_Close(void)
-{
- if (!logfile.fp) return;
- if (fclose(logfile.fp))
- {
- botimport.Print(PRT_ERROR, "can't close log file %s\n", logfile.filename);
- return;
- } //end if
- logfile.fp = NULL;
- botimport.Print(PRT_MESSAGE, "Closed log %s\n", logfile.filename);
-} //end of the function Log_Close
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Log_Shutdown(void)
-{
- if (logfile.fp) Log_Close();
-} //end of the function Log_Shutdown
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void QDECL Log_Write(char *fmt, ...)
-{
- va_list ap;
-
- if (!logfile.fp) return;
- va_start(ap, fmt);
- vfprintf(logfile.fp, fmt, ap);
- va_end(ap);
- //fprintf(logfile.fp, "\r\n");
- fflush(logfile.fp);
-} //end of the function Log_Write
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void QDECL Log_WriteTimeStamped(char *fmt, ...)
-{
- va_list ap;
-
- if (!logfile.fp) return;
- fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ",
- logfile.numwrites,
- (int) (botlibglobals.time / 60 / 60),
- (int) (botlibglobals.time / 60),
- (int) (botlibglobals.time),
- (int) ((int) (botlibglobals.time * 100)) -
- ((int) botlibglobals.time) * 100);
- va_start(ap, fmt);
- vfprintf(logfile.fp, fmt, ap);
- va_end(ap);
- fprintf(logfile.fp, "\r\n");
- logfile.numwrites++;
- fflush(logfile.fp);
-} //end of the function Log_Write
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-FILE *Log_FilePointer(void)
-{
- return logfile.fp;
-} //end of the function Log_FilePointer
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Log_Flush(void)
-{
- if (logfile.fp) fflush(logfile.fp);
-} //end of the function Log_Flush
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_log.c + * + * desc: log file + * + * $Archive: /MissionPack/CODE/botlib/l_log.c $ + * + *****************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../game/q_shared.h" +#include "../game/botlib.h" +#include "be_interface.h" //for botimport.Print +#include "l_libvar.h" + +#define MAX_LOGFILENAMESIZE 1024 + +typedef struct logfile_s +{ + char filename[MAX_LOGFILENAMESIZE]; + FILE *fp; + int numwrites; +} logfile_t; + +static logfile_t logfile; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Open(char *filename) +{ + if (!LibVarValue("log", "0")) return; + if (!filename || !strlen(filename)) + { + botimport.Print(PRT_MESSAGE, "openlog <filename>\n"); + return; + } //end if + if (logfile.fp) + { + botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); + return; + } //end if + logfile.fp = fopen(filename, "wb"); + if (!logfile.fp) + { + botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename); + return; + } //end if + strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE); + botimport.Print(PRT_MESSAGE, "Opened log %s\n", logfile.filename); +} //end of the function Log_Create +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Close(void) +{ + if (!logfile.fp) return; + if (fclose(logfile.fp)) + { + botimport.Print(PRT_ERROR, "can't close log file %s\n", logfile.filename); + return; + } //end if + logfile.fp = NULL; + botimport.Print(PRT_MESSAGE, "Closed log %s\n", logfile.filename); +} //end of the function Log_Close +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Shutdown(void) +{ + if (logfile.fp) Log_Close(); +} //end of the function Log_Shutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL Log_Write(char *fmt, ...) +{ + va_list ap; + + if (!logfile.fp) return; + va_start(ap, fmt); + vfprintf(logfile.fp, fmt, ap); + va_end(ap); + //fprintf(logfile.fp, "\r\n"); + fflush(logfile.fp); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL Log_WriteTimeStamped(char *fmt, ...) +{ + va_list ap; + + if (!logfile.fp) return; + fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ", + logfile.numwrites, + (int) (botlibglobals.time / 60 / 60), + (int) (botlibglobals.time / 60), + (int) (botlibglobals.time), + (int) ((int) (botlibglobals.time * 100)) - + ((int) botlibglobals.time) * 100); + va_start(ap, fmt); + vfprintf(logfile.fp, fmt, ap); + va_end(ap); + fprintf(logfile.fp, "\r\n"); + logfile.numwrites++; + fflush(logfile.fp); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +FILE *Log_FilePointer(void) +{ + return logfile.fp; +} //end of the function Log_FilePointer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Flush(void) +{ + if (logfile.fp) fflush(logfile.fp); +} //end of the function Log_Flush + diff --git a/code/botlib/l_log.h b/code/botlib/l_log.h index 2d07f9e..d7b143e 100755 --- a/code/botlib/l_log.h +++ b/code/botlib/l_log.h @@ -1,46 +1,46 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_log.h
- *
- * desc: log file
- *
- * $Archive: /source/code/botlib/l_log.h $
- *
- *****************************************************************************/
-
-//open a log file
-void Log_Open(char *filename);
-//close the current log file
-void Log_Close(void);
-//close log file if present
-void Log_Shutdown(void);
-//write to the current opened log file
-void QDECL Log_Write(char *fmt, ...);
-//write to the current opened log file with a time stamp
-void QDECL Log_WriteTimeStamped(char *fmt, ...);
-//returns a pointer to the log file
-FILE *Log_FilePointer(void);
-//flush log file
-void Log_Flush(void);
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_log.h + * + * desc: log file + * + * $Archive: /source/code/botlib/l_log.h $ + * + *****************************************************************************/ + +//open a log file +void Log_Open(char *filename); +//close the current log file +void Log_Close(void); +//close log file if present +void Log_Shutdown(void); +//write to the current opened log file +void QDECL Log_Write(char *fmt, ...); +//write to the current opened log file with a time stamp +void QDECL Log_WriteTimeStamped(char *fmt, ...); +//returns a pointer to the log file +FILE *Log_FilePointer(void); +//flush log file +void Log_Flush(void); + diff --git a/code/botlib/l_memory.c b/code/botlib/l_memory.c index 68928ab..7bc12f6 100755 --- a/code/botlib/l_memory.c +++ b/code/botlib/l_memory.c @@ -1,463 +1,463 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_memory.c
- *
- * desc: memory allocation
- *
- * $Archive: /MissionPack/code/botlib/l_memory.c $
- *
- *****************************************************************************/
-
-#include "../game/q_shared.h"
-#include "../game/botlib.h"
-#include "l_log.h"
-#include "be_interface.h"
-
-//#define MEMDEBUG
-//#define MEMORYMANEGER
-
-#define MEM_ID 0x12345678l
-#define HUNK_ID 0x87654321l
-
-int allocatedmemory;
-int totalmemorysize;
-int numblocks;
-
-#ifdef MEMORYMANEGER
-
-typedef struct memoryblock_s
-{
- unsigned long int id;
- void *ptr;
- int size;
-#ifdef MEMDEBUG
- char *label;
- char *file;
- int line;
-#endif //MEMDEBUG
- struct memoryblock_s *prev, *next;
-} memoryblock_t;
-
-memoryblock_t *memory;
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void LinkMemoryBlock(memoryblock_t *block)
-{
- block->prev = NULL;
- block->next = memory;
- if (memory) memory->prev = block;
- memory = block;
-} //end of the function LinkMemoryBlock
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void UnlinkMemoryBlock(memoryblock_t *block)
-{
- if (block->prev) block->prev->next = block->next;
- else memory = block->next;
- if (block->next) block->next->prev = block->prev;
-} //end of the function UnlinkMemoryBlock
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
- memoryblock_t *block;
- assert(botimport.GetMemory); // bk001129 - was NULL'ed
- ptr = botimport.GetMemory(size + sizeof(memoryblock_t));
- block = (memoryblock_t *) ptr;
- block->id = MEM_ID;
- block->ptr = (char *) ptr + sizeof(memoryblock_t);
- block->size = size + sizeof(memoryblock_t);
-#ifdef MEMDEBUG
- block->label = label;
- block->file = file;
- block->line = line;
-#endif //MEMDEBUG
- LinkMemoryBlock(block);
- allocatedmemory += block->size;
- totalmemorysize += block->size + sizeof(memoryblock_t);
- numblocks++;
- return block->ptr;
-} //end of the function GetMemoryDebug
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetClearedMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
-#ifdef MEMDEBUG
- ptr = GetMemoryDebug(size, label, file, line);
-#else
- ptr = GetMemory(size);
-#endif //MEMDEBUG
- Com_Memset(ptr, 0, size);
- return ptr;
-} //end of the function GetClearedMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetHunkMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
- memoryblock_t *block;
-
- ptr = botimport.HunkAlloc(size + sizeof(memoryblock_t));
- block = (memoryblock_t *) ptr;
- block->id = HUNK_ID;
- block->ptr = (char *) ptr + sizeof(memoryblock_t);
- block->size = size + sizeof(memoryblock_t);
-#ifdef MEMDEBUG
- block->label = label;
- block->file = file;
- block->line = line;
-#endif //MEMDEBUG
- LinkMemoryBlock(block);
- allocatedmemory += block->size;
- totalmemorysize += block->size + sizeof(memoryblock_t);
- numblocks++;
- return block->ptr;
-} //end of the function GetHunkMemoryDebug
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetClearedHunkMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
-#ifdef MEMDEBUG
- ptr = GetHunkMemoryDebug(size, label, file, line);
-#else
- ptr = GetHunkMemory(size);
-#endif //MEMDEBUG
- Com_Memset(ptr, 0, size);
- return ptr;
-} //end of the function GetClearedHunkMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-memoryblock_t *BlockFromPointer(void *ptr, char *str)
-{
- memoryblock_t *block;
-
- if (!ptr)
- {
-#ifdef MEMDEBUG
- //char *crash = (char *) NULL;
- //crash[0] = 1;
- botimport.Print(PRT_FATAL, "%s: NULL pointer\n", str);
-#endif // MEMDEBUG
- return NULL;
- } //end if
- block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t));
- if (block->id != MEM_ID && block->id != HUNK_ID)
- {
- botimport.Print(PRT_FATAL, "%s: invalid memory block\n", str);
- return NULL;
- } //end if
- if (block->ptr != ptr)
- {
- botimport.Print(PRT_FATAL, "%s: memory block pointer invalid\n", str);
- return NULL;
- } //end if
- return block;
-} //end of the function BlockFromPointer
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeMemory(void *ptr)
-{
- memoryblock_t *block;
-
- block = BlockFromPointer(ptr, "FreeMemory");
- if (!block) return;
- UnlinkMemoryBlock(block);
- allocatedmemory -= block->size;
- totalmemorysize -= block->size + sizeof(memoryblock_t);
- numblocks--;
- //
- if (block->id == MEM_ID)
- {
- botimport.FreeMemory(block);
- } //end if
-} //end of the function FreeMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AvailableMemory(void)
-{
- return botimport.AvailableMemory();
-} //end of the function AvailableMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int MemoryByteSize(void *ptr)
-{
- memoryblock_t *block;
-
- block = BlockFromPointer(ptr, "MemoryByteSize");
- if (!block) return 0;
- return block->size;
-} //end of the function MemoryByteSize
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void PrintUsedMemorySize(void)
-{
- botimport.Print(PRT_MESSAGE, "total allocated memory: %d KB\n", allocatedmemory >> 10);
- botimport.Print(PRT_MESSAGE, "total botlib memory: %d KB\n", totalmemorysize >> 10);
- botimport.Print(PRT_MESSAGE, "total memory blocks: %d\n", numblocks);
-} //end of the function PrintUsedMemorySize
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void PrintMemoryLabels(void)
-{
- memoryblock_t *block;
- int i;
-
- PrintUsedMemorySize();
- i = 0;
- Log_Write("============= Botlib memory log ==============\r\n");
- Log_Write("\r\n");
- for (block = memory; block; block = block->next)
- {
-#ifdef MEMDEBUG
- if (block->id == HUNK_ID)
- {
- Log_Write("%6d, hunk %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label);
- } //end if
- else
- {
- Log_Write("%6d, %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label);
- } //end else
-#endif //MEMDEBUG
- i++;
- } //end for
-} //end of the function PrintMemoryLabels
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void DumpMemory(void)
-{
- memoryblock_t *block;
-
- for (block = memory; block; block = memory)
- {
- FreeMemory(block->ptr);
- } //end for
- totalmemorysize = 0;
- allocatedmemory = 0;
-} //end of the function DumpMemory
-
-#else
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
- unsigned long int *memid;
-
- ptr = botimport.GetMemory(size + sizeof(unsigned long int));
- if (!ptr) return NULL;
- memid = (unsigned long int *) ptr;
- *memid = MEM_ID;
- return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int));
-} //end of the function GetMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetClearedMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
-#ifdef MEMDEBUG
- ptr = GetMemoryDebug(size, label, file, line);
-#else
- ptr = GetMemory(size);
-#endif //MEMDEBUG
- Com_Memset(ptr, 0, size);
- return ptr;
-} //end of the function GetClearedMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetHunkMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
- unsigned long int *memid;
-
- ptr = botimport.HunkAlloc(size + sizeof(unsigned long int));
- if (!ptr) return NULL;
- memid = (unsigned long int *) ptr;
- *memid = HUNK_ID;
- return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int));
-} //end of the function GetHunkMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-#ifdef MEMDEBUG
-void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line)
-#else
-void *GetClearedHunkMemory(unsigned long size)
-#endif //MEMDEBUG
-{
- void *ptr;
-#ifdef MEMDEBUG
- ptr = GetHunkMemoryDebug(size, label, file, line);
-#else
- ptr = GetHunkMemory(size);
-#endif //MEMDEBUG
- Com_Memset(ptr, 0, size);
- return ptr;
-} //end of the function GetClearedHunkMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void FreeMemory(void *ptr)
-{
- unsigned long int *memid;
-
- memid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int));
-
- if (*memid == MEM_ID)
- {
- botimport.FreeMemory(memid);
- } //end if
-} //end of the function FreeMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int AvailableMemory(void)
-{
- return botimport.AvailableMemory();
-} //end of the function AvailableMemory
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void PrintUsedMemorySize(void)
-{
-} //end of the function PrintUsedMemorySize
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void PrintMemoryLabels(void)
-{
-} //end of the function PrintMemoryLabels
-
-#endif
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_memory.c + * + * desc: memory allocation + * + * $Archive: /MissionPack/code/botlib/l_memory.c $ + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "../game/botlib.h" +#include "l_log.h" +#include "be_interface.h" + +//#define MEMDEBUG +//#define MEMORYMANEGER + +#define MEM_ID 0x12345678l +#define HUNK_ID 0x87654321l + +int allocatedmemory; +int totalmemorysize; +int numblocks; + +#ifdef MEMORYMANEGER + +typedef struct memoryblock_s +{ + unsigned long int id; + void *ptr; + int size; +#ifdef MEMDEBUG + char *label; + char *file; + int line; +#endif //MEMDEBUG + struct memoryblock_s *prev, *next; +} memoryblock_t; + +memoryblock_t *memory; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LinkMemoryBlock(memoryblock_t *block) +{ + block->prev = NULL; + block->next = memory; + if (memory) memory->prev = block; + memory = block; +} //end of the function LinkMemoryBlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void UnlinkMemoryBlock(memoryblock_t *block) +{ + if (block->prev) block->prev->next = block->next; + else memory = block->next; + if (block->next) block->next->prev = block->prev; +} //end of the function UnlinkMemoryBlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + memoryblock_t *block; + assert(botimport.GetMemory); // bk001129 - was NULL'ed + ptr = botimport.GetMemory(size + sizeof(memoryblock_t)); + block = (memoryblock_t *) ptr; + block->id = MEM_ID; + block->ptr = (char *) ptr + sizeof(memoryblock_t); + block->size = size + sizeof(memoryblock_t); +#ifdef MEMDEBUG + block->label = label; + block->file = file; + block->line = line; +#endif //MEMDEBUG + LinkMemoryBlock(block); + allocatedmemory += block->size; + totalmemorysize += block->size + sizeof(memoryblock_t); + numblocks++; + return block->ptr; +} //end of the function GetMemoryDebug +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetMemoryDebug(size, label, file, line); +#else + ptr = GetMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + memoryblock_t *block; + + ptr = botimport.HunkAlloc(size + sizeof(memoryblock_t)); + block = (memoryblock_t *) ptr; + block->id = HUNK_ID; + block->ptr = (char *) ptr + sizeof(memoryblock_t); + block->size = size + sizeof(memoryblock_t); +#ifdef MEMDEBUG + block->label = label; + block->file = file; + block->line = line; +#endif //MEMDEBUG + LinkMemoryBlock(block); + allocatedmemory += block->size; + totalmemorysize += block->size + sizeof(memoryblock_t); + numblocks++; + return block->ptr; +} //end of the function GetHunkMemoryDebug +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetHunkMemoryDebug(size, label, file, line); +#else + ptr = GetHunkMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +memoryblock_t *BlockFromPointer(void *ptr, char *str) +{ + memoryblock_t *block; + + if (!ptr) + { +#ifdef MEMDEBUG + //char *crash = (char *) NULL; + //crash[0] = 1; + botimport.Print(PRT_FATAL, "%s: NULL pointer\n", str); +#endif // MEMDEBUG + return NULL; + } //end if + block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t)); + if (block->id != MEM_ID && block->id != HUNK_ID) + { + botimport.Print(PRT_FATAL, "%s: invalid memory block\n", str); + return NULL; + } //end if + if (block->ptr != ptr) + { + botimport.Print(PRT_FATAL, "%s: memory block pointer invalid\n", str); + return NULL; + } //end if + return block; +} //end of the function BlockFromPointer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeMemory(void *ptr) +{ + memoryblock_t *block; + + block = BlockFromPointer(ptr, "FreeMemory"); + if (!block) return; + UnlinkMemoryBlock(block); + allocatedmemory -= block->size; + totalmemorysize -= block->size + sizeof(memoryblock_t); + numblocks--; + // + if (block->id == MEM_ID) + { + botimport.FreeMemory(block); + } //end if +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AvailableMemory(void) +{ + return botimport.AvailableMemory(); +} //end of the function AvailableMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int MemoryByteSize(void *ptr) +{ + memoryblock_t *block; + + block = BlockFromPointer(ptr, "MemoryByteSize"); + if (!block) return 0; + return block->size; +} //end of the function MemoryByteSize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize(void) +{ + botimport.Print(PRT_MESSAGE, "total allocated memory: %d KB\n", allocatedmemory >> 10); + botimport.Print(PRT_MESSAGE, "total botlib memory: %d KB\n", totalmemorysize >> 10); + botimport.Print(PRT_MESSAGE, "total memory blocks: %d\n", numblocks); +} //end of the function PrintUsedMemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels(void) +{ + memoryblock_t *block; + int i; + + PrintUsedMemorySize(); + i = 0; + Log_Write("============= Botlib memory log ==============\r\n"); + Log_Write("\r\n"); + for (block = memory; block; block = block->next) + { +#ifdef MEMDEBUG + if (block->id == HUNK_ID) + { + Log_Write("%6d, hunk %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); + } //end if + else + { + Log_Write("%6d, %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label); + } //end else +#endif //MEMDEBUG + i++; + } //end for +} //end of the function PrintMemoryLabels +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DumpMemory(void) +{ + memoryblock_t *block; + + for (block = memory; block; block = memory) + { + FreeMemory(block->ptr); + } //end for + totalmemorysize = 0; + allocatedmemory = 0; +} //end of the function DumpMemory + +#else + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + unsigned long int *memid; + + ptr = botimport.GetMemory(size + sizeof(unsigned long int)); + if (!ptr) return NULL; + memid = (unsigned long int *) ptr; + *memid = MEM_ID; + return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); +} //end of the function GetMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetMemoryDebug(size, label, file, line); +#else + ptr = GetMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; + unsigned long int *memid; + + ptr = botimport.HunkAlloc(size + sizeof(unsigned long int)); + if (!ptr) return NULL; + memid = (unsigned long int *) ptr; + *memid = HUNK_ID; + return (unsigned long int *) ((char *) ptr + sizeof(unsigned long int)); +} //end of the function GetHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line) +#else +void *GetClearedHunkMemory(unsigned long size) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetHunkMemoryDebug(size, label, file, line); +#else + ptr = GetHunkMemory(size); +#endif //MEMDEBUG + Com_Memset(ptr, 0, size); + return ptr; +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeMemory(void *ptr) +{ + unsigned long int *memid; + + memid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int)); + + if (*memid == MEM_ID) + { + botimport.FreeMemory(memid); + } //end if +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AvailableMemory(void) +{ + return botimport.AvailableMemory(); +} //end of the function AvailableMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize(void) +{ +} //end of the function PrintUsedMemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels(void) +{ +} //end of the function PrintMemoryLabels + +#endif diff --git a/code/botlib/l_memory.h b/code/botlib/l_memory.h index d0cfbb4..353f351 100755 --- a/code/botlib/l_memory.h +++ b/code/botlib/l_memory.h @@ -1,76 +1,76 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_memory.h
- *
- * desc: memory management
- *
- * $Archive: /source/code/botlib/l_memory.h $
- *
- *****************************************************************************/
-
-//#define MEMDEBUG
-
-#ifdef MEMDEBUG
-#define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__);
-#define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__);
-//allocate a memory block of the given size
-void *GetMemoryDebug(unsigned long size, char *label, char *file, int line);
-//allocate a memory block of the given size and clear it
-void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line);
-//
-#define GetHunkMemory(size) GetHunkMemoryDebug(size, #size, __FILE__, __LINE__);
-#define GetClearedHunkMemory(size) GetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__);
-//allocate a memory block of the given size
-void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line);
-//allocate a memory block of the given size and clear it
-void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line);
-#else
-//allocate a memory block of the given size
-void *GetMemory(unsigned long size);
-//allocate a memory block of the given size and clear it
-void *GetClearedMemory(unsigned long size);
-//
-#ifdef BSPC
-#define GetHunkMemory GetMemory
-#define GetClearedHunkMemory GetClearedMemory
-#else
-//allocate a memory block of the given size
-void *GetHunkMemory(unsigned long size);
-//allocate a memory block of the given size and clear it
-void *GetClearedHunkMemory(unsigned long size);
-#endif
-#endif
-
-//free the given memory block
-void FreeMemory(void *ptr);
-//returns the amount available memory
-int AvailableMemory(void);
-//prints the total used memory size
-void PrintUsedMemorySize(void);
-//print all memory blocks with label
-void PrintMemoryLabels(void);
-//returns the size of the memory block in bytes
-int MemoryByteSize(void *ptr);
-//free all allocated memory
-void DumpMemory(void);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_memory.h + * + * desc: memory management + * + * $Archive: /source/code/botlib/l_memory.h $ + * + *****************************************************************************/ + +//#define MEMDEBUG + +#ifdef MEMDEBUG +#define GetMemory(size) GetMemoryDebug(size, #size, __FILE__, __LINE__); +#define GetClearedMemory(size) GetClearedMemoryDebug(size, #size, __FILE__, __LINE__); +//allocate a memory block of the given size +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line); +//allocate a memory block of the given size and clear it +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line); +// +#define GetHunkMemory(size) GetHunkMemoryDebug(size, #size, __FILE__, __LINE__); +#define GetClearedHunkMemory(size) GetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__); +//allocate a memory block of the given size +void *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line); +//allocate a memory block of the given size and clear it +void *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line); +#else +//allocate a memory block of the given size +void *GetMemory(unsigned long size); +//allocate a memory block of the given size and clear it +void *GetClearedMemory(unsigned long size); +// +#ifdef BSPC +#define GetHunkMemory GetMemory +#define GetClearedHunkMemory GetClearedMemory +#else +//allocate a memory block of the given size +void *GetHunkMemory(unsigned long size); +//allocate a memory block of the given size and clear it +void *GetClearedHunkMemory(unsigned long size); +#endif +#endif + +//free the given memory block +void FreeMemory(void *ptr); +//returns the amount available memory +int AvailableMemory(void); +//prints the total used memory size +void PrintUsedMemorySize(void); +//print all memory blocks with label +void PrintMemoryLabels(void); +//returns the size of the memory block in bytes +int MemoryByteSize(void *ptr); +//free all allocated memory +void DumpMemory(void); diff --git a/code/botlib/l_precomp.c b/code/botlib/l_precomp.c index 4fa708b..ae82981 100755 --- a/code/botlib/l_precomp.c +++ b/code/botlib/l_precomp.c @@ -1,3228 +1,3228 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-//
-
-/*****************************************************************************
- * name: l_precomp.c
- *
- * desc: pre compiler
- *
- * $Archive: /MissionPack/code/botlib/l_precomp.c $
- *
- *****************************************************************************/
-
-//Notes: fix: PC_StringizeTokens
-
-//#define SCREWUP
-//#define BOTLIB
-//#define QUAKE
-//#define QUAKEC
-//#define MEQCC
-
-#ifdef SCREWUP
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-
-typedef enum {qfalse, qtrue} qboolean;
-#endif //SCREWUP
-
-#ifdef BOTLIB
-#include "../game/q_shared.h"
-#include "../game/botlib.h"
-#include "be_interface.h"
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_log.h"
-#endif //BOTLIB
-
-#ifdef MEQCC
-#include "qcc.h"
-#include "time.h" //time & ctime
-#include "math.h" //fabs
-#include "l_memory.h"
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_log.h"
-
-#define qtrue true
-#define qfalse false
-#endif //MEQCC
-
-#ifdef BSPC
-//include files for usage in the BSP Converter
-#include "../bspc/qbsp.h"
-#include "../bspc/l_log.h"
-#include "../bspc/l_mem.h"
-#include "l_precomp.h"
-
-#define qtrue true
-#define qfalse false
-#define Q_stricmp stricmp
-
-#endif //BSPC
-
-#if defined(QUAKE) && !defined(BSPC)
-#include "l_utils.h"
-#endif //QUAKE
-
-//#define DEBUG_EVAL
-
-#define MAX_DEFINEPARMS 128
-
-#define DEFINEHASHING 1
-
-//directive name with parse function
-typedef struct directive_s
-{
- char *name;
- int (*func)(source_t *source);
-} directive_t;
-
-#define DEFINEHASHSIZE 1024
-
-#define TOKEN_HEAP_SIZE 4096
-
-int numtokens;
-/*
-int tokenheapinitialized; //true when the token heap is initialized
-token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens
-token_t *freetokens; //free tokens from the heap
-*/
-
-//list with global defines added to every source loaded
-define_t *globaldefines;
-
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void QDECL SourceError(source_t *source, char *str, ...)
-{
- char text[1024];
- va_list ap;
-
- va_start(ap, str);
- vsprintf(text, str, ap);
- va_end(ap);
-#ifdef BOTLIB
- botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
-#endif //BOTLIB
-#ifdef MEQCC
- printf("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
-#endif //MEQCC
-#ifdef BSPC
- Log_Print("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
-#endif //BSPC
-} //end of the function SourceError
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void QDECL SourceWarning(source_t *source, char *str, ...)
-{
- char text[1024];
- va_list ap;
-
- va_start(ap, str);
- vsprintf(text, str, ap);
- va_end(ap);
-#ifdef BOTLIB
- botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
-#endif //BOTLIB
-#ifdef MEQCC
- printf("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
-#endif //MEQCC
-#ifdef BSPC
- Log_Print("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
-#endif //BSPC
-} //end of the function ScriptWarning
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_PushIndent(source_t *source, int type, int skip)
-{
- indent_t *indent;
-
- indent = (indent_t *) GetMemory(sizeof(indent_t));
- indent->type = type;
- indent->script = source->scriptstack;
- indent->skip = (skip != 0);
- source->skip += indent->skip;
- indent->next = source->indentstack;
- source->indentstack = indent;
-} //end of the function PC_PushIndent
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_PopIndent(source_t *source, int *type, int *skip)
-{
- indent_t *indent;
-
- *type = 0;
- *skip = 0;
-
- indent = source->indentstack;
- if (!indent) return;
-
- //must be an indent from the current script
- if (source->indentstack->script != source->scriptstack) return;
-
- *type = indent->type;
- *skip = indent->skip;
- source->indentstack = source->indentstack->next;
- source->skip -= indent->skip;
- FreeMemory(indent);
-} //end of the function PC_PopIndent
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_PushScript(source_t *source, script_t *script)
-{
- script_t *s;
-
- for (s = source->scriptstack; s; s = s->next)
- {
- if (!Q_stricmp(s->filename, script->filename))
- {
- SourceError(source, "%s recursively included", script->filename);
- return;
- } //end if
- } //end for
- //push the script on the script stack
- script->next = source->scriptstack;
- source->scriptstack = script;
-} //end of the function PC_PushScript
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_InitTokenHeap(void)
-{
- /*
- int i;
-
- if (tokenheapinitialized) return;
- freetokens = NULL;
- for (i = 0; i < TOKEN_HEAP_SIZE; i++)
- {
- token_heap[i].next = freetokens;
- freetokens = &token_heap[i];
- } //end for
- tokenheapinitialized = qtrue;
- */
-} //end of the function PC_InitTokenHeap
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-token_t *PC_CopyToken(token_t *token)
-{
- token_t *t;
-
-// t = (token_t *) malloc(sizeof(token_t));
- t = (token_t *) GetMemory(sizeof(token_t));
-// t = freetokens;
- if (!t)
- {
-#ifdef BSPC
- Error("out of token space\n");
-#else
- Com_Error(ERR_FATAL, "out of token space\n");
-#endif
- return NULL;
- } //end if
-// freetokens = freetokens->next;
- Com_Memcpy(t, token, sizeof(token_t));
- t->next = NULL;
- numtokens++;
- return t;
-} //end of the function PC_CopyToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_FreeToken(token_t *token)
-{
- //free(token);
- FreeMemory(token);
-// token->next = freetokens;
-// freetokens = token;
- numtokens--;
-} //end of the function PC_FreeToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ReadSourceToken(source_t *source, token_t *token)
-{
- token_t *t;
- script_t *script;
- int type, skip;
-
- //if there's no token already available
- while(!source->tokens)
- {
- //if there's a token to read from the script
- if (PS_ReadToken(source->scriptstack, token)) return qtrue;
- //if at the end of the script
- if (EndOfScript(source->scriptstack))
- {
- //remove all indents of the script
- while(source->indentstack &&
- source->indentstack->script == source->scriptstack)
- {
- SourceWarning(source, "missing #endif");
- PC_PopIndent(source, &type, &skip);
- } //end if
- } //end if
- //if this was the initial script
- if (!source->scriptstack->next) return qfalse;
- //remove the script and return to the last one
- script = source->scriptstack;
- source->scriptstack = source->scriptstack->next;
- FreeScript(script);
- } //end while
- //copy the already available token
- Com_Memcpy(token, source->tokens, sizeof(token_t));
- //free the read token
- t = source->tokens;
- source->tokens = source->tokens->next;
- PC_FreeToken(t);
- return qtrue;
-} //end of the function PC_ReadSourceToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_UnreadSourceToken(source_t *source, token_t *token)
-{
- token_t *t;
-
- t = PC_CopyToken(token);
- t->next = source->tokens;
- source->tokens = t;
- return qtrue;
-} //end of the function PC_UnreadSourceToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms)
-{
- token_t token, *t, *last;
- int i, done, lastcomma, numparms, indent;
-
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "define %s missing parms", define->name);
- return qfalse;
- } //end if
- //
- if (define->numparms > maxparms)
- {
- SourceError(source, "define with more than %d parameters", maxparms);
- return qfalse;
- } //end if
- //
- for (i = 0; i < define->numparms; i++) parms[i] = NULL;
- //if no leading "("
- if (strcmp(token.string, "("))
- {
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "define %s missing parms", define->name);
- return qfalse;
- } //end if
- //read the define parameters
- for (done = 0, numparms = 0, indent = 0; !done;)
- {
- if (numparms >= maxparms)
- {
- SourceError(source, "define %s with too many parms", define->name);
- return qfalse;
- } //end if
- if (numparms >= define->numparms)
- {
- SourceWarning(source, "define %s has too many parms", define->name);
- return qfalse;
- } //end if
- parms[numparms] = NULL;
- lastcomma = 1;
- last = NULL;
- while(!done)
- {
- //
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "define %s incomplete", define->name);
- return qfalse;
- } //end if
- //
- if (!strcmp(token.string, ","))
- {
- if (indent <= 0)
- {
- if (lastcomma) SourceWarning(source, "too many comma's");
- lastcomma = 1;
- break;
- } //end if
- } //end if
- lastcomma = 0;
- //
- if (!strcmp(token.string, "("))
- {
- indent++;
- continue;
- } //end if
- else if (!strcmp(token.string, ")"))
- {
- if (--indent <= 0)
- {
- if (!parms[define->numparms-1])
- {
- SourceWarning(source, "too few define parms");
- } //end if
- done = 1;
- break;
- } //end if
- } //end if
- //
- if (numparms < define->numparms)
- {
- //
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (last) last->next = t;
- else parms[numparms] = t;
- last = t;
- } //end if
- } //end while
- numparms++;
- } //end for
- return qtrue;
-} //end of the function PC_ReadDefineParms
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_StringizeTokens(token_t *tokens, token_t *token)
-{
- token_t *t;
-
- token->type = TT_STRING;
- token->whitespace_p = NULL;
- token->endwhitespace_p = NULL;
- token->string[0] = '\0';
- strcat(token->string, "\"");
- for (t = tokens; t; t = t->next)
- {
- strncat(token->string, t->string, MAX_TOKEN - strlen(token->string));
- } //end for
- strncat(token->string, "\"", MAX_TOKEN - strlen(token->string));
- return qtrue;
-} //end of the function PC_StringizeTokens
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_MergeTokens(token_t *t1, token_t *t2)
-{
- //merging of a name with a name or number
- if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER))
- {
- strcat(t1->string, t2->string);
- return qtrue;
- } //end if
- //merging of two strings
- if (t1->type == TT_STRING && t2->type == TT_STRING)
- {
- //remove trailing double quote
- t1->string[strlen(t1->string)-1] = '\0';
- //concat without leading double quote
- strcat(t1->string, &t2->string[1]);
- return qtrue;
- } //end if
- //FIXME: merging of two number of the same sub type
- return qfalse;
-} //end of the function PC_MergeTokens
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-/*
-void PC_PrintDefine(define_t *define)
-{
- printf("define->name = %s\n", define->name);
- printf("define->flags = %d\n", define->flags);
- printf("define->builtin = %d\n", define->builtin);
- printf("define->numparms = %d\n", define->numparms);
-// token_t *parms; //define parameters
-// token_t *tokens; //macro tokens (possibly containing parm tokens)
-// struct define_s *next; //next defined macro in a list
-} //end of the function PC_PrintDefine*/
-#if DEFINEHASHING
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_PrintDefineHashTable(define_t **definehash)
-{
- int i;
- define_t *d;
-
- for (i = 0; i < DEFINEHASHSIZE; i++)
- {
- Log_Write("%4d:", i);
- for (d = definehash[i]; d; d = d->hashnext)
- {
- Log_Write(" %s", d->name);
- } //end for
- Log_Write("\n");
- } //end for
-} //end of the function PC_PrintDefineHashTable
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47};
-
-int PC_NameHash(char *name)
-{
- int register hash, i;
-
- hash = 0;
- for (i = 0; name[i] != '\0'; i++)
- {
- hash += name[i] * (119 + i);
- //hash += (name[i] << 7) + i;
- //hash += (name[i] << (i&15));
- } //end while
- hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1);
- return hash;
-} //end of the function PC_NameHash
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_AddDefineToHash(define_t *define, define_t **definehash)
-{
- int hash;
-
- hash = PC_NameHash(define->name);
- define->hashnext = definehash[hash];
- definehash[hash] = define;
-} //end of the function PC_AddDefineToHash
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-define_t *PC_FindHashedDefine(define_t **definehash, char *name)
-{
- define_t *d;
- int hash;
-
- hash = PC_NameHash(name);
- for (d = definehash[hash]; d; d = d->hashnext)
- {
- if (!strcmp(d->name, name)) return d;
- } //end for
- return NULL;
-} //end of the function PC_FindHashedDefine
-#endif //DEFINEHASHING
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-define_t *PC_FindDefine(define_t *defines, char *name)
-{
- define_t *d;
-
- for (d = defines; d; d = d->next)
- {
- if (!strcmp(d->name, name)) return d;
- } //end for
- return NULL;
-} //end of the function PC_FindDefine
-//============================================================================
-//
-// Parameter: -
-// Returns: number of the parm
-// if no parm found with the given name -1 is returned
-// Changes Globals: -
-//============================================================================
-int PC_FindDefineParm(define_t *define, char *name)
-{
- token_t *p;
- int i;
-
- i = 0;
- for (p = define->parms; p; p = p->next)
- {
- if (!strcmp(p->string, name)) return i;
- i++;
- } //end for
- return -1;
-} //end of the function PC_FindDefineParm
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_FreeDefine(define_t *define)
-{
- token_t *t, *next;
-
- //free the define parameters
- for (t = define->parms; t; t = next)
- {
- next = t->next;
- PC_FreeToken(t);
- } //end for
- //free the define tokens
- for (t = define->tokens; t; t = next)
- {
- next = t->next;
- PC_FreeToken(t);
- } //end for
- //free the define
- FreeMemory(define);
-} //end of the function PC_FreeDefine
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_AddBuiltinDefines(source_t *source)
-{
- int i;
- define_t *define;
- struct builtin
- {
- char *string;
- int builtin;
- } builtin[] = { // bk001204 - brackets
- { "__LINE__", BUILTIN_LINE },
- { "__FILE__", BUILTIN_FILE },
- { "__DATE__", BUILTIN_DATE },
- { "__TIME__", BUILTIN_TIME },
-// { "__STDC__", BUILTIN_STDC },
- { NULL, 0 }
- };
-
- for (i = 0; builtin[i].string; i++)
- {
- define = (define_t *) GetMemory(sizeof(define_t) + strlen(builtin[i].string) + 1);
- Com_Memset(define, 0, sizeof(define_t));
- define->name = (char *) define + sizeof(define_t);
- strcpy(define->name, builtin[i].string);
- define->flags |= DEFINE_FIXED;
- define->builtin = builtin[i].builtin;
- //add the define to the source
-#if DEFINEHASHING
- PC_AddDefineToHash(define, source->definehash);
-#else
- define->next = source->defines;
- source->defines = define;
-#endif //DEFINEHASHING
- } //end for
-} //end of the function PC_AddBuiltinDefines
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define,
- token_t **firsttoken, token_t **lasttoken)
-{
- token_t *token;
- unsigned long t; // time_t t; //to prevent LCC warning
- char *curtime;
-
- token = PC_CopyToken(deftoken);
- switch(define->builtin)
- {
- case BUILTIN_LINE:
- {
- sprintf(token->string, "%d", deftoken->line);
-#ifdef NUMBERVALUE
- token->intvalue = deftoken->line;
- token->floatvalue = deftoken->line;
-#endif //NUMBERVALUE
- token->type = TT_NUMBER;
- token->subtype = TT_DECIMAL | TT_INTEGER;
- *firsttoken = token;
- *lasttoken = token;
- break;
- } //end case
- case BUILTIN_FILE:
- {
- strcpy(token->string, source->scriptstack->filename);
- token->type = TT_NAME;
- token->subtype = strlen(token->string);
- *firsttoken = token;
- *lasttoken = token;
- break;
- } //end case
- case BUILTIN_DATE:
- {
- t = time(NULL);
- curtime = ctime(&t);
- strcpy(token->string, "\"");
- strncat(token->string, curtime+4, 7);
- strncat(token->string+7, curtime+20, 4);
- strcat(token->string, "\"");
- free(curtime);
- token->type = TT_NAME;
- token->subtype = strlen(token->string);
- *firsttoken = token;
- *lasttoken = token;
- break;
- } //end case
- case BUILTIN_TIME:
- {
- t = time(NULL);
- curtime = ctime(&t);
- strcpy(token->string, "\"");
- strncat(token->string, curtime+11, 8);
- strcat(token->string, "\"");
- free(curtime);
- token->type = TT_NAME;
- token->subtype = strlen(token->string);
- *firsttoken = token;
- *lasttoken = token;
- break;
- } //end case
- case BUILTIN_STDC:
- default:
- {
- *firsttoken = NULL;
- *lasttoken = NULL;
- break;
- } //end case
- } //end switch
- return qtrue;
-} //end of the function PC_ExpandBuiltinDefine
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define,
- token_t **firsttoken, token_t **lasttoken)
-{
- token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t;
- token_t *t1, *t2, *first, *last, *nextpt, token;
- int parmnum, i;
-
- //if it is a builtin define
- if (define->builtin)
- {
- return PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken);
- } //end if
- //if the define has parameters
- if (define->numparms)
- {
- if (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse;
-#ifdef DEBUG_EVAL
- for (i = 0; i < define->numparms; i++)
- {
- Log_Write("define parms %d:", i);
- for (pt = parms[i]; pt; pt = pt->next)
- {
- Log_Write("%s", pt->string);
- } //end for
- } //end for
-#endif //DEBUG_EVAL
- } //end if
- //empty list at first
- first = NULL;
- last = NULL;
- //create a list with tokens of the expanded define
- for (dt = define->tokens; dt; dt = dt->next)
- {
- parmnum = -1;
- //if the token is a name, it could be a define parameter
- if (dt->type == TT_NAME)
- {
- parmnum = PC_FindDefineParm(define, dt->string);
- } //end if
- //if it is a define parameter
- if (parmnum >= 0)
- {
- for (pt = parms[parmnum]; pt; pt = pt->next)
- {
- t = PC_CopyToken(pt);
- //add the token to the list
- t->next = NULL;
- if (last) last->next = t;
- else first = t;
- last = t;
- } //end for
- } //end if
- else
- {
- //if stringizing operator
- if (dt->string[0] == '#' && dt->string[1] == '\0')
- {
- //the stringizing operator must be followed by a define parameter
- if (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string);
- else parmnum = -1;
- //
- if (parmnum >= 0)
- {
- //step over the stringizing operator
- dt = dt->next;
- //stringize the define parameter tokens
- if (!PC_StringizeTokens(parms[parmnum], &token))
- {
- SourceError(source, "can't stringize tokens");
- return qfalse;
- } //end if
- t = PC_CopyToken(&token);
- } //end if
- else
- {
- SourceWarning(source, "stringizing operator without define parameter");
- continue;
- } //end if
- } //end if
- else
- {
- t = PC_CopyToken(dt);
- } //end else
- //add the token to the list
- t->next = NULL;
- if (last) last->next = t;
- else first = t;
- last = t;
- } //end else
- } //end for
- //check for the merging operator
- for (t = first; t; )
- {
- if (t->next)
- {
- //if the merging operator
- if (t->next->string[0] == '#' && t->next->string[1] == '#')
- {
- t1 = t;
- t2 = t->next->next;
- if (t2)
- {
- if (!PC_MergeTokens(t1, t2))
- {
- SourceError(source, "can't merge %s with %s", t1->string, t2->string);
- return qfalse;
- } //end if
- PC_FreeToken(t1->next);
- t1->next = t2->next;
- if (t2 == last) last = t1;
- PC_FreeToken(t2);
- continue;
- } //end if
- } //end if
- } //end if
- t = t->next;
- } //end for
- //store the first and last token of the list
- *firsttoken = first;
- *lasttoken = last;
- //free all the parameter tokens
- for (i = 0; i < define->numparms; i++)
- {
- for (pt = parms[i]; pt; pt = nextpt)
- {
- nextpt = pt->next;
- PC_FreeToken(pt);
- } //end for
- } //end for
- //
- return qtrue;
-} //end of the function PC_ExpandDefine
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define)
-{
- token_t *firsttoken, *lasttoken;
-
- if (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse;
-
- if (firsttoken && lasttoken)
- {
- lasttoken->next = source->tokens;
- source->tokens = firsttoken;
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function PC_ExpandDefineIntoSource
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_ConvertPath(char *path)
-{
- char *ptr;
-
- //remove double path seperators
- for (ptr = path; *ptr;)
- {
- if ((*ptr == '\\' || *ptr == '/') &&
- (*(ptr+1) == '\\' || *(ptr+1) == '/'))
- {
- strcpy(ptr, ptr+1);
- } //end if
- else
- {
- ptr++;
- } //end else
- } //end while
- //set OS dependent path seperators
- for (ptr = path; *ptr;)
- {
- if (*ptr == '/' || *ptr == '\\') *ptr = PATHSEPERATOR_CHAR;
- ptr++;
- } //end while
-} //end of the function PC_ConvertPath
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_include(source_t *source)
-{
- script_t *script;
- token_t token;
- char path[MAX_PATH];
-#ifdef QUAKE
- foundfile_t file;
-#endif //QUAKE
-
- if (source->skip > 0) return qtrue;
- //
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "#include without file name");
- return qfalse;
- } //end if
- if (token.linescrossed > 0)
- {
- SourceError(source, "#include without file name");
- return qfalse;
- } //end if
- if (token.type == TT_STRING)
- {
- StripDoubleQuotes(token.string);
- PC_ConvertPath(token.string);
- script = LoadScriptFile(token.string);
- if (!script)
- {
- strcpy(path, source->includepath);
- strcat(path, token.string);
- script = LoadScriptFile(path);
- } //end if
- } //end if
- else if (token.type == TT_PUNCTUATION && *token.string == '<')
- {
- strcpy(path, source->includepath);
- while(PC_ReadSourceToken(source, &token))
- {
- if (token.linescrossed > 0)
- {
- PC_UnreadSourceToken(source, &token);
- break;
- } //end if
- if (token.type == TT_PUNCTUATION && *token.string == '>') break;
- strncat(path, token.string, MAX_PATH);
- } //end while
- if (*token.string != '>')
- {
- SourceWarning(source, "#include missing trailing >");
- } //end if
- if (!strlen(path))
- {
- SourceError(source, "#include without file name between < >");
- return qfalse;
- } //end if
- PC_ConvertPath(path);
- script = LoadScriptFile(path);
- } //end if
- else
- {
- SourceError(source, "#include without file name");
- return qfalse;
- } //end else
-#ifdef QUAKE
- if (!script)
- {
- Com_Memset(&file, 0, sizeof(foundfile_t));
- script = LoadScriptFile(path);
- if (script) strncpy(script->filename, path, MAX_PATH);
- } //end if
-#endif //QUAKE
- if (!script)
- {
-#ifdef SCREWUP
- SourceWarning(source, "file %s not found", path);
- return qtrue;
-#else
- SourceError(source, "file %s not found", path);
- return qfalse;
-#endif //SCREWUP
- } //end if
- PC_PushScript(source, script);
- return qtrue;
-} //end of the function PC_Directive_include
-//============================================================================
-// reads a token from the current line, continues reading on the next
-// line only if a backslash '\' is encountered.
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ReadLine(source_t *source, token_t *token)
-{
- int crossline;
-
- crossline = 0;
- do
- {
- if (!PC_ReadSourceToken(source, token)) return qfalse;
-
- if (token->linescrossed > crossline)
- {
- PC_UnreadSourceToken(source, token);
- return qfalse;
- } //end if
- crossline = 1;
- } while(!strcmp(token->string, "\\"));
- return qtrue;
-} //end of the function PC_ReadLine
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_WhiteSpaceBeforeToken(token_t *token)
-{
- return token->endwhitespace_p - token->whitespace_p > 0;
-} //end of the function PC_WhiteSpaceBeforeToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_ClearTokenWhiteSpace(token_t *token)
-{
- token->whitespace_p = NULL;
- token->endwhitespace_p = NULL;
- token->linescrossed = 0;
-} //end of the function PC_ClearTokenWhiteSpace
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_undef(source_t *source)
-{
- token_t token;
- define_t *define, *lastdefine;
- int hash;
-
- if (source->skip > 0) return qtrue;
- //
- if (!PC_ReadLine(source, &token))
- {
- SourceError(source, "undef without name");
- return qfalse;
- } //end if
- if (token.type != TT_NAME)
- {
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "expected name, found %s", token.string);
- return qfalse;
- } //end if
-#if DEFINEHASHING
-
- hash = PC_NameHash(token.string);
- for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext)
- {
- if (!strcmp(define->name, token.string))
- {
- if (define->flags & DEFINE_FIXED)
- {
- SourceWarning(source, "can't undef %s", token.string);
- } //end if
- else
- {
- if (lastdefine) lastdefine->hashnext = define->hashnext;
- else source->definehash[hash] = define->hashnext;
- PC_FreeDefine(define);
- } //end else
- break;
- } //end if
- lastdefine = define;
- } //end for
-#else //DEFINEHASHING
- for (lastdefine = NULL, define = source->defines; define; define = define->next)
- {
- if (!strcmp(define->name, token.string))
- {
- if (define->flags & DEFINE_FIXED)
- {
- SourceWarning(source, "can't undef %s", token.string);
- } //end if
- else
- {
- if (lastdefine) lastdefine->next = define->next;
- else source->defines = define->next;
- PC_FreeDefine(define);
- } //end else
- break;
- } //end if
- lastdefine = define;
- } //end for
-#endif //DEFINEHASHING
- return qtrue;
-} //end of the function PC_Directive_undef
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_define(source_t *source)
-{
- token_t token, *t, *last;
- define_t *define;
-
- if (source->skip > 0) return qtrue;
- //
- if (!PC_ReadLine(source, &token))
- {
- SourceError(source, "#define without name");
- return qfalse;
- } //end if
- if (token.type != TT_NAME)
- {
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "expected name after #define, found %s", token.string);
- return qfalse;
- } //end if
- //check if the define already exists
-#if DEFINEHASHING
- define = PC_FindHashedDefine(source->definehash, token.string);
-#else
- define = PC_FindDefine(source->defines, token.string);
-#endif //DEFINEHASHING
- if (define)
- {
- if (define->flags & DEFINE_FIXED)
- {
- SourceError(source, "can't redefine %s", token.string);
- return qfalse;
- } //end if
- SourceWarning(source, "redefinition of %s", token.string);
- //unread the define name before executing the #undef directive
- PC_UnreadSourceToken(source, &token);
- if (!PC_Directive_undef(source)) return qfalse;
- //if the define was not removed (define->flags & DEFINE_FIXED)
-#if DEFINEHASHING
- define = PC_FindHashedDefine(source->definehash, token.string);
-#else
- define = PC_FindDefine(source->defines, token.string);
-#endif //DEFINEHASHING
- } //end if
- //allocate define
- define = (define_t *) GetMemory(sizeof(define_t) + strlen(token.string) + 1);
- Com_Memset(define, 0, sizeof(define_t));
- define->name = (char *) define + sizeof(define_t);
- strcpy(define->name, token.string);
- //add the define to the source
-#if DEFINEHASHING
- PC_AddDefineToHash(define, source->definehash);
-#else //DEFINEHASHING
- define->next = source->defines;
- source->defines = define;
-#endif //DEFINEHASHING
- //if nothing is defined, just return
- if (!PC_ReadLine(source, &token)) return qtrue;
- //if it is a define with parameters
- if (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "("))
- {
- //read the define parameters
- last = NULL;
- if (!PC_CheckTokenString(source, ")"))
- {
- while(1)
- {
- if (!PC_ReadLine(source, &token))
- {
- SourceError(source, "expected define parameter");
- return qfalse;
- } //end if
- //if it isn't a name
- if (token.type != TT_NAME)
- {
- SourceError(source, "invalid define parameter");
- return qfalse;
- } //end if
- //
- if (PC_FindDefineParm(define, token.string) >= 0)
- {
- SourceError(source, "two the same define parameters");
- return qfalse;
- } //end if
- //add the define parm
- t = PC_CopyToken(&token);
- PC_ClearTokenWhiteSpace(t);
- t->next = NULL;
- if (last) last->next = t;
- else define->parms = t;
- last = t;
- define->numparms++;
- //read next token
- if (!PC_ReadLine(source, &token))
- {
- SourceError(source, "define parameters not terminated");
- return qfalse;
- } //end if
- //
- if (!strcmp(token.string, ")")) break;
- //then it must be a comma
- if (strcmp(token.string, ","))
- {
- SourceError(source, "define not terminated");
- return qfalse;
- } //end if
- } //end while
- } //end if
- if (!PC_ReadLine(source, &token)) return qtrue;
- } //end if
- //read the defined stuff
- last = NULL;
- do
- {
- t = PC_CopyToken(&token);
- if (t->type == TT_NAME && !strcmp(t->string, define->name))
- {
- SourceError(source, "recursive define (removed recursion)");
- continue;
- } //end if
- PC_ClearTokenWhiteSpace(t);
- t->next = NULL;
- if (last) last->next = t;
- else define->tokens = t;
- last = t;
- } while(PC_ReadLine(source, &token));
- //
- if (last)
- {
- //check for merge operators at the beginning or end
- if (!strcmp(define->tokens->string, "##") ||
- !strcmp(last->string, "##"))
- {
- SourceError(source, "define with misplaced ##");
- return qfalse;
- } //end if
- } //end if
- return qtrue;
-} //end of the function PC_Directive_define
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-define_t *PC_DefineFromString(char *string)
-{
- script_t *script;
- source_t src;
- token_t *t;
- int res, i;
- define_t *def;
-
- PC_InitTokenHeap();
-
- script = LoadScriptMemory(string, strlen(string), "*extern");
- //create a new source
- Com_Memset(&src, 0, sizeof(source_t));
- strncpy(src.filename, "*extern", MAX_PATH);
- src.scriptstack = script;
-#if DEFINEHASHING
- src.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));
-#endif //DEFINEHASHING
- //create a define from the source
- res = PC_Directive_define(&src);
- //free any tokens if left
- for (t = src.tokens; t; t = src.tokens)
- {
- src.tokens = src.tokens->next;
- PC_FreeToken(t);
- } //end for
-#ifdef DEFINEHASHING
- def = NULL;
- for (i = 0; i < DEFINEHASHSIZE; i++)
- {
- if (src.definehash[i])
- {
- def = src.definehash[i];
- break;
- } //end if
- } //end for
-#else
- def = src.defines;
-#endif //DEFINEHASHING
- //
-#if DEFINEHASHING
- FreeMemory(src.definehash);
-#endif //DEFINEHASHING
- //
- FreeScript(script);
- //if the define was created succesfully
- if (res > 0) return def;
- //free the define is created
- if (src.defines) PC_FreeDefine(def);
- //
- return NULL;
-} //end of the function PC_DefineFromString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_AddDefine(source_t *source, char *string)
-{
- define_t *define;
-
- define = PC_DefineFromString(string);
- if (!define) return qfalse;
-#if DEFINEHASHING
- PC_AddDefineToHash(define, source->definehash);
-#else //DEFINEHASHING
- define->next = source->defines;
- source->defines = define;
-#endif //DEFINEHASHING
- return qtrue;
-} //end of the function PC_AddDefine
-//============================================================================
-// add a globals define that will be added to all opened sources
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_AddGlobalDefine(char *string)
-{
- define_t *define;
-
- define = PC_DefineFromString(string);
- if (!define) return qfalse;
- define->next = globaldefines;
- globaldefines = define;
- return qtrue;
-} //end of the function PC_AddGlobalDefine
-//============================================================================
-// remove the given global define
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_RemoveGlobalDefine(char *name)
-{
- define_t *define;
-
- define = PC_FindDefine(globaldefines, name);
- if (define)
- {
- PC_FreeDefine(define);
- return qtrue;
- } //end if
- return qfalse;
-} //end of the function PC_RemoveGlobalDefine
-//============================================================================
-// remove all globals defines
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_RemoveAllGlobalDefines(void)
-{
- define_t *define;
-
- for (define = globaldefines; define; define = globaldefines)
- {
- globaldefines = globaldefines->next;
- PC_FreeDefine(define);
- } //end for
-} //end of the function PC_RemoveAllGlobalDefines
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-define_t *PC_CopyDefine(source_t *source, define_t *define)
-{
- define_t *newdefine;
- token_t *token, *newtoken, *lasttoken;
-
- newdefine = (define_t *) GetMemory(sizeof(define_t) + strlen(define->name) + 1);
- //copy the define name
- newdefine->name = (char *) newdefine + sizeof(define_t);
- strcpy(newdefine->name, define->name);
- newdefine->flags = define->flags;
- newdefine->builtin = define->builtin;
- newdefine->numparms = define->numparms;
- //the define is not linked
- newdefine->next = NULL;
- newdefine->hashnext = NULL;
- //copy the define tokens
- newdefine->tokens = NULL;
- for (lasttoken = NULL, token = define->tokens; token; token = token->next)
- {
- newtoken = PC_CopyToken(token);
- newtoken->next = NULL;
- if (lasttoken) lasttoken->next = newtoken;
- else newdefine->tokens = newtoken;
- lasttoken = newtoken;
- } //end for
- //copy the define parameters
- newdefine->parms = NULL;
- for (lasttoken = NULL, token = define->parms; token; token = token->next)
- {
- newtoken = PC_CopyToken(token);
- newtoken->next = NULL;
- if (lasttoken) lasttoken->next = newtoken;
- else newdefine->parms = newtoken;
- lasttoken = newtoken;
- } //end for
- return newdefine;
-} //end of the function PC_CopyDefine
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_AddGlobalDefinesToSource(source_t *source)
-{
- define_t *define, *newdefine;
-
- for (define = globaldefines; define; define = define->next)
- {
- newdefine = PC_CopyDefine(source, define);
-#if DEFINEHASHING
- PC_AddDefineToHash(newdefine, source->definehash);
-#else //DEFINEHASHING
- newdefine->next = source->defines;
- source->defines = newdefine;
-#endif //DEFINEHASHING
- } //end for
-} //end of the function PC_AddGlobalDefinesToSource
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_if_def(source_t *source, int type)
-{
- token_t token;
- define_t *d;
- int skip;
-
- if (!PC_ReadLine(source, &token))
- {
- SourceError(source, "#ifdef without name");
- return qfalse;
- } //end if
- if (token.type != TT_NAME)
- {
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "expected name after #ifdef, found %s", token.string);
- return qfalse;
- } //end if
-#if DEFINEHASHING
- d = PC_FindHashedDefine(source->definehash, token.string);
-#else
- d = PC_FindDefine(source->defines, token.string);
-#endif //DEFINEHASHING
- skip = (type == INDENT_IFDEF) == (d == NULL);
- PC_PushIndent(source, type, skip);
- return qtrue;
-} //end of the function PC_Directiveif_def
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_ifdef(source_t *source)
-{
- return PC_Directive_if_def(source, INDENT_IFDEF);
-} //end of the function PC_Directive_ifdef
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_ifndef(source_t *source)
-{
- return PC_Directive_if_def(source, INDENT_IFNDEF);
-} //end of the function PC_Directive_ifndef
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_else(source_t *source)
-{
- int type, skip;
-
- PC_PopIndent(source, &type, &skip);
- if (!type)
- {
- SourceError(source, "misplaced #else");
- return qfalse;
- } //end if
- if (type == INDENT_ELSE)
- {
- SourceError(source, "#else after #else");
- return qfalse;
- } //end if
- PC_PushIndent(source, INDENT_ELSE, !skip);
- return qtrue;
-} //end of the function PC_Directive_else
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_endif(source_t *source)
-{
- int type, skip;
-
- PC_PopIndent(source, &type, &skip);
- if (!type)
- {
- SourceError(source, "misplaced #endif");
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function PC_Directive_endif
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-typedef struct operator_s
-{
- int operator;
- int priority;
- int parentheses;
- struct operator_s *prev, *next;
-} operator_t;
-
-typedef struct value_s
-{
- signed long int intvalue;
- double floatvalue;
- int parentheses;
- struct value_s *prev, *next;
-} value_t;
-
-int PC_OperatorPriority(int op)
-{
- switch(op)
- {
- case P_MUL: return 15;
- case P_DIV: return 15;
- case P_MOD: return 15;
- case P_ADD: return 14;
- case P_SUB: return 14;
-
- case P_LOGIC_AND: return 7;
- case P_LOGIC_OR: return 6;
- case P_LOGIC_GEQ: return 12;
- case P_LOGIC_LEQ: return 12;
- case P_LOGIC_EQ: return 11;
- case P_LOGIC_UNEQ: return 11;
-
- case P_LOGIC_NOT: return 16;
- case P_LOGIC_GREATER: return 12;
- case P_LOGIC_LESS: return 12;
-
- case P_RSHIFT: return 13;
- case P_LSHIFT: return 13;
-
- case P_BIN_AND: return 10;
- case P_BIN_OR: return 8;
- case P_BIN_XOR: return 9;
- case P_BIN_NOT: return 16;
-
- case P_COLON: return 5;
- case P_QUESTIONMARK: return 5;
- } //end switch
- return qfalse;
-} //end of the function PC_OperatorPriority
-
-//#define AllocValue() GetClearedMemory(sizeof(value_t));
-//#define FreeValue(val) FreeMemory(val)
-//#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t));
-//#define FreeOperator(op) FreeMemory(op);
-
-#define MAX_VALUES 64
-#define MAX_OPERATORS 64
-#define AllocValue(val) \
- if (numvalues >= MAX_VALUES) { \
- SourceError(source, "out of value space\n"); \
- error = 1; \
- break; \
- } \
- else \
- val = &value_heap[numvalues++];
-#define FreeValue(val)
-//
-#define AllocOperator(op) \
- if (numoperators >= MAX_OPERATORS) { \
- SourceError(source, "out of operator space\n"); \
- error = 1; \
- break; \
- } \
- else \
- op = &operator_heap[numoperators++];
-#define FreeOperator(op)
-
-int PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue,
- double *floatvalue, int integer)
-{
- operator_t *o, *firstoperator, *lastoperator;
- value_t *v, *firstvalue, *lastvalue, *v1, *v2;
- token_t *t;
- int brace = 0;
- int parentheses = 0;
- int error = 0;
- int lastwasvalue = 0;
- int negativevalue = 0;
- int questmarkintvalue = 0;
- double questmarkfloatvalue = 0;
- int gotquestmarkvalue = qfalse;
- int lastoperatortype = 0;
- //
- operator_t operator_heap[MAX_OPERATORS];
- int numoperators = 0;
- value_t value_heap[MAX_VALUES];
- int numvalues = 0;
-
- firstoperator = lastoperator = NULL;
- firstvalue = lastvalue = NULL;
- if (intvalue) *intvalue = 0;
- if (floatvalue) *floatvalue = 0;
- for (t = tokens; t; t = t->next)
- {
- switch(t->type)
- {
- case TT_NAME:
- {
- if (lastwasvalue || negativevalue)
- {
- SourceError(source, "syntax error in #if/#elif");
- error = 1;
- break;
- } //end if
- if (strcmp(t->string, "defined"))
- {
- SourceError(source, "undefined name %s in #if/#elif", t->string);
- error = 1;
- break;
- } //end if
- t = t->next;
- if (!strcmp(t->string, "("))
- {
- brace = qtrue;
- t = t->next;
- } //end if
- if (!t || t->type != TT_NAME)
- {
- SourceError(source, "defined without name in #if/#elif");
- error = 1;
- break;
- } //end if
- //v = (value_t *) GetClearedMemory(sizeof(value_t));
- AllocValue(v);
-#if DEFINEHASHING
- if (PC_FindHashedDefine(source->definehash, t->string))
-#else
- if (PC_FindDefine(source->defines, t->string))
-#endif //DEFINEHASHING
- {
- v->intvalue = 1;
- v->floatvalue = 1;
- } //end if
- else
- {
- v->intvalue = 0;
- v->floatvalue = 0;
- } //end else
- v->parentheses = parentheses;
- v->next = NULL;
- v->prev = lastvalue;
- if (lastvalue) lastvalue->next = v;
- else firstvalue = v;
- lastvalue = v;
- if (brace)
- {
- t = t->next;
- if (!t || strcmp(t->string, ")"))
- {
- SourceError(source, "defined without ) in #if/#elif");
- error = 1;
- break;
- } //end if
- } //end if
- brace = qfalse;
- // defined() creates a value
- lastwasvalue = 1;
- break;
- } //end case
- case TT_NUMBER:
- {
- if (lastwasvalue)
- {
- SourceError(source, "syntax error in #if/#elif");
- error = 1;
- break;
- } //end if
- //v = (value_t *) GetClearedMemory(sizeof(value_t));
- AllocValue(v);
- if (negativevalue)
- {
- v->intvalue = - (signed int) t->intvalue;
- v->floatvalue = - t->floatvalue;
- } //end if
- else
- {
- v->intvalue = t->intvalue;
- v->floatvalue = t->floatvalue;
- } //end else
- v->parentheses = parentheses;
- v->next = NULL;
- v->prev = lastvalue;
- if (lastvalue) lastvalue->next = v;
- else firstvalue = v;
- lastvalue = v;
- //last token was a value
- lastwasvalue = 1;
- //
- negativevalue = 0;
- break;
- } //end case
- case TT_PUNCTUATION:
- {
- if (negativevalue)
- {
- SourceError(source, "misplaced minus sign in #if/#elif");
- error = 1;
- break;
- } //end if
- if (t->subtype == P_PARENTHESESOPEN)
- {
- parentheses++;
- break;
- } //end if
- else if (t->subtype == P_PARENTHESESCLOSE)
- {
- parentheses--;
- if (parentheses < 0)
- {
- SourceError(source, "too many ) in #if/#elsif");
- error = 1;
- } //end if
- break;
- } //end else if
- //check for invalid operators on floating point values
- if (!integer)
- {
- if (t->subtype == P_BIN_NOT || t->subtype == P_MOD ||
- t->subtype == P_RSHIFT || t->subtype == P_LSHIFT ||
- t->subtype == P_BIN_AND || t->subtype == P_BIN_OR ||
- t->subtype == P_BIN_XOR)
- {
- SourceError(source, "illigal operator %s on floating point operands\n", t->string);
- error = 1;
- break;
- } //end if
- } //end if
- switch(t->subtype)
- {
- case P_LOGIC_NOT:
- case P_BIN_NOT:
- {
- if (lastwasvalue)
- {
- SourceError(source, "! or ~ after value in #if/#elif");
- error = 1;
- break;
- } //end if
- break;
- } //end case
- case P_INC:
- case P_DEC:
- {
- SourceError(source, "++ or -- used in #if/#elif");
- break;
- } //end case
- case P_SUB:
- {
- if (!lastwasvalue)
- {
- negativevalue = 1;
- break;
- } //end if
- } //end case
-
- case P_MUL:
- case P_DIV:
- case P_MOD:
- case P_ADD:
-
- case P_LOGIC_AND:
- case P_LOGIC_OR:
- case P_LOGIC_GEQ:
- case P_LOGIC_LEQ:
- case P_LOGIC_EQ:
- case P_LOGIC_UNEQ:
-
- case P_LOGIC_GREATER:
- case P_LOGIC_LESS:
-
- case P_RSHIFT:
- case P_LSHIFT:
-
- case P_BIN_AND:
- case P_BIN_OR:
- case P_BIN_XOR:
-
- case P_COLON:
- case P_QUESTIONMARK:
- {
- if (!lastwasvalue)
- {
- SourceError(source, "operator %s after operator in #if/#elif", t->string);
- error = 1;
- break;
- } //end if
- break;
- } //end case
- default:
- {
- SourceError(source, "invalid operator %s in #if/#elif", t->string);
- error = 1;
- break;
- } //end default
- } //end switch
- if (!error && !negativevalue)
- {
- //o = (operator_t *) GetClearedMemory(sizeof(operator_t));
- AllocOperator(o);
- o->operator = t->subtype;
- o->priority = PC_OperatorPriority(t->subtype);
- o->parentheses = parentheses;
- o->next = NULL;
- o->prev = lastoperator;
- if (lastoperator) lastoperator->next = o;
- else firstoperator = o;
- lastoperator = o;
- lastwasvalue = 0;
- } //end if
- break;
- } //end case
- default:
- {
- SourceError(source, "unknown %s in #if/#elif", t->string);
- error = 1;
- break;
- } //end default
- } //end switch
- if (error) break;
- } //end for
- if (!error)
- {
- if (!lastwasvalue)
- {
- SourceError(source, "trailing operator in #if/#elif");
- error = 1;
- } //end if
- else if (parentheses)
- {
- SourceError(source, "too many ( in #if/#elif");
- error = 1;
- } //end else if
- } //end if
- //
- gotquestmarkvalue = qfalse;
- questmarkintvalue = 0;
- questmarkfloatvalue = 0;
- //while there are operators
- while(!error && firstoperator)
- {
- v = firstvalue;
- for (o = firstoperator; o->next; o = o->next)
- {
- //if the current operator is nested deeper in parentheses
- //than the next operator
- if (o->parentheses > o->next->parentheses) break;
- //if the current and next operator are nested equally deep in parentheses
- if (o->parentheses == o->next->parentheses)
- {
- //if the priority of the current operator is equal or higher
- //than the priority of the next operator
- if (o->priority >= o->next->priority) break;
- } //end if
- //if the arity of the operator isn't equal to 1
- if (o->operator != P_LOGIC_NOT
- && o->operator != P_BIN_NOT) v = v->next;
- //if there's no value or no next value
- if (!v)
- {
- SourceError(source, "mising values in #if/#elif");
- error = 1;
- break;
- } //end if
- } //end for
- if (error) break;
- v1 = v;
- v2 = v->next;
-#ifdef DEBUG_EVAL
- if (integer)
- {
- Log_Write("operator %s, value1 = %d", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue);
- if (v2) Log_Write("value2 = %d", v2->intvalue);
- } //end if
- else
- {
- Log_Write("operator %s, value1 = %f", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue);
- if (v2) Log_Write("value2 = %f", v2->floatvalue);
- } //end else
-#endif //DEBUG_EVAL
- switch(o->operator)
- {
- case P_LOGIC_NOT: v1->intvalue = !v1->intvalue;
- v1->floatvalue = !v1->floatvalue; break;
- case P_BIN_NOT: v1->intvalue = ~v1->intvalue;
- break;
- case P_MUL: v1->intvalue *= v2->intvalue;
- v1->floatvalue *= v2->floatvalue; break;
- case P_DIV: if (!v2->intvalue || !v2->floatvalue)
- {
- SourceError(source, "divide by zero in #if/#elif\n");
- error = 1;
- break;
- }
- v1->intvalue /= v2->intvalue;
- v1->floatvalue /= v2->floatvalue; break;
- case P_MOD: if (!v2->intvalue)
- {
- SourceError(source, "divide by zero in #if/#elif\n");
- error = 1;
- break;
- }
- v1->intvalue %= v2->intvalue; break;
- case P_ADD: v1->intvalue += v2->intvalue;
- v1->floatvalue += v2->floatvalue; break;
- case P_SUB: v1->intvalue -= v2->intvalue;
- v1->floatvalue -= v2->floatvalue; break;
- case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue;
- v1->floatvalue = v1->floatvalue && v2->floatvalue; break;
- case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue;
- v1->floatvalue = v1->floatvalue || v2->floatvalue; break;
- case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue;
- v1->floatvalue = v1->floatvalue >= v2->floatvalue; break;
- case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue;
- v1->floatvalue = v1->floatvalue <= v2->floatvalue; break;
- case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue;
- v1->floatvalue = v1->floatvalue == v2->floatvalue; break;
- case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue;
- v1->floatvalue = v1->floatvalue != v2->floatvalue; break;
- case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue;
- v1->floatvalue = v1->floatvalue > v2->floatvalue; break;
- case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue;
- v1->floatvalue = v1->floatvalue < v2->floatvalue; break;
- case P_RSHIFT: v1->intvalue >>= v2->intvalue;
- break;
- case P_LSHIFT: v1->intvalue <<= v2->intvalue;
- break;
- case P_BIN_AND: v1->intvalue &= v2->intvalue;
- break;
- case P_BIN_OR: v1->intvalue |= v2->intvalue;
- break;
- case P_BIN_XOR: v1->intvalue ^= v2->intvalue;
- break;
- case P_COLON:
- {
- if (!gotquestmarkvalue)
- {
- SourceError(source, ": without ? in #if/#elif");
- error = 1;
- break;
- } //end if
- if (integer)
- {
- if (!questmarkintvalue) v1->intvalue = v2->intvalue;
- } //end if
- else
- {
- if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue;
- } //end else
- gotquestmarkvalue = qfalse;
- break;
- } //end case
- case P_QUESTIONMARK:
- {
- if (gotquestmarkvalue)
- {
- SourceError(source, "? after ? in #if/#elif");
- error = 1;
- break;
- } //end if
- questmarkintvalue = v1->intvalue;
- questmarkfloatvalue = v1->floatvalue;
- gotquestmarkvalue = qtrue;
- break;
- } //end if
- } //end switch
-#ifdef DEBUG_EVAL
- if (integer) Log_Write("result value = %d", v1->intvalue);
- else Log_Write("result value = %f", v1->floatvalue);
-#endif //DEBUG_EVAL
- if (error) break;
- lastoperatortype = o->operator;
- //if not an operator with arity 1
- if (o->operator != P_LOGIC_NOT
- && o->operator != P_BIN_NOT)
- {
- //remove the second value if not question mark operator
- if (o->operator != P_QUESTIONMARK) v = v->next;
- //
- if (v->prev) v->prev->next = v->next;
- else firstvalue = v->next;
- if (v->next) v->next->prev = v->prev;
- else lastvalue = v->prev;
- //FreeMemory(v);
- FreeValue(v);
- } //end if
- //remove the operator
- if (o->prev) o->prev->next = o->next;
- else firstoperator = o->next;
- if (o->next) o->next->prev = o->prev;
- else lastoperator = o->prev;
- //FreeMemory(o);
- FreeOperator(o);
- } //end while
- if (firstvalue)
- {
- if (intvalue) *intvalue = firstvalue->intvalue;
- if (floatvalue) *floatvalue = firstvalue->floatvalue;
- } //end if
- for (o = firstoperator; o; o = lastoperator)
- {
- lastoperator = o->next;
- //FreeMemory(o);
- FreeOperator(o);
- } //end for
- for (v = firstvalue; v; v = lastvalue)
- {
- lastvalue = v->next;
- //FreeMemory(v);
- FreeValue(v);
- } //end for
- if (!error) return qtrue;
- if (intvalue) *intvalue = 0;
- if (floatvalue) *floatvalue = 0;
- return qfalse;
-} //end of the function PC_EvaluateTokens
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Evaluate(source_t *source, signed long int *intvalue,
- double *floatvalue, int integer)
-{
- token_t token, *firsttoken, *lasttoken;
- token_t *t, *nexttoken;
- define_t *define;
- int defined = qfalse;
-
- if (intvalue) *intvalue = 0;
- if (floatvalue) *floatvalue = 0;
- //
- if (!PC_ReadLine(source, &token))
- {
- SourceError(source, "no value after #if/#elif");
- return qfalse;
- } //end if
- firsttoken = NULL;
- lasttoken = NULL;
- do
- {
- //if the token is a name
- if (token.type == TT_NAME)
- {
- if (defined)
- {
- defined = qfalse;
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (lasttoken) lasttoken->next = t;
- else firsttoken = t;
- lasttoken = t;
- } //end if
- else if (!strcmp(token.string, "defined"))
- {
- defined = qtrue;
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (lasttoken) lasttoken->next = t;
- else firsttoken = t;
- lasttoken = t;
- } //end if
- else
- {
- //then it must be a define
-#if DEFINEHASHING
- define = PC_FindHashedDefine(source->definehash, token.string);
-#else
- define = PC_FindDefine(source->defines, token.string);
-#endif //DEFINEHASHING
- if (!define)
- {
- SourceError(source, "can't evaluate %s, not defined", token.string);
- return qfalse;
- } //end if
- if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse;
- } //end else
- } //end if
- //if the token is a number or a punctuation
- else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION)
- {
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (lasttoken) lasttoken->next = t;
- else firsttoken = t;
- lasttoken = t;
- } //end else
- else //can't evaluate the token
- {
- SourceError(source, "can't evaluate %s", token.string);
- return qfalse;
- } //end else
- } while(PC_ReadLine(source, &token));
- //
- if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse;
- //
-#ifdef DEBUG_EVAL
- Log_Write("eval:");
-#endif //DEBUG_EVAL
- for (t = firsttoken; t; t = nexttoken)
- {
-#ifdef DEBUG_EVAL
- Log_Write(" %s", t->string);
-#endif //DEBUG_EVAL
- nexttoken = t->next;
- PC_FreeToken(t);
- } //end for
-#ifdef DEBUG_EVAL
- if (integer) Log_Write("eval result: %d", *intvalue);
- else Log_Write("eval result: %f", *floatvalue);
-#endif //DEBUG_EVAL
- //
- return qtrue;
-} //end of the function PC_Evaluate
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_DollarEvaluate(source_t *source, signed long int *intvalue,
- double *floatvalue, int integer)
-{
- int indent, defined = qfalse;
- token_t token, *firsttoken, *lasttoken;
- token_t *t, *nexttoken;
- define_t *define;
-
- if (intvalue) *intvalue = 0;
- if (floatvalue) *floatvalue = 0;
- //
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "no leading ( after $evalint/$evalfloat");
- return qfalse;
- } //end if
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "nothing to evaluate");
- return qfalse;
- } //end if
- indent = 1;
- firsttoken = NULL;
- lasttoken = NULL;
- do
- {
- //if the token is a name
- if (token.type == TT_NAME)
- {
- if (defined)
- {
- defined = qfalse;
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (lasttoken) lasttoken->next = t;
- else firsttoken = t;
- lasttoken = t;
- } //end if
- else if (!strcmp(token.string, "defined"))
- {
- defined = qtrue;
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (lasttoken) lasttoken->next = t;
- else firsttoken = t;
- lasttoken = t;
- } //end if
- else
- {
- //then it must be a define
-#if DEFINEHASHING
- define = PC_FindHashedDefine(source->definehash, token.string);
-#else
- define = PC_FindDefine(source->defines, token.string);
-#endif //DEFINEHASHING
- if (!define)
- {
- SourceError(source, "can't evaluate %s, not defined", token.string);
- return qfalse;
- } //end if
- if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse;
- } //end else
- } //end if
- //if the token is a number or a punctuation
- else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION)
- {
- if (*token.string == '(') indent++;
- else if (*token.string == ')') indent--;
- if (indent <= 0) break;
- t = PC_CopyToken(&token);
- t->next = NULL;
- if (lasttoken) lasttoken->next = t;
- else firsttoken = t;
- lasttoken = t;
- } //end else
- else //can't evaluate the token
- {
- SourceError(source, "can't evaluate %s", token.string);
- return qfalse;
- } //end else
- } while(PC_ReadSourceToken(source, &token));
- //
- if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse;
- //
-#ifdef DEBUG_EVAL
- Log_Write("$eval:");
-#endif //DEBUG_EVAL
- for (t = firsttoken; t; t = nexttoken)
- {
-#ifdef DEBUG_EVAL
- Log_Write(" %s", t->string);
-#endif //DEBUG_EVAL
- nexttoken = t->next;
- PC_FreeToken(t);
- } //end for
-#ifdef DEBUG_EVAL
- if (integer) Log_Write("$eval result: %d", *intvalue);
- else Log_Write("$eval result: %f", *floatvalue);
-#endif //DEBUG_EVAL
- //
- return qtrue;
-} //end of the function PC_DollarEvaluate
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_elif(source_t *source)
-{
- signed long int value;
- int type, skip;
-
- PC_PopIndent(source, &type, &skip);
- if (!type || type == INDENT_ELSE)
- {
- SourceError(source, "misplaced #elif");
- return qfalse;
- } //end if
- if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;
- skip = (value == 0);
- PC_PushIndent(source, INDENT_ELIF, skip);
- return qtrue;
-} //end of the function PC_Directive_elif
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_if(source_t *source)
-{
- signed long int value;
- int skip;
-
- if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;
- skip = (value == 0);
- PC_PushIndent(source, INDENT_IF, skip);
- return qtrue;
-} //end of the function PC_Directive
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_line(source_t *source)
-{
- SourceError(source, "#line directive not supported");
- return qfalse;
-} //end of the function PC_Directive_line
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_error(source_t *source)
-{
- token_t token;
-
- strcpy(token.string, "");
- PC_ReadSourceToken(source, &token);
- SourceError(source, "#error directive: %s", token.string);
- return qfalse;
-} //end of the function PC_Directive_error
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_pragma(source_t *source)
-{
- token_t token;
-
- SourceWarning(source, "#pragma directive not supported");
- while(PC_ReadLine(source, &token)) ;
- return qtrue;
-} //end of the function PC_Directive_pragma
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void UnreadSignToken(source_t *source)
-{
- token_t token;
-
- token.line = source->scriptstack->line;
- token.whitespace_p = source->scriptstack->script_p;
- token.endwhitespace_p = source->scriptstack->script_p;
- token.linescrossed = 0;
- strcpy(token.string, "-");
- token.type = TT_PUNCTUATION;
- token.subtype = P_SUB;
- PC_UnreadSourceToken(source, &token);
-} //end of the function UnreadSignToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_eval(source_t *source)
-{
- signed long int value;
- token_t token;
-
- if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;
- //
- token.line = source->scriptstack->line;
- token.whitespace_p = source->scriptstack->script_p;
- token.endwhitespace_p = source->scriptstack->script_p;
- token.linescrossed = 0;
- sprintf(token.string, "%d", abs(value));
- token.type = TT_NUMBER;
- token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;
- PC_UnreadSourceToken(source, &token);
- if (value < 0) UnreadSignToken(source);
- return qtrue;
-} //end of the function PC_Directive_eval
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_Directive_evalfloat(source_t *source)
-{
- double value;
- token_t token;
-
- if (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse;
- token.line = source->scriptstack->line;
- token.whitespace_p = source->scriptstack->script_p;
- token.endwhitespace_p = source->scriptstack->script_p;
- token.linescrossed = 0;
- sprintf(token.string, "%1.2f", fabs(value));
- token.type = TT_NUMBER;
- token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;
- PC_UnreadSourceToken(source, &token);
- if (value < 0) UnreadSignToken(source);
- return qtrue;
-} //end of the function PC_Directive_evalfloat
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-directive_t directives[20] =
-{
- {"if", PC_Directive_if},
- {"ifdef", PC_Directive_ifdef},
- {"ifndef", PC_Directive_ifndef},
- {"elif", PC_Directive_elif},
- {"else", PC_Directive_else},
- {"endif", PC_Directive_endif},
- {"include", PC_Directive_include},
- {"define", PC_Directive_define},
- {"undef", PC_Directive_undef},
- {"line", PC_Directive_line},
- {"error", PC_Directive_error},
- {"pragma", PC_Directive_pragma},
- {"eval", PC_Directive_eval},
- {"evalfloat", PC_Directive_evalfloat},
- {NULL, NULL}
-};
-
-int PC_ReadDirective(source_t *source)
-{
- token_t token;
- int i;
-
- //read the directive name
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "found # without name");
- return qfalse;
- } //end if
- //directive name must be on the same line
- if (token.linescrossed > 0)
- {
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "found # at end of line");
- return qfalse;
- } //end if
- //if if is a name
- if (token.type == TT_NAME)
- {
- //find the precompiler directive
- for (i = 0; directives[i].name; i++)
- {
- if (!strcmp(directives[i].name, token.string))
- {
- return directives[i].func(source);
- } //end if
- } //end for
- } //end if
- SourceError(source, "unknown precompiler directive %s", token.string);
- return qfalse;
-} //end of the function PC_ReadDirective
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_DollarDirective_evalint(source_t *source)
-{
- signed long int value;
- token_t token;
-
- if (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse;
- //
- token.line = source->scriptstack->line;
- token.whitespace_p = source->scriptstack->script_p;
- token.endwhitespace_p = source->scriptstack->script_p;
- token.linescrossed = 0;
- sprintf(token.string, "%d", abs(value));
- token.type = TT_NUMBER;
- token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;
-#ifdef NUMBERVALUE
- token.intvalue = value;
- token.floatvalue = value;
-#endif //NUMBERVALUE
- PC_UnreadSourceToken(source, &token);
- if (value < 0) UnreadSignToken(source);
- return qtrue;
-} //end of the function PC_DollarDirective_evalint
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_DollarDirective_evalfloat(source_t *source)
-{
- double value;
- token_t token;
-
- if (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse;
- token.line = source->scriptstack->line;
- token.whitespace_p = source->scriptstack->script_p;
- token.endwhitespace_p = source->scriptstack->script_p;
- token.linescrossed = 0;
- sprintf(token.string, "%1.2f", fabs(value));
- token.type = TT_NUMBER;
- token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;
-#ifdef NUMBERVALUE
- token.intvalue = (unsigned long) value;
- token.floatvalue = value;
-#endif //NUMBERVALUE
- PC_UnreadSourceToken(source, &token);
- if (value < 0) UnreadSignToken(source);
- return qtrue;
-} //end of the function PC_DollarDirective_evalfloat
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-directive_t dollardirectives[20] =
-{
- {"evalint", PC_DollarDirective_evalint},
- {"evalfloat", PC_DollarDirective_evalfloat},
- {NULL, NULL}
-};
-
-int PC_ReadDollarDirective(source_t *source)
-{
- token_t token;
- int i;
-
- //read the directive name
- if (!PC_ReadSourceToken(source, &token))
- {
- SourceError(source, "found $ without name");
- return qfalse;
- } //end if
- //directive name must be on the same line
- if (token.linescrossed > 0)
- {
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "found $ at end of line");
- return qfalse;
- } //end if
- //if if is a name
- if (token.type == TT_NAME)
- {
- //find the precompiler directive
- for (i = 0; dollardirectives[i].name; i++)
- {
- if (!strcmp(dollardirectives[i].name, token.string))
- {
- return dollardirectives[i].func(source);
- } //end if
- } //end for
- } //end if
- PC_UnreadSourceToken(source, &token);
- SourceError(source, "unknown precompiler directive %s", token.string);
- return qfalse;
-} //end of the function PC_ReadDirective
-
-#ifdef QUAKEC
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int BuiltinFunction(source_t *source)
-{
- token_t token;
-
- if (!PC_ReadSourceToken(source, &token)) return qfalse;
- if (token.type == TT_NUMBER)
- {
- PC_UnreadSourceToken(source, &token);
- return qtrue;
- } //end if
- else
- {
- PC_UnreadSourceToken(source, &token);
- return qfalse;
- } //end else
-} //end of the function BuiltinFunction
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int QuakeCMacro(source_t *source)
-{
- int i;
- token_t token;
-
- if (!PC_ReadSourceToken(source, &token)) return qtrue;
- if (token.type != TT_NAME)
- {
- PC_UnreadSourceToken(source, &token);
- return qtrue;
- } //end if
- //find the precompiler directive
- for (i = 0; dollardirectives[i].name; i++)
- {
- if (!strcmp(dollardirectives[i].name, token.string))
- {
- PC_UnreadSourceToken(source, &token);
- return qfalse;
- } //end if
- } //end for
- PC_UnreadSourceToken(source, &token);
- return qtrue;
-} //end of the function QuakeCMacro
-#endif //QUAKEC
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ReadToken(source_t *source, token_t *token)
-{
- define_t *define;
-
- while(1)
- {
- if (!PC_ReadSourceToken(source, token)) return qfalse;
- //check for precompiler directives
- if (token->type == TT_PUNCTUATION && *token->string == '#')
- {
-#ifdef QUAKEC
- if (!BuiltinFunction(source))
-#endif //QUAKC
- {
- //read the precompiler directive
- if (!PC_ReadDirective(source)) return qfalse;
- continue;
- } //end if
- } //end if
- if (token->type == TT_PUNCTUATION && *token->string == '$')
- {
-#ifdef QUAKEC
- if (!QuakeCMacro(source))
-#endif //QUAKEC
- {
- //read the precompiler directive
- if (!PC_ReadDollarDirective(source)) return qfalse;
- continue;
- } //end if
- } //end if
- // recursively concatenate strings that are behind each other still resolving defines
- if (token->type == TT_STRING)
- {
- token_t newtoken;
- if (PC_ReadToken(source, &newtoken))
- {
- if (newtoken.type == TT_STRING)
- {
- token->string[strlen(token->string)-1] = '\0';
- if (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN)
- {
- SourceError(source, "string longer than MAX_TOKEN %d\n", MAX_TOKEN);
- return qfalse;
- }
- strcat(token->string, newtoken.string+1);
- }
- else
- {
- PC_UnreadToken(source, &newtoken);
- }
- }
- } //end if
- //if skipping source because of conditional compilation
- if (source->skip) continue;
- //if the token is a name
- if (token->type == TT_NAME)
- {
- //check if the name is a define macro
-#if DEFINEHASHING
- define = PC_FindHashedDefine(source->definehash, token->string);
-#else
- define = PC_FindDefine(source->defines, token->string);
-#endif //DEFINEHASHING
- //if it is a define macro
- if (define)
- {
- //expand the defined macro
- if (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse;
- continue;
- } //end if
- } //end if
- //copy token for unreading
- Com_Memcpy(&source->token, token, sizeof(token_t));
- //found a token
- return qtrue;
- } //end while
-} //end of the function PC_ReadToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ExpectTokenString(source_t *source, char *string)
-{
- token_t token;
-
- if (!PC_ReadToken(source, &token))
- {
- SourceError(source, "couldn't find expected %s", string);
- return qfalse;
- } //end if
-
- if (strcmp(token.string, string))
- {
- SourceError(source, "expected %s, found %s", string, token.string);
- return qfalse;
- } //end if
- return qtrue;
-} //end of the function PC_ExpectTokenString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token)
-{
- char str[MAX_TOKEN];
-
- if (!PC_ReadToken(source, token))
- {
- SourceError(source, "couldn't read expected token");
- return qfalse;
- } //end if
-
- if (token->type != type)
- {
- strcpy(str, "");
- if (type == TT_STRING) strcpy(str, "string");
- if (type == TT_LITERAL) strcpy(str, "literal");
- if (type == TT_NUMBER) strcpy(str, "number");
- if (type == TT_NAME) strcpy(str, "name");
- if (type == TT_PUNCTUATION) strcpy(str, "punctuation");
- SourceError(source, "expected a %s, found %s", str, token->string);
- return qfalse;
- } //end if
- if (token->type == TT_NUMBER)
- {
- if ((token->subtype & subtype) != subtype)
- {
- if (subtype & TT_DECIMAL) strcpy(str, "decimal");
- if (subtype & TT_HEX) strcpy(str, "hex");
- if (subtype & TT_OCTAL) strcpy(str, "octal");
- if (subtype & TT_BINARY) strcpy(str, "binary");
- if (subtype & TT_LONG) strcat(str, " long");
- if (subtype & TT_UNSIGNED) strcat(str, " unsigned");
- if (subtype & TT_FLOAT) strcat(str, " float");
- if (subtype & TT_INTEGER) strcat(str, " integer");
- SourceError(source, "expected %s, found %s", str, token->string);
- return qfalse;
- } //end if
- } //end if
- else if (token->type == TT_PUNCTUATION)
- {
- if (token->subtype != subtype)
- {
- SourceError(source, "found %s", token->string);
- return qfalse;
- } //end if
- } //end else if
- return qtrue;
-} //end of the function PC_ExpectTokenType
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ExpectAnyToken(source_t *source, token_t *token)
-{
- if (!PC_ReadToken(source, token))
- {
- SourceError(source, "couldn't read expected token");
- return qfalse;
- } //end if
- else
- {
- return qtrue;
- } //end else
-} //end of the function PC_ExpectAnyToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_CheckTokenString(source_t *source, char *string)
-{
- token_t tok;
-
- if (!PC_ReadToken(source, &tok)) return qfalse;
- //if the token is available
- if (!strcmp(tok.string, string)) return qtrue;
- //
- PC_UnreadSourceToken(source, &tok);
- return qfalse;
-} //end of the function PC_CheckTokenString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token)
-{
- token_t tok;
-
- if (!PC_ReadToken(source, &tok)) return qfalse;
- //if the type matches
- if (tok.type == type &&
- (tok.subtype & subtype) == subtype)
- {
- Com_Memcpy(token, &tok, sizeof(token_t));
- return qtrue;
- } //end if
- //
- PC_UnreadSourceToken(source, &tok);
- return qfalse;
-} //end of the function PC_CheckTokenType
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_SkipUntilString(source_t *source, char *string)
-{
- token_t token;
-
- while(PC_ReadToken(source, &token))
- {
- if (!strcmp(token.string, string)) return qtrue;
- } //end while
- return qfalse;
-} //end of the function PC_SkipUntilString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_UnreadLastToken(source_t *source)
-{
- PC_UnreadSourceToken(source, &source->token);
-} //end of the function PC_UnreadLastToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_UnreadToken(source_t *source, token_t *token)
-{
- PC_UnreadSourceToken(source, token);
-} //end of the function PC_UnreadToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_SetIncludePath(source_t *source, char *path)
-{
- strncpy(source->includepath, path, MAX_PATH);
- //add trailing path seperator
- if (source->includepath[strlen(source->includepath)-1] != '\\' &&
- source->includepath[strlen(source->includepath)-1] != '/')
- {
- strcat(source->includepath, PATHSEPERATOR_STR);
- } //end if
-} //end of the function PC_SetIncludePath
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_SetPunctuations(source_t *source, punctuation_t *p)
-{
- source->punctuations = p;
-} //end of the function PC_SetPunctuations
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-source_t *LoadSourceFile(const char *filename)
-{
- source_t *source;
- script_t *script;
-
- PC_InitTokenHeap();
-
- script = LoadScriptFile(filename);
- if (!script) return NULL;
-
- script->next = NULL;
-
- source = (source_t *) GetMemory(sizeof(source_t));
- Com_Memset(source, 0, sizeof(source_t));
-
- strncpy(source->filename, filename, MAX_PATH);
- source->scriptstack = script;
- source->tokens = NULL;
- source->defines = NULL;
- source->indentstack = NULL;
- source->skip = 0;
-
-#if DEFINEHASHING
- source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));
-#endif //DEFINEHASHING
- PC_AddGlobalDefinesToSource(source);
- return source;
-} //end of the function LoadSourceFile
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-source_t *LoadSourceMemory(char *ptr, int length, char *name)
-{
- source_t *source;
- script_t *script;
-
- PC_InitTokenHeap();
-
- script = LoadScriptMemory(ptr, length, name);
- if (!script) return NULL;
- script->next = NULL;
-
- source = (source_t *) GetMemory(sizeof(source_t));
- Com_Memset(source, 0, sizeof(source_t));
-
- strncpy(source->filename, name, MAX_PATH);
- source->scriptstack = script;
- source->tokens = NULL;
- source->defines = NULL;
- source->indentstack = NULL;
- source->skip = 0;
-
-#if DEFINEHASHING
- source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));
-#endif //DEFINEHASHING
- PC_AddGlobalDefinesToSource(source);
- return source;
-} //end of the function LoadSourceMemory
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void FreeSource(source_t *source)
-{
- script_t *script;
- token_t *token;
- define_t *define;
- indent_t *indent;
- int i;
-
- //PC_PrintDefineHashTable(source->definehash);
- //free all the scripts
- while(source->scriptstack)
- {
- script = source->scriptstack;
- source->scriptstack = source->scriptstack->next;
- FreeScript(script);
- } //end for
- //free all the tokens
- while(source->tokens)
- {
- token = source->tokens;
- source->tokens = source->tokens->next;
- PC_FreeToken(token);
- } //end for
-#if DEFINEHASHING
- for (i = 0; i < DEFINEHASHSIZE; i++)
- {
- while(source->definehash[i])
- {
- define = source->definehash[i];
- source->definehash[i] = source->definehash[i]->hashnext;
- PC_FreeDefine(define);
- } //end while
- } //end for
-#else //DEFINEHASHING
- //free all defines
- while(source->defines)
- {
- define = source->defines;
- source->defines = source->defines->next;
- PC_FreeDefine(define);
- } //end for
-#endif //DEFINEHASHING
- //free all indents
- while(source->indentstack)
- {
- indent = source->indentstack;
- source->indentstack = source->indentstack->next;
- FreeMemory(indent);
- } //end for
-#if DEFINEHASHING
- //
- if (source->definehash) FreeMemory(source->definehash);
-#endif //DEFINEHASHING
- //free the source itself
- FreeMemory(source);
-} //end of the function FreeSource
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-
-#define MAX_SOURCEFILES 64
-
-source_t *sourceFiles[MAX_SOURCEFILES];
-
-int PC_LoadSourceHandle(const char *filename)
-{
- source_t *source;
- int i;
-
- for (i = 1; i < MAX_SOURCEFILES; i++)
- {
- if (!sourceFiles[i])
- break;
- } //end for
- if (i >= MAX_SOURCEFILES)
- return 0;
- PS_SetBaseFolder("");
- source = LoadSourceFile(filename);
- if (!source)
- return 0;
- sourceFiles[i] = source;
- return i;
-} //end of the function PC_LoadSourceHandle
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_FreeSourceHandle(int handle)
-{
- if (handle < 1 || handle >= MAX_SOURCEFILES)
- return qfalse;
- if (!sourceFiles[handle])
- return qfalse;
-
- FreeSource(sourceFiles[handle]);
- sourceFiles[handle] = NULL;
- return qtrue;
-} //end of the function PC_FreeSourceHandle
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_ReadTokenHandle(int handle, pc_token_t *pc_token)
-{
- token_t token;
- int ret;
-
- if (handle < 1 || handle >= MAX_SOURCEFILES)
- return 0;
- if (!sourceFiles[handle])
- return 0;
-
- ret = PC_ReadToken(sourceFiles[handle], &token);
- strcpy(pc_token->string, token.string);
- pc_token->type = token.type;
- pc_token->subtype = token.subtype;
- pc_token->intvalue = token.intvalue;
- pc_token->floatvalue = token.floatvalue;
- if (pc_token->type == TT_STRING)
- StripDoubleQuotes(pc_token->string);
- return ret;
-} //end of the function PC_ReadTokenHandle
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PC_SourceFileAndLine(int handle, char *filename, int *line)
-{
- if (handle < 1 || handle >= MAX_SOURCEFILES)
- return qfalse;
- if (!sourceFiles[handle])
- return qfalse;
-
- strcpy(filename, sourceFiles[handle]->filename);
- if (sourceFiles[handle]->scriptstack)
- *line = sourceFiles[handle]->scriptstack->line;
- else
- *line = 0;
- return qtrue;
-} //end of the function PC_SourceFileAndLine
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_SetBaseFolder(char *path)
-{
- PS_SetBaseFolder(path);
-} //end of the function PC_SetBaseFolder
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PC_CheckOpenSourceHandles(void)
-{
- int i;
-
- for (i = 1; i < MAX_SOURCEFILES; i++)
- {
- if (sourceFiles[i])
- {
-#ifdef BOTLIB
- botimport.Print(PRT_ERROR, "file %s still open in precompiler\n", sourceFiles[i]->scriptstack->filename);
-#endif //BOTLIB
- } //end if
- } //end for
-} //end of the function PC_CheckOpenSourceHandles
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// + +/***************************************************************************** + * name: l_precomp.c + * + * desc: pre compiler + * + * $Archive: /MissionPack/code/botlib/l_precomp.c $ + * + *****************************************************************************/ + +//Notes: fix: PC_StringizeTokens + +//#define SCREWUP +//#define BOTLIB +//#define QUAKE +//#define QUAKEC +//#define MEQCC + +#ifdef SCREWUP +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" + +typedef enum {qfalse, qtrue} qboolean; +#endif //SCREWUP + +#ifdef BOTLIB +#include "../game/q_shared.h" +#include "../game/botlib.h" +#include "be_interface.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" +#endif //BOTLIB + +#ifdef MEQCC +#include "qcc.h" +#include "time.h" //time & ctime +#include "math.h" //fabs +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" + +#define qtrue true +#define qfalse false +#endif //MEQCC + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" +#include "l_precomp.h" + +#define qtrue true +#define qfalse false +#define Q_stricmp stricmp + +#endif //BSPC + +#if defined(QUAKE) && !defined(BSPC) +#include "l_utils.h" +#endif //QUAKE + +//#define DEBUG_EVAL + +#define MAX_DEFINEPARMS 128 + +#define DEFINEHASHING 1 + +//directive name with parse function +typedef struct directive_s +{ + char *name; + int (*func)(source_t *source); +} directive_t; + +#define DEFINEHASHSIZE 1024 + +#define TOKEN_HEAP_SIZE 4096 + +int numtokens; +/* +int tokenheapinitialized; //true when the token heap is initialized +token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens +token_t *freetokens; //free tokens from the heap +*/ + +//list with global defines added to every source loaded +define_t *globaldefines; + +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void QDECL SourceError(source_t *source, char *str, ...) +{ + char text[1024]; + va_list ap; + + va_start(ap, str); + vsprintf(text, str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BSPC +} //end of the function SourceError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL SourceWarning(source_t *source, char *str, ...) +{ + char text[1024]; + va_list ap; + + va_start(ap, str); + vsprintf(text, str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); +#endif //BSPC +} //end of the function ScriptWarning +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PushIndent(source_t *source, int type, int skip) +{ + indent_t *indent; + + indent = (indent_t *) GetMemory(sizeof(indent_t)); + indent->type = type; + indent->script = source->scriptstack; + indent->skip = (skip != 0); + source->skip += indent->skip; + indent->next = source->indentstack; + source->indentstack = indent; +} //end of the function PC_PushIndent +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PopIndent(source_t *source, int *type, int *skip) +{ + indent_t *indent; + + *type = 0; + *skip = 0; + + indent = source->indentstack; + if (!indent) return; + + //must be an indent from the current script + if (source->indentstack->script != source->scriptstack) return; + + *type = indent->type; + *skip = indent->skip; + source->indentstack = source->indentstack->next; + source->skip -= indent->skip; + FreeMemory(indent); +} //end of the function PC_PopIndent +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PushScript(source_t *source, script_t *script) +{ + script_t *s; + + for (s = source->scriptstack; s; s = s->next) + { + if (!Q_stricmp(s->filename, script->filename)) + { + SourceError(source, "%s recursively included", script->filename); + return; + } //end if + } //end for + //push the script on the script stack + script->next = source->scriptstack; + source->scriptstack = script; +} //end of the function PC_PushScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_InitTokenHeap(void) +{ + /* + int i; + + if (tokenheapinitialized) return; + freetokens = NULL; + for (i = 0; i < TOKEN_HEAP_SIZE; i++) + { + token_heap[i].next = freetokens; + freetokens = &token_heap[i]; + } //end for + tokenheapinitialized = qtrue; + */ +} //end of the function PC_InitTokenHeap +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +token_t *PC_CopyToken(token_t *token) +{ + token_t *t; + +// t = (token_t *) malloc(sizeof(token_t)); + t = (token_t *) GetMemory(sizeof(token_t)); +// t = freetokens; + if (!t) + { +#ifdef BSPC + Error("out of token space\n"); +#else + Com_Error(ERR_FATAL, "out of token space\n"); +#endif + return NULL; + } //end if +// freetokens = freetokens->next; + Com_Memcpy(t, token, sizeof(token_t)); + t->next = NULL; + numtokens++; + return t; +} //end of the function PC_CopyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeToken(token_t *token) +{ + //free(token); + FreeMemory(token); +// token->next = freetokens; +// freetokens = token; + numtokens--; +} //end of the function PC_FreeToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadSourceToken(source_t *source, token_t *token) +{ + token_t *t; + script_t *script; + int type, skip; + + //if there's no token already available + while(!source->tokens) + { + //if there's a token to read from the script + if (PS_ReadToken(source->scriptstack, token)) return qtrue; + //if at the end of the script + if (EndOfScript(source->scriptstack)) + { + //remove all indents of the script + while(source->indentstack && + source->indentstack->script == source->scriptstack) + { + SourceWarning(source, "missing #endif"); + PC_PopIndent(source, &type, &skip); + } //end if + } //end if + //if this was the initial script + if (!source->scriptstack->next) return qfalse; + //remove the script and return to the last one + script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + FreeScript(script); + } //end while + //copy the already available token + Com_Memcpy(token, source->tokens, sizeof(token_t)); + //free the read token + t = source->tokens; + source->tokens = source->tokens->next; + PC_FreeToken(t); + return qtrue; +} //end of the function PC_ReadSourceToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_UnreadSourceToken(source_t *source, token_t *token) +{ + token_t *t; + + t = PC_CopyToken(token); + t->next = source->tokens; + source->tokens = t; + return qtrue; +} //end of the function PC_UnreadSourceToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms) +{ + token_t token, *t, *last; + int i, done, lastcomma, numparms, indent; + + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "define %s missing parms", define->name); + return qfalse; + } //end if + // + if (define->numparms > maxparms) + { + SourceError(source, "define with more than %d parameters", maxparms); + return qfalse; + } //end if + // + for (i = 0; i < define->numparms; i++) parms[i] = NULL; + //if no leading "(" + if (strcmp(token.string, "(")) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "define %s missing parms", define->name); + return qfalse; + } //end if + //read the define parameters + for (done = 0, numparms = 0, indent = 0; !done;) + { + if (numparms >= maxparms) + { + SourceError(source, "define %s with too many parms", define->name); + return qfalse; + } //end if + if (numparms >= define->numparms) + { + SourceWarning(source, "define %s has too many parms", define->name); + return qfalse; + } //end if + parms[numparms] = NULL; + lastcomma = 1; + last = NULL; + while(!done) + { + // + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "define %s incomplete", define->name); + return qfalse; + } //end if + // + if (!strcmp(token.string, ",")) + { + if (indent <= 0) + { + if (lastcomma) SourceWarning(source, "too many comma's"); + lastcomma = 1; + break; + } //end if + } //end if + lastcomma = 0; + // + if (!strcmp(token.string, "(")) + { + indent++; + continue; + } //end if + else if (!strcmp(token.string, ")")) + { + if (--indent <= 0) + { + if (!parms[define->numparms-1]) + { + SourceWarning(source, "too few define parms"); + } //end if + done = 1; + break; + } //end if + } //end if + // + if (numparms < define->numparms) + { + // + t = PC_CopyToken(&token); + t->next = NULL; + if (last) last->next = t; + else parms[numparms] = t; + last = t; + } //end if + } //end while + numparms++; + } //end for + return qtrue; +} //end of the function PC_ReadDefineParms +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_StringizeTokens(token_t *tokens, token_t *token) +{ + token_t *t; + + token->type = TT_STRING; + token->whitespace_p = NULL; + token->endwhitespace_p = NULL; + token->string[0] = '\0'; + strcat(token->string, "\""); + for (t = tokens; t; t = t->next) + { + strncat(token->string, t->string, MAX_TOKEN - strlen(token->string)); + } //end for + strncat(token->string, "\"", MAX_TOKEN - strlen(token->string)); + return qtrue; +} //end of the function PC_StringizeTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_MergeTokens(token_t *t1, token_t *t2) +{ + //merging of a name with a name or number + if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER)) + { + strcat(t1->string, t2->string); + return qtrue; + } //end if + //merging of two strings + if (t1->type == TT_STRING && t2->type == TT_STRING) + { + //remove trailing double quote + t1->string[strlen(t1->string)-1] = '\0'; + //concat without leading double quote + strcat(t1->string, &t2->string[1]); + return qtrue; + } //end if + //FIXME: merging of two number of the same sub type + return qfalse; +} //end of the function PC_MergeTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +/* +void PC_PrintDefine(define_t *define) +{ + printf("define->name = %s\n", define->name); + printf("define->flags = %d\n", define->flags); + printf("define->builtin = %d\n", define->builtin); + printf("define->numparms = %d\n", define->numparms); +// token_t *parms; //define parameters +// token_t *tokens; //macro tokens (possibly containing parm tokens) +// struct define_s *next; //next defined macro in a list +} //end of the function PC_PrintDefine*/ +#if DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PrintDefineHashTable(define_t **definehash) +{ + int i; + define_t *d; + + for (i = 0; i < DEFINEHASHSIZE; i++) + { + Log_Write("%4d:", i); + for (d = definehash[i]; d; d = d->hashnext) + { + Log_Write(" %s", d->name); + } //end for + Log_Write("\n"); + } //end for +} //end of the function PC_PrintDefineHashTable +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47}; + +int PC_NameHash(char *name) +{ + int register hash, i; + + hash = 0; + for (i = 0; name[i] != '\0'; i++) + { + hash += name[i] * (119 + i); + //hash += (name[i] << 7) + i; + //hash += (name[i] << (i&15)); + } //end while + hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1); + return hash; +} //end of the function PC_NameHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddDefineToHash(define_t *define, define_t **definehash) +{ + int hash; + + hash = PC_NameHash(define->name); + define->hashnext = definehash[hash]; + definehash[hash] = define; +} //end of the function PC_AddDefineToHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindHashedDefine(define_t **definehash, char *name) +{ + define_t *d; + int hash; + + hash = PC_NameHash(name); + for (d = definehash[hash]; d; d = d->hashnext) + { + if (!strcmp(d->name, name)) return d; + } //end for + return NULL; +} //end of the function PC_FindHashedDefine +#endif //DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindDefine(define_t *defines, char *name) +{ + define_t *d; + + for (d = defines; d; d = d->next) + { + if (!strcmp(d->name, name)) return d; + } //end for + return NULL; +} //end of the function PC_FindDefine +//============================================================================ +// +// Parameter: - +// Returns: number of the parm +// if no parm found with the given name -1 is returned +// Changes Globals: - +//============================================================================ +int PC_FindDefineParm(define_t *define, char *name) +{ + token_t *p; + int i; + + i = 0; + for (p = define->parms; p; p = p->next) + { + if (!strcmp(p->string, name)) return i; + i++; + } //end for + return -1; +} //end of the function PC_FindDefineParm +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeDefine(define_t *define) +{ + token_t *t, *next; + + //free the define parameters + for (t = define->parms; t; t = next) + { + next = t->next; + PC_FreeToken(t); + } //end for + //free the define tokens + for (t = define->tokens; t; t = next) + { + next = t->next; + PC_FreeToken(t); + } //end for + //free the define + FreeMemory(define); +} //end of the function PC_FreeDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddBuiltinDefines(source_t *source) +{ + int i; + define_t *define; + struct builtin + { + char *string; + int builtin; + } builtin[] = { // bk001204 - brackets + { "__LINE__", BUILTIN_LINE }, + { "__FILE__", BUILTIN_FILE }, + { "__DATE__", BUILTIN_DATE }, + { "__TIME__", BUILTIN_TIME }, +// { "__STDC__", BUILTIN_STDC }, + { NULL, 0 } + }; + + for (i = 0; builtin[i].string; i++) + { + define = (define_t *) GetMemory(sizeof(define_t) + strlen(builtin[i].string) + 1); + Com_Memset(define, 0, sizeof(define_t)); + define->name = (char *) define + sizeof(define_t); + strcpy(define->name, builtin[i].string); + define->flags |= DEFINE_FIXED; + define->builtin = builtin[i].builtin; + //add the define to the source +#if DEFINEHASHING + PC_AddDefineToHash(define, source->definehash); +#else + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + } //end for +} //end of the function PC_AddBuiltinDefines +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define, + token_t **firsttoken, token_t **lasttoken) +{ + token_t *token; + unsigned long t; // time_t t; //to prevent LCC warning + char *curtime; + + token = PC_CopyToken(deftoken); + switch(define->builtin) + { + case BUILTIN_LINE: + { + sprintf(token->string, "%d", deftoken->line); +#ifdef NUMBERVALUE + token->intvalue = deftoken->line; + token->floatvalue = deftoken->line; +#endif //NUMBERVALUE + token->type = TT_NUMBER; + token->subtype = TT_DECIMAL | TT_INTEGER; + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_FILE: + { + strcpy(token->string, source->scriptstack->filename); + token->type = TT_NAME; + token->subtype = strlen(token->string); + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_DATE: + { + t = time(NULL); + curtime = ctime(&t); + strcpy(token->string, "\""); + strncat(token->string, curtime+4, 7); + strncat(token->string+7, curtime+20, 4); + strcat(token->string, "\""); + free(curtime); + token->type = TT_NAME; + token->subtype = strlen(token->string); + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_TIME: + { + t = time(NULL); + curtime = ctime(&t); + strcpy(token->string, "\""); + strncat(token->string, curtime+11, 8); + strcat(token->string, "\""); + free(curtime); + token->type = TT_NAME; + token->subtype = strlen(token->string); + *firsttoken = token; + *lasttoken = token; + break; + } //end case + case BUILTIN_STDC: + default: + { + *firsttoken = NULL; + *lasttoken = NULL; + break; + } //end case + } //end switch + return qtrue; +} //end of the function PC_ExpandBuiltinDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define, + token_t **firsttoken, token_t **lasttoken) +{ + token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t; + token_t *t1, *t2, *first, *last, *nextpt, token; + int parmnum, i; + + //if it is a builtin define + if (define->builtin) + { + return PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken); + } //end if + //if the define has parameters + if (define->numparms) + { + if (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse; +#ifdef DEBUG_EVAL + for (i = 0; i < define->numparms; i++) + { + Log_Write("define parms %d:", i); + for (pt = parms[i]; pt; pt = pt->next) + { + Log_Write("%s", pt->string); + } //end for + } //end for +#endif //DEBUG_EVAL + } //end if + //empty list at first + first = NULL; + last = NULL; + //create a list with tokens of the expanded define + for (dt = define->tokens; dt; dt = dt->next) + { + parmnum = -1; + //if the token is a name, it could be a define parameter + if (dt->type == TT_NAME) + { + parmnum = PC_FindDefineParm(define, dt->string); + } //end if + //if it is a define parameter + if (parmnum >= 0) + { + for (pt = parms[parmnum]; pt; pt = pt->next) + { + t = PC_CopyToken(pt); + //add the token to the list + t->next = NULL; + if (last) last->next = t; + else first = t; + last = t; + } //end for + } //end if + else + { + //if stringizing operator + if (dt->string[0] == '#' && dt->string[1] == '\0') + { + //the stringizing operator must be followed by a define parameter + if (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string); + else parmnum = -1; + // + if (parmnum >= 0) + { + //step over the stringizing operator + dt = dt->next; + //stringize the define parameter tokens + if (!PC_StringizeTokens(parms[parmnum], &token)) + { + SourceError(source, "can't stringize tokens"); + return qfalse; + } //end if + t = PC_CopyToken(&token); + } //end if + else + { + SourceWarning(source, "stringizing operator without define parameter"); + continue; + } //end if + } //end if + else + { + t = PC_CopyToken(dt); + } //end else + //add the token to the list + t->next = NULL; + if (last) last->next = t; + else first = t; + last = t; + } //end else + } //end for + //check for the merging operator + for (t = first; t; ) + { + if (t->next) + { + //if the merging operator + if (t->next->string[0] == '#' && t->next->string[1] == '#') + { + t1 = t; + t2 = t->next->next; + if (t2) + { + if (!PC_MergeTokens(t1, t2)) + { + SourceError(source, "can't merge %s with %s", t1->string, t2->string); + return qfalse; + } //end if + PC_FreeToken(t1->next); + t1->next = t2->next; + if (t2 == last) last = t1; + PC_FreeToken(t2); + continue; + } //end if + } //end if + } //end if + t = t->next; + } //end for + //store the first and last token of the list + *firsttoken = first; + *lasttoken = last; + //free all the parameter tokens + for (i = 0; i < define->numparms; i++) + { + for (pt = parms[i]; pt; pt = nextpt) + { + nextpt = pt->next; + PC_FreeToken(pt); + } //end for + } //end for + // + return qtrue; +} //end of the function PC_ExpandDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define) +{ + token_t *firsttoken, *lasttoken; + + if (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse; + + if (firsttoken && lasttoken) + { + lasttoken->next = source->tokens; + source->tokens = firsttoken; + return qtrue; + } //end if + return qfalse; +} //end of the function PC_ExpandDefineIntoSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_ConvertPath(char *path) +{ + char *ptr; + + //remove double path seperators + for (ptr = path; *ptr;) + { + if ((*ptr == '\\' || *ptr == '/') && + (*(ptr+1) == '\\' || *(ptr+1) == '/')) + { + strcpy(ptr, ptr+1); + } //end if + else + { + ptr++; + } //end else + } //end while + //set OS dependent path seperators + for (ptr = path; *ptr;) + { + if (*ptr == '/' || *ptr == '\\') *ptr = PATHSEPERATOR_CHAR; + ptr++; + } //end while +} //end of the function PC_ConvertPath +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_include(source_t *source) +{ + script_t *script; + token_t token; + char path[MAX_PATH]; +#ifdef QUAKE + foundfile_t file; +#endif //QUAKE + + if (source->skip > 0) return qtrue; + // + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "#include without file name"); + return qfalse; + } //end if + if (token.linescrossed > 0) + { + SourceError(source, "#include without file name"); + return qfalse; + } //end if + if (token.type == TT_STRING) + { + StripDoubleQuotes(token.string); + PC_ConvertPath(token.string); + script = LoadScriptFile(token.string); + if (!script) + { + strcpy(path, source->includepath); + strcat(path, token.string); + script = LoadScriptFile(path); + } //end if + } //end if + else if (token.type == TT_PUNCTUATION && *token.string == '<') + { + strcpy(path, source->includepath); + while(PC_ReadSourceToken(source, &token)) + { + if (token.linescrossed > 0) + { + PC_UnreadSourceToken(source, &token); + break; + } //end if + if (token.type == TT_PUNCTUATION && *token.string == '>') break; + strncat(path, token.string, MAX_PATH); + } //end while + if (*token.string != '>') + { + SourceWarning(source, "#include missing trailing >"); + } //end if + if (!strlen(path)) + { + SourceError(source, "#include without file name between < >"); + return qfalse; + } //end if + PC_ConvertPath(path); + script = LoadScriptFile(path); + } //end if + else + { + SourceError(source, "#include without file name"); + return qfalse; + } //end else +#ifdef QUAKE + if (!script) + { + Com_Memset(&file, 0, sizeof(foundfile_t)); + script = LoadScriptFile(path); + if (script) strncpy(script->filename, path, MAX_PATH); + } //end if +#endif //QUAKE + if (!script) + { +#ifdef SCREWUP + SourceWarning(source, "file %s not found", path); + return qtrue; +#else + SourceError(source, "file %s not found", path); + return qfalse; +#endif //SCREWUP + } //end if + PC_PushScript(source, script); + return qtrue; +} //end of the function PC_Directive_include +//============================================================================ +// reads a token from the current line, continues reading on the next +// line only if a backslash '\' is encountered. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadLine(source_t *source, token_t *token) +{ + int crossline; + + crossline = 0; + do + { + if (!PC_ReadSourceToken(source, token)) return qfalse; + + if (token->linescrossed > crossline) + { + PC_UnreadSourceToken(source, token); + return qfalse; + } //end if + crossline = 1; + } while(!strcmp(token->string, "\\")); + return qtrue; +} //end of the function PC_ReadLine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_WhiteSpaceBeforeToken(token_t *token) +{ + return token->endwhitespace_p - token->whitespace_p > 0; +} //end of the function PC_WhiteSpaceBeforeToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_ClearTokenWhiteSpace(token_t *token) +{ + token->whitespace_p = NULL; + token->endwhitespace_p = NULL; + token->linescrossed = 0; +} //end of the function PC_ClearTokenWhiteSpace +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_undef(source_t *source) +{ + token_t token; + define_t *define, *lastdefine; + int hash; + + if (source->skip > 0) return qtrue; + // + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "undef without name"); + return qfalse; + } //end if + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "expected name, found %s", token.string); + return qfalse; + } //end if +#if DEFINEHASHING + + hash = PC_NameHash(token.string); + for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext) + { + if (!strcmp(define->name, token.string)) + { + if (define->flags & DEFINE_FIXED) + { + SourceWarning(source, "can't undef %s", token.string); + } //end if + else + { + if (lastdefine) lastdefine->hashnext = define->hashnext; + else source->definehash[hash] = define->hashnext; + PC_FreeDefine(define); + } //end else + break; + } //end if + lastdefine = define; + } //end for +#else //DEFINEHASHING + for (lastdefine = NULL, define = source->defines; define; define = define->next) + { + if (!strcmp(define->name, token.string)) + { + if (define->flags & DEFINE_FIXED) + { + SourceWarning(source, "can't undef %s", token.string); + } //end if + else + { + if (lastdefine) lastdefine->next = define->next; + else source->defines = define->next; + PC_FreeDefine(define); + } //end else + break; + } //end if + lastdefine = define; + } //end for +#endif //DEFINEHASHING + return qtrue; +} //end of the function PC_Directive_undef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_define(source_t *source) +{ + token_t token, *t, *last; + define_t *define; + + if (source->skip > 0) return qtrue; + // + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "#define without name"); + return qfalse; + } //end if + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "expected name after #define, found %s", token.string); + return qfalse; + } //end if + //check if the define already exists +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + if (define) + { + if (define->flags & DEFINE_FIXED) + { + SourceError(source, "can't redefine %s", token.string); + return qfalse; + } //end if + SourceWarning(source, "redefinition of %s", token.string); + //unread the define name before executing the #undef directive + PC_UnreadSourceToken(source, &token); + if (!PC_Directive_undef(source)) return qfalse; + //if the define was not removed (define->flags & DEFINE_FIXED) +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + } //end if + //allocate define + define = (define_t *) GetMemory(sizeof(define_t) + strlen(token.string) + 1); + Com_Memset(define, 0, sizeof(define_t)); + define->name = (char *) define + sizeof(define_t); + strcpy(define->name, token.string); + //add the define to the source +#if DEFINEHASHING + PC_AddDefineToHash(define, source->definehash); +#else //DEFINEHASHING + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + //if nothing is defined, just return + if (!PC_ReadLine(source, &token)) return qtrue; + //if it is a define with parameters + if (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "(")) + { + //read the define parameters + last = NULL; + if (!PC_CheckTokenString(source, ")")) + { + while(1) + { + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "expected define parameter"); + return qfalse; + } //end if + //if it isn't a name + if (token.type != TT_NAME) + { + SourceError(source, "invalid define parameter"); + return qfalse; + } //end if + // + if (PC_FindDefineParm(define, token.string) >= 0) + { + SourceError(source, "two the same define parameters"); + return qfalse; + } //end if + //add the define parm + t = PC_CopyToken(&token); + PC_ClearTokenWhiteSpace(t); + t->next = NULL; + if (last) last->next = t; + else define->parms = t; + last = t; + define->numparms++; + //read next token + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "define parameters not terminated"); + return qfalse; + } //end if + // + if (!strcmp(token.string, ")")) break; + //then it must be a comma + if (strcmp(token.string, ",")) + { + SourceError(source, "define not terminated"); + return qfalse; + } //end if + } //end while + } //end if + if (!PC_ReadLine(source, &token)) return qtrue; + } //end if + //read the defined stuff + last = NULL; + do + { + t = PC_CopyToken(&token); + if (t->type == TT_NAME && !strcmp(t->string, define->name)) + { + SourceError(source, "recursive define (removed recursion)"); + continue; + } //end if + PC_ClearTokenWhiteSpace(t); + t->next = NULL; + if (last) last->next = t; + else define->tokens = t; + last = t; + } while(PC_ReadLine(source, &token)); + // + if (last) + { + //check for merge operators at the beginning or end + if (!strcmp(define->tokens->string, "##") || + !strcmp(last->string, "##")) + { + SourceError(source, "define with misplaced ##"); + return qfalse; + } //end if + } //end if + return qtrue; +} //end of the function PC_Directive_define +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_DefineFromString(char *string) +{ + script_t *script; + source_t src; + token_t *t; + int res, i; + define_t *def; + + PC_InitTokenHeap(); + + script = LoadScriptMemory(string, strlen(string), "*extern"); + //create a new source + Com_Memset(&src, 0, sizeof(source_t)); + strncpy(src.filename, "*extern", MAX_PATH); + src.scriptstack = script; +#if DEFINEHASHING + src.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); +#endif //DEFINEHASHING + //create a define from the source + res = PC_Directive_define(&src); + //free any tokens if left + for (t = src.tokens; t; t = src.tokens) + { + src.tokens = src.tokens->next; + PC_FreeToken(t); + } //end for +#ifdef DEFINEHASHING + def = NULL; + for (i = 0; i < DEFINEHASHSIZE; i++) + { + if (src.definehash[i]) + { + def = src.definehash[i]; + break; + } //end if + } //end for +#else + def = src.defines; +#endif //DEFINEHASHING + // +#if DEFINEHASHING + FreeMemory(src.definehash); +#endif //DEFINEHASHING + // + FreeScript(script); + //if the define was created succesfully + if (res > 0) return def; + //free the define is created + if (src.defines) PC_FreeDefine(def); + // + return NULL; +} //end of the function PC_DefineFromString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_AddDefine(source_t *source, char *string) +{ + define_t *define; + + define = PC_DefineFromString(string); + if (!define) return qfalse; +#if DEFINEHASHING + PC_AddDefineToHash(define, source->definehash); +#else //DEFINEHASHING + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + return qtrue; +} //end of the function PC_AddDefine +//============================================================================ +// add a globals define that will be added to all opened sources +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_AddGlobalDefine(char *string) +{ + define_t *define; + + define = PC_DefineFromString(string); + if (!define) return qfalse; + define->next = globaldefines; + globaldefines = define; + return qtrue; +} //end of the function PC_AddGlobalDefine +//============================================================================ +// remove the given global define +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_RemoveGlobalDefine(char *name) +{ + define_t *define; + + define = PC_FindDefine(globaldefines, name); + if (define) + { + PC_FreeDefine(define); + return qtrue; + } //end if + return qfalse; +} //end of the function PC_RemoveGlobalDefine +//============================================================================ +// remove all globals defines +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_RemoveAllGlobalDefines(void) +{ + define_t *define; + + for (define = globaldefines; define; define = globaldefines) + { + globaldefines = globaldefines->next; + PC_FreeDefine(define); + } //end for +} //end of the function PC_RemoveAllGlobalDefines +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_CopyDefine(source_t *source, define_t *define) +{ + define_t *newdefine; + token_t *token, *newtoken, *lasttoken; + + newdefine = (define_t *) GetMemory(sizeof(define_t) + strlen(define->name) + 1); + //copy the define name + newdefine->name = (char *) newdefine + sizeof(define_t); + strcpy(newdefine->name, define->name); + newdefine->flags = define->flags; + newdefine->builtin = define->builtin; + newdefine->numparms = define->numparms; + //the define is not linked + newdefine->next = NULL; + newdefine->hashnext = NULL; + //copy the define tokens + newdefine->tokens = NULL; + for (lasttoken = NULL, token = define->tokens; token; token = token->next) + { + newtoken = PC_CopyToken(token); + newtoken->next = NULL; + if (lasttoken) lasttoken->next = newtoken; + else newdefine->tokens = newtoken; + lasttoken = newtoken; + } //end for + //copy the define parameters + newdefine->parms = NULL; + for (lasttoken = NULL, token = define->parms; token; token = token->next) + { + newtoken = PC_CopyToken(token); + newtoken->next = NULL; + if (lasttoken) lasttoken->next = newtoken; + else newdefine->parms = newtoken; + lasttoken = newtoken; + } //end for + return newdefine; +} //end of the function PC_CopyDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddGlobalDefinesToSource(source_t *source) +{ + define_t *define, *newdefine; + + for (define = globaldefines; define; define = define->next) + { + newdefine = PC_CopyDefine(source, define); +#if DEFINEHASHING + PC_AddDefineToHash(newdefine, source->definehash); +#else //DEFINEHASHING + newdefine->next = source->defines; + source->defines = newdefine; +#endif //DEFINEHASHING + } //end for +} //end of the function PC_AddGlobalDefinesToSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_if_def(source_t *source, int type) +{ + token_t token; + define_t *d; + int skip; + + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "#ifdef without name"); + return qfalse; + } //end if + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "expected name after #ifdef, found %s", token.string); + return qfalse; + } //end if +#if DEFINEHASHING + d = PC_FindHashedDefine(source->definehash, token.string); +#else + d = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + skip = (type == INDENT_IFDEF) == (d == NULL); + PC_PushIndent(source, type, skip); + return qtrue; +} //end of the function PC_Directiveif_def +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_ifdef(source_t *source) +{ + return PC_Directive_if_def(source, INDENT_IFDEF); +} //end of the function PC_Directive_ifdef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_ifndef(source_t *source) +{ + return PC_Directive_if_def(source, INDENT_IFNDEF); +} //end of the function PC_Directive_ifndef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_else(source_t *source) +{ + int type, skip; + + PC_PopIndent(source, &type, &skip); + if (!type) + { + SourceError(source, "misplaced #else"); + return qfalse; + } //end if + if (type == INDENT_ELSE) + { + SourceError(source, "#else after #else"); + return qfalse; + } //end if + PC_PushIndent(source, INDENT_ELSE, !skip); + return qtrue; +} //end of the function PC_Directive_else +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_endif(source_t *source) +{ + int type, skip; + + PC_PopIndent(source, &type, &skip); + if (!type) + { + SourceError(source, "misplaced #endif"); + return qfalse; + } //end if + return qtrue; +} //end of the function PC_Directive_endif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +typedef struct operator_s +{ + int operator; + int priority; + int parentheses; + struct operator_s *prev, *next; +} operator_t; + +typedef struct value_s +{ + signed long int intvalue; + double floatvalue; + int parentheses; + struct value_s *prev, *next; +} value_t; + +int PC_OperatorPriority(int op) +{ + switch(op) + { + case P_MUL: return 15; + case P_DIV: return 15; + case P_MOD: return 15; + case P_ADD: return 14; + case P_SUB: return 14; + + case P_LOGIC_AND: return 7; + case P_LOGIC_OR: return 6; + case P_LOGIC_GEQ: return 12; + case P_LOGIC_LEQ: return 12; + case P_LOGIC_EQ: return 11; + case P_LOGIC_UNEQ: return 11; + + case P_LOGIC_NOT: return 16; + case P_LOGIC_GREATER: return 12; + case P_LOGIC_LESS: return 12; + + case P_RSHIFT: return 13; + case P_LSHIFT: return 13; + + case P_BIN_AND: return 10; + case P_BIN_OR: return 8; + case P_BIN_XOR: return 9; + case P_BIN_NOT: return 16; + + case P_COLON: return 5; + case P_QUESTIONMARK: return 5; + } //end switch + return qfalse; +} //end of the function PC_OperatorPriority + +//#define AllocValue() GetClearedMemory(sizeof(value_t)); +//#define FreeValue(val) FreeMemory(val) +//#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t)); +//#define FreeOperator(op) FreeMemory(op); + +#define MAX_VALUES 64 +#define MAX_OPERATORS 64 +#define AllocValue(val) \ + if (numvalues >= MAX_VALUES) { \ + SourceError(source, "out of value space\n"); \ + error = 1; \ + break; \ + } \ + else \ + val = &value_heap[numvalues++]; +#define FreeValue(val) +// +#define AllocOperator(op) \ + if (numoperators >= MAX_OPERATORS) { \ + SourceError(source, "out of operator space\n"); \ + error = 1; \ + break; \ + } \ + else \ + op = &operator_heap[numoperators++]; +#define FreeOperator(op) + +int PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue, + double *floatvalue, int integer) +{ + operator_t *o, *firstoperator, *lastoperator; + value_t *v, *firstvalue, *lastvalue, *v1, *v2; + token_t *t; + int brace = 0; + int parentheses = 0; + int error = 0; + int lastwasvalue = 0; + int negativevalue = 0; + int questmarkintvalue = 0; + double questmarkfloatvalue = 0; + int gotquestmarkvalue = qfalse; + int lastoperatortype = 0; + // + operator_t operator_heap[MAX_OPERATORS]; + int numoperators = 0; + value_t value_heap[MAX_VALUES]; + int numvalues = 0; + + firstoperator = lastoperator = NULL; + firstvalue = lastvalue = NULL; + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + for (t = tokens; t; t = t->next) + { + switch(t->type) + { + case TT_NAME: + { + if (lastwasvalue || negativevalue) + { + SourceError(source, "syntax error in #if/#elif"); + error = 1; + break; + } //end if + if (strcmp(t->string, "defined")) + { + SourceError(source, "undefined name %s in #if/#elif", t->string); + error = 1; + break; + } //end if + t = t->next; + if (!strcmp(t->string, "(")) + { + brace = qtrue; + t = t->next; + } //end if + if (!t || t->type != TT_NAME) + { + SourceError(source, "defined without name in #if/#elif"); + error = 1; + break; + } //end if + //v = (value_t *) GetClearedMemory(sizeof(value_t)); + AllocValue(v); +#if DEFINEHASHING + if (PC_FindHashedDefine(source->definehash, t->string)) +#else + if (PC_FindDefine(source->defines, t->string)) +#endif //DEFINEHASHING + { + v->intvalue = 1; + v->floatvalue = 1; + } //end if + else + { + v->intvalue = 0; + v->floatvalue = 0; + } //end else + v->parentheses = parentheses; + v->next = NULL; + v->prev = lastvalue; + if (lastvalue) lastvalue->next = v; + else firstvalue = v; + lastvalue = v; + if (brace) + { + t = t->next; + if (!t || strcmp(t->string, ")")) + { + SourceError(source, "defined without ) in #if/#elif"); + error = 1; + break; + } //end if + } //end if + brace = qfalse; + // defined() creates a value + lastwasvalue = 1; + break; + } //end case + case TT_NUMBER: + { + if (lastwasvalue) + { + SourceError(source, "syntax error in #if/#elif"); + error = 1; + break; + } //end if + //v = (value_t *) GetClearedMemory(sizeof(value_t)); + AllocValue(v); + if (negativevalue) + { + v->intvalue = - (signed int) t->intvalue; + v->floatvalue = - t->floatvalue; + } //end if + else + { + v->intvalue = t->intvalue; + v->floatvalue = t->floatvalue; + } //end else + v->parentheses = parentheses; + v->next = NULL; + v->prev = lastvalue; + if (lastvalue) lastvalue->next = v; + else firstvalue = v; + lastvalue = v; + //last token was a value + lastwasvalue = 1; + // + negativevalue = 0; + break; + } //end case + case TT_PUNCTUATION: + { + if (negativevalue) + { + SourceError(source, "misplaced minus sign in #if/#elif"); + error = 1; + break; + } //end if + if (t->subtype == P_PARENTHESESOPEN) + { + parentheses++; + break; + } //end if + else if (t->subtype == P_PARENTHESESCLOSE) + { + parentheses--; + if (parentheses < 0) + { + SourceError(source, "too many ) in #if/#elsif"); + error = 1; + } //end if + break; + } //end else if + //check for invalid operators on floating point values + if (!integer) + { + if (t->subtype == P_BIN_NOT || t->subtype == P_MOD || + t->subtype == P_RSHIFT || t->subtype == P_LSHIFT || + t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || + t->subtype == P_BIN_XOR) + { + SourceError(source, "illigal operator %s on floating point operands\n", t->string); + error = 1; + break; + } //end if + } //end if + switch(t->subtype) + { + case P_LOGIC_NOT: + case P_BIN_NOT: + { + if (lastwasvalue) + { + SourceError(source, "! or ~ after value in #if/#elif"); + error = 1; + break; + } //end if + break; + } //end case + case P_INC: + case P_DEC: + { + SourceError(source, "++ or -- used in #if/#elif"); + break; + } //end case + case P_SUB: + { + if (!lastwasvalue) + { + negativevalue = 1; + break; + } //end if + } //end case + + case P_MUL: + case P_DIV: + case P_MOD: + case P_ADD: + + case P_LOGIC_AND: + case P_LOGIC_OR: + case P_LOGIC_GEQ: + case P_LOGIC_LEQ: + case P_LOGIC_EQ: + case P_LOGIC_UNEQ: + + case P_LOGIC_GREATER: + case P_LOGIC_LESS: + + case P_RSHIFT: + case P_LSHIFT: + + case P_BIN_AND: + case P_BIN_OR: + case P_BIN_XOR: + + case P_COLON: + case P_QUESTIONMARK: + { + if (!lastwasvalue) + { + SourceError(source, "operator %s after operator in #if/#elif", t->string); + error = 1; + break; + } //end if + break; + } //end case + default: + { + SourceError(source, "invalid operator %s in #if/#elif", t->string); + error = 1; + break; + } //end default + } //end switch + if (!error && !negativevalue) + { + //o = (operator_t *) GetClearedMemory(sizeof(operator_t)); + AllocOperator(o); + o->operator = t->subtype; + o->priority = PC_OperatorPriority(t->subtype); + o->parentheses = parentheses; + o->next = NULL; + o->prev = lastoperator; + if (lastoperator) lastoperator->next = o; + else firstoperator = o; + lastoperator = o; + lastwasvalue = 0; + } //end if + break; + } //end case + default: + { + SourceError(source, "unknown %s in #if/#elif", t->string); + error = 1; + break; + } //end default + } //end switch + if (error) break; + } //end for + if (!error) + { + if (!lastwasvalue) + { + SourceError(source, "trailing operator in #if/#elif"); + error = 1; + } //end if + else if (parentheses) + { + SourceError(source, "too many ( in #if/#elif"); + error = 1; + } //end else if + } //end if + // + gotquestmarkvalue = qfalse; + questmarkintvalue = 0; + questmarkfloatvalue = 0; + //while there are operators + while(!error && firstoperator) + { + v = firstvalue; + for (o = firstoperator; o->next; o = o->next) + { + //if the current operator is nested deeper in parentheses + //than the next operator + if (o->parentheses > o->next->parentheses) break; + //if the current and next operator are nested equally deep in parentheses + if (o->parentheses == o->next->parentheses) + { + //if the priority of the current operator is equal or higher + //than the priority of the next operator + if (o->priority >= o->next->priority) break; + } //end if + //if the arity of the operator isn't equal to 1 + if (o->operator != P_LOGIC_NOT + && o->operator != P_BIN_NOT) v = v->next; + //if there's no value or no next value + if (!v) + { + SourceError(source, "mising values in #if/#elif"); + error = 1; + break; + } //end if + } //end for + if (error) break; + v1 = v; + v2 = v->next; +#ifdef DEBUG_EVAL + if (integer) + { + Log_Write("operator %s, value1 = %d", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue); + if (v2) Log_Write("value2 = %d", v2->intvalue); + } //end if + else + { + Log_Write("operator %s, value1 = %f", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue); + if (v2) Log_Write("value2 = %f", v2->floatvalue); + } //end else +#endif //DEBUG_EVAL + switch(o->operator) + { + case P_LOGIC_NOT: v1->intvalue = !v1->intvalue; + v1->floatvalue = !v1->floatvalue; break; + case P_BIN_NOT: v1->intvalue = ~v1->intvalue; + break; + case P_MUL: v1->intvalue *= v2->intvalue; + v1->floatvalue *= v2->floatvalue; break; + case P_DIV: if (!v2->intvalue || !v2->floatvalue) + { + SourceError(source, "divide by zero in #if/#elif\n"); + error = 1; + break; + } + v1->intvalue /= v2->intvalue; + v1->floatvalue /= v2->floatvalue; break; + case P_MOD: if (!v2->intvalue) + { + SourceError(source, "divide by zero in #if/#elif\n"); + error = 1; + break; + } + v1->intvalue %= v2->intvalue; break; + case P_ADD: v1->intvalue += v2->intvalue; + v1->floatvalue += v2->floatvalue; break; + case P_SUB: v1->intvalue -= v2->intvalue; + v1->floatvalue -= v2->floatvalue; break; + case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue; + v1->floatvalue = v1->floatvalue && v2->floatvalue; break; + case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue; + v1->floatvalue = v1->floatvalue || v2->floatvalue; break; + case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue; + v1->floatvalue = v1->floatvalue >= v2->floatvalue; break; + case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue; + v1->floatvalue = v1->floatvalue <= v2->floatvalue; break; + case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue; + v1->floatvalue = v1->floatvalue == v2->floatvalue; break; + case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue; + v1->floatvalue = v1->floatvalue != v2->floatvalue; break; + case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue; + v1->floatvalue = v1->floatvalue > v2->floatvalue; break; + case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue; + v1->floatvalue = v1->floatvalue < v2->floatvalue; break; + case P_RSHIFT: v1->intvalue >>= v2->intvalue; + break; + case P_LSHIFT: v1->intvalue <<= v2->intvalue; + break; + case P_BIN_AND: v1->intvalue &= v2->intvalue; + break; + case P_BIN_OR: v1->intvalue |= v2->intvalue; + break; + case P_BIN_XOR: v1->intvalue ^= v2->intvalue; + break; + case P_COLON: + { + if (!gotquestmarkvalue) + { + SourceError(source, ": without ? in #if/#elif"); + error = 1; + break; + } //end if + if (integer) + { + if (!questmarkintvalue) v1->intvalue = v2->intvalue; + } //end if + else + { + if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue; + } //end else + gotquestmarkvalue = qfalse; + break; + } //end case + case P_QUESTIONMARK: + { + if (gotquestmarkvalue) + { + SourceError(source, "? after ? in #if/#elif"); + error = 1; + break; + } //end if + questmarkintvalue = v1->intvalue; + questmarkfloatvalue = v1->floatvalue; + gotquestmarkvalue = qtrue; + break; + } //end if + } //end switch +#ifdef DEBUG_EVAL + if (integer) Log_Write("result value = %d", v1->intvalue); + else Log_Write("result value = %f", v1->floatvalue); +#endif //DEBUG_EVAL + if (error) break; + lastoperatortype = o->operator; + //if not an operator with arity 1 + if (o->operator != P_LOGIC_NOT + && o->operator != P_BIN_NOT) + { + //remove the second value if not question mark operator + if (o->operator != P_QUESTIONMARK) v = v->next; + // + if (v->prev) v->prev->next = v->next; + else firstvalue = v->next; + if (v->next) v->next->prev = v->prev; + else lastvalue = v->prev; + //FreeMemory(v); + FreeValue(v); + } //end if + //remove the operator + if (o->prev) o->prev->next = o->next; + else firstoperator = o->next; + if (o->next) o->next->prev = o->prev; + else lastoperator = o->prev; + //FreeMemory(o); + FreeOperator(o); + } //end while + if (firstvalue) + { + if (intvalue) *intvalue = firstvalue->intvalue; + if (floatvalue) *floatvalue = firstvalue->floatvalue; + } //end if + for (o = firstoperator; o; o = lastoperator) + { + lastoperator = o->next; + //FreeMemory(o); + FreeOperator(o); + } //end for + for (v = firstvalue; v; v = lastvalue) + { + lastvalue = v->next; + //FreeMemory(v); + FreeValue(v); + } //end for + if (!error) return qtrue; + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + return qfalse; +} //end of the function PC_EvaluateTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Evaluate(source_t *source, signed long int *intvalue, + double *floatvalue, int integer) +{ + token_t token, *firsttoken, *lasttoken; + token_t *t, *nexttoken; + define_t *define; + int defined = qfalse; + + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + // + if (!PC_ReadLine(source, &token)) + { + SourceError(source, "no value after #if/#elif"); + return qfalse; + } //end if + firsttoken = NULL; + lasttoken = NULL; + do + { + //if the token is a name + if (token.type == TT_NAME) + { + if (defined) + { + defined = qfalse; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else if (!strcmp(token.string, "defined")) + { + defined = qtrue; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else + { + //then it must be a define +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + if (!define) + { + SourceError(source, "can't evaluate %s, not defined", token.string); + return qfalse; + } //end if + if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; + } //end else + } //end if + //if the token is a number or a punctuation + else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) + { + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end else + else //can't evaluate the token + { + SourceError(source, "can't evaluate %s", token.string); + return qfalse; + } //end else + } while(PC_ReadLine(source, &token)); + // + if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; + // +#ifdef DEBUG_EVAL + Log_Write("eval:"); +#endif //DEBUG_EVAL + for (t = firsttoken; t; t = nexttoken) + { +#ifdef DEBUG_EVAL + Log_Write(" %s", t->string); +#endif //DEBUG_EVAL + nexttoken = t->next; + PC_FreeToken(t); + } //end for +#ifdef DEBUG_EVAL + if (integer) Log_Write("eval result: %d", *intvalue); + else Log_Write("eval result: %f", *floatvalue); +#endif //DEBUG_EVAL + // + return qtrue; +} //end of the function PC_Evaluate +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarEvaluate(source_t *source, signed long int *intvalue, + double *floatvalue, int integer) +{ + int indent, defined = qfalse; + token_t token, *firsttoken, *lasttoken; + token_t *t, *nexttoken; + define_t *define; + + if (intvalue) *intvalue = 0; + if (floatvalue) *floatvalue = 0; + // + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "no leading ( after $evalint/$evalfloat"); + return qfalse; + } //end if + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "nothing to evaluate"); + return qfalse; + } //end if + indent = 1; + firsttoken = NULL; + lasttoken = NULL; + do + { + //if the token is a name + if (token.type == TT_NAME) + { + if (defined) + { + defined = qfalse; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else if (!strcmp(token.string, "defined")) + { + defined = qtrue; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end if + else + { + //then it must be a define +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token.string); +#else + define = PC_FindDefine(source->defines, token.string); +#endif //DEFINEHASHING + if (!define) + { + SourceError(source, "can't evaluate %s, not defined", token.string); + return qfalse; + } //end if + if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse; + } //end else + } //end if + //if the token is a number or a punctuation + else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) + { + if (*token.string == '(') indent++; + else if (*token.string == ')') indent--; + if (indent <= 0) break; + t = PC_CopyToken(&token); + t->next = NULL; + if (lasttoken) lasttoken->next = t; + else firsttoken = t; + lasttoken = t; + } //end else + else //can't evaluate the token + { + SourceError(source, "can't evaluate %s", token.string); + return qfalse; + } //end else + } while(PC_ReadSourceToken(source, &token)); + // + if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; + // +#ifdef DEBUG_EVAL + Log_Write("$eval:"); +#endif //DEBUG_EVAL + for (t = firsttoken; t; t = nexttoken) + { +#ifdef DEBUG_EVAL + Log_Write(" %s", t->string); +#endif //DEBUG_EVAL + nexttoken = t->next; + PC_FreeToken(t); + } //end for +#ifdef DEBUG_EVAL + if (integer) Log_Write("$eval result: %d", *intvalue); + else Log_Write("$eval result: %f", *floatvalue); +#endif //DEBUG_EVAL + // + return qtrue; +} //end of the function PC_DollarEvaluate +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_elif(source_t *source) +{ + signed long int value; + int type, skip; + + PC_PopIndent(source, &type, &skip); + if (!type || type == INDENT_ELSE) + { + SourceError(source, "misplaced #elif"); + return qfalse; + } //end if + if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; + skip = (value == 0); + PC_PushIndent(source, INDENT_ELIF, skip); + return qtrue; +} //end of the function PC_Directive_elif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_if(source_t *source) +{ + signed long int value; + int skip; + + if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; + skip = (value == 0); + PC_PushIndent(source, INDENT_IF, skip); + return qtrue; +} //end of the function PC_Directive +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_line(source_t *source) +{ + SourceError(source, "#line directive not supported"); + return qfalse; +} //end of the function PC_Directive_line +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_error(source_t *source) +{ + token_t token; + + strcpy(token.string, ""); + PC_ReadSourceToken(source, &token); + SourceError(source, "#error directive: %s", token.string); + return qfalse; +} //end of the function PC_Directive_error +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_pragma(source_t *source) +{ + token_t token; + + SourceWarning(source, "#pragma directive not supported"); + while(PC_ReadLine(source, &token)) ; + return qtrue; +} //end of the function PC_Directive_pragma +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void UnreadSignToken(source_t *source) +{ + token_t token; + + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + strcpy(token.string, "-"); + token.type = TT_PUNCTUATION; + token.subtype = P_SUB; + PC_UnreadSourceToken(source, &token); +} //end of the function UnreadSignToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_eval(source_t *source) +{ + signed long int value; + token_t token; + + if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse; + // + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%d", abs(value)); + token.type = TT_NUMBER; + token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_Directive_eval +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_evalfloat(source_t *source) +{ + double value; + token_t token; + + if (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse; + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%1.2f", fabs(value)); + token.type = TT_NUMBER; + token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_Directive_evalfloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +directive_t directives[20] = +{ + {"if", PC_Directive_if}, + {"ifdef", PC_Directive_ifdef}, + {"ifndef", PC_Directive_ifndef}, + {"elif", PC_Directive_elif}, + {"else", PC_Directive_else}, + {"endif", PC_Directive_endif}, + {"include", PC_Directive_include}, + {"define", PC_Directive_define}, + {"undef", PC_Directive_undef}, + {"line", PC_Directive_line}, + {"error", PC_Directive_error}, + {"pragma", PC_Directive_pragma}, + {"eval", PC_Directive_eval}, + {"evalfloat", PC_Directive_evalfloat}, + {NULL, NULL} +}; + +int PC_ReadDirective(source_t *source) +{ + token_t token; + int i; + + //read the directive name + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "found # without name"); + return qfalse; + } //end if + //directive name must be on the same line + if (token.linescrossed > 0) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "found # at end of line"); + return qfalse; + } //end if + //if if is a name + if (token.type == TT_NAME) + { + //find the precompiler directive + for (i = 0; directives[i].name; i++) + { + if (!strcmp(directives[i].name, token.string)) + { + return directives[i].func(source); + } //end if + } //end for + } //end if + SourceError(source, "unknown precompiler directive %s", token.string); + return qfalse; +} //end of the function PC_ReadDirective +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarDirective_evalint(source_t *source) +{ + signed long int value; + token_t token; + + if (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse; + // + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%d", abs(value)); + token.type = TT_NUMBER; + token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; +#ifdef NUMBERVALUE + token.intvalue = value; + token.floatvalue = value; +#endif //NUMBERVALUE + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_DollarDirective_evalint +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarDirective_evalfloat(source_t *source) +{ + double value; + token_t token; + + if (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse; + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf(token.string, "%1.2f", fabs(value)); + token.type = TT_NUMBER; + token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; +#ifdef NUMBERVALUE + token.intvalue = (unsigned long) value; + token.floatvalue = value; +#endif //NUMBERVALUE + PC_UnreadSourceToken(source, &token); + if (value < 0) UnreadSignToken(source); + return qtrue; +} //end of the function PC_DollarDirective_evalfloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +directive_t dollardirectives[20] = +{ + {"evalint", PC_DollarDirective_evalint}, + {"evalfloat", PC_DollarDirective_evalfloat}, + {NULL, NULL} +}; + +int PC_ReadDollarDirective(source_t *source) +{ + token_t token; + int i; + + //read the directive name + if (!PC_ReadSourceToken(source, &token)) + { + SourceError(source, "found $ without name"); + return qfalse; + } //end if + //directive name must be on the same line + if (token.linescrossed > 0) + { + PC_UnreadSourceToken(source, &token); + SourceError(source, "found $ at end of line"); + return qfalse; + } //end if + //if if is a name + if (token.type == TT_NAME) + { + //find the precompiler directive + for (i = 0; dollardirectives[i].name; i++) + { + if (!strcmp(dollardirectives[i].name, token.string)) + { + return dollardirectives[i].func(source); + } //end if + } //end for + } //end if + PC_UnreadSourceToken(source, &token); + SourceError(source, "unknown precompiler directive %s", token.string); + return qfalse; +} //end of the function PC_ReadDirective + +#ifdef QUAKEC +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int BuiltinFunction(source_t *source) +{ + token_t token; + + if (!PC_ReadSourceToken(source, &token)) return qfalse; + if (token.type == TT_NUMBER) + { + PC_UnreadSourceToken(source, &token); + return qtrue; + } //end if + else + { + PC_UnreadSourceToken(source, &token); + return qfalse; + } //end else +} //end of the function BuiltinFunction +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int QuakeCMacro(source_t *source) +{ + int i; + token_t token; + + if (!PC_ReadSourceToken(source, &token)) return qtrue; + if (token.type != TT_NAME) + { + PC_UnreadSourceToken(source, &token); + return qtrue; + } //end if + //find the precompiler directive + for (i = 0; dollardirectives[i].name; i++) + { + if (!strcmp(dollardirectives[i].name, token.string)) + { + PC_UnreadSourceToken(source, &token); + return qfalse; + } //end if + } //end for + PC_UnreadSourceToken(source, &token); + return qtrue; +} //end of the function QuakeCMacro +#endif //QUAKEC +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadToken(source_t *source, token_t *token) +{ + define_t *define; + + while(1) + { + if (!PC_ReadSourceToken(source, token)) return qfalse; + //check for precompiler directives + if (token->type == TT_PUNCTUATION && *token->string == '#') + { +#ifdef QUAKEC + if (!BuiltinFunction(source)) +#endif //QUAKC + { + //read the precompiler directive + if (!PC_ReadDirective(source)) return qfalse; + continue; + } //end if + } //end if + if (token->type == TT_PUNCTUATION && *token->string == '$') + { +#ifdef QUAKEC + if (!QuakeCMacro(source)) +#endif //QUAKEC + { + //read the precompiler directive + if (!PC_ReadDollarDirective(source)) return qfalse; + continue; + } //end if + } //end if + // recursively concatenate strings that are behind each other still resolving defines + if (token->type == TT_STRING) + { + token_t newtoken; + if (PC_ReadToken(source, &newtoken)) + { + if (newtoken.type == TT_STRING) + { + token->string[strlen(token->string)-1] = '\0'; + if (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN) + { + SourceError(source, "string longer than MAX_TOKEN %d\n", MAX_TOKEN); + return qfalse; + } + strcat(token->string, newtoken.string+1); + } + else + { + PC_UnreadToken(source, &newtoken); + } + } + } //end if + //if skipping source because of conditional compilation + if (source->skip) continue; + //if the token is a name + if (token->type == TT_NAME) + { + //check if the name is a define macro +#if DEFINEHASHING + define = PC_FindHashedDefine(source->definehash, token->string); +#else + define = PC_FindDefine(source->defines, token->string); +#endif //DEFINEHASHING + //if it is a define macro + if (define) + { + //expand the defined macro + if (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse; + continue; + } //end if + } //end if + //copy token for unreading + Com_Memcpy(&source->token, token, sizeof(token_t)); + //found a token + return qtrue; + } //end while +} //end of the function PC_ReadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectTokenString(source_t *source, char *string) +{ + token_t token; + + if (!PC_ReadToken(source, &token)) + { + SourceError(source, "couldn't find expected %s", string); + return qfalse; + } //end if + + if (strcmp(token.string, string)) + { + SourceError(source, "expected %s, found %s", string, token.string); + return qfalse; + } //end if + return qtrue; +} //end of the function PC_ExpectTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token) +{ + char str[MAX_TOKEN]; + + if (!PC_ReadToken(source, token)) + { + SourceError(source, "couldn't read expected token"); + return qfalse; + } //end if + + if (token->type != type) + { + strcpy(str, ""); + if (type == TT_STRING) strcpy(str, "string"); + if (type == TT_LITERAL) strcpy(str, "literal"); + if (type == TT_NUMBER) strcpy(str, "number"); + if (type == TT_NAME) strcpy(str, "name"); + if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); + SourceError(source, "expected a %s, found %s", str, token->string); + return qfalse; + } //end if + if (token->type == TT_NUMBER) + { + if ((token->subtype & subtype) != subtype) + { + if (subtype & TT_DECIMAL) strcpy(str, "decimal"); + if (subtype & TT_HEX) strcpy(str, "hex"); + if (subtype & TT_OCTAL) strcpy(str, "octal"); + if (subtype & TT_BINARY) strcpy(str, "binary"); + if (subtype & TT_LONG) strcat(str, " long"); + if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); + if (subtype & TT_FLOAT) strcat(str, " float"); + if (subtype & TT_INTEGER) strcat(str, " integer"); + SourceError(source, "expected %s, found %s", str, token->string); + return qfalse; + } //end if + } //end if + else if (token->type == TT_PUNCTUATION) + { + if (token->subtype != subtype) + { + SourceError(source, "found %s", token->string); + return qfalse; + } //end if + } //end else if + return qtrue; +} //end of the function PC_ExpectTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectAnyToken(source_t *source, token_t *token) +{ + if (!PC_ReadToken(source, token)) + { + SourceError(source, "couldn't read expected token"); + return qfalse; + } //end if + else + { + return qtrue; + } //end else +} //end of the function PC_ExpectAnyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_CheckTokenString(source_t *source, char *string) +{ + token_t tok; + + if (!PC_ReadToken(source, &tok)) return qfalse; + //if the token is available + if (!strcmp(tok.string, string)) return qtrue; + // + PC_UnreadSourceToken(source, &tok); + return qfalse; +} //end of the function PC_CheckTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token) +{ + token_t tok; + + if (!PC_ReadToken(source, &tok)) return qfalse; + //if the type matches + if (tok.type == type && + (tok.subtype & subtype) == subtype) + { + Com_Memcpy(token, &tok, sizeof(token_t)); + return qtrue; + } //end if + // + PC_UnreadSourceToken(source, &tok); + return qfalse; +} //end of the function PC_CheckTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_SkipUntilString(source_t *source, char *string) +{ + token_t token; + + while(PC_ReadToken(source, &token)) + { + if (!strcmp(token.string, string)) return qtrue; + } //end while + return qfalse; +} //end of the function PC_SkipUntilString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadLastToken(source_t *source) +{ + PC_UnreadSourceToken(source, &source->token); +} //end of the function PC_UnreadLastToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadToken(source_t *source, token_t *token) +{ + PC_UnreadSourceToken(source, token); +} //end of the function PC_UnreadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetIncludePath(source_t *source, char *path) +{ + strncpy(source->includepath, path, MAX_PATH); + //add trailing path seperator + if (source->includepath[strlen(source->includepath)-1] != '\\' && + source->includepath[strlen(source->includepath)-1] != '/') + { + strcat(source->includepath, PATHSEPERATOR_STR); + } //end if +} //end of the function PC_SetIncludePath +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetPunctuations(source_t *source, punctuation_t *p) +{ + source->punctuations = p; +} //end of the function PC_SetPunctuations +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +source_t *LoadSourceFile(const char *filename) +{ + source_t *source; + script_t *script; + + PC_InitTokenHeap(); + + script = LoadScriptFile(filename); + if (!script) return NULL; + + script->next = NULL; + + source = (source_t *) GetMemory(sizeof(source_t)); + Com_Memset(source, 0, sizeof(source_t)); + + strncpy(source->filename, filename, MAX_PATH); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + +#if DEFINEHASHING + source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); +#endif //DEFINEHASHING + PC_AddGlobalDefinesToSource(source); + return source; +} //end of the function LoadSourceFile +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +source_t *LoadSourceMemory(char *ptr, int length, char *name) +{ + source_t *source; + script_t *script; + + PC_InitTokenHeap(); + + script = LoadScriptMemory(ptr, length, name); + if (!script) return NULL; + script->next = NULL; + + source = (source_t *) GetMemory(sizeof(source_t)); + Com_Memset(source, 0, sizeof(source_t)); + + strncpy(source->filename, name, MAX_PATH); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + +#if DEFINEHASHING + source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); +#endif //DEFINEHASHING + PC_AddGlobalDefinesToSource(source); + return source; +} //end of the function LoadSourceMemory +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void FreeSource(source_t *source) +{ + script_t *script; + token_t *token; + define_t *define; + indent_t *indent; + int i; + + //PC_PrintDefineHashTable(source->definehash); + //free all the scripts + while(source->scriptstack) + { + script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + FreeScript(script); + } //end for + //free all the tokens + while(source->tokens) + { + token = source->tokens; + source->tokens = source->tokens->next; + PC_FreeToken(token); + } //end for +#if DEFINEHASHING + for (i = 0; i < DEFINEHASHSIZE; i++) + { + while(source->definehash[i]) + { + define = source->definehash[i]; + source->definehash[i] = source->definehash[i]->hashnext; + PC_FreeDefine(define); + } //end while + } //end for +#else //DEFINEHASHING + //free all defines + while(source->defines) + { + define = source->defines; + source->defines = source->defines->next; + PC_FreeDefine(define); + } //end for +#endif //DEFINEHASHING + //free all indents + while(source->indentstack) + { + indent = source->indentstack; + source->indentstack = source->indentstack->next; + FreeMemory(indent); + } //end for +#if DEFINEHASHING + // + if (source->definehash) FreeMemory(source->definehash); +#endif //DEFINEHASHING + //free the source itself + FreeMemory(source); +} //end of the function FreeSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ + +#define MAX_SOURCEFILES 64 + +source_t *sourceFiles[MAX_SOURCEFILES]; + +int PC_LoadSourceHandle(const char *filename) +{ + source_t *source; + int i; + + for (i = 1; i < MAX_SOURCEFILES; i++) + { + if (!sourceFiles[i]) + break; + } //end for + if (i >= MAX_SOURCEFILES) + return 0; + PS_SetBaseFolder(""); + source = LoadSourceFile(filename); + if (!source) + return 0; + sourceFiles[i] = source; + return i; +} //end of the function PC_LoadSourceHandle +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_FreeSourceHandle(int handle) +{ + if (handle < 1 || handle >= MAX_SOURCEFILES) + return qfalse; + if (!sourceFiles[handle]) + return qfalse; + + FreeSource(sourceFiles[handle]); + sourceFiles[handle] = NULL; + return qtrue; +} //end of the function PC_FreeSourceHandle +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadTokenHandle(int handle, pc_token_t *pc_token) +{ + token_t token; + int ret; + + if (handle < 1 || handle >= MAX_SOURCEFILES) + return 0; + if (!sourceFiles[handle]) + return 0; + + ret = PC_ReadToken(sourceFiles[handle], &token); + strcpy(pc_token->string, token.string); + pc_token->type = token.type; + pc_token->subtype = token.subtype; + pc_token->intvalue = token.intvalue; + pc_token->floatvalue = token.floatvalue; + if (pc_token->type == TT_STRING) + StripDoubleQuotes(pc_token->string); + return ret; +} //end of the function PC_ReadTokenHandle +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_SourceFileAndLine(int handle, char *filename, int *line) +{ + if (handle < 1 || handle >= MAX_SOURCEFILES) + return qfalse; + if (!sourceFiles[handle]) + return qfalse; + + strcpy(filename, sourceFiles[handle]->filename); + if (sourceFiles[handle]->scriptstack) + *line = sourceFiles[handle]->scriptstack->line; + else + *line = 0; + return qtrue; +} //end of the function PC_SourceFileAndLine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetBaseFolder(char *path) +{ + PS_SetBaseFolder(path); +} //end of the function PC_SetBaseFolder +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_CheckOpenSourceHandles(void) +{ + int i; + + for (i = 1; i < MAX_SOURCEFILES; i++) + { + if (sourceFiles[i]) + { +#ifdef BOTLIB + botimport.Print(PRT_ERROR, "file %s still open in precompiler\n", sourceFiles[i]->scriptstack->filename); +#endif //BOTLIB + } //end if + } //end for +} //end of the function PC_CheckOpenSourceHandles + diff --git a/code/botlib/l_precomp.h b/code/botlib/l_precomp.h index b2da94f..c789527 100755 --- a/code/botlib/l_precomp.h +++ b/code/botlib/l_precomp.h @@ -1,180 +1,180 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_precomp.h
- *
- * desc: pre compiler
- *
- * $Archive: /source/code/botlib/l_precomp.h $
- *
- *****************************************************************************/
-
-#ifndef MAX_PATH
- #define MAX_PATH MAX_QPATH
-#endif
-
-#ifndef PATH_SEPERATORSTR
- #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__)
- #define PATHSEPERATOR_STR "\\"
- #else
- #define PATHSEPERATOR_STR "/"
- #endif
-#endif
-#ifndef PATH_SEPERATORCHAR
- #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__)
- #define PATHSEPERATOR_CHAR '\\'
- #else
- #define PATHSEPERATOR_CHAR '/'
- #endif
-#endif
-
-#if defined(BSPC) && !defined(QDECL)
-#define QDECL
-#endif
-
-
-#define DEFINE_FIXED 0x0001
-
-#define BUILTIN_LINE 1
-#define BUILTIN_FILE 2
-#define BUILTIN_DATE 3
-#define BUILTIN_TIME 4
-#define BUILTIN_STDC 5
-
-#define INDENT_IF 0x0001
-#define INDENT_ELSE 0x0002
-#define INDENT_ELIF 0x0004
-#define INDENT_IFDEF 0x0008
-#define INDENT_IFNDEF 0x0010
-
-//macro definitions
-typedef struct define_s
-{
- char *name; //define name
- int flags; //define flags
- int builtin; // > 0 if builtin define
- int numparms; //number of define parameters
- token_t *parms; //define parameters
- token_t *tokens; //macro tokens (possibly containing parm tokens)
- struct define_s *next; //next defined macro in a list
- struct define_s *hashnext; //next define in the hash chain
-} define_t;
-
-//indents
-//used for conditional compilation directives:
-//#if, #else, #elif, #ifdef, #ifndef
-typedef struct indent_s
-{
- int type; //indent type
- int skip; //true if skipping current indent
- script_t *script; //script the indent was in
- struct indent_s *next; //next indent on the indent stack
-} indent_t;
-
-//source file
-typedef struct source_s
-{
- char filename[1024]; //file name of the script
- char includepath[1024]; //path to include files
- punctuation_t *punctuations; //punctuations to use
- script_t *scriptstack; //stack with scripts of the source
- token_t *tokens; //tokens to read first
- define_t *defines; //list with macro definitions
- define_t **definehash; //hash chain with defines
- indent_t *indentstack; //stack with indents
- int skip; // > 0 if skipping conditional code
- token_t token; //last read token
-} source_t;
-
-
-//read a token from the source
-int PC_ReadToken(source_t *source, token_t *token);
-//expect a certain token
-int PC_ExpectTokenString(source_t *source, char *string);
-//expect a certain token type
-int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token);
-//expect a token
-int PC_ExpectAnyToken(source_t *source, token_t *token);
-//returns true when the token is available
-int PC_CheckTokenString(source_t *source, char *string);
-//returns true an reads the token when a token with the given type is available
-int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token);
-//skip tokens until the given token string is read
-int PC_SkipUntilString(source_t *source, char *string);
-//unread the last token read from the script
-void PC_UnreadLastToken(source_t *source);
-//unread the given token
-void PC_UnreadToken(source_t *source, token_t *token);
-//read a token only if on the same line, lines are concatenated with a slash
-int PC_ReadLine(source_t *source, token_t *token);
-//returns true if there was a white space in front of the token
-int PC_WhiteSpaceBeforeToken(token_t *token);
-//add a define to the source
-int PC_AddDefine(source_t *source, char *string);
-//add a globals define that will be added to all opened sources
-int PC_AddGlobalDefine(char *string);
-//remove the given global define
-int PC_RemoveGlobalDefine(char *name);
-//remove all globals defines
-void PC_RemoveAllGlobalDefines(void);
-//add builtin defines
-void PC_AddBuiltinDefines(source_t *source);
-//set the source include path
-void PC_SetIncludePath(source_t *source, char *path);
-//set the punction set
-void PC_SetPunctuations(source_t *source, punctuation_t *p);
-//set the base folder to load files from
-void PC_SetBaseFolder(char *path);
-//load a source file
-source_t *LoadSourceFile(const char *filename);
-//load a source from memory
-source_t *LoadSourceMemory(char *ptr, int length, char *name);
-//free the given source
-void FreeSource(source_t *source);
-//print a source error
-void QDECL SourceError(source_t *source, char *str, ...);
-//print a source warning
-void QDECL SourceWarning(source_t *source, char *str, ...);
-
-#ifdef BSPC
-// some of BSPC source does include game/q_shared.h and some does not
-// we define pc_token_s pc_token_t if needed (yes, it's ugly)
-#ifndef __Q_SHARED_H
-#define MAX_TOKENLENGTH 1024
-typedef struct pc_token_s
-{
- int type;
- int subtype;
- int intvalue;
- float floatvalue;
- char string[MAX_TOKENLENGTH];
-} pc_token_t;
-#endif //!_Q_SHARED_H
-#endif //BSPC
-
-//
-int PC_LoadSourceHandle(const char *filename);
-int PC_FreeSourceHandle(int handle);
-int PC_ReadTokenHandle(int handle, pc_token_t *pc_token);
-int PC_SourceFileAndLine(int handle, char *filename, int *line);
-void PC_CheckOpenSourceHandles(void);
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_precomp.h + * + * desc: pre compiler + * + * $Archive: /source/code/botlib/l_precomp.h $ + * + *****************************************************************************/ + +#ifndef MAX_PATH + #define MAX_PATH MAX_QPATH +#endif + +#ifndef PATH_SEPERATORSTR + #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) + #define PATHSEPERATOR_STR "\\" + #else + #define PATHSEPERATOR_STR "/" + #endif +#endif +#ifndef PATH_SEPERATORCHAR + #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) + #define PATHSEPERATOR_CHAR '\\' + #else + #define PATHSEPERATOR_CHAR '/' + #endif +#endif + +#if defined(BSPC) && !defined(QDECL) +#define QDECL +#endif + + +#define DEFINE_FIXED 0x0001 + +#define BUILTIN_LINE 1 +#define BUILTIN_FILE 2 +#define BUILTIN_DATE 3 +#define BUILTIN_TIME 4 +#define BUILTIN_STDC 5 + +#define INDENT_IF 0x0001 +#define INDENT_ELSE 0x0002 +#define INDENT_ELIF 0x0004 +#define INDENT_IFDEF 0x0008 +#define INDENT_IFNDEF 0x0010 + +//macro definitions +typedef struct define_s +{ + char *name; //define name + int flags; //define flags + int builtin; // > 0 if builtin define + int numparms; //number of define parameters + token_t *parms; //define parameters + token_t *tokens; //macro tokens (possibly containing parm tokens) + struct define_s *next; //next defined macro in a list + struct define_s *hashnext; //next define in the hash chain +} define_t; + +//indents +//used for conditional compilation directives: +//#if, #else, #elif, #ifdef, #ifndef +typedef struct indent_s +{ + int type; //indent type + int skip; //true if skipping current indent + script_t *script; //script the indent was in + struct indent_s *next; //next indent on the indent stack +} indent_t; + +//source file +typedef struct source_s +{ + char filename[1024]; //file name of the script + char includepath[1024]; //path to include files + punctuation_t *punctuations; //punctuations to use + script_t *scriptstack; //stack with scripts of the source + token_t *tokens; //tokens to read first + define_t *defines; //list with macro definitions + define_t **definehash; //hash chain with defines + indent_t *indentstack; //stack with indents + int skip; // > 0 if skipping conditional code + token_t token; //last read token +} source_t; + + +//read a token from the source +int PC_ReadToken(source_t *source, token_t *token); +//expect a certain token +int PC_ExpectTokenString(source_t *source, char *string); +//expect a certain token type +int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token); +//expect a token +int PC_ExpectAnyToken(source_t *source, token_t *token); +//returns true when the token is available +int PC_CheckTokenString(source_t *source, char *string); +//returns true an reads the token when a token with the given type is available +int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token); +//skip tokens until the given token string is read +int PC_SkipUntilString(source_t *source, char *string); +//unread the last token read from the script +void PC_UnreadLastToken(source_t *source); +//unread the given token +void PC_UnreadToken(source_t *source, token_t *token); +//read a token only if on the same line, lines are concatenated with a slash +int PC_ReadLine(source_t *source, token_t *token); +//returns true if there was a white space in front of the token +int PC_WhiteSpaceBeforeToken(token_t *token); +//add a define to the source +int PC_AddDefine(source_t *source, char *string); +//add a globals define that will be added to all opened sources +int PC_AddGlobalDefine(char *string); +//remove the given global define +int PC_RemoveGlobalDefine(char *name); +//remove all globals defines +void PC_RemoveAllGlobalDefines(void); +//add builtin defines +void PC_AddBuiltinDefines(source_t *source); +//set the source include path +void PC_SetIncludePath(source_t *source, char *path); +//set the punction set +void PC_SetPunctuations(source_t *source, punctuation_t *p); +//set the base folder to load files from +void PC_SetBaseFolder(char *path); +//load a source file +source_t *LoadSourceFile(const char *filename); +//load a source from memory +source_t *LoadSourceMemory(char *ptr, int length, char *name); +//free the given source +void FreeSource(source_t *source); +//print a source error +void QDECL SourceError(source_t *source, char *str, ...); +//print a source warning +void QDECL SourceWarning(source_t *source, char *str, ...); + +#ifdef BSPC +// some of BSPC source does include game/q_shared.h and some does not +// we define pc_token_s pc_token_t if needed (yes, it's ugly) +#ifndef __Q_SHARED_H +#define MAX_TOKENLENGTH 1024 +typedef struct pc_token_s +{ + int type; + int subtype; + int intvalue; + float floatvalue; + char string[MAX_TOKENLENGTH]; +} pc_token_t; +#endif //!_Q_SHARED_H +#endif //BSPC + +// +int PC_LoadSourceHandle(const char *filename); +int PC_FreeSourceHandle(int handle); +int PC_ReadTokenHandle(int handle, pc_token_t *pc_token); +int PC_SourceFileAndLine(int handle, char *filename, int *line); +void PC_CheckOpenSourceHandles(void); diff --git a/code/botlib/l_script.c b/code/botlib/l_script.c index 2f1e4fa..e782e7f 100755 --- a/code/botlib/l_script.c +++ b/code/botlib/l_script.c @@ -1,1433 +1,1433 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_script.c
- *
- * desc: lexicographical parser
- *
- * $Archive: /MissionPack/code/botlib/l_script.c $
- *
- *****************************************************************************/
-
-//#define SCREWUP
-//#define BOTLIB
-//#define MEQCC
-//#define BSPC
-
-#ifdef SCREWUP
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-#include <stdarg.h>
-#include "l_memory.h"
-#include "l_script.h"
-
-typedef enum {qfalse, qtrue} qboolean;
-
-#endif //SCREWUP
-
-#ifdef BOTLIB
-//include files for usage in the bot library
-#include "../game/q_shared.h"
-#include "../game/botlib.h"
-#include "be_interface.h"
-#include "l_script.h"
-#include "l_memory.h"
-#include "l_log.h"
-#include "l_libvar.h"
-#endif //BOTLIB
-
-#ifdef MEQCC
-//include files for usage in MrElusive's QuakeC Compiler
-#include "qcc.h"
-#include "l_script.h"
-#include "l_memory.h"
-#include "l_log.h"
-
-#define qtrue true
-#define qfalse false
-#endif //MEQCC
-
-#ifdef BSPC
-//include files for usage in the BSP Converter
-#include "../bspc/qbsp.h"
-#include "../bspc/l_log.h"
-#include "../bspc/l_mem.h"
-
-#define qtrue true
-#define qfalse false
-#endif //BSPC
-
-
-#define PUNCTABLE
-
-//longer punctuations first
-punctuation_t default_punctuations[] =
-{
- //binary operators
- {">>=",P_RSHIFT_ASSIGN, NULL},
- {"<<=",P_LSHIFT_ASSIGN, NULL},
- //
- {"...",P_PARMS, NULL},
- //define merge operator
- {"##",P_PRECOMPMERGE, NULL},
- //logic operators
- {"&&",P_LOGIC_AND, NULL},
- {"||",P_LOGIC_OR, NULL},
- {">=",P_LOGIC_GEQ, NULL},
- {"<=",P_LOGIC_LEQ, NULL},
- {"==",P_LOGIC_EQ, NULL},
- {"!=",P_LOGIC_UNEQ, NULL},
- //arithmatic operators
- {"*=",P_MUL_ASSIGN, NULL},
- {"/=",P_DIV_ASSIGN, NULL},
- {"%=",P_MOD_ASSIGN, NULL},
- {"+=",P_ADD_ASSIGN, NULL},
- {"-=",P_SUB_ASSIGN, NULL},
- {"++",P_INC, NULL},
- {"--",P_DEC, NULL},
- //binary operators
- {"&=",P_BIN_AND_ASSIGN, NULL},
- {"|=",P_BIN_OR_ASSIGN, NULL},
- {"^=",P_BIN_XOR_ASSIGN, NULL},
- {">>",P_RSHIFT, NULL},
- {"<<",P_LSHIFT, NULL},
- //reference operators
- {"->",P_POINTERREF, NULL},
- //C++
- {"::",P_CPP1, NULL},
- {".*",P_CPP2, NULL},
- //arithmatic operators
- {"*",P_MUL, NULL},
- {"/",P_DIV, NULL},
- {"%",P_MOD, NULL},
- {"+",P_ADD, NULL},
- {"-",P_SUB, NULL},
- {"=",P_ASSIGN, NULL},
- //binary operators
- {"&",P_BIN_AND, NULL},
- {"|",P_BIN_OR, NULL},
- {"^",P_BIN_XOR, NULL},
- {"~",P_BIN_NOT, NULL},
- //logic operators
- {"!",P_LOGIC_NOT, NULL},
- {">",P_LOGIC_GREATER, NULL},
- {"<",P_LOGIC_LESS, NULL},
- //reference operator
- {".",P_REF, NULL},
- //seperators
- {",",P_COMMA, NULL},
- {";",P_SEMICOLON, NULL},
- //label indication
- {":",P_COLON, NULL},
- //if statement
- {"?",P_QUESTIONMARK, NULL},
- //embracements
- {"(",P_PARENTHESESOPEN, NULL},
- {")",P_PARENTHESESCLOSE, NULL},
- {"{",P_BRACEOPEN, NULL},
- {"}",P_BRACECLOSE, NULL},
- {"[",P_SQBRACKETOPEN, NULL},
- {"]",P_SQBRACKETCLOSE, NULL},
- //
- {"\\",P_BACKSLASH, NULL},
- //precompiler operator
- {"#",P_PRECOMP, NULL},
-#ifdef DOLLAR
- {"$",P_DOLLAR, NULL},
-#endif //DOLLAR
- {NULL, 0}
-};
-
-#ifdef BSPC
-char basefolder[MAX_PATH];
-#else
-char basefolder[MAX_QPATH];
-#endif
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations)
-{
- int i;
- punctuation_t *p, *lastp, *newp;
-
- //get memory for the table
- if (!script->punctuationtable) script->punctuationtable = (punctuation_t **)
- GetMemory(256 * sizeof(punctuation_t *));
- Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *));
- //add the punctuations in the list to the punctuation table
- for (i = 0; punctuations[i].p; i++)
- {
- newp = &punctuations[i];
- lastp = NULL;
- //sort the punctuations in this table entry on length (longer punctuations first)
- for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next)
- {
- if (strlen(p->p) < strlen(newp->p))
- {
- newp->next = p;
- if (lastp) lastp->next = newp;
- else script->punctuationtable[(unsigned int) newp->p[0]] = newp;
- break;
- } //end if
- lastp = p;
- } //end for
- if (!p)
- {
- newp->next = NULL;
- if (lastp) lastp->next = newp;
- else script->punctuationtable[(unsigned int) newp->p[0]] = newp;
- } //end if
- } //end for
-} //end of the function PS_CreatePunctuationTable
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-char *PunctuationFromNum(script_t *script, int num)
-{
- int i;
-
- for (i = 0; script->punctuations[i].p; i++)
- {
- if (script->punctuations[i].n == num) return script->punctuations[i].p;
- } //end for
- return "unkown punctuation";
-} //end of the function PunctuationFromNum
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void QDECL ScriptError(script_t *script, char *str, ...)
-{
- char text[1024];
- va_list ap;
-
- if (script->flags & SCFL_NOERRORS) return;
-
- va_start(ap, str);
- vsprintf(text, str, ap);
- va_end(ap);
-#ifdef BOTLIB
- botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text);
-#endif //BOTLIB
-#ifdef MEQCC
- printf("error: file %s, line %d: %s\n", script->filename, script->line, text);
-#endif //MEQCC
-#ifdef BSPC
- Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text);
-#endif //BSPC
-} //end of the function ScriptError
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void QDECL ScriptWarning(script_t *script, char *str, ...)
-{
- char text[1024];
- va_list ap;
-
- if (script->flags & SCFL_NOWARNINGS) return;
-
- va_start(ap, str);
- vsprintf(text, str, ap);
- va_end(ap);
-#ifdef BOTLIB
- botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text);
-#endif //BOTLIB
-#ifdef MEQCC
- printf("warning: file %s, line %d: %s\n", script->filename, script->line, text);
-#endif //MEQCC
-#ifdef BSPC
- Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text);
-#endif //BSPC
-} //end of the function ScriptWarning
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void SetScriptPunctuations(script_t *script, punctuation_t *p)
-{
-#ifdef PUNCTABLE
- if (p) PS_CreatePunctuationTable(script, p);
- else PS_CreatePunctuationTable(script, default_punctuations);
-#endif //PUNCTABLE
- if (p) script->punctuations = p;
- else script->punctuations = default_punctuations;
-} //end of the function SetScriptPunctuations
-//============================================================================
-// Reads spaces, tabs, C-like comments etc.
-// When a newline character is found the scripts line counter is increased.
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadWhiteSpace(script_t *script)
-{
- while(1)
- {
- //skip white space
- while(*script->script_p <= ' ')
- {
- if (!*script->script_p) return 0;
- if (*script->script_p == '\n') script->line++;
- script->script_p++;
- } //end while
- //skip comments
- if (*script->script_p == '/')
- {
- //comments //
- if (*(script->script_p+1) == '/')
- {
- script->script_p++;
- do
- {
- script->script_p++;
- if (!*script->script_p) return 0;
- } //end do
- while(*script->script_p != '\n');
- script->line++;
- script->script_p++;
- if (!*script->script_p) return 0;
- continue;
- } //end if
- //comments /* */
- else if (*(script->script_p+1) == '*')
- {
- script->script_p++;
- do
- {
- script->script_p++;
- if (!*script->script_p) return 0;
- if (*script->script_p == '\n') script->line++;
- } //end do
- while(!(*script->script_p == '*' && *(script->script_p+1) == '/'));
- script->script_p++;
- if (!*script->script_p) return 0;
- script->script_p++;
- if (!*script->script_p) return 0;
- continue;
- } //end if
- } //end if
- break;
- } //end while
- return 1;
-} //end of the function PS_ReadWhiteSpace
-//============================================================================
-// Reads an escape character.
-//
-// Parameter: script : script to read from
-// ch : place to store the read escape character
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadEscapeCharacter(script_t *script, char *ch)
-{
- int c, val, i;
-
- //step over the leading '\\'
- script->script_p++;
- //determine the escape character
- switch(*script->script_p)
- {
- case '\\': c = '\\'; break;
- case 'n': c = '\n'; break;
- case 'r': c = '\r'; break;
- case 't': c = '\t'; break;
- case 'v': c = '\v'; break;
- case 'b': c = '\b'; break;
- case 'f': c = '\f'; break;
- case 'a': c = '\a'; break;
- case '\'': c = '\''; break;
- case '\"': c = '\"'; break;
- case '\?': c = '\?'; break;
- case 'x':
- {
- script->script_p++;
- for (i = 0, val = 0; ; i++, script->script_p++)
- {
- c = *script->script_p;
- if (c >= '0' && c <= '9') c = c - '0';
- else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10;
- else if (c >= 'a' && c <= 'z') c = c - 'a' + 10;
- else break;
- val = (val << 4) + c;
- } //end for
- script->script_p--;
- if (val > 0xFF)
- {
- ScriptWarning(script, "too large value in escape character");
- val = 0xFF;
- } //end if
- c = val;
- break;
- } //end case
- default: //NOTE: decimal ASCII code, NOT octal
- {
- if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char");
- for (i = 0, val = 0; ; i++, script->script_p++)
- {
- c = *script->script_p;
- if (c >= '0' && c <= '9') c = c - '0';
- else break;
- val = val * 10 + c;
- } //end for
- script->script_p--;
- if (val > 0xFF)
- {
- ScriptWarning(script, "too large value in escape character");
- val = 0xFF;
- } //end if
- c = val;
- break;
- } //end default
- } //end switch
- //step over the escape character or the last digit of the number
- script->script_p++;
- //store the escape character
- *ch = c;
- //succesfully read escape character
- return 1;
-} //end of the function PS_ReadEscapeCharacter
-//============================================================================
-// Reads C-like string. Escape characters are interpretted.
-// Quotes are included with the string.
-// Reads two strings with a white space between them as one string.
-//
-// Parameter: script : script to read from
-// token : buffer to store the string
-// Returns: qtrue when a string was read succesfully
-// Changes Globals: -
-//============================================================================
-int PS_ReadString(script_t *script, token_t *token, int quote)
-{
- int len, tmpline;
- char *tmpscript_p;
-
- if (quote == '\"') token->type = TT_STRING;
- else token->type = TT_LITERAL;
-
- len = 0;
- //leading quote
- token->string[len++] = *script->script_p++;
- //
- while(1)
- {
- //minus 2 because trailing double quote and zero have to be appended
- if (len >= MAX_TOKEN - 2)
- {
- ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN);
- return 0;
- } //end if
- //if there is an escape character and
- //if escape characters inside a string are allowed
- if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS))
- {
- if (!PS_ReadEscapeCharacter(script, &token->string[len]))
- {
- token->string[len] = 0;
- return 0;
- } //end if
- len++;
- } //end if
- //if a trailing quote
- else if (*script->script_p == quote)
- {
- //step over the double quote
- script->script_p++;
- //if white spaces in a string are not allowed
- if (script->flags & SCFL_NOSTRINGWHITESPACES) break;
- //
- tmpscript_p = script->script_p;
- tmpline = script->line;
- //read unusefull stuff between possible two following strings
- if (!PS_ReadWhiteSpace(script))
- {
- script->script_p = tmpscript_p;
- script->line = tmpline;
- break;
- } //end if
- //if there's no leading double qoute
- if (*script->script_p != quote)
- {
- script->script_p = tmpscript_p;
- script->line = tmpline;
- break;
- } //end if
- //step over the new leading double quote
- script->script_p++;
- } //end if
- else
- {
- if (*script->script_p == '\0')
- {
- token->string[len] = 0;
- ScriptError(script, "missing trailing quote");
- return 0;
- } //end if
- if (*script->script_p == '\n')
- {
- token->string[len] = 0;
- ScriptError(script, "newline inside string %s", token->string);
- return 0;
- } //end if
- token->string[len++] = *script->script_p++;
- } //end else
- } //end while
- //trailing quote
- token->string[len++] = quote;
- //end string with a zero
- token->string[len] = '\0';
- //the sub type is the length of the string
- token->subtype = len;
- return 1;
-} //end of the function PS_ReadString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadName(script_t *script, token_t *token)
-{
- int len = 0;
- char c;
-
- token->type = TT_NAME;
- do
- {
- token->string[len++] = *script->script_p++;
- if (len >= MAX_TOKEN)
- {
- ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN);
- return 0;
- } //end if
- c = *script->script_p;
- } while ((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '_');
- token->string[len] = '\0';
- //the sub type is the length of the name
- token->subtype = len;
- return 1;
-} //end of the function PS_ReadName
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void NumberValue(char *string, int subtype, unsigned long int *intvalue,
- long double *floatvalue)
-{
- unsigned long int dotfound = 0;
-
- *intvalue = 0;
- *floatvalue = 0;
- //floating point number
- if (subtype & TT_FLOAT)
- {
- while(*string)
- {
- if (*string == '.')
- {
- if (dotfound) return;
- dotfound = 10;
- string++;
- } //end if
- if (dotfound)
- {
- *floatvalue = *floatvalue + (long double) (*string - '0') /
- (long double) dotfound;
- dotfound *= 10;
- } //end if
- else
- {
- *floatvalue = *floatvalue * 10.0 + (long double) (*string - '0');
- } //end else
- string++;
- } //end while
- *intvalue = (unsigned long) *floatvalue;
- } //end if
- else if (subtype & TT_DECIMAL)
- {
- while(*string) *intvalue = *intvalue * 10 + (*string++ - '0');
- *floatvalue = *intvalue;
- } //end else if
- else if (subtype & TT_HEX)
- {
- //step over the leading 0x or 0X
- string += 2;
- while(*string)
- {
- *intvalue <<= 4;
- if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10;
- else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10;
- else *intvalue += *string - '0';
- string++;
- } //end while
- *floatvalue = *intvalue;
- } //end else if
- else if (subtype & TT_OCTAL)
- {
- //step over the first zero
- string += 1;
- while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0');
- *floatvalue = *intvalue;
- } //end else if
- else if (subtype & TT_BINARY)
- {
- //step over the leading 0b or 0B
- string += 2;
- while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0');
- *floatvalue = *intvalue;
- } //end else if
-} //end of the function NumberValue
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadNumber(script_t *script, token_t *token)
-{
- int len = 0, i;
- int octal, dot;
- char c;
-// unsigned long int intvalue = 0;
-// long double floatvalue = 0;
-
- token->type = TT_NUMBER;
- //check for a hexadecimal number
- if (*script->script_p == '0' &&
- (*(script->script_p + 1) == 'x' ||
- *(script->script_p + 1) == 'X'))
- {
- token->string[len++] = *script->script_p++;
- token->string[len++] = *script->script_p++;
- c = *script->script_p;
- //hexadecimal
- while((c >= '0' && c <= '9') ||
- (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'A'))
- {
- token->string[len++] = *script->script_p++;
- if (len >= MAX_TOKEN)
- {
- ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN);
- return 0;
- } //end if
- c = *script->script_p;
- } //end while
- token->subtype |= TT_HEX;
- } //end if
-#ifdef BINARYNUMBERS
- //check for a binary number
- else if (*script->script_p == '0' &&
- (*(script->script_p + 1) == 'b' ||
- *(script->script_p + 1) == 'B'))
- {
- token->string[len++] = *script->script_p++;
- token->string[len++] = *script->script_p++;
- c = *script->script_p;
- //binary
- while(c == '0' || c == '1')
- {
- token->string[len++] = *script->script_p++;
- if (len >= MAX_TOKEN)
- {
- ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN);
- return 0;
- } //end if
- c = *script->script_p;
- } //end while
- token->subtype |= TT_BINARY;
- } //end if
-#endif //BINARYNUMBERS
- else //decimal or octal integer or floating point number
- {
- octal = qfalse;
- dot = qfalse;
- if (*script->script_p == '0') octal = qtrue;
- while(1)
- {
- c = *script->script_p;
- if (c == '.') dot = qtrue;
- else if (c == '8' || c == '9') octal = qfalse;
- else if (c < '0' || c > '9') break;
- token->string[len++] = *script->script_p++;
- if (len >= MAX_TOKEN - 1)
- {
- ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN);
- return 0;
- } //end if
- } //end while
- if (octal) token->subtype |= TT_OCTAL;
- else token->subtype |= TT_DECIMAL;
- if (dot) token->subtype |= TT_FLOAT;
- } //end else
- for (i = 0; i < 2; i++)
- {
- c = *script->script_p;
- //check for a LONG number
- if ( (c == 'l' || c == 'L') // bk001204 - brackets
- && !(token->subtype & TT_LONG))
- {
- script->script_p++;
- token->subtype |= TT_LONG;
- } //end if
- //check for an UNSIGNED number
- else if ( (c == 'u' || c == 'U') // bk001204 - brackets
- && !(token->subtype & (TT_UNSIGNED | TT_FLOAT)))
- {
- script->script_p++;
- token->subtype |= TT_UNSIGNED;
- } //end if
- } //end for
- token->string[len] = '\0';
-#ifdef NUMBERVALUE
- NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue);
-#endif //NUMBERVALUE
- if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER;
- return 1;
-} //end of the function PS_ReadNumber
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadLiteral(script_t *script, token_t *token)
-{
- token->type = TT_LITERAL;
- //first quote
- token->string[0] = *script->script_p++;
- //check for end of file
- if (!*script->script_p)
- {
- ScriptError(script, "end of file before trailing \'");
- return 0;
- } //end if
- //if it is an escape character
- if (*script->script_p == '\\')
- {
- if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0;
- } //end if
- else
- {
- token->string[1] = *script->script_p++;
- } //end else
- //check for trailing quote
- if (*script->script_p != '\'')
- {
- ScriptWarning(script, "too many characters in literal, ignored");
- while(*script->script_p &&
- *script->script_p != '\'' &&
- *script->script_p != '\n')
- {
- script->script_p++;
- } //end while
- if (*script->script_p == '\'') script->script_p++;
- } //end if
- //store the trailing quote
- token->string[2] = *script->script_p++;
- //store trailing zero to end the string
- token->string[3] = '\0';
- //the sub type is the integer literal value
- token->subtype = token->string[1];
- //
- return 1;
-} //end of the function PS_ReadLiteral
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadPunctuation(script_t *script, token_t *token)
-{
- int len;
- char *p;
- punctuation_t *punc;
-
-#ifdef PUNCTABLE
- for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next)
- {
-#else
- int i;
-
- for (i = 0; script->punctuations[i].p; i++)
- {
- punc = &script->punctuations[i];
-#endif //PUNCTABLE
- p = punc->p;
- len = strlen(p);
- //if the script contains at least as much characters as the punctuation
- if (script->script_p + len <= script->end_p)
- {
- //if the script contains the punctuation
- if (!strncmp(script->script_p, p, len))
- {
- strncpy(token->string, p, MAX_TOKEN);
- script->script_p += len;
- token->type = TT_PUNCTUATION;
- //sub type is the number of the punctuation
- token->subtype = punc->n;
- return 1;
- } //end if
- } //end if
- } //end for
- return 0;
-} //end of the function PS_ReadPunctuation
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadPrimitive(script_t *script, token_t *token)
-{
- int len;
-
- len = 0;
- while(*script->script_p > ' ' && *script->script_p != ';')
- {
- if (len >= MAX_TOKEN)
- {
- ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN);
- return 0;
- } //end if
- token->string[len++] = *script->script_p++;
- } //end while
- token->string[len] = 0;
- //copy the token into the script structure
- Com_Memcpy(&script->token, token, sizeof(token_t));
- //primitive reading successfull
- return 1;
-} //end of the function PS_ReadPrimitive
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ReadToken(script_t *script, token_t *token)
-{
- //if there is a token available (from UnreadToken)
- if (script->tokenavailable)
- {
- script->tokenavailable = 0;
- Com_Memcpy(token, &script->token, sizeof(token_t));
- return 1;
- } //end if
- //save script pointer
- script->lastscript_p = script->script_p;
- //save line counter
- script->lastline = script->line;
- //clear the token stuff
- Com_Memset(token, 0, sizeof(token_t));
- //start of the white space
- script->whitespace_p = script->script_p;
- token->whitespace_p = script->script_p;
- //read unusefull stuff
- if (!PS_ReadWhiteSpace(script)) return 0;
- //end of the white space
- script->endwhitespace_p = script->script_p;
- token->endwhitespace_p = script->script_p;
- //line the token is on
- token->line = script->line;
- //number of lines crossed before token
- token->linescrossed = script->line - script->lastline;
- //if there is a leading double quote
- if (*script->script_p == '\"')
- {
- if (!PS_ReadString(script, token, '\"')) return 0;
- } //end if
- //if an literal
- else if (*script->script_p == '\'')
- {
- //if (!PS_ReadLiteral(script, token)) return 0;
- if (!PS_ReadString(script, token, '\'')) return 0;
- } //end if
- //if there is a number
- else if ((*script->script_p >= '0' && *script->script_p <= '9') ||
- (*script->script_p == '.' &&
- (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9')))
- {
- if (!PS_ReadNumber(script, token)) return 0;
- } //end if
- //if this is a primitive script
- else if (script->flags & SCFL_PRIMITIVE)
- {
- return PS_ReadPrimitive(script, token);
- } //end else if
- //if there is a name
- else if ((*script->script_p >= 'a' && *script->script_p <= 'z') ||
- (*script->script_p >= 'A' && *script->script_p <= 'Z') ||
- *script->script_p == '_')
- {
- if (!PS_ReadName(script, token)) return 0;
- } //end if
- //check for punctuations
- else if (!PS_ReadPunctuation(script, token))
- {
- ScriptError(script, "can't read token");
- return 0;
- } //end if
- //copy the token into the script structure
- Com_Memcpy(&script->token, token, sizeof(token_t));
- //succesfully read a token
- return 1;
-} //end of the function PS_ReadToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ExpectTokenString(script_t *script, char *string)
-{
- token_t token;
-
- if (!PS_ReadToken(script, &token))
- {
- ScriptError(script, "couldn't find expected %s", string);
- return 0;
- } //end if
-
- if (strcmp(token.string, string))
- {
- ScriptError(script, "expected %s, found %s", string, token.string);
- return 0;
- } //end if
- return 1;
-} //end of the function PS_ExpectToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token)
-{
- char str[MAX_TOKEN];
-
- if (!PS_ReadToken(script, token))
- {
- ScriptError(script, "couldn't read expected token");
- return 0;
- } //end if
-
- if (token->type != type)
- {
- if (type == TT_STRING) strcpy(str, "string");
- if (type == TT_LITERAL) strcpy(str, "literal");
- if (type == TT_NUMBER) strcpy(str, "number");
- if (type == TT_NAME) strcpy(str, "name");
- if (type == TT_PUNCTUATION) strcpy(str, "punctuation");
- ScriptError(script, "expected a %s, found %s", str, token->string);
- return 0;
- } //end if
- if (token->type == TT_NUMBER)
- {
- if ((token->subtype & subtype) != subtype)
- {
- if (subtype & TT_DECIMAL) strcpy(str, "decimal");
- if (subtype & TT_HEX) strcpy(str, "hex");
- if (subtype & TT_OCTAL) strcpy(str, "octal");
- if (subtype & TT_BINARY) strcpy(str, "binary");
- if (subtype & TT_LONG) strcat(str, " long");
- if (subtype & TT_UNSIGNED) strcat(str, " unsigned");
- if (subtype & TT_FLOAT) strcat(str, " float");
- if (subtype & TT_INTEGER) strcat(str, " integer");
- ScriptError(script, "expected %s, found %s", str, token->string);
- return 0;
- } //end if
- } //end if
- else if (token->type == TT_PUNCTUATION)
- {
- if (subtype < 0)
- {
- ScriptError(script, "BUG: wrong punctuation subtype");
- return 0;
- } //end if
- if (token->subtype != subtype)
- {
- ScriptError(script, "expected %s, found %s",
- script->punctuations[subtype], token->string);
- return 0;
- } //end if
- } //end else if
- return 1;
-} //end of the function PS_ExpectTokenType
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_ExpectAnyToken(script_t *script, token_t *token)
-{
- if (!PS_ReadToken(script, token))
- {
- ScriptError(script, "couldn't read expected token");
- return 0;
- } //end if
- else
- {
- return 1;
- } //end else
-} //end of the function PS_ExpectAnyToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_CheckTokenString(script_t *script, char *string)
-{
- token_t tok;
-
- if (!PS_ReadToken(script, &tok)) return 0;
- //if the token is available
- if (!strcmp(tok.string, string)) return 1;
- //token not available
- script->script_p = script->lastscript_p;
- return 0;
-} //end of the function PS_CheckTokenString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token)
-{
- token_t tok;
-
- if (!PS_ReadToken(script, &tok)) return 0;
- //if the type matches
- if (tok.type == type &&
- (tok.subtype & subtype) == subtype)
- {
- Com_Memcpy(token, &tok, sizeof(token_t));
- return 1;
- } //end if
- //token is not available
- script->script_p = script->lastscript_p;
- return 0;
-} //end of the function PS_CheckTokenType
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int PS_SkipUntilString(script_t *script, char *string)
-{
- token_t token;
-
- while(PS_ReadToken(script, &token))
- {
- if (!strcmp(token.string, string)) return 1;
- } //end while
- return 0;
-} //end of the function PS_SkipUntilString
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PS_UnreadLastToken(script_t *script)
-{
- script->tokenavailable = 1;
-} //end of the function UnreadLastToken
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PS_UnreadToken(script_t *script, token_t *token)
-{
- Com_Memcpy(&script->token, token, sizeof(token_t));
- script->tokenavailable = 1;
-} //end of the function UnreadToken
-//============================================================================
-// returns the next character of the read white space, returns NULL if none
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-char PS_NextWhiteSpaceChar(script_t *script)
-{
- if (script->whitespace_p != script->endwhitespace_p)
- {
- return *script->whitespace_p++;
- } //end if
- else
- {
- return 0;
- } //end else
-} //end of the function PS_NextWhiteSpaceChar
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void StripDoubleQuotes(char *string)
-{
- if (*string == '\"')
- {
- strcpy(string, string+1);
- } //end if
- if (string[strlen(string)-1] == '\"')
- {
- string[strlen(string)-1] = '\0';
- } //end if
-} //end of the function StripDoubleQuotes
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void StripSingleQuotes(char *string)
-{
- if (*string == '\'')
- {
- strcpy(string, string+1);
- } //end if
- if (string[strlen(string)-1] == '\'')
- {
- string[strlen(string)-1] = '\0';
- } //end if
-} //end of the function StripSingleQuotes
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-long double ReadSignedFloat(script_t *script)
-{
- token_t token;
- long double sign = 1;
-
- PS_ExpectAnyToken(script, &token);
- if (!strcmp(token.string, "-"))
- {
- sign = -1;
- PS_ExpectTokenType(script, TT_NUMBER, 0, &token);
- } //end if
- else if (token.type != TT_NUMBER)
- {
- ScriptError(script, "expected float value, found %s\n", token.string);
- } //end else if
- return sign * token.floatvalue;
-} //end of the function ReadSignedFloat
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-signed long int ReadSignedInt(script_t *script)
-{
- token_t token;
- signed long int sign = 1;
-
- PS_ExpectAnyToken(script, &token);
- if (!strcmp(token.string, "-"))
- {
- sign = -1;
- PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token);
- } //end if
- else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT)
- {
- ScriptError(script, "expected integer value, found %s\n", token.string);
- } //end else if
- return sign * token.intvalue;
-} //end of the function ReadSignedInt
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void SetScriptFlags(script_t *script, int flags)
-{
- script->flags = flags;
-} //end of the function SetScriptFlags
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int GetScriptFlags(script_t *script)
-{
- return script->flags;
-} //end of the function GetScriptFlags
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void ResetScript(script_t *script)
-{
- //pointer in script buffer
- script->script_p = script->buffer;
- //pointer in script buffer before reading token
- script->lastscript_p = script->buffer;
- //begin of white space
- script->whitespace_p = NULL;
- //end of white space
- script->endwhitespace_p = NULL;
- //set if there's a token available in script->token
- script->tokenavailable = 0;
- //
- script->line = 1;
- script->lastline = 1;
- //clear the saved token
- Com_Memset(&script->token, 0, sizeof(token_t));
-} //end of the function ResetScript
-//============================================================================
-// returns true if at the end of the script
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int EndOfScript(script_t *script)
-{
- return script->script_p >= script->end_p;
-} //end of the function EndOfScript
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int NumLinesCrossed(script_t *script)
-{
- return script->line - script->lastline;
-} //end of the function NumLinesCrossed
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int ScriptSkipTo(script_t *script, char *value)
-{
- int len;
- char firstchar;
-
- firstchar = *value;
- len = strlen(value);
- do
- {
- if (!PS_ReadWhiteSpace(script)) return 0;
- if (*script->script_p == firstchar)
- {
- if (!strncmp(script->script_p, value, len))
- {
- return 1;
- } //end if
- } //end if
- script->script_p++;
- } while(1);
-} //end of the function ScriptSkipTo
-#ifndef BOTLIB
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-int FileLength(FILE *fp)
-{
- int pos;
- int end;
-
- pos = ftell(fp);
- fseek(fp, 0, SEEK_END);
- end = ftell(fp);
- fseek(fp, pos, SEEK_SET);
-
- return end;
-} //end of the function FileLength
-#endif
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-script_t *LoadScriptFile(const char *filename)
-{
-#ifdef BOTLIB
- fileHandle_t fp;
- char pathname[MAX_QPATH];
-#else
- FILE *fp;
-#endif
- int length;
- void *buffer;
- script_t *script;
-
-#ifdef BOTLIB
- if (strlen(basefolder))
- Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename);
- else
- Com_sprintf(pathname, sizeof(pathname), "%s", filename);
- length = botimport.FS_FOpenFile( pathname, &fp, FS_READ );
- if (!fp) return NULL;
-#else
- fp = fopen(filename, "rb");
- if (!fp) return NULL;
-
- length = FileLength(fp);
-#endif
-
- buffer = GetClearedMemory(sizeof(script_t) + length + 1);
- script = (script_t *) buffer;
- Com_Memset(script, 0, sizeof(script_t));
- strcpy(script->filename, filename);
- script->buffer = (char *) buffer + sizeof(script_t);
- script->buffer[length] = 0;
- script->length = length;
- //pointer in script buffer
- script->script_p = script->buffer;
- //pointer in script buffer before reading token
- script->lastscript_p = script->buffer;
- //pointer to end of script buffer
- script->end_p = &script->buffer[length];
- //set if there's a token available in script->token
- script->tokenavailable = 0;
- //
- script->line = 1;
- script->lastline = 1;
- //
- SetScriptPunctuations(script, NULL);
- //
-#ifdef BOTLIB
- botimport.FS_Read(script->buffer, length, fp);
- botimport.FS_FCloseFile(fp);
-#else
- if (fread(script->buffer, length, 1, fp) != 1)
- {
- FreeMemory(buffer);
- script = NULL;
- } //end if
- fclose(fp);
-#endif
- //
- script->length = COM_Compress(script->buffer);
-
- return script;
-} //end of the function LoadScriptFile
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-script_t *LoadScriptMemory(char *ptr, int length, char *name)
-{
- void *buffer;
- script_t *script;
-
- buffer = GetClearedMemory(sizeof(script_t) + length + 1);
- script = (script_t *) buffer;
- Com_Memset(script, 0, sizeof(script_t));
- strcpy(script->filename, name);
- script->buffer = (char *) buffer + sizeof(script_t);
- script->buffer[length] = 0;
- script->length = length;
- //pointer in script buffer
- script->script_p = script->buffer;
- //pointer in script buffer before reading token
- script->lastscript_p = script->buffer;
- //pointer to end of script buffer
- script->end_p = &script->buffer[length];
- //set if there's a token available in script->token
- script->tokenavailable = 0;
- //
- script->line = 1;
- script->lastline = 1;
- //
- SetScriptPunctuations(script, NULL);
- //
- Com_Memcpy(script->buffer, ptr, length);
- //
- return script;
-} //end of the function LoadScriptMemory
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void FreeScript(script_t *script)
-{
-#ifdef PUNCTABLE
- if (script->punctuationtable) FreeMemory(script->punctuationtable);
-#endif //PUNCTABLE
- FreeMemory(script);
-} //end of the function FreeScript
-//============================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//============================================================================
-void PS_SetBaseFolder(char *path)
-{
-#ifdef BSPC
- sprintf(basefolder, path);
-#else
- Com_sprintf(basefolder, sizeof(basefolder), path);
-#endif
-} //end of the function PS_SetBaseFolder
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_script.c + * + * desc: lexicographical parser + * + * $Archive: /MissionPack/code/botlib/l_script.c $ + * + *****************************************************************************/ + +//#define SCREWUP +//#define BOTLIB +//#define MEQCC +//#define BSPC + +#ifdef SCREWUP +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <stdarg.h> +#include "l_memory.h" +#include "l_script.h" + +typedef enum {qfalse, qtrue} qboolean; + +#endif //SCREWUP + +#ifdef BOTLIB +//include files for usage in the bot library +#include "../game/q_shared.h" +#include "../game/botlib.h" +#include "be_interface.h" +#include "l_script.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_libvar.h" +#endif //BOTLIB + +#ifdef MEQCC +//include files for usage in MrElusive's QuakeC Compiler +#include "qcc.h" +#include "l_script.h" +#include "l_memory.h" +#include "l_log.h" + +#define qtrue true +#define qfalse false +#endif //MEQCC + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" + +#define qtrue true +#define qfalse false +#endif //BSPC + + +#define PUNCTABLE + +//longer punctuations first +punctuation_t default_punctuations[] = +{ + //binary operators + {">>=",P_RSHIFT_ASSIGN, NULL}, + {"<<=",P_LSHIFT_ASSIGN, NULL}, + // + {"...",P_PARMS, NULL}, + //define merge operator + {"##",P_PRECOMPMERGE, NULL}, + //logic operators + {"&&",P_LOGIC_AND, NULL}, + {"||",P_LOGIC_OR, NULL}, + {">=",P_LOGIC_GEQ, NULL}, + {"<=",P_LOGIC_LEQ, NULL}, + {"==",P_LOGIC_EQ, NULL}, + {"!=",P_LOGIC_UNEQ, NULL}, + //arithmatic operators + {"*=",P_MUL_ASSIGN, NULL}, + {"/=",P_DIV_ASSIGN, NULL}, + {"%=",P_MOD_ASSIGN, NULL}, + {"+=",P_ADD_ASSIGN, NULL}, + {"-=",P_SUB_ASSIGN, NULL}, + {"++",P_INC, NULL}, + {"--",P_DEC, NULL}, + //binary operators + {"&=",P_BIN_AND_ASSIGN, NULL}, + {"|=",P_BIN_OR_ASSIGN, NULL}, + {"^=",P_BIN_XOR_ASSIGN, NULL}, + {">>",P_RSHIFT, NULL}, + {"<<",P_LSHIFT, NULL}, + //reference operators + {"->",P_POINTERREF, NULL}, + //C++ + {"::",P_CPP1, NULL}, + {".*",P_CPP2, NULL}, + //arithmatic operators + {"*",P_MUL, NULL}, + {"/",P_DIV, NULL}, + {"%",P_MOD, NULL}, + {"+",P_ADD, NULL}, + {"-",P_SUB, NULL}, + {"=",P_ASSIGN, NULL}, + //binary operators + {"&",P_BIN_AND, NULL}, + {"|",P_BIN_OR, NULL}, + {"^",P_BIN_XOR, NULL}, + {"~",P_BIN_NOT, NULL}, + //logic operators + {"!",P_LOGIC_NOT, NULL}, + {">",P_LOGIC_GREATER, NULL}, + {"<",P_LOGIC_LESS, NULL}, + //reference operator + {".",P_REF, NULL}, + //seperators + {",",P_COMMA, NULL}, + {";",P_SEMICOLON, NULL}, + //label indication + {":",P_COLON, NULL}, + //if statement + {"?",P_QUESTIONMARK, NULL}, + //embracements + {"(",P_PARENTHESESOPEN, NULL}, + {")",P_PARENTHESESCLOSE, NULL}, + {"{",P_BRACEOPEN, NULL}, + {"}",P_BRACECLOSE, NULL}, + {"[",P_SQBRACKETOPEN, NULL}, + {"]",P_SQBRACKETCLOSE, NULL}, + // + {"\\",P_BACKSLASH, NULL}, + //precompiler operator + {"#",P_PRECOMP, NULL}, +#ifdef DOLLAR + {"$",P_DOLLAR, NULL}, +#endif //DOLLAR + {NULL, 0} +}; + +#ifdef BSPC +char basefolder[MAX_PATH]; +#else +char basefolder[MAX_QPATH]; +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations) +{ + int i; + punctuation_t *p, *lastp, *newp; + + //get memory for the table + if (!script->punctuationtable) script->punctuationtable = (punctuation_t **) + GetMemory(256 * sizeof(punctuation_t *)); + Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *)); + //add the punctuations in the list to the punctuation table + for (i = 0; punctuations[i].p; i++) + { + newp = &punctuations[i]; + lastp = NULL; + //sort the punctuations in this table entry on length (longer punctuations first) + for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next) + { + if (strlen(p->p) < strlen(newp->p)) + { + newp->next = p; + if (lastp) lastp->next = newp; + else script->punctuationtable[(unsigned int) newp->p[0]] = newp; + break; + } //end if + lastp = p; + } //end for + if (!p) + { + newp->next = NULL; + if (lastp) lastp->next = newp; + else script->punctuationtable[(unsigned int) newp->p[0]] = newp; + } //end if + } //end for +} //end of the function PS_CreatePunctuationTable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *PunctuationFromNum(script_t *script, int num) +{ + int i; + + for (i = 0; script->punctuations[i].p; i++) + { + if (script->punctuations[i].n == num) return script->punctuations[i].p; + } //end for + return "unkown punctuation"; +} //end of the function PunctuationFromNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL ScriptError(script_t *script, char *str, ...) +{ + char text[1024]; + va_list ap; + + if (script->flags & SCFL_NOERRORS) return; + + va_start(ap, str); + vsprintf(text, str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("error: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BSPC +} //end of the function ScriptError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL ScriptWarning(script_t *script, char *str, ...) +{ + char text[1024]; + va_list ap; + + if (script->flags & SCFL_NOWARNINGS) return; + + va_start(ap, str); + vsprintf(text, str, ap); + va_end(ap); +#ifdef BOTLIB + botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BOTLIB +#ifdef MEQCC + printf("warning: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //MEQCC +#ifdef BSPC + Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text); +#endif //BSPC +} //end of the function ScriptWarning +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SetScriptPunctuations(script_t *script, punctuation_t *p) +{ +#ifdef PUNCTABLE + if (p) PS_CreatePunctuationTable(script, p); + else PS_CreatePunctuationTable(script, default_punctuations); +#endif //PUNCTABLE + if (p) script->punctuations = p; + else script->punctuations = default_punctuations; +} //end of the function SetScriptPunctuations +//============================================================================ +// Reads spaces, tabs, C-like comments etc. +// When a newline character is found the scripts line counter is increased. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadWhiteSpace(script_t *script) +{ + while(1) + { + //skip white space + while(*script->script_p <= ' ') + { + if (!*script->script_p) return 0; + if (*script->script_p == '\n') script->line++; + script->script_p++; + } //end while + //skip comments + if (*script->script_p == '/') + { + //comments // + if (*(script->script_p+1) == '/') + { + script->script_p++; + do + { + script->script_p++; + if (!*script->script_p) return 0; + } //end do + while(*script->script_p != '\n'); + script->line++; + script->script_p++; + if (!*script->script_p) return 0; + continue; + } //end if + //comments /* */ + else if (*(script->script_p+1) == '*') + { + script->script_p++; + do + { + script->script_p++; + if (!*script->script_p) return 0; + if (*script->script_p == '\n') script->line++; + } //end do + while(!(*script->script_p == '*' && *(script->script_p+1) == '/')); + script->script_p++; + if (!*script->script_p) return 0; + script->script_p++; + if (!*script->script_p) return 0; + continue; + } //end if + } //end if + break; + } //end while + return 1; +} //end of the function PS_ReadWhiteSpace +//============================================================================ +// Reads an escape character. +// +// Parameter: script : script to read from +// ch : place to store the read escape character +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadEscapeCharacter(script_t *script, char *ch) +{ + int c, val, i; + + //step over the leading '\\' + script->script_p++; + //determine the escape character + switch(*script->script_p) + { + case '\\': c = '\\'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'a': c = '\a'; break; + case '\'': c = '\''; break; + case '\"': c = '\"'; break; + case '\?': c = '\?'; break; + case 'x': + { + script->script_p++; + for (i = 0, val = 0; ; i++, script->script_p++) + { + c = *script->script_p; + if (c >= '0' && c <= '9') c = c - '0'; + else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') c = c - 'a' + 10; + else break; + val = (val << 4) + c; + } //end for + script->script_p--; + if (val > 0xFF) + { + ScriptWarning(script, "too large value in escape character"); + val = 0xFF; + } //end if + c = val; + break; + } //end case + default: //NOTE: decimal ASCII code, NOT octal + { + if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char"); + for (i = 0, val = 0; ; i++, script->script_p++) + { + c = *script->script_p; + if (c >= '0' && c <= '9') c = c - '0'; + else break; + val = val * 10 + c; + } //end for + script->script_p--; + if (val > 0xFF) + { + ScriptWarning(script, "too large value in escape character"); + val = 0xFF; + } //end if + c = val; + break; + } //end default + } //end switch + //step over the escape character or the last digit of the number + script->script_p++; + //store the escape character + *ch = c; + //succesfully read escape character + return 1; +} //end of the function PS_ReadEscapeCharacter +//============================================================================ +// Reads C-like string. Escape characters are interpretted. +// Quotes are included with the string. +// Reads two strings with a white space between them as one string. +// +// Parameter: script : script to read from +// token : buffer to store the string +// Returns: qtrue when a string was read succesfully +// Changes Globals: - +//============================================================================ +int PS_ReadString(script_t *script, token_t *token, int quote) +{ + int len, tmpline; + char *tmpscript_p; + + if (quote == '\"') token->type = TT_STRING; + else token->type = TT_LITERAL; + + len = 0; + //leading quote + token->string[len++] = *script->script_p++; + // + while(1) + { + //minus 2 because trailing double quote and zero have to be appended + if (len >= MAX_TOKEN - 2) + { + ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + //if there is an escape character and + //if escape characters inside a string are allowed + if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS)) + { + if (!PS_ReadEscapeCharacter(script, &token->string[len])) + { + token->string[len] = 0; + return 0; + } //end if + len++; + } //end if + //if a trailing quote + else if (*script->script_p == quote) + { + //step over the double quote + script->script_p++; + //if white spaces in a string are not allowed + if (script->flags & SCFL_NOSTRINGWHITESPACES) break; + // + tmpscript_p = script->script_p; + tmpline = script->line; + //read unusefull stuff between possible two following strings + if (!PS_ReadWhiteSpace(script)) + { + script->script_p = tmpscript_p; + script->line = tmpline; + break; + } //end if + //if there's no leading double qoute + if (*script->script_p != quote) + { + script->script_p = tmpscript_p; + script->line = tmpline; + break; + } //end if + //step over the new leading double quote + script->script_p++; + } //end if + else + { + if (*script->script_p == '\0') + { + token->string[len] = 0; + ScriptError(script, "missing trailing quote"); + return 0; + } //end if + if (*script->script_p == '\n') + { + token->string[len] = 0; + ScriptError(script, "newline inside string %s", token->string); + return 0; + } //end if + token->string[len++] = *script->script_p++; + } //end else + } //end while + //trailing quote + token->string[len++] = quote; + //end string with a zero + token->string[len] = '\0'; + //the sub type is the length of the string + token->subtype = len; + return 1; +} //end of the function PS_ReadString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadName(script_t *script, token_t *token) +{ + int len = 0; + char c; + + token->type = TT_NAME; + do + { + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN) + { + ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + c = *script->script_p; + } while ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '_'); + token->string[len] = '\0'; + //the sub type is the length of the name + token->subtype = len; + return 1; +} //end of the function PS_ReadName +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void NumberValue(char *string, int subtype, unsigned long int *intvalue, + long double *floatvalue) +{ + unsigned long int dotfound = 0; + + *intvalue = 0; + *floatvalue = 0; + //floating point number + if (subtype & TT_FLOAT) + { + while(*string) + { + if (*string == '.') + { + if (dotfound) return; + dotfound = 10; + string++; + } //end if + if (dotfound) + { + *floatvalue = *floatvalue + (long double) (*string - '0') / + (long double) dotfound; + dotfound *= 10; + } //end if + else + { + *floatvalue = *floatvalue * 10.0 + (long double) (*string - '0'); + } //end else + string++; + } //end while + *intvalue = (unsigned long) *floatvalue; + } //end if + else if (subtype & TT_DECIMAL) + { + while(*string) *intvalue = *intvalue * 10 + (*string++ - '0'); + *floatvalue = *intvalue; + } //end else if + else if (subtype & TT_HEX) + { + //step over the leading 0x or 0X + string += 2; + while(*string) + { + *intvalue <<= 4; + if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10; + else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10; + else *intvalue += *string - '0'; + string++; + } //end while + *floatvalue = *intvalue; + } //end else if + else if (subtype & TT_OCTAL) + { + //step over the first zero + string += 1; + while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0'); + *floatvalue = *intvalue; + } //end else if + else if (subtype & TT_BINARY) + { + //step over the leading 0b or 0B + string += 2; + while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0'); + *floatvalue = *intvalue; + } //end else if +} //end of the function NumberValue +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadNumber(script_t *script, token_t *token) +{ + int len = 0, i; + int octal, dot; + char c; +// unsigned long int intvalue = 0; +// long double floatvalue = 0; + + token->type = TT_NUMBER; + //check for a hexadecimal number + if (*script->script_p == '0' && + (*(script->script_p + 1) == 'x' || + *(script->script_p + 1) == 'X')) + { + token->string[len++] = *script->script_p++; + token->string[len++] = *script->script_p++; + c = *script->script_p; + //hexadecimal + while((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'A')) + { + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN) + { + ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + c = *script->script_p; + } //end while + token->subtype |= TT_HEX; + } //end if +#ifdef BINARYNUMBERS + //check for a binary number + else if (*script->script_p == '0' && + (*(script->script_p + 1) == 'b' || + *(script->script_p + 1) == 'B')) + { + token->string[len++] = *script->script_p++; + token->string[len++] = *script->script_p++; + c = *script->script_p; + //binary + while(c == '0' || c == '1') + { + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN) + { + ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + c = *script->script_p; + } //end while + token->subtype |= TT_BINARY; + } //end if +#endif //BINARYNUMBERS + else //decimal or octal integer or floating point number + { + octal = qfalse; + dot = qfalse; + if (*script->script_p == '0') octal = qtrue; + while(1) + { + c = *script->script_p; + if (c == '.') dot = qtrue; + else if (c == '8' || c == '9') octal = qfalse; + else if (c < '0' || c > '9') break; + token->string[len++] = *script->script_p++; + if (len >= MAX_TOKEN - 1) + { + ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + } //end while + if (octal) token->subtype |= TT_OCTAL; + else token->subtype |= TT_DECIMAL; + if (dot) token->subtype |= TT_FLOAT; + } //end else + for (i = 0; i < 2; i++) + { + c = *script->script_p; + //check for a LONG number + if ( (c == 'l' || c == 'L') // bk001204 - brackets + && !(token->subtype & TT_LONG)) + { + script->script_p++; + token->subtype |= TT_LONG; + } //end if + //check for an UNSIGNED number + else if ( (c == 'u' || c == 'U') // bk001204 - brackets + && !(token->subtype & (TT_UNSIGNED | TT_FLOAT))) + { + script->script_p++; + token->subtype |= TT_UNSIGNED; + } //end if + } //end for + token->string[len] = '\0'; +#ifdef NUMBERVALUE + NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue); +#endif //NUMBERVALUE + if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER; + return 1; +} //end of the function PS_ReadNumber +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadLiteral(script_t *script, token_t *token) +{ + token->type = TT_LITERAL; + //first quote + token->string[0] = *script->script_p++; + //check for end of file + if (!*script->script_p) + { + ScriptError(script, "end of file before trailing \'"); + return 0; + } //end if + //if it is an escape character + if (*script->script_p == '\\') + { + if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0; + } //end if + else + { + token->string[1] = *script->script_p++; + } //end else + //check for trailing quote + if (*script->script_p != '\'') + { + ScriptWarning(script, "too many characters in literal, ignored"); + while(*script->script_p && + *script->script_p != '\'' && + *script->script_p != '\n') + { + script->script_p++; + } //end while + if (*script->script_p == '\'') script->script_p++; + } //end if + //store the trailing quote + token->string[2] = *script->script_p++; + //store trailing zero to end the string + token->string[3] = '\0'; + //the sub type is the integer literal value + token->subtype = token->string[1]; + // + return 1; +} //end of the function PS_ReadLiteral +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadPunctuation(script_t *script, token_t *token) +{ + int len; + char *p; + punctuation_t *punc; + +#ifdef PUNCTABLE + for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next) + { +#else + int i; + + for (i = 0; script->punctuations[i].p; i++) + { + punc = &script->punctuations[i]; +#endif //PUNCTABLE + p = punc->p; + len = strlen(p); + //if the script contains at least as much characters as the punctuation + if (script->script_p + len <= script->end_p) + { + //if the script contains the punctuation + if (!strncmp(script->script_p, p, len)) + { + strncpy(token->string, p, MAX_TOKEN); + script->script_p += len; + token->type = TT_PUNCTUATION; + //sub type is the number of the punctuation + token->subtype = punc->n; + return 1; + } //end if + } //end if + } //end for + return 0; +} //end of the function PS_ReadPunctuation +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadPrimitive(script_t *script, token_t *token) +{ + int len; + + len = 0; + while(*script->script_p > ' ' && *script->script_p != ';') + { + if (len >= MAX_TOKEN) + { + ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN); + return 0; + } //end if + token->string[len++] = *script->script_p++; + } //end while + token->string[len] = 0; + //copy the token into the script structure + Com_Memcpy(&script->token, token, sizeof(token_t)); + //primitive reading successfull + return 1; +} //end of the function PS_ReadPrimitive +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadToken(script_t *script, token_t *token) +{ + //if there is a token available (from UnreadToken) + if (script->tokenavailable) + { + script->tokenavailable = 0; + Com_Memcpy(token, &script->token, sizeof(token_t)); + return 1; + } //end if + //save script pointer + script->lastscript_p = script->script_p; + //save line counter + script->lastline = script->line; + //clear the token stuff + Com_Memset(token, 0, sizeof(token_t)); + //start of the white space + script->whitespace_p = script->script_p; + token->whitespace_p = script->script_p; + //read unusefull stuff + if (!PS_ReadWhiteSpace(script)) return 0; + //end of the white space + script->endwhitespace_p = script->script_p; + token->endwhitespace_p = script->script_p; + //line the token is on + token->line = script->line; + //number of lines crossed before token + token->linescrossed = script->line - script->lastline; + //if there is a leading double quote + if (*script->script_p == '\"') + { + if (!PS_ReadString(script, token, '\"')) return 0; + } //end if + //if an literal + else if (*script->script_p == '\'') + { + //if (!PS_ReadLiteral(script, token)) return 0; + if (!PS_ReadString(script, token, '\'')) return 0; + } //end if + //if there is a number + else if ((*script->script_p >= '0' && *script->script_p <= '9') || + (*script->script_p == '.' && + (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9'))) + { + if (!PS_ReadNumber(script, token)) return 0; + } //end if + //if this is a primitive script + else if (script->flags & SCFL_PRIMITIVE) + { + return PS_ReadPrimitive(script, token); + } //end else if + //if there is a name + else if ((*script->script_p >= 'a' && *script->script_p <= 'z') || + (*script->script_p >= 'A' && *script->script_p <= 'Z') || + *script->script_p == '_') + { + if (!PS_ReadName(script, token)) return 0; + } //end if + //check for punctuations + else if (!PS_ReadPunctuation(script, token)) + { + ScriptError(script, "can't read token"); + return 0; + } //end if + //copy the token into the script structure + Com_Memcpy(&script->token, token, sizeof(token_t)); + //succesfully read a token + return 1; +} //end of the function PS_ReadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectTokenString(script_t *script, char *string) +{ + token_t token; + + if (!PS_ReadToken(script, &token)) + { + ScriptError(script, "couldn't find expected %s", string); + return 0; + } //end if + + if (strcmp(token.string, string)) + { + ScriptError(script, "expected %s, found %s", string, token.string); + return 0; + } //end if + return 1; +} //end of the function PS_ExpectToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token) +{ + char str[MAX_TOKEN]; + + if (!PS_ReadToken(script, token)) + { + ScriptError(script, "couldn't read expected token"); + return 0; + } //end if + + if (token->type != type) + { + if (type == TT_STRING) strcpy(str, "string"); + if (type == TT_LITERAL) strcpy(str, "literal"); + if (type == TT_NUMBER) strcpy(str, "number"); + if (type == TT_NAME) strcpy(str, "name"); + if (type == TT_PUNCTUATION) strcpy(str, "punctuation"); + ScriptError(script, "expected a %s, found %s", str, token->string); + return 0; + } //end if + if (token->type == TT_NUMBER) + { + if ((token->subtype & subtype) != subtype) + { + if (subtype & TT_DECIMAL) strcpy(str, "decimal"); + if (subtype & TT_HEX) strcpy(str, "hex"); + if (subtype & TT_OCTAL) strcpy(str, "octal"); + if (subtype & TT_BINARY) strcpy(str, "binary"); + if (subtype & TT_LONG) strcat(str, " long"); + if (subtype & TT_UNSIGNED) strcat(str, " unsigned"); + if (subtype & TT_FLOAT) strcat(str, " float"); + if (subtype & TT_INTEGER) strcat(str, " integer"); + ScriptError(script, "expected %s, found %s", str, token->string); + return 0; + } //end if + } //end if + else if (token->type == TT_PUNCTUATION) + { + if (subtype < 0) + { + ScriptError(script, "BUG: wrong punctuation subtype"); + return 0; + } //end if + if (token->subtype != subtype) + { + ScriptError(script, "expected %s, found %s", + script->punctuations[subtype], token->string); + return 0; + } //end if + } //end else if + return 1; +} //end of the function PS_ExpectTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectAnyToken(script_t *script, token_t *token) +{ + if (!PS_ReadToken(script, token)) + { + ScriptError(script, "couldn't read expected token"); + return 0; + } //end if + else + { + return 1; + } //end else +} //end of the function PS_ExpectAnyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_CheckTokenString(script_t *script, char *string) +{ + token_t tok; + + if (!PS_ReadToken(script, &tok)) return 0; + //if the token is available + if (!strcmp(tok.string, string)) return 1; + //token not available + script->script_p = script->lastscript_p; + return 0; +} //end of the function PS_CheckTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token) +{ + token_t tok; + + if (!PS_ReadToken(script, &tok)) return 0; + //if the type matches + if (tok.type == type && + (tok.subtype & subtype) == subtype) + { + Com_Memcpy(token, &tok, sizeof(token_t)); + return 1; + } //end if + //token is not available + script->script_p = script->lastscript_p; + return 0; +} //end of the function PS_CheckTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_SkipUntilString(script_t *script, char *string) +{ + token_t token; + + while(PS_ReadToken(script, &token)) + { + if (!strcmp(token.string, string)) return 1; + } //end while + return 0; +} //end of the function PS_SkipUntilString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_UnreadLastToken(script_t *script) +{ + script->tokenavailable = 1; +} //end of the function UnreadLastToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_UnreadToken(script_t *script, token_t *token) +{ + Com_Memcpy(&script->token, token, sizeof(token_t)); + script->tokenavailable = 1; +} //end of the function UnreadToken +//============================================================================ +// returns the next character of the read white space, returns NULL if none +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +char PS_NextWhiteSpaceChar(script_t *script) +{ + if (script->whitespace_p != script->endwhitespace_p) + { + return *script->whitespace_p++; + } //end if + else + { + return 0; + } //end else +} //end of the function PS_NextWhiteSpaceChar +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void StripDoubleQuotes(char *string) +{ + if (*string == '\"') + { + strcpy(string, string+1); + } //end if + if (string[strlen(string)-1] == '\"') + { + string[strlen(string)-1] = '\0'; + } //end if +} //end of the function StripDoubleQuotes +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void StripSingleQuotes(char *string) +{ + if (*string == '\'') + { + strcpy(string, string+1); + } //end if + if (string[strlen(string)-1] == '\'') + { + string[strlen(string)-1] = '\0'; + } //end if +} //end of the function StripSingleQuotes +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +long double ReadSignedFloat(script_t *script) +{ + token_t token; + long double sign = 1; + + PS_ExpectAnyToken(script, &token); + if (!strcmp(token.string, "-")) + { + sign = -1; + PS_ExpectTokenType(script, TT_NUMBER, 0, &token); + } //end if + else if (token.type != TT_NUMBER) + { + ScriptError(script, "expected float value, found %s\n", token.string); + } //end else if + return sign * token.floatvalue; +} //end of the function ReadSignedFloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +signed long int ReadSignedInt(script_t *script) +{ + token_t token; + signed long int sign = 1; + + PS_ExpectAnyToken(script, &token); + if (!strcmp(token.string, "-")) + { + sign = -1; + PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token); + } //end if + else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT) + { + ScriptError(script, "expected integer value, found %s\n", token.string); + } //end else if + return sign * token.intvalue; +} //end of the function ReadSignedInt +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void SetScriptFlags(script_t *script, int flags) +{ + script->flags = flags; +} //end of the function SetScriptFlags +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int GetScriptFlags(script_t *script) +{ + return script->flags; +} //end of the function GetScriptFlags +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void ResetScript(script_t *script) +{ + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //begin of white space + script->whitespace_p = NULL; + //end of white space + script->endwhitespace_p = NULL; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + //clear the saved token + Com_Memset(&script->token, 0, sizeof(token_t)); +} //end of the function ResetScript +//============================================================================ +// returns true if at the end of the script +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int EndOfScript(script_t *script) +{ + return script->script_p >= script->end_p; +} //end of the function EndOfScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int NumLinesCrossed(script_t *script) +{ + return script->line - script->lastline; +} //end of the function NumLinesCrossed +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int ScriptSkipTo(script_t *script, char *value) +{ + int len; + char firstchar; + + firstchar = *value; + len = strlen(value); + do + { + if (!PS_ReadWhiteSpace(script)) return 0; + if (*script->script_p == firstchar) + { + if (!strncmp(script->script_p, value, len)) + { + return 1; + } //end if + } //end if + script->script_p++; + } while(1); +} //end of the function ScriptSkipTo +#ifndef BOTLIB +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int FileLength(FILE *fp) +{ + int pos; + int end; + + pos = ftell(fp); + fseek(fp, 0, SEEK_END); + end = ftell(fp); + fseek(fp, pos, SEEK_SET); + + return end; +} //end of the function FileLength +#endif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +script_t *LoadScriptFile(const char *filename) +{ +#ifdef BOTLIB + fileHandle_t fp; + char pathname[MAX_QPATH]; +#else + FILE *fp; +#endif + int length; + void *buffer; + script_t *script; + +#ifdef BOTLIB + if (strlen(basefolder)) + Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename); + else + Com_sprintf(pathname, sizeof(pathname), "%s", filename); + length = botimport.FS_FOpenFile( pathname, &fp, FS_READ ); + if (!fp) return NULL; +#else + fp = fopen(filename, "rb"); + if (!fp) return NULL; + + length = FileLength(fp); +#endif + + buffer = GetClearedMemory(sizeof(script_t) + length + 1); + script = (script_t *) buffer; + Com_Memset(script, 0, sizeof(script_t)); + strcpy(script->filename, filename); + script->buffer = (char *) buffer + sizeof(script_t); + script->buffer[length] = 0; + script->length = length; + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //pointer to end of script buffer + script->end_p = &script->buffer[length]; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + // + SetScriptPunctuations(script, NULL); + // +#ifdef BOTLIB + botimport.FS_Read(script->buffer, length, fp); + botimport.FS_FCloseFile(fp); +#else + if (fread(script->buffer, length, 1, fp) != 1) + { + FreeMemory(buffer); + script = NULL; + } //end if + fclose(fp); +#endif + // + script->length = COM_Compress(script->buffer); + + return script; +} //end of the function LoadScriptFile +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +script_t *LoadScriptMemory(char *ptr, int length, char *name) +{ + void *buffer; + script_t *script; + + buffer = GetClearedMemory(sizeof(script_t) + length + 1); + script = (script_t *) buffer; + Com_Memset(script, 0, sizeof(script_t)); + strcpy(script->filename, name); + script->buffer = (char *) buffer + sizeof(script_t); + script->buffer[length] = 0; + script->length = length; + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //pointer to end of script buffer + script->end_p = &script->buffer[length]; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + // + SetScriptPunctuations(script, NULL); + // + Com_Memcpy(script->buffer, ptr, length); + // + return script; +} //end of the function LoadScriptMemory +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void FreeScript(script_t *script) +{ +#ifdef PUNCTABLE + if (script->punctuationtable) FreeMemory(script->punctuationtable); +#endif //PUNCTABLE + FreeMemory(script); +} //end of the function FreeScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_SetBaseFolder(char *path) +{ +#ifdef BSPC + sprintf(basefolder, path); +#else + Com_sprintf(basefolder, sizeof(basefolder), path); +#endif +} //end of the function PS_SetBaseFolder diff --git a/code/botlib/l_script.h b/code/botlib/l_script.h index 2317907..88696bf 100755 --- a/code/botlib/l_script.h +++ b/code/botlib/l_script.h @@ -1,247 +1,247 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_script.h
- *
- * desc: lexicographical parser
- *
- * $Archive: /source/code/botlib/l_script.h $
- *
- *****************************************************************************/
-
-//undef if binary numbers of the form 0b... or 0B... are not allowed
-#define BINARYNUMBERS
-//undef if not using the token.intvalue and token.floatvalue
-#define NUMBERVALUE
-//use dollar sign also as punctuation
-#define DOLLAR
-
-//maximum token length
-#define MAX_TOKEN 1024
-
-#if defined(BSPC) && !defined(QDECL)
-#define QDECL
-#endif
-
-
-//script flags
-#define SCFL_NOERRORS 0x0001
-#define SCFL_NOWARNINGS 0x0002
-#define SCFL_NOSTRINGWHITESPACES 0x0004
-#define SCFL_NOSTRINGESCAPECHARS 0x0008
-#define SCFL_PRIMITIVE 0x0010
-#define SCFL_NOBINARYNUMBERS 0x0020
-#define SCFL_NONUMBERVALUES 0x0040
-
-//token types
-#define TT_STRING 1 // string
-#define TT_LITERAL 2 // literal
-#define TT_NUMBER 3 // number
-#define TT_NAME 4 // name
-#define TT_PUNCTUATION 5 // punctuation
-
-//string sub type
-//---------------
-// the length of the string
-//literal sub type
-//----------------
-// the ASCII code of the literal
-//number sub type
-//---------------
-#define TT_DECIMAL 0x0008 // decimal number
-#define TT_HEX 0x0100 // hexadecimal number
-#define TT_OCTAL 0x0200 // octal number
-#ifdef BINARYNUMBERS
-#define TT_BINARY 0x0400 // binary number
-#endif //BINARYNUMBERS
-#define TT_FLOAT 0x0800 // floating point number
-#define TT_INTEGER 0x1000 // integer number
-#define TT_LONG 0x2000 // long number
-#define TT_UNSIGNED 0x4000 // unsigned number
-//punctuation sub type
-//--------------------
-#define P_RSHIFT_ASSIGN 1
-#define P_LSHIFT_ASSIGN 2
-#define P_PARMS 3
-#define P_PRECOMPMERGE 4
-
-#define P_LOGIC_AND 5
-#define P_LOGIC_OR 6
-#define P_LOGIC_GEQ 7
-#define P_LOGIC_LEQ 8
-#define P_LOGIC_EQ 9
-#define P_LOGIC_UNEQ 10
-
-#define P_MUL_ASSIGN 11
-#define P_DIV_ASSIGN 12
-#define P_MOD_ASSIGN 13
-#define P_ADD_ASSIGN 14
-#define P_SUB_ASSIGN 15
-#define P_INC 16
-#define P_DEC 17
-
-#define P_BIN_AND_ASSIGN 18
-#define P_BIN_OR_ASSIGN 19
-#define P_BIN_XOR_ASSIGN 20
-#define P_RSHIFT 21
-#define P_LSHIFT 22
-
-#define P_POINTERREF 23
-#define P_CPP1 24
-#define P_CPP2 25
-#define P_MUL 26
-#define P_DIV 27
-#define P_MOD 28
-#define P_ADD 29
-#define P_SUB 30
-#define P_ASSIGN 31
-
-#define P_BIN_AND 32
-#define P_BIN_OR 33
-#define P_BIN_XOR 34
-#define P_BIN_NOT 35
-
-#define P_LOGIC_NOT 36
-#define P_LOGIC_GREATER 37
-#define P_LOGIC_LESS 38
-
-#define P_REF 39
-#define P_COMMA 40
-#define P_SEMICOLON 41
-#define P_COLON 42
-#define P_QUESTIONMARK 43
-
-#define P_PARENTHESESOPEN 44
-#define P_PARENTHESESCLOSE 45
-#define P_BRACEOPEN 46
-#define P_BRACECLOSE 47
-#define P_SQBRACKETOPEN 48
-#define P_SQBRACKETCLOSE 49
-#define P_BACKSLASH 50
-
-#define P_PRECOMP 51
-#define P_DOLLAR 52
-//name sub type
-//-------------
-// the length of the name
-
-//punctuation
-typedef struct punctuation_s
-{
- char *p; //punctuation character(s)
- int n; //punctuation indication
- struct punctuation_s *next; //next punctuation
-} punctuation_t;
-
-//token
-typedef struct token_s
-{
- char string[MAX_TOKEN]; //available token
- int type; //last read token type
- int subtype; //last read token sub type
-#ifdef NUMBERVALUE
- unsigned long int intvalue; //integer value
- long double floatvalue; //floating point value
-#endif //NUMBERVALUE
- char *whitespace_p; //start of white space before token
- char *endwhitespace_p; //start of white space before token
- int line; //line the token was on
- int linescrossed; //lines crossed in white space
- struct token_s *next; //next token in chain
-} token_t;
-
-//script file
-typedef struct script_s
-{
- char filename[1024]; //file name of the script
- char *buffer; //buffer containing the script
- char *script_p; //current pointer in the script
- char *end_p; //pointer to the end of the script
- char *lastscript_p; //script pointer before reading token
- char *whitespace_p; //begin of the white space
- char *endwhitespace_p; //end of the white space
- int length; //length of the script in bytes
- int line; //current line in script
- int lastline; //line before reading token
- int tokenavailable; //set by UnreadLastToken
- int flags; //several script flags
- punctuation_t *punctuations; //the punctuations used in the script
- punctuation_t **punctuationtable;
- token_t token; //available token
- struct script_s *next; //next script in a chain
-} script_t;
-
-//read a token from the script
-int PS_ReadToken(script_t *script, token_t *token);
-//expect a certain token
-int PS_ExpectTokenString(script_t *script, char *string);
-//expect a certain token type
-int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token);
-//expect a token
-int PS_ExpectAnyToken(script_t *script, token_t *token);
-//returns true when the token is available
-int PS_CheckTokenString(script_t *script, char *string);
-//returns true an reads the token when a token with the given type is available
-int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token);
-//skip tokens until the given token string is read
-int PS_SkipUntilString(script_t *script, char *string);
-//unread the last token read from the script
-void PS_UnreadLastToken(script_t *script);
-//unread the given token
-void PS_UnreadToken(script_t *script, token_t *token);
-//returns the next character of the read white space, returns NULL if none
-char PS_NextWhiteSpaceChar(script_t *script);
-//remove any leading and trailing double quotes from the token
-void StripDoubleQuotes(char *string);
-//remove any leading and trailing single quotes from the token
-void StripSingleQuotes(char *string);
-//read a possible signed integer
-signed long int ReadSignedInt(script_t *script);
-//read a possible signed floating point number
-long double ReadSignedFloat(script_t *script);
-//set an array with punctuations, NULL restores default C/C++ set
-void SetScriptPunctuations(script_t *script, punctuation_t *p);
-//set script flags
-void SetScriptFlags(script_t *script, int flags);
-//get script flags
-int GetScriptFlags(script_t *script);
-//reset a script
-void ResetScript(script_t *script);
-//returns true if at the end of the script
-int EndOfScript(script_t *script);
-//returns a pointer to the punctuation with the given number
-char *PunctuationFromNum(script_t *script, int num);
-//load a script from the given file at the given offset with the given length
-script_t *LoadScriptFile(const char *filename);
-//load a script from the given memory with the given length
-script_t *LoadScriptMemory(char *ptr, int length, char *name);
-//free a script
-void FreeScript(script_t *script);
-//set the base folder to load files from
-void PS_SetBaseFolder(char *path);
-//print a script error with filename and line number
-void QDECL ScriptError(script_t *script, char *str, ...);
-//print a script warning with filename and line number
-void QDECL ScriptWarning(script_t *script, char *str, ...);
-
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_script.h + * + * desc: lexicographical parser + * + * $Archive: /source/code/botlib/l_script.h $ + * + *****************************************************************************/ + +//undef if binary numbers of the form 0b... or 0B... are not allowed +#define BINARYNUMBERS +//undef if not using the token.intvalue and token.floatvalue +#define NUMBERVALUE +//use dollar sign also as punctuation +#define DOLLAR + +//maximum token length +#define MAX_TOKEN 1024 + +#if defined(BSPC) && !defined(QDECL) +#define QDECL +#endif + + +//script flags +#define SCFL_NOERRORS 0x0001 +#define SCFL_NOWARNINGS 0x0002 +#define SCFL_NOSTRINGWHITESPACES 0x0004 +#define SCFL_NOSTRINGESCAPECHARS 0x0008 +#define SCFL_PRIMITIVE 0x0010 +#define SCFL_NOBINARYNUMBERS 0x0020 +#define SCFL_NONUMBERVALUES 0x0040 + +//token types +#define TT_STRING 1 // string +#define TT_LITERAL 2 // literal +#define TT_NUMBER 3 // number +#define TT_NAME 4 // name +#define TT_PUNCTUATION 5 // punctuation + +//string sub type +//--------------- +// the length of the string +//literal sub type +//---------------- +// the ASCII code of the literal +//number sub type +//--------------- +#define TT_DECIMAL 0x0008 // decimal number +#define TT_HEX 0x0100 // hexadecimal number +#define TT_OCTAL 0x0200 // octal number +#ifdef BINARYNUMBERS +#define TT_BINARY 0x0400 // binary number +#endif //BINARYNUMBERS +#define TT_FLOAT 0x0800 // floating point number +#define TT_INTEGER 0x1000 // integer number +#define TT_LONG 0x2000 // long number +#define TT_UNSIGNED 0x4000 // unsigned number +//punctuation sub type +//-------------------- +#define P_RSHIFT_ASSIGN 1 +#define P_LSHIFT_ASSIGN 2 +#define P_PARMS 3 +#define P_PRECOMPMERGE 4 + +#define P_LOGIC_AND 5 +#define P_LOGIC_OR 6 +#define P_LOGIC_GEQ 7 +#define P_LOGIC_LEQ 8 +#define P_LOGIC_EQ 9 +#define P_LOGIC_UNEQ 10 + +#define P_MUL_ASSIGN 11 +#define P_DIV_ASSIGN 12 +#define P_MOD_ASSIGN 13 +#define P_ADD_ASSIGN 14 +#define P_SUB_ASSIGN 15 +#define P_INC 16 +#define P_DEC 17 + +#define P_BIN_AND_ASSIGN 18 +#define P_BIN_OR_ASSIGN 19 +#define P_BIN_XOR_ASSIGN 20 +#define P_RSHIFT 21 +#define P_LSHIFT 22 + +#define P_POINTERREF 23 +#define P_CPP1 24 +#define P_CPP2 25 +#define P_MUL 26 +#define P_DIV 27 +#define P_MOD 28 +#define P_ADD 29 +#define P_SUB 30 +#define P_ASSIGN 31 + +#define P_BIN_AND 32 +#define P_BIN_OR 33 +#define P_BIN_XOR 34 +#define P_BIN_NOT 35 + +#define P_LOGIC_NOT 36 +#define P_LOGIC_GREATER 37 +#define P_LOGIC_LESS 38 + +#define P_REF 39 +#define P_COMMA 40 +#define P_SEMICOLON 41 +#define P_COLON 42 +#define P_QUESTIONMARK 43 + +#define P_PARENTHESESOPEN 44 +#define P_PARENTHESESCLOSE 45 +#define P_BRACEOPEN 46 +#define P_BRACECLOSE 47 +#define P_SQBRACKETOPEN 48 +#define P_SQBRACKETCLOSE 49 +#define P_BACKSLASH 50 + +#define P_PRECOMP 51 +#define P_DOLLAR 52 +//name sub type +//------------- +// the length of the name + +//punctuation +typedef struct punctuation_s +{ + char *p; //punctuation character(s) + int n; //punctuation indication + struct punctuation_s *next; //next punctuation +} punctuation_t; + +//token +typedef struct token_s +{ + char string[MAX_TOKEN]; //available token + int type; //last read token type + int subtype; //last read token sub type +#ifdef NUMBERVALUE + unsigned long int intvalue; //integer value + long double floatvalue; //floating point value +#endif //NUMBERVALUE + char *whitespace_p; //start of white space before token + char *endwhitespace_p; //start of white space before token + int line; //line the token was on + int linescrossed; //lines crossed in white space + struct token_s *next; //next token in chain +} token_t; + +//script file +typedef struct script_s +{ + char filename[1024]; //file name of the script + char *buffer; //buffer containing the script + char *script_p; //current pointer in the script + char *end_p; //pointer to the end of the script + char *lastscript_p; //script pointer before reading token + char *whitespace_p; //begin of the white space + char *endwhitespace_p; //end of the white space + int length; //length of the script in bytes + int line; //current line in script + int lastline; //line before reading token + int tokenavailable; //set by UnreadLastToken + int flags; //several script flags + punctuation_t *punctuations; //the punctuations used in the script + punctuation_t **punctuationtable; + token_t token; //available token + struct script_s *next; //next script in a chain +} script_t; + +//read a token from the script +int PS_ReadToken(script_t *script, token_t *token); +//expect a certain token +int PS_ExpectTokenString(script_t *script, char *string); +//expect a certain token type +int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token); +//expect a token +int PS_ExpectAnyToken(script_t *script, token_t *token); +//returns true when the token is available +int PS_CheckTokenString(script_t *script, char *string); +//returns true an reads the token when a token with the given type is available +int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token); +//skip tokens until the given token string is read +int PS_SkipUntilString(script_t *script, char *string); +//unread the last token read from the script +void PS_UnreadLastToken(script_t *script); +//unread the given token +void PS_UnreadToken(script_t *script, token_t *token); +//returns the next character of the read white space, returns NULL if none +char PS_NextWhiteSpaceChar(script_t *script); +//remove any leading and trailing double quotes from the token +void StripDoubleQuotes(char *string); +//remove any leading and trailing single quotes from the token +void StripSingleQuotes(char *string); +//read a possible signed integer +signed long int ReadSignedInt(script_t *script); +//read a possible signed floating point number +long double ReadSignedFloat(script_t *script); +//set an array with punctuations, NULL restores default C/C++ set +void SetScriptPunctuations(script_t *script, punctuation_t *p); +//set script flags +void SetScriptFlags(script_t *script, int flags); +//get script flags +int GetScriptFlags(script_t *script); +//reset a script +void ResetScript(script_t *script); +//returns true if at the end of the script +int EndOfScript(script_t *script); +//returns a pointer to the punctuation with the given number +char *PunctuationFromNum(script_t *script, int num); +//load a script from the given file at the given offset with the given length +script_t *LoadScriptFile(const char *filename); +//load a script from the given memory with the given length +script_t *LoadScriptMemory(char *ptr, int length, char *name); +//free a script +void FreeScript(script_t *script); +//set the base folder to load files from +void PS_SetBaseFolder(char *path); +//print a script error with filename and line number +void QDECL ScriptError(script_t *script, char *str, ...); +//print a script warning with filename and line number +void QDECL ScriptWarning(script_t *script, char *str, ...); + + diff --git a/code/botlib/l_struct.c b/code/botlib/l_struct.c index dea3b20..c628821 100755 --- a/code/botlib/l_struct.c +++ b/code/botlib/l_struct.c @@ -1,462 +1,462 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_struct.c
- *
- * desc: structure reading / writing
- *
- * $Archive: /MissionPack/CODE/botlib/l_struct.c $
- *
- *****************************************************************************/
-
-#ifdef BOTLIB
-#include "../game/q_shared.h"
-#include "../game/botlib.h" //for the include of be_interface.h
-#include "l_script.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-#include "l_utils.h"
-#include "be_interface.h"
-#endif //BOTLIB
-
-#ifdef BSPC
-//include files for usage in the BSP Converter
-#include "../bspc/qbsp.h"
-#include "../bspc/l_log.h"
-#include "../bspc/l_mem.h"
-#include "l_precomp.h"
-#include "l_struct.h"
-
-#define qtrue true
-#define qfalse false
-#endif //BSPC
-
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-fielddef_t *FindField(fielddef_t *defs, char *name)
-{
- int i;
-
- for (i = 0; defs[i].name; i++)
- {
- if (!strcmp(defs[i].name, name)) return &defs[i];
- } //end for
- return NULL;
-} //end of the function FindField
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean ReadNumber(source_t *source, fielddef_t *fd, void *p)
-{
- token_t token;
- int negative = qfalse;
- long int intval, intmin = 0, intmax = 0;
- double floatval;
-
- if (!PC_ExpectAnyToken(source, &token)) return 0;
-
- //check for minus sign
- if (token.type == TT_PUNCTUATION)
- {
- if (fd->type & FT_UNSIGNED)
- {
- SourceError(source, "expected unsigned value, found %s", token.string);
- return 0;
- } //end if
- //if not a minus sign
- if (strcmp(token.string, "-"))
- {
- SourceError(source, "unexpected punctuation %s", token.string);
- return 0;
- } //end if
- negative = qtrue;
- //read the number
- if (!PC_ExpectAnyToken(source, &token)) return 0;
- } //end if
- //check if it is a number
- if (token.type != TT_NUMBER)
- {
- SourceError(source, "expected number, found %s", token.string);
- return 0;
- } //end if
- //check for a float value
- if (token.subtype & TT_FLOAT)
- {
- if ((fd->type & FT_TYPE) != FT_FLOAT)
- {
- SourceError(source, "unexpected float");
- return 0;
- } //end if
- floatval = token.floatvalue;
- if (negative) floatval = -floatval;
- if (fd->type & FT_BOUNDED)
- {
- if (floatval < fd->floatmin || floatval > fd->floatmax)
- {
- SourceError(source, "float out of range [%f, %f]", fd->floatmin, fd->floatmax);
- return 0;
- } //end if
- } //end if
- *(float *) p = (float) floatval;
- return 1;
- } //end if
- //
- intval = token.intvalue;
- if (negative) intval = -intval;
- //check bounds
- if ((fd->type & FT_TYPE) == FT_CHAR)
- {
- if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 255;}
- else {intmin = -128; intmax = 127;}
- } //end if
- if ((fd->type & FT_TYPE) == FT_INT)
- {
- if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 65535;}
- else {intmin = -32768; intmax = 32767;}
- } //end else if
- if ((fd->type & FT_TYPE) == FT_CHAR || (fd->type & FT_TYPE) == FT_INT)
- {
- if (fd->type & FT_BOUNDED)
- {
- intmin = Maximum(intmin, fd->floatmin);
- intmax = Minimum(intmax, fd->floatmax);
- } //end if
- if (intval < intmin || intval > intmax)
- {
- SourceError(source, "value %d out of range [%d, %d]", intval, intmin, intmax);
- return 0;
- } //end if
- } //end if
- else if ((fd->type & FT_TYPE) == FT_FLOAT)
- {
- if (fd->type & FT_BOUNDED)
- {
- if (intval < fd->floatmin || intval > fd->floatmax)
- {
- SourceError(source, "value %d out of range [%f, %f]", intval, fd->floatmin, fd->floatmax);
- return 0;
- } //end if
- } //end if
- } //end else if
- //store the value
- if ((fd->type & FT_TYPE) == FT_CHAR)
- {
- if (fd->type & FT_UNSIGNED) *(unsigned char *) p = (unsigned char) intval;
- else *(char *) p = (char) intval;
- } //end if
- else if ((fd->type & FT_TYPE) == FT_INT)
- {
- if (fd->type & FT_UNSIGNED) *(unsigned int *) p = (unsigned int) intval;
- else *(int *) p = (int) intval;
- } //end else
- else if ((fd->type & FT_TYPE) == FT_FLOAT)
- {
- *(float *) p = (float) intval;
- } //end else
- return 1;
-} //end of the function ReadNumber
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-qboolean ReadChar(source_t *source, fielddef_t *fd, void *p)
-{
- token_t token;
-
- if (!PC_ExpectAnyToken(source, &token)) return 0;
-
- //take literals into account
- if (token.type == TT_LITERAL)
- {
- StripSingleQuotes(token.string);
- *(char *) p = token.string[0];
- } //end if
- else
- {
- PC_UnreadLastToken(source);
- if (!ReadNumber(source, fd, p)) return 0;
- } //end if
- return 1;
-} //end of the function ReadChar
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int ReadString(source_t *source, fielddef_t *fd, void *p)
-{
- token_t token;
-
- if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) return 0;
- //remove the double quotes
- StripDoubleQuotes(token.string);
- //copy the string
- strncpy((char *) p, token.string, MAX_STRINGFIELD);
- //make sure the string is closed with a zero
- ((char *)p)[MAX_STRINGFIELD-1] = '\0';
- //
- return 1;
-} //end of the function ReadString
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int ReadStructure(source_t *source, structdef_t *def, char *structure)
-{
- token_t token;
- fielddef_t *fd;
- void *p;
- int num;
-
- if (!PC_ExpectTokenString(source, "{")) return 0;
- while(1)
- {
- if (!PC_ExpectAnyToken(source, &token)) return qfalse;
- //if end of structure
- if (!strcmp(token.string, "}")) break;
- //find the field with the name
- fd = FindField(def->fields, token.string);
- if (!fd)
- {
- SourceError(source, "unknown structure field %s", token.string);
- return qfalse;
- } //end if
- if (fd->type & FT_ARRAY)
- {
- num = fd->maxarray;
- if (!PC_ExpectTokenString(source, "{")) return qfalse;
- } //end if
- else
- {
- num = 1;
- } //end else
- p = (void *)(structure + fd->offset);
- while (num-- > 0)
- {
- if (fd->type & FT_ARRAY)
- {
- if (PC_CheckTokenString(source, "}")) break;
- } //end if
- switch(fd->type & FT_TYPE)
- {
- case FT_CHAR:
- {
- if (!ReadChar(source, fd, p)) return qfalse;
- p = (char *) p + sizeof(char);
- break;
- } //end case
- case FT_INT:
- {
- if (!ReadNumber(source, fd, p)) return qfalse;
- p = (char *) p + sizeof(int);
- break;
- } //end case
- case FT_FLOAT:
- {
- if (!ReadNumber(source, fd, p)) return qfalse;
- p = (char *) p + sizeof(float);
- break;
- } //end case
- case FT_STRING:
- {
- if (!ReadString(source, fd, p)) return qfalse;
- p = (char *) p + MAX_STRINGFIELD;
- break;
- } //end case
- case FT_STRUCT:
- {
- if (!fd->substruct)
- {
- SourceError(source, "BUG: no sub structure defined");
- return qfalse;
- } //end if
- ReadStructure(source, fd->substruct, (char *) p);
- p = (char *) p + fd->substruct->size;
- break;
- } //end case
- } //end switch
- if (fd->type & FT_ARRAY)
- {
- if (!PC_ExpectAnyToken(source, &token)) return qfalse;
- if (!strcmp(token.string, "}")) break;
- if (strcmp(token.string, ","))
- {
- SourceError(source, "expected a comma, found %s", token.string);
- return qfalse;
- } //end if
- } //end if
- } //end while
- } //end while
- return qtrue;
-} //end of the function ReadStructure
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int WriteIndent(FILE *fp, int indent)
-{
- while(indent-- > 0)
- {
- if (fprintf(fp, "\t") < 0) return qfalse;
- } //end while
- return qtrue;
-} //end of the function WriteIndent
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int WriteFloat(FILE *fp, float value)
-{
- char buf[128];
- int l;
-
- sprintf(buf, "%f", value);
- l = strlen(buf);
- //strip any trailing zeros
- while(l-- > 1)
- {
- if (buf[l] != '0' && buf[l] != '.') break;
- if (buf[l] == '.')
- {
- buf[l] = 0;
- break;
- } //end if
- buf[l] = 0;
- } //end while
- //write the float to file
- if (fprintf(fp, "%s", buf) < 0) return 0;
- return 1;
-} //end of the function WriteFloat
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int WriteStructWithIndent(FILE *fp, structdef_t *def, char *structure, int indent)
-{
- int i, num;
- void *p;
- fielddef_t *fd;
-
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "{\r\n") < 0) return qfalse;
-
- indent++;
- for (i = 0; def->fields[i].name; i++)
- {
- fd = &def->fields[i];
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "%s\t", fd->name) < 0) return qfalse;
- p = (void *)(structure + fd->offset);
- if (fd->type & FT_ARRAY)
- {
- num = fd->maxarray;
- if (fprintf(fp, "{") < 0) return qfalse;
- } //end if
- else
- {
- num = 1;
- } //end else
- while(num-- > 0)
- {
- switch(fd->type & FT_TYPE)
- {
- case FT_CHAR:
- {
- if (fprintf(fp, "%d", *(char *) p) < 0) return qfalse;
- p = (char *) p + sizeof(char);
- break;
- } //end case
- case FT_INT:
- {
- if (fprintf(fp, "%d", *(int *) p) < 0) return qfalse;
- p = (char *) p + sizeof(int);
- break;
- } //end case
- case FT_FLOAT:
- {
- if (!WriteFloat(fp, *(float *)p)) return qfalse;
- p = (char *) p + sizeof(float);
- break;
- } //end case
- case FT_STRING:
- {
- if (fprintf(fp, "\"%s\"", (char *) p) < 0) return qfalse;
- p = (char *) p + MAX_STRINGFIELD;
- break;
- } //end case
- case FT_STRUCT:
- {
- if (!WriteStructWithIndent(fp, fd->substruct, structure, indent)) return qfalse;
- p = (char *) p + fd->substruct->size;
- break;
- } //end case
- } //end switch
- if (fd->type & FT_ARRAY)
- {
- if (num > 0)
- {
- if (fprintf(fp, ",") < 0) return qfalse;
- } //end if
- else
- {
- if (fprintf(fp, "}") < 0) return qfalse;
- } //end else
- } //end if
- } //end while
- if (fprintf(fp, "\r\n") < 0) return qfalse;
- } //end for
- indent--;
-
- if (!WriteIndent(fp, indent)) return qfalse;
- if (fprintf(fp, "}\r\n") < 0) return qfalse;
- return qtrue;
-} //end of the function WriteStructWithIndent
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int WriteStructure(FILE *fp, structdef_t *def, char *structure)
-{
- return WriteStructWithIndent(fp, def, structure, 0);
-} //end of the function WriteStructure
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_struct.c + * + * desc: structure reading / writing + * + * $Archive: /MissionPack/CODE/botlib/l_struct.c $ + * + *****************************************************************************/ + +#ifdef BOTLIB +#include "../game/q_shared.h" +#include "../game/botlib.h" //for the include of be_interface.h +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "l_utils.h" +#include "be_interface.h" +#endif //BOTLIB + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" +#include "l_precomp.h" +#include "l_struct.h" + +#define qtrue true +#define qfalse false +#endif //BSPC + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +fielddef_t *FindField(fielddef_t *defs, char *name) +{ + int i; + + for (i = 0; defs[i].name; i++) + { + if (!strcmp(defs[i].name, name)) return &defs[i]; + } //end for + return NULL; +} //end of the function FindField +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ReadNumber(source_t *source, fielddef_t *fd, void *p) +{ + token_t token; + int negative = qfalse; + long int intval, intmin = 0, intmax = 0; + double floatval; + + if (!PC_ExpectAnyToken(source, &token)) return 0; + + //check for minus sign + if (token.type == TT_PUNCTUATION) + { + if (fd->type & FT_UNSIGNED) + { + SourceError(source, "expected unsigned value, found %s", token.string); + return 0; + } //end if + //if not a minus sign + if (strcmp(token.string, "-")) + { + SourceError(source, "unexpected punctuation %s", token.string); + return 0; + } //end if + negative = qtrue; + //read the number + if (!PC_ExpectAnyToken(source, &token)) return 0; + } //end if + //check if it is a number + if (token.type != TT_NUMBER) + { + SourceError(source, "expected number, found %s", token.string); + return 0; + } //end if + //check for a float value + if (token.subtype & TT_FLOAT) + { + if ((fd->type & FT_TYPE) != FT_FLOAT) + { + SourceError(source, "unexpected float"); + return 0; + } //end if + floatval = token.floatvalue; + if (negative) floatval = -floatval; + if (fd->type & FT_BOUNDED) + { + if (floatval < fd->floatmin || floatval > fd->floatmax) + { + SourceError(source, "float out of range [%f, %f]", fd->floatmin, fd->floatmax); + return 0; + } //end if + } //end if + *(float *) p = (float) floatval; + return 1; + } //end if + // + intval = token.intvalue; + if (negative) intval = -intval; + //check bounds + if ((fd->type & FT_TYPE) == FT_CHAR) + { + if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 255;} + else {intmin = -128; intmax = 127;} + } //end if + if ((fd->type & FT_TYPE) == FT_INT) + { + if (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 65535;} + else {intmin = -32768; intmax = 32767;} + } //end else if + if ((fd->type & FT_TYPE) == FT_CHAR || (fd->type & FT_TYPE) == FT_INT) + { + if (fd->type & FT_BOUNDED) + { + intmin = Maximum(intmin, fd->floatmin); + intmax = Minimum(intmax, fd->floatmax); + } //end if + if (intval < intmin || intval > intmax) + { + SourceError(source, "value %d out of range [%d, %d]", intval, intmin, intmax); + return 0; + } //end if + } //end if + else if ((fd->type & FT_TYPE) == FT_FLOAT) + { + if (fd->type & FT_BOUNDED) + { + if (intval < fd->floatmin || intval > fd->floatmax) + { + SourceError(source, "value %d out of range [%f, %f]", intval, fd->floatmin, fd->floatmax); + return 0; + } //end if + } //end if + } //end else if + //store the value + if ((fd->type & FT_TYPE) == FT_CHAR) + { + if (fd->type & FT_UNSIGNED) *(unsigned char *) p = (unsigned char) intval; + else *(char *) p = (char) intval; + } //end if + else if ((fd->type & FT_TYPE) == FT_INT) + { + if (fd->type & FT_UNSIGNED) *(unsigned int *) p = (unsigned int) intval; + else *(int *) p = (int) intval; + } //end else + else if ((fd->type & FT_TYPE) == FT_FLOAT) + { + *(float *) p = (float) intval; + } //end else + return 1; +} //end of the function ReadNumber +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean ReadChar(source_t *source, fielddef_t *fd, void *p) +{ + token_t token; + + if (!PC_ExpectAnyToken(source, &token)) return 0; + + //take literals into account + if (token.type == TT_LITERAL) + { + StripSingleQuotes(token.string); + *(char *) p = token.string[0]; + } //end if + else + { + PC_UnreadLastToken(source); + if (!ReadNumber(source, fd, p)) return 0; + } //end if + return 1; +} //end of the function ReadChar +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadString(source_t *source, fielddef_t *fd, void *p) +{ + token_t token; + + if (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) return 0; + //remove the double quotes + StripDoubleQuotes(token.string); + //copy the string + strncpy((char *) p, token.string, MAX_STRINGFIELD); + //make sure the string is closed with a zero + ((char *)p)[MAX_STRINGFIELD-1] = '\0'; + // + return 1; +} //end of the function ReadString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadStructure(source_t *source, structdef_t *def, char *structure) +{ + token_t token; + fielddef_t *fd; + void *p; + int num; + + if (!PC_ExpectTokenString(source, "{")) return 0; + while(1) + { + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + //if end of structure + if (!strcmp(token.string, "}")) break; + //find the field with the name + fd = FindField(def->fields, token.string); + if (!fd) + { + SourceError(source, "unknown structure field %s", token.string); + return qfalse; + } //end if + if (fd->type & FT_ARRAY) + { + num = fd->maxarray; + if (!PC_ExpectTokenString(source, "{")) return qfalse; + } //end if + else + { + num = 1; + } //end else + p = (void *)(structure + fd->offset); + while (num-- > 0) + { + if (fd->type & FT_ARRAY) + { + if (PC_CheckTokenString(source, "}")) break; + } //end if + switch(fd->type & FT_TYPE) + { + case FT_CHAR: + { + if (!ReadChar(source, fd, p)) return qfalse; + p = (char *) p + sizeof(char); + break; + } //end case + case FT_INT: + { + if (!ReadNumber(source, fd, p)) return qfalse; + p = (char *) p + sizeof(int); + break; + } //end case + case FT_FLOAT: + { + if (!ReadNumber(source, fd, p)) return qfalse; + p = (char *) p + sizeof(float); + break; + } //end case + case FT_STRING: + { + if (!ReadString(source, fd, p)) return qfalse; + p = (char *) p + MAX_STRINGFIELD; + break; + } //end case + case FT_STRUCT: + { + if (!fd->substruct) + { + SourceError(source, "BUG: no sub structure defined"); + return qfalse; + } //end if + ReadStructure(source, fd->substruct, (char *) p); + p = (char *) p + fd->substruct->size; + break; + } //end case + } //end switch + if (fd->type & FT_ARRAY) + { + if (!PC_ExpectAnyToken(source, &token)) return qfalse; + if (!strcmp(token.string, "}")) break; + if (strcmp(token.string, ",")) + { + SourceError(source, "expected a comma, found %s", token.string); + return qfalse; + } //end if + } //end if + } //end while + } //end while + return qtrue; +} //end of the function ReadStructure +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteIndent(FILE *fp, int indent) +{ + while(indent-- > 0) + { + if (fprintf(fp, "\t") < 0) return qfalse; + } //end while + return qtrue; +} //end of the function WriteIndent +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteFloat(FILE *fp, float value) +{ + char buf[128]; + int l; + + sprintf(buf, "%f", value); + l = strlen(buf); + //strip any trailing zeros + while(l-- > 1) + { + if (buf[l] != '0' && buf[l] != '.') break; + if (buf[l] == '.') + { + buf[l] = 0; + break; + } //end if + buf[l] = 0; + } //end while + //write the float to file + if (fprintf(fp, "%s", buf) < 0) return 0; + return 1; +} //end of the function WriteFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteStructWithIndent(FILE *fp, structdef_t *def, char *structure, int indent) +{ + int i, num; + void *p; + fielddef_t *fd; + + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "{\r\n") < 0) return qfalse; + + indent++; + for (i = 0; def->fields[i].name; i++) + { + fd = &def->fields[i]; + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "%s\t", fd->name) < 0) return qfalse; + p = (void *)(structure + fd->offset); + if (fd->type & FT_ARRAY) + { + num = fd->maxarray; + if (fprintf(fp, "{") < 0) return qfalse; + } //end if + else + { + num = 1; + } //end else + while(num-- > 0) + { + switch(fd->type & FT_TYPE) + { + case FT_CHAR: + { + if (fprintf(fp, "%d", *(char *) p) < 0) return qfalse; + p = (char *) p + sizeof(char); + break; + } //end case + case FT_INT: + { + if (fprintf(fp, "%d", *(int *) p) < 0) return qfalse; + p = (char *) p + sizeof(int); + break; + } //end case + case FT_FLOAT: + { + if (!WriteFloat(fp, *(float *)p)) return qfalse; + p = (char *) p + sizeof(float); + break; + } //end case + case FT_STRING: + { + if (fprintf(fp, "\"%s\"", (char *) p) < 0) return qfalse; + p = (char *) p + MAX_STRINGFIELD; + break; + } //end case + case FT_STRUCT: + { + if (!WriteStructWithIndent(fp, fd->substruct, structure, indent)) return qfalse; + p = (char *) p + fd->substruct->size; + break; + } //end case + } //end switch + if (fd->type & FT_ARRAY) + { + if (num > 0) + { + if (fprintf(fp, ",") < 0) return qfalse; + } //end if + else + { + if (fprintf(fp, "}") < 0) return qfalse; + } //end else + } //end if + } //end while + if (fprintf(fp, "\r\n") < 0) return qfalse; + } //end for + indent--; + + if (!WriteIndent(fp, indent)) return qfalse; + if (fprintf(fp, "}\r\n") < 0) return qfalse; + return qtrue; +} //end of the function WriteStructWithIndent +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WriteStructure(FILE *fp, structdef_t *def, char *structure) +{ + return WriteStructWithIndent(fp, def, structure, 0); +} //end of the function WriteStructure + diff --git a/code/botlib/l_struct.h b/code/botlib/l_struct.h index 66ac035..b8ef719 100755 --- a/code/botlib/l_struct.h +++ b/code/botlib/l_struct.h @@ -1,75 +1,75 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_struct.h
- *
- * desc: structure reading/writing
- *
- * $Archive: /source/code/botlib/l_struct.h $
- *
- *****************************************************************************/
-
-
-#define MAX_STRINGFIELD 80
-//field types
-#define FT_CHAR 1 // char
-#define FT_INT 2 // int
-#define FT_FLOAT 3 // float
-#define FT_STRING 4 // char [MAX_STRINGFIELD]
-#define FT_STRUCT 6 // struct (sub structure)
-//type only mask
-#define FT_TYPE 0x00FF // only type, clear subtype
-//sub types
-#define FT_ARRAY 0x0100 // array of type
-#define FT_BOUNDED 0x0200 // bounded value
-#define FT_UNSIGNED 0x0400
-
-//structure field definition
-typedef struct fielddef_s
-{
- char *name; //name of the field
- int offset; //offset in the structure
- int type; //type of the field
- //type specific fields
- int maxarray; //maximum array size
- float floatmin, floatmax; //float min and max
- struct structdef_s *substruct; //sub structure
-} fielddef_t;
-
-//structure definition
-typedef struct structdef_s
-{
- int size;
- fielddef_t *fields;
-} structdef_t;
-
-//read a structure from a script
-int ReadStructure(source_t *source, structdef_t *def, char *structure);
-//write a structure to a file
-int WriteStructure(FILE *fp, structdef_t *def, char *structure);
-//writes indents
-int WriteIndent(FILE *fp, int indent);
-//writes a float without traling zeros
-int WriteFloat(FILE *fp, float value);
-
-
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_struct.h + * + * desc: structure reading/writing + * + * $Archive: /source/code/botlib/l_struct.h $ + * + *****************************************************************************/ + + +#define MAX_STRINGFIELD 80 +//field types +#define FT_CHAR 1 // char +#define FT_INT 2 // int +#define FT_FLOAT 3 // float +#define FT_STRING 4 // char [MAX_STRINGFIELD] +#define FT_STRUCT 6 // struct (sub structure) +//type only mask +#define FT_TYPE 0x00FF // only type, clear subtype +//sub types +#define FT_ARRAY 0x0100 // array of type +#define FT_BOUNDED 0x0200 // bounded value +#define FT_UNSIGNED 0x0400 + +//structure field definition +typedef struct fielddef_s +{ + char *name; //name of the field + int offset; //offset in the structure + int type; //type of the field + //type specific fields + int maxarray; //maximum array size + float floatmin, floatmax; //float min and max + struct structdef_s *substruct; //sub structure +} fielddef_t; + +//structure definition +typedef struct structdef_s +{ + int size; + fielddef_t *fields; +} structdef_t; + +//read a structure from a script +int ReadStructure(source_t *source, structdef_t *def, char *structure); +//write a structure to a file +int WriteStructure(FILE *fp, structdef_t *def, char *structure); +//writes indents +int WriteIndent(FILE *fp, int indent); +//writes a float without traling zeros +int WriteFloat(FILE *fp, float value); + + diff --git a/code/botlib/l_utils.h b/code/botlib/l_utils.h index 04bcc6a..a784d7a 100755 --- a/code/botlib/l_utils.h +++ b/code/botlib/l_utils.h @@ -1,35 +1,35 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-/*****************************************************************************
- * name: l_util.h
- *
- * desc: utils
- *
- * $Archive: /source/code/botlib/l_util.h $
- *
- *****************************************************************************/
-
-#define Vector2Angles(v,a) vectoangles(v,a)
-#define MAX_PATH MAX_QPATH
-#define Maximum(x,y) (x > y ? x : y)
-#define Minimum(x,y) (x < y ? x : y)
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/***************************************************************************** + * name: l_util.h + * + * desc: utils + * + * $Archive: /source/code/botlib/l_util.h $ + * + *****************************************************************************/ + +#define Vector2Angles(v,a) vectoangles(v,a) +#define MAX_PATH MAX_QPATH +#define Maximum(x,y) (x > y ? x : y) +#define Minimum(x,y) (x < y ? x : y) diff --git a/code/botlib/lcc.mak b/code/botlib/lcc.mak index 6de559e..f5567c3 100755 --- a/code/botlib/lcc.mak +++ b/code/botlib/lcc.mak @@ -1,55 +1,55 @@ -#
-# Makefile for Gladiator Bot library: gladiator.dll
-# Intended for LCC-Win32
-#
-
-CC=lcc
-CFLAGS=-DC_ONLY -o
-OBJS= be_aas_bspq2.obj \
- be_aas_bsphl.obj \
- be_aas_cluster.obj \
- be_aas_debug.obj \
- be_aas_entity.obj \
- be_aas_file.obj \
- be_aas_light.obj \
- be_aas_main.obj \
- be_aas_move.obj \
- be_aas_optimize.obj \
- be_aas_reach.obj \
- be_aas_route.obj \
- be_aas_routealt.obj \
- be_aas_sample.obj \
- be_aas_sound.obj \
- be_ai2_dm.obj \
- be_ai2_dmnet.obj \
- be_ai2_main.obj \
- be_ai_char.obj \
- be_ai_chat.obj \
- be_ai_goal.obj \
- be_ai_load.obj \
- be_ai_move.obj \
- be_ai_weap.obj \
- be_ai_weight.obj \
- be_ea.obj \
- be_interface.obj \
- l_crc.obj \
- l_libvar.obj \
- l_log.obj \
- l_memory.obj \
- l_precomp.obj \
- l_script.obj \
- l_struct.obj \
- l_utils.obj \
- q_shared.obj
-
-all: gladiator.dll
-
-gladiator.dll: $(OBJS)
- lcclnk -dll -entry GetBotAPI *.obj botlib.def -o gladiator.dll
-
-clean:
- del *.obj gladiator.dll
-
-%.obj: %.c
- $(CC) $(CFLAGS) $<
-
+# +# Makefile for Gladiator Bot library: gladiator.dll +# Intended for LCC-Win32 +# + +CC=lcc +CFLAGS=-DC_ONLY -o +OBJS= be_aas_bspq2.obj \ + be_aas_bsphl.obj \ + be_aas_cluster.obj \ + be_aas_debug.obj \ + be_aas_entity.obj \ + be_aas_file.obj \ + be_aas_light.obj \ + be_aas_main.obj \ + be_aas_move.obj \ + be_aas_optimize.obj \ + be_aas_reach.obj \ + be_aas_route.obj \ + be_aas_routealt.obj \ + be_aas_sample.obj \ + be_aas_sound.obj \ + be_ai2_dm.obj \ + be_ai2_dmnet.obj \ + be_ai2_main.obj \ + be_ai_char.obj \ + be_ai_chat.obj \ + be_ai_goal.obj \ + be_ai_load.obj \ + be_ai_move.obj \ + be_ai_weap.obj \ + be_ai_weight.obj \ + be_ea.obj \ + be_interface.obj \ + l_crc.obj \ + l_libvar.obj \ + l_log.obj \ + l_memory.obj \ + l_precomp.obj \ + l_script.obj \ + l_struct.obj \ + l_utils.obj \ + q_shared.obj + +all: gladiator.dll + +gladiator.dll: $(OBJS) + lcclnk -dll -entry GetBotAPI *.obj botlib.def -o gladiator.dll + +clean: + del *.obj gladiator.dll + +%.obj: %.c + $(CC) $(CFLAGS) $< + diff --git a/code/botlib/linux-i386.mak b/code/botlib/linux-i386.mak index db6ee20..c9607a7 100755 --- a/code/botlib/linux-i386.mak +++ b/code/botlib/linux-i386.mak @@ -1,92 +1,92 @@ -#
-# Makefile for Gladiator Bot library: gladiator.so
-# Intended for gcc/Linux
-#
-
-ARCH=i386
-CC=gcc
-BASE_CFLAGS=-Dstricmp=strcasecmp
-
-#use these cflags to optimize it
-CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \
- -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \
- -malign-jumps=2 -malign-functions=2
-#use these when debugging
-#CFLAGS=$(BASE_CFLAGS) -g
-
-LDFLAGS=-ldl -lm
-SHLIBEXT=so
-SHLIBCFLAGS=-fPIC
-SHLIBLDFLAGS=-shared
-
-DO_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $<
-
-#############################################################################
-# SETUP AND BUILD
-# GLADIATOR BOT
-#############################################################################
-
-.c.o:
- $(DO_CC)
-
-GAME_OBJS = \
- be_aas_bsphl.o\
- be_aas_bspq2.o\
- be_aas_cluster.o\
- be_aas_debug.o\
- be_aas_entity.o\
- be_aas_file.o\
- be_aas_light.o\
- be_aas_main.o\
- be_aas_move.o\
- be_aas_optimize.o\
- be_aas_reach.o\
- be_aas_route.o\
- be_aas_routealt.o\
- be_aas_sample.o\
- be_aas_sound.o\
- be_ai2_dmq2.o\
- be_ai2_dmhl.o\
- be_ai2_dmnet.o\
- be_ai2_main.o\
- be_ai_char.o\
- be_ai_chat.o\
- be_ai_goal.o\
- be_ai_load.o\
- be_ai_move.o\
- be_ai_weap.o\
- be_ai_weight.o\
- be_ea.o\
- be_interface.o\
- l_crc.o\
- l_libvar.o\
- l_log.o\
- l_memory.o\
- l_precomp.o\
- l_script.o\
- l_struct.o\
- l_utils.o\
- q_shared.o
-
-glad$(ARCH).$(SHLIBEXT) : $(GAME_OBJS)
- $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS)
-
-
-#############################################################################
-# MISC
-#############################################################################
-
-clean:
- -rm -f $(GAME_OBJS)
-
-depend:
- gcc -MM $(GAME_OBJS:.o=.c)
-
-
-install:
- cp gladiator.so ..
-
-#
-# From "make depend"
-#
-
+# +# Makefile for Gladiator Bot library: gladiator.so +# Intended for gcc/Linux +# + +ARCH=i386 +CC=gcc +BASE_CFLAGS=-Dstricmp=strcasecmp + +#use these cflags to optimize it +CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \ + -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \ + -malign-jumps=2 -malign-functions=2 +#use these when debugging +#CFLAGS=$(BASE_CFLAGS) -g + +LDFLAGS=-ldl -lm +SHLIBEXT=so +SHLIBCFLAGS=-fPIC +SHLIBLDFLAGS=-shared + +DO_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< + +############################################################################# +# SETUP AND BUILD +# GLADIATOR BOT +############################################################################# + +.c.o: + $(DO_CC) + +GAME_OBJS = \ + be_aas_bsphl.o\ + be_aas_bspq2.o\ + be_aas_cluster.o\ + be_aas_debug.o\ + be_aas_entity.o\ + be_aas_file.o\ + be_aas_light.o\ + be_aas_main.o\ + be_aas_move.o\ + be_aas_optimize.o\ + be_aas_reach.o\ + be_aas_route.o\ + be_aas_routealt.o\ + be_aas_sample.o\ + be_aas_sound.o\ + be_ai2_dmq2.o\ + be_ai2_dmhl.o\ + be_ai2_dmnet.o\ + be_ai2_main.o\ + be_ai_char.o\ + be_ai_chat.o\ + be_ai_goal.o\ + be_ai_load.o\ + be_ai_move.o\ + be_ai_weap.o\ + be_ai_weight.o\ + be_ea.o\ + be_interface.o\ + l_crc.o\ + l_libvar.o\ + l_log.o\ + l_memory.o\ + l_precomp.o\ + l_script.o\ + l_struct.o\ + l_utils.o\ + q_shared.o + +glad$(ARCH).$(SHLIBEXT) : $(GAME_OBJS) + $(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(GAME_OBJS) + + +############################################################################# +# MISC +############################################################################# + +clean: + -rm -f $(GAME_OBJS) + +depend: + gcc -MM $(GAME_OBJS:.o=.c) + + +install: + cp gladiator.so .. + +# +# From "make depend" +# + |