forked from OctaForge/OctaCore
318 lines
10 KiB
C++
318 lines
10 KiB
C++
struct gameent;
|
|
|
|
#define MAXBOTS 32
|
|
|
|
enum { AI_NONE = 0, AI_BOT, AI_MAX };
|
|
#define isaitype(a) (a >= 0 && a <= AI_MAX-1)
|
|
|
|
namespace ai
|
|
{
|
|
const int MAXWAYPOINTS = USHRT_MAX - 2;
|
|
const int MAXWAYPOINTLINKS = 6;
|
|
const int WAYPOINTRADIUS = 16;
|
|
|
|
const float MINWPDIST = 4.f; // is on top of
|
|
const float CLOSEDIST = 32.f; // is close
|
|
const float FARDIST = 128.f; // too far to remap close
|
|
const float JUMPMIN = 4.f; // decides to jump
|
|
const float JUMPMAX = 32.f; // max jump
|
|
const float SIGHTMIN = 64.f; // minimum line of sight
|
|
const float SIGHTMAX = 1024.f; // maximum line of sight
|
|
const float VIEWMIN = 90.f; // minimum field of view
|
|
const float VIEWMAX = 180.f; // maximum field of view
|
|
|
|
struct waypoint
|
|
{
|
|
vec o;
|
|
float curscore, estscore;
|
|
int weight;
|
|
ushort route, prev;
|
|
ushort links[MAXWAYPOINTLINKS];
|
|
|
|
waypoint() {}
|
|
waypoint(const vec &o, int weight = 0) : o(o), weight(weight), route(0) { memset(links, 0, sizeof(links)); }
|
|
|
|
int score() const { return int(curscore) + int(estscore); }
|
|
|
|
int find(int wp)
|
|
{
|
|
loopi(MAXWAYPOINTLINKS) if(links[i] == wp) return i;
|
|
return -1;
|
|
}
|
|
|
|
bool haslinks() { return links[0]!=0; }
|
|
};
|
|
extern vector<waypoint> waypoints;
|
|
|
|
static inline bool iswaypoint(int n)
|
|
{
|
|
return n > 0 && n < waypoints.length();
|
|
}
|
|
|
|
extern int showwaypoints, dropwaypoints;
|
|
extern int closestwaypoint(const vec &pos, float mindist, bool links, gameent *d = NULL);
|
|
extern void findwaypointswithin(const vec &pos, float mindist, float maxdist, vector<int> &results);
|
|
extern void inferwaypoints(gameent *d, const vec &o, const vec &v, float mindist = ai::CLOSEDIST);
|
|
|
|
struct avoidset
|
|
{
|
|
struct obstacle
|
|
{
|
|
void *owner;
|
|
int numwaypoints;
|
|
float above;
|
|
|
|
obstacle(void *owner, float above = -1) : owner(owner), numwaypoints(0), above(above) {}
|
|
};
|
|
|
|
vector<obstacle> obstacles;
|
|
vector<int> waypoints;
|
|
|
|
void clear()
|
|
{
|
|
obstacles.setsize(0);
|
|
waypoints.setsize(0);
|
|
}
|
|
|
|
void add(void *owner, float above)
|
|
{
|
|
obstacles.add(obstacle(owner, above));
|
|
}
|
|
|
|
void add(void *owner, float above, int wp)
|
|
{
|
|
if(obstacles.empty() || owner != obstacles.last().owner) add(owner, above);
|
|
obstacles.last().numwaypoints++;
|
|
waypoints.add(wp);
|
|
}
|
|
|
|
void add(avoidset &avoid)
|
|
{
|
|
waypoints.put(avoid.waypoints.getbuf(), avoid.waypoints.length());
|
|
loopv(avoid.obstacles)
|
|
{
|
|
obstacle &o = avoid.obstacles[i];
|
|
if(obstacles.empty() || o.owner != obstacles.last().owner) add(o.owner, o.above);
|
|
obstacles.last().numwaypoints += o.numwaypoints;
|
|
}
|
|
}
|
|
|
|
void avoidnear(void *owner, float above, const vec &pos, float limit);
|
|
|
|
#define loopavoid(v, d, body) \
|
|
if(!(v).obstacles.empty()) \
|
|
{ \
|
|
int cur = 0; \
|
|
loopv((v).obstacles) \
|
|
{ \
|
|
const ai::avoidset::obstacle &ob = (v).obstacles[i]; \
|
|
int next = cur + ob.numwaypoints; \
|
|
if(ob.owner != d) \
|
|
{ \
|
|
for(; cur < next; cur++) \
|
|
{ \
|
|
int wp = (v).waypoints[cur]; \
|
|
body; \
|
|
} \
|
|
} \
|
|
cur = next; \
|
|
} \
|
|
}
|
|
|
|
bool find(int n, gameent *d) const
|
|
{
|
|
loopavoid(*this, d, { if(wp == n) return true; });
|
|
return false;
|
|
}
|
|
|
|
int remap(gameent *d, int n, vec &pos, bool retry = false);
|
|
};
|
|
|
|
extern bool route(gameent *d, int node, int goal, vector<int> &route, const avoidset &obstacles, int retries = 0);
|
|
extern void navigate();
|
|
extern void clearwaypoints(bool full = false);
|
|
extern void seedwaypoints();
|
|
extern void loadwaypoints(bool force = false, const char *mname = NULL);
|
|
extern void savewaypoints(bool force = false, const char *mname = NULL);
|
|
|
|
// ai state information for the owner client
|
|
enum
|
|
{
|
|
AI_S_WAIT = 0, // waiting for next command
|
|
AI_S_DEFEND, // defend goal target
|
|
AI_S_PURSUE, // pursue goal target
|
|
AI_S_INTEREST, // interest in goal entity
|
|
AI_S_MAX
|
|
};
|
|
|
|
enum
|
|
{
|
|
AI_T_NODE,
|
|
AI_T_PLAYER,
|
|
AI_T_AFFINITY,
|
|
AI_T_ENTITY,
|
|
AI_T_MAX
|
|
};
|
|
|
|
struct interest
|
|
{
|
|
int state, node, target, targtype;
|
|
float score;
|
|
interest() : state(-1), node(-1), target(-1), targtype(-1), score(0.f) {}
|
|
~interest() {}
|
|
};
|
|
|
|
struct aistate
|
|
{
|
|
int type, millis, targtype, target, idle;
|
|
bool override;
|
|
|
|
aistate(int m, int t, int r = -1, int v = -1) : type(t), millis(m), targtype(r), target(v)
|
|
{
|
|
reset();
|
|
}
|
|
~aistate() {}
|
|
|
|
void reset()
|
|
{
|
|
idle = 0;
|
|
override = false;
|
|
}
|
|
};
|
|
|
|
const int NUMPREVNODES = 6;
|
|
|
|
struct aiinfo
|
|
{
|
|
vector<aistate> state;
|
|
vector<int> route;
|
|
vec target, spot;
|
|
int enemy, enemyseen, enemymillis, weappref, prevnodes[NUMPREVNODES], targnode, targlast, targtime, targseq,
|
|
lastrun, lasthunt, lastaction, lastcheck, jumpseed, jumprand, blocktime, huntseq, blockseq, lastaimrnd;
|
|
float targyaw, targpitch, views[3], aimrnd[3];
|
|
bool dontmove, becareful, tryreset, trywipe;
|
|
|
|
aiinfo()
|
|
{
|
|
clearsetup();
|
|
reset();
|
|
loopk(3) views[k] = 0.f;
|
|
}
|
|
~aiinfo() {}
|
|
|
|
void clearsetup()
|
|
{
|
|
weappref = GUN_RAIL;
|
|
spot = target = vec(0, 0, 0);
|
|
lastaction = lasthunt = lastcheck = enemyseen = enemymillis = blocktime = huntseq = blockseq = targtime = targseq = lastaimrnd = 0;
|
|
lastrun = jumpseed = lastmillis;
|
|
jumprand = lastmillis+5000;
|
|
targnode = targlast = enemy = -1;
|
|
}
|
|
|
|
void clear(bool prev = false)
|
|
{
|
|
if(prev) memset(prevnodes, -1, sizeof(prevnodes));
|
|
route.setsize(0);
|
|
}
|
|
|
|
void wipe(bool prev = false)
|
|
{
|
|
clear(prev);
|
|
state.setsize(0);
|
|
addstate(AI_S_WAIT);
|
|
trywipe = false;
|
|
}
|
|
|
|
void clean(bool tryit = false)
|
|
{
|
|
if(!tryit) becareful = dontmove = false;
|
|
targyaw = rnd(360);
|
|
targpitch = 0.f;
|
|
tryreset = tryit;
|
|
}
|
|
|
|
void reset(bool tryit = false) { wipe(); clean(tryit); }
|
|
|
|
bool hasprevnode(int n) const
|
|
{
|
|
loopi(NUMPREVNODES) if(prevnodes[i] == n) return true;
|
|
return false;
|
|
}
|
|
|
|
void addprevnode(int n)
|
|
{
|
|
if(prevnodes[0] != n)
|
|
{
|
|
memmove(&prevnodes[1], prevnodes, sizeof(prevnodes) - sizeof(prevnodes[0]));
|
|
prevnodes[0] = n;
|
|
}
|
|
}
|
|
|
|
aistate &addstate(int t, int r = -1, int v = -1)
|
|
{
|
|
return state.add(aistate(lastmillis, t, r, v));
|
|
}
|
|
|
|
void removestate(int index = -1)
|
|
{
|
|
if(index < 0) state.pop();
|
|
else if(state.inrange(index)) state.remove(index);
|
|
if(!state.length()) addstate(AI_S_WAIT);
|
|
}
|
|
|
|
aistate &getstate(int idx = -1)
|
|
{
|
|
if(state.inrange(idx)) return state[idx];
|
|
return state.last();
|
|
}
|
|
|
|
aistate &switchstate(aistate &b, int t, int r = -1, int v = -1)
|
|
{
|
|
if((b.type == t && b.targtype == r) || (b.type == AI_S_INTEREST && b.targtype == AI_T_NODE))
|
|
{
|
|
b.millis = lastmillis;
|
|
b.target = v;
|
|
b.reset();
|
|
return b;
|
|
}
|
|
return addstate(t, r, v);
|
|
}
|
|
};
|
|
|
|
extern avoidset obstacles;
|
|
extern vec aitarget;
|
|
|
|
extern float viewdist(int x = 101);
|
|
extern float viewfieldx(int x = 101);
|
|
extern float viewfieldy(int x = 101);
|
|
extern bool targetable(gameent *d, gameent *e);
|
|
extern bool cansee(gameent *d, vec &x, vec &y, vec &targ = aitarget);
|
|
|
|
extern void init(gameent *d, int at, int on, int sk, int bn, int pm, int col, const char *name, int team);
|
|
extern void update();
|
|
extern void avoid();
|
|
extern void think(gameent *d, bool run);
|
|
|
|
extern bool badhealth(gameent *d);
|
|
extern bool checkothers(vector<int> &targets, gameent *d = NULL, int state = -1, int targtype = -1, int target = -1, bool teams = false, int *members = NULL);
|
|
extern bool makeroute(gameent *d, aistate &b, int node, bool changed = true, int retries = 0);
|
|
extern bool makeroute(gameent *d, aistate &b, const vec &pos, bool changed = true, int retries = 0);
|
|
extern bool randomnode(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX);
|
|
extern bool randomnode(gameent *d, aistate &b, float guard = SIGHTMIN, float wander = SIGHTMAX);
|
|
extern bool violence(gameent *d, aistate &b, gameent *e, int pursue = 0);
|
|
extern bool patrol(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX, int walk = 1, bool retry = false);
|
|
extern bool defend(gameent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX, int walk = 1);
|
|
extern void assist(gameent *d, aistate &b, vector<interest> &interests, bool all = false, bool force = false);
|
|
extern bool parseinterests(gameent *d, aistate &b, vector<interest> &interests, bool override = false, bool ignore = false);
|
|
|
|
extern void spawned(gameent *d);
|
|
extern void damaged(gameent *d, gameent *e);
|
|
extern void killed(gameent *d, gameent *e);
|
|
extern void itemspawned(int ent);
|
|
|
|
extern void render();
|
|
}
|
|
|
|
|